@hasna/loops 0.4.13 → 0.4.22
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 +99 -3
- package/README.md +171 -39
- package/dist/api/index.d.ts +36 -0
- package/dist/api/index.js +1518 -15
- package/dist/cli/index.js +7280 -3213
- package/dist/cli/ui.d.ts +44 -0
- package/dist/daemon/index.js +1433 -303
- package/dist/generated/storage-kit/health.d.ts +19 -0
- package/dist/generated/storage-kit/index.d.ts +7 -0
- package/dist/generated/storage-kit/migrations.d.ts +47 -0
- package/dist/generated/storage-kit/mode.d.ts +47 -0
- package/dist/generated/storage-kit/pool.d.ts +33 -0
- package/dist/generated/storage-kit/query.d.ts +35 -0
- package/dist/generated/storage-kit/tls.d.ts +25 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +8689 -4837
- package/dist/lib/cloud/mode.d.ts +17 -0
- package/dist/lib/cloud/resolve.d.ts +16 -0
- package/dist/lib/cloud/storage.d.ts +82 -0
- package/dist/lib/cloud/transport.d.ts +148 -0
- package/dist/lib/format.d.ts +2 -1
- package/dist/lib/health.d.ts +83 -3
- package/dist/lib/migration.d.ts +40 -0
- package/dist/lib/mode.js +15 -3
- package/dist/lib/route/index.d.ts +2 -0
- package/dist/lib/route/policies.d.ts +51 -0
- package/dist/lib/route/provider-admission.d.ts +37 -0
- package/dist/lib/route/throttle.d.ts +4 -0
- package/dist/lib/route/types.d.ts +8 -0
- package/dist/lib/run-receipts.d.ts +14 -0
- package/dist/lib/scheduler.d.ts +5 -2
- package/dist/lib/storage/contract.d.ts +7 -1
- package/dist/lib/storage/index.d.ts +1 -0
- package/dist/lib/storage/index.js +2028 -231
- package/dist/lib/storage/pg-executor.d.ts +27 -0
- package/dist/lib/storage/pg-runner-claim.d.ts +40 -0
- package/dist/lib/storage/postgres-loop-storage.d.ts +120 -0
- package/dist/lib/storage/postgres-schema.js +29 -0
- package/dist/lib/storage/postgres.js +29 -0
- package/dist/lib/storage/sqlite.d.ts +6 -0
- package/dist/lib/storage/sqlite.js +748 -26
- package/dist/lib/store/index.d.ts +268 -0
- package/dist/lib/store.d.ts +282 -1
- package/dist/lib/store.js +746 -26
- package/dist/lib/template-kit.d.ts +25 -0
- package/dist/lib/templates.d.ts +16 -1
- package/dist/mcp/http.d.ts +16 -0
- package/dist/mcp/index.js +2885 -634
- package/dist/runner/index.js +198 -32
- package/dist/sdk/http.d.ts +182 -0
- package/dist/sdk/http.js +166 -0
- package/dist/sdk/index.d.ts +55 -18
- package/dist/sdk/index.js +2734 -617
- package/dist/serve/index.d.ts +4 -0
- package/dist/serve/index.js +9095 -0
- package/dist/types.d.ts +52 -0
- package/docs/AUTOMATION_RUNTIME_DESIGN.md +442 -1
- package/docs/CUTOVER-RUNBOOK.md +78 -0
- package/docs/DEPLOYMENT_MODES.md +35 -18
- package/docs/RUNTIME_BOUNDARY.md +203 -0
- package/docs/USAGE.md +195 -38
- package/package.json +15 -3
package/docs/USAGE.md
CHANGED
|
@@ -15,12 +15,13 @@ It supports deterministic command loops, JSON-defined workflows, and guarded CLI
|
|
|
15
15
|
|
|
16
16
|
OpenLoops defaults to `local`, where SQLite in `LOOPS_DATA_DIR` is
|
|
17
17
|
authoritative and `loops-daemon` executes scheduled work. The package also
|
|
18
|
-
defines `self_hosted` and `cloud` contracts for
|
|
19
|
-
planes:
|
|
18
|
+
defines `self_hosted` and `cloud` contracts for non-local control planes:
|
|
20
19
|
|
|
21
|
-
- `self_hosted`:
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
- `self_hosted`: the Hasna-owned AWS/RDS control-plane deployment, served by
|
|
21
|
+
`loops-serve` and backed by Postgres, with the embeddable `loops-api` contract
|
|
22
|
+
shared by serve, SDK, and tests. This release exposes storage-backed `/v1`
|
|
23
|
+
loop CRUD and run listing, runner registration/claim/heartbeat/finalize
|
|
24
|
+
foundations, and local migration previews.
|
|
24
25
|
- `cloud`: hosted control-plane contract; this release exposes client/runner
|
|
25
26
|
status only, and requires `LOOPS_CLOUD_API_URL` plus `LOOPS_CLOUD_TOKEN` or
|
|
26
27
|
`HASNA_LOOPS_CLOUD_TOKEN` before status can report ready.
|
|
@@ -29,13 +30,16 @@ Scheduler state is explicit in status JSON. `schedulerState.localStore` is
|
|
|
29
30
|
SQLite plus local run artifact files: authoritative in `local`, cache/spool in
|
|
30
31
|
non-local modes. `schedulerState.remoteStore` names the non-local contract
|
|
31
32
|
(`api_control_plane_contract`, `postgres_contract`, or
|
|
32
|
-
`hosted_control_plane_contract`)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
`hosted_control_plane_contract`). The standalone `loops` CLI reports
|
|
34
|
+
`applySupported=false` for non-local apply because it does not perform
|
|
35
|
+
id-preserving remote migration, S3/object storage mutation, AWS resource
|
|
36
|
+
mutation, or hosted credential mutation. `loops-serve` mutates self-hosted
|
|
37
|
+
Postgres for normal control-plane CRUD and runner protocol routes when it is
|
|
38
|
+
explicitly configured. Route admission remains bounded by `max_dispatch`,
|
|
39
|
+
`max_active`, `max_active_per_project`, `max_active_per_project_group`,
|
|
40
|
+
`max_active_scope`, and `max_per_profile`.
|
|
37
41
|
|
|
38
|
-
Useful status commands:
|
|
42
|
+
Useful status and setup commands:
|
|
39
43
|
|
|
40
44
|
```bash
|
|
41
45
|
loops mode
|
|
@@ -48,6 +52,8 @@ loops self-hosted runner-register --runner-id <id> --machine-id <machine>
|
|
|
48
52
|
loops self-hosted runner-register --runner-id <id> --machine-id <machine> --apply
|
|
49
53
|
loops cloud status
|
|
50
54
|
loops-api status
|
|
55
|
+
loops-serve version
|
|
56
|
+
HASNA_LOOPS_DATABASE_URL=... loops-serve migrate --dry-run
|
|
51
57
|
loops-runner status
|
|
52
58
|
```
|
|
53
59
|
|
|
@@ -93,14 +99,16 @@ The preview may inspect `LOOPS_API_URL`/`HASNA_LOOPS_API_URL`, but it refuses
|
|
|
93
99
|
remote apply because normal loop CRUD would generate new ids. Use
|
|
94
100
|
`loops self-hosted runner-register` to verify runner registration against an
|
|
95
101
|
API, then use `loops-runner run-once` for the current bounded non-workflow
|
|
96
|
-
claim/execute/finalize protocol.
|
|
102
|
+
claim/execute/finalize protocol. `loops-serve migrate` applies the Postgres
|
|
103
|
+
schema and `api_keys` table for a self-hosted control-plane host; the standalone
|
|
104
|
+
CLI migration previews still do not perform id-preserving remote apply.
|
|
97
105
|
Runner registration is preview-only unless `--apply` is present.
|
|
98
106
|
|
|
99
107
|
## Install
|
|
100
108
|
|
|
101
109
|
**OpenLoops requires the [Bun](https://bun.sh) runtime (`bun >= 1.0`).** The
|
|
102
|
-
installed binaries are `loops`, `loops-daemon`, `loops-api`, `loops-
|
|
103
|
-
`loops-mcp`; each uses a `#!/usr/bin/env bun` shebang.
|
|
110
|
+
installed binaries are `loops`, `loops-daemon`, `loops-api`, `loops-serve`,
|
|
111
|
+
`loops-runner`, and `loops-mcp`; each uses a `#!/usr/bin/env bun` shebang.
|
|
104
112
|
|
|
105
113
|
From npm:
|
|
106
114
|
|
|
@@ -208,6 +216,23 @@ failures are recorded as failed loop runs with a `runtime preflight failed`
|
|
|
208
216
|
error, so health/routing checks can create follow-up tasks without spawning the
|
|
209
217
|
worker.
|
|
210
218
|
|
|
219
|
+
For controlled launches, scheduled todos-task drains can be guarded by blocker
|
|
220
|
+
tasks:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
loops routes schedule todos-task platform-drain \
|
|
224
|
+
--every 5m \
|
|
225
|
+
--todos-project /path/to/source/todos-project \
|
|
226
|
+
--tags auto:route \
|
|
227
|
+
--launch-gate "pa19-controlled-launch" \
|
|
228
|
+
--launch-gate-blocker "/path/to/open-codewith::2d9d931b" \
|
|
229
|
+
--launch-gate-blocker "/path/to/open-loops::816e99db" \
|
|
230
|
+
--worktree-mode required
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
The drain creates zero worker loops while any blocker task is not completed.
|
|
234
|
+
Each blocked tick writes launch-gate evidence and leaves source tasks untouched.
|
|
235
|
+
|
|
211
236
|
Run a Claude loop every morning:
|
|
212
237
|
|
|
213
238
|
```bash
|
|
@@ -418,6 +443,13 @@ for those. Set `"blockedExitCodes": []` on a gate-named step to opt out.
|
|
|
418
443
|
|
|
419
444
|
## Templates And Task Events
|
|
420
445
|
|
|
446
|
+
OpenLoops is the runtime/scheduler/workflow engine for these flows — not the
|
|
447
|
+
automation domain model. Todos-task routes are OpenLoops-native admission; they
|
|
448
|
+
do not replace the OpenAutomations product queue. See
|
|
449
|
+
[Runtime Boundary](./RUNTIME_BOUNDARY.md) for ownership split and external
|
|
450
|
+
compiler handoff paths (`@hasna/automations` claim-queue,
|
|
451
|
+
`@hasna/actions` planned upsert-one-shot).
|
|
452
|
+
|
|
421
453
|
Built-in templates turn common orchestration flows into reusable workflow JSON.
|
|
422
454
|
`todos-task-worker-verifier` performs one todos task and then verifies it.
|
|
423
455
|
`event-worker-verifier` handles any Hasna event envelope and then verifies the
|
|
@@ -425,7 +457,11 @@ handling. `bounded-agent-worker-verifier` is for recurring bounded agent work:
|
|
|
425
457
|
one worker runs a narrow objective, then a fresh verifier audits the result.
|
|
426
458
|
The catalog also includes `task-lifecycle`, `pr-review`, `scheduled-audit`,
|
|
427
459
|
`knowledge-refresh`, `report-only`, `incident-response`, and
|
|
428
|
-
`deterministic-check-create-task` for common operator workflows.
|
|
460
|
+
`deterministic-check-create-task` for common operator workflows. Use
|
|
461
|
+
`routing-remediation` for bounded routing-doctor repair runs: it dry-runs by
|
|
462
|
+
default, gates `safe_auto` capacity, applies only supported
|
|
463
|
+
`todos doctor routing --apply` repairs when explicitly enabled, and files
|
|
464
|
+
blocker tasks for human/cross-repo/unsupported findings.
|
|
429
465
|
|
|
430
466
|
```bash
|
|
431
467
|
loops templates list
|
|
@@ -459,6 +495,12 @@ loops templates render pr-review \
|
|
|
459
495
|
loops templates render deterministic-check-create-task \
|
|
460
496
|
--var projectPath=/path/to/repo \
|
|
461
497
|
--var checkCommand='your deterministic check and todos upsert command'
|
|
498
|
+
loops templates render routing-remediation \
|
|
499
|
+
--var projectPath=/path/to/repo \
|
|
500
|
+
--var todosProjectPath=/path/to/todos-project \
|
|
501
|
+
--var shard=0/6 \
|
|
502
|
+
--var maxRepairs=25 \
|
|
503
|
+
--var idempotencyKey=routing-health:repo:shard0
|
|
462
504
|
```
|
|
463
505
|
|
|
464
506
|
Custom reusable workflow templates live under the OpenLoops app data directory:
|
|
@@ -478,21 +520,39 @@ worker finishes; pass `--verifier-idle-timeout none` or template variable
|
|
|
478
520
|
`verifierIdleTimeoutMs=none` only when another heartbeat is guaranteed. Use a
|
|
479
521
|
positive numeric `timeoutMs` only when an agentic step is intentionally bounded.
|
|
480
522
|
|
|
481
|
-
To migrate existing
|
|
482
|
-
|
|
483
|
-
|
|
523
|
+
To migrate existing agentic loops, use the timeout migrator instead of editing
|
|
524
|
+
the database directly. Workflow loops are migrated append-only because
|
|
525
|
+
historical workflow runs must keep pointing at their original spec; direct
|
|
526
|
+
agent loops selected with `--loop` update their stored target in place for
|
|
527
|
+
future executions:
|
|
484
528
|
|
|
485
529
|
```bash
|
|
486
530
|
loops workflows migrate-agent-timeouts --loop <loop-id-or-name>
|
|
487
531
|
loops workflows migrate-agent-timeouts --loop <loop-id-or-name> --apply
|
|
532
|
+
loops workflows migrate-goal-wrappers --loop <loop-id-or-name>
|
|
533
|
+
loops workflows migrate-goal-wrappers --loop <loop-id-or-name> --apply
|
|
534
|
+
loops workflows migrate-goal-wrappers --loop <loop-id-or-name> --apply --archive-old
|
|
488
535
|
```
|
|
489
536
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
537
|
+
Both migrators dry-run by default. For eligible non-running workflow loops,
|
|
538
|
+
`--apply` creates a new workflow spec and retargets only future executions;
|
|
539
|
+
historical workflow runs keep pointing at their original spec. For direct agent
|
|
540
|
+
loops selected with `--loop`, `migrate-agent-timeouts --apply` updates the
|
|
541
|
+
stored target in place for future executions. Use `--archive-old` to archive
|
|
542
|
+
superseded workflow specs when no active loops still reference them.
|
|
543
|
+
|
|
544
|
+
`migrate-agent-timeouts` clones the workflow with the requested agent timeout
|
|
545
|
+
policy (`--timeout none` by default). `migrate-goal-wrappers` targets loops that
|
|
546
|
+
define both a loop-level goal and a redundant workflow-level top-level goal: it
|
|
547
|
+
clones a goal-free workflow spec, retargets the loop, and leaves the loop-level
|
|
548
|
+
goal as the sole orchestration wrapper. Loops with only a workflow-level goal or
|
|
549
|
+
only a loop-level goal are skipped. New workflow loops cannot combine both
|
|
550
|
+
wrappers; use loop-level `--goal` on `loops create workflow` instead of nesting
|
|
551
|
+
a top-level `"goal"` in the workflow JSON.
|
|
552
|
+
|
|
553
|
+
Use `loops workflows recover` only for interrupted `running` workflow runs whose
|
|
554
|
+
recorded child process is gone; terminal `timed_out` runs must be requeued with
|
|
555
|
+
`loops routes requeue <work-item-id> --reason "<cause fixed>"` before
|
|
496
556
|
re-delivering or draining the original task/event route.
|
|
497
557
|
|
|
498
558
|
```json
|
|
@@ -561,12 +621,14 @@ on the remote machine. The generated agent target includes worktree metadata
|
|
|
561
621
|
inspection expose the exact checkout.
|
|
562
622
|
|
|
563
623
|
Before a worker starts, worktree preparation verifies that any existing
|
|
564
|
-
managed path is a real git worktree with the same top-level checkout
|
|
565
|
-
git common directory as the source repo
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
624
|
+
managed path is a real git worktree with the same top-level checkout and the
|
|
625
|
+
same git common directory as the source repo. If the checkout is on a detached
|
|
626
|
+
HEAD or unexpected branch, OpenLoops only reattaches it to the expected
|
|
627
|
+
generated branch when the worktree is clean. Dirty worktrees, symlinked paths,
|
|
628
|
+
non-worktree paths, different common dirs, and unrecoverable branch switches
|
|
629
|
+
fail closed with cleanup evidence (`worktreeMode=required`) instead of
|
|
630
|
+
silently running a mutating workflow in the wrong state; `worktreeMode=auto`
|
|
631
|
+
falls back to the original checkout and records the fallback.
|
|
570
632
|
|
|
571
633
|
Use explicit main/default checkout mode only when the task truly requires it:
|
|
572
634
|
|
|
@@ -610,6 +672,9 @@ blocked, completed/done, cancelled/canceled, failed, archived, manual,
|
|
|
610
672
|
approval-required, or `no-auto` tasks. This guard exists even when the upstream
|
|
611
673
|
`@hasna/events` webhook filter is misconfigured, so task existence alone is not
|
|
612
674
|
permission to execute agent work.
|
|
675
|
+
Route work items are the durable reservation ledger: they include the stable
|
|
676
|
+
idempotency key, task/event references, project/group keys, admitting machine
|
|
677
|
+
ID, route scope, workflow/loop IDs, and the current terminal or active status.
|
|
613
678
|
|
|
614
679
|
Task route drains can select providers from task metadata instead of running one
|
|
615
680
|
fixed provider/account pool for the whole drain. Add one or more
|
|
@@ -619,13 +684,20 @@ matching rule wins. Rule profiles become a Codewith auth-profile pool for
|
|
|
619
684
|
can also carry `provider_hint`/`route_provider`, `auth_profile_pool`, or
|
|
620
685
|
`account_pool` metadata. Dry-run, drain evidence, and route invocation scope
|
|
621
686
|
include `providerRouting` so operators can see why a provider/account was
|
|
622
|
-
selected.
|
|
687
|
+
selected. Selector values may contain `:`; the parser treats the colon before a
|
|
688
|
+
supported provider id as the route delimiter, so exact tag selectors such as
|
|
689
|
+
`tags=area:frontend:claude:account003,account015` and
|
|
690
|
+
`tags=provider:claude-code:claude:account003,account015` match literal task
|
|
691
|
+
tags `area:frontend` and `provider:claude-code`.
|
|
623
692
|
|
|
624
693
|
```bash
|
|
625
694
|
loops routes drain todos-task \
|
|
626
695
|
--dry-run \
|
|
696
|
+
--provider-rule tags=area:frontend:claude:account003,account015 \
|
|
697
|
+
--provider-rule tags=provider:claude-code:claude:account003,account015 \
|
|
627
698
|
--provider-rule area=frontend:claude:claude-ui-a,claude-ui-b \
|
|
628
699
|
--provider-rule area=backend:codewith:account001,account002 \
|
|
700
|
+
--provider-rule tags=task-lifecycle:codewith:account001,account002 \
|
|
629
701
|
--worktree-mode required
|
|
630
702
|
```
|
|
631
703
|
|
|
@@ -703,6 +775,10 @@ equivalent directory-scoped write controls.
|
|
|
703
775
|
Inspect route state with:
|
|
704
776
|
|
|
705
777
|
```bash
|
|
778
|
+
loops routes policies list
|
|
779
|
+
loops routes policies show oss
|
|
780
|
+
loops routes policies render oss
|
|
781
|
+
loops routes policies validate
|
|
706
782
|
cat task-created-event.json | loops routes preview todos-task --sandbox workspace-write
|
|
707
783
|
cat task-created-event.json | loops routes create todos-task --sandbox workspace-write
|
|
708
784
|
loops routes drain todos-task --task-list oss --max-dispatch 2 --compact
|
|
@@ -713,6 +789,29 @@ loops routes requeue <work-item-id> --reason "fixed upstream blocker"
|
|
|
713
789
|
loops routes invocations
|
|
714
790
|
```
|
|
715
791
|
|
|
792
|
+
Compact drain output includes route reservation IDs, work-item status, machine
|
|
793
|
+
ID, workflow ID, loop ID, and route scope for each considered task.
|
|
794
|
+
|
|
795
|
+
Named route policies expand the long recurring drain commands used by the live
|
|
796
|
+
task routers into explicit, replayable options. `repoops-pr-queue`, `oss`,
|
|
797
|
+
`pilot`, and `machine-sync` are built in. Operators can inspect the policy,
|
|
798
|
+
render the exact `loops --json routes drain todos-task ...` command, and validate
|
|
799
|
+
that the rendered args no longer depend on `--policy`/`--preset`:
|
|
800
|
+
|
|
801
|
+
```bash
|
|
802
|
+
loops routes policies render oss
|
|
803
|
+
loops routes schedule todos-task machine-oss-task-lifecycle-router --policy oss
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
Scheduled policy routes store explicit drain args plus
|
|
807
|
+
`--route-policy-evidence <id>`, so future runs remain auditable even if a policy
|
|
808
|
+
definition changes later. Policy drains and dry-runs include `routePolicy`
|
|
809
|
+
evidence with the source, safety class, guards, expanded options, and rendered
|
|
810
|
+
args. Passing a conflicting explicit option fails before the route is created.
|
|
811
|
+
The `pilot` policy uses `sandbox=danger-full-access` and is treated as a paused
|
|
812
|
+
manual break-glass lane; applying it requires the operator to pass
|
|
813
|
+
`--manual-break-glass` explicitly.
|
|
814
|
+
|
|
716
815
|
When a workflow run starts from an admitted work item, OpenLoops writes a
|
|
717
816
|
manifest under:
|
|
718
817
|
|
|
@@ -744,6 +843,8 @@ loops routes drain todos-task \
|
|
|
744
843
|
--max-active-per-project 1 \
|
|
745
844
|
--max-active-per-project-group 4 \
|
|
746
845
|
--max-active 12 \
|
|
846
|
+
--provider-active-cap 6 \
|
|
847
|
+
--provider-admission-check \
|
|
747
848
|
--worktree-mode required \
|
|
748
849
|
--evidence-dir "$HOME/.hasna/loops/reports/task-drain"
|
|
749
850
|
```
|
|
@@ -761,6 +862,16 @@ when requested, and leaves excess ready tasks in todos for a later drain pass.
|
|
|
761
862
|
Use `--dry-run` to preview candidates and rendered workflows without mutating
|
|
762
863
|
OpenLoops state.
|
|
763
864
|
|
|
865
|
+
The route throttle flags count OpenLoops routed workflow work items, not the
|
|
866
|
+
live background-agent slots inside a provider. For Codewith drains, add
|
|
867
|
+
`--provider-active-cap <n>` (or `--codewith-active-cap <n>`) so each candidate
|
|
868
|
+
checks `codewith agent diagnostics --json` before workflow-loop creation and
|
|
869
|
+
defers when `activeRunCount >= n`. Use `--provider-admission-check` when the
|
|
870
|
+
drain should also fail closed on diagnostics errors, unsupported providers, or
|
|
871
|
+
Codewith reports with no available admission slots. Backlog prioritizer and
|
|
872
|
+
drain loops should use these first-class flags rather than a shell guard around
|
|
873
|
+
`codewith agent diagnostics`.
|
|
874
|
+
|
|
764
875
|
For an OSS task-created route, keep the drain deterministic and narrow:
|
|
765
876
|
|
|
766
877
|
```bash
|
|
@@ -779,6 +890,8 @@ loops routes schedule todos-task oss-task-route-drain \
|
|
|
779
890
|
--max-active-per-project 1 \
|
|
780
891
|
--max-active-per-project-group 4 \
|
|
781
892
|
--max-active 12 \
|
|
893
|
+
--provider-active-cap 6 \
|
|
894
|
+
--provider-admission-check \
|
|
782
895
|
--worktree-mode required \
|
|
783
896
|
--evidence-dir "$HOME/.hasna/loops/reports/oss-task-route-drain" \
|
|
784
897
|
--compact
|
|
@@ -789,11 +902,13 @@ in with the `auto:route` tag, `route_enabled=true`, or
|
|
|
789
902
|
`automation.allowed=true` should be routed. Keep repo-mutating worker/verifier
|
|
790
903
|
runs on a Codewith account pool with `--worktree-mode required`. Do not dispatch
|
|
791
904
|
or paste task prompts into tmux panes. Use max-active throttles and
|
|
792
|
-
`--max-dispatch` to bound agent fan-out, and
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
905
|
+
`--max-dispatch` to bound OpenLoops agent fan-out, and use
|
|
906
|
+
`--provider-active-cap` plus `--provider-admission-check` to bound live Codewith
|
|
907
|
+
background-agent admission. Write compact evidence into a bounded reports
|
|
908
|
+
directory so operators can audit each drain without unbounded stdout or loop
|
|
909
|
+
history growth. Keep `--scan-limit` large enough for the current ready-task
|
|
910
|
+
backlog; if the scan is exhausted before matching tasks appear, the drain will
|
|
911
|
+
correctly do no work.
|
|
797
912
|
|
|
798
913
|
Generated task/event route workflow specs are lifecycle-managed. After a
|
|
799
914
|
generated one-shot route workflow run reaches `succeeded`, `failed`,
|
|
@@ -829,6 +944,27 @@ is requested without `manualBreakGlass=true`. Use `workspace-write` for
|
|
|
829
944
|
unattended task/event routes. Full access is an explicit manual emergency path,
|
|
830
945
|
not a default automation mode.
|
|
831
946
|
|
|
947
|
+
## Run Receipts
|
|
948
|
+
|
|
949
|
+
Use run receipts when an agent, scheduler, route, or external workflow needs a
|
|
950
|
+
stable run outcome without parsing raw stdout or wrapper-script text. Receipts
|
|
951
|
+
are scheduler-neutral JSON records with these public snake_case fields:
|
|
952
|
+
`loop_id`, `run_id`, `machine`, `repo`, `task_ids`, `knowledge_ids`,
|
|
953
|
+
`digest_id`, `started_at`, `finished_at`, `status`, `exit_code`, `summary`, and
|
|
954
|
+
`evidence_paths`. The summary contains bounded, scrubbed excerpts and byte
|
|
955
|
+
counts; raw unbounded stdout/stderr stays out of the receipt contract.
|
|
956
|
+
|
|
957
|
+
```bash
|
|
958
|
+
cat receipt.json | loops --json receipts write --file -
|
|
959
|
+
loops --json receipts read run_123
|
|
960
|
+
loops --json receipts list --loop-id loop_123 --task-id task_123
|
|
961
|
+
```
|
|
962
|
+
|
|
963
|
+
MCP clients can use `loops_receipt_read`, `loops_receipts_list`, and the
|
|
964
|
+
mutation-gated `loops_receipt_write`. The SDK exposes `writeReceipt`,
|
|
965
|
+
`receipt`, and `receipts`; the HTTP API exposes `POST /v1/receipts`,
|
|
966
|
+
`GET /v1/receipts/{runId}`, and `GET /v1/receipts`.
|
|
967
|
+
|
|
832
968
|
## Transcript-Driven Loops
|
|
833
969
|
|
|
834
970
|
OpenLoops can turn long-form media or meeting transcripts into recurring workflow work when paired with `iapp-transcriber`. The template at `docs/workflows/transcript-feedback-to-loops.json` transcribes an authorized media URL, asks an agent to extract recurring loop candidates, authors workflow specs, and validates generated workflows before scheduling. Copy it into the target repo, replace `/path/to/repo` with that repo's absolute path, and provide `TRANSCRIBER_SOURCE_URL` through the runner environment or a private, uncommitted workflow copy before storing or scheduling it. Do not commit private or signed media URLs.
|
|
@@ -865,25 +1001,46 @@ agent-run failures for default-loop SLOs:
|
|
|
865
1001
|
|
|
866
1002
|
```bash
|
|
867
1003
|
loops health --json
|
|
1004
|
+
loops health scan --include active,paused --latest-run --doctor --daemon --json
|
|
868
1005
|
loops expectations <loop-id-or-name> --json
|
|
869
1006
|
```
|
|
870
1007
|
|
|
871
1008
|
The JSON contains the expectation result, bounded error/stdout/stderr evidence,
|
|
872
1009
|
a stable failure fingerprint, route metadata, and recommended task fields.
|
|
873
|
-
OpenLoops does not mutate Todos from `health
|
|
874
|
-
expectations into deduped tasks,
|
|
1010
|
+
OpenLoops does not mutate Todos from `health`, `expectations`, or read-only
|
|
1011
|
+
`health scan`. To turn failed expectations or scan findings into deduped tasks,
|
|
1012
|
+
use an explicit mutating command:
|
|
875
1013
|
|
|
876
1014
|
```bash
|
|
877
1015
|
loops health route-tasks \
|
|
878
1016
|
--project ~/.hasna/loops \
|
|
879
1017
|
--task-list loop-error-self-heal \
|
|
880
1018
|
--max-actions 5
|
|
1019
|
+
|
|
1020
|
+
loops health scan \
|
|
1021
|
+
--include active,paused \
|
|
1022
|
+
--latest-run \
|
|
1023
|
+
--doctor \
|
|
1024
|
+
--daemon \
|
|
1025
|
+
--upsert-todos \
|
|
1026
|
+
--dry-run \
|
|
1027
|
+
--max-actions 5 \
|
|
1028
|
+
--evidence-dir ~/.hasna/loops/reports/health-scan
|
|
881
1029
|
```
|
|
882
1030
|
|
|
883
1031
|
Use `--dry-run --json` first when testing a new automation path. Routed tasks
|
|
884
1032
|
include the stable failure fingerprint, classification, loop id/name, and
|
|
885
1033
|
`no_tmux_dispatch=true` metadata.
|
|
886
1034
|
|
|
1035
|
+
`health scan` replaces local loop-error self-heal scripts with package-owned
|
|
1036
|
+
CLI/SDK/MCP primitives. It inventories included loop statuses, detects daemon,
|
|
1037
|
+
doctor, preflight, latest-run, and stale-running issues, writes bounded
|
|
1038
|
+
`summary.json` and `report.md` files under
|
|
1039
|
+
`$LOOPS_DATA_DIR/reports/health-scan` or `--report-dir`/`--evidence-dir`, and
|
|
1040
|
+
keeps output compact. It is read-only by default. The only safe self-heal is
|
|
1041
|
+
`--start-daemon`, which starts the daemon only when status proves it is not
|
|
1042
|
+
running; it does not stop, resume, archive, delete, or reap loops.
|
|
1043
|
+
|
|
887
1044
|
Use `--evidence-dir <dir>` when a deterministic loop needs a compact JSON
|
|
888
1045
|
heartbeat/report on disk. Use `--auto-route` only on task lists that should feed
|
|
889
1046
|
the task-created headless worker/verifier workflow; it adds the `auto:route`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/loops",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.22",
|
|
4
4
|
"description": "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"loops": "dist/cli/index.js",
|
|
10
10
|
"loops-daemon": "dist/daemon/index.js",
|
|
11
11
|
"loops-api": "dist/api/index.js",
|
|
12
|
+
"loops-serve": "dist/serve/index.js",
|
|
12
13
|
"loops-runner": "dist/runner/index.js",
|
|
13
14
|
"loops-mcp": "dist/mcp/index.js"
|
|
14
15
|
},
|
|
@@ -21,6 +22,14 @@
|
|
|
21
22
|
"types": "./dist/sdk/index.d.ts",
|
|
22
23
|
"import": "./dist/sdk/index.js"
|
|
23
24
|
},
|
|
25
|
+
"./sdk/http": {
|
|
26
|
+
"types": "./dist/sdk/http.d.ts",
|
|
27
|
+
"import": "./dist/sdk/http.js"
|
|
28
|
+
},
|
|
29
|
+
"./serve": {
|
|
30
|
+
"types": "./dist/serve/index.d.ts",
|
|
31
|
+
"import": "./dist/serve/index.js"
|
|
32
|
+
},
|
|
24
33
|
"./mcp": {
|
|
25
34
|
"types": "./dist/mcp/index.d.ts",
|
|
26
35
|
"import": "./dist/mcp/index.js"
|
|
@@ -66,7 +75,7 @@
|
|
|
66
75
|
"LICENSE"
|
|
67
76
|
],
|
|
68
77
|
"scripts": {
|
|
69
|
-
"build": "rm -rf dist && bun build src/cli/index.ts src/daemon/index.ts src/api/index.ts src/runner/index.ts src/mcp/index.ts src/index.ts src/sdk/index.ts src/lib/store.ts src/lib/mode.ts src/lib/storage/index.ts src/lib/storage/contract.ts src/lib/storage/sqlite.ts src/lib/storage/postgres.ts src/lib/storage/postgres-schema.ts --root src --outdir dist --target bun --packages external && chmod +x dist/cli/index.js dist/daemon/index.js dist/api/index.js dist/runner/index.js dist/mcp/index.js && tsc -p tsconfig.build.json --emitDeclarationOnly --outDir dist",
|
|
78
|
+
"build": "rm -rf dist && bun build src/cli/index.ts src/daemon/index.ts src/api/index.ts src/serve/index.ts src/runner/index.ts src/mcp/index.ts src/index.ts src/sdk/index.ts src/sdk/http.ts src/lib/store.ts src/lib/mode.ts src/lib/storage/index.ts src/lib/storage/contract.ts src/lib/storage/sqlite.ts src/lib/storage/postgres.ts src/lib/storage/postgres-schema.ts --root src --outdir dist --target bun --packages external && chmod +x dist/cli/index.js dist/daemon/index.js dist/api/index.js dist/serve/index.js dist/runner/index.js dist/mcp/index.js && tsc -p tsconfig.build.json --emitDeclarationOnly --outDir dist",
|
|
70
79
|
"build:bin": "bun build src/cli/index.ts --compile --outfile dist/loops",
|
|
71
80
|
"typecheck": "tsc --noEmit",
|
|
72
81
|
"test": "bun test",
|
|
@@ -106,13 +115,16 @@
|
|
|
106
115
|
"@openrouter/ai-sdk-provider": "2.9.1",
|
|
107
116
|
"ai": "6.0.204",
|
|
108
117
|
"commander": "^13.1.0",
|
|
118
|
+
"pg": "^8.13.1",
|
|
109
119
|
"zod": "4.4.3"
|
|
110
120
|
},
|
|
111
121
|
"optionalDependencies": {
|
|
112
|
-
"@hasna/machines": "0.0.49"
|
|
122
|
+
"@hasna/machines": "0.0.49",
|
|
123
|
+
"@hasna/contracts": "^0.4.2"
|
|
113
124
|
},
|
|
114
125
|
"devDependencies": {
|
|
115
126
|
"@types/bun": "latest",
|
|
127
|
+
"@types/pg": "^8.11.10",
|
|
116
128
|
"typescript": "^5.7.3"
|
|
117
129
|
},
|
|
118
130
|
"publishConfig": {
|