@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.
Files changed (62) hide show
  1. package/CHANGELOG.md +99 -3
  2. package/README.md +171 -39
  3. package/dist/api/index.d.ts +36 -0
  4. package/dist/api/index.js +1518 -15
  5. package/dist/cli/index.js +7280 -3213
  6. package/dist/cli/ui.d.ts +44 -0
  7. package/dist/daemon/index.js +1433 -303
  8. package/dist/generated/storage-kit/health.d.ts +19 -0
  9. package/dist/generated/storage-kit/index.d.ts +7 -0
  10. package/dist/generated/storage-kit/migrations.d.ts +47 -0
  11. package/dist/generated/storage-kit/mode.d.ts +47 -0
  12. package/dist/generated/storage-kit/pool.d.ts +33 -0
  13. package/dist/generated/storage-kit/query.d.ts +35 -0
  14. package/dist/generated/storage-kit/tls.d.ts +25 -0
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.js +8689 -4837
  17. package/dist/lib/cloud/mode.d.ts +17 -0
  18. package/dist/lib/cloud/resolve.d.ts +16 -0
  19. package/dist/lib/cloud/storage.d.ts +82 -0
  20. package/dist/lib/cloud/transport.d.ts +148 -0
  21. package/dist/lib/format.d.ts +2 -1
  22. package/dist/lib/health.d.ts +83 -3
  23. package/dist/lib/migration.d.ts +40 -0
  24. package/dist/lib/mode.js +15 -3
  25. package/dist/lib/route/index.d.ts +2 -0
  26. package/dist/lib/route/policies.d.ts +51 -0
  27. package/dist/lib/route/provider-admission.d.ts +37 -0
  28. package/dist/lib/route/throttle.d.ts +4 -0
  29. package/dist/lib/route/types.d.ts +8 -0
  30. package/dist/lib/run-receipts.d.ts +14 -0
  31. package/dist/lib/scheduler.d.ts +5 -2
  32. package/dist/lib/storage/contract.d.ts +7 -1
  33. package/dist/lib/storage/index.d.ts +1 -0
  34. package/dist/lib/storage/index.js +2028 -231
  35. package/dist/lib/storage/pg-executor.d.ts +27 -0
  36. package/dist/lib/storage/pg-runner-claim.d.ts +40 -0
  37. package/dist/lib/storage/postgres-loop-storage.d.ts +120 -0
  38. package/dist/lib/storage/postgres-schema.js +29 -0
  39. package/dist/lib/storage/postgres.js +29 -0
  40. package/dist/lib/storage/sqlite.d.ts +6 -0
  41. package/dist/lib/storage/sqlite.js +748 -26
  42. package/dist/lib/store/index.d.ts +268 -0
  43. package/dist/lib/store.d.ts +282 -1
  44. package/dist/lib/store.js +746 -26
  45. package/dist/lib/template-kit.d.ts +25 -0
  46. package/dist/lib/templates.d.ts +16 -1
  47. package/dist/mcp/http.d.ts +16 -0
  48. package/dist/mcp/index.js +2885 -634
  49. package/dist/runner/index.js +198 -32
  50. package/dist/sdk/http.d.ts +182 -0
  51. package/dist/sdk/http.js +166 -0
  52. package/dist/sdk/index.d.ts +55 -18
  53. package/dist/sdk/index.js +2734 -617
  54. package/dist/serve/index.d.ts +4 -0
  55. package/dist/serve/index.js +9095 -0
  56. package/dist/types.d.ts +52 -0
  57. package/docs/AUTOMATION_RUNTIME_DESIGN.md +442 -1
  58. package/docs/CUTOVER-RUNBOOK.md +78 -0
  59. package/docs/DEPLOYMENT_MODES.md +35 -18
  60. package/docs/RUNTIME_BOUNDARY.md +203 -0
  61. package/docs/USAGE.md +195 -38
  62. 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 future non-local control
19
- planes:
18
+ defines `self_hosted` and `cloud` contracts for non-local control planes:
20
19
 
21
- - `self_hosted`: user-operated `loops-api` control-plane contract; this
22
- release exposes storage-backed API/runner foundations plus local migration
23
- previews.
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`) and reports `applySupported=false` because this
33
- public package does not directly mutate remote Postgres, S3/object storage, AWS
34
- resources, or hosted credentials. Route admission remains bounded by
35
- `max_dispatch`, `max_active`, `max_active_per_project`,
36
- `max_active_per_project_group`, `max_active_scope`, and `max_per_profile`.
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-runner`, and
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 workflow loops, do not edit `workflow_specs.steps_json`
482
- directly because historical workflow runs must keep pointing at their original
483
- spec. Use the append-only migrator:
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
- The command dry-runs by default. With `--apply`, it creates a new workflow spec
491
- with the requested agent timeout policy, retargets only future executions of
492
- eligible non-running workflow loops, and leaves terminal timed-out workflow runs
493
- as audit history. Use `loops workflows recover` only for interrupted `running`
494
- workflow runs whose recorded child process is gone; terminal `timed_out` runs
495
- must be requeued with `loops routes requeue <work-item-id> --reason "<cause fixed>"` before
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, the same
565
- git common directory as the source repo, and the expected generated branch. A
566
- detached HEAD or unexpected branch fails closed with evidence
567
- (`worktreeMode=required`) instead of silently running a mutating workflow in
568
- the wrong state; `worktreeMode=auto` falls back to the original checkout and
569
- records the fallback.
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 write compact evidence into a
793
- bounded reports directory so operators can audit each drain without unbounded
794
- stdout or loop history growth. Keep `--scan-limit` large enough for the current
795
- ready-task backlog; if the scan is exhausted before matching tasks appear, the
796
- drain will correctly do no work.
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` or `expectations`. To turn failed
874
- expectations into deduped tasks, use the explicit routing command:
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.13",
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": {