@hasna/loops 0.4.10 → 0.4.12
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 +93 -0
- package/README.md +19 -2
- package/dist/api/index.js +1 -1
- package/dist/cli/index.js +317 -39
- package/dist/daemon/daemon.d.ts +18 -0
- package/dist/daemon/index.js +84 -15
- package/dist/index.js +131 -21
- package/dist/lib/mode.js +1 -1
- package/dist/lib/route/pr-review.d.ts +16 -0
- package/dist/lib/route/profile-pool.d.ts +60 -0
- package/dist/lib/route/throttle.d.ts +1 -0
- package/dist/lib/route/types.d.ts +2 -0
- package/dist/lib/scheduler.d.ts +13 -0
- package/dist/lib/storage/index.js +40 -5
- package/dist/lib/storage/postgres-schema.js +4 -0
- package/dist/lib/storage/postgres.js +4 -0
- package/dist/lib/storage/sqlite.js +36 -5
- package/dist/lib/store.d.ts +11 -0
- package/dist/lib/store.js +36 -5
- package/dist/mcp/index.js +50 -6
- package/dist/runner/index.js +1 -1
- package/dist/sdk/index.js +50 -6
- package/dist/types.d.ts +10 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,99 @@ documented in this file. Version entries are generated from the
|
|
|
5
5
|
conventional-commit git history; one commit maps to one released patch version
|
|
6
6
|
unless noted.
|
|
7
7
|
|
|
8
|
+
## 0.4.12 (2026-07-05)
|
|
9
|
+
|
|
10
|
+
Drain throughput: `--max-active` is now a per-route ceiling instead of a
|
|
11
|
+
store-wide one, drain dispatch spreads across subscription accounts by live load,
|
|
12
|
+
and every step records the account it ran on.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **Route admission — per-route `--max-active`:** the global admission count was
|
|
17
|
+
store-wide, so the busiest router's `--max-active` became a shared ceiling that
|
|
18
|
+
throttled every other router (average concurrency ~1.9 while the agent lane
|
|
19
|
+
allowed 12; ~253 items/24h deferred as `global active workflow limit reached`).
|
|
20
|
+
The global count is now scoped to the route/drain that set the limit; scope
|
|
21
|
+
precedence is an explicit `--max-active-scope <key>` first, then the running
|
|
22
|
+
loop's `LOOPS_LOOP_NAME`, then the route key as the final fallback — so each
|
|
23
|
+
router's `--max-active` is its own ceiling.
|
|
24
|
+
Existing routers get correct scoping with no config change. `project` and
|
|
25
|
+
`project_group` counts are unchanged: they remain cross-route anti-hog caps.
|
|
26
|
+
- **Per-account attribution:** Codewith agent steps carry their subscription
|
|
27
|
+
account in `authProfile`, not an `AccountRef`, so `workflow_step_runs.account_profile`
|
|
28
|
+
was NULL for every drain worker and per-account burn could not be attributed.
|
|
29
|
+
The resolved auth profile (and provider as `account_tool`) is now recorded on
|
|
30
|
+
each step at run creation, and the per-role assignment is surfaced in drain
|
|
31
|
+
reports.
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- **Least-loaded auth-profile pool selection + `--max-per-profile`:** pooled
|
|
36
|
+
Codewith dispatch picked a member by a pure hash of the work item, which stacked
|
|
37
|
+
several concurrent workers on one account at high concurrency and tripped
|
|
38
|
+
provider-side 429/stream-drop limits. Live drains now count running steps per
|
|
39
|
+
account and place each route on the least-loaded pool member (verifier/planner
|
|
40
|
+
still land on a different account than the worker; the hash is the deterministic
|
|
41
|
+
tie-break, so a cold store reproduces the prior assignment). New
|
|
42
|
+
`--max-per-profile <K>` (default 2 for pools of two or more, `0` disables) defers
|
|
43
|
+
a route when every pool account already has `K` running steps.
|
|
44
|
+
|
|
45
|
+
Schema: additive sqlite migration `0008_work_item_route_scope` (nullable
|
|
46
|
+
`workflow_work_items.route_scope`; its index is created only by the migration —
|
|
47
|
+
never in the baseline DDL, which re-runs on every open and would crash pre-0008
|
|
48
|
+
databases). `SCHEMA_USER_VERSION` is intentionally not bumped so an older binary
|
|
49
|
+
can still open a database this migration touched (rollback-safe). Postgres gets
|
|
50
|
+
the equivalent additive migration `0004_work_item_route_scope`; the released,
|
|
51
|
+
checksummed `0002_workflows_goals` block is untouched so existing postgres
|
|
52
|
+
ledgers keep verifying. Upgrade coverage: a real pre-0008 (0.4.11 schema)
|
|
53
|
+
fixture database must open cleanly, gain the column + index, and keep its data;
|
|
54
|
+
released postgres migration checksums are pinned in tests.
|
|
55
|
+
|
|
56
|
+
## 0.4.11 (2026-07-05)
|
|
57
|
+
|
|
58
|
+
Scheduler fairness and PR-route hygiene: fast command loops no longer starve
|
|
59
|
+
behind long agent workers, PR tasks dedupe by GitHub identity, and merged/closed
|
|
60
|
+
PR routes close themselves out of the queue.
|
|
61
|
+
|
|
62
|
+
### Fixed
|
|
63
|
+
|
|
64
|
+
- **Daemon scheduler — separated concurrency lanes:** the daemon shared a single
|
|
65
|
+
concurrency pool, so long agent/workflow workers (minutes to over an hour) could
|
|
66
|
+
occupy every slot and starve fast command loops (monitors, digests, syncs) —
|
|
67
|
+
even the merge router starved. Command-target and agent/workflow-target loops
|
|
68
|
+
now draw from independent claim budgets. New env `LOOPS_DAEMON_COMMAND_CONCURRENCY`
|
|
69
|
+
(default 4) and `LOOPS_DAEMON_AGENT_CONCURRENCY` (default 8); the legacy
|
|
70
|
+
`LOOPS_DAEMON_CONCURRENCY` / `--concurrency` still work as the agent-lane knob for
|
|
71
|
+
back-compat, and new `--command-concurrency` / `--agent-concurrency` daemon flags
|
|
72
|
+
are added. A saturated lane no longer consumes the other lane's budget.
|
|
73
|
+
- **Todos-task routing — PR fingerprint dedupe:** the repos registry maps several
|
|
74
|
+
local checkouts to one GitHub repo, so a single PR was minted as 2-3 duplicate
|
|
75
|
+
todos tasks (distinct ids + checkout paths) that each dispatched a full worker.
|
|
76
|
+
PR-subject tasks now dedupe by a stable `owner/repo#number` fingerprint (from an
|
|
77
|
+
explicit PR fingerprint field or a canonical PR URL / `github-pr:` handle,
|
|
78
|
+
lowercased) instead of the `(source-path, task-id)` key, collapsing the
|
|
79
|
+
duplicates to one work item. Non-PR tasks keep the path/id key (no false dedupe).
|
|
80
|
+
- **Todos-task routing — freshness skip closes the task:** 0.4.10's freshness gate
|
|
81
|
+
stopped dispatching a worker for a merged/closed PR but left the task pending +
|
|
82
|
+
route-opted-in, so every drain tick re-skipped it forever. On a definitive
|
|
83
|
+
MERGED/CLOSED freshness skip the drain now marks the source task done and strips
|
|
84
|
+
its `auto:route`/`route:enabled` opt-in so it leaves the queue. Only fires when
|
|
85
|
+
gh/metadata definitively reports MERGED/CLOSED; never on dry-run.
|
|
86
|
+
|
|
87
|
+
## 0.4.10 (2026-07-05)
|
|
88
|
+
|
|
89
|
+
Hotfix bundle for the PR-merge drain pipeline.
|
|
90
|
+
|
|
91
|
+
### Fixed
|
|
92
|
+
|
|
93
|
+
- **Todos-task drain:** re-admit a terminal work item whose todos task is still
|
|
94
|
+
actionable (bounded by a redispatch cap + per-attempt backoff) instead of
|
|
95
|
+
deduping it away forever, so real task work keeps dispatching.
|
|
96
|
+
- **PR review gate:** freshness gate skips merge/review routes for already
|
|
97
|
+
merged/closed PRs; normalize `app/<slug>` and `<slug>[bot]` bot logins so
|
|
98
|
+
bot-authored PRs resolve an author instead of hard-failing the format check.
|
|
99
|
+
- **Runtime/store:** cap persisted run stdout/stderr; project path resolution fix.
|
|
100
|
+
|
|
8
101
|
## 0.4.9 (2026-07-04)
|
|
9
102
|
|
|
10
103
|
Unblock the PR-merge pipeline: task-lifecycle/route workers now dispatch real
|
package/README.md
CHANGED
|
@@ -594,8 +594,15 @@ This is the intended deterministic-to-agentic path: a producer creates a todos
|
|
|
594
594
|
task, `@hasna/events` delivers `task.created`, OpenLoops records the invocation
|
|
595
595
|
and admission item, OpenLoops creates a worker/verifier workflow when admitted,
|
|
596
596
|
and the workflow updates todos with evidence. Use account pools so worker and
|
|
597
|
-
verifier steps do not burn the same profile
|
|
598
|
-
|
|
597
|
+
verifier steps do not burn the same profile. For live Codewith drains, OpenLoops
|
|
598
|
+
spreads each route to the **least-loaded** pool account (counting running steps
|
|
599
|
+
per account) and still keeps the verifier on a different profile than the worker;
|
|
600
|
+
the deterministic hash is the tie-break, so a cold store reproduces the previous
|
|
601
|
+
assignment exactly. `--max-per-profile <K>` (default 2 for pools of two or more,
|
|
602
|
+
`0` to disable) defers a route when every pool account already has `K` running
|
|
603
|
+
steps — the guardrail that keeps higher concurrency from stacking on one
|
|
604
|
+
subscription and tripping provider-side rate limits. The chosen account per role
|
|
605
|
+
is recorded on each step (`account_profile`) and surfaced in drain reports.
|
|
599
606
|
Use `--dry-run` to inspect the rendered invocation, work item, workflow, and
|
|
600
607
|
loop input without storing anything.
|
|
601
608
|
|
|
@@ -645,11 +652,21 @@ loops routes schedule todos-task oss-task-route-drain \
|
|
|
645
652
|
--max-active-per-project 1 \
|
|
646
653
|
--max-active-per-project-group 4 \
|
|
647
654
|
--max-active 12 \
|
|
655
|
+
--max-per-profile 2 \
|
|
648
656
|
--worktree-mode required \
|
|
649
657
|
--evidence-dir "$HOME/.hasna/loops/reports/oss-task-route-drain" \
|
|
650
658
|
--compact
|
|
651
659
|
```
|
|
652
660
|
|
|
661
|
+
`--max-active` counts active routed workflows **per route**. Scope precedence:
|
|
662
|
+
an explicit `--max-active-scope <key>` wins, else the running loop's
|
|
663
|
+
`LOOPS_LOOP_NAME`, else the route key — so each router's limit is its own
|
|
664
|
+
ceiling rather than a store-wide total shared by every router.
|
|
665
|
+
`--max-active-per-project` and `--max-active-per-project-group` remain cross-route
|
|
666
|
+
anti-hog caps counted over all routes. Raise a router's `--max-active`
|
|
667
|
+
deliberately once counting is per-route; keep `--max-per-profile` set so the
|
|
668
|
+
extra concurrency spreads across subscription accounts.
|
|
669
|
+
|
|
653
670
|
Only route tasks that explicitly opt in with `auto:route`, `route_enabled=true`,
|
|
654
671
|
or `automation.allowed=true`. Use Codewith account pools, required worktrees,
|
|
655
672
|
max-active throttles, and bounded evidence directories. Do not dispatch or paste
|
package/dist/api/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// package.json
|
|
4
4
|
var package_default = {
|
|
5
5
|
name: "@hasna/loops",
|
|
6
|
-
version: "0.4.
|
|
6
|
+
version: "0.4.12",
|
|
7
7
|
description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
|
|
8
8
|
type: "module",
|
|
9
9
|
main: "dist/index.js",
|