@loops-adk/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +486 -0
  3. package/bin/loops.mjs +16 -0
  4. package/dist/App-3YQS6DXA.js +461 -0
  5. package/dist/App-3YQS6DXA.js.map +1 -0
  6. package/dist/agent-sdk-RF5VJZAT.js +95 -0
  7. package/dist/agent-sdk-RF5VJZAT.js.map +1 -0
  8. package/dist/anthropic-api-XJY6Y4T2.js +131 -0
  9. package/dist/anthropic-api-XJY6Y4T2.js.map +1 -0
  10. package/dist/api.d.ts +949 -0
  11. package/dist/api.js +898 -0
  12. package/dist/api.js.map +1 -0
  13. package/dist/chunk-33YIGWNU.js +63 -0
  14. package/dist/chunk-33YIGWNU.js.map +1 -0
  15. package/dist/chunk-3BPU34DE.js +2163 -0
  16. package/dist/chunk-3BPU34DE.js.map +1 -0
  17. package/dist/chunk-CXEPZHSR.js +86 -0
  18. package/dist/chunk-CXEPZHSR.js.map +1 -0
  19. package/dist/chunk-I3STY7U6.js +61 -0
  20. package/dist/chunk-I3STY7U6.js.map +1 -0
  21. package/dist/chunk-JFTXJ7I2.js +18 -0
  22. package/dist/chunk-JFTXJ7I2.js.map +1 -0
  23. package/dist/chunk-XC46B4FD.js +9 -0
  24. package/dist/chunk-XC46B4FD.js.map +1 -0
  25. package/dist/chunk-Y2SD7GBL.js +30 -0
  26. package/dist/chunk-Y2SD7GBL.js.map +1 -0
  27. package/dist/claude-cli-U7WEVAOL.js +124 -0
  28. package/dist/claude-cli-U7WEVAOL.js.map +1 -0
  29. package/dist/codex-6I5UZ2HM.js +60 -0
  30. package/dist/codex-6I5UZ2HM.js.map +1 -0
  31. package/dist/env/command.d.ts +53 -0
  32. package/dist/env/command.js +3 -0
  33. package/dist/env/command.js.map +1 -0
  34. package/dist/env/docker.d.ts +38 -0
  35. package/dist/env/docker.js +33 -0
  36. package/dist/env/docker.js.map +1 -0
  37. package/dist/env/sst.d.ts +39 -0
  38. package/dist/env/sst.js +20 -0
  39. package/dist/env/sst.js.map +1 -0
  40. package/dist/index.d.ts +1 -0
  41. package/dist/index.js +620 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/types-B4wGVpqo.d.ts +898 -0
  44. package/package.json +100 -0
  45. package/skills/author-loop/SKILL.md +121 -0
package/package.json ADDED
@@ -0,0 +1,100 @@
1
+ {
2
+ "name": "@loops-adk/core",
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "author": "Jonny Neill",
6
+ "description": "Run an agent in a convergence loop with an honest done-gate. A small, nestable loop and DAG primitive: deterministic plus agent-judge conditions, git as memory, review-restart, budgets, and a live TUI.",
7
+ "keywords": [
8
+ "agent",
9
+ "agents",
10
+ "loop",
11
+ "loops",
12
+ "convergence",
13
+ "ai",
14
+ "llm",
15
+ "claude",
16
+ "orchestration"
17
+ ],
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/jonny981/loops.git"
21
+ },
22
+ "homepage": "https://github.com/jonny981/loops#readme",
23
+ "bugs": {
24
+ "url": "https://github.com/jonny981/loops/issues"
25
+ },
26
+ "type": "module",
27
+ "main": "./dist/api.js",
28
+ "types": "./dist/api.d.ts",
29
+ "bin": {
30
+ "loops": "./bin/loops.mjs"
31
+ },
32
+ "exports": {
33
+ ".": {
34
+ "types": "./dist/api.d.ts",
35
+ "import": "./dist/api.js"
36
+ },
37
+ "./env/command": {
38
+ "types": "./dist/env/command.d.ts",
39
+ "import": "./dist/env/command.js"
40
+ },
41
+ "./env/sst": {
42
+ "types": "./dist/env/sst.d.ts",
43
+ "import": "./dist/env/sst.js"
44
+ },
45
+ "./env/docker": {
46
+ "types": "./dist/env/docker.d.ts",
47
+ "import": "./dist/env/docker.js"
48
+ },
49
+ "./package.json": "./package.json"
50
+ },
51
+ "files": [
52
+ "dist",
53
+ "bin",
54
+ "skills",
55
+ "README.md",
56
+ "LICENSE"
57
+ ],
58
+ "publishConfig": {
59
+ "access": "public"
60
+ },
61
+ "scripts": {
62
+ "loops": "tsx src/index.ts",
63
+ "build": "tsup",
64
+ "typecheck": "tsc --noEmit",
65
+ "test": "vitest run",
66
+ "test:watch": "vitest",
67
+ "example:poll": "tsx src/index.ts run examples/simple-poll.loop.ts --no-tui",
68
+ "example:gate": "tsx src/index.ts run examples/confidence-gate.loop.ts",
69
+ "prepack": "npm run build",
70
+ "prepublishOnly": "npm run typecheck"
71
+ },
72
+ "engines": {
73
+ "node": ">=20"
74
+ },
75
+ "dependencies": {
76
+ "@anthropic-ai/claude-agent-sdk": "^0.3.185",
77
+ "@anthropic-ai/sdk": "^0.105.0",
78
+ "commander": "^15.0.0",
79
+ "execa": "^9.6.1",
80
+ "ink": "^7.1.0",
81
+ "ms": "^2.1.3",
82
+ "p-limit": "^7.3.0",
83
+ "p-retry": "^8.0.0",
84
+ "p-timeout": "^7.0.1",
85
+ "picocolors": "^1.1.1",
86
+ "react": "^19.2.7",
87
+ "toposort": "^2.0.2",
88
+ "tsx": "^4.22.4",
89
+ "zod": "^4.4.3"
90
+ },
91
+ "devDependencies": {
92
+ "@types/ms": "^2.1.0",
93
+ "@types/node": "^26.0.0",
94
+ "@types/react": "^19.2.17",
95
+ "@types/toposort": "^2.0.7",
96
+ "tsup": "^8.5.1",
97
+ "typescript": "^6.0.3",
98
+ "vitest": "^4.1.9"
99
+ }
100
+ }
@@ -0,0 +1,121 @@
1
+ ---
2
+ name: author-loop
3
+ description: Use when writing, running, or validating a loops `.loop.ts` — the mental model, the honest-convergence gate, the git-memory tiers, the loop archetypes, and copy-paste recipes for authoring convergence loops with the `loops` library. Load this before composing a loop.
4
+ ---
5
+
6
+ # Authoring loops
7
+
8
+ `loops` runs an agent in a convergence loop: do a bit of work with a fresh context, check whether it is *actually* done against a gate you define, and if not, go again. You author a loop as a small TypeScript file, validate it offline, then run it.
9
+
10
+ ## The one idea
11
+
12
+ There is one unit of work and two supporting types:
13
+
14
+ - `Job = (ctx) => Promise<Outcome>` — a unit of work of any size.
15
+ - `Condition = (ctx, last) => Promise<{ met, reason, confidence? }>` — a yes/no gate.
16
+ - `Engine` — where an agent turn runs (a model backend).
17
+
18
+ `loop()` returns a `Job`. `dag()` returns a `Job`. So loops and DAGs **nest both ways**: a DAG node can be a loop, a loop body can be a DAG. Nesting is the absence of a special case. Author with that freedom; do not reach for a node type that only works in one position.
19
+
20
+ ## A loop file
21
+
22
+ A `.loop.ts` `export default`s a `Job`. Wrap it in `defineJob(...)` to pin the type.
23
+
24
+ ```ts
25
+ import { defineJob, loop, agentJob, commandSucceeds, agentCheck } from '@loops-adk/core';
26
+
27
+ export default defineJob(
28
+ loop({
29
+ name: 'build-feature',
30
+ max: 20,
31
+ body: agentJob({
32
+ prompt: (c) => `Iteration ${c.iteration}: make concrete progress on TASK.md.`,
33
+ ground: true, // read the commit log + scratch files before working
34
+ }),
35
+ until: [
36
+ commandSucceeds('npm', ['test']), // ground truth
37
+ agentCheck({ question: 'Does it match TASK.md?', threshold: 0.85 }), // intent
38
+ ],
39
+ commit: { subject: 'feat: TASK.md' }, // one milestone commit on convergence
40
+ }),
41
+ );
42
+ ```
43
+
44
+ `loop()` config worth knowing: `body` (the Job per iteration), `until` (stop gate), `start` (gate before iterating), `stopOn` (hard early-exit), `review` (runs when `until` is met; a failing review folds its findings back into the next iteration), `max` (iteration cap), `delayMs` (polling delay), `commit` (milestone commit on convergence).
45
+
46
+ ## The gate is the whole point
47
+
48
+ The trap this library exists to avoid is "ask the model if it is done" — the model grades its own homework and always says yes. Make the gate **honest**:
49
+
50
+ - Combine a **deterministic** signal (`commandSucceeds('npm', ['test'])` — the tests really pass) with a **separate judge** (`agentCheck`). Prefer this mixed form over a lone judge.
51
+ - `until`/`start`/`stopOn` take one item or many. Arrays are `all` by default; wrap in `any(...)` for or.
52
+ - Harden the judge: `quorum(2, judgeA, judgeB, judgeC)` is a k-of-n jury. `agentCheck({ dimensions: [...] })` opens on the geometric mean, so one weak dimension drags the verdict down.
53
+ - A missing confidence scores 0 (fail-closed). Never lean on the model's self-report alone.
54
+
55
+ ```ts
56
+ until: [
57
+ commandSucceeds('npm', ['test']),
58
+ quorum(2,
59
+ agentCheck({ question: 'Correct?', dimensions: ['intent match', 'evidence', 'no regressions'] }),
60
+ agentCheck({ question: 'Correct?', model: 'opus' }),
61
+ ),
62
+ ],
63
+ ```
64
+
65
+ ## Memory is git
66
+
67
+ Progress accumulates on disk, so each iteration starts with a clean context but not a blank one.
68
+
69
+ - `ground: true` on an `agentJob` reads the recent commit log + this run's scratch files into the next prompt, so a fresh turn knows what was already tried.
70
+ - `commit: { subject }` (or `commit: true`) writes one structured milestone commit on convergence — the reasoning welded to the diff. Later turns ground on it.
71
+ - For long, noisy histories use `ground: { retrieve: true }` (select relevant commits, not recent-N); for indefinite processes add `consolidateJob` to fold history into a bounded, decision-preserving record.
72
+
73
+ ## Three archetypes
74
+
75
+ A loop is not one shape. Pick the one that matches the work:
76
+
77
+ - **Converge** — one hard target, retried until a gate passes: `loop({ until: gate, max })`.
78
+ - **Sweep** — a known worklist, one fresh task each: a `loop`/`dag` over the list.
79
+ - **Tend** — an unbounded process picking the next unit: `loop({ until: dynamicCondition, max })`, body dispatches to a sub-loop (wrap in `isolated(...)` for its own worktree).
80
+
81
+ They nest: triage is Tend ∘ Converge; a research sweep is Sweep ∘ Converge.
82
+
83
+ ## Compose
84
+
85
+ ```ts
86
+ import { dag, sequence, parallel } from '@loops-adk/core';
87
+
88
+ dag({
89
+ name: 'ship',
90
+ nodes: {
91
+ research: agentJob({ label: 'research', prompt: '…' }),
92
+ implement: { needs: ['research'], job: loop({ /* a loop as a node */ }) },
93
+ review: { needs: ['implement'], job: gateJob('review', agentCheck({ /* … */ })) },
94
+ },
95
+ });
96
+ ```
97
+
98
+ `needs` are dependencies; `optional` nodes never block; an unmet `when` skips a node; `isolation: 'worktree'` (on the dag) or `isolate: true` (per node) runs writers in parallel worktrees that land back on pass. `sequence` and `parallel` are sugar over `dag`.
99
+
100
+ ## Author → validate → run
101
+
102
+ ```bash
103
+ loops validate path/to/feature.loop.ts # offline pre-flight: loads + prints the shape, no model calls, no spend
104
+ loops describe path/to/feature.loop.ts # print the loop's shape (gate, body, nodes) without running
105
+ loops run path/to/feature.loop.ts # live Ink TUI
106
+ loops run path/to/feature.loop.ts --no-tui # plain streamed logs
107
+ loops run path/to/feature.loop.ts --json # NDJSON event stream (parse this from an agent)
108
+ ```
109
+
110
+ Always `loops validate` first. It imports and constructs the loop (catching syntax, import, and bad-export errors) without running it, so you fix authoring mistakes for free before spending a single agent turn. It also prints the loop's shape (its gate, body, and dag nodes), so you can confirm you built what you intended. `loops describe` prints that shape on its own.
111
+
112
+ `loops run` works from any repo, including one that uses `loops` as a submodule or dependency. The recipe's folder must be an ES module scope (a `package.json` with `{"type":"module"}`); repos that consume `loops` already have this. If a load fails with an ES-module error, that scope is what is missing.
113
+
114
+ ## Gotchas
115
+
116
+ - **Test offline first.** Use the `mock` engine, or an engine-free `fnJob`/`predicate` body, to prove the loop's shape with zero network. A change to convergence logic deserves a deterministic check, not a live model call.
117
+ - **Conditions default to `all`.** A bare array of conditions must *all* hold. Wrap in `any(...)` when you mean or.
118
+ - **The body is a Job, so it can be another `loop`/`dag`.** Reach for nesting before inventing a new construct.
119
+ - **One milestone, not one commit per iteration.** `commit` fires on convergence. Want finer commits? Compose finer loops or nodes.
120
+
121
+ The full surface is the package's only export (`loops`); see the repo README for engines, environments, budgets, and the PR/forge jobs.