@indigoai-us/hq-cli 5.312.2-beta.0 → 5.314.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/CHANGELOG.md +1 -4293
- package/assets/scaffold/core/scripts/checkpoint-stop-gate.sh +43 -281
- package/assets/scaffold/core/scripts/hook-lib.sh +50 -345
- package/assets/scaffold/core/scripts/hq-session.sh +23 -184
- package/assets/scaffold/core/scripts/lib/session-id.sh +2 -3
- package/assets/scaffold/core/scripts/lib/session-scope-capability.sh +1 -1
- package/assets/scaffold/core/scripts/lib/work-mesh-enqueue.sh +1 -2
- package/assets/scaffold/core/scripts/qmd-reindex-after-sync.sh +87 -10
- package/assets/scaffold/core/scripts/rebuild-company-knowledge-index.sh +3 -36
- package/assets/scaffold/core/scripts/rebuild-projects-index.sh +12 -99
- package/assets/scaffold/core/scripts/token-usage-report.sh +20 -81
- package/dist/bin/hq-auth-refresh.d.ts +0 -2
- package/dist/bin/hq-auth-refresh.js +6 -12
- package/dist/commands/__fixtures__/access-vault.d.ts +0 -2
- package/dist/commands/__fixtures__/access-vault.js +1 -1
- package/dist/commands/_patterns.d.ts +0 -16
- package/dist/commands/_patterns.js +3 -20
- package/dist/commands/access.d.ts +1 -2
- package/dist/commands/access.js +49 -58
- package/dist/commands/agents.d.ts +4 -106
- package/dist/commands/agents.js +45 -924
- package/dist/commands/auth.d.ts +0 -1
- package/dist/commands/auth.js +19 -189
- package/dist/commands/billing.d.ts +0 -3
- package/dist/commands/billing.js +1 -6
- package/dist/commands/channels.js +1 -56
- package/dist/commands/cloud-demote.js +2 -3
- package/dist/commands/cloud-provision.d.ts +0 -14
- package/dist/commands/cloud-provision.js +58 -232
- package/dist/commands/cloud.d.ts +15 -49
- package/dist/commands/cloud.js +122 -358
- package/dist/commands/company-transfer.d.ts +28 -4
- package/dist/commands/company-transfer.js +49 -5
- package/dist/commands/company.js +3 -66
- package/dist/commands/core-checkpoint.d.ts +9 -19
- package/dist/commands/core-checkpoint.js +37 -301
- package/dist/commands/core.d.ts +0 -4
- package/dist/commands/core.js +80 -270
- package/dist/commands/crm.js +3 -52
- package/dist/commands/db-provision.js +4 -4
- package/dist/commands/db-sql.js +21 -7
- package/dist/commands/dm.d.ts +12 -119
- package/dist/commands/dm.js +38 -388
- package/dist/commands/doctor.d.ts +0 -12
- package/dist/commands/doctor.js +12 -158
- package/dist/commands/feedback.d.ts +0 -6
- package/dist/commands/feedback.js +37 -86
- package/dist/commands/files-browse.d.ts +12 -32
- package/dist/commands/files-browse.js +60 -176
- package/dist/commands/files.d.ts +0 -17
- package/dist/commands/files.js +47 -300
- package/dist/commands/group-grants.js +2 -3
- package/dist/commands/groups.js +5 -6
- package/dist/commands/index-cmd.d.ts +1 -19
- package/dist/commands/index-cmd.js +7 -74
- package/dist/commands/integrations-api.d.ts +10 -15
- package/dist/commands/integrations-api.js +22 -27
- package/dist/commands/integrations-connect.d.ts +13 -51
- package/dist/commands/integrations-connect.js +71 -86
- package/dist/commands/integrations-core.d.ts +6 -6
- package/dist/commands/integrations-core.js +11 -25
- package/dist/commands/integrations-import.d.ts +1 -1
- package/dist/commands/integrations-import.js +24 -43
- package/dist/commands/integrations-manage.js +13 -20
- package/dist/commands/integrations.d.ts +0 -8
- package/dist/commands/integrations.js +7 -94
- package/dist/commands/login.js +6 -49
- package/dist/commands/mcp-registration.d.ts +25 -32
- package/dist/commands/mcp-registration.js +23 -256
- package/dist/commands/mcp-status.js +0 -1
- package/dist/commands/meetings.js +34 -55
- package/dist/commands/members.js +1 -5
- package/dist/commands/mesh.d.ts +0 -11
- package/dist/commands/mesh.js +76 -1086
- package/dist/commands/onboard-identity-guard.d.ts +1 -1
- package/dist/commands/onboard.js +5 -24
- package/dist/commands/pack-install.js +1 -3
- package/dist/commands/packs.js +1 -11
- package/dist/commands/people.d.ts +0 -4
- package/dist/commands/people.js +3 -37
- package/dist/commands/publish.d.ts +1 -8
- package/dist/commands/publish.js +4 -17
- package/dist/commands/reindex.js +6 -56
- package/dist/commands/rescue.d.ts +0 -4
- package/dist/commands/rescue.js +17 -27
- package/dist/commands/run.js +4 -18
- package/dist/commands/safe-extract.d.ts +2 -11
- package/dist/commands/safe-extract.js +8 -36
- package/dist/commands/scaffold-fast.d.ts +41 -0
- package/dist/commands/scaffold-fast.js +57 -0
- package/dist/commands/secrets.d.ts +2 -20
- package/dist/commands/secrets.js +71 -175
- package/dist/commands/skill.d.ts +4 -35
- package/dist/commands/skill.js +18 -357
- package/dist/commands/sync-mode.d.ts +0 -11
- package/dist/commands/sync-mode.js +0 -30
- package/dist/commands/sync-narrow.js +5 -36
- package/dist/commands/team-sync.js +10 -71
- package/dist/commands/update.js +0 -6
- package/dist/commands/whoami.d.ts +0 -22
- package/dist/commands/whoami.js +4 -137
- package/dist/commands/workers.d.ts +14 -2
- package/dist/commands/workers.js +10 -7
- package/dist/fast-core.d.ts +7 -6
- package/dist/fast-core.js +24 -80
- package/dist/index.d.ts +4 -3
- package/dist/index.js +9 -59
- package/dist/lazy-commands.d.ts +44 -20
- package/dist/lazy-commands.js +58 -60
- package/dist/lib/company-skill-wrapper.d.ts +0 -1
- package/dist/lib/company-skill-wrapper.js +29 -121
- package/dist/lib/core-utils/codex-skill-bridge.d.ts +1 -2
- package/dist/lib/core-utils/qmd-reindex-after-sync.d.ts +1 -14
- package/dist/lib/core-utils/qmd-reindex-after-sync.js +17 -101
- package/dist/lib/core-utils/soft-timeout.js +1 -1
- package/dist/lib/core-utils/timeout-guard.d.ts +3 -3
- package/dist/lib/core-utils/timeout-guard.js +13 -28
- package/dist/lib/core-utils/token-usage-report.js +3 -17
- package/dist/lib/db/control-plane.js +0 -6
- package/dist/lib/db/local.d.ts +5 -0
- package/dist/lib/db/local.js +1 -1
- package/dist/lib/db/migrate.d.ts +12 -0
- package/dist/lib/db/migrate.js +5 -5
- package/dist/lib/db/remote-sql.d.ts +2 -2
- package/dist/lib/db/remote-sql.js +3 -3
- package/dist/lib/db/sql.d.ts +10 -0
- package/dist/lib/db/sql.js +8 -4
- package/dist/lib/doctor/__testing__/fake-hq-tree.d.ts +1 -1
- package/dist/lib/doctor/__testing__/fake-hq-tree.js +1 -1
- package/dist/lib/doctor/checks/claude-wiring.js +1 -70
- package/dist/lib/doctor/checks/codex-wiring.d.ts +4 -0
- package/dist/lib/doctor/checks/codex-wiring.js +35 -3
- package/dist/lib/doctor/checks/grok-wiring.js +34 -1
- package/dist/lib/doctor/checks/integrations.js +0 -11
- package/dist/lib/doctor/checks/runtime-health.d.ts +17 -4
- package/dist/lib/doctor/checks/runtime-health.js +7 -20
- package/dist/lib/doctor/checks/sync-health.d.ts +0 -21
- package/dist/lib/doctor/checks/sync-health.js +2 -72
- package/dist/lib/doctor/checks/work-context.js +6 -12
- package/dist/lib/doctor/compat.d.ts +16 -0
- package/dist/lib/doctor/compat.js +3 -4
- package/dist/lib/doctor/deep/effects.js +3 -1
- package/dist/lib/doctor/deep/executor.d.ts +10 -4
- package/dist/lib/doctor/deep/executor.js +1 -1
- package/dist/lib/doctor/deep/parity.d.ts +29 -2
- package/dist/lib/doctor/deep/parity.js +7 -7
- package/dist/lib/doctor/deep/sandbox.d.ts +25 -7
- package/dist/lib/doctor/deep/sandbox.js +7 -5
- package/dist/lib/doctor/fix/apply.d.ts +8 -79
- package/dist/lib/doctor/fix/apply.js +80 -292
- package/dist/lib/doctor/fix/remediation.d.ts +6 -14
- package/dist/lib/doctor/fix/remediation.js +9 -49
- package/dist/lib/doctor/fixtures/schema.js +3 -1
- package/dist/lib/doctor/json-output.d.ts +1 -17
- package/dist/lib/doctor/json-output.js +2 -7
- package/dist/lib/doctor/platform.d.ts +26 -0
- package/dist/lib/doctor/platform.js +5 -5
- package/dist/lib/doctor/registry.d.ts +0 -14
- package/dist/lib/doctor/registry.js +0 -71
- package/dist/lib/doctor/report.js +1 -1
- package/dist/lib/doctor/types.d.ts +0 -5
- package/dist/lib/doctor/types.js +0 -20
- package/dist/lib/flag-registry-worker.js +1 -12
- package/dist/lib/flag-registry.d.ts +6 -26
- package/dist/lib/flag-registry.js +16 -133
- package/dist/lib/index-render/companies.d.ts +1 -4
- package/dist/lib/index-render/companies.js +3 -66
- package/dist/lib/index-render/company-knowledge.js +17 -80
- package/dist/lib/index-render/index.d.ts +0 -1
- package/dist/lib/index-render/index.js +1 -2
- package/dist/lib/index-render/projects.js +5 -82
- package/dist/lib/index-render/shared.d.ts +1 -13
- package/dist/lib/index-render/shared.js +2 -41
- package/dist/lib/mesh/api.d.ts +10 -103
- package/dist/lib/mesh/api.js +64 -334
- package/dist/lib/mesh/client.d.ts +2 -3
- package/dist/lib/mesh/client.js +2 -3
- package/dist/lib/mesh/live/backfill-held.d.ts +16 -67
- package/dist/lib/mesh/live/backfill-held.js +61 -415
- package/dist/lib/mesh/live/backoff.d.ts +2 -2
- package/dist/lib/mesh/live/backoff.js +3 -16
- package/dist/lib/mesh/live/daemon/board-refresh.d.ts +1 -7
- package/dist/lib/mesh/live/daemon/board-refresh.js +30 -78
- package/dist/lib/mesh/live/daemon/credentials.d.ts +10 -64
- package/dist/lib/mesh/live/daemon/credentials.js +20 -270
- package/dist/lib/mesh/live/daemon/doctor.d.ts +0 -51
- package/dist/lib/mesh/live/daemon/doctor.js +6 -180
- package/dist/lib/mesh/live/daemon/index.d.ts +10 -13
- package/dist/lib/mesh/live/daemon/index.js +6 -7
- package/dist/lib/mesh/live/daemon/install.d.ts +1 -255
- package/dist/lib/mesh/live/daemon/install.js +13 -811
- package/dist/lib/mesh/live/daemon/paths.d.ts +0 -2
- package/dist/lib/mesh/live/daemon/paths.js +0 -2
- package/dist/lib/mesh/live/daemon/presence.d.ts +5 -24
- package/dist/lib/mesh/live/daemon/presence.js +10 -79
- package/dist/lib/mesh/live/daemon/run.d.ts +2 -48
- package/dist/lib/mesh/live/daemon/run.js +33 -485
- package/dist/lib/mesh/live/daemon/state.d.ts +2 -19
- package/dist/lib/mesh/live/daemon/state.js +2 -34
- package/dist/lib/mesh/live/daemon/transcript-watch.d.ts +9 -45
- package/dist/lib/mesh/live/daemon/transcript-watch.js +31 -227
- package/dist/lib/mesh/live/emit-client.d.ts +0 -8
- package/dist/lib/mesh/live/emit-client.js +8 -23
- package/dist/lib/mesh/live/emit-evidence.d.ts +0 -19
- package/dist/lib/mesh/live/emit-evidence.js +2 -20
- package/dist/lib/mesh/live/emit.d.ts +0 -9
- package/dist/lib/mesh/live/emit.js +2 -28
- package/dist/lib/mesh/live/enqueue.d.ts +0 -5
- package/dist/lib/mesh/live/enqueue.js +0 -19
- package/dist/lib/mesh/live/flush.d.ts +5 -17
- package/dist/lib/mesh/live/flush.js +38 -391
- package/dist/lib/mesh/live/format-spool-line.d.ts +2 -5
- package/dist/lib/mesh/live/format-spool-line.js +0 -10
- package/dist/lib/mesh/live/paths.d.ts +0 -2
- package/dist/lib/mesh/live/paths.js +0 -4
- package/dist/lib/mesh/live/session-event.schema.json +1 -24
- package/dist/lib/mesh/live/session-events-client.d.ts +0 -2
- package/dist/lib/mesh/live/session-events-client.js +0 -26
- package/dist/lib/mesh/live/spool.d.ts +0 -19
- package/dist/lib/mesh/live/spool.js +3 -269
- package/dist/lib/mesh/live/validate-session-event.d.ts +1 -1
- package/dist/lib/mesh/live/validate-session-event.js +1 -1
- package/dist/lib/plan-limit-nag.d.ts +6 -102
- package/dist/lib/plan-limit-nag.js +43 -348
- package/dist/lib/scan-packages/index.d.ts +0 -2
- package/dist/lib/scan-packages/index.js +6 -168
- package/dist/lib/search-index/background.d.ts +1 -8
- package/dist/lib/search-index/background.js +200 -55
- package/dist/lib/search-index/index.d.ts +0 -12
- package/dist/lib/search-index/index.js +7 -153
- package/dist/lib/work-context/company.d.ts +20 -22
- package/dist/lib/work-context/company.js +40 -124
- package/dist/lib/work-context/config.d.ts +11 -58
- package/dist/lib/work-context/config.js +83 -334
- package/dist/lib/work-context/contract.d.ts +0 -10
- package/dist/lib/work-context/index.d.ts +25 -0
- package/dist/lib/work-context/index.js +21 -0
- package/dist/lib/work-context/outbox.d.ts +1 -14
- package/dist/lib/work-context/outbox.js +1 -87
- package/dist/lib/work-context/reconcile.d.ts +0 -15
- package/dist/lib/work-context/reconcile.js +25 -380
- package/dist/lib/work-context/state.d.ts +10 -93
- package/dist/lib/work-context/state.js +29 -342
- package/dist/lib/workers-registry/index.js +8 -62
- package/dist/main.d.ts +0 -12
- package/dist/main.js +73 -266
- package/dist/node-network-compat.d.ts +30 -2
- package/dist/node-network-compat.js +51 -4
- package/dist/node-preflight.d.ts +1 -7
- package/dist/node-preflight.js +5 -5
- package/dist/register-all.d.ts +29 -4
- package/dist/register-all.js +233 -4
- package/dist/run/discover-schemas.d.ts +1 -2
- package/dist/run/env-graph-guard.d.ts +2 -2
- package/dist/run/env-graph-guard.js +2 -5
- package/dist/run/hq-plugin.d.ts +0 -4
- package/dist/run/hq-plugin.js +8 -36
- package/dist/sentry-before-send.js +0 -80
- package/dist/sentry.d.ts +2 -8
- package/dist/sentry.js +13 -117
- package/dist/startup-registration.d.ts +2 -26
- package/dist/startup-registration.js +15 -116
- package/dist/utils/cli-telemetry.d.ts +1 -74
- package/dist/utils/cli-telemetry.js +7 -434
- package/dist/utils/client-health.d.ts +4 -0
- package/dist/utils/client-health.js +2 -2
- package/dist/utils/cognito-session.d.ts +0 -71
- package/dist/utils/cognito-session.js +27 -481
- package/dist/utils/contribution-table.d.ts +1 -1
- package/dist/utils/environmental-error.d.ts +0 -18
- package/dist/utils/environmental-error.js +7 -240
- package/dist/utils/expected-cli-error.d.ts +0 -2
- package/dist/utils/expected-cli-error.js +0 -4
- package/dist/utils/feedback-diagnostics.d.ts +1 -2
- package/dist/utils/feedback-log-bundle.d.ts +26 -51
- package/dist/utils/feedback-log-bundle.js +31 -211
- package/dist/utils/feedback-logs.d.ts +47 -18
- package/dist/utils/feedback-logs.js +21 -50
- package/dist/utils/feedback-screenshots.d.ts +1 -0
- package/dist/utils/feedback-screenshots.js +1 -1
- package/dist/utils/hq-roots.d.ts +2 -25
- package/dist/utils/hq-roots.js +7 -67
- package/dist/utils/incomplete-install-error.d.ts +19 -87
- package/dist/utils/incomplete-install-error.js +68 -319
- package/dist/utils/install-tree-torn.d.ts +24 -260
- package/dist/utils/install-tree-torn.js +51 -998
- package/dist/utils/large-file-guard.d.ts +10 -0
- package/dist/utils/large-file-guard.js +1 -1
- package/dist/utils/local-files-overview.d.ts +8 -4
- package/dist/utils/local-files-overview.js +7 -48
- package/dist/utils/login-provider.d.ts +0 -7
- package/dist/utils/login-provider.js +0 -34
- package/dist/utils/pack-contributions.d.ts +9 -2
- package/dist/utils/pack-contributions.js +43 -6
- package/dist/utils/package-root-diagnostics.d.ts +1 -1
- package/dist/utils/people.d.ts +11 -7
- package/dist/utils/people.js +20 -5
- package/dist/utils/plan-gate-error.d.ts +10 -52
- package/dist/utils/plan-gate-error.js +41 -364
- package/dist/utils/qmd-model-download-error.d.ts +8 -0
- package/dist/utils/qmd-model-download-error.js +1 -1
- package/dist/utils/qmd-query-document-error.d.ts +6 -0
- package/dist/utils/qmd-query-document-error.js +1 -1
- package/dist/utils/redact-error-text.js +1 -2
- package/dist/utils/run-bundled-script.d.ts +0 -6
- package/dist/utils/run-bundled-script.js +4 -117
- package/dist/utils/secrets-cache.d.ts +3 -7
- package/dist/utils/secrets-cache.js +14 -162
- package/dist/utils/self-update.d.ts +1 -34
- package/dist/utils/self-update.js +42 -660
- package/dist/utils/sentry-fingerprint.d.ts +1 -23
- package/dist/utils/sentry-fingerprint.js +31 -223
- package/dist/utils/team-upgrade.d.ts +1 -1
- package/dist/utils/team-upgrade.js +3 -4
- package/dist/utils/vault-api.d.ts +1 -21
- package/dist/utils/vault-api.js +84 -251
- package/dist/utils/version-check.d.ts +1 -11
- package/dist/utils/version-check.js +3 -32
- package/dist/utils/version-gate.d.ts +17 -101
- package/dist/utils/version-gate.js +70 -645
- package/dist/utils/windows-spawn.d.ts +0 -6
- package/dist/utils/windows-spawn.js +0 -37
- package/package.json +8 -20
- package/README.md +0 -113
- package/assets/bot-workers/setup/context/USER-GUIDE.md +0 -404
- package/assets/bot-workers/setup/context/quick-reference.md +0 -199
- package/assets/bot-workers/setup/skills/agents-and-team.md +0 -167
- package/assets/bot-workers/setup/skills/explain-hq.md +0 -35
- package/assets/bot-workers/setup/skills/first-company.md +0 -74
- package/assets/bot-workers/setup/skills/joining-a-team.md +0 -125
- package/assets/bot-workers/setup/skills/pricing.md +0 -92
- package/assets/bot-workers/setup/skills/standing-help.md +0 -81
- package/assets/bot-workers/setup/skills/your-business.md +0 -281
- package/assets/bot-workers/setup/worker.yaml +0 -791
- package/assets/lanes-hooks/hq-lanes-drain.json +0 -15
- package/assets/lanes-hooks/hq-lanes-drain.mjs +0 -275
- package/assets/lanes-hooks/hq-lanes-drain.sh +0 -21
- package/assets/lanes-reconcile/hq-lanes-reconcile.service +0 -11
- package/assets/lanes-reconcile/hq-lanes-reconcile.timer +0 -12
- package/assets/scaffold/.claude/hooks/validate-policy-frontmatter.sh +0 -485
- package/assets/scaffold/core/scripts/audit-log.sh +0 -384
- package/assets/scaffold/core/scripts/clean-worktree-reconcile-handoff.sh +0 -171
- package/assets/scaffold/core/scripts/eval-trigger.sh +0 -193
- package/assets/scaffold/core/scripts/git-pack-extension-check.sh +0 -164
- package/assets/scaffold/core/scripts/handoff-finalize.sh +0 -688
- package/assets/scaffold/core/scripts/handoff-open-steps.sh +0 -81
- package/assets/scaffold/core/scripts/handoff-post.sh +0 -252
- package/assets/scaffold/core/scripts/hq-delegate-bundle.sh +0 -307
- package/assets/scaffold/core/scripts/hq-delegate-grant.sh +0 -252
- package/assets/scaffold/core/scripts/hq-delegate-pickup.sh +0 -99
- package/assets/scaffold/core/scripts/hq-delegate-publish.sh +0 -91
- package/assets/scaffold/core/scripts/hq-delegate-repo.sh +0 -158
- package/assets/scaffold/core/scripts/hq-delegate-resolve.sh +0 -145
- package/assets/scaffold/core/scripts/hq-delegate-secrets.sh +0 -129
- package/assets/scaffold/core/scripts/hq-delegate-send.sh +0 -259
- package/assets/scaffold/core/scripts/hq-delegate-transfer.sh +0 -175
- package/assets/scaffold/core/scripts/hq-delegate-verify.sh +0 -238
- package/assets/scaffold/core/scripts/hq-dm-bind.sh +0 -585
- package/assets/scaffold/core/scripts/hq-job-notify.sh +0 -389
- package/assets/scaffold/core/scripts/hq-job-probe.sh +0 -835
- package/assets/scaffold/core/scripts/hq-job-remediate.sh +0 -359
- package/assets/scaffold/core/scripts/hq-job-run.sh +0 -1025
- package/assets/scaffold/core/scripts/hq-pin.sh +0 -118
- package/assets/scaffold/core/scripts/jobs-schedule-parse.sh +0 -310
- package/assets/scaffold/core/scripts/jobs-validate.sh +0 -451
- package/assets/scaffold/core/scripts/knowledge-prefs.sh +0 -55
- package/assets/scaffold/core/scripts/lib/portable.sh +0 -211
- package/assets/scaffold/core/scripts/lib/secret-patterns.sh +0 -58
- package/assets/scaffold/core/scripts/ontology-audience-grant.sh +0 -81
- package/assets/scaffold/core/scripts/ontology-brief.mjs +0 -93
- package/assets/scaffold/core/scripts/ontology-candidate.sh +0 -100
- package/assets/scaffold/core/scripts/ontology-garden.mjs +0 -238
- package/assets/scaffold/core/scripts/ontology-source-worklist.sh +0 -79
- package/assets/scaffold/core/scripts/outpost-jobs-reconcile.sh +0 -834
- package/assets/scaffold/core/scripts/policy-age-report.sh +0 -156
- package/assets/scaffold/core/scripts/policy-retire.sh +0 -199
- package/assets/scaffold/core/scripts/qmd-reindex-bg.sh +0 -40
- package/assets/scaffold/core/scripts/read-policy-frontmatter.sh +0 -32
- package/assets/scaffold/core/scripts/refresh-vault-access.sh +0 -188
- package/assets/scaffold/core/scripts/resolve-company.sh +0 -199
- package/assets/scaffold/core/scripts/resume-thread-lock.sh +0 -285
- package/assets/scaffold/core/scripts/source-yaml-validate.sh +0 -36
- package/assets/scaffold/core/scripts/verify-story-deliverables.sh +0 -148
- package/assets/scaffold/core/scripts/work-mesh-live-bind-trusted.sh +0 -152
- package/assets/scaffold/core/scripts/worktree-gc.sh +0 -362
- package/contracts/preflight/v1/fixtures.json +0 -87
- package/contracts/preflight/v1/schema.json +0 -58
- package/dist/command-catalog.generated.d.ts +0 -10655
- package/dist/command-catalog.generated.js +0 -13737
- package/dist/command-registration-plan.d.ts +0 -474
- package/dist/command-registration-plan.js +0 -136
- package/dist/commands/agent-enroll.d.ts +0 -146
- package/dist/commands/agent-enroll.js +0 -401
- package/dist/commands/agent-inbox.d.ts +0 -9
- package/dist/commands/agent-inbox.js +0 -102
- package/dist/commands/agent-kit.d.ts +0 -87
- package/dist/commands/agent-kit.js +0 -480
- package/dist/commands/agent-mcp.d.ts +0 -24
- package/dist/commands/agent-mcp.js +0 -127
- package/dist/commands/agent-probe.d.ts +0 -77
- package/dist/commands/agent-probe.js +0 -335
- package/dist/commands/agent.d.ts +0 -13
- package/dist/commands/agent.js +0 -40
- package/dist/commands/billing-sponsor.d.ts +0 -83
- package/dist/commands/billing-sponsor.js +0 -344
- package/dist/commands/bot-companies.d.ts +0 -40
- package/dist/commands/bot-companies.js +0 -64
- package/dist/commands/bot-continuity.d.ts +0 -28
- package/dist/commands/bot-continuity.js +0 -68
- package/dist/commands/bot-jobs.d.ts +0 -127
- package/dist/commands/bot-jobs.js +0 -454
- package/dist/commands/bot.d.ts +0 -278
- package/dist/commands/bot.js +0 -1982
- package/dist/commands/cloud-retire.d.ts +0 -51
- package/dist/commands/cloud-retire.js +0 -154
- package/dist/commands/core-audit-log-native.d.ts +0 -9
- package/dist/commands/core-audit-log-native.js +0 -257
- package/dist/commands/core-checkpoint-gate-setting.d.ts +0 -59
- package/dist/commands/core-checkpoint-gate-setting.js +0 -163
- package/dist/commands/core-checkpoint-stop-gate-native.d.ts +0 -34
- package/dist/commands/core-checkpoint-stop-gate-native.js +0 -725
- package/dist/commands/core-command-catalog.d.ts +0 -34
- package/dist/commands/core-command-catalog.js +0 -62
- package/dist/commands/core-company-index-scope-flag.d.ts +0 -3
- package/dist/commands/core-company-index-scope-flag.js +0 -6
- package/dist/commands/core-delegate-w1b.d.ts +0 -10
- package/dist/commands/core-delegate-w1b.js +0 -1219
- package/dist/commands/core-delegate.d.ts +0 -46
- package/dist/commands/core-delegate.js +0 -1040
- package/dist/commands/core-dm-bind-native.d.ts +0 -32
- package/dist/commands/core-dm-bind-native.js +0 -668
- package/dist/commands/core-git-pack-extension-check-native.d.ts +0 -22
- package/dist/commands/core-git-pack-extension-check-native.js +0 -190
- package/dist/commands/core-handoff-fast.d.ts +0 -18
- package/dist/commands/core-handoff-fast.js +0 -104
- package/dist/commands/core-handoff-open-steps.d.ts +0 -21
- package/dist/commands/core-handoff-open-steps.js +0 -264
- package/dist/commands/core-handoff.d.ts +0 -24
- package/dist/commands/core-handoff.js +0 -1144
- package/dist/commands/core-hq-session-native.d.ts +0 -39
- package/dist/commands/core-hq-session-native.js +0 -579
- package/dist/commands/core-job-native-utils.d.ts +0 -6
- package/dist/commands/core-job-native-utils.js +0 -80
- package/dist/commands/core-job-native.d.ts +0 -5
- package/dist/commands/core-job-native.js +0 -16
- package/dist/commands/core-job-notify-native.d.ts +0 -36
- package/dist/commands/core-job-notify-native.js +0 -359
- package/dist/commands/core-job-probe-native.d.ts +0 -45
- package/dist/commands/core-job-probe-native.js +0 -564
- package/dist/commands/core-job-remediate-native.d.ts +0 -26
- package/dist/commands/core-job-remediate-native.js +0 -374
- package/dist/commands/core-job-remote-launcher.d.ts +0 -7
- package/dist/commands/core-job-remote-launcher.js +0 -51
- package/dist/commands/core-job-run-native.d.ts +0 -47
- package/dist/commands/core-job-run-native.js +0 -1215
- package/dist/commands/core-jobs-native.d.ts +0 -25
- package/dist/commands/core-jobs-native.js +0 -1339
- package/dist/commands/core-knowledge-prefs.d.ts +0 -12
- package/dist/commands/core-knowledge-prefs.js +0 -137
- package/dist/commands/core-native-handoff-flag.d.ts +0 -4
- package/dist/commands/core-native-handoff-flag.js +0 -7
- package/dist/commands/core-native-jobs-flag.d.ts +0 -4
- package/dist/commands/core-native-jobs-flag.js +0 -7
- package/dist/commands/core-native-policy-flag.d.ts +0 -9
- package/dist/commands/core-native-policy-flag.js +0 -12
- package/dist/commands/core-native-session-flag.d.ts +0 -4
- package/dist/commands/core-native-session-flag.js +0 -7
- package/dist/commands/core-native-utilities-flag.d.ts +0 -4
- package/dist/commands/core-native-utilities-flag.js +0 -7
- package/dist/commands/core-native-utilities.d.ts +0 -29
- package/dist/commands/core-native-utilities.js +0 -54
- package/dist/commands/core-native-worker-skills-flag.d.ts +0 -4
- package/dist/commands/core-native-worker-skills-flag.js +0 -7
- package/dist/commands/core-ontology-native.d.ts +0 -17
- package/dist/commands/core-ontology-native.js +0 -916
- package/dist/commands/core-ontology.d.ts +0 -66
- package/dist/commands/core-ontology.js +0 -430
- package/dist/commands/core-pin-native.d.ts +0 -14
- package/dist/commands/core-pin-native.js +0 -236
- package/dist/commands/core-policy-frontmatter.d.ts +0 -8
- package/dist/commands/core-policy-frontmatter.js +0 -52
- package/dist/commands/core-policy.d.ts +0 -14
- package/dist/commands/core-policy.js +0 -1018
- package/dist/commands/core-refresh-vault-access-native.d.ts +0 -20
- package/dist/commands/core-refresh-vault-access-native.js +0 -229
- package/dist/commands/core-resolve-company-command.d.ts +0 -4
- package/dist/commands/core-resolve-company-command.js +0 -42
- package/dist/commands/core-resolve-company-native.d.ts +0 -28
- package/dist/commands/core-resolve-company-native.js +0 -408
- package/dist/commands/core-work-mesh-bind-trusted-native.d.ts +0 -32
- package/dist/commands/core-work-mesh-bind-trusted-native.js +0 -190
- package/dist/commands/core-worker-lint-native.d.ts +0 -4
- package/dist/commands/core-worker-lint-native.js +0 -160
- package/dist/commands/core-worker-share-native.d.ts +0 -5
- package/dist/commands/core-worker-share-native.js +0 -260
- package/dist/commands/core-worker-skills-command.d.ts +0 -8
- package/dist/commands/core-worker-skills-command.js +0 -33
- package/dist/commands/core-worker-skills-runtime.d.ts +0 -33
- package/dist/commands/core-worker-skills-runtime.js +0 -86
- package/dist/commands/daemon.d.ts +0 -63
- package/dist/commands/daemon.js +0 -877
- package/dist/commands/deploy-comments.d.ts +0 -3
- package/dist/commands/deploy-comments.js +0 -162
- package/dist/commands/lanes-accounts.d.ts +0 -28
- package/dist/commands/lanes-accounts.js +0 -595
- package/dist/commands/lanes-autopilot.d.ts +0 -11
- package/dist/commands/lanes-autopilot.js +0 -336
- package/dist/commands/lanes-checks.d.ts +0 -51
- package/dist/commands/lanes-checks.js +0 -941
- package/dist/commands/lanes-hold.d.ts +0 -43
- package/dist/commands/lanes-hold.js +0 -655
- package/dist/commands/lanes-kpis.d.ts +0 -11
- package/dist/commands/lanes-kpis.js +0 -34
- package/dist/commands/lanes-message.d.ts +0 -9
- package/dist/commands/lanes-message.js +0 -934
- package/dist/commands/lanes-move.d.ts +0 -12
- package/dist/commands/lanes-move.js +0 -397
- package/dist/commands/lanes-questions.d.ts +0 -36
- package/dist/commands/lanes-questions.js +0 -1004
- package/dist/commands/lanes-report.d.ts +0 -11
- package/dist/commands/lanes-report.js +0 -333
- package/dist/commands/lanes-resume.d.ts +0 -6
- package/dist/commands/lanes-resume.js +0 -561
- package/dist/commands/lanes-review.d.ts +0 -10
- package/dist/commands/lanes-review.js +0 -56
- package/dist/commands/lanes-shared.d.ts +0 -448
- package/dist/commands/lanes-shared.js +0 -170
- package/dist/commands/lanes-stories.d.ts +0 -15
- package/dist/commands/lanes-stories.js +0 -347
- package/dist/commands/lanes-watch.d.ts +0 -7
- package/dist/commands/lanes-watch.js +0 -216
- package/dist/commands/lanes.d.ts +0 -149
- package/dist/commands/lanes.js +0 -8843
- package/dist/commands/monitor.d.ts +0 -58
- package/dist/commands/monitor.js +0 -980
- package/dist/commands/onboard-bot-membership.d.ts +0 -32
- package/dist/commands/onboard-bot-membership.js +0 -40
- package/dist/commands/onboard-create-identity.d.ts +0 -26
- package/dist/commands/onboard-create-identity.js +0 -23
- package/dist/commands/outpost-pruner.d.ts +0 -3
- package/dist/commands/outpost-pruner.js +0 -125
- package/dist/commands/personal-connector-settings-flag.d.ts +0 -4
- package/dist/commands/personal-connector-settings-flag.js +0 -7
- package/dist/commands/personal-targeted-push-flag.d.ts +0 -4
- package/dist/commands/personal-targeted-push-flag.js +0 -7
- package/dist/commands/personal-targeted-push-paths.d.ts +0 -3
- package/dist/commands/personal-targeted-push-paths.js +0 -67
- package/dist/commands/report.d.ts +0 -19
- package/dist/commands/report.js +0 -537
- package/dist/commands/run-feature-telemetry.d.ts +0 -3
- package/dist/commands/run-feature-telemetry.js +0 -6
- package/dist/commands/staff/reset-test-account.d.ts +0 -110
- package/dist/commands/staff/reset-test-account.js +0 -268
- package/dist/commands/staff/test-account.d.ts +0 -51
- package/dist/commands/staff/test-account.js +0 -196
- package/dist/commands/vault-retention-flag.d.ts +0 -3
- package/dist/commands/vault-retention-flag.js +0 -9
- package/dist/commands/vault-retention.d.ts +0 -11
- package/dist/commands/vault-retention.js +0 -69
- package/dist/commands/version.d.ts +0 -10
- package/dist/commands/version.js +0 -11
- package/dist/fast-scaffold.d.ts +0 -84
- package/dist/fast-scaffold.js +0 -353
- package/dist/lanes-drain-hook.d.ts +0 -14
- package/dist/lanes-drain-hook.js +0 -203
- package/dist/lib/agent-kit/adopt-identity.d.ts +0 -27
- package/dist/lib/agent-kit/adopt-identity.js +0 -60
- package/dist/lib/agent-kit/attachment-download.d.ts +0 -24
- package/dist/lib/agent-kit/attachment-download.js +0 -47
- package/dist/lib/agent-kit/creds.d.ts +0 -106
- package/dist/lib/agent-kit/creds.js +0 -163
- package/dist/lib/agent-kit/fallback.d.ts +0 -78
- package/dist/lib/agent-kit/fallback.js +0 -210
- package/dist/lib/agent-kit/inbox-state.d.ts +0 -83
- package/dist/lib/agent-kit/inbox-state.js +0 -159
- package/dist/lib/agent-kit/kit-config.d.ts +0 -56
- package/dist/lib/agent-kit/kit-config.js +0 -65
- package/dist/lib/agent-kit/log.d.ts +0 -16
- package/dist/lib/agent-kit/log.js +0 -46
- package/dist/lib/agent-kit/mcp/attachments.d.ts +0 -126
- package/dist/lib/agent-kit/mcp/attachments.js +0 -222
- package/dist/lib/agent-kit/mcp/jsonrpc.d.ts +0 -75
- package/dist/lib/agent-kit/mcp/jsonrpc.js +0 -164
- package/dist/lib/agent-kit/mcp/tools.d.ts +0 -64
- package/dist/lib/agent-kit/mcp/tools.js +0 -397
- package/dist/lib/agent-kit/paths.d.ts +0 -65
- package/dist/lib/agent-kit/paths.js +0 -118
- package/dist/lib/agent-kit/private-file.d.ts +0 -29
- package/dist/lib/agent-kit/private-file.js +0 -91
- package/dist/lib/agent-kit/run/heartbeat.d.ts +0 -72
- package/dist/lib/agent-kit/run/heartbeat.js +0 -128
- package/dist/lib/agent-kit/run/inbox.d.ts +0 -76
- package/dist/lib/agent-kit/run/inbox.js +0 -205
- package/dist/lib/agent-kit/run/supervisor.d.ts +0 -35
- package/dist/lib/agent-kit/run/supervisor.js +0 -85
- package/dist/lib/agent-kit/run/sync.d.ts +0 -33
- package/dist/lib/agent-kit/run/sync.js +0 -58
- package/dist/lib/agent-kit/services.d.ts +0 -31
- package/dist/lib/agent-kit/services.js +0 -55
- package/dist/lib/agent-kit/skills.d.ts +0 -18
- package/dist/lib/agent-kit/skills.js +0 -151
- package/dist/lib/agent-kit/wake.d.ts +0 -83
- package/dist/lib/agent-kit/wake.js +0 -243
- package/dist/lib/agents/model-catalog.d.ts +0 -62
- package/dist/lib/agents/model-catalog.js +0 -275
- package/dist/lib/auth-login-provider-state.d.ts +0 -8
- package/dist/lib/auth-login-provider-state.js +0 -63
- package/dist/lib/aws-sdk-node-warning-output.d.ts +0 -12
- package/dist/lib/aws-sdk-node-warning-output.js +0 -22
- package/dist/lib/billing/free-month.d.ts +0 -18
- package/dist/lib/billing/free-month.js +0 -31
- package/dist/lib/billing/plan-limit-refusals.d.ts +0 -69
- package/dist/lib/billing/plan-limit-refusals.js +0 -187
- package/dist/lib/billing/plan-limit-upgrade-url.d.ts +0 -11
- package/dist/lib/billing/plan-limit-upgrade-url.js +0 -66
- package/dist/lib/billing/plan-lock.d.ts +0 -143
- package/dist/lib/billing/plan-lock.js +0 -439
- package/dist/lib/bot/api.d.ts +0 -349
- package/dist/lib/bot/api.js +0 -316
- package/dist/lib/bot/attachments.d.ts +0 -45
- package/dist/lib/bot/attachments.js +0 -89
- package/dist/lib/bot/cli-update.d.ts +0 -138
- package/dist/lib/bot/cli-update.js +0 -278
- package/dist/lib/bot/company-bind.d.ts +0 -27
- package/dist/lib/bot/company-bind.js +0 -62
- package/dist/lib/bot/config.d.ts +0 -153
- package/dist/lib/bot/config.js +0 -233
- package/dist/lib/bot/continuity-download.d.ts +0 -28
- package/dist/lib/bot/continuity-download.js +0 -75
- package/dist/lib/bot/continuity-install.d.ts +0 -14
- package/dist/lib/bot/continuity-install.js +0 -101
- package/dist/lib/bot/continuity.d.ts +0 -67
- package/dist/lib/bot/continuity.js +0 -301
- package/dist/lib/bot/creds.d.ts +0 -24
- package/dist/lib/bot/creds.js +0 -51
- package/dist/lib/bot/daemon.d.ts +0 -173
- package/dist/lib/bot/daemon.js +0 -552
- package/dist/lib/bot/inbox-poll-backoff-flag.d.ts +0 -24
- package/dist/lib/bot/inbox-poll-backoff-flag.js +0 -135
- package/dist/lib/bot/inbox-state.d.ts +0 -18
- package/dist/lib/bot/inbox-state.js +0 -51
- package/dist/lib/bot/index.d.ts +0 -23
- package/dist/lib/bot/index.js +0 -23
- package/dist/lib/bot/inflight.d.ts +0 -119
- package/dist/lib/bot/inflight.js +0 -83
- package/dist/lib/bot/job-completions.d.ts +0 -89
- package/dist/lib/bot/job-completions.js +0 -132
- package/dist/lib/bot/job-run.d.ts +0 -109
- package/dist/lib/bot/job-run.js +0 -246
- package/dist/lib/bot/local-config.d.ts +0 -61
- package/dist/lib/bot/local-config.js +0 -145
- package/dist/lib/bot/local-jobs.d.ts +0 -89
- package/dist/lib/bot/local-jobs.js +0 -459
- package/dist/lib/bot/local-name.d.ts +0 -54
- package/dist/lib/bot/local-name.js +0 -114
- package/dist/lib/bot/log.d.ts +0 -13
- package/dist/lib/bot/log.js +0 -59
- package/dist/lib/bot/owner-context.d.ts +0 -79
- package/dist/lib/bot/owner-context.js +0 -175
- package/dist/lib/bot/paths.d.ts +0 -76
- package/dist/lib/bot/paths.js +0 -131
- package/dist/lib/bot/progress.d.ts +0 -132
- package/dist/lib/bot/progress.js +0 -289
- package/dist/lib/bot/promote.d.ts +0 -16
- package/dist/lib/bot/promote.js +0 -106
- package/dist/lib/bot/promotion-hold.d.ts +0 -24
- package/dist/lib/bot/promotion-hold.js +0 -103
- package/dist/lib/bot/promotion-receipt.d.ts +0 -9
- package/dist/lib/bot/promotion-receipt.js +0 -56
- package/dist/lib/bot/promotion-upload.d.ts +0 -16
- package/dist/lib/bot/promotion-upload.js +0 -80
- package/dist/lib/bot/prompt.d.ts +0 -136
- package/dist/lib/bot/prompt.js +0 -433
- package/dist/lib/bot/room-policy.d.ts +0 -63
- package/dist/lib/bot/room-policy.js +0 -91
- package/dist/lib/bot/run.d.ts +0 -217
- package/dist/lib/bot/run.js +0 -1869
- package/dist/lib/bot/runnable.d.ts +0 -51
- package/dist/lib/bot/runnable.js +0 -65
- package/dist/lib/bot/runtime/claude.d.ts +0 -50
- package/dist/lib/bot/runtime/claude.js +0 -170
- package/dist/lib/bot/runtime/codex.d.ts +0 -28
- package/dist/lib/bot/runtime/codex.js +0 -153
- package/dist/lib/bot/runtime/grok-acp.d.ts +0 -40
- package/dist/lib/bot/runtime/grok-acp.js +0 -265
- package/dist/lib/bot/runtime/grok.d.ts +0 -41
- package/dist/lib/bot/runtime/grok.js +0 -169
- package/dist/lib/bot/runtime/index.d.ts +0 -36
- package/dist/lib/bot/runtime/index.js +0 -417
- package/dist/lib/bot/runtime/messages-stream.d.ts +0 -56
- package/dist/lib/bot/runtime/messages-stream.js +0 -194
- package/dist/lib/bot/runtime/sanitize.d.ts +0 -18
- package/dist/lib/bot/runtime/sanitize.js +0 -28
- package/dist/lib/bot/runtime/types.d.ts +0 -159
- package/dist/lib/bot/runtime/types.js +0 -51
- package/dist/lib/bot/runtime/windows.d.ts +0 -10
- package/dist/lib/bot/runtime/windows.js +0 -53
- package/dist/lib/bot/runtime-sign-in.d.ts +0 -24
- package/dist/lib/bot/runtime-sign-in.js +0 -31
- package/dist/lib/bot/scaffold.d.ts +0 -38
- package/dist/lib/bot/scaffold.js +0 -94
- package/dist/lib/bot/self-heal.d.ts +0 -52
- package/dist/lib/bot/self-heal.js +0 -79
- package/dist/lib/bot/self-process.d.ts +0 -19
- package/dist/lib/bot/self-process.js +0 -35
- package/dist/lib/bot/session.d.ts +0 -19
- package/dist/lib/bot/session.js +0 -39
- package/dist/lib/bot/setup-explain.d.ts +0 -64
- package/dist/lib/bot/setup-explain.js +0 -141
- package/dist/lib/bot/split.d.ts +0 -32
- package/dist/lib/bot/split.js +0 -241
- package/dist/lib/bot/status.d.ts +0 -57
- package/dist/lib/bot/status.js +0 -66
- package/dist/lib/bot/worker-source.d.ts +0 -66
- package/dist/lib/bot/worker-source.js +0 -283
- package/dist/lib/checkpoint-ontology/aliases.d.ts +0 -17
- package/dist/lib/checkpoint-ontology/aliases.js +0 -44
- package/dist/lib/checkpoint-ontology/apply.d.ts +0 -110
- package/dist/lib/checkpoint-ontology/apply.js +0 -777
- package/dist/lib/checkpoint-ontology/company-brief.d.ts +0 -18
- package/dist/lib/checkpoint-ontology/company-brief.js +0 -337
- package/dist/lib/checkpoint-ontology/company.d.ts +0 -44
- package/dist/lib/checkpoint-ontology/company.js +0 -118
- package/dist/lib/checkpoint-ontology/drop-aliases.d.ts +0 -41
- package/dist/lib/checkpoint-ontology/drop-aliases.js +0 -76
- package/dist/lib/checkpoint-ontology/entity-file.d.ts +0 -109
- package/dist/lib/checkpoint-ontology/entity-file.js +0 -371
- package/dist/lib/checkpoint-ontology/identity.d.ts +0 -38
- package/dist/lib/checkpoint-ontology/identity.js +0 -95
- package/dist/lib/checkpoint-ontology/merge-copies.d.ts +0 -199
- package/dist/lib/checkpoint-ontology/merge-copies.js +0 -1086
- package/dist/lib/checkpoint-ontology/multi-writer.d.ts +0 -101
- package/dist/lib/checkpoint-ontology/multi-writer.js +0 -394
- package/dist/lib/checkpoint-ontology/plan.d.ts +0 -193
- package/dist/lib/checkpoint-ontology/plan.js +0 -504
- package/dist/lib/checkpoint-ontology/queued-touches.d.ts +0 -29
- package/dist/lib/checkpoint-ontology/queued-touches.js +0 -193
- package/dist/lib/checkpoint-ontology/sync-client.d.ts +0 -38
- package/dist/lib/checkpoint-ontology/sync-client.js +0 -84
- package/dist/lib/checkpoint-ontology/tree.d.ts +0 -55
- package/dist/lib/checkpoint-ontology/tree.js +0 -198
- package/dist/lib/company-errors-sdk-warning-flag.d.ts +0 -5
- package/dist/lib/company-errors-sdk-warning-flag.js +0 -8
- package/dist/lib/core-utils/hook-timeout-debug-context-flag.d.ts +0 -20
- package/dist/lib/core-utils/hook-timeout-debug-context-flag.js +0 -81
- package/dist/lib/core-utils/jq-json-serializer.d.ts +0 -5
- package/dist/lib/core-utils/jq-json-serializer.js +0 -10
- package/dist/lib/core-utils/secret-patterns.d.ts +0 -43
- package/dist/lib/core-utils/secret-patterns.js +0 -34
- package/dist/lib/core-utils/sentry-report.d.ts +0 -114
- package/dist/lib/core-utils/sentry-report.js +0 -1034
- package/dist/lib/core-utils/timeout-guard-native.d.ts +0 -7
- package/dist/lib/core-utils/timeout-guard-native.js +0 -40
- package/dist/lib/core-utils/worktree-resume.d.ts +0 -20
- package/dist/lib/core-utils/worktree-resume.js +0 -1198
- package/dist/lib/daemon/catalog.d.ts +0 -134
- package/dist/lib/daemon/catalog.js +0 -358
- package/dist/lib/daemon/config.d.ts +0 -35
- package/dist/lib/daemon/config.js +0 -108
- package/dist/lib/daemon/flag.d.ts +0 -25
- package/dist/lib/daemon/flag.js +0 -59
- package/dist/lib/daemon/handoff.d.ts +0 -56
- package/dist/lib/daemon/handoff.js +0 -161
- package/dist/lib/daemon/hq-cloud.d.ts +0 -79
- package/dist/lib/daemon/hq-cloud.js +0 -183
- package/dist/lib/daemon/install.d.ts +0 -85
- package/dist/lib/daemon/install.js +0 -320
- package/dist/lib/daemon/legacy-sync-env.d.ts +0 -10
- package/dist/lib/daemon/legacy-sync-env.js +0 -58
- package/dist/lib/daemon/legacy.d.ts +0 -45
- package/dist/lib/daemon/legacy.js +0 -133
- package/dist/lib/daemon/lock.d.ts +0 -47
- package/dist/lib/daemon/lock.js +0 -138
- package/dist/lib/daemon/machine-type.d.ts +0 -50
- package/dist/lib/daemon/machine-type.js +0 -159
- package/dist/lib/daemon/migrate.d.ts +0 -77
- package/dist/lib/daemon/migrate.js +0 -265
- package/dist/lib/daemon/paths.d.ts +0 -24
- package/dist/lib/daemon/paths.js +0 -43
- package/dist/lib/daemon/protocol-tail.d.ts +0 -33
- package/dist/lib/daemon/protocol-tail.js +0 -131
- package/dist/lib/daemon/remote-control.d.ts +0 -74
- package/dist/lib/daemon/remote-control.js +0 -153
- package/dist/lib/daemon/requests.d.ts +0 -28
- package/dist/lib/daemon/requests.js +0 -65
- package/dist/lib/daemon/run.d.ts +0 -79
- package/dist/lib/daemon/run.js +0 -755
- package/dist/lib/daemon/succession.d.ts +0 -118
- package/dist/lib/daemon/succession.js +0 -263
- package/dist/lib/daemon/supervisor.d.ts +0 -132
- package/dist/lib/daemon/supervisor.js +0 -836
- package/dist/lib/daemon/sync-runner.d.ts +0 -58
- package/dist/lib/daemon/sync-runner.js +0 -277
- package/dist/lib/daemon/tools.d.ts +0 -52
- package/dist/lib/daemon/tools.js +0 -95
- package/dist/lib/daemon/updater.d.ts +0 -57
- package/dist/lib/daemon/updater.js +0 -163
- package/dist/lib/deploy-api.d.ts +0 -5
- package/dist/lib/deploy-api.js +0 -21
- package/dist/lib/deploy-apps.d.ts +0 -13
- package/dist/lib/deploy-apps.js +0 -29
- package/dist/lib/doctor/__testing__/run-doctor.d.ts +0 -8
- package/dist/lib/doctor/__testing__/run-doctor.js +0 -52
- package/dist/lib/doctor/checks/bots.d.ts +0 -13
- package/dist/lib/doctor/checks/bots.js +0 -189
- package/dist/lib/doctor/checks/scaffold-cli-floor.d.ts +0 -6
- package/dist/lib/doctor/checks/scaffold-cli-floor.js +0 -73
- package/dist/lib/doctor/checks/sync-runtime.d.ts +0 -11
- package/dist/lib/doctor/checks/sync-runtime.js +0 -216
- package/dist/lib/doctor/client-health-flag-token.d.ts +0 -15
- package/dist/lib/doctor/client-health-flag-token.js +0 -66
- package/dist/lib/doctor/client-health-reason-codes-flag.d.ts +0 -27
- package/dist/lib/doctor/client-health-reason-codes-flag.js +0 -106
- package/dist/lib/doctor/fix/json-output.d.ts +0 -28
- package/dist/lib/doctor/fix/json-output.js +0 -33
- package/dist/lib/doctor/fix/refresh-sync-outcome.d.ts +0 -12
- package/dist/lib/doctor/fix/refresh-sync-outcome.js +0 -25
- package/dist/lib/doctor/shared.d.ts +0 -8
- package/dist/lib/doctor/shared.js +0 -38
- package/dist/lib/doctor/stray-gate-entries.d.ts +0 -35
- package/dist/lib/doctor/stray-gate-entries.js +0 -83
- package/dist/lib/first-sync-next-step.d.ts +0 -37
- package/dist/lib/first-sync-next-step.js +0 -189
- package/dist/lib/flag-registry-endpoint.d.ts +0 -5
- package/dist/lib/flag-registry-endpoint.js +0 -9
- package/dist/lib/flag-registry-worker-source.generated.d.ts +0 -2
- package/dist/lib/flag-registry-worker-source.generated.js +0 -3
- package/dist/lib/flag-snapshot-file.d.ts +0 -20
- package/dist/lib/flag-snapshot-file.js +0 -91
- package/dist/lib/heartbeat-identity.d.ts +0 -30
- package/dist/lib/heartbeat-identity.js +0 -76
- package/dist/lib/lanes/account-home.d.ts +0 -8
- package/dist/lib/lanes/account-home.js +0 -10
- package/dist/lib/lanes/accounts.d.ts +0 -117
- package/dist/lib/lanes/accounts.js +0 -366
- package/dist/lib/lanes/admission-host.d.ts +0 -22
- package/dist/lib/lanes/admission-host.js +0 -37
- package/dist/lib/lanes/admission.d.ts +0 -26
- package/dist/lib/lanes/admission.js +0 -210
- package/dist/lib/lanes/approval-gate.d.ts +0 -8
- package/dist/lib/lanes/approval-gate.js +0 -90
- package/dist/lib/lanes/autopilot-metrics.d.ts +0 -99
- package/dist/lib/lanes/autopilot-metrics.js +0 -301
- package/dist/lib/lanes/autopilot-registry.d.ts +0 -62
- package/dist/lib/lanes/autopilot-registry.js +0 -192
- package/dist/lib/lanes/autopilot.d.ts +0 -120
- package/dist/lib/lanes/autopilot.js +0 -1061
- package/dist/lib/lanes/blocker-freshness.d.ts +0 -31
- package/dist/lib/lanes/blocker-freshness.js +0 -168
- package/dist/lib/lanes/checked-company-path.d.ts +0 -3
- package/dist/lib/lanes/checked-company-path.js +0 -39
- package/dist/lib/lanes/claude-balance.d.ts +0 -97
- package/dist/lib/lanes/claude-balance.js +0 -319
- package/dist/lib/lanes/compaction.d.ts +0 -110
- package/dist/lib/lanes/compaction.js +0 -293
- package/dist/lib/lanes/config.d.ts +0 -91
- package/dist/lib/lanes/config.js +0 -592
- package/dist/lib/lanes/constants.d.ts +0 -3
- package/dist/lib/lanes/constants.js +0 -3
- package/dist/lib/lanes/delivery-core.d.ts +0 -15
- package/dist/lib/lanes/delivery-core.js +0 -21
- package/dist/lib/lanes/delivery.d.ts +0 -180
- package/dist/lib/lanes/delivery.js +0 -443
- package/dist/lib/lanes/dropbox.d.ts +0 -129
- package/dist/lib/lanes/dropbox.js +0 -601
- package/dist/lib/lanes/effort.d.ts +0 -51
- package/dist/lib/lanes/effort.js +0 -113
- package/dist/lib/lanes/envelope-repair-dispatch.d.ts +0 -26
- package/dist/lib/lanes/envelope-repair-dispatch.js +0 -74
- package/dist/lib/lanes/errors.d.ts +0 -39
- package/dist/lib/lanes/errors.js +0 -72
- package/dist/lib/lanes/flag-keys.d.ts +0 -13
- package/dist/lib/lanes/flag-keys.js +0 -13
- package/dist/lib/lanes/grants.d.ts +0 -29
- package/dist/lib/lanes/grants.js +0 -166
- package/dist/lib/lanes/hire-grant-dispatch.d.ts +0 -29
- package/dist/lib/lanes/hire-grant-dispatch.js +0 -316
- package/dist/lib/lanes/hooks-install.d.ts +0 -82
- package/dist/lib/lanes/hooks-install.js +0 -479
- package/dist/lib/lanes/host-capacity.d.ts +0 -69
- package/dist/lib/lanes/host-capacity.js +0 -254
- package/dist/lib/lanes/hq-root.d.ts +0 -19
- package/dist/lib/lanes/hq-root.js +0 -28
- package/dist/lib/lanes/index.d.ts +0 -76
- package/dist/lib/lanes/index.js +0 -42
- package/dist/lib/lanes/keep-alive.d.ts +0 -68
- package/dist/lib/lanes/keep-alive.js +0 -343
- package/dist/lib/lanes/kr-freshness.d.ts +0 -76
- package/dist/lib/lanes/kr-freshness.js +0 -413
- package/dist/lib/lanes/lane-company.d.ts +0 -10
- package/dist/lib/lanes/lane-company.js +0 -41
- package/dist/lib/lanes/liveness.d.ts +0 -108
- package/dist/lib/lanes/liveness.js +0 -396
- package/dist/lib/lanes/machine-identity.d.ts +0 -34
- package/dist/lib/lanes/machine-identity.js +0 -92
- package/dist/lib/lanes/manager-status-gate.d.ts +0 -17
- package/dist/lib/lanes/manager-status-gate.js +0 -133
- package/dist/lib/lanes/merge-command.d.ts +0 -15
- package/dist/lib/lanes/merge-command.js +0 -629
- package/dist/lib/lanes/mesh-events.d.ts +0 -127
- package/dist/lib/lanes/mesh-events.js +0 -564
- package/dist/lib/lanes/message-replay.d.ts +0 -21
- package/dist/lib/lanes/message-replay.js +0 -122
- package/dist/lib/lanes/models.d.ts +0 -86
- package/dist/lib/lanes/models.js +0 -234
- package/dist/lib/lanes/monitor.d.ts +0 -48
- package/dist/lib/lanes/monitor.js +0 -428
- package/dist/lib/lanes/move-account.d.ts +0 -56
- package/dist/lib/lanes/move-account.js +0 -193
- package/dist/lib/lanes/orphans.d.ts +0 -25
- package/dist/lib/lanes/orphans.js +0 -102
- package/dist/lib/lanes/outpost-dispatch.d.ts +0 -147
- package/dist/lib/lanes/outpost-dispatch.js +0 -271
- package/dist/lib/lanes/outpost-hire-cache.d.ts +0 -22
- package/dist/lib/lanes/outpost-hire-cache.js +0 -153
- package/dist/lib/lanes/outpost-identity.d.ts +0 -33
- package/dist/lib/lanes/outpost-identity.js +0 -59
- package/dist/lib/lanes/outpost-profile-repin.d.ts +0 -17
- package/dist/lib/lanes/outpost-profile-repin.js +0 -84
- package/dist/lib/lanes/outpost-pruner-install.d.ts +0 -37
- package/dist/lib/lanes/outpost-pruner-install.js +0 -238
- package/dist/lib/lanes/outpost-registry-worker-approval.d.ts +0 -18
- package/dist/lib/lanes/outpost-registry-worker-approval.js +0 -109
- package/dist/lib/lanes/outpost-snapshot-memo.d.ts +0 -6
- package/dist/lib/lanes/outpost-snapshot-memo.js +0 -14
- package/dist/lib/lanes/outpost-worktree-pruner-flag.d.ts +0 -19
- package/dist/lib/lanes/outpost-worktree-pruner-flag.js +0 -86
- package/dist/lib/lanes/outpost-worktree-pruner.d.ts +0 -62
- package/dist/lib/lanes/outpost-worktree-pruner.js +0 -1201
- package/dist/lib/lanes/profile.d.ts +0 -69
- package/dist/lib/lanes/profile.js +0 -552
- package/dist/lib/lanes/provider-rejection.d.ts +0 -86
- package/dist/lib/lanes/provider-rejection.js +0 -286
- package/dist/lib/lanes/questions.d.ts +0 -90
- package/dist/lib/lanes/questions.js +0 -384
- package/dist/lib/lanes/quota.d.ts +0 -134
- package/dist/lib/lanes/quota.js +0 -471
- package/dist/lib/lanes/reconcile-install.d.ts +0 -90
- package/dist/lib/lanes/reconcile-install.js +0 -257
- package/dist/lib/lanes/reconcile.d.ts +0 -155
- package/dist/lib/lanes/reconcile.js +0 -3193
- package/dist/lib/lanes/registry-flag.d.ts +0 -29
- package/dist/lib/lanes/registry-flag.js +0 -139
- package/dist/lib/lanes/registry-persona.d.ts +0 -20
- package/dist/lib/lanes/registry-persona.js +0 -329
- package/dist/lib/lanes/registry.d.ts +0 -122
- package/dist/lib/lanes/registry.js +0 -570
- package/dist/lib/lanes/reviews.d.ts +0 -38
- package/dist/lib/lanes/reviews.js +0 -395
- package/dist/lib/lanes/runaway-tool-guard.d.ts +0 -38
- package/dist/lib/lanes/runaway-tool-guard.js +0 -284
- package/dist/lib/lanes/session-relink.d.ts +0 -25
- package/dist/lib/lanes/session-relink.js +0 -216
- package/dist/lib/lanes/spawn.d.ts +0 -350
- package/dist/lib/lanes/spawn.js +0 -2643
- package/dist/lib/lanes/ssm-command-context.d.ts +0 -12
- package/dist/lib/lanes/ssm-command-context.js +0 -52
- package/dist/lib/lanes/store.d.ts +0 -91
- package/dist/lib/lanes/store.js +0 -1811
- package/dist/lib/lanes/stories.d.ts +0 -23
- package/dist/lib/lanes/stories.js +0 -92
- package/dist/lib/lanes/story-ledger.d.ts +0 -80
- package/dist/lib/lanes/story-ledger.js +0 -815
- package/dist/lib/lanes/story-switch.d.ts +0 -21
- package/dist/lib/lanes/story-switch.js +0 -75
- package/dist/lib/lanes/text-scan.d.ts +0 -11
- package/dist/lib/lanes/text-scan.js +0 -103
- package/dist/lib/lanes/throttle.d.ts +0 -116
- package/dist/lib/lanes/throttle.js +0 -437
- package/dist/lib/lanes/timestamp.d.ts +0 -3
- package/dist/lib/lanes/timestamp.js +0 -26
- package/dist/lib/lanes/tracker-checks.d.ts +0 -77
- package/dist/lib/lanes/tracker-checks.js +0 -134
- package/dist/lib/lanes/tracker-report-state.d.ts +0 -10
- package/dist/lib/lanes/tracker-report-state.js +0 -124
- package/dist/lib/lanes/tracker-report.d.ts +0 -203
- package/dist/lib/lanes/tracker-report.js +0 -522
- package/dist/lib/lanes/tracker-store.d.ts +0 -18
- package/dist/lib/lanes/tracker-store.js +0 -106
- package/dist/lib/lanes/types.d.ts +0 -1020
- package/dist/lib/lanes/types.js +0 -417
- package/dist/lib/lanes/wait.d.ts +0 -39
- package/dist/lib/lanes/wait.js +0 -159
- package/dist/lib/lanes/watch.d.ts +0 -39
- package/dist/lib/lanes/watch.js +0 -249
- package/dist/lib/lanes/watcher-main.d.ts +0 -11
- package/dist/lib/lanes/watcher-main.js +0 -108
- package/dist/lib/lanes/watcher.d.ts +0 -287
- package/dist/lib/lanes/watcher.js +0 -1972
- package/dist/lib/mesh/live/daemon/config.d.ts +0 -36
- package/dist/lib/mesh/live/daemon/config.js +0 -120
- package/dist/lib/mesh/live/daemon/legacy-listen.d.ts +0 -43
- package/dist/lib/mesh/live/daemon/legacy-listen.js +0 -89
- package/dist/lib/mesh/live/daemon/live-cache.d.ts +0 -26
- package/dist/lib/mesh/live/daemon/live-cache.js +0 -116
- package/dist/lib/mesh/live/daemon/mqtt-events.d.ts +0 -7
- package/dist/lib/mesh/live/daemon/mqtt-events.js +0 -2
- package/dist/lib/mesh/live/daemon/mqtt-events.test-d.d.ts +0 -2
- package/dist/lib/mesh/live/daemon/mqtt-events.test-d.js +0 -10
- package/dist/lib/mesh/live/daemon/version-drift.d.ts +0 -5
- package/dist/lib/mesh/live/daemon/version-drift.js +0 -47
- package/dist/lib/mesh/live/daemon/work-subscribe.d.ts +0 -32
- package/dist/lib/mesh/live/daemon/work-subscribe.js +0 -262
- package/dist/lib/mesh/live/report-summary.d.ts +0 -45
- package/dist/lib/mesh/live/report-summary.js +0 -234
- package/dist/lib/mesh/live/session-seq.d.ts +0 -4
- package/dist/lib/mesh/live/session-seq.js +0 -33
- package/dist/lib/mesh/project-board.d.ts +0 -65
- package/dist/lib/mesh/project-board.js +0 -181
- package/dist/lib/mesh/story-status.d.ts +0 -22
- package/dist/lib/mesh/story-status.js +0 -49
- package/dist/lib/monitor.d.ts +0 -149
- package/dist/lib/monitor.js +0 -519
- package/dist/lib/node-binary.d.ts +0 -50
- package/dist/lib/node-binary.js +0 -133
- package/dist/lib/notify-recipient.d.ts +0 -7
- package/dist/lib/notify-recipient.js +0 -11
- package/dist/lib/onboarding/checkpoint.d.ts +0 -13
- package/dist/lib/onboarding/checkpoint.js +0 -44
- package/dist/lib/onboarding/cli/onboard.d.ts +0 -53
- package/dist/lib/onboarding/cli/onboard.js +0 -135
- package/dist/lib/onboarding/cli/prompts.d.ts +0 -28
- package/dist/lib/onboarding/cli/prompts.js +0 -83
- package/dist/lib/onboarding/errors.d.ts +0 -34
- package/dist/lib/onboarding/errors.js +0 -59
- package/dist/lib/onboarding/index.d.ts +0 -13
- package/dist/lib/onboarding/index.js +0 -13
- package/dist/lib/onboarding/orchestrator.d.ts +0 -47
- package/dist/lib/onboarding/orchestrator.js +0 -569
- package/dist/lib/onboarding/types.d.ts +0 -79
- package/dist/lib/onboarding/types.js +0 -8
- package/dist/lib/policy-validator.d.ts +0 -7
- package/dist/lib/policy-validator.js +0 -187
- package/dist/lib/process-flag-company-context.d.ts +0 -8
- package/dist/lib/process-flag-company-context.js +0 -67
- package/dist/lib/report-publish.d.ts +0 -175
- package/dist/lib/report-publish.js +0 -421
- package/dist/lib/report-run.d.ts +0 -62
- package/dist/lib/report-run.js +0 -123
- package/dist/lib/report-snapshot.d.ts +0 -12
- package/dist/lib/report-snapshot.js +0 -135
- package/dist/lib/report-templates.d.ts +0 -9
- package/dist/lib/report-templates.js +0 -21
- package/dist/lib/scaffold-cli-floor.d.ts +0 -8
- package/dist/lib/scaffold-cli-floor.js +0 -50
- package/dist/lib/search-index/collection-path-repair-flag.d.ts +0 -4
- package/dist/lib/search-index/collection-path-repair-flag.js +0 -7
- package/dist/lib/search-index/embed-lock.d.ts +0 -32
- package/dist/lib/search-index/embed-lock.js +0 -286
- package/dist/lib/search-index/load-gate.d.ts +0 -57
- package/dist/lib/search-index/load-gate.js +0 -270
- package/dist/lib/search-index/max-doc-bytes.d.ts +0 -219
- package/dist/lib/search-index/max-doc-bytes.js +0 -463
- package/dist/lib/search-index/qmd-exit-diagnostics.d.ts +0 -5
- package/dist/lib/search-index/qmd-exit-diagnostics.js +0 -40
- package/dist/lib/search-index/reindex-single-flight-flag.d.ts +0 -5
- package/dist/lib/search-index/reindex-single-flight-flag.js +0 -8
- package/dist/lib/service-manager/index.d.ts +0 -42
- package/dist/lib/service-manager/index.js +0 -105
- package/dist/lib/service-manager/launchd.d.ts +0 -81
- package/dist/lib/service-manager/launchd.js +0 -212
- package/dist/lib/service-manager/systemd.d.ts +0 -19
- package/dist/lib/service-manager/systemd.js +0 -72
- package/dist/lib/service-manager/types.d.ts +0 -34
- package/dist/lib/service-manager/types.js +0 -26
- package/dist/lib/test-utils/isolated-flag-company-context.d.ts +0 -3
- package/dist/lib/test-utils/isolated-flag-company-context.js +0 -18
- package/dist/lib/work-context/bind-best.d.ts +0 -66
- package/dist/lib/work-context/bind-best.js +0 -344
- package/dist/lib/work-context/bind-calling-session.d.ts +0 -26
- package/dist/lib/work-context/bind-calling-session.js +0 -63
- package/dist/lib/work-context/company-rebind.d.ts +0 -23
- package/dist/lib/work-context/company-rebind.js +0 -59
- package/dist/lib/work-context/default-company.d.ts +0 -43
- package/dist/lib/work-context/default-company.js +0 -83
- package/dist/lib/work-context/outbox-quarantine-status-flag.d.ts +0 -4
- package/dist/lib/work-context/outbox-quarantine-status-flag.js +0 -7
- package/dist/lib/work-context/prd-sync.d.ts +0 -56
- package/dist/lib/work-context/prd-sync.js +0 -197
- package/dist/lib/work-context/preflight-contract.d.ts +0 -28
- package/dist/lib/work-context/preflight-contract.js +0 -85
- package/dist/lib/work-context/prompt-rebind.d.ts +0 -25
- package/dist/lib/work-context/prompt-rebind.js +0 -85
- package/dist/lib/work-context/windows-acl.d.ts +0 -123
- package/dist/lib/work-context/windows-acl.js +0 -425
- package/dist/lib/workers-registry/read.d.ts +0 -15
- package/dist/lib/workers-registry/read.js +0 -17
- package/dist/native-fast-route.d.ts +0 -7
- package/dist/native-fast-route.js +0 -45
- package/dist/node-network-compat-core.d.ts +0 -12
- package/dist/node-network-compat-core.js +0 -41
- package/dist/node-network-compat-fast.d.ts +0 -7
- package/dist/node-network-compat-fast.js +0 -23
- package/dist/secrets-env-fast-path.d.ts +0 -5
- package/dist/secrets-env-fast-path.js +0 -16
- package/dist/secrets-env-fast-route.d.ts +0 -18
- package/dist/secrets-env-fast-route.js +0 -54
- package/dist/utils/checkpoint-stamp-write-error.d.ts +0 -28
- package/dist/utils/checkpoint-stamp-write-error.js +0 -99
- package/dist/utils/cli-network-lifecycle.d.ts +0 -4
- package/dist/utils/cli-network-lifecycle.js +0 -11
- package/dist/utils/cognito-refresh-sentry.d.ts +0 -3
- package/dist/utils/cognito-refresh-sentry.js +0 -49
- package/dist/utils/console-origin.d.ts +0 -9
- package/dist/utils/console-origin.js +0 -28
- package/dist/utils/exclusive-lock.d.ts +0 -32
- package/dist/utils/exclusive-lock.js +0 -75
- package/dist/utils/fast-stream-errors.d.ts +0 -12
- package/dist/utils/fast-stream-errors.js +0 -18
- package/dist/utils/files-browse-acl.d.ts +0 -5
- package/dist/utils/files-browse-acl.js +0 -11
- package/dist/utils/hq-core-yaml.d.ts +0 -10
- package/dist/utils/hq-core-yaml.js +0 -40
- package/dist/utils/hq-root-resolver.d.ts +0 -13
- package/dist/utils/hq-root-resolver.js +0 -103
- package/dist/utils/redact-url.d.ts +0 -14
- package/dist/utils/redact-url.js +0 -218
- package/dist/utils/scaffold-fast-import-guard.d.ts +0 -2
- package/dist/utils/scaffold-fast-import-guard.js +0 -40
- package/dist/utils/scaffold-packlist-guard.d.ts +0 -7
- package/dist/utils/scaffold-packlist-guard.js +0 -23
- package/dist/utils/scaffold-sibling-references.d.ts +0 -22
- package/dist/utils/scaffold-sibling-references.js +0 -156
- package/dist/utils/secret-load-availability.d.ts +0 -8
- package/dist/utils/secret-load-availability.js +0 -54
- package/dist/utils/staged-install.d.ts +0 -34
- package/dist/utils/staged-install.js +0 -346
- package/dist/utils/staged-npm-install.d.ts +0 -16
- package/dist/utils/staged-npm-install.js +0 -264
- package/dist/utils/sync-log-sources.d.ts +0 -15
- package/dist/utils/sync-log-sources.js +0 -36
- package/dist/utils/table-width.d.ts +0 -7
- package/dist/utils/table-width.js +0 -15
- package/dist/utils/update-command-supervisor.cjs +0 -247
- package/dist/utils/upload-author.d.ts +0 -15
- package/dist/utils/upload-author.js +0 -39
- package/dist/utils/version-report.d.ts +0 -27
- package/dist/utils/version-report.js +0 -105
- package/dist/version-request.d.ts +0 -14
- package/dist/version-request.js +0 -36
- package/scripts/mesh-daemon-if-stale.mjs +0 -20
- package/templates/reports/blank.md +0 -3
- package/templates/reports/daily-digest.md +0 -11
- package/templates/runbooks/update-report.md +0 -21
|
@@ -1,1972 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-lane watcher (spec §5.7).
|
|
3
|
-
*
|
|
4
|
-
* Armed at spawn, detached from the caller. Emits on progress, terminal, and
|
|
5
|
-
* silence (20 minutes with the process still alive). Terminal classification
|
|
6
|
-
* follows spec §5: an envelope decides done/ask/blocked; a process that exits
|
|
7
|
-
* with partial output and no envelope is `interrupted`; silence is never
|
|
8
|
-
* success.
|
|
9
|
-
*
|
|
10
|
-
* Progress has two sources. Output files (worker.out, worker.err,
|
|
11
|
-
* last-message.txt) cover codex and grok, which stream as they work. They do
|
|
12
|
-
* not cover Claude: `claude -p --output-format json` writes nothing until it
|
|
13
|
-
* exits, so a healthy Claude lane used to hit the silence deadline at exactly
|
|
14
|
-
* 20 minutes every time, and a hung one looked identical. The second source
|
|
15
|
-
* is logs/tool-activity.json, which the drain hook rewrites on every
|
|
16
|
-
* PreToolUse call (dropbox.ts). A worker that is working calls tools; a
|
|
17
|
-
* worker that has not called a tool or written output for the whole window
|
|
18
|
-
* is `stalled`, and the watcher says so on the lane record.
|
|
19
|
-
*
|
|
20
|
-
* What this cannot tell apart: a worker hung inside one tool call from a
|
|
21
|
-
* worker running one legitimately long tool call (a 25-minute build); a
|
|
22
|
-
* worker looping on tool calls without making progress from a working one;
|
|
23
|
-
* a model stuck mid-generation with no tool call from one that is writing a
|
|
24
|
-
* long answer. It also depends on the drain hook being registered in the
|
|
25
|
-
* provider home (hooks-install.ts): with no hook there is no touch, and the
|
|
26
|
-
* lane reads as stalled every window, which is the pre-existing behaviour.
|
|
27
|
-
* Distinguishing a hung tool from a slow one needs the provider to report
|
|
28
|
-
* per-call state, which none of the three CLIs expose today. `stalled` is
|
|
29
|
-
* therefore a flag for the senior to look at, never a kill.
|
|
30
|
-
*
|
|
31
|
-
* Queue admission of the next lane when a slot frees is owned by the
|
|
32
|
-
* periodic reconcile pass and is out of scope here.
|
|
33
|
-
*/
|
|
34
|
-
import * as fs from "node:fs";
|
|
35
|
-
import * as path from "node:path";
|
|
36
|
-
import { LanesError } from "./errors.js";
|
|
37
|
-
import { questionRecordRefusalCode, raiseEnvelopeQuestions, resolveQuestionCompanyWithFallback, } from "./questions.js";
|
|
38
|
-
import { isTerminalLaneState, ENVELOPE_INVALID_REASON, parseLaneEnvelope, STORY_STATUSES, LANE_PROVIDERS, } from "./types.js";
|
|
39
|
-
import { laneStories, terminalEnvelopeStoryStatus } from "./stories.js";
|
|
40
|
-
import { applyStorySwitchEnvelope } from "./story-switch.js";
|
|
41
|
-
import { appendCappedHistory, listQuestionIds, readLane, readQuestion, retryUpdateLane, withLaneHireLocks } from "./store.js";
|
|
42
|
-
import { enforceRegistryApprovalGate } from "./approval-gate.js";
|
|
43
|
-
import { readWorkerRunResult } from "./delivery.js";
|
|
44
|
-
import { applyDefaultQuotaHold, clearAccountQuotaAfterSuccessfulTurnBestEffort, quotaSignalFromFields, readQuotaSignal, recordAccountQuota, } from "./quota.js";
|
|
45
|
-
import { PROVIDER_REJECTED_REASON, QUOTA_STATUS, isProviderLoginFailure, providerRejectionDetails, readProviderRejection, } from "./provider-rejection.js";
|
|
46
|
-
import { appendDropboxMessageAtPath, dropboxInboxHasMessages, readToolActivity, toolActivityPath } from "./dropbox.js";
|
|
47
|
-
import { loadLanesConfig, providerProgressEventMinIntervalMs, providerQuotaDefaultHoldMs } from "./config.js";
|
|
48
|
-
import { resolveAdmissionHost as resolveProcessAdmissionHost } from "./admission-host.js";
|
|
49
|
-
import { createLaneMesh, emitLaneDecisionEvents, emitLaneProgressEvent, emitLaneTerminalStateEvent, laneStoryStatusSuppressionKey, progressEventDue, } from "./mesh-events.js";
|
|
50
|
-
import { envelopeQuestionId } from "./questions.js";
|
|
51
|
-
import { readLanesRegistryFlag } from "./registry-flag.js";
|
|
52
|
-
import { laneCompanyUidSource, resolveLaneCompany } from "./lane-company.js";
|
|
53
|
-
import { blockerFreshnessFingerprint, resolveBlockerFreshnessForLane, } from "./blocker-freshness.js";
|
|
54
|
-
import { krFreshnessFingerprint, resolveKrFreshnessForLane, } from "./kr-freshness.js";
|
|
55
|
-
import { managerStatusWasWritten, readStatusSnapshot, resolveManagerStatusTarget, } from "./manager-status-gate.js";
|
|
56
|
-
import { LEAD_LANE_KEEP_ALIVE_FIRST_RESUME_SETTLE_MS, LEAD_LANE_KEEP_ALIVE_FLAG, LEAD_LANE_KEEP_ALIVE_IMMEDIATE_FIRST_RESUME_FLAG, LEAD_LANE_KEEP_ALIVE_TERMINAL_TRIGGER_FLAG, queueLaneTerminalResume, queueManagerStatusGateResume, } from "./keep-alive.js";
|
|
57
|
-
import { LANE_ENVELOPE_REPAIR_CONTINUE_FLAG } from "./flag-keys.js";
|
|
58
|
-
import { monitorSessionDir } from "../monitor.js";
|
|
59
|
-
import { laneRunDir, missingPidProbe, probePidRecord, readPidRecord, readProcStarttime, readSpawnManifest, envelopeRepairMode, spawnEnvelopeRepair, writePidRecord, retryLaneOnAlternateAccount, } from "./spawn.js";
|
|
60
|
-
/** Spec §5.7: no signal for 20 minutes while the process is still alive. */
|
|
61
|
-
export const SILENCE_MS = 20 * 60 * 1000;
|
|
62
|
-
export const WATCHER_POLL_MS = 5_000;
|
|
63
|
-
export const ENVELOPE_REPAIR_CONTINUE_TEXT = "continue the task; the previous turn ended without an envelope.";
|
|
64
|
-
/** Synced last_host_report_at cadence. Reconcile dead-host TTL is 20 minutes. */
|
|
65
|
-
export const WATCHER_HEARTBEAT_MS = 60 * 1000;
|
|
66
|
-
/**
|
|
67
|
-
* Cap on logs/watcher.out. A decision line is typically under 300 bytes, so
|
|
68
|
-
* 256 KiB holds hundreds of transitions. A stuck lane cannot fill the disk:
|
|
69
|
-
* once the file would exceed this, the oldest complete lines are dropped.
|
|
70
|
-
*/
|
|
71
|
-
export const WATCHER_OUT_MAX_BYTES = 256 * 1024;
|
|
72
|
-
export const WATCHER_DECISION_EVENTS = [
|
|
73
|
-
"liveness_transition",
|
|
74
|
-
"silence_deadline",
|
|
75
|
-
"envelope_observed",
|
|
76
|
-
"envelope_invalid",
|
|
77
|
-
"envelope_repair",
|
|
78
|
-
"envelope_repair_continue",
|
|
79
|
-
"classification",
|
|
80
|
-
"terminal_write",
|
|
81
|
-
"message_recovery",
|
|
82
|
-
"message_recovery_failed",
|
|
83
|
-
"message_undeliverable",
|
|
84
|
-
"mesh_event",
|
|
85
|
-
"mesh_event_failed",
|
|
86
|
-
];
|
|
87
|
-
/** Terminal reason when the envelope failed to parse after the repair turn (spec §4). */
|
|
88
|
-
export { ENVELOPE_INVALID_REASON };
|
|
89
|
-
export const WATCHER_SIGNAL_KINDS = ["progress", "terminal", "silence"];
|
|
90
|
-
export function parseWatcherSignalKind(value) {
|
|
91
|
-
if (typeof value === "string" &&
|
|
92
|
-
WATCHER_SIGNAL_KINDS.includes(value)) {
|
|
93
|
-
return value;
|
|
94
|
-
}
|
|
95
|
-
throw new LanesError("unknown_watcher_signal", `Unknown watcher signal ${JSON.stringify(value)}. Accepted: ${WATCHER_SIGNAL_KINDS.join(", ")}`, { accepted: [...WATCHER_SIGNAL_KINDS], value });
|
|
96
|
-
}
|
|
97
|
-
export function classifyLaneExit(input) {
|
|
98
|
-
if (input.envelope) {
|
|
99
|
-
if (input.envelope.decision === "done")
|
|
100
|
-
return { state: "done" };
|
|
101
|
-
if (input.envelope.decision === "ask")
|
|
102
|
-
return { state: "awaiting_input" };
|
|
103
|
-
if (input.envelope.decision === "blocked")
|
|
104
|
-
return { state: "blocked" };
|
|
105
|
-
throw new LanesError("unknown_envelope_decision", `Unknown envelope decision ${JSON.stringify(input.envelope.decision)}. Accepted: done, ask, blocked`, { accepted: ["done", "ask", "blocked"], value: input.envelope.decision });
|
|
106
|
-
}
|
|
107
|
-
if (input.providerRejection) {
|
|
108
|
-
return {
|
|
109
|
-
state: "failed",
|
|
110
|
-
reason: isProviderLoginFailure(input.providerRejection)
|
|
111
|
-
? "account_not_logged_in"
|
|
112
|
-
: PROVIDER_REJECTED_REASON,
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
if (input.invalidEnvelope) {
|
|
116
|
-
return { state: "failed", reason: ENVELOPE_INVALID_REASON };
|
|
117
|
-
}
|
|
118
|
-
if (input.quota) {
|
|
119
|
-
return {
|
|
120
|
-
state: input.hasPartialOutput ? "interrupted" : "failed",
|
|
121
|
-
reason: input.quota.reason,
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
if (input.hasPartialOutput) {
|
|
125
|
-
return { state: "interrupted", reason: "exited_with_partial_output" };
|
|
126
|
-
}
|
|
127
|
-
return { state: "failed", reason: "exited_without_envelope" };
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* One decision for an exited worker's output. An explicit provider status
|
|
131
|
-
* wins over the quota text heuristics: a 429 from any provider (including
|
|
132
|
-
* codex `turn.failed` and grok error objects, which the claude-shaped quota
|
|
133
|
-
* reader never sees) becomes a quota signal; any other explicit status is a
|
|
134
|
-
* rejection even when its text sounds like a quota message (a 402 "requires
|
|
135
|
-
* usage credits" is a rejection, not a hold). A failure with no status at
|
|
136
|
-
* all is matched against the quota text heuristics first: a status-less
|
|
137
|
-
* "usage limit" / "rate limit" turn.failed still creates the account hold;
|
|
138
|
-
* any other status-less message is a rejection. Only when the stream
|
|
139
|
-
* carries no failure object does the quota reader's own read apply.
|
|
140
|
-
*/
|
|
141
|
-
export function routeProviderFailure(failure, readQuota, model, now) {
|
|
142
|
-
if (failure && failure.status === QUOTA_STATUS) {
|
|
143
|
-
return {
|
|
144
|
-
quota: quotaSignalFromFields({ api_error_status: QUOTA_STATUS, is_error: true, result: failure.message }, model, now),
|
|
145
|
-
rejection: null,
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
if (failure && failure.status === null) {
|
|
149
|
-
const quota = quotaSignalFromFields({ api_error_status: undefined, is_error: true, result: failure.message }, model, now);
|
|
150
|
-
if (quota)
|
|
151
|
-
return { quota, rejection: null };
|
|
152
|
-
}
|
|
153
|
-
if (failure)
|
|
154
|
-
return { quota: null, rejection: failure };
|
|
155
|
-
return { quota: readQuota(), rejection: null };
|
|
156
|
-
}
|
|
157
|
-
export function tickWatcher(input) {
|
|
158
|
-
const silenceMs = input.silenceMs ?? SILENCE_MS;
|
|
159
|
-
const at = new Date(input.nowMs).toISOString();
|
|
160
|
-
if (!input.processAlive) {
|
|
161
|
-
const classification = classifyLaneExit({
|
|
162
|
-
envelope: input.envelope,
|
|
163
|
-
hasPartialOutput: input.hasPartialOutput,
|
|
164
|
-
quota: input.quota,
|
|
165
|
-
invalidEnvelope: input.invalidEnvelope,
|
|
166
|
-
providerRejection: input.providerRejection,
|
|
167
|
-
});
|
|
168
|
-
return {
|
|
169
|
-
emit: { kind: "terminal", at, classification },
|
|
170
|
-
nextLastSignalMs: input.nowMs,
|
|
171
|
-
done: true,
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
if (input.progressed) {
|
|
175
|
-
return {
|
|
176
|
-
emit: { kind: "progress", at },
|
|
177
|
-
nextLastSignalMs: input.nowMs,
|
|
178
|
-
done: false,
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
if (input.nowMs - input.lastSignalMs >= silenceMs) {
|
|
182
|
-
return {
|
|
183
|
-
emit: { kind: "silence", at },
|
|
184
|
-
nextLastSignalMs: input.nowMs,
|
|
185
|
-
done: false,
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
return { nextLastSignalMs: input.lastSignalMs, done: false };
|
|
189
|
-
}
|
|
190
|
-
export function watcherEventsPath(hqRoot, laneId) {
|
|
191
|
-
return path.join(laneRunDir(hqRoot, laneId), "watcher", "events.jsonl");
|
|
192
|
-
}
|
|
193
|
-
export function watcherOutLogPath(hqRoot, laneId) {
|
|
194
|
-
return path.join(laneRunDir(hqRoot, laneId), "logs", "watcher.out");
|
|
195
|
-
}
|
|
196
|
-
export function appendCappedLog(filePath, line, maxBytes = WATCHER_OUT_MAX_BYTES) {
|
|
197
|
-
const payload = line.endsWith("\n") ? line : `${line}\n`;
|
|
198
|
-
const payloadBytes = Buffer.byteLength(payload, "utf8");
|
|
199
|
-
if (payloadBytes > maxBytes)
|
|
200
|
-
return;
|
|
201
|
-
let existing = "";
|
|
202
|
-
try {
|
|
203
|
-
existing = fs.readFileSync(filePath, "utf8");
|
|
204
|
-
}
|
|
205
|
-
catch (err) {
|
|
206
|
-
const code = err.code;
|
|
207
|
-
if (code !== "ENOENT")
|
|
208
|
-
throw err;
|
|
209
|
-
}
|
|
210
|
-
let body = existing;
|
|
211
|
-
while (Buffer.byteLength(body, "utf8") + payloadBytes > maxBytes) {
|
|
212
|
-
const nl = body.indexOf("\n");
|
|
213
|
-
if (nl === -1) {
|
|
214
|
-
body = "";
|
|
215
|
-
break;
|
|
216
|
-
}
|
|
217
|
-
body = body.slice(nl + 1);
|
|
218
|
-
}
|
|
219
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
220
|
-
fs.writeFileSync(filePath, body + payload, "utf8");
|
|
221
|
-
}
|
|
222
|
-
export function writeWatcherDecision(filePath, record, maxBytes = WATCHER_OUT_MAX_BYTES) {
|
|
223
|
-
const compact = {};
|
|
224
|
-
for (const [key, value] of Object.entries(record)) {
|
|
225
|
-
if (value !== undefined)
|
|
226
|
-
compact[key] = value;
|
|
227
|
-
}
|
|
228
|
-
appendCappedLog(filePath, JSON.stringify(compact), maxBytes);
|
|
229
|
-
}
|
|
230
|
-
/** Append a decision to the shared event stream without a read-modify-write. */
|
|
231
|
-
export function appendWatcherEvent(filePath, record, appendLine = (target, line) => fs.appendFileSync(target, line, "utf8")) {
|
|
232
|
-
const compact = {};
|
|
233
|
-
for (const [key, value] of Object.entries(record)) {
|
|
234
|
-
if (value !== undefined)
|
|
235
|
-
compact[key] = value;
|
|
236
|
-
}
|
|
237
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
238
|
-
appendLine(filePath, `${JSON.stringify(compact)}\n`);
|
|
239
|
-
}
|
|
240
|
-
export function watcherLastSignalPath(hqRoot, laneId) {
|
|
241
|
-
return path.join(laneRunDir(hqRoot, laneId), "watcher", "last-signal.json");
|
|
242
|
-
}
|
|
243
|
-
export function appendWatcherSignal(hqRoot, laneId, signal) {
|
|
244
|
-
const eventsPath = watcherEventsPath(hqRoot, laneId);
|
|
245
|
-
fs.mkdirSync(path.dirname(eventsPath), { recursive: true });
|
|
246
|
-
fs.appendFileSync(eventsPath, `${JSON.stringify(signal)}\n`, "utf8");
|
|
247
|
-
fs.writeFileSync(watcherLastSignalPath(hqRoot, laneId), `${JSON.stringify(signal, null, 2)}\n`, "utf8");
|
|
248
|
-
}
|
|
249
|
-
/**
|
|
250
|
-
* Slot-free notification. The periodic reconcile pass admits the next queued
|
|
251
|
-
* lane; this watcher must not do it. Intentionally empty.
|
|
252
|
-
*/
|
|
253
|
-
export function notifySlotFreed(_input) {
|
|
254
|
-
// Reconcile lane owns admission when a running slot frees (spec §5.7).
|
|
255
|
-
}
|
|
256
|
-
/**
|
|
257
|
-
* On exit, read the worker's own result for a resumable provider and copy
|
|
258
|
-
* only the session id and the two cache counters onto the record. A missing
|
|
259
|
-
* or unparseable id is recorded as absent with its reason, never invented.
|
|
260
|
-
* Non-resumable providers return null and the record is left as it was.
|
|
261
|
-
*/
|
|
262
|
-
function runResultFields(hqRoot, lane, at) {
|
|
263
|
-
const runDir = laneRunDir(hqRoot, lane.lane_id);
|
|
264
|
-
const result = readWorkerRunResult(runDir, lane.provider, readSpawnManifest(runDir).worker_out_offset ?? 0);
|
|
265
|
-
if (result === null)
|
|
266
|
-
return {};
|
|
267
|
-
const fields = {
|
|
268
|
-
last_run_usage: {
|
|
269
|
-
cache_read_input_tokens: result.cache_read_input_tokens,
|
|
270
|
-
cache_creation_input_tokens: result.cache_creation_input_tokens,
|
|
271
|
-
},
|
|
272
|
-
last_run_usage_at: at,
|
|
273
|
-
};
|
|
274
|
-
if (result.session_id !== null) {
|
|
275
|
-
fields.provider_session_id = result.session_id;
|
|
276
|
-
fields.provider_session_id_absent = undefined;
|
|
277
|
-
}
|
|
278
|
-
else {
|
|
279
|
-
// A resumed provider may exit before it emits a new result object. Keep
|
|
280
|
-
// the prior session id so the lane remains resumable; only describe an
|
|
281
|
-
// absent id when no earlier run recorded one.
|
|
282
|
-
if (lane.provider_session_id || lane.last_resume?.session_id) {
|
|
283
|
-
fields.provider_session_id_absent = undefined;
|
|
284
|
-
}
|
|
285
|
-
else {
|
|
286
|
-
fields.provider_session_id_absent = result.session_id_absent ?? "no_result_json";
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
return fields;
|
|
290
|
-
}
|
|
291
|
-
function maybeHeartbeatLane(hqRoot, laneId, nowMs) {
|
|
292
|
-
try {
|
|
293
|
-
retryUpdateLane(hqRoot, laneId, "watcher", (current) => {
|
|
294
|
-
if (current.state !== "running")
|
|
295
|
-
return current;
|
|
296
|
-
const last = Date.parse(current.last_host_report_at ?? "");
|
|
297
|
-
if (Number.isFinite(last) && nowMs - last < WATCHER_HEARTBEAT_MS) {
|
|
298
|
-
return current;
|
|
299
|
-
}
|
|
300
|
-
return { ...current, last_host_report_at: new Date(nowMs).toISOString() };
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
catch (err) {
|
|
304
|
-
process.stderr.write(`hq lanes watcher: heartbeat failed for ${laneId}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
export function stalledReason(silenceMs) {
|
|
308
|
-
return `no_tool_call_or_output_for_${Math.round(silenceMs / 1000)}s`;
|
|
309
|
-
}
|
|
310
|
-
/**
|
|
311
|
-
* The consumer of the silence signal. A silence emit on a running lane sets
|
|
312
|
-
* `stalled` on the record so `hq lanes list` and `show` surface it; the next
|
|
313
|
-
* progress emit clears it. Nothing here changes `state`.
|
|
314
|
-
*/
|
|
315
|
-
export function markLaneStalled(hqRoot, laneId, at, reason) {
|
|
316
|
-
return retryUpdateLane(hqRoot, laneId, "watcher", (current) => {
|
|
317
|
-
if (current.state !== "running")
|
|
318
|
-
return current;
|
|
319
|
-
if (current.stalled === true && current.stalled_reason === reason)
|
|
320
|
-
return current;
|
|
321
|
-
return { ...current, stalled: true, stalled_at: at, stalled_reason: reason };
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
export function clearLaneStalled(hqRoot, laneId) {
|
|
325
|
-
return retryUpdateLane(hqRoot, laneId, "watcher", (current) => {
|
|
326
|
-
if (current.stalled === undefined && current.stalled_at === undefined)
|
|
327
|
-
return current;
|
|
328
|
-
return { ...current, stalled: undefined, stalled_at: undefined, stalled_reason: undefined };
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
export function applyTerminalToLane(hqRoot, laneId, classification, opts = {}) {
|
|
332
|
-
return applyTerminalToLaneWithResult(hqRoot, laneId, classification, opts).record;
|
|
333
|
-
}
|
|
334
|
-
/** Terminal write outcome for emitters: only the CAS winner may publish mesh effects. */
|
|
335
|
-
export function applyTerminalToLaneWithResult(hqRoot, laneId, classification, opts = {}) {
|
|
336
|
-
const at = opts.at ?? new Date().toISOString();
|
|
337
|
-
const envelopeRef = opts.envelopeRef ?? "envelopes/unknown.json";
|
|
338
|
-
const existing = readLane(hqRoot, laneId);
|
|
339
|
-
let questionRefusalCode = null;
|
|
340
|
-
if (!isTerminalLaneState(existing.state) &&
|
|
341
|
-
classification.state === "awaiting_input" &&
|
|
342
|
-
opts.envelope &&
|
|
343
|
-
(opts.envelope.questions?.length ?? 0) > 0) {
|
|
344
|
-
try {
|
|
345
|
-
raiseEnvelopeQuestions(hqRoot, existing, opts.envelope, opts.envelopeRef ?? "envelopes/unknown.json", at, opts.questionIndexes, opts.activeCompany);
|
|
346
|
-
}
|
|
347
|
-
catch (err) {
|
|
348
|
-
questionRefusalCode = questionRecordRefusalCode(err);
|
|
349
|
-
if (questionRefusalCode === null)
|
|
350
|
-
throw err;
|
|
351
|
-
process.stderr.write(`hq lanes watcher: terminal ask refused for ${laneId}: ${questionRefusalCode}\n`);
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
const terminalClassification = questionRefusalCode === null
|
|
355
|
-
? classification
|
|
356
|
-
: {
|
|
357
|
-
state: "failed",
|
|
358
|
-
reason: `question_record_refused:${questionRefusalCode}`,
|
|
359
|
-
};
|
|
360
|
-
let changed = false;
|
|
361
|
-
let effectiveEnvelope = opts.envelope ?? undefined;
|
|
362
|
-
let effectiveClassification = terminalClassification;
|
|
363
|
-
const updated = retryUpdateLane(hqRoot, laneId, "watcher", (current) => {
|
|
364
|
-
const approvalGate = opts.envelope !== undefined && opts.envelope !== null
|
|
365
|
-
? enforceRegistryApprovalGate(current, opts.envelope)
|
|
366
|
-
: undefined;
|
|
367
|
-
effectiveEnvelope = approvalGate?.envelope ?? opts.envelope ?? undefined;
|
|
368
|
-
effectiveClassification = approvalGate?.violation
|
|
369
|
-
? { state: "blocked", reason: "approval_gate_violated" }
|
|
370
|
-
: terminalClassification;
|
|
371
|
-
const terminal = current.state === "done" ||
|
|
372
|
-
current.state === "failed" ||
|
|
373
|
-
current.state === "interrupted" ||
|
|
374
|
-
current.state === "cancelled" ||
|
|
375
|
-
current.state === "killed";
|
|
376
|
-
if (terminal)
|
|
377
|
-
return current;
|
|
378
|
-
if (current.state === "awaiting_input" || current.state === "blocked") {
|
|
379
|
-
if (current.state === effectiveClassification.state)
|
|
380
|
-
return current;
|
|
381
|
-
}
|
|
382
|
-
const next = {
|
|
383
|
-
...current,
|
|
384
|
-
...runResultFields(hqRoot, current, at),
|
|
385
|
-
state: effectiveClassification.state,
|
|
386
|
-
queued_reason: undefined,
|
|
387
|
-
queued_details: undefined,
|
|
388
|
-
stalled: undefined,
|
|
389
|
-
stalled_at: undefined,
|
|
390
|
-
stalled_reason: undefined,
|
|
391
|
-
interrupted_at: effectiveClassification.state === "interrupted" ? at : undefined,
|
|
392
|
-
envelope_validation_errors: opts.envelopeValidationErrors,
|
|
393
|
-
};
|
|
394
|
-
changed = true;
|
|
395
|
-
if (effectiveClassification.reason) {
|
|
396
|
-
next.terminal_reason =
|
|
397
|
-
effectiveClassification.reason;
|
|
398
|
-
}
|
|
399
|
-
if (opts.quota?.quota_reset_at) {
|
|
400
|
-
next.quota_reset_at = opts.quota.quota_reset_at;
|
|
401
|
-
}
|
|
402
|
-
if (opts.quota?.marker) {
|
|
403
|
-
next.model_quota_marker = opts.quota.marker;
|
|
404
|
-
}
|
|
405
|
-
if (opts.providerRejection) {
|
|
406
|
-
next.terminal_details = providerRejectionDetails(opts.providerRejection, current);
|
|
407
|
-
}
|
|
408
|
-
const envelope = effectiveEnvelope;
|
|
409
|
-
if (approvalGate && !approvalGate.violation && approvalGate.approvedIdsToConsume.length > 0) {
|
|
410
|
-
next.registry_worker_approval_history = approvalGate.approvedIdsToConsume.reduce((history, approvalId) => appendCappedHistory(history, {
|
|
411
|
-
event: "consumed",
|
|
412
|
-
approval_id: approvalId,
|
|
413
|
-
envelope_ref: envelopeRef,
|
|
414
|
-
at,
|
|
415
|
-
}), current.registry_worker_approval_history ?? []);
|
|
416
|
-
}
|
|
417
|
-
if (envelope) {
|
|
418
|
-
const storySwitch = applyStorySwitchEnvelope(current, envelope, at, envelopeRef);
|
|
419
|
-
if (envelope.decision !== "blocked")
|
|
420
|
-
next.keep_alive_blocked_resume_streak = 0;
|
|
421
|
-
next.last_envelope = {
|
|
422
|
-
ref: envelopeRef,
|
|
423
|
-
decision: envelope.decision,
|
|
424
|
-
at,
|
|
425
|
-
artifacts: envelope.artifacts,
|
|
426
|
-
...(envelope.blocked_reason !== undefined ? { blocked_reason: envelope.blocked_reason } : {}),
|
|
427
|
-
...(envelope.next_check_at !== undefined ? { next_check_at: envelope.next_check_at } : {}),
|
|
428
|
-
};
|
|
429
|
-
if (storySwitch.clearPending) {
|
|
430
|
-
next.story_switch_pending = undefined;
|
|
431
|
-
}
|
|
432
|
-
if (storySwitch.storySwitchUnacknowledged !== undefined) {
|
|
433
|
-
next.story_switch_unacknowledged = storySwitch.storySwitchUnacknowledged;
|
|
434
|
-
next.attribution_history = storySwitch.attributionHistory ?? current.attribution_history;
|
|
435
|
-
}
|
|
436
|
-
const storyStatus = storySwitch.suppressCompletion
|
|
437
|
-
? "in_progress"
|
|
438
|
-
: terminalEnvelopeStoryStatus(current, envelope.decision, envelope.story_status);
|
|
439
|
-
if (storyStatus !== undefined) {
|
|
440
|
-
next.story_status = storySwitch.suppressCompletion ? "in_progress" : envelope.story_status;
|
|
441
|
-
next.story_complete = storySwitch.suppressCompletion ? false : envelope.story_complete;
|
|
442
|
-
next.story_complete_reason = storySwitch.suppressCompletion ? undefined : envelope.story_complete_reason;
|
|
443
|
-
const currentProject = current.project_id;
|
|
444
|
-
const currentStory = current.story_id;
|
|
445
|
-
const updatedStories = laneStories(current).map((story) => story.project_id === currentProject && story.story_id === currentStory
|
|
446
|
-
? {
|
|
447
|
-
...story,
|
|
448
|
-
status: storyStatus,
|
|
449
|
-
updated_at: at,
|
|
450
|
-
...(envelope.decision === "blocked"
|
|
451
|
-
? { blocked_reason: envelope.blocked_reason ?? "" }
|
|
452
|
-
: { blocked_reason: undefined }),
|
|
453
|
-
}
|
|
454
|
-
: story);
|
|
455
|
-
next.stories = updatedStories;
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
return next;
|
|
459
|
-
});
|
|
460
|
-
if (updated.state === "done" &&
|
|
461
|
-
effectiveClassification.state === "done" &&
|
|
462
|
-
effectiveEnvelope?.decision === "done" &&
|
|
463
|
-
opts.quota == null &&
|
|
464
|
-
opts.providerRejection == null &&
|
|
465
|
-
updated.account &&
|
|
466
|
-
updated.model) {
|
|
467
|
-
clearAccountQuotaAfterSuccessfulTurnBestEffort(hqRoot, updated.provider, updated.account, updated.model, new Date(at));
|
|
468
|
-
}
|
|
469
|
-
notifySlotFreed({
|
|
470
|
-
hqRoot,
|
|
471
|
-
provider: updated.provider,
|
|
472
|
-
account: updated.account,
|
|
473
|
-
});
|
|
474
|
-
return {
|
|
475
|
-
record: updated,
|
|
476
|
-
changed,
|
|
477
|
-
...(effectiveEnvelope !== undefined ? { envelope: effectiveEnvelope } : {}),
|
|
478
|
-
classification: effectiveClassification,
|
|
479
|
-
};
|
|
480
|
-
}
|
|
481
|
-
export function isPidAlive(pid) {
|
|
482
|
-
try {
|
|
483
|
-
process.kill(pid, 0);
|
|
484
|
-
return true;
|
|
485
|
-
}
|
|
486
|
-
catch (err) {
|
|
487
|
-
const code = err.code;
|
|
488
|
-
if (code === "ESRCH")
|
|
489
|
-
return false;
|
|
490
|
-
if (code === "EPERM")
|
|
491
|
-
return true;
|
|
492
|
-
process.stderr.write(`hq lanes watcher: liveness probe for pid ${pid} failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
493
|
-
throw err;
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
function fileMtimeMs(filePath) {
|
|
497
|
-
try {
|
|
498
|
-
return fs.statSync(filePath).mtimeMs;
|
|
499
|
-
}
|
|
500
|
-
catch (err) {
|
|
501
|
-
const code = err.code;
|
|
502
|
-
if (code === "ENOENT")
|
|
503
|
-
return null;
|
|
504
|
-
process.stderr.write(`hq lanes watcher: failed to stat ${filePath}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
505
|
-
throw err;
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
function fileNonEmpty(filePath) {
|
|
509
|
-
try {
|
|
510
|
-
return fs.statSync(filePath).size > 0;
|
|
511
|
-
}
|
|
512
|
-
catch (err) {
|
|
513
|
-
const code = err.code;
|
|
514
|
-
if (code === "ENOENT")
|
|
515
|
-
return false;
|
|
516
|
-
process.stderr.write(`hq lanes watcher: failed to stat ${filePath}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
517
|
-
throw err;
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
function isPlainObject(value) {
|
|
521
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
522
|
-
}
|
|
523
|
-
/** Keep only questions that the normal envelope parser accepts. */
|
|
524
|
-
function recoverAskEnvelope(text, validationErrors) {
|
|
525
|
-
const raw = JSON.parse(text);
|
|
526
|
-
if (!isPlainObject(raw) || raw.decision !== "ask" || !Array.isArray(raw.questions)) {
|
|
527
|
-
return null;
|
|
528
|
-
}
|
|
529
|
-
const validQuestions = [];
|
|
530
|
-
const questionIndexes = [];
|
|
531
|
-
const questionErrors = [];
|
|
532
|
-
for (let index = 0; index < raw.questions.length; index += 1) {
|
|
533
|
-
const question = raw.questions[index];
|
|
534
|
-
try {
|
|
535
|
-
const parsed = parseLaneEnvelope({
|
|
536
|
-
schema_version: "hq-lane-envelope.v1",
|
|
537
|
-
decision: "ask",
|
|
538
|
-
summary: "Recovered operator question",
|
|
539
|
-
story_status: "in_progress",
|
|
540
|
-
artifacts: [],
|
|
541
|
-
questions: [question],
|
|
542
|
-
});
|
|
543
|
-
const item = parsed.questions?.[0];
|
|
544
|
-
if (item === undefined) {
|
|
545
|
-
throw new Error("question parser returned no question");
|
|
546
|
-
}
|
|
547
|
-
validQuestions.push(item);
|
|
548
|
-
questionIndexes.push(index);
|
|
549
|
-
}
|
|
550
|
-
catch (err) {
|
|
551
|
-
questionErrors.push(`questions[${index}] is invalid: ${err instanceof Error ? err.message : String(err)}`);
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
if (validQuestions.length === 0) {
|
|
555
|
-
return {
|
|
556
|
-
envelope: {
|
|
557
|
-
schema_version: "hq-lane-envelope.v1",
|
|
558
|
-
decision: "ask",
|
|
559
|
-
summary: "The worker needs operator input after envelope validation failed.",
|
|
560
|
-
story_status: STORY_STATUSES.includes(raw.story_status)
|
|
561
|
-
? raw.story_status
|
|
562
|
-
: "in_progress",
|
|
563
|
-
artifacts: [],
|
|
564
|
-
questions: [],
|
|
565
|
-
},
|
|
566
|
-
questionIndexes,
|
|
567
|
-
questionErrors,
|
|
568
|
-
sourceQuestionCount: raw.questions.length,
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
const contextErrors = [...new Set([...validationErrors, ...questionErrors])];
|
|
572
|
-
const storyStatus = STORY_STATUSES.includes(raw.story_status) && raw.story_status !== "done"
|
|
573
|
-
? raw.story_status
|
|
574
|
-
: "in_progress";
|
|
575
|
-
const questions = validQuestions.map((question) => ({
|
|
576
|
-
...question,
|
|
577
|
-
context: `${question.context}\n\nEnvelope validation errors:\n${contextErrors.map((error) => `- ${error}`).join("\n")}`,
|
|
578
|
-
}));
|
|
579
|
-
return {
|
|
580
|
-
envelope: {
|
|
581
|
-
schema_version: "hq-lane-envelope.v1",
|
|
582
|
-
decision: "ask",
|
|
583
|
-
summary: typeof raw.summary === "string" && raw.summary.length > 0
|
|
584
|
-
? raw.summary
|
|
585
|
-
: "The worker needs operator input after envelope validation failed.",
|
|
586
|
-
story_status: storyStatus,
|
|
587
|
-
artifacts: [],
|
|
588
|
-
questions,
|
|
589
|
-
},
|
|
590
|
-
questionIndexes,
|
|
591
|
-
questionErrors,
|
|
592
|
-
sourceQuestionCount: raw.questions.length,
|
|
593
|
-
};
|
|
594
|
-
}
|
|
595
|
-
export function readLatestEnvelope(hqRoot, laneId, minName) {
|
|
596
|
-
return readLatestEnvelopeRecord(hqRoot, laneId, minName)?.envelope ?? null;
|
|
597
|
-
}
|
|
598
|
-
/** Valid-only view over `readLatestEnvelopeFile`; an unparseable file reads as absent. */
|
|
599
|
-
export function readLatestEnvelopeRecord(hqRoot, laneId, minName) {
|
|
600
|
-
const latest = readLatestEnvelopeFile(hqRoot, laneId, minName);
|
|
601
|
-
if (latest === null)
|
|
602
|
-
return null;
|
|
603
|
-
if (latest.envelope === null) {
|
|
604
|
-
process.stderr.write(`hq lanes watcher: envelope ${latest.path} is not a valid hq-lane-envelope.v1: ${latest.error}\n`);
|
|
605
|
-
return null;
|
|
606
|
-
}
|
|
607
|
-
return { envelope: latest.envelope, ref: latest.ref };
|
|
608
|
-
}
|
|
609
|
-
/**
|
|
610
|
-
* `minName` is this run's envelope slot (basename from spawn.json). Envelopes
|
|
611
|
-
* that sort before it belong to an earlier run of the same lane and must not
|
|
612
|
-
* classify this exit: a resumed run that dies before writing its own envelope
|
|
613
|
-
* would otherwise be marked done again from the previous one.
|
|
614
|
-
*/
|
|
615
|
-
export function readLatestEnvelopeFile(hqRoot, laneId, minName) {
|
|
616
|
-
const dir = path.join(laneRunDir(hqRoot, laneId), "envelopes");
|
|
617
|
-
let names;
|
|
618
|
-
try {
|
|
619
|
-
names = fs
|
|
620
|
-
.readdirSync(dir)
|
|
621
|
-
.filter((name) => name.endsWith(".json"))
|
|
622
|
-
.filter((name) => minName === undefined || name >= minName)
|
|
623
|
-
.sort();
|
|
624
|
-
}
|
|
625
|
-
catch (err) {
|
|
626
|
-
const code = err.code;
|
|
627
|
-
if (code === "ENOENT")
|
|
628
|
-
return null;
|
|
629
|
-
process.stderr.write(`hq lanes watcher: failed to list envelopes for ${laneId}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
630
|
-
throw err;
|
|
631
|
-
}
|
|
632
|
-
if (names.length === 0)
|
|
633
|
-
return null;
|
|
634
|
-
const latest = names[names.length - 1];
|
|
635
|
-
const filePath = path.join(dir, latest);
|
|
636
|
-
let text;
|
|
637
|
-
try {
|
|
638
|
-
text = fs.readFileSync(filePath, "utf8");
|
|
639
|
-
}
|
|
640
|
-
catch (err) {
|
|
641
|
-
process.stderr.write(`hq lanes watcher: failed to read envelope ${filePath}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
642
|
-
throw err;
|
|
643
|
-
}
|
|
644
|
-
const ref = path.posix.join("envelopes", latest);
|
|
645
|
-
try {
|
|
646
|
-
return {
|
|
647
|
-
ref,
|
|
648
|
-
path: filePath,
|
|
649
|
-
text,
|
|
650
|
-
envelope: parseLaneEnvelope(JSON.parse(text)),
|
|
651
|
-
error: null,
|
|
652
|
-
};
|
|
653
|
-
}
|
|
654
|
-
catch (err) {
|
|
655
|
-
return {
|
|
656
|
-
ref,
|
|
657
|
-
path: filePath,
|
|
658
|
-
text,
|
|
659
|
-
envelope: null,
|
|
660
|
-
error: err instanceof Error ? err.message : String(err),
|
|
661
|
-
};
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
/** A descriptor for this run's manifest slot when no envelope file was written. */
|
|
665
|
-
export function missingExpectedEnvelopeFile(hqRoot, laneId, envelopePath) {
|
|
666
|
-
if (!envelopePath || !path.isAbsolute(envelopePath))
|
|
667
|
-
return null;
|
|
668
|
-
const runDir = laneRunDir(hqRoot, laneId);
|
|
669
|
-
const expectedPath = path.resolve(envelopePath);
|
|
670
|
-
const expectedDir = path.resolve(runDir, "envelopes");
|
|
671
|
-
const filename = path.basename(expectedPath);
|
|
672
|
-
if (path.dirname(expectedPath) !== expectedDir || !filename.endsWith(".json"))
|
|
673
|
-
return null;
|
|
674
|
-
return {
|
|
675
|
-
ref: path.posix.join("envelopes", filename),
|
|
676
|
-
path: expectedPath,
|
|
677
|
-
text: "",
|
|
678
|
-
envelope: null,
|
|
679
|
-
error: `Missing expected envelope file: ${expectedPath}`,
|
|
680
|
-
missing: true,
|
|
681
|
-
};
|
|
682
|
-
}
|
|
683
|
-
async function settleManagerStatusGate(hqRoot, lane, manifestTurnId, at, deps, logDecision) {
|
|
684
|
-
let target;
|
|
685
|
-
try {
|
|
686
|
-
resolveLaneCompany(hqRoot, lane);
|
|
687
|
-
target = resolveManagerStatusTarget(hqRoot, lane);
|
|
688
|
-
}
|
|
689
|
-
catch (err) {
|
|
690
|
-
process.stderr.write(`hq lanes watcher: manager status mapping failed for ${lane.lane_id}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
691
|
-
logDecision({ event: "status_gate_mapping_failed", at, error: err instanceof Error ? err.message : String(err) });
|
|
692
|
-
return lane;
|
|
693
|
-
}
|
|
694
|
-
const gate = lane.status_gate;
|
|
695
|
-
if (!target.manager || target.statusPath === undefined || gate === undefined)
|
|
696
|
-
return lane;
|
|
697
|
-
if (manifestTurnId !== undefined && manifestTurnId !== gate.turn_id ||
|
|
698
|
-
target.statusPath !== gate.status_path) {
|
|
699
|
-
logDecision({ event: "status_gate_baseline_mismatch", at, turn_id: gate.turn_id });
|
|
700
|
-
return lane;
|
|
701
|
-
}
|
|
702
|
-
let written;
|
|
703
|
-
try {
|
|
704
|
-
written = managerStatusWasWritten(gate, readStatusSnapshot(gate.status_path));
|
|
705
|
-
}
|
|
706
|
-
catch (err) {
|
|
707
|
-
process.stderr.write(`hq lanes watcher: manager status read failed for ${lane.lane_id}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
708
|
-
logDecision({ event: "status_gate_status_read_failed", at, error: err instanceof Error ? err.message : String(err) });
|
|
709
|
-
written = false;
|
|
710
|
-
}
|
|
711
|
-
if (written) {
|
|
712
|
-
return retryUpdateLane(hqRoot, lane.lane_id, "watcher", (current) => {
|
|
713
|
-
if (current.status_gate?.turn_id !== gate.turn_id || current.status_gate.consecutive_misses === 0)
|
|
714
|
-
return current;
|
|
715
|
-
return { ...current, status_gate: { ...current.status_gate, consecutive_misses: 0 } };
|
|
716
|
-
});
|
|
717
|
-
}
|
|
718
|
-
const resumeText = `Your turn ended without updating ${gate.status_path}. Write your status now: update updated_at, kpis and current_work, then end the turn.`;
|
|
719
|
-
const updated = queueManagerStatusGateResume(hqRoot, lane.lane_id, {
|
|
720
|
-
turn_id: gate.turn_id,
|
|
721
|
-
path: gate.status_path,
|
|
722
|
-
at,
|
|
723
|
-
identified_by: target.identified_by ?? "juniors",
|
|
724
|
-
}, resumeText);
|
|
725
|
-
const miss = updated.status_gate_history?.find((entry) => entry.event === "status_missed" && entry.turn_id === gate.turn_id);
|
|
726
|
-
if (miss) {
|
|
727
|
-
logDecision({
|
|
728
|
-
event: "status_missed",
|
|
729
|
-
at,
|
|
730
|
-
turn_id: gate.turn_id,
|
|
731
|
-
path: gate.status_path,
|
|
732
|
-
consecutive_misses: updated.status_gate?.consecutive_misses ?? 0,
|
|
733
|
-
resume_queued: "resume_queued" in miss && miss.resume_queued === true,
|
|
734
|
-
resume_skipped_reason: "resume_skipped_reason" in miss ? miss.resume_skipped_reason : undefined,
|
|
735
|
-
state: updated.state,
|
|
736
|
-
});
|
|
737
|
-
}
|
|
738
|
-
return updated;
|
|
739
|
-
}
|
|
740
|
-
async function settleBlockerFreshness(hqRoot, lane, manifestTurnId, at, deps, mesh, logDecision) {
|
|
741
|
-
let errorLogged = false;
|
|
742
|
-
const resolution = await resolveBlockerFreshnessForLane(hqRoot, lane, {
|
|
743
|
-
readFlag: deps.readBlockerFreshnessFlag ?? readLanesRegistryFlag,
|
|
744
|
-
now: Date.parse(at),
|
|
745
|
-
onError: (error) => {
|
|
746
|
-
if (errorLogged)
|
|
747
|
-
return;
|
|
748
|
-
errorLogged = true;
|
|
749
|
-
const code = error instanceof LanesError ? error.error : "blocker_freshness_unavailable";
|
|
750
|
-
process.stderr.write(`hq lanes watcher: blocker freshness disabled for ${lane.lane_id}: ${code}\n`);
|
|
751
|
-
logDecision({ event: "blocker_freshness_disabled", at, error: code });
|
|
752
|
-
},
|
|
753
|
-
});
|
|
754
|
-
if (!resolution.enabled || resolution.statusPath === undefined || resolution.error !== undefined)
|
|
755
|
-
return lane;
|
|
756
|
-
if (manifestTurnId !== undefined && lane.status_gate !== undefined && lane.status_gate.turn_id !== manifestTurnId) {
|
|
757
|
-
logDecision({ event: "blocker_freshness_baseline_mismatch", at, turn_id: lane.status_gate.turn_id });
|
|
758
|
-
return lane;
|
|
759
|
-
}
|
|
760
|
-
if (resolution.stale.length === 0) {
|
|
761
|
-
if (lane.blocker_freshness_state === undefined)
|
|
762
|
-
return lane;
|
|
763
|
-
return retryUpdateLane(hqRoot, lane.lane_id, "watcher", (current) => current.blocker_freshness_state === undefined
|
|
764
|
-
? current
|
|
765
|
-
: { ...current, blocker_freshness_state: undefined });
|
|
766
|
-
}
|
|
767
|
-
const staleKeys = resolution.stale.map((blocker) => blocker.key).sort();
|
|
768
|
-
const staleLabels = resolution.stale.map((blocker) => blocker.label);
|
|
769
|
-
const fingerprint = blockerFreshnessFingerprint(resolution.stale);
|
|
770
|
-
const turnId = manifestTurnId ?? lane.status_gate?.turn_id ?? `unknown-${at}`;
|
|
771
|
-
const resumeText = `These open blockers in ${resolution.statusPath} were not re-verified in the last ${resolution.windowHours}h: ${staleLabels.join(", ")}. Check each one now; set verified_at on the ones that still hold and close the rest, then end the turn.`;
|
|
772
|
-
let deliverEscalation = false;
|
|
773
|
-
let deliverFallback = false;
|
|
774
|
-
let retryUndeliverable = false;
|
|
775
|
-
let undeliverableReason;
|
|
776
|
-
let escalationText = "";
|
|
777
|
-
const sessionInbox = lane.senior.kind === "session"
|
|
778
|
-
? lane.senior.id
|
|
779
|
-
? findBlockerFreshnessSessionInbox(hqRoot, lane.senior.id)
|
|
780
|
-
: { reason: "The session senior has no session id" }
|
|
781
|
-
: undefined;
|
|
782
|
-
let updated = retryUpdateLane(hqRoot, lane.lane_id, "watcher", (current) => {
|
|
783
|
-
deliverEscalation = false;
|
|
784
|
-
deliverFallback = false;
|
|
785
|
-
retryUndeliverable = false;
|
|
786
|
-
undeliverableReason = undefined;
|
|
787
|
-
const prior = current.blocker_freshness_state;
|
|
788
|
-
const repeated = prior !== undefined && staleKeys.some((key) => prior.stale_keys.includes(key));
|
|
789
|
-
const history = current.status_gate_history ?? [];
|
|
790
|
-
const alreadyEscalatedThisTurn = history.some((entry) => entry.event === "blocker_stale_escalated" && entry.turn_id === turnId && entry.fingerprint === fingerprint);
|
|
791
|
-
const escalate = repeated && prior?.escalated_fingerprint !== fingerprint && !alreadyEscalatedThisTurn;
|
|
792
|
-
const alreadyEscalatedSet = prior?.escalated_fingerprint === fingerprint;
|
|
793
|
-
const pendingUndeliverable = prior?.undeliverable_fingerprint === fingerprint;
|
|
794
|
-
const failedFallback = history.find((entry) => entry.event === "blocker_stale_escalation_undeliverable" &&
|
|
795
|
-
entry.fingerprint === fingerprint && entry.delivery_failed === true &&
|
|
796
|
-
entry.delivery_recovered !== true);
|
|
797
|
-
const statusResumeQueued = history.some((entry) => entry.event === "status_missed" && entry.turn_id === turnId && entry.resume_queued === true);
|
|
798
|
-
let resumeQueued = false;
|
|
799
|
-
let resumeSkippedReason;
|
|
800
|
-
let nextLane = current;
|
|
801
|
-
if (!escalate && !alreadyEscalatedSet) {
|
|
802
|
-
const resume = queueLaneTerminalResume(current, at, resumeText, {
|
|
803
|
-
mergeExisting: statusResumeQueued,
|
|
804
|
-
});
|
|
805
|
-
resumeQueued = resume.resumeQueued;
|
|
806
|
-
resumeSkippedReason = resume.resumeSkippedReason;
|
|
807
|
-
nextLane = resume.lane;
|
|
808
|
-
}
|
|
809
|
-
const staleEntry = {
|
|
810
|
-
event: "blocker_stale",
|
|
811
|
-
turn_id: turnId,
|
|
812
|
-
path: resolution.statusPath,
|
|
813
|
-
at,
|
|
814
|
-
labels: staleLabels,
|
|
815
|
-
count: resolution.stale.length,
|
|
816
|
-
fingerprint,
|
|
817
|
-
...(resumeQueued ? { resume_queued: true } : {}),
|
|
818
|
-
...(resumeSkippedReason !== undefined ? { resume_skipped_reason: resumeSkippedReason } : {}),
|
|
819
|
-
};
|
|
820
|
-
let nextHistory = history.some((entry) => entry.event === "blocker_stale" && entry.turn_id === turnId)
|
|
821
|
-
? [...history]
|
|
822
|
-
: appendCappedHistory(history, staleEntry);
|
|
823
|
-
let escalatedFingerprint = prior?.escalated_fingerprint;
|
|
824
|
-
if (escalate) {
|
|
825
|
-
const escalatedEntry = {
|
|
826
|
-
event: "blocker_stale_escalated",
|
|
827
|
-
turn_id: turnId,
|
|
828
|
-
path: resolution.statusPath,
|
|
829
|
-
at,
|
|
830
|
-
labels: staleLabels,
|
|
831
|
-
count: resolution.stale.length,
|
|
832
|
-
fingerprint,
|
|
833
|
-
};
|
|
834
|
-
nextHistory = appendCappedHistory(nextHistory, escalatedEntry);
|
|
835
|
-
escalatedFingerprint = fingerprint;
|
|
836
|
-
deliverEscalation = true;
|
|
837
|
-
escalationText = `Blocker freshness escalation for ${current.lane_id}: these blockers remain stale after consecutive turns: ${staleLabels.join(", ")}. Status file: ${resolution.statusPath}.`;
|
|
838
|
-
if (lane.senior.kind === "session" && sessionInbox !== undefined && !("inbox" in sessionInbox)) {
|
|
839
|
-
deliverFallback = true;
|
|
840
|
-
undeliverableReason = sessionInbox.reason;
|
|
841
|
-
nextHistory = appendCappedHistory(nextHistory, {
|
|
842
|
-
event: "blocker_stale_escalation_undeliverable",
|
|
843
|
-
turn_id: turnId,
|
|
844
|
-
path: resolution.statusPath,
|
|
845
|
-
at,
|
|
846
|
-
labels: staleLabels,
|
|
847
|
-
count: resolution.stale.length,
|
|
848
|
-
fingerprint,
|
|
849
|
-
reason: sessionInbox.reason,
|
|
850
|
-
});
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
else if (pendingUndeliverable && sessionInbox !== undefined && "inbox" in sessionInbox) {
|
|
854
|
-
// A monitor inbox registered after the local fallback appeared; retry delivery once.
|
|
855
|
-
deliverEscalation = true;
|
|
856
|
-
retryUndeliverable = true;
|
|
857
|
-
escalationText = `Blocker freshness escalation for ${current.lane_id}: these blockers remain stale after consecutive turns: ${staleLabels.join(", ")}. Status file: ${resolution.statusPath}.`;
|
|
858
|
-
}
|
|
859
|
-
else if (failedFallback !== undefined && sessionInbox !== undefined && "inbox" in sessionInbox) {
|
|
860
|
-
// Prefer the newly available senior inbox over retrying a failed local fallback.
|
|
861
|
-
deliverEscalation = true;
|
|
862
|
-
retryUndeliverable = true;
|
|
863
|
-
escalationText = `Blocker freshness escalation for ${current.lane_id}: these blockers remain stale after consecutive turns: ${staleLabels.join(", ")}. Status file: ${resolution.statusPath}.`;
|
|
864
|
-
}
|
|
865
|
-
else if (failedFallback !== undefined) {
|
|
866
|
-
deliverFallback = true;
|
|
867
|
-
undeliverableReason = failedFallback.reason ?? prior?.undeliverable_reason ?? "The session senior inbox is unavailable";
|
|
868
|
-
escalationText = `Blocker freshness escalation for ${current.lane_id}: these blockers remain stale after consecutive turns: ${staleLabels.join(", ")}. Status file: ${resolution.statusPath}.`;
|
|
869
|
-
}
|
|
870
|
-
else if (prior !== undefined && blockerFreshnessFingerprint(prior.stale_keys.map((key, index) => ({ key, label: prior.stale_labels[index] ?? "" }))) !== fingerprint) {
|
|
871
|
-
escalatedFingerprint = undefined;
|
|
872
|
-
}
|
|
873
|
-
return {
|
|
874
|
-
...nextLane,
|
|
875
|
-
blocker_freshness_state: {
|
|
876
|
-
stale_keys: staleKeys,
|
|
877
|
-
stale_labels: staleLabels,
|
|
878
|
-
...(escalatedFingerprint !== undefined ? { escalated_fingerprint: escalatedFingerprint } : {}),
|
|
879
|
-
...(undeliverableReason !== undefined
|
|
880
|
-
? { undeliverable_fingerprint: fingerprint, undeliverable_reason: undeliverableReason }
|
|
881
|
-
: prior?.undeliverable_fingerprint === fingerprint
|
|
882
|
-
? { undeliverable_fingerprint: fingerprint, undeliverable_reason: prior.undeliverable_reason }
|
|
883
|
-
: {}),
|
|
884
|
-
},
|
|
885
|
-
status_gate_history: nextHistory,
|
|
886
|
-
};
|
|
887
|
-
});
|
|
888
|
-
logDecision({ event: "blocker_stale", at, turn_id: turnId, path: resolution.statusPath, labels: staleLabels, count: staleLabels.length });
|
|
889
|
-
const escalated = updated.status_gate_history?.find((entry) => entry.event === "blocker_stale_escalated" && entry.fingerprint === fingerprint &&
|
|
890
|
-
(entry.turn_id === turnId || retryUndeliverable || deliverFallback));
|
|
891
|
-
if (escalated && deliverFallback) {
|
|
892
|
-
const reason = undeliverableReason ?? "The session senior has no unique monitor-session inbox";
|
|
893
|
-
try {
|
|
894
|
-
appendDropboxMessageAtPath(path.join(laneRunDir(hqRoot, lane.lane_id), "dropbox"), {
|
|
895
|
-
at,
|
|
896
|
-
message_id: `blocker-freshness-undeliverable:${lane.lane_id}:${fingerprint}`,
|
|
897
|
-
kind: "blocker_stale_escalation_undeliverable",
|
|
898
|
-
text: `${escalationText} The session senior inbox is unavailable (${reason}); this escalation is recorded in the lane inbox for its next turn.`,
|
|
899
|
-
});
|
|
900
|
-
updated = retryUpdateLane(hqRoot, lane.lane_id, "watcher", (current) => {
|
|
901
|
-
const state = current.blocker_freshness_state;
|
|
902
|
-
if (state === undefined)
|
|
903
|
-
return current;
|
|
904
|
-
return {
|
|
905
|
-
...current,
|
|
906
|
-
blocker_freshness_state: {
|
|
907
|
-
...state,
|
|
908
|
-
undeliverable_fingerprint: fingerprint,
|
|
909
|
-
undeliverable_reason: reason,
|
|
910
|
-
},
|
|
911
|
-
status_gate_history: (current.status_gate_history ?? []).map((entry) => entry.event === "blocker_stale_escalation_undeliverable" &&
|
|
912
|
-
entry.fingerprint === fingerprint && entry.delivery_failed === true
|
|
913
|
-
? { ...entry, delivery_recovered: true }
|
|
914
|
-
: entry),
|
|
915
|
-
};
|
|
916
|
-
});
|
|
917
|
-
logDecision({ event: "blocker_stale_escalation_undeliverable", at, turn_id: turnId, fingerprint, reason, fallback: "lane_dropbox" });
|
|
918
|
-
}
|
|
919
|
-
catch (error) {
|
|
920
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
921
|
-
process.stderr.write(`hq lanes watcher: blocker freshness fallback delivery failed for ${lane.lane_id}: ${message}\n`);
|
|
922
|
-
logDecision({ event: "blocker_stale_escalation_fallback_failed", at, turn_id: turnId, fingerprint, error: message });
|
|
923
|
-
updated = retryUpdateLane(hqRoot, lane.lane_id, "watcher", (current) => {
|
|
924
|
-
const state = current.blocker_freshness_state;
|
|
925
|
-
const retryableState = state === undefined ? undefined : { ...state };
|
|
926
|
-
if (retryableState !== undefined) {
|
|
927
|
-
delete retryableState.undeliverable_fingerprint;
|
|
928
|
-
delete retryableState.undeliverable_reason;
|
|
929
|
-
}
|
|
930
|
-
return {
|
|
931
|
-
...current,
|
|
932
|
-
...(retryableState !== undefined ? { blocker_freshness_state: retryableState } : {}),
|
|
933
|
-
status_gate_history: (current.status_gate_history ?? []).map((entry) => entry.event === "blocker_stale_escalation_undeliverable" && entry.fingerprint === fingerprint
|
|
934
|
-
? { ...entry, delivery_failed: true }
|
|
935
|
-
: entry),
|
|
936
|
-
};
|
|
937
|
-
});
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
if ((escalated || retryUndeliverable) && deliverEscalation && !deliverFallback) {
|
|
941
|
-
try {
|
|
942
|
-
if (lane.senior.kind === "lane" && lane.senior.id) {
|
|
943
|
-
if (deps.deliverLaneSeniorMessage === undefined) {
|
|
944
|
-
throw new LanesError("blocker_freshness_delivery_unavailable", "Lane-senior blocker escalations require the lane-message delivery path", { lane_id: lane.lane_id, senior_lane_id: lane.senior.id });
|
|
945
|
-
}
|
|
946
|
-
await deps.deliverLaneSeniorMessage(lane.senior.id, escalationText);
|
|
947
|
-
}
|
|
948
|
-
else if (lane.senior.kind === "session" && lane.senior.id) {
|
|
949
|
-
const inbox = sessionInbox !== undefined && "inbox" in sessionInbox
|
|
950
|
-
? sessionInbox.inbox
|
|
951
|
-
: resolveBlockerFreshnessSessionInbox(hqRoot, lane.senior.id);
|
|
952
|
-
appendDropboxMessageAtPath(inbox, {
|
|
953
|
-
at,
|
|
954
|
-
message_id: `blocker-freshness:${lane.lane_id}:${fingerprint}:${turnId}`,
|
|
955
|
-
kind: "blocker_stale_escalation",
|
|
956
|
-
text: escalationText,
|
|
957
|
-
});
|
|
958
|
-
}
|
|
959
|
-
else {
|
|
960
|
-
throw new LanesError("blocker_freshness_delivery_unavailable", "No durable senior inbox is available for this blocker escalation", { lane_id: lane.lane_id, senior_kind: lane.senior.kind });
|
|
961
|
-
}
|
|
962
|
-
logDecision({ event: "blocker_stale_escalation_delivered", at, turn_id: turnId, senior_kind: lane.senior.kind });
|
|
963
|
-
if (retryUndeliverable) {
|
|
964
|
-
updated = retryUpdateLane(hqRoot, lane.lane_id, "watcher", (current) => {
|
|
965
|
-
const state = current.blocker_freshness_state;
|
|
966
|
-
const failedFallbackStillOpen = (current.status_gate_history ?? []).some((entry) => entry.event === "blocker_stale_escalation_undeliverable" &&
|
|
967
|
-
entry.fingerprint === fingerprint && entry.delivery_failed === true &&
|
|
968
|
-
entry.delivery_recovered !== true);
|
|
969
|
-
if (state?.undeliverable_fingerprint !== fingerprint && !failedFallbackStillOpen)
|
|
970
|
-
return current;
|
|
971
|
-
const recoveredHistory = (current.status_gate_history ?? []).map((entry) => entry.event === "blocker_stale_escalation_undeliverable" && entry.fingerprint === fingerprint
|
|
972
|
-
? { ...entry, delivery_recovered: true }
|
|
973
|
-
: entry);
|
|
974
|
-
if (state?.undeliverable_fingerprint === fingerprint) {
|
|
975
|
-
const recoveredState = { ...state };
|
|
976
|
-
delete recoveredState.undeliverable_fingerprint;
|
|
977
|
-
delete recoveredState.undeliverable_reason;
|
|
978
|
-
return { ...current, blocker_freshness_state: recoveredState, status_gate_history: recoveredHistory };
|
|
979
|
-
}
|
|
980
|
-
return { ...current, status_gate_history: recoveredHistory };
|
|
981
|
-
});
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
catch (error) {
|
|
985
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
986
|
-
process.stderr.write(`hq lanes watcher: blocker freshness escalation delivery failed for ${lane.lane_id}: ${message}\n`);
|
|
987
|
-
logDecision({ event: "blocker_stale_escalation_delivery_failed", at, turn_id: turnId, error: message });
|
|
988
|
-
updated = retryUpdateLane(hqRoot, lane.lane_id, "watcher", (current) => {
|
|
989
|
-
const state = current.blocker_freshness_state;
|
|
990
|
-
if (state?.escalated_fingerprint !== fingerprint)
|
|
991
|
-
return current;
|
|
992
|
-
const retryableState = { ...state };
|
|
993
|
-
delete retryableState.escalated_fingerprint;
|
|
994
|
-
return {
|
|
995
|
-
...current,
|
|
996
|
-
blocker_freshness_state: retryableState,
|
|
997
|
-
status_gate_history: (current.status_gate_history ?? []).map((entry) => entry.event === "blocker_stale_escalated"
|
|
998
|
-
&& entry.turn_id === turnId
|
|
999
|
-
&& entry.fingerprint === fingerprint
|
|
1000
|
-
? { ...entry, delivery_failed: true }
|
|
1001
|
-
: entry),
|
|
1002
|
-
};
|
|
1003
|
-
});
|
|
1004
|
-
}
|
|
1005
|
-
if (mesh !== null) {
|
|
1006
|
-
await emitMeshSoft("blocker_freshness_escalation_signal", logDecision, at, (signal) => emitLaneProgressEvent(mesh, lane, escalationText, signal));
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
return updated;
|
|
1010
|
-
}
|
|
1011
|
-
async function settleKrFreshness(hqRoot, lane, manifestTurnId, at, deps, logDecision) {
|
|
1012
|
-
if (manifestTurnId !== undefined && lane.status_gate !== undefined && lane.status_gate.turn_id !== manifestTurnId) {
|
|
1013
|
-
logDecision({ event: "kr_freshness_baseline_mismatch", at, turn_id: lane.status_gate.turn_id });
|
|
1014
|
-
return lane;
|
|
1015
|
-
}
|
|
1016
|
-
let errorLogged = false;
|
|
1017
|
-
const resolution = await resolveKrFreshnessForLane(hqRoot, lane, {
|
|
1018
|
-
readFlag: deps.readKrFreshnessFlag ?? readLanesRegistryFlag,
|
|
1019
|
-
now: Date.parse(at),
|
|
1020
|
-
onError: (error) => {
|
|
1021
|
-
if (errorLogged)
|
|
1022
|
-
return;
|
|
1023
|
-
errorLogged = true;
|
|
1024
|
-
const code = error instanceof LanesError ? error.error : "kr_freshness_unavailable";
|
|
1025
|
-
process.stderr.write(`hq lanes watcher: KR freshness disabled for ${lane.lane_id}: ${code}\n`);
|
|
1026
|
-
logDecision({ event: "kr_freshness_disabled", at, error: code });
|
|
1027
|
-
},
|
|
1028
|
-
});
|
|
1029
|
-
if (!resolution.enabled || resolution.statusPath === undefined || resolution.error !== undefined)
|
|
1030
|
-
return lane;
|
|
1031
|
-
const stalled = resolution.kpis.filter((kpi) => kpi.verdict === "stalled");
|
|
1032
|
-
if (stalled.length === 0) {
|
|
1033
|
-
if (lane.kr_freshness_state === undefined)
|
|
1034
|
-
return lane;
|
|
1035
|
-
return retryUpdateLane(hqRoot, lane.lane_id, "watcher", (current) => current.kr_freshness_state === undefined
|
|
1036
|
-
? current
|
|
1037
|
-
: { ...current, kr_freshness_state: undefined });
|
|
1038
|
-
}
|
|
1039
|
-
const fingerprint = krFreshnessFingerprint(resolution.stalledKeys);
|
|
1040
|
-
const names = stalled.map((kpi) => kpi.name);
|
|
1041
|
-
const turnId = manifestTurnId ?? lane.status_gate?.turn_id ?? `unknown-${at}`;
|
|
1042
|
-
const resumeText = `These KPIs in ${resolution.statusPath} were not re-measured in the last ${resolution.windowHours}h: ${names.join(", ")}. Measure each one now, update value and measured_at, then end the turn.`;
|
|
1043
|
-
return retryUpdateLane(hqRoot, lane.lane_id, "watcher", (current) => {
|
|
1044
|
-
const history = current.status_gate_history ?? [];
|
|
1045
|
-
if (history.some((entry) => entry.event === "kr_stalled" && entry.turn_id === turnId))
|
|
1046
|
-
return current;
|
|
1047
|
-
const sameStalledSet = current.kr_freshness_state?.stalled_fingerprint === fingerprint;
|
|
1048
|
-
let nextLane = current;
|
|
1049
|
-
let resumeQueued = false;
|
|
1050
|
-
let resumeSkippedReason;
|
|
1051
|
-
if (!sameStalledSet) {
|
|
1052
|
-
const mergeExisting = current.pending_resume !== undefined &&
|
|
1053
|
-
current.state !== "blocked" && current.state !== "awaiting_input";
|
|
1054
|
-
const resume = queueLaneTerminalResume(current, at, resumeText, { mergeExisting });
|
|
1055
|
-
nextLane = resume.lane;
|
|
1056
|
-
resumeQueued = resume.resumeQueued;
|
|
1057
|
-
resumeSkippedReason = resume.resumeSkippedReason;
|
|
1058
|
-
}
|
|
1059
|
-
const entry = {
|
|
1060
|
-
event: "kr_stalled",
|
|
1061
|
-
turn_id: turnId,
|
|
1062
|
-
path: resolution.statusPath,
|
|
1063
|
-
at,
|
|
1064
|
-
names,
|
|
1065
|
-
count: stalled.length,
|
|
1066
|
-
fingerprint,
|
|
1067
|
-
...(resumeQueued ? { resume_queued: true } : {}),
|
|
1068
|
-
...(resumeSkippedReason !== undefined ? { resume_skipped_reason: resumeSkippedReason } : {}),
|
|
1069
|
-
};
|
|
1070
|
-
return {
|
|
1071
|
-
...nextLane,
|
|
1072
|
-
kr_freshness_state: { stalled_fingerprint: fingerprint },
|
|
1073
|
-
status_gate_history: appendCappedHistory(history, entry),
|
|
1074
|
-
};
|
|
1075
|
-
});
|
|
1076
|
-
}
|
|
1077
|
-
function findBlockerFreshnessSessionInbox(hqRoot, sessionId) {
|
|
1078
|
-
let candidates;
|
|
1079
|
-
try {
|
|
1080
|
-
candidates = LANE_PROVIDERS
|
|
1081
|
-
.map((provider) => monitorSessionDir(hqRoot, provider, sessionId))
|
|
1082
|
-
.filter((sessionDir) => fs.existsSync(sessionDir));
|
|
1083
|
-
}
|
|
1084
|
-
catch (error) {
|
|
1085
|
-
return { reason: `Could not resolve the monitor inbox for session senior: ${error instanceof Error ? error.message : String(error)}` };
|
|
1086
|
-
}
|
|
1087
|
-
if (candidates.length !== 1) {
|
|
1088
|
-
return { reason: candidates.length === 0
|
|
1089
|
-
? "No monitor-session inbox is registered for the session senior"
|
|
1090
|
-
: "The session senior matches more than one monitor-session inbox" };
|
|
1091
|
-
}
|
|
1092
|
-
return { inbox: path.join(candidates[0], "dropbox") };
|
|
1093
|
-
}
|
|
1094
|
-
function resolveBlockerFreshnessSessionInbox(hqRoot, sessionId) {
|
|
1095
|
-
const result = findBlockerFreshnessSessionInbox(hqRoot, sessionId);
|
|
1096
|
-
if ("inbox" in result)
|
|
1097
|
-
return result.inbox;
|
|
1098
|
-
throw new LanesError("blocker_freshness_session_inbox_unavailable", result.reason, { session_id: sessionId });
|
|
1099
|
-
}
|
|
1100
|
-
/**
|
|
1101
|
-
* Wrap one mesh call. A failed emit is logged as `mesh_event_failed` with
|
|
1102
|
-
* the event name and the error message, and never propagates: the lane's
|
|
1103
|
-
* state is decided by the envelope and the process, not by the network.
|
|
1104
|
-
*/
|
|
1105
|
-
export async function emitMeshSoft(event, logDecision, at, fn, timeoutMs = 10_000) {
|
|
1106
|
-
let timer;
|
|
1107
|
-
try {
|
|
1108
|
-
const controller = new AbortController();
|
|
1109
|
-
await Promise.race([
|
|
1110
|
-
fn(controller.signal),
|
|
1111
|
-
new Promise((_resolve, reject) => {
|
|
1112
|
-
timer = setTimeout(() => { controller.abort(); reject(new Error(`mesh request timed out after ${timeoutMs}ms`)); }, timeoutMs);
|
|
1113
|
-
}),
|
|
1114
|
-
]);
|
|
1115
|
-
logDecision({ event: "mesh_event", at, mesh_event: event });
|
|
1116
|
-
return true;
|
|
1117
|
-
}
|
|
1118
|
-
catch (err) {
|
|
1119
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1120
|
-
logDecision({ event: "mesh_event_failed", at, mesh_event: event, error: message });
|
|
1121
|
-
process.stderr.write(`hq lanes watcher: mesh_event_failed ${event}: ${message}\n`);
|
|
1122
|
-
return false;
|
|
1123
|
-
}
|
|
1124
|
-
finally {
|
|
1125
|
-
if (timer)
|
|
1126
|
-
clearTimeout(timer);
|
|
1127
|
-
}
|
|
1128
|
-
}
|
|
1129
|
-
function readProgressIntervalMs(hqRoot, lane) {
|
|
1130
|
-
try {
|
|
1131
|
-
const config = loadLanesConfig(hqRoot);
|
|
1132
|
-
if (lane)
|
|
1133
|
-
return providerProgressEventMinIntervalMs(config.providers[lane.provider]);
|
|
1134
|
-
}
|
|
1135
|
-
catch (err) {
|
|
1136
|
-
process.stderr.write(`hq lanes watcher: lanes config unreadable, using default progress interval: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
1137
|
-
}
|
|
1138
|
-
return providerProgressEventMinIntervalMs({ max_running: 1, max_queued: 1, accounts: {} });
|
|
1139
|
-
}
|
|
1140
|
-
function readLaneSoft(hqRoot, laneId) {
|
|
1141
|
-
try {
|
|
1142
|
-
return readLane(hqRoot, laneId);
|
|
1143
|
-
}
|
|
1144
|
-
catch (err) {
|
|
1145
|
-
process.stderr.write(`hq lanes watcher: lane read failed for ${laneId}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
1146
|
-
return null;
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
/** Question ids an ask envelope raised, as `raiseEnvelopeQuestions` mints them. */
|
|
1150
|
-
export function envelopeQuestionIds(laneId, envelopeRef, envelope, questionIndexes) {
|
|
1151
|
-
return (envelope.questions ?? []).map((_q, i) => envelopeQuestionId(laneId, envelopeRef, questionIndexes?.[i] ?? i));
|
|
1152
|
-
}
|
|
1153
|
-
function defaultSleep(ms) {
|
|
1154
|
-
return new Promise((resolve) => {
|
|
1155
|
-
setTimeout(resolve, ms);
|
|
1156
|
-
});
|
|
1157
|
-
}
|
|
1158
|
-
const WATCHER_LOOP_OWNER_FILE = "watcher-loop.pid";
|
|
1159
|
-
function claimWatcherLoop(hqRoot, laneId, deps) {
|
|
1160
|
-
const runDir = laneRunDir(hqRoot, laneId);
|
|
1161
|
-
const ownerPath = path.join(runDir, WATCHER_LOOP_OWNER_FILE);
|
|
1162
|
-
const isAlive = deps.isAlive ?? isPidAlive;
|
|
1163
|
-
const readStarttime = deps.readStarttime ?? readProcStarttime;
|
|
1164
|
-
const owner = withLaneHireLocks(hqRoot, [laneId], () => {
|
|
1165
|
-
fs.mkdirSync(runDir, { recursive: true });
|
|
1166
|
-
const existing = readPidRecord(ownerPath);
|
|
1167
|
-
if (existing !== null && probePidRecord(existing, isAlive, readStarttime).alive) {
|
|
1168
|
-
return null;
|
|
1169
|
-
}
|
|
1170
|
-
if (existing !== null)
|
|
1171
|
-
fs.unlinkSync(ownerPath);
|
|
1172
|
-
writePidRecord(ownerPath, {
|
|
1173
|
-
pid: process.pid,
|
|
1174
|
-
starttime: readStarttime(process.pid) ?? readProcStarttime(process.pid) ?? undefined,
|
|
1175
|
-
});
|
|
1176
|
-
const stat = fs.statSync(ownerPath);
|
|
1177
|
-
return { dev: stat.dev, ino: stat.ino };
|
|
1178
|
-
});
|
|
1179
|
-
if (owner === null)
|
|
1180
|
-
return null;
|
|
1181
|
-
return () => {
|
|
1182
|
-
withLaneHireLocks(hqRoot, [laneId], () => {
|
|
1183
|
-
try {
|
|
1184
|
-
const current = fs.statSync(ownerPath);
|
|
1185
|
-
if (current.dev === owner.dev && current.ino === owner.ino)
|
|
1186
|
-
fs.unlinkSync(ownerPath);
|
|
1187
|
-
}
|
|
1188
|
-
catch (err) {
|
|
1189
|
-
if (err.code !== "ENOENT")
|
|
1190
|
-
throw err;
|
|
1191
|
-
}
|
|
1192
|
-
});
|
|
1193
|
-
};
|
|
1194
|
-
}
|
|
1195
|
-
async function maybeContinueAfterMissingEnvelopeRepair(hqRoot, lane, envelope, envelopeRef, at, deps, logDecision) {
|
|
1196
|
-
const repair = lane.envelope_repair;
|
|
1197
|
-
const missingRepair = repair?.attempted === true &&
|
|
1198
|
-
repair.succeeded === true &&
|
|
1199
|
-
repair.envelope_ref === envelopeRef &&
|
|
1200
|
-
repair.first_error.startsWith("Missing expected envelope file:");
|
|
1201
|
-
if (!missingRepair || envelope === null || envelopeRef === undefined) {
|
|
1202
|
-
if (envelope?.decision === "blocked")
|
|
1203
|
-
logDecision({ event: "envelope_repair_continue", at, outcome: "skipped", reason: "not_a_successful_missing_repair", missing_repair: missingRepair, envelope_ref: envelopeRef, repair_ref: repair?.envelope_ref, first_error: repair?.first_error, succeeded: repair?.succeeded });
|
|
1204
|
-
return lane;
|
|
1205
|
-
}
|
|
1206
|
-
const incompleteDone = envelope.decision === "done" && envelope.story_complete === false;
|
|
1207
|
-
const repairInstructionBlock = envelope.decision === "blocked" &&
|
|
1208
|
-
(envelope.questions?.length ?? 0) === 0 &&
|
|
1209
|
-
/(?:not to resume|do not resume|don't resume)(?: the)? task/i.test(envelope.blocked_reason ?? "");
|
|
1210
|
-
if (!incompleteDone && !repairInstructionBlock) {
|
|
1211
|
-
if (envelope.decision === "blocked")
|
|
1212
|
-
logDecision({ event: "envelope_repair_continue", at, outcome: "skipped", reason: "blocked_reason_not_repair_instruction", blocked_reason: envelope.blocked_reason });
|
|
1213
|
-
return lane;
|
|
1214
|
-
}
|
|
1215
|
-
if (lane.last_resume?.text === ENVELOPE_REPAIR_CONTINUE_TEXT) {
|
|
1216
|
-
logDecision({ event: "envelope_repair_continue", at, outcome: "skipped", reason: "continue_already_used" });
|
|
1217
|
-
return lane;
|
|
1218
|
-
}
|
|
1219
|
-
let pendingQuestion = false;
|
|
1220
|
-
try {
|
|
1221
|
-
for (const id of listQuestionIds(hqRoot)) {
|
|
1222
|
-
const question = readQuestion(hqRoot, id);
|
|
1223
|
-
if (question.lane_id === lane.lane_id && question.status === "pending") {
|
|
1224
|
-
pendingQuestion = true;
|
|
1225
|
-
break;
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
catch (error) {
|
|
1230
|
-
logDecision({ event: "envelope_repair_continue", at, outcome: "skipped", reason: "question_check_failed", error: error instanceof Error ? error.message : String(error) });
|
|
1231
|
-
return lane;
|
|
1232
|
-
}
|
|
1233
|
-
if (pendingQuestion) {
|
|
1234
|
-
logDecision({ event: "envelope_repair_continue", at, outcome: "skipped", reason: "pending_question" });
|
|
1235
|
-
return lane;
|
|
1236
|
-
}
|
|
1237
|
-
const readFlag = deps.readEnvelopeRepairContinueFlag ?? readLanesRegistryFlag;
|
|
1238
|
-
let enabled = false;
|
|
1239
|
-
try {
|
|
1240
|
-
const resolution = resolveLaneCompany(hqRoot, lane);
|
|
1241
|
-
const company = resolution.company ?? undefined;
|
|
1242
|
-
enabled = await readFlag(LANE_ENVELOPE_REPAIR_CONTINUE_FLAG, {
|
|
1243
|
-
company,
|
|
1244
|
-
...(company ? { companyUidSource: laneCompanyUidSource(resolution.source) } : {}),
|
|
1245
|
-
});
|
|
1246
|
-
}
|
|
1247
|
-
catch (error) {
|
|
1248
|
-
logDecision({ event: "envelope_repair_continue", at, outcome: "skipped", reason: "flag_read_failed", error: error instanceof Error ? error.message : String(error) });
|
|
1249
|
-
return lane;
|
|
1250
|
-
}
|
|
1251
|
-
if (!enabled)
|
|
1252
|
-
return lane;
|
|
1253
|
-
const queued = retryUpdateLane(hqRoot, lane.lane_id, "watcher", (current) => {
|
|
1254
|
-
if (current.version !== lane.version ||
|
|
1255
|
-
(current.state !== "done" && current.state !== "blocked") ||
|
|
1256
|
-
current.last_envelope?.ref !== envelopeRef ||
|
|
1257
|
-
current.envelope_repair?.attempted !== true ||
|
|
1258
|
-
current.envelope_repair.succeeded !== true ||
|
|
1259
|
-
current.envelope_repair.envelope_ref !== envelopeRef ||
|
|
1260
|
-
current.envelope_repair.first_error !== repair.first_error ||
|
|
1261
|
-
current.last_resume?.text === ENVELOPE_REPAIR_CONTINUE_TEXT)
|
|
1262
|
-
return current;
|
|
1263
|
-
const result = queueLaneTerminalResume(current, at, ENVELOPE_REPAIR_CONTINUE_TEXT, { allowBlockedResume: true });
|
|
1264
|
-
if (!result.resumeQueued || result.lane.pending_resume?.session_id === undefined)
|
|
1265
|
-
return current;
|
|
1266
|
-
return {
|
|
1267
|
-
...result.lane,
|
|
1268
|
-
status_gate_history: appendCappedHistory(current.status_gate_history ?? [], {
|
|
1269
|
-
event: "envelope_repair_continued",
|
|
1270
|
-
at,
|
|
1271
|
-
envelope_ref: envelopeRef,
|
|
1272
|
-
session_id: result.lane.pending_resume.session_id,
|
|
1273
|
-
}),
|
|
1274
|
-
};
|
|
1275
|
-
});
|
|
1276
|
-
if (queued.state === "queued" && queued.pending_resume?.text === ENVELOPE_REPAIR_CONTINUE_TEXT) {
|
|
1277
|
-
logDecision({ event: "envelope_repair_continue", at, outcome: "queued", envelope_ref: envelopeRef, session_id: queued.pending_resume.session_id });
|
|
1278
|
-
}
|
|
1279
|
-
return queued;
|
|
1280
|
-
}
|
|
1281
|
-
/** Detached and synchronous classifiers share one process-identity claim. */
|
|
1282
|
-
export async function runWatcherLoop(hqRoot, laneId, deps = {}) {
|
|
1283
|
-
const release = claimWatcherLoop(hqRoot, laneId, deps);
|
|
1284
|
-
if (release === null)
|
|
1285
|
-
return;
|
|
1286
|
-
try {
|
|
1287
|
-
await runWatcherLoopOwned(hqRoot, laneId, deps);
|
|
1288
|
-
}
|
|
1289
|
-
finally {
|
|
1290
|
-
release();
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
async function runWatcherLoopOwned(hqRoot, laneId, deps) {
|
|
1294
|
-
const sleep = deps.sleep ?? defaultSleep;
|
|
1295
|
-
const now = deps.now ?? (() => Date.now());
|
|
1296
|
-
const isAlive = deps.isAlive ?? isPidAlive;
|
|
1297
|
-
const readStarttime = deps.readStarttime ?? readProcStarttime;
|
|
1298
|
-
const pollMs = deps.pollMs ?? WATCHER_POLL_MS;
|
|
1299
|
-
const silenceMs = deps.silenceMs ?? SILENCE_MS;
|
|
1300
|
-
const runDir = laneRunDir(hqRoot, laneId);
|
|
1301
|
-
const outPath = deps.watcherOutPath ?? watcherOutLogPath(hqRoot, laneId);
|
|
1302
|
-
const outMax = deps.watcherOutMaxBytes ?? WATCHER_OUT_MAX_BYTES;
|
|
1303
|
-
const workerPidPath = path.join(runDir, "worker.pid");
|
|
1304
|
-
const manifest = readSpawnManifest(runDir);
|
|
1305
|
-
const minEnvelopeName = manifest.envelope_path !== undefined
|
|
1306
|
-
? path.basename(manifest.envelope_path)
|
|
1307
|
-
: undefined;
|
|
1308
|
-
const logFiles = [
|
|
1309
|
-
path.join(runDir, "logs", "worker.out"),
|
|
1310
|
-
path.join(runDir, "logs", "worker.err"),
|
|
1311
|
-
path.join(runDir, "logs", "last-message.txt"),
|
|
1312
|
-
];
|
|
1313
|
-
// Progress only. A tool call is not partial output: a worker that called
|
|
1314
|
-
// tools and exited with no envelope and no output is still `failed`.
|
|
1315
|
-
const activityFiles = [toolActivityPath(hqRoot, laneId)];
|
|
1316
|
-
const progressFiles = [...logFiles, ...activityFiles];
|
|
1317
|
-
// Mesh seam (spec §8). Non-interactive: nobody is at this process's tty.
|
|
1318
|
-
const mesh = deps.mesh === undefined ? createLaneMesh({ interactive: false }) : deps.mesh;
|
|
1319
|
-
const storyStatusSuppressions = new Set();
|
|
1320
|
-
const meshRequestTimeoutMs = deps.meshRequestTimeoutMs ?? 10_000;
|
|
1321
|
-
const initialLane = readLaneSoft(hqRoot, laneId);
|
|
1322
|
-
if (mesh && initialLane) {
|
|
1323
|
-
// Every armed watcher refreshes the server descriptor. This migrates old
|
|
1324
|
-
// shared project threads and picks up account/model changes after resume.
|
|
1325
|
-
const company = initialLane.mesh_company ?? initialLane.company ?? undefined;
|
|
1326
|
-
if (company &&
|
|
1327
|
-
(mesh.ensureLaneThread || !initialLane.thread_id || initialLane.thread_id.startsWith("proj-"))) {
|
|
1328
|
-
let timer;
|
|
1329
|
-
try {
|
|
1330
|
-
const threadId = await Promise.race([
|
|
1331
|
-
mesh.ensureLaneThread
|
|
1332
|
-
? mesh.ensureLaneThread(company, initialLane)
|
|
1333
|
-
: mesh.ensureProjectThread(company, initialLane.project_id),
|
|
1334
|
-
new Promise((_resolve, reject) => {
|
|
1335
|
-
timer = setTimeout(() => reject(new Error(`mesh thread ensure timed out after ${meshRequestTimeoutMs}ms`)), meshRequestTimeoutMs);
|
|
1336
|
-
}),
|
|
1337
|
-
]);
|
|
1338
|
-
const updated = initialLane.thread_id === threadId
|
|
1339
|
-
? initialLane
|
|
1340
|
-
: retryUpdateLane(hqRoot, laneId, "spawn", (current) => current.thread_id === threadId ? current : { ...current, thread_id: threadId });
|
|
1341
|
-
if (mesh.ensureLaneThread && updated.version !== initialLane.version) {
|
|
1342
|
-
await mesh.ensureLaneThread(company, updated);
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
catch (err) {
|
|
1346
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1347
|
-
if (company && initialLane.story_id &&
|
|
1348
|
-
err instanceof LanesError && err.error === "story_not_on_board") {
|
|
1349
|
-
storyStatusSuppressions.add(laneStoryStatusSuppressionKey(company, initialLane.project_id, initialLane.story_id));
|
|
1350
|
-
}
|
|
1351
|
-
process.stderr.write(`hq lanes watcher: mesh thread ensure failed for ${laneId}: ${message}\n`);
|
|
1352
|
-
writeWatcherDecision(outPath, {
|
|
1353
|
-
event: "mesh_event_failed",
|
|
1354
|
-
at: new Date(now()).toISOString(),
|
|
1355
|
-
mesh_event: "ensure_lane_thread",
|
|
1356
|
-
error: message,
|
|
1357
|
-
}, outMax);
|
|
1358
|
-
}
|
|
1359
|
-
finally {
|
|
1360
|
-
if (timer)
|
|
1361
|
-
clearTimeout(timer);
|
|
1362
|
-
}
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
const progressIntervalMs = deps.progressEventMinIntervalMs ?? readProgressIntervalMs(hqRoot, readLaneSoft(hqRoot, laneId));
|
|
1366
|
-
const spawnRepair = deps.spawnRepair ?? spawnEnvelopeRepair;
|
|
1367
|
-
const retryAccount = deps.retryAccount ?? ((root, lane, reason) => retryLaneOnAlternateAccount(root, lane, reason, "watcher", {
|
|
1368
|
-
...deps.retrySpawnDeps,
|
|
1369
|
-
admissionHost: (deps.resolveAdmissionHost ?? resolveProcessAdmissionHost)(),
|
|
1370
|
-
}));
|
|
1371
|
-
let lastSignalMs = now();
|
|
1372
|
-
let lastSeenMtime = 0;
|
|
1373
|
-
let lastAlive = null;
|
|
1374
|
-
let lastEnvelopeRef = null;
|
|
1375
|
-
let lastInvalidRef = null;
|
|
1376
|
-
let lastProgressEmitMs = (() => {
|
|
1377
|
-
const stamp = readLaneSoft(hqRoot, laneId)?.last_progress_event_at;
|
|
1378
|
-
return stamp ? Date.parse(stamp) || null : null;
|
|
1379
|
-
})();
|
|
1380
|
-
for (const file of progressFiles) {
|
|
1381
|
-
const mtime = fileMtimeMs(file);
|
|
1382
|
-
if (mtime !== null && mtime > lastSeenMtime)
|
|
1383
|
-
lastSeenMtime = mtime;
|
|
1384
|
-
}
|
|
1385
|
-
const logDecision = (record) => {
|
|
1386
|
-
writeWatcherDecision(outPath, record, outMax);
|
|
1387
|
-
};
|
|
1388
|
-
while (true) {
|
|
1389
|
-
const worker = readPidRecord(workerPidPath);
|
|
1390
|
-
const probe = worker === null
|
|
1391
|
-
? missingPidProbe()
|
|
1392
|
-
: probePidRecord(worker, isAlive, readStarttime);
|
|
1393
|
-
const processAlive = probe.alive;
|
|
1394
|
-
if (lastAlive !== false && !processAlive) {
|
|
1395
|
-
logDecision({
|
|
1396
|
-
event: "liveness_transition",
|
|
1397
|
-
at: new Date(now()).toISOString(),
|
|
1398
|
-
pid: probe.pid,
|
|
1399
|
-
recorded_starttime: probe.recorded_starttime,
|
|
1400
|
-
live_starttime: probe.live_starttime,
|
|
1401
|
-
starttime_error: probe.starttime_error,
|
|
1402
|
-
decided_by: probe.decided_by,
|
|
1403
|
-
});
|
|
1404
|
-
}
|
|
1405
|
-
lastAlive = processAlive;
|
|
1406
|
-
let progressed = false;
|
|
1407
|
-
let newestMtime = lastSeenMtime;
|
|
1408
|
-
let newestFile = null;
|
|
1409
|
-
for (const file of progressFiles) {
|
|
1410
|
-
const mtime = fileMtimeMs(file);
|
|
1411
|
-
if (mtime !== null && mtime > newestMtime) {
|
|
1412
|
-
newestMtime = mtime;
|
|
1413
|
-
newestFile = file;
|
|
1414
|
-
progressed = true;
|
|
1415
|
-
}
|
|
1416
|
-
}
|
|
1417
|
-
const latestFile = readLatestEnvelopeFile(hqRoot, laneId, minEnvelopeName);
|
|
1418
|
-
const missingExpected = !processAlive && latestFile === null
|
|
1419
|
-
? missingExpectedEnvelopeFile(hqRoot, laneId, manifest.envelope_path)
|
|
1420
|
-
: null;
|
|
1421
|
-
let latest = latestFile && latestFile.envelope
|
|
1422
|
-
? { envelope: latestFile.envelope, ref: latestFile.ref }
|
|
1423
|
-
: null;
|
|
1424
|
-
let envelope = latest?.envelope ?? null;
|
|
1425
|
-
if (latest && latest.ref !== lastEnvelopeRef) {
|
|
1426
|
-
logDecision({
|
|
1427
|
-
event: "envelope_observed",
|
|
1428
|
-
at: new Date(now()).toISOString(),
|
|
1429
|
-
filename: path.posix.basename(latest.ref),
|
|
1430
|
-
decision: latest.envelope.decision,
|
|
1431
|
-
});
|
|
1432
|
-
lastEnvelopeRef = latest.ref;
|
|
1433
|
-
}
|
|
1434
|
-
if (latestFile && latestFile.envelope === null && latestFile.ref !== lastInvalidRef) {
|
|
1435
|
-
logDecision({
|
|
1436
|
-
event: "envelope_invalid",
|
|
1437
|
-
at: new Date(now()).toISOString(),
|
|
1438
|
-
filename: path.posix.basename(latestFile.ref),
|
|
1439
|
-
error: latestFile.error,
|
|
1440
|
-
});
|
|
1441
|
-
lastInvalidRef = latestFile.ref;
|
|
1442
|
-
}
|
|
1443
|
-
const hasPartialOutput = logFiles.some((file) => fileNonEmpty(file));
|
|
1444
|
-
let quota = null;
|
|
1445
|
-
let quotaLane = null;
|
|
1446
|
-
let providerRejection = null;
|
|
1447
|
-
if (!processAlive && !envelope) {
|
|
1448
|
-
try {
|
|
1449
|
-
quotaLane = readLane(hqRoot, laneId);
|
|
1450
|
-
const workerOutOffset = readSpawnManifest(runDir).worker_out_offset ?? 0;
|
|
1451
|
-
const routed = routeProviderFailure(readProviderRejection(runDir, quotaLane.provider, workerOutOffset), () => readQuotaSignal(runDir, quotaLane.model, new Date(now()), workerOutOffset), quotaLane.model, new Date(now()));
|
|
1452
|
-
// A reset-less 429 still holds the account: the configured default
|
|
1453
|
-
// hold becomes its reset, recorded as such.
|
|
1454
|
-
quota = routed.quota
|
|
1455
|
-
? applyDefaultQuotaHold(routed.quota, providerQuotaDefaultHoldMs(loadLanesConfig(hqRoot).providers[quotaLane.provider]), new Date(now()))
|
|
1456
|
-
: null;
|
|
1457
|
-
providerRejection = routed.rejection;
|
|
1458
|
-
}
|
|
1459
|
-
catch (err) {
|
|
1460
|
-
process.stderr.write(`hq lanes watcher: quota read failed for ${laneId}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
|
-
// Spec §4: the process exited and its envelope file does not parse, or
|
|
1464
|
-
// this run never wrote its expected slot. Exactly one repair turn. A provider
|
|
1465
|
-
// rejection skips the repair: the provider refused the turn, so a
|
|
1466
|
-
// second turn on the same model and account would be refused the same
|
|
1467
|
-
// way. A quota signal skips it too: a repair turn on an exhausted account
|
|
1468
|
-
// fails the same way, and would end the lane envelope_invalid without
|
|
1469
|
-
// ever recording the hold. The quota result applies immediately instead.
|
|
1470
|
-
let invalidEnvelope = null;
|
|
1471
|
-
let envelopeValidationErrors;
|
|
1472
|
-
let questionIndexes;
|
|
1473
|
-
let recoveredAsk = false;
|
|
1474
|
-
const repairCandidate = latestFile ?? missingExpected;
|
|
1475
|
-
if (!processAlive &&
|
|
1476
|
-
!envelope &&
|
|
1477
|
-
!providerRejection &&
|
|
1478
|
-
!quota &&
|
|
1479
|
-
repairCandidate &&
|
|
1480
|
-
repairCandidate.envelope === null) {
|
|
1481
|
-
const lane = repairCandidate.missing ? readLaneSoft(hqRoot, laneId) : null;
|
|
1482
|
-
const missingRepairAllowed = !repairCandidate.missing ||
|
|
1483
|
-
(lane !== null && envelopeRepairMode(lane.provider) === "resume");
|
|
1484
|
-
if (missingRepairAllowed) {
|
|
1485
|
-
const repair = decideEnvelopeRepair(hqRoot, laneId, repairCandidate, now(), spawnRepair, logDecision);
|
|
1486
|
-
if (repair.action === "spawned") {
|
|
1487
|
-
lastSignalMs = now();
|
|
1488
|
-
lastAlive = null;
|
|
1489
|
-
if (progressed)
|
|
1490
|
-
lastSeenMtime = newestMtime;
|
|
1491
|
-
await sleep(pollMs);
|
|
1492
|
-
continue;
|
|
1493
|
-
}
|
|
1494
|
-
if (!repairCandidate.missing) {
|
|
1495
|
-
const validationErrors = repair.errors.filter((error) => !error.startsWith("repair_spawn_failed:"));
|
|
1496
|
-
let recovery = null;
|
|
1497
|
-
try {
|
|
1498
|
-
recovery = recoverAskEnvelope(repairCandidate.text, validationErrors);
|
|
1499
|
-
}
|
|
1500
|
-
catch (err) {
|
|
1501
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1502
|
-
process.stderr.write(`hq lanes watcher: invalid ask fallback could not parse ${repairCandidate.path} for ${laneId}: ${message}\n`);
|
|
1503
|
-
logDecision({
|
|
1504
|
-
event: "envelope_repair",
|
|
1505
|
-
at: new Date(now()).toISOString(),
|
|
1506
|
-
outcome: "ask_fallback_skipped",
|
|
1507
|
-
reason: "invalid_json",
|
|
1508
|
-
error: message,
|
|
1509
|
-
validation_errors: validationErrors,
|
|
1510
|
-
});
|
|
1511
|
-
}
|
|
1512
|
-
if (recovery !== null && (recovery.envelope.questions?.length ?? 0) > 0) {
|
|
1513
|
-
const errors = [...new Set([...validationErrors, ...recovery.questionErrors])];
|
|
1514
|
-
envelopeValidationErrors = errors;
|
|
1515
|
-
questionIndexes = recovery.questionIndexes;
|
|
1516
|
-
latest = { envelope: recovery.envelope, ref: repairCandidate.ref };
|
|
1517
|
-
envelope = recovery.envelope;
|
|
1518
|
-
recoveredAsk = true;
|
|
1519
|
-
const message = `hq lanes watcher: routing invalid ask envelope for ${laneId} to awaiting_input ` +
|
|
1520
|
-
`with ${recovery.envelope.questions?.length ?? 0} validated question(s); errors: ${errors.join("; ")}`;
|
|
1521
|
-
process.stderr.write(`${message}\n`);
|
|
1522
|
-
logDecision({
|
|
1523
|
-
event: "envelope_repair",
|
|
1524
|
-
at: new Date(now()).toISOString(),
|
|
1525
|
-
outcome: "ask_fallback_routed",
|
|
1526
|
-
state: "awaiting_input",
|
|
1527
|
-
questions: recovery.envelope.questions?.length ?? 0,
|
|
1528
|
-
validation_errors: errors,
|
|
1529
|
-
});
|
|
1530
|
-
}
|
|
1531
|
-
else {
|
|
1532
|
-
const errors = recovery === null
|
|
1533
|
-
? repair.errors
|
|
1534
|
-
: [...new Set([...repair.errors, ...recovery.questionErrors])];
|
|
1535
|
-
invalidEnvelope = { errors };
|
|
1536
|
-
if (recovery !== null) {
|
|
1537
|
-
const message = `hq lanes watcher: invalid ask envelope for ${laneId} remains failed; ` +
|
|
1538
|
-
`0 of ${recovery.sourceQuestionCount} questions passed validation`;
|
|
1539
|
-
process.stderr.write(`${message}\n`);
|
|
1540
|
-
logDecision({
|
|
1541
|
-
event: "envelope_repair",
|
|
1542
|
-
at: new Date(now()).toISOString(),
|
|
1543
|
-
outcome: "ask_fallback_skipped",
|
|
1544
|
-
reason: "no_valid_questions",
|
|
1545
|
-
questions: 0,
|
|
1546
|
-
validation_errors: errors,
|
|
1547
|
-
});
|
|
1548
|
-
}
|
|
1549
|
-
}
|
|
1550
|
-
}
|
|
1551
|
-
}
|
|
1552
|
-
}
|
|
1553
|
-
if (processAlive === false &&
|
|
1554
|
-
envelope &&
|
|
1555
|
-
latestFile !== null &&
|
|
1556
|
-
latestFile.envelope !== null &&
|
|
1557
|
-
!recoveredAsk) {
|
|
1558
|
-
markRepairSucceeded(hqRoot, laneId, latestFile.ref);
|
|
1559
|
-
}
|
|
1560
|
-
const tick = tickWatcher({
|
|
1561
|
-
nowMs: now(),
|
|
1562
|
-
processAlive,
|
|
1563
|
-
progressed,
|
|
1564
|
-
lastSignalMs,
|
|
1565
|
-
envelope,
|
|
1566
|
-
hasPartialOutput,
|
|
1567
|
-
silenceMs,
|
|
1568
|
-
quota,
|
|
1569
|
-
invalidEnvelope,
|
|
1570
|
-
providerRejection,
|
|
1571
|
-
});
|
|
1572
|
-
if (processAlive) {
|
|
1573
|
-
maybeHeartbeatLane(hqRoot, laneId, now());
|
|
1574
|
-
}
|
|
1575
|
-
if (tick.emit) {
|
|
1576
|
-
parseWatcherSignalKind(tick.emit.kind);
|
|
1577
|
-
appendWatcherSignal(hqRoot, laneId, tick.emit);
|
|
1578
|
-
if (tick.emit.kind === "silence") {
|
|
1579
|
-
const reason = stalledReason(silenceMs);
|
|
1580
|
-
logDecision({
|
|
1581
|
-
event: "silence_deadline",
|
|
1582
|
-
at: tick.emit.at,
|
|
1583
|
-
last_activity_at: new Date(lastSignalMs).toISOString(),
|
|
1584
|
-
last_tool_call_at: readToolActivity(hqRoot, laneId)?.at ?? null,
|
|
1585
|
-
deadline_at: new Date(lastSignalMs + silenceMs).toISOString(),
|
|
1586
|
-
silence_ms: silenceMs,
|
|
1587
|
-
stalled_reason: reason,
|
|
1588
|
-
});
|
|
1589
|
-
try {
|
|
1590
|
-
markLaneStalled(hqRoot, laneId, tick.emit.at, reason);
|
|
1591
|
-
}
|
|
1592
|
-
catch (err) {
|
|
1593
|
-
process.stderr.write(`hq lanes watcher: stalled write failed for ${laneId}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1596
|
-
if (tick.emit.kind === "progress") {
|
|
1597
|
-
const progressEmitAt = tick.emit.at;
|
|
1598
|
-
try {
|
|
1599
|
-
clearLaneStalled(hqRoot, laneId);
|
|
1600
|
-
}
|
|
1601
|
-
catch (err) {
|
|
1602
|
-
process.stderr.write(`hq lanes watcher: stalled clear failed for ${laneId}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
1603
|
-
}
|
|
1604
|
-
// Spec §8 progress row. Only a tick that saw new tool activity or
|
|
1605
|
-
// new output gets here (tickWatcher emits progress on `progressed`
|
|
1606
|
-
// alone), and the throttle bounds the rate. Silence never reaches
|
|
1607
|
-
// this branch.
|
|
1608
|
-
if (mesh &&
|
|
1609
|
-
progressEventDue({
|
|
1610
|
-
lastEmitMs: lastProgressEmitMs,
|
|
1611
|
-
nowMs: progressEmitAt ? Date.parse(progressEmitAt) : now(),
|
|
1612
|
-
minIntervalMs: progressIntervalMs,
|
|
1613
|
-
})) {
|
|
1614
|
-
const lane = readLaneSoft(hqRoot, laneId);
|
|
1615
|
-
if (lane) {
|
|
1616
|
-
const detail = newestFile && activityFiles.includes(newestFile)
|
|
1617
|
-
? `tool call at ${readToolActivity(hqRoot, laneId)?.at ?? tick.emit.at}`
|
|
1618
|
-
: envelope
|
|
1619
|
-
? "envelope written"
|
|
1620
|
-
: "new output";
|
|
1621
|
-
const sent = await emitMeshSoft("lane_progress", logDecision, progressEmitAt, (signal) => emitLaneProgressEvent(mesh, lane, detail, signal), meshRequestTimeoutMs);
|
|
1622
|
-
if (sent) {
|
|
1623
|
-
lastProgressEmitMs = Date.parse(progressEmitAt);
|
|
1624
|
-
try {
|
|
1625
|
-
retryUpdateLane(hqRoot, laneId, "watcher", (current) => ({
|
|
1626
|
-
...current,
|
|
1627
|
-
last_progress_event_at: progressEmitAt,
|
|
1628
|
-
}));
|
|
1629
|
-
}
|
|
1630
|
-
catch (err) {
|
|
1631
|
-
process.stderr.write(`hq lanes watcher: progress timestamp write failed for ${laneId}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
1632
|
-
}
|
|
1633
|
-
}
|
|
1634
|
-
}
|
|
1635
|
-
}
|
|
1636
|
-
}
|
|
1637
|
-
if (tick.emit.kind === "terminal" && tick.emit.classification) {
|
|
1638
|
-
const classification = tick.emit.classification;
|
|
1639
|
-
logDecision({
|
|
1640
|
-
event: "classification",
|
|
1641
|
-
at: tick.emit.at,
|
|
1642
|
-
envelope_present: envelope !== null,
|
|
1643
|
-
has_partial_output: hasPartialOutput,
|
|
1644
|
-
state: classification.state,
|
|
1645
|
-
reason: classification.reason,
|
|
1646
|
-
...(invalidEnvelope ? { envelope_errors: invalidEnvelope.errors } : {}),
|
|
1647
|
-
...(providerRejection
|
|
1648
|
-
? {
|
|
1649
|
-
provider_status: providerRejection.status,
|
|
1650
|
-
provider_message: providerRejection.message,
|
|
1651
|
-
retryable: providerRejection.retryable,
|
|
1652
|
-
}
|
|
1653
|
-
: {}),
|
|
1654
|
-
});
|
|
1655
|
-
if (quota &&
|
|
1656
|
-
quotaLane?.account &&
|
|
1657
|
-
(classification.reason === "account_quota_exhausted" ||
|
|
1658
|
-
classification.reason === "account_model_unavailable")) {
|
|
1659
|
-
try {
|
|
1660
|
-
recordAccountQuota(hqRoot, quotaLane.provider, quotaLane.account, quota);
|
|
1661
|
-
}
|
|
1662
|
-
catch (err) {
|
|
1663
|
-
process.stderr.write(`hq lanes watcher: quota record failed for ${laneId}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
1664
|
-
}
|
|
1665
|
-
}
|
|
1666
|
-
const accountRetryReason = quota
|
|
1667
|
-
? "usage_limit"
|
|
1668
|
-
: isProviderLoginFailure(providerRejection)
|
|
1669
|
-
? "account_unavailable"
|
|
1670
|
-
: null;
|
|
1671
|
-
if (accountRetryReason && quotaLane?.account) {
|
|
1672
|
-
try {
|
|
1673
|
-
const retry = await retryAccount(hqRoot, quotaLane, accountRetryReason);
|
|
1674
|
-
logDecision({
|
|
1675
|
-
event: "account_failover",
|
|
1676
|
-
at: tick.emit.at,
|
|
1677
|
-
from: quotaLane.account,
|
|
1678
|
-
to: retry.account ?? null,
|
|
1679
|
-
reason: accountRetryReason,
|
|
1680
|
-
state: retry.state,
|
|
1681
|
-
});
|
|
1682
|
-
return;
|
|
1683
|
-
}
|
|
1684
|
-
catch (err) {
|
|
1685
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1686
|
-
process.stderr.write(`hq lanes watcher: account failover failed for ${laneId}: ${message}\n`);
|
|
1687
|
-
logDecision({ event: "account_failover_failed", at: tick.emit.at, reason: accountRetryReason, error: message });
|
|
1688
|
-
if (readLane(hqRoot, laneId).state !== "running")
|
|
1689
|
-
return;
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
const terminalLane = readLane(hqRoot, laneId);
|
|
1693
|
-
let activeQuestionCompany;
|
|
1694
|
-
if (envelope?.decision === "ask" && (envelope.questions?.length ?? 0) > 0) {
|
|
1695
|
-
try {
|
|
1696
|
-
activeQuestionCompany = await resolveQuestionCompanyWithFallback(hqRoot, terminalLane, deps.resolveQuestionActiveCompany);
|
|
1697
|
-
}
|
|
1698
|
-
catch (err) {
|
|
1699
|
-
if (questionRecordRefusalCode(err) !== "question_company_unavailable")
|
|
1700
|
-
throw err;
|
|
1701
|
-
// applyTerminalToLaneWithResult repeats the synchronous lane and
|
|
1702
|
-
// project checks, then terminalizes this ask with the named refusal.
|
|
1703
|
-
}
|
|
1704
|
-
}
|
|
1705
|
-
const terminalWrite = applyTerminalToLaneWithResult(hqRoot, laneId, classification, {
|
|
1706
|
-
envelope,
|
|
1707
|
-
envelopeRef: latest?.ref,
|
|
1708
|
-
envelopeValidationErrors,
|
|
1709
|
-
questionIndexes,
|
|
1710
|
-
activeCompany: activeQuestionCompany,
|
|
1711
|
-
at: tick.emit.at,
|
|
1712
|
-
quota,
|
|
1713
|
-
providerRejection,
|
|
1714
|
-
});
|
|
1715
|
-
let updated = terminalWrite.record;
|
|
1716
|
-
const effectiveEnvelope = terminalWrite.envelope ?? envelope;
|
|
1717
|
-
const effectiveClassification = terminalWrite.classification;
|
|
1718
|
-
logDecision({
|
|
1719
|
-
event: "terminal_write",
|
|
1720
|
-
at: tick.emit.at,
|
|
1721
|
-
state: updated.state,
|
|
1722
|
-
version: updated.version,
|
|
1723
|
-
});
|
|
1724
|
-
// Spec §8 terminal rows: done → lane_done + story review; blocked →
|
|
1725
|
-
// lane_blocked; ask → lane_ask with the queue ids. After the state
|
|
1726
|
-
// write, so a mesh failure can never leave the lane unclassified.
|
|
1727
|
-
if (mesh && terminalWrite.changed) {
|
|
1728
|
-
const envelopeState = effectiveEnvelope?.decision === "ask"
|
|
1729
|
-
? "awaiting_input"
|
|
1730
|
-
: effectiveEnvelope?.decision === "blocked"
|
|
1731
|
-
? "blocked"
|
|
1732
|
-
: "done";
|
|
1733
|
-
if (effectiveEnvelope && latest && updated.state === envelopeState) {
|
|
1734
|
-
const questionIds = effectiveEnvelope.decision === "ask"
|
|
1735
|
-
? envelopeQuestionIds(laneId, latest.ref, effectiveEnvelope, questionIndexes)
|
|
1736
|
-
: undefined;
|
|
1737
|
-
await emitMeshSoft(`lane_${effectiveEnvelope.decision}`, logDecision, tick.emit.at, (signal) => emitLaneDecisionEvents(mesh, updated, effectiveEnvelope, {
|
|
1738
|
-
questionIds,
|
|
1739
|
-
signal,
|
|
1740
|
-
storyStatusSuppressions,
|
|
1741
|
-
}), meshRequestTimeoutMs);
|
|
1742
|
-
}
|
|
1743
|
-
else if (updated.state === "failed" || updated.state === "interrupted" ||
|
|
1744
|
-
updated.state === "cancelled" || updated.state === "killed") {
|
|
1745
|
-
await emitMeshSoft(`lane_${updated.state}`, logDecision, tick.emit.at, (signal) => emitLaneTerminalStateEvent(mesh, updated, updated.state, updated.terminal_reason ?? effectiveClassification.reason, signal), meshRequestTimeoutMs);
|
|
1746
|
-
}
|
|
1747
|
-
}
|
|
1748
|
-
if (terminalWrite.changed) {
|
|
1749
|
-
updated = await settleManagerStatusGate(hqRoot, updated, manifest.turn_id, tick.emit.at, deps, logDecision);
|
|
1750
|
-
updated = await settleBlockerFreshness(hqRoot, updated, manifest.turn_id, tick.emit.at, deps, mesh, logDecision);
|
|
1751
|
-
updated = await settleKrFreshness(hqRoot, updated, manifest.turn_id, tick.emit.at, deps, logDecision);
|
|
1752
|
-
}
|
|
1753
|
-
const managerStatusFollowupQueued = manifest.turn_id !== undefined &&
|
|
1754
|
-
updated.status_gate_history?.some((entry) => "turn_id" in entry && entry.turn_id === manifest.turn_id &&
|
|
1755
|
-
((entry.event === "status_missed" && entry.resume_queued === true) ||
|
|
1756
|
-
(entry.event === "blocker_stale" && entry.resume_queued === true) ||
|
|
1757
|
-
(entry.event === "kr_stalled" && entry.resume_queued === true))) === true;
|
|
1758
|
-
if (terminalWrite.changed && !managerStatusFollowupQueued) {
|
|
1759
|
-
updated = await maybeContinueAfterMissingEnvelopeRepair(hqRoot, updated, effectiveEnvelope ?? null, latest?.ref, tick.emit.at, deps, logDecision);
|
|
1760
|
-
}
|
|
1761
|
-
if (terminalWrite.changed && deps.recoverQueuedMessages && !managerStatusFollowupQueued && updated.state !== "queued") {
|
|
1762
|
-
try {
|
|
1763
|
-
if (dropboxInboxHasMessages(hqRoot, laneId)) {
|
|
1764
|
-
await deps.recoverQueuedMessages(updated);
|
|
1765
|
-
logDecision({
|
|
1766
|
-
event: "message_recovery",
|
|
1767
|
-
at: tick.emit.at,
|
|
1768
|
-
state: updated.state,
|
|
1769
|
-
});
|
|
1770
|
-
}
|
|
1771
|
-
}
|
|
1772
|
-
catch (err) {
|
|
1773
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1774
|
-
process.stderr.write(`hq lanes watcher: queued message recovery failed for ${laneId}: ${message}\n`);
|
|
1775
|
-
logDecision({
|
|
1776
|
-
event: "message_recovery_failed",
|
|
1777
|
-
at: tick.emit.at,
|
|
1778
|
-
state: updated.state,
|
|
1779
|
-
error: message,
|
|
1780
|
-
});
|
|
1781
|
-
}
|
|
1782
|
-
}
|
|
1783
|
-
const terminalEnvelopeNextCheckMs = effectiveEnvelope?.next_check_at === undefined
|
|
1784
|
-
? undefined
|
|
1785
|
-
: Date.parse(effectiveEnvelope.next_check_at);
|
|
1786
|
-
const hasFutureTerminalNextCheck = terminalEnvelopeNextCheckMs !== undefined &&
|
|
1787
|
-
Number.isFinite(terminalEnvelopeNextCheckMs) &&
|
|
1788
|
-
terminalEnvelopeNextCheckMs > Date.parse(tick.emit.at);
|
|
1789
|
-
if (terminalWrite.changed &&
|
|
1790
|
-
updated.keep_alive === true &&
|
|
1791
|
-
effectiveEnvelope?.decision === "done" &&
|
|
1792
|
-
effectiveEnvelope.story_complete === false &&
|
|
1793
|
-
!hasFutureTerminalNextCheck &&
|
|
1794
|
-
updated.state === "done" &&
|
|
1795
|
-
deps.scheduleTerminalKeepAlive) {
|
|
1796
|
-
try {
|
|
1797
|
-
const current = readLane(hqRoot, laneId);
|
|
1798
|
-
if (current.state === "done" && current.version === updated.version && current.keep_alive === true) {
|
|
1799
|
-
const resolution = resolveLaneCompany(hqRoot, current);
|
|
1800
|
-
const company = resolution.company;
|
|
1801
|
-
if (company) {
|
|
1802
|
-
const readFlag = deps.readTerminalKeepAliveFlag ?? readLanesRegistryFlag;
|
|
1803
|
-
const flagOptions = { company, companyUidSource: laneCompanyUidSource(resolution.source) };
|
|
1804
|
-
const [triggerEnabled, keepAliveEnabled, immediateFirstResumeEnabled] = await Promise.all([
|
|
1805
|
-
readFlag(LEAD_LANE_KEEP_ALIVE_TERMINAL_TRIGGER_FLAG, flagOptions),
|
|
1806
|
-
readFlag(LEAD_LANE_KEEP_ALIVE_FLAG, flagOptions),
|
|
1807
|
-
readFlag(LEAD_LANE_KEEP_ALIVE_IMMEDIATE_FIRST_RESUME_FLAG, flagOptions),
|
|
1808
|
-
]);
|
|
1809
|
-
if (triggerEnabled && keepAliveEnabled && immediateFirstResumeEnabled) {
|
|
1810
|
-
const terminalAtMs = Date.parse(tick.emit.at);
|
|
1811
|
-
const dueAt = new Date(terminalAtMs + LEAD_LANE_KEEP_ALIVE_FIRST_RESUME_SETTLE_MS).toISOString();
|
|
1812
|
-
logDecision({ event: "keep_alive_terminal_trigger_scheduled", at: tick.emit.at, due_at: dueAt, version: current.version });
|
|
1813
|
-
await deps.scheduleTerminalKeepAlive({
|
|
1814
|
-
laneId,
|
|
1815
|
-
version: current.version,
|
|
1816
|
-
terminalAt: tick.emit.at,
|
|
1817
|
-
dueAt,
|
|
1818
|
-
});
|
|
1819
|
-
}
|
|
1820
|
-
}
|
|
1821
|
-
}
|
|
1822
|
-
}
|
|
1823
|
-
catch (err) {
|
|
1824
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1825
|
-
process.stderr.write(`hq lanes watcher: terminal keep-alive trigger failed for ${laneId}: ${message}\n`);
|
|
1826
|
-
logDecision({ event: "keep_alive_terminal_trigger_failed", at: tick.emit.at, error: message });
|
|
1827
|
-
}
|
|
1828
|
-
}
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1831
|
-
lastSignalMs = tick.nextLastSignalMs;
|
|
1832
|
-
if (progressed)
|
|
1833
|
-
lastSeenMtime = newestMtime;
|
|
1834
|
-
if (tick.done)
|
|
1835
|
-
return;
|
|
1836
|
-
await sleep(pollMs);
|
|
1837
|
-
}
|
|
1838
|
-
}
|
|
1839
|
-
/**
|
|
1840
|
-
* Spec §4 repair decision for an exited worker whose expected envelope is
|
|
1841
|
-
* invalid or missing. First failure on a resumable provider with a recorded
|
|
1842
|
-
* session id: record `envelope_repair` on the lane and spawn the repair turn.
|
|
1843
|
-
* A provider with no session resume gets the same single attempt as a fresh
|
|
1844
|
-
* turn for invalid files. Anything else (already attempted, no session id on a
|
|
1845
|
-
* resumable provider, non-running lane, spawn failure): give up with every
|
|
1846
|
-
* error collected. The caller preserves the normal interrupted classification
|
|
1847
|
-
* for a missing file and uses `envelope_invalid` only for an existing invalid
|
|
1848
|
-
* file.
|
|
1849
|
-
*/
|
|
1850
|
-
export function decideEnvelopeRepair(hqRoot, laneId, latest, nowMs, spawnRepair, logDecision) {
|
|
1851
|
-
const at = new Date(nowMs).toISOString();
|
|
1852
|
-
const error = latest.error ?? "unparseable envelope";
|
|
1853
|
-
const lane = readLaneSoft(hqRoot, laneId);
|
|
1854
|
-
if (!lane)
|
|
1855
|
-
return { action: "give_up", errors: [error] };
|
|
1856
|
-
if (lane.state !== "running") {
|
|
1857
|
-
logDecision({ event: "envelope_repair", at, outcome: "skipped", skipped_reason: "lane_not_running", error });
|
|
1858
|
-
return { action: "give_up", errors: [error] };
|
|
1859
|
-
}
|
|
1860
|
-
const runDir = laneRunDir(hqRoot, laneId);
|
|
1861
|
-
const manifest = readSpawnManifest(runDir);
|
|
1862
|
-
const prior = lane.envelope_repair;
|
|
1863
|
-
// A resumed worker can write its next envelope before spawn.json points to
|
|
1864
|
-
// that slot. Use the newest of the manifest's expected slot and the actual
|
|
1865
|
-
// latest file so an older manifest or repair record cannot spend its budget.
|
|
1866
|
-
const expectedRef = missingExpectedEnvelopeFile(hqRoot, laneId, manifest.envelope_path)?.ref;
|
|
1867
|
-
const repairRef = expectedRef !== undefined && expectedRef > latest.ref ? expectedRef : latest.ref;
|
|
1868
|
-
const priorAppliesToSlot = prior?.envelope_ref === undefined || prior.envelope_ref === repairRef;
|
|
1869
|
-
if (prior && prior.attempted && priorAppliesToSlot) {
|
|
1870
|
-
// The repair turn ran and its output does not parse either.
|
|
1871
|
-
if (prior.attempted) {
|
|
1872
|
-
const errors = [prior.first_error, error];
|
|
1873
|
-
try {
|
|
1874
|
-
retryUpdateLane(hqRoot, laneId, "watcher", (current) => ({
|
|
1875
|
-
...current,
|
|
1876
|
-
envelope_repair: { ...prior, succeeded: false, second_error: error },
|
|
1877
|
-
}));
|
|
1878
|
-
}
|
|
1879
|
-
catch (err) {
|
|
1880
|
-
process.stderr.write(`hq lanes watcher: envelope_repair write failed for ${laneId}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
1881
|
-
}
|
|
1882
|
-
logDecision({ event: "envelope_repair", at, outcome: "second_failure", errors });
|
|
1883
|
-
return { action: "give_up", errors };
|
|
1884
|
-
}
|
|
1885
|
-
return { action: "give_up", errors: [prior.first_error] };
|
|
1886
|
-
}
|
|
1887
|
-
// `resume` (claude, codex) continues the recorded session; `fresh_turn`
|
|
1888
|
-
// (grok) runs the repair prompt as a new single turn on the same account.
|
|
1889
|
-
const mode = envelopeRepairMode(lane.provider);
|
|
1890
|
-
let skipped;
|
|
1891
|
-
let sessionId = null;
|
|
1892
|
-
if (mode === "resume") {
|
|
1893
|
-
const result = readWorkerRunResult(runDir, lane.provider, manifest.worker_out_offset ?? 0);
|
|
1894
|
-
sessionId = result?.session_id ??
|
|
1895
|
-
[manifest.resume?.session_id, lane.provider_session_id, lane.last_resume?.session_id].find((candidate) => typeof candidate === "string" && candidate.trim().length > 0) ?? null;
|
|
1896
|
-
if (sessionId === null)
|
|
1897
|
-
skipped = result?.session_id_absent ?? "no_result_json";
|
|
1898
|
-
}
|
|
1899
|
-
const base = {
|
|
1900
|
-
first_error: error,
|
|
1901
|
-
envelope_ref: repairRef,
|
|
1902
|
-
at,
|
|
1903
|
-
};
|
|
1904
|
-
if (skipped !== undefined) {
|
|
1905
|
-
writeRepairRecord(hqRoot, laneId, { ...base, attempted: false, succeeded: false, skipped_reason: skipped });
|
|
1906
|
-
logDecision({ event: "envelope_repair", at, outcome: "skipped", skipped_reason: skipped, error });
|
|
1907
|
-
return { action: "give_up", errors: [error] };
|
|
1908
|
-
}
|
|
1909
|
-
writeRepairRecord(hqRoot, laneId, { ...base, mode, attempted: true, succeeded: false });
|
|
1910
|
-
try {
|
|
1911
|
-
const pid = spawnRepair(hqRoot, lane, {
|
|
1912
|
-
session_id: sessionId,
|
|
1913
|
-
envelopePath: latest.path,
|
|
1914
|
-
error,
|
|
1915
|
-
rawText: latest.text,
|
|
1916
|
-
missing: latest.missing,
|
|
1917
|
-
onProfileWarning: (fields) => {
|
|
1918
|
-
logDecision({
|
|
1919
|
-
event: "envelope_repair",
|
|
1920
|
-
at,
|
|
1921
|
-
outcome: "profile_unknown_meta_fields_ignored",
|
|
1922
|
-
fields: [...fields],
|
|
1923
|
-
});
|
|
1924
|
-
},
|
|
1925
|
-
});
|
|
1926
|
-
logDecision({ event: "envelope_repair", at, outcome: "spawned", mode, pid, session_id: sessionId, error });
|
|
1927
|
-
return { action: "spawned" };
|
|
1928
|
-
}
|
|
1929
|
-
catch (err) {
|
|
1930
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1931
|
-
process.stderr.write(`hq lanes watcher: envelope repair spawn failed for ${laneId}: ${message}\n`);
|
|
1932
|
-
writeRepairRecord(hqRoot, laneId, {
|
|
1933
|
-
...base,
|
|
1934
|
-
mode,
|
|
1935
|
-
attempted: true,
|
|
1936
|
-
succeeded: false,
|
|
1937
|
-
second_error: `repair_spawn_failed: ${message}`,
|
|
1938
|
-
});
|
|
1939
|
-
logDecision({ event: "envelope_repair", at, outcome: "spawn_failed", error: message });
|
|
1940
|
-
return { action: "give_up", errors: [error, `repair_spawn_failed: ${message}`] };
|
|
1941
|
-
}
|
|
1942
|
-
}
|
|
1943
|
-
function writeRepairRecord(hqRoot, laneId, record) {
|
|
1944
|
-
retryUpdateLane(hqRoot, laneId, "watcher", (current) => ({ ...current, envelope_repair: record }));
|
|
1945
|
-
}
|
|
1946
|
-
/** The repair run wrote a parseable envelope: close the record as succeeded. */
|
|
1947
|
-
function markRepairSucceeded(hqRoot, laneId, _envelopeRef) {
|
|
1948
|
-
try {
|
|
1949
|
-
retryUpdateLane(hqRoot, laneId, "watcher", (current) => {
|
|
1950
|
-
const prior = current.envelope_repair;
|
|
1951
|
-
if (!prior || !prior.attempted || prior.succeeded) {
|
|
1952
|
-
return current;
|
|
1953
|
-
}
|
|
1954
|
-
return { ...current, envelope_repair: { ...prior, succeeded: true, second_error: undefined } };
|
|
1955
|
-
});
|
|
1956
|
-
}
|
|
1957
|
-
catch (err) {
|
|
1958
|
-
process.stderr.write(`hq lanes watcher: envelope_repair success write failed for ${laneId}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
1959
|
-
}
|
|
1960
|
-
}
|
|
1961
|
-
/** Exported so tests can prove the watcher reads the live record, not spawn env. */
|
|
1962
|
-
export function laneIdentityFromRecord(hqRoot, laneId) {
|
|
1963
|
-
const lane = readLane(hqRoot, laneId);
|
|
1964
|
-
return {
|
|
1965
|
-
lane_id: lane.lane_id,
|
|
1966
|
-
company: lane.company,
|
|
1967
|
-
project_id: lane.project_id,
|
|
1968
|
-
story_id: lane.story_id,
|
|
1969
|
-
senior: lane.senior,
|
|
1970
|
-
};
|
|
1971
|
-
}
|
|
1972
|
-
//# sourceMappingURL=watcher.js.map
|