@mclawnet/swarm 0.1.13 → 0.1.14
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/dist/__tests__/always-on-activity-reader.test.d.ts +2 -0
- package/dist/__tests__/always-on-activity-reader.test.d.ts.map +1 -0
- package/dist/__tests__/always-on-activity-reader.test.js +193 -0
- package/dist/__tests__/always-on-activity-reader.test.js.map +1 -0
- package/dist/__tests__/always-on-config.test.d.ts +2 -0
- package/dist/__tests__/always-on-config.test.d.ts.map +1 -0
- package/dist/__tests__/always-on-config.test.js +285 -0
- package/dist/__tests__/always-on-config.test.js.map +1 -0
- package/dist/__tests__/always-on-manager.test.d.ts +2 -0
- package/dist/__tests__/always-on-manager.test.d.ts.map +1 -0
- package/dist/__tests__/always-on-manager.test.js +797 -0
- package/dist/__tests__/always-on-manager.test.js.map +1 -0
- package/dist/__tests__/always-on-parity.test.d.ts +2 -0
- package/dist/__tests__/always-on-parity.test.d.ts.map +1 -0
- package/dist/__tests__/always-on-parity.test.js +20 -0
- package/dist/__tests__/always-on-parity.test.js.map +1 -0
- package/dist/__tests__/cascade-picker.test.d.ts +2 -0
- package/dist/__tests__/cascade-picker.test.d.ts.map +1 -0
- package/dist/__tests__/cascade-picker.test.js +122 -0
- package/dist/__tests__/cascade-picker.test.js.map +1 -0
- package/dist/__tests__/coordinator-shipment.test.d.ts +2 -0
- package/dist/__tests__/coordinator-shipment.test.d.ts.map +1 -0
- package/dist/__tests__/coordinator-shipment.test.js +280 -0
- package/dist/__tests__/coordinator-shipment.test.js.map +1 -0
- package/dist/__tests__/coordinator-workspace-recover.test.d.ts +2 -0
- package/dist/__tests__/coordinator-workspace-recover.test.d.ts.map +1 -0
- package/dist/__tests__/coordinator-workspace-recover.test.js +140 -0
- package/dist/__tests__/coordinator-workspace-recover.test.js.map +1 -0
- package/dist/__tests__/coordinator-workspace.test.d.ts +2 -0
- package/dist/__tests__/coordinator-workspace.test.d.ts.map +1 -0
- package/dist/__tests__/coordinator-workspace.test.js +135 -0
- package/dist/__tests__/coordinator-workspace.test.js.map +1 -0
- package/dist/__tests__/default-runner-epipe.test.d.ts +2 -0
- package/dist/__tests__/default-runner-epipe.test.d.ts.map +1 -0
- package/dist/__tests__/default-runner-epipe.test.js +43 -0
- package/dist/__tests__/default-runner-epipe.test.js.map +1 -0
- package/dist/__tests__/discovery-scheduler.test.d.ts +2 -0
- package/dist/__tests__/discovery-scheduler.test.d.ts.map +1 -0
- package/dist/__tests__/discovery-scheduler.test.js +367 -0
- package/dist/__tests__/discovery-scheduler.test.js.map +1 -0
- package/dist/__tests__/env-forward-e2e.test.d.ts +2 -0
- package/dist/__tests__/env-forward-e2e.test.d.ts.map +1 -0
- package/dist/__tests__/env-forward-e2e.test.js +57 -0
- package/dist/__tests__/env-forward-e2e.test.js.map +1 -0
- package/dist/__tests__/gh-pr-creator.test.d.ts +2 -0
- package/dist/__tests__/gh-pr-creator.test.d.ts.map +1 -0
- package/dist/__tests__/gh-pr-creator.test.js +107 -0
- package/dist/__tests__/gh-pr-creator.test.js.map +1 -0
- package/dist/__tests__/git-worktree-provider.test.d.ts +2 -0
- package/dist/__tests__/git-worktree-provider.test.d.ts.map +1 -0
- package/dist/__tests__/git-worktree-provider.test.js +98 -0
- package/dist/__tests__/git-worktree-provider.test.js.map +1 -0
- package/dist/__tests__/gitignore-check.test.d.ts +2 -0
- package/dist/__tests__/gitignore-check.test.d.ts.map +1 -0
- package/dist/__tests__/gitignore-check.test.js +39 -0
- package/dist/__tests__/gitignore-check.test.js.map +1 -0
- package/dist/__tests__/idea-research-source.test.d.ts +2 -0
- package/dist/__tests__/idea-research-source.test.d.ts.map +1 -0
- package/dist/__tests__/idea-research-source.test.js +425 -0
- package/dist/__tests__/idea-research-source.test.js.map +1 -0
- package/dist/__tests__/idea-todo-source.test.d.ts +2 -0
- package/dist/__tests__/idea-todo-source.test.d.ts.map +1 -0
- package/dist/__tests__/idea-todo-source.test.js +258 -0
- package/dist/__tests__/idea-todo-source.test.js.map +1 -0
- package/dist/__tests__/introspection-dedupe.test.d.ts +2 -0
- package/dist/__tests__/introspection-dedupe.test.d.ts.map +1 -0
- package/dist/__tests__/introspection-dedupe.test.js +484 -0
- package/dist/__tests__/introspection-dedupe.test.js.map +1 -0
- package/dist/__tests__/introspection-source.test.d.ts +2 -0
- package/dist/__tests__/introspection-source.test.d.ts.map +1 -0
- package/dist/__tests__/introspection-source.test.js +1051 -0
- package/dist/__tests__/introspection-source.test.js.map +1 -0
- package/dist/__tests__/migration-roles.test.js +1 -22
- package/dist/__tests__/migration-roles.test.js.map +1 -1
- package/dist/__tests__/reconcile-researching.test.d.ts +2 -0
- package/dist/__tests__/reconcile-researching.test.d.ts.map +1 -0
- package/dist/__tests__/reconcile-researching.test.js +224 -0
- package/dist/__tests__/reconcile-researching.test.js.map +1 -0
- package/dist/__tests__/role-loader-preamble-all.test.js +3 -1
- package/dist/__tests__/role-loader-preamble-all.test.js.map +1 -1
- package/dist/__tests__/role-loader.test.js +95 -0
- package/dist/__tests__/role-loader.test.js.map +1 -1
- package/dist/__tests__/role-prompt-no-legacy-protocol.test.js +3 -1
- package/dist/__tests__/role-prompt-no-legacy-protocol.test.js.map +1 -1
- package/dist/__tests__/secret-scrub.test.d.ts +2 -0
- package/dist/__tests__/secret-scrub.test.d.ts.map +1 -0
- package/dist/__tests__/secret-scrub.test.js +55 -0
- package/dist/__tests__/secret-scrub.test.js.map +1 -0
- package/dist/__tests__/shipment-actions.test.d.ts +2 -0
- package/dist/__tests__/shipment-actions.test.d.ts.map +1 -0
- package/dist/__tests__/shipment-actions.test.js +378 -0
- package/dist/__tests__/shipment-actions.test.js.map +1 -0
- package/dist/__tests__/shipment-persistence.test.d.ts +2 -0
- package/dist/__tests__/shipment-persistence.test.d.ts.map +1 -0
- package/dist/__tests__/shipment-persistence.test.js +120 -0
- package/dist/__tests__/shipment-persistence.test.js.map +1 -0
- package/dist/__tests__/shipment-pipeline.test.d.ts +2 -0
- package/dist/__tests__/shipment-pipeline.test.d.ts.map +1 -0
- package/dist/__tests__/shipment-pipeline.test.js +392 -0
- package/dist/__tests__/shipment-pipeline.test.js.map +1 -0
- package/dist/__tests__/shipment-report.test.d.ts +2 -0
- package/dist/__tests__/shipment-report.test.d.ts.map +1 -0
- package/dist/__tests__/shipment-report.test.js +78 -0
- package/dist/__tests__/shipment-report.test.js.map +1 -0
- package/dist/__tests__/shipment-stdin-integration.test.d.ts +2 -0
- package/dist/__tests__/shipment-stdin-integration.test.d.ts.map +1 -0
- package/dist/__tests__/shipment-stdin-integration.test.js +49 -0
- package/dist/__tests__/shipment-stdin-integration.test.js.map +1 -0
- package/dist/__tests__/shipment-type-parity.test.d.ts +2 -0
- package/dist/__tests__/shipment-type-parity.test.d.ts.map +1 -0
- package/dist/__tests__/shipment-type-parity.test.js +10 -0
- package/dist/__tests__/shipment-type-parity.test.js.map +1 -0
- package/dist/__tests__/snapshot-copy-provider.test.d.ts +2 -0
- package/dist/__tests__/snapshot-copy-provider.test.d.ts.map +1 -0
- package/dist/__tests__/snapshot-copy-provider.test.js +88 -0
- package/dist/__tests__/snapshot-copy-provider.test.js.map +1 -0
- package/dist/__tests__/swarm-coordinator-backend.test.js +153 -0
- package/dist/__tests__/swarm-coordinator-backend.test.js.map +1 -1
- package/dist/__tests__/swarm-coordinator-complete-intercept.test.d.ts +2 -0
- package/dist/__tests__/swarm-coordinator-complete-intercept.test.d.ts.map +1 -0
- package/dist/__tests__/swarm-coordinator-complete-intercept.test.js +111 -0
- package/dist/__tests__/swarm-coordinator-complete-intercept.test.js.map +1 -0
- package/dist/__tests__/task-store-source.test.d.ts +2 -0
- package/dist/__tests__/task-store-source.test.d.ts.map +1 -0
- package/dist/__tests__/task-store-source.test.js +56 -0
- package/dist/__tests__/task-store-source.test.js.map +1 -0
- package/dist/__tests__/transport-detect.test.d.ts +2 -0
- package/dist/__tests__/transport-detect.test.d.ts.map +1 -0
- package/dist/__tests__/transport-detect.test.js +92 -0
- package/dist/__tests__/transport-detect.test.js.map +1 -0
- package/dist/__tests__/workcycle-runner-cascade.test.d.ts +2 -0
- package/dist/__tests__/workcycle-runner-cascade.test.d.ts.map +1 -0
- package/dist/__tests__/workcycle-runner-cascade.test.js +203 -0
- package/dist/__tests__/workcycle-runner-cascade.test.js.map +1 -0
- package/dist/__tests__/workcycle-runner.test.d.ts +2 -0
- package/dist/__tests__/workcycle-runner.test.d.ts.map +1 -0
- package/dist/__tests__/workcycle-runner.test.js +369 -0
- package/dist/__tests__/workcycle-runner.test.js.map +1 -0
- package/dist/__tests__/workspace-diff.test.d.ts +2 -0
- package/dist/__tests__/workspace-diff.test.d.ts.map +1 -0
- package/dist/__tests__/workspace-diff.test.js +62 -0
- package/dist/__tests__/workspace-diff.test.js.map +1 -0
- package/dist/__tests__/workspace-manager.test.d.ts +2 -0
- package/dist/__tests__/workspace-manager.test.d.ts.map +1 -0
- package/dist/__tests__/workspace-manager.test.js +120 -0
- package/dist/__tests__/workspace-manager.test.js.map +1 -0
- package/dist/__tests__/workspace-types.test.d.ts +2 -0
- package/dist/__tests__/workspace-types.test.d.ts.map +1 -0
- package/dist/__tests__/workspace-types.test.js +37 -0
- package/dist/__tests__/workspace-types.test.js.map +1 -0
- package/dist/__tests__/worktree-gc.test.d.ts +2 -0
- package/dist/__tests__/worktree-gc.test.d.ts.map +1 -0
- package/dist/__tests__/worktree-gc.test.js +183 -0
- package/dist/__tests__/worktree-gc.test.js.map +1 -0
- package/dist/always-on/activity-reader.d.ts +27 -0
- package/dist/always-on/activity-reader.d.ts.map +1 -0
- package/dist/always-on/activity-reader.js +95 -0
- package/dist/always-on/activity-reader.js.map +1 -0
- package/dist/always-on/always-on-manager.d.ts +170 -0
- package/dist/always-on/always-on-manager.d.ts.map +1 -0
- package/dist/always-on/always-on-manager.js +538 -0
- package/dist/always-on/always-on-manager.js.map +1 -0
- package/dist/always-on/config.d.ts +141 -0
- package/dist/always-on/config.d.ts.map +1 -0
- package/dist/always-on/config.js +324 -0
- package/dist/always-on/config.js.map +1 -0
- package/dist/always-on/discovery-scheduler.d.ts +60 -0
- package/dist/always-on/discovery-scheduler.d.ts.map +1 -0
- package/dist/always-on/discovery-scheduler.js +287 -0
- package/dist/always-on/discovery-scheduler.js.map +1 -0
- package/dist/always-on/ideas-client.d.ts +23 -0
- package/dist/always-on/ideas-client.d.ts.map +1 -0
- package/dist/always-on/ideas-client.js +13 -0
- package/dist/always-on/ideas-client.js.map +1 -0
- package/dist/always-on/reconcile-researching.d.ts +42 -0
- package/dist/always-on/reconcile-researching.d.ts.map +1 -0
- package/dist/always-on/reconcile-researching.js +133 -0
- package/dist/always-on/reconcile-researching.js.map +1 -0
- package/dist/always-on/task-sources/cascade-picker.d.ts +42 -0
- package/dist/always-on/task-sources/cascade-picker.d.ts.map +1 -0
- package/dist/always-on/task-sources/cascade-picker.js +65 -0
- package/dist/always-on/task-sources/cascade-picker.js.map +1 -0
- package/dist/always-on/task-sources/idea-dedupe.d.ts +62 -0
- package/dist/always-on/task-sources/idea-dedupe.d.ts.map +1 -0
- package/dist/always-on/task-sources/idea-dedupe.js +130 -0
- package/dist/always-on/task-sources/idea-dedupe.js.map +1 -0
- package/dist/always-on/task-sources/idea-research-source.d.ts +46 -0
- package/dist/always-on/task-sources/idea-research-source.d.ts.map +1 -0
- package/dist/always-on/task-sources/idea-research-source.js +308 -0
- package/dist/always-on/task-sources/idea-research-source.js.map +1 -0
- package/dist/always-on/task-sources/idea-sort.d.ts +3 -0
- package/dist/always-on/task-sources/idea-sort.d.ts.map +1 -0
- package/dist/always-on/task-sources/idea-sort.js +25 -0
- package/dist/always-on/task-sources/idea-sort.js.map +1 -0
- package/dist/always-on/task-sources/idea-todo-source.d.ts +48 -0
- package/dist/always-on/task-sources/idea-todo-source.d.ts.map +1 -0
- package/dist/always-on/task-sources/idea-todo-source.js +226 -0
- package/dist/always-on/task-sources/idea-todo-source.js.map +1 -0
- package/dist/always-on/task-sources/introspection-source.d.ts +101 -0
- package/dist/always-on/task-sources/introspection-source.d.ts.map +1 -0
- package/dist/always-on/task-sources/introspection-source.js +695 -0
- package/dist/always-on/task-sources/introspection-source.js.map +1 -0
- package/dist/always-on/task-sources/task-store-source.d.ts +15 -0
- package/dist/always-on/task-sources/task-store-source.d.ts.map +1 -0
- package/dist/always-on/task-sources/task-store-source.js +59 -0
- package/dist/always-on/task-sources/task-store-source.js.map +1 -0
- package/dist/always-on/task-sources/types.d.ts +108 -0
- package/dist/always-on/task-sources/types.d.ts.map +1 -0
- package/dist/always-on/task-sources/types.js +13 -0
- package/dist/always-on/task-sources/types.js.map +1 -0
- package/dist/always-on/types.d.ts +76 -0
- package/dist/always-on/types.d.ts.map +1 -0
- package/dist/always-on/types.js +17 -0
- package/dist/always-on/types.js.map +1 -0
- package/dist/always-on/workcycle-runner.d.ts +115 -0
- package/dist/always-on/workcycle-runner.d.ts.map +1 -0
- package/dist/always-on/workcycle-runner.js +285 -0
- package/dist/always-on/workcycle-runner.js.map +1 -0
- package/dist/always-on/worktree-gc.d.ts +41 -0
- package/dist/always-on/worktree-gc.d.ts.map +1 -0
- package/dist/always-on/worktree-gc.js +167 -0
- package/dist/always-on/worktree-gc.js.map +1 -0
- package/dist/index.d.ts +26 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -1
- package/dist/index.js.map +1 -1
- package/dist/persistence.d.ts +37 -1
- package/dist/persistence.d.ts.map +1 -1
- package/dist/persistence.js +48 -0
- package/dist/persistence.js.map +1 -1
- package/dist/retrospective.d.ts.map +1 -1
- package/dist/retrospective.js +6 -0
- package/dist/retrospective.js.map +1 -1
- package/dist/roles/role-loader.d.ts +1 -1
- package/dist/roles/role-loader.d.ts.map +1 -1
- package/dist/roles/role-loader.js +18 -0
- package/dist/roles/role-loader.js.map +1 -1
- package/dist/roles/types.d.ts +12 -0
- package/dist/roles/types.d.ts.map +1 -1
- package/dist/shipment/gh-pr-creator.d.ts +28 -0
- package/dist/shipment/gh-pr-creator.d.ts.map +1 -0
- package/dist/shipment/gh-pr-creator.js +80 -0
- package/dist/shipment/gh-pr-creator.js.map +1 -0
- package/dist/shipment/report.d.ts +27 -0
- package/dist/shipment/report.d.ts.map +1 -0
- package/dist/shipment/report.js +41 -0
- package/dist/shipment/report.js.map +1 -0
- package/dist/shipment/secret-scrub.d.ts +12 -0
- package/dist/shipment/secret-scrub.d.ts.map +1 -0
- package/dist/shipment/secret-scrub.js +30 -0
- package/dist/shipment/secret-scrub.js.map +1 -0
- package/dist/shipment/shipment-actions.d.ts +85 -0
- package/dist/shipment/shipment-actions.d.ts.map +1 -0
- package/dist/shipment/shipment-actions.js +190 -0
- package/dist/shipment/shipment-actions.js.map +1 -0
- package/dist/shipment/shipment-pipeline.d.ts +48 -0
- package/dist/shipment/shipment-pipeline.d.ts.map +1 -0
- package/dist/shipment/shipment-pipeline.js +256 -0
- package/dist/shipment/shipment-pipeline.js.map +1 -0
- package/dist/shipment/transport-detect.d.ts +16 -0
- package/dist/shipment/transport-detect.d.ts.map +1 -0
- package/dist/shipment/transport-detect.js +54 -0
- package/dist/shipment/transport-detect.js.map +1 -0
- package/dist/shipment/workspace-diff.d.ts +39 -0
- package/dist/shipment/workspace-diff.d.ts.map +1 -0
- package/dist/shipment/workspace-diff.js +64 -0
- package/dist/shipment/workspace-diff.js.map +1 -0
- package/dist/swarm-coordinator.d.ts +20 -1
- package/dist/swarm-coordinator.d.ts.map +1 -1
- package/dist/swarm-coordinator.js +193 -10
- package/dist/swarm-coordinator.js.map +1 -1
- package/dist/types.d.ts +62 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/workspace/git-worktree-provider.d.ts +11 -0
- package/dist/workspace/git-worktree-provider.d.ts.map +1 -0
- package/dist/workspace/git-worktree-provider.js +123 -0
- package/dist/workspace/git-worktree-provider.js.map +1 -0
- package/dist/workspace/gitignore-check.d.ts +10 -0
- package/dist/workspace/gitignore-check.d.ts.map +1 -0
- package/dist/workspace/gitignore-check.js +25 -0
- package/dist/workspace/gitignore-check.js.map +1 -0
- package/dist/workspace/index.d.ts +5 -0
- package/dist/workspace/index.d.ts.map +1 -0
- package/dist/workspace/index.js +5 -0
- package/dist/workspace/index.js.map +1 -0
- package/dist/workspace/snapshot-copy-provider.d.ts +11 -0
- package/dist/workspace/snapshot-copy-provider.d.ts.map +1 -0
- package/dist/workspace/snapshot-copy-provider.js +66 -0
- package/dist/workspace/snapshot-copy-provider.js.map +1 -0
- package/dist/workspace/types.d.ts +36 -0
- package/dist/workspace/types.d.ts.map +1 -0
- package/dist/workspace/types.js +2 -0
- package/dist/workspace/types.js.map +1 -0
- package/dist/workspace/workspace-manager.d.ts +30 -0
- package/dist/workspace/workspace-manager.d.ts.map +1 -0
- package/dist/workspace/workspace-manager.js +104 -0
- package/dist/workspace/workspace-manager.js.map +1 -0
- package/package.json +4 -4
- package/roles/queen.md +1 -0
- package/templates/introspection.md +64 -0
- package/templates/research-only.md +58 -0
- package/roles/preset-analyst-simons.md +0 -39
- package/roles/preset-architect-knuth.md +0 -39
- package/roles/preset-designer-norman.md +0 -39
- package/roles/preset-designer.md +0 -39
- package/roles/preset-dev-carmack.md +0 -39
- package/roles/preset-dev-gosling.md +0 -39
- package/roles/preset-developer.md +0 -52
- package/roles/preset-manager-grove.md +0 -39
- package/roles/preset-manager-musk.md +0 -39
- package/roles/preset-pm.md +0 -78
- package/roles/preset-researcher-feynman.md +0 -39
- package/roles/preset-reviewer.md +0 -46
- package/roles/preset-strategist-buffett.md +0 -39
- package/roles/preset-strategist-munger.md +0 -39
- package/roles/preset-strategist-sunzi.md +0 -39
- package/roles/preset-tester-beck.md +0 -40
- package/roles/preset-tester.md +0 -47
- package/roles/preset-writer-orwell.md +0 -39
- package/roles/preset-writer.md +0 -39
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
// DiscoveryScheduler — M7.0 standalone primitive.
|
|
2
|
+
//
|
|
3
|
+
// Drives the always-on tick loop. Owns: tick timer, gate cascade, daily budget,
|
|
4
|
+
// ring buffer of recent decisions. Owns nothing about swarms / TaskStore /
|
|
5
|
+
// queens — that wiring lands in M7.1 via the `onFire` callback.
|
|
6
|
+
//
|
|
7
|
+
// Persistence: NONE in M7.0. Restart resets ticksToday and recentTicks.
|
|
8
|
+
// M7.3 will add a persisted view.
|
|
9
|
+
import { createLogger } from "@mclawnet/logger";
|
|
10
|
+
import { localYMD } from "./config.js";
|
|
11
|
+
import { DEFAULT_DAILY_BUDGET, DEFAULT_TICK_INTERVAL_MS, RECENT_TICKS_CAP, } from "./types.js";
|
|
12
|
+
const log = createLogger({ module: "swarm" });
|
|
13
|
+
export class DiscoveryScheduler {
|
|
14
|
+
mode;
|
|
15
|
+
tickIntervalOverride;
|
|
16
|
+
dailyBudget;
|
|
17
|
+
userGates;
|
|
18
|
+
onFire;
|
|
19
|
+
onTick;
|
|
20
|
+
now;
|
|
21
|
+
timer = null;
|
|
22
|
+
running = false;
|
|
23
|
+
halted = false;
|
|
24
|
+
inFlight = false; // a tick is currently executing
|
|
25
|
+
ticksToday = 0;
|
|
26
|
+
lastFireAt = null;
|
|
27
|
+
recentTicks = [];
|
|
28
|
+
nextTickAt = null;
|
|
29
|
+
constructor(opts) {
|
|
30
|
+
this.mode = opts.mode;
|
|
31
|
+
this.tickIntervalOverride = opts.tickInterval;
|
|
32
|
+
this.dailyBudget = opts.dailyBudget === undefined ? DEFAULT_DAILY_BUDGET : opts.dailyBudget;
|
|
33
|
+
this.userGates = opts.gates ?? [];
|
|
34
|
+
this.onFire = opts.onFire;
|
|
35
|
+
this.onTick = opts.onTick;
|
|
36
|
+
this.now = opts.now ?? (() => new Date());
|
|
37
|
+
}
|
|
38
|
+
/** Start the tick loop. No-op when mode === "off" or scheduler is halted. */
|
|
39
|
+
start() {
|
|
40
|
+
if (this.halted) {
|
|
41
|
+
log.warn("DiscoveryScheduler.start() called after halt — ignoring");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (this.running)
|
|
45
|
+
return;
|
|
46
|
+
this.running = true;
|
|
47
|
+
this.schedule();
|
|
48
|
+
}
|
|
49
|
+
/** Stop the tick loop. Idempotent. */
|
|
50
|
+
stop() {
|
|
51
|
+
this.running = false;
|
|
52
|
+
if (this.timer) {
|
|
53
|
+
clearTimeout(this.timer);
|
|
54
|
+
this.timer = null;
|
|
55
|
+
}
|
|
56
|
+
this.nextTickAt = null;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Manually trigger a tick. Bypasses the interval but still runs gates and
|
|
60
|
+
* still respects the in-flight guard: concurrent tickNow() calls reject the
|
|
61
|
+
* second caller with an Error so callers see deterministic semantics rather
|
|
62
|
+
* than silent overlap. Use `await` to serialise explicitly.
|
|
63
|
+
*/
|
|
64
|
+
async tickNow() {
|
|
65
|
+
if (this.inFlight) {
|
|
66
|
+
throw new Error("DiscoveryScheduler.tickNow: a tick is already in progress");
|
|
67
|
+
}
|
|
68
|
+
return this.runTick();
|
|
69
|
+
}
|
|
70
|
+
/** Change mode at runtime. Cancels current timer and reschedules. */
|
|
71
|
+
setMode(mode) {
|
|
72
|
+
if (this.mode === mode)
|
|
73
|
+
return;
|
|
74
|
+
this.mode = mode;
|
|
75
|
+
// S2 hotfix: previously bailed when `!running`, which forced applyConfig
|
|
76
|
+
// to call start() explicitly after every setMode("active"). Let schedule()
|
|
77
|
+
// make the decision — it already no-ops on `!running` and on mode === "off".
|
|
78
|
+
this.schedule();
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Change daily budget at runtime (M7.3). `null` removes the cap entirely.
|
|
82
|
+
* Does not retro-affect counters; if the new budget is lower than
|
|
83
|
+
* `ticksToday`, the next gate evaluation will simply skip.
|
|
84
|
+
*/
|
|
85
|
+
setDailyBudget(budget) {
|
|
86
|
+
this.dailyBudget = budget;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Restore counters from persisted state (M7.3 AlwaysOnManager). Must be
|
|
90
|
+
* called BEFORE start() — restoring into a running scheduler is undefined
|
|
91
|
+
* behaviour. `lastFireAt` may be null when no prior fire exists.
|
|
92
|
+
*/
|
|
93
|
+
restoreState(state) {
|
|
94
|
+
if (this.running) {
|
|
95
|
+
log.warn("DiscoveryScheduler.restoreState() called while running — ignoring");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (Number.isInteger(state.ticksToday) && state.ticksToday >= 0) {
|
|
99
|
+
this.ticksToday = state.ticksToday;
|
|
100
|
+
}
|
|
101
|
+
this.lastFireAt = state.lastFireAt;
|
|
102
|
+
}
|
|
103
|
+
snapshot() {
|
|
104
|
+
return {
|
|
105
|
+
mode: this.mode,
|
|
106
|
+
ticksToday: this.ticksToday,
|
|
107
|
+
lastFireAt: this.lastFireAt,
|
|
108
|
+
nextTickAt: this.nextTickAt,
|
|
109
|
+
recentTicks: [...this.recentTicks],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// ---- internal ---------------------------------------------------------
|
|
113
|
+
currentInterval() {
|
|
114
|
+
if (this.tickIntervalOverride !== undefined)
|
|
115
|
+
return this.tickIntervalOverride;
|
|
116
|
+
if (this.mode === "off")
|
|
117
|
+
return Number.POSITIVE_INFINITY;
|
|
118
|
+
return DEFAULT_TICK_INTERVAL_MS[this.mode];
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Arm the next-tick timer. No-op when not running, halted, or mode=off.
|
|
122
|
+
* Always clears any existing timer first — both setMode() and a manual
|
|
123
|
+
* tickNow() can race with the previously armed interval.
|
|
124
|
+
*/
|
|
125
|
+
schedule() {
|
|
126
|
+
if (this.timer) {
|
|
127
|
+
clearTimeout(this.timer);
|
|
128
|
+
this.timer = null;
|
|
129
|
+
}
|
|
130
|
+
if (!this.running || this.halted) {
|
|
131
|
+
this.nextTickAt = null;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (this.mode === "off") {
|
|
135
|
+
this.nextTickAt = null;
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const interval = this.currentInterval();
|
|
139
|
+
if (!Number.isFinite(interval)) {
|
|
140
|
+
this.nextTickAt = null;
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
this.nextTickAt = new Date(this.now().getTime() + interval);
|
|
144
|
+
const t = setTimeout(() => {
|
|
145
|
+
this.timer = null;
|
|
146
|
+
// runTick is async; we deliberately do not await — schedule() is called
|
|
147
|
+
// by runTick itself once the tick (and onFire) completes.
|
|
148
|
+
this.runTick().catch((err) => {
|
|
149
|
+
log.warn({ err }, "DiscoveryScheduler tick threw unexpectedly");
|
|
150
|
+
// Defensive: still reschedule so a single bad tick doesn't kill the loop.
|
|
151
|
+
if (this.running && !this.halted)
|
|
152
|
+
this.schedule();
|
|
153
|
+
});
|
|
154
|
+
}, interval);
|
|
155
|
+
// Don't keep the event loop alive purely for ticks (matches WakeupScheduler).
|
|
156
|
+
if (typeof t.unref === "function") {
|
|
157
|
+
t.unref();
|
|
158
|
+
}
|
|
159
|
+
this.timer = t;
|
|
160
|
+
}
|
|
161
|
+
async runTick() {
|
|
162
|
+
this.inFlight = true;
|
|
163
|
+
const startedAt = this.now();
|
|
164
|
+
const startedMs = startedAt.getTime();
|
|
165
|
+
try {
|
|
166
|
+
// Daily reset: if calendar date has changed since lastFireAt, zero today.
|
|
167
|
+
// S3 hotfix: localYMD shared with AlwaysOnManager (single source of
|
|
168
|
+
// truth for "today" — was `Date.toDateString()` before, which uses a
|
|
169
|
+
// human-readable English string and didn't match the YMD persisted on
|
|
170
|
+
// disk).
|
|
171
|
+
if (this.lastFireAt && localYMD(this.lastFireAt) !== localYMD(startedAt)) {
|
|
172
|
+
this.ticksToday = 0;
|
|
173
|
+
}
|
|
174
|
+
const ctx = {
|
|
175
|
+
now: startedAt,
|
|
176
|
+
mode: this.mode,
|
|
177
|
+
ticksToday: this.ticksToday,
|
|
178
|
+
lastFireAt: this.lastFireAt,
|
|
179
|
+
};
|
|
180
|
+
const breakdown = [];
|
|
181
|
+
const gates = this.buildGateChain();
|
|
182
|
+
let finalDecision = { kind: "fire" };
|
|
183
|
+
for (const g of gates) {
|
|
184
|
+
const decision = await g.evaluate(ctx);
|
|
185
|
+
breakdown.push({ gate: g.name, decision });
|
|
186
|
+
if (decision.kind !== "fire") {
|
|
187
|
+
finalDecision = decision;
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
let outcome;
|
|
192
|
+
if (finalDecision.kind === "halt") {
|
|
193
|
+
outcome = "halted";
|
|
194
|
+
this.halted = true;
|
|
195
|
+
this.stop();
|
|
196
|
+
}
|
|
197
|
+
else if (finalDecision.kind === "skip") {
|
|
198
|
+
outcome = "skipped";
|
|
199
|
+
}
|
|
200
|
+
else if (this.onFire) {
|
|
201
|
+
// I1 hotfix: increment budget AFTER onFire resolves. A rejected onFire
|
|
202
|
+
// (e.g. C1's "template not found", or any transient swarm spawn error)
|
|
203
|
+
// used to charge the daily quota AND lose the work — three quiet
|
|
204
|
+
// failures and the project was locked for the day. Now: a throw is
|
|
205
|
+
// recorded as a "skipped" decision so the next tick keeps trying.
|
|
206
|
+
try {
|
|
207
|
+
await this.onFire(ctx);
|
|
208
|
+
this.ticksToday += 1;
|
|
209
|
+
this.lastFireAt = startedAt;
|
|
210
|
+
outcome = "fired";
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
log.warn({ err }, "DiscoveryScheduler onFire rejected; not burning budget");
|
|
214
|
+
outcome = "skipped";
|
|
215
|
+
breakdown.push({
|
|
216
|
+
gate: "onFire",
|
|
217
|
+
decision: {
|
|
218
|
+
kind: "skip",
|
|
219
|
+
reason: `onFire threw: ${err instanceof Error ? err.message : String(err)}`,
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
// No onFire wired (M7.0 standalone primitive) — treat the gate-pass
|
|
226
|
+
// as a fire so existing counters / tests still observe ticksToday++.
|
|
227
|
+
this.ticksToday += 1;
|
|
228
|
+
this.lastFireAt = startedAt;
|
|
229
|
+
outcome = "fired";
|
|
230
|
+
}
|
|
231
|
+
const record = {
|
|
232
|
+
at: startedAt,
|
|
233
|
+
decision: outcome,
|
|
234
|
+
gateBreakdown: breakdown,
|
|
235
|
+
durationMs: this.now().getTime() - startedMs,
|
|
236
|
+
};
|
|
237
|
+
this.pushRecent(record);
|
|
238
|
+
if (this.onTick) {
|
|
239
|
+
try {
|
|
240
|
+
this.onTick(record);
|
|
241
|
+
}
|
|
242
|
+
catch (err) {
|
|
243
|
+
log.warn({ err }, "DiscoveryScheduler onTick observer threw; ignored");
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return record;
|
|
247
|
+
}
|
|
248
|
+
finally {
|
|
249
|
+
this.inFlight = false;
|
|
250
|
+
// Schedule the next tick unless we halted or were stopped.
|
|
251
|
+
if (this.running && !this.halted) {
|
|
252
|
+
this.schedule();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/** Built-ins (in order) then user gates. */
|
|
257
|
+
buildGateChain() {
|
|
258
|
+
const builtins = [
|
|
259
|
+
{
|
|
260
|
+
name: "modeNotOff",
|
|
261
|
+
evaluate: (c) => c.mode === "off"
|
|
262
|
+
? { kind: "skip", reason: "mode is off" }
|
|
263
|
+
: { kind: "fire" },
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
name: "dailyBudget",
|
|
267
|
+
evaluate: (c) => {
|
|
268
|
+
if (this.dailyBudget !== null && c.ticksToday >= this.dailyBudget) {
|
|
269
|
+
return {
|
|
270
|
+
kind: "skip",
|
|
271
|
+
reason: `daily budget exhausted (${c.ticksToday}/${this.dailyBudget})`,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
return { kind: "fire" };
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
];
|
|
278
|
+
return [...builtins, ...this.userGates];
|
|
279
|
+
}
|
|
280
|
+
pushRecent(record) {
|
|
281
|
+
this.recentTicks.push(record);
|
|
282
|
+
if (this.recentTicks.length > RECENT_TICKS_CAP) {
|
|
283
|
+
this.recentTicks.splice(0, this.recentTicks.length - RECENT_TICKS_CAP);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
//# sourceMappingURL=discovery-scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery-scheduler.js","sourceRoot":"","sources":["../../src/always-on/discovery-scheduler.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,EAAE;AACF,gFAAgF;AAChF,2EAA2E;AAC3E,gEAAgE;AAChE,EAAE;AACF,wEAAwE;AACxE,kCAAkC;AAElC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAEL,oBAAoB,EACpB,wBAAwB,EAMxB,gBAAgB,GAEjB,MAAM,YAAY,CAAC;AAEpB,MAAM,GAAG,GAAG,YAAY,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;AAE9C,MAAM,OAAO,kBAAkB;IACrB,IAAI,CAAe;IACnB,oBAAoB,CAAqB;IACzC,WAAW,CAAgB;IAC3B,SAAS,CAAS;IAClB,MAAM,CAAoD;IAC1D,MAAM,CAA6C;IACnD,GAAG,CAAa;IAEhB,KAAK,GAAyC,IAAI,CAAC;IACnD,OAAO,GAAG,KAAK,CAAC;IAChB,MAAM,GAAG,KAAK,CAAC;IACf,QAAQ,GAAG,KAAK,CAAC,CAAY,gCAAgC;IAE7D,UAAU,GAAG,CAAC,CAAC;IACf,UAAU,GAAgB,IAAI,CAAC;IAC/B,WAAW,GAAiB,EAAE,CAAC;IAC/B,UAAU,GAAgB,IAAI,CAAC;IAEvC,YAAY,IAA+B;QACzC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC,YAAY,CAAC;QAC9C,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC;QAC5F,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;QAClC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED,6EAA6E;IAC7E,KAAK;QACH,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,GAAG,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;YACpE,OAAO;QACT,CAAC;QACD,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,QAAQ,EAAE,CAAC;IAClB,CAAC;IAED,sCAAsC;IACtC,IAAI;QACF,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACzB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACpB,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;IACzB,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;QAC/E,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;IACxB,CAAC;IAED,qEAAqE;IACrE,OAAO,CAAC,IAAkB;QACxB,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI;YAAE,OAAO;QAC/B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,yEAAyE;QACzE,2EAA2E;QAC3E,6EAA6E;QAC7E,IAAI,CAAC,QAAQ,EAAE,CAAC;IAClB,CAAC;IAED;;;;OAIG;IACH,cAAc,CAAC,MAAqB;QAClC,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC;IAC5B,CAAC;IAED;;;;OAIG;IACH,YAAY,CAAC,KAAsD;QACjE,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,GAAG,CAAC,IAAI,CAAC,mEAAmE,CAAC,CAAC;YAC9E,OAAO;QACT,CAAC;QACD,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,UAAU,IAAI,CAAC,EAAE,CAAC;YAChE,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;QACrC,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;IACrC,CAAC;IAED,QAAQ;QACN,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,WAAW,EAAE,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC;SACnC,CAAC;IACJ,CAAC;IAED,0EAA0E;IAElE,eAAe;QACrB,IAAI,IAAI,CAAC,oBAAoB,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,oBAAoB,CAAC;QAC9E,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK;YAAE,OAAO,MAAM,CAAC,iBAAiB,CAAC;QACzD,OAAO,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7C,CAAC;IAED;;;;OAIG;IACK,QAAQ;QACd,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACzB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACpB,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACjC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;YACvB,OAAO;QACT,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YACxB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;YACvB,OAAO;QACT,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QACxC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;YACvB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,GAAG,QAAQ,CAAC,CAAC;QAC5D,MAAM,CAAC,GAAG,UAAU,CAAC,GAAG,EAAE;YACxB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAClB,wEAAwE;YACxE,0DAA0D;YAC1D,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;gBAC3B,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,4CAA4C,CAAC,CAAC;gBAChE,0EAA0E;gBAC1E,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM;oBAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpD,CAAC,CAAC,CAAC;QACL,CAAC,EAAE,QAAQ,CAAC,CAAC;QACb,8EAA8E;QAC9E,IAAI,OAAQ,CAA4B,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;YAC7D,CAA2B,CAAC,KAAK,EAAE,CAAC;QACvC,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;IACjB,CAAC;IAEO,KAAK,CAAC,OAAO;QACnB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC;QACtC,IAAI,CAAC;YACH,0EAA0E;YAC1E,oEAAoE;YACpE,qEAAqE;YACrE,sEAAsE;YACtE,SAAS;YACT,IAAI,IAAI,CAAC,UAAU,IAAI,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;gBACzE,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;YACtB,CAAC;YAED,MAAM,GAAG,GAAgB;gBACvB,GAAG,EAAE,SAAS;gBACd,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,UAAU,EAAE,IAAI,CAAC,UAAU;aAC5B,CAAC;YAEF,MAAM,SAAS,GAAoD,EAAE,CAAC;YACtE,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;YAEpC,IAAI,aAAa,GAAiB,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;YACnD,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;gBACtB,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;gBACvC,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;gBAC3C,IAAI,QAAQ,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;oBAC7B,aAAa,GAAG,QAAQ,CAAC;oBACzB,MAAM;gBACR,CAAC;YACH,CAAC;YAED,IAAI,OAA+B,CAAC;YACpC,IAAI,aAAa,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAClC,OAAO,GAAG,QAAQ,CAAC;gBACnB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;gBACnB,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,CAAC;iBAAM,IAAI,aAAa,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBACzC,OAAO,GAAG,SAAS,CAAC;YACtB,CAAC;iBAAM,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBACvB,uEAAuE;gBACvE,uEAAuE;gBACvE,iEAAiE;gBACjE,mEAAmE;gBACnE,kEAAkE;gBAClE,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;oBACvB,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;oBACrB,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;oBAC5B,OAAO,GAAG,OAAO,CAAC;gBACpB,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,wDAAwD,CAAC,CAAC;oBAC5E,OAAO,GAAG,SAAS,CAAC;oBACpB,SAAS,CAAC,IAAI,CAAC;wBACb,IAAI,EAAE,QAAQ;wBACd,QAAQ,EAAE;4BACR,IAAI,EAAE,MAAM;4BACZ,MAAM,EAAE,iBAAiB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;yBAC5E;qBACF,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,oEAAoE;gBACpE,qEAAqE;gBACrE,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;gBACrB,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;gBAC5B,OAAO,GAAG,OAAO,CAAC;YACpB,CAAC;YAED,MAAM,MAAM,GAAe;gBACzB,EAAE,EAAE,SAAS;gBACb,QAAQ,EAAE,OAAO;gBACjB,aAAa,EAAE,SAAS;gBACxB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,GAAG,SAAS;aAC7C,CAAC;YACF,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YACxB,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChB,IAAI,CAAC;oBAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBAAC,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACxC,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,mDAAmD,CAAC,CAAC;gBACzE,CAAC;YACH,CAAC;YAED,OAAO,MAAM,CAAC;QAChB,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;YACtB,2DAA2D;YAC3D,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACjC,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,CAAC;QACH,CAAC;IACH,CAAC;IAED,4CAA4C;IACpC,cAAc;QACpB,MAAM,QAAQ,GAAW;YACvB;gBACE,IAAI,EAAE,YAAY;gBAClB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CACd,CAAC,CAAC,IAAI,KAAK,KAAK;oBACd,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE;oBACzC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE;aACvB;YACD;gBACE,IAAI,EAAE,aAAa;gBACnB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE;oBACd,IAAI,IAAI,CAAC,WAAW,KAAK,IAAI,IAAI,CAAC,CAAC,UAAU,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;wBAClE,OAAO;4BACL,IAAI,EAAE,MAAM;4BACZ,MAAM,EAAE,2BAA2B,CAAC,CAAC,UAAU,IAAI,IAAI,CAAC,WAAW,GAAG;yBACvE,CAAC;oBACJ,CAAC;oBACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;gBAC1B,CAAC;aACF;SACF,CAAC;QACF,OAAO,CAAC,GAAG,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;IAC1C,CAAC;IAEO,UAAU,CAAC,MAAkB;QACnC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC9B,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,gBAAgB,EAAE,CAAC;YAC/C,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,gBAAgB,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Idea, IdeaStatus, IdeaPriority, UpdateIdeaInput, CreateIdeaInput, ListIdeasInput, ResearchRef, ResearchKind, ResearchConclusion } from "@mclawnet/shared";
|
|
2
|
+
export type { Idea, IdeaStatus, IdeaPriority, CreateIdeaInput, ResearchRef, ResearchKind, ResearchConclusion, };
|
|
3
|
+
/**
|
|
4
|
+
* Subset of UpdateIdeaInput the M8.0 IdeaTodoSource + M8.1 IdeaResearchSource
|
|
5
|
+
* need. M8.1 extends with researchReports (append-only array writeback) and
|
|
6
|
+
* researchKind (disambiguates the two researching flows; cleared back to null
|
|
7
|
+
* on failure-revert).
|
|
8
|
+
*/
|
|
9
|
+
export type IdeasPatch = Partial<Pick<UpdateIdeaInput, "status" | "priority" | "linkedSwarmTaskId" | "linkedSessionId" | "tags" | "title" | "body" | "researchReports" | "researchKind" | "shipFailureReason">>;
|
|
10
|
+
/** Subset of ListIdeasInput supported by the cascade source today. */
|
|
11
|
+
export type IdeasListFilter = Partial<Pick<ListIdeasInput, "status" | "priority" | "projectId" | "tag" | "q" | "limit">>;
|
|
12
|
+
export interface IdeasClient {
|
|
13
|
+
list(filter: IdeasListFilter): Promise<Idea[]>;
|
|
14
|
+
get(id: string): Promise<Idea | null>;
|
|
15
|
+
patch(id: string, partial: IdeasPatch): Promise<Idea>;
|
|
16
|
+
/**
|
|
17
|
+
* M8.2 — POST /api/ideas. Used by IntrospectionSource to write back
|
|
18
|
+
* agent-discovered ideas (status defaults to "idea" hub-side when omitted).
|
|
19
|
+
* Returns the persisted row so callers can grab the assigned id.
|
|
20
|
+
*/
|
|
21
|
+
create(input: CreateIdeaInput): Promise<Idea>;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=ideas-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ideas-client.d.ts","sourceRoot":"","sources":["../../src/always-on/ideas-client.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,IAAI,EACJ,UAAU,EACV,YAAY,EACZ,eAAe,EACf,eAAe,EACf,cAAc,EACd,WAAW,EACX,YAAY,EACZ,kBAAkB,EACnB,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,IAAI,EACJ,UAAU,EACV,YAAY,EACZ,eAAe,EACf,WAAW,EACX,YAAY,EACZ,kBAAkB,GACnB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,CAC9B,IAAI,CACF,eAAe,EACb,QAAQ,GACR,UAAU,GACV,mBAAmB,GACnB,iBAAiB,GACjB,MAAM,GACN,OAAO,GACP,MAAM,GACN,iBAAiB,GACjB,cAAc,GACd,mBAAmB,CACtB,CACF,CAAC;AAEF,sEAAsE;AACtE,MAAM,MAAM,eAAe,GAAG,OAAO,CACnC,IAAI,CAAC,cAAc,EAAE,QAAQ,GAAG,UAAU,GAAG,WAAW,GAAG,KAAK,GAAG,GAAG,GAAG,OAAO,CAAC,CAClF,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/C,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// M8.0 — IdeasClient interface.
|
|
2
|
+
//
|
|
3
|
+
// Decouples IdeaTodoSource from the actual HTTP transport: tests inject a
|
|
4
|
+
// stub, the agent injects an HTTP-backed implementation
|
|
5
|
+
// (`createIdeasRestClient` in @mclawnet/agent) that talks to hub's
|
|
6
|
+
// /api/ideas/* routes. Keeping the interface in @mclawnet/swarm avoids
|
|
7
|
+
// circular package deps (swarm doesn't depend on agent).
|
|
8
|
+
//
|
|
9
|
+
// Re-exports the canonical Idea / IdeaStatus / IdeaPriority shapes from
|
|
10
|
+
// @mclawnet/shared so callers don't have to remember which package they live
|
|
11
|
+
// in.
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=ideas-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ideas-client.js","sourceRoot":"","sources":["../../src/always-on/ideas-client.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,EAAE;AACF,0EAA0E;AAC1E,wDAAwD;AACxD,mEAAmE;AACnE,uEAAuE;AACvE,yDAAyD;AACzD,EAAE;AACF,wEAAwE;AACxE,6EAA6E;AAC7E,MAAM"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { IdeasClient } from "./ideas-client.js";
|
|
2
|
+
export interface ReconcileLogger {
|
|
3
|
+
warn?: (obj: Record<string, unknown>, msg?: string) => void;
|
|
4
|
+
info?: (obj: Record<string, unknown>, msg?: string) => void;
|
|
5
|
+
}
|
|
6
|
+
export interface ReconcileOptions {
|
|
7
|
+
ideasClient: IdeasClient;
|
|
8
|
+
/** Returns the list of projectRoots the agent currently has wired. */
|
|
9
|
+
getProjectRoots: () => string[];
|
|
10
|
+
/**
|
|
11
|
+
* Returns the swarmIds currently recoverable on disk (recovery.json
|
|
12
|
+
* still present). The check is per-call — we don't cache so this runs
|
|
13
|
+
* after any inflight recovery on the swarm side.
|
|
14
|
+
*/
|
|
15
|
+
listRecoverableSwarmIds: () => Array<{
|
|
16
|
+
swarmId: string;
|
|
17
|
+
workDir: string;
|
|
18
|
+
}>;
|
|
19
|
+
/** Optional logger. Defaults to silent. */
|
|
20
|
+
log?: ReconcileLogger;
|
|
21
|
+
/** Max ideas to list per project on the boot scan. Default 100. */
|
|
22
|
+
perProjectLimit?: number;
|
|
23
|
+
}
|
|
24
|
+
export interface ReconcileReport {
|
|
25
|
+
/** Projects scanned (post-encodeCwd failures excluded). */
|
|
26
|
+
projectsScanned: number;
|
|
27
|
+
/** Ideas observed at status=researching across all scanned projects. */
|
|
28
|
+
candidatesFound: number;
|
|
29
|
+
/** Ideas that were reverted (orphans whose linked swarm is gone). */
|
|
30
|
+
reverted: number;
|
|
31
|
+
/** Ideas left alone because their linked swarm still has recovery.json on disk. */
|
|
32
|
+
preserved: number;
|
|
33
|
+
/** Errors encountered during the sweep (best-effort; not re-thrown). */
|
|
34
|
+
errors: string[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* One-shot reconciliation. Designed to be awaited (or fire-and-forget) at
|
|
38
|
+
* the end of startAgent() after AlwaysOnManager has booted. Returns a
|
|
39
|
+
* report for logging — no exceptions cross the boundary.
|
|
40
|
+
*/
|
|
41
|
+
export declare function reconcileOrphanedResearching(opts: ReconcileOptions): Promise<ReconcileReport>;
|
|
42
|
+
//# sourceMappingURL=reconcile-researching.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconcile-researching.d.ts","sourceRoot":"","sources":["../../src/always-on/reconcile-researching.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,mBAAmB,CAAC;AAIjE,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5D,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7D;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,WAAW,CAAC;IACzB,sEAAsE;IACtE,eAAe,EAAE,MAAM,MAAM,EAAE,CAAC;IAChC;;;;OAIG;IACH,uBAAuB,EAAE,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3E,2CAA2C;IAC3C,GAAG,CAAC,EAAE,eAAe,CAAC;IACtB,mEAAmE;IACnE,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,eAAe,EAAE,MAAM,CAAC;IACxB,wEAAwE;IACxE,eAAe,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,SAAS,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AASD;;;;GAIG;AACH,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,eAAe,CAAC,CA0G1B"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// M8 hotfix I6 — orphaned-researching reconciliation.
|
|
2
|
+
//
|
|
3
|
+
// If the agent dies between IdeaTodoSource.onClaim / IdeaResearchSource.onClaim
|
|
4
|
+
// (which PATCH the idea to status=researching + linkedSwarmTaskId=<swarmId>)
|
|
5
|
+
// and the runner's terminal-state callback, the idea is left dangling at
|
|
6
|
+
// status=researching forever. No code path on the boot side noticed.
|
|
7
|
+
//
|
|
8
|
+
// This module exposes a one-shot reconciliation that the agent runs once,
|
|
9
|
+
// right after AlwaysOnManager.start() has booted all projects. It walks
|
|
10
|
+
// every still-researching idea, decides whether its linked swarm is still
|
|
11
|
+
// alive, and reverts the orphans:
|
|
12
|
+
// - researchKind="execute" → back to status="todo"
|
|
13
|
+
// - researchKind="investigate" → back to status="idea"
|
|
14
|
+
// - missing/unknown kind → defaults to "idea" (the safer revert)
|
|
15
|
+
// and clears `linkedSwarmTaskId` + `researchKind` on the way.
|
|
16
|
+
//
|
|
17
|
+
// We intentionally do NOT try to resume the cycle — the swarm process is
|
|
18
|
+
// gone and the most useful thing for the user is to see the idea back in
|
|
19
|
+
// the inbox where they can re-trigger it manually.
|
|
20
|
+
//
|
|
21
|
+
// Best-effort: hub failures, individual idea failures, and per-project
|
|
22
|
+
// scan failures all log a warn and continue. The agent must boot even when
|
|
23
|
+
// the hub is briefly unreachable.
|
|
24
|
+
import { encodeCwd } from "@mclawnet/shared";
|
|
25
|
+
/** Map a researching idea back to its pre-claim status. */
|
|
26
|
+
function revertStatus(idea) {
|
|
27
|
+
if (idea.researchKind === "execute")
|
|
28
|
+
return "todo";
|
|
29
|
+
// "investigate" or null/undefined → safest revert is "idea".
|
|
30
|
+
return "idea";
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* One-shot reconciliation. Designed to be awaited (or fire-and-forget) at
|
|
34
|
+
* the end of startAgent() after AlwaysOnManager has booted. Returns a
|
|
35
|
+
* report for logging — no exceptions cross the boundary.
|
|
36
|
+
*/
|
|
37
|
+
export async function reconcileOrphanedResearching(opts) {
|
|
38
|
+
const { ideasClient, getProjectRoots, listRecoverableSwarmIds, log, perProjectLimit = 100 } = opts;
|
|
39
|
+
const report = {
|
|
40
|
+
projectsScanned: 0,
|
|
41
|
+
candidatesFound: 0,
|
|
42
|
+
reverted: 0,
|
|
43
|
+
preserved: 0,
|
|
44
|
+
errors: [],
|
|
45
|
+
};
|
|
46
|
+
let aliveSwarmIds;
|
|
47
|
+
try {
|
|
48
|
+
aliveSwarmIds = new Set(listRecoverableSwarmIds().map((s) => s.swarmId));
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
52
|
+
log?.warn?.({ err: msg }, "reconcile: failed to list recoverable swarms — skipping sweep");
|
|
53
|
+
report.errors.push(`listRecoverableSwarmIds: ${msg}`);
|
|
54
|
+
return report;
|
|
55
|
+
}
|
|
56
|
+
let projectRoots;
|
|
57
|
+
try {
|
|
58
|
+
projectRoots = getProjectRoots();
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
62
|
+
log?.warn?.({ err: msg }, "reconcile: getProjectRoots threw — skipping sweep");
|
|
63
|
+
report.errors.push(`getProjectRoots: ${msg}`);
|
|
64
|
+
return report;
|
|
65
|
+
}
|
|
66
|
+
for (const projectRoot of projectRoots) {
|
|
67
|
+
let projectId;
|
|
68
|
+
try {
|
|
69
|
+
projectId = encodeCwd(projectRoot);
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
73
|
+
log?.warn?.({ err: msg, projectRoot }, "reconcile: encodeCwd failed — skipping project");
|
|
74
|
+
report.errors.push(`encodeCwd(${projectRoot}): ${msg}`);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
report.projectsScanned += 1;
|
|
78
|
+
let researching;
|
|
79
|
+
try {
|
|
80
|
+
researching = await ideasClient.list({
|
|
81
|
+
status: "researching",
|
|
82
|
+
projectId,
|
|
83
|
+
limit: perProjectLimit,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
88
|
+
log?.warn?.({ err: msg, projectId }, "reconcile: list(status=researching) failed");
|
|
89
|
+
report.errors.push(`list(${projectId}): ${msg}`);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
for (const idea of researching) {
|
|
93
|
+
report.candidatesFound += 1;
|
|
94
|
+
const swarmId = idea.linkedSwarmTaskId;
|
|
95
|
+
if (swarmId && aliveSwarmIds.has(swarmId)) {
|
|
96
|
+
// Linked swarm is still recoverable on disk — leave alone; the
|
|
97
|
+
// normal flow (or a manual recovery) will catch up.
|
|
98
|
+
report.preserved += 1;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const patch = {
|
|
102
|
+
status: revertStatus(idea),
|
|
103
|
+
researchKind: null,
|
|
104
|
+
linkedSwarmTaskId: null,
|
|
105
|
+
};
|
|
106
|
+
try {
|
|
107
|
+
await ideasClient.patch(idea.id, patch);
|
|
108
|
+
report.reverted += 1;
|
|
109
|
+
log?.info?.({
|
|
110
|
+
ideaId: idea.id,
|
|
111
|
+
projectId,
|
|
112
|
+
prevStatus: "researching",
|
|
113
|
+
nextStatus: patch.status,
|
|
114
|
+
prevSwarmId: swarmId ?? null,
|
|
115
|
+
}, "reconcile: reverted orphaned researching idea");
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
119
|
+
log?.warn?.({ err: msg, ideaId: idea.id, projectId }, "reconcile: PATCH revert failed (will retry on next boot)");
|
|
120
|
+
report.errors.push(`patch(${idea.id}): ${msg}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
log?.info?.({
|
|
125
|
+
projectsScanned: report.projectsScanned,
|
|
126
|
+
candidatesFound: report.candidatesFound,
|
|
127
|
+
reverted: report.reverted,
|
|
128
|
+
preserved: report.preserved,
|
|
129
|
+
errorCount: report.errors.length,
|
|
130
|
+
}, "reconcile: sweep complete");
|
|
131
|
+
return report;
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=reconcile-researching.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconcile-researching.js","sourceRoot":"","sources":["../../src/always-on/reconcile-researching.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { TaskSource, TaskRef } from "./types.js";
|
|
2
|
+
export interface CascadePickerLogger {
|
|
3
|
+
warn?: (obj: Record<string, unknown>, msg?: string) => void;
|
|
4
|
+
info?: (obj: Record<string, unknown>, msg?: string) => void;
|
|
5
|
+
debug?: (obj: Record<string, unknown>, msg?: string) => void;
|
|
6
|
+
}
|
|
7
|
+
export interface CascadePickResult {
|
|
8
|
+
taskRef: TaskRef;
|
|
9
|
+
source: TaskSource;
|
|
10
|
+
}
|
|
11
|
+
export interface CascadePicker {
|
|
12
|
+
/** Try each source in order and return the first hit, or null. */
|
|
13
|
+
pick(projectRoot: string): Promise<CascadePickResult | null>;
|
|
14
|
+
/**
|
|
15
|
+
* Convenience adapter that drops the source reference — matches the
|
|
16
|
+
* legacy `pickNextTask: (root) => Promise<TaskRef | null>` shape used by
|
|
17
|
+
* M7 callers (e.g. AlwaysOnManager constructor option). The cascade
|
|
18
|
+
* picker remembers the last hit internally so the runner's terminal
|
|
19
|
+
* hooks can still resolve it.
|
|
20
|
+
*/
|
|
21
|
+
pickNextTask(projectRoot: string): Promise<TaskRef | null>;
|
|
22
|
+
/**
|
|
23
|
+
* Returns the source that produced the given TaskRef in the most recent
|
|
24
|
+
* `pick()` / `pickNextTask()` call. Used by the runner to resolve hooks
|
|
25
|
+
* after a swarm reaches terminal state. Returns null when the ref is
|
|
26
|
+
* unknown (e.g. the runner is using the legacy single-source DI path).
|
|
27
|
+
*/
|
|
28
|
+
resolveSource(taskRef: TaskRef): TaskSource | null;
|
|
29
|
+
}
|
|
30
|
+
export interface CascadePickerOptions {
|
|
31
|
+
/**
|
|
32
|
+
* Hotfix C1 — per-pick source filter. Called for every source before
|
|
33
|
+
* `source.pick()`; returning false skips the source for this tick (it is
|
|
34
|
+
* still resolvable via `resolveSource` for outstanding refs). The filter
|
|
35
|
+
* is consulted fresh on every pick, so toggling a source on/off in the
|
|
36
|
+
* always-on config takes effect without rebuilding the cascade. Default:
|
|
37
|
+
* every source enabled.
|
|
38
|
+
*/
|
|
39
|
+
filter?: (source: TaskSource, projectRoot: string) => boolean;
|
|
40
|
+
}
|
|
41
|
+
export declare function createCascadePicker(sources: TaskSource[], log?: CascadePickerLogger, options?: CascadePickerOptions): CascadePicker;
|
|
42
|
+
//# sourceMappingURL=cascade-picker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cascade-picker.d.ts","sourceRoot":"","sources":["../../../src/always-on/task-sources/cascade-picker.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5D,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5D,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9D;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,kEAAkE;IAClE,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IAC7D;;;;;;OAMG;IACH,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAC3D;;;;;OAKG;IACH,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI,CAAC;CACpD;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC;CAC/D;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,UAAU,EAAE,EACrB,GAAG,CAAC,EAAE,mBAAmB,EACzB,OAAO,GAAE,oBAAyB,GACjC,aAAa,CA+Df"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// M8.0 — cascade-picker: tries TaskSource instances in order and returns the
|
|
2
|
+
// first hit along with the source that produced it. The runner needs the
|
|
3
|
+
// source reference so it can later call onClaim / onSuccess / onFailure
|
|
4
|
+
// hooks against the same instance that picked the task.
|
|
5
|
+
//
|
|
6
|
+
// Resilient by design: if a source's `pick()` throws we log a warn and try
|
|
7
|
+
// the next source. A single source crashing must not kill the whole cascade
|
|
8
|
+
// or the always-on cycle — silent fall-through is preferable to a stuck loop.
|
|
9
|
+
export function createCascadePicker(sources, log, options = {}) {
|
|
10
|
+
const sourceFilter = options.filter;
|
|
11
|
+
// Tracks which source produced each outstanding TaskRef (by reference id).
|
|
12
|
+
// Cleared lazily — entries live until overwritten or the process restarts;
|
|
13
|
+
// memory pressure is negligible because the cascade runs one pick per cycle.
|
|
14
|
+
const sourceByRefId = new Map();
|
|
15
|
+
async function pick(projectRoot) {
|
|
16
|
+
for (const source of sources) {
|
|
17
|
+
if (sourceFilter && !sourceFilter(source, projectRoot)) {
|
|
18
|
+
log?.debug?.({ source: source.name, projectRoot }, "cascade-picker: source disabled by filter — skipping");
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
let result = null;
|
|
22
|
+
try {
|
|
23
|
+
result = await source.pick(projectRoot);
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
log?.warn?.({
|
|
27
|
+
err: err instanceof Error ? err.message : String(err),
|
|
28
|
+
source: source.name,
|
|
29
|
+
projectRoot,
|
|
30
|
+
}, "cascade-picker: source threw — trying next");
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (result) {
|
|
34
|
+
sourceByRefId.set(result.id, source);
|
|
35
|
+
log?.debug?.({ source: source.name, taskId: result.id, projectRoot }, "cascade-picker: hit");
|
|
36
|
+
return { taskRef: result, source };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
pick,
|
|
43
|
+
async pickNextTask(projectRoot) {
|
|
44
|
+
const hit = await pick(projectRoot);
|
|
45
|
+
return hit ? hit.taskRef : null;
|
|
46
|
+
},
|
|
47
|
+
resolveSource(taskRef) {
|
|
48
|
+
// S6 hotfix — prefer matching by `kind` (the explicit TaskSourceKind
|
|
49
|
+
// union member), then fall back to `name` for sources that haven't
|
|
50
|
+
// adopted the `kind` field yet, and finally to the per-id map.
|
|
51
|
+
// resolveSource is called by the runner after a swarm reaches
|
|
52
|
+
// terminal state, so we need both signals to be authoritative.
|
|
53
|
+
if (taskRef.sourceKind) {
|
|
54
|
+
const byKind = sources.find((s) => s.kind === taskRef.sourceKind);
|
|
55
|
+
if (byKind)
|
|
56
|
+
return byKind;
|
|
57
|
+
const byName = sources.find((s) => s.name === taskRef.sourceKind);
|
|
58
|
+
if (byName)
|
|
59
|
+
return byName;
|
|
60
|
+
}
|
|
61
|
+
return sourceByRefId.get(taskRef.id) ?? null;
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=cascade-picker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cascade-picker.js","sourceRoot":"","sources":["../../../src/always-on/task-sources/cascade-picker.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,yEAAyE;AACzE,wEAAwE;AACxE,wDAAwD;AACxD,EAAE;AACF,2EAA2E;AAC3E,4EAA4E;AAC5E,8EAA8E;AA+C9E,MAAM,UAAU,mBAAmB,CACjC,OAAqB,EACrB,GAAyB,EACzB,UAAgC,EAAE;IAElC,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IACpC,2EAA2E;IAC3E,2EAA2E;IAC3E,6EAA6E;IAC7E,MAAM,aAAa,GAAG,IAAI,GAAG,EAAsB,CAAC;IAEpD,KAAK,UAAU,IAAI,CAAC,WAAmB;QACrC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,YAAY,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE,CAAC;gBACvD,GAAG,EAAE,KAAK,EAAE,CACV,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,EACpC,sDAAsD,CACvD,CAAC;gBACF,SAAS;YACX,CAAC;YACD,IAAI,MAAM,GAAmB,IAAI,CAAC;YAClC,IAAI,CAAC;gBACH,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC1C,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,GAAG,EAAE,IAAI,EAAE,CACT;oBACE,GAAG,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;oBACrD,MAAM,EAAE,MAAM,CAAC,IAAI;oBACnB,WAAW;iBACZ,EACD,4CAA4C,CAC7C,CAAC;gBACF,SAAS;YACX,CAAC;YACD,IAAI,MAAM,EAAE,CAAC;gBACX,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;gBACrC,GAAG,EAAE,KAAK,EAAE,CACV,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,EACvD,qBAAqB,CACtB,CAAC;gBACF,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;YACrC,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,IAAI;QACJ,KAAK,CAAC,YAAY,CAAC,WAAmB;YACpC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC;YACpC,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;QAClC,CAAC;QACD,aAAa,CAAC,OAAgB;YAC5B,qEAAqE;YACrE,mEAAmE;YACnE,+DAA+D;YAC/D,8DAA8D;YAC9D,+DAA+D;YAC/D,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;gBACvB,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;gBAClE,IAAI,MAAM;oBAAE,OAAO,MAAM,CAAC;gBAC1B,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;gBAClE,IAAI,MAAM;oBAAE,OAAO,MAAM,CAAC;YAC5B,CAAC;YACD,OAAO,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC;QAC/C,CAAC;KACF,CAAC;AACJ,CAAC"}
|