@mobrienv/autoloop 0.7.4 → 0.8.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.
package/README.md CHANGED
@@ -59,6 +59,9 @@ If Ralph is the broader experimentation surface, autoloop is the cleaner runtime
59
59
  - **Designed for agents too** -- autoloop is intentionally shaped as a surface that agents can use directly, not just a human operator CLI
60
60
  - **Dynamic chains** -- compose presets into multi-stage pipelines
61
61
  - **Persistent memory + profiles** -- loops accumulate learnings across runs and can load repo/user tuning profiles
62
+ - **Cost budgets + stall protection** -- stop runaway loops automatically with `event_loop.max_cost_usd` (journaled token cost) and `event_loop.stall_iterations` (repeated identical outputs)
63
+ - **Runtime budgets** -- bound wall-clock time with `event_loop.max_runtime` (whole loop) and `event_loop.max_iteration_runtime` (per iteration, for iterations that wait days on long-running workflows); both take `"3d"`/`"12h"`-style durations
64
+ - **Finish notifications** -- run any command when a loop ends (`[notify] command = "..."`) with run metadata in env vars and a JSON payload on stdin
62
65
 
63
66
  ## What autoloop is
64
67
 
@@ -140,23 +143,34 @@ The main commands are:
140
143
 
141
144
  ```
142
145
  autoloop run <preset-name|preset-dir> [prompt...] [flags]
143
- autoloop list
144
- autoloop loops [--all]
145
- autoloop loops show <run-id>
146
- autoloop loops artifacts <run-id>
146
+ autoloop init [--preset <name>] [dir]
147
+ autoloop list [--json]
148
+ autoloop loops [--all] [--json]
149
+ autoloop loops show <run-id> [--json]
150
+ autoloop loops artifacts <run-id> [--json]
147
151
  autoloop loops watch <run-id>
152
+ autoloop loops health [--verbose] [--json]
148
153
  autoloop inspect <artifact> [selector] [project-dir] [--format <md|terminal|text|json|csv|graph>]
149
154
  autoloop dashboard [--port <port>]
150
- autoloop worktree <list|show|merge|clean> [args]
151
- autoloop chain <list|run> [args]
155
+ autoloop worktree <list|show|diff|merge|clean> [args]
156
+ autoloop chain list [project-dir]
157
+ autoloop chain run <name> [project-dir] [prompt...] [--dry-run] [--json]
152
158
  autoloop emit <topic> [summary]
153
- autoloop memory <list|status|find|add|remove> [args]
159
+ autoloop memory <list|status|find|add|remove|compact|prune> [args]
154
160
  autoloop task <add|complete|update|remove|list> [args]
155
161
  autoloop runs clean [--max-age <days>]
162
+ autoloop stats [project-dir] [--json]
163
+ autoloop doctor [project-dir] [--json]
164
+ autoloop triage [project-dir] [--json]
156
165
  autoloop config <show|set|unset|path> [args]
166
+ autoloop capabilities
167
+ autoloop robot-docs
168
+ autoloop --version
157
169
  ```
158
170
 
159
- Use `run` to start work, `loops` and `inspect` to understand what happened, and `dashboard` when you want a local operator surface.
171
+ Use `run` to start work, `loops` and `inspect` to understand what happened, and `dashboard` when you want a local operator surface. `stats` summarizes success rates and cost per preset; `doctor` diagnoses the environment and `.autoloop` state. `triage` is the one-call status command for agents — runs, health, doctor, and recommended next commands in a single (optionally `--json`) invocation. `capabilities` prints the machine-readable CLI contract (commands, JSON surfaces, exit codes) and `robot-docs` prints a paste-ready agent handbook.
172
+
173
+ Errors always go to stderr with a documented exit code (0 success, 1 user-input error, 2 environment error), and mistyped commands, subcommands, and flags get a "Did you mean" correction instead of being misinterpreted.
160
174
 
161
175
  ### Flags
162
176
 
@@ -164,7 +178,7 @@ Use `run` to start work, `loops` and `inspect` to understand what happened, and
164
178
  |------|-------------|
165
179
  | `-h`, `--help` | Show usage |
166
180
  | `-v`, `--verbose` | Debug-level logging |
167
- | `-b`, `--backend` | Override backend command (e.g. `-b claude`) |
181
+ | `-b`, `--backend` | Override backend (e.g. `-b claude-sdk`, `-b pi`, `-b kiro`, or a command) |
168
182
  | `-p`, `--preset` | Resolve a named or custom preset |
169
183
  | `--chain` | Run an inline chain of comma-separated presets |
170
184
  | `--profile <spec>` | Activate a profile (`repo:<name>` or `user:<name>`), repeatable |
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Deterministic mock ACP stdio server for autoloop integration tests.
4
+ *
5
+ * Speaks newline-delimited JSON-RPC 2.0 on stdin/stdout, implementing the
6
+ * minimal agent surface the autoloop ACP client exercises:
7
+ *
8
+ * initialize → echoes the requested protocol version
9
+ * session/new → returns a fixed session id
10
+ * session/set_mode → records the mode id
11
+ * session/set_model → records the model id
12
+ * session/prompt → requests tool permission from the client, streams an
13
+ * agent_message_chunk that reports the recorded
14
+ * mode/model/permission outcome, then ends the turn
15
+ *
16
+ * Environment knobs:
17
+ * MOCK_ACP_RESPONSE extra response text (defaults to "LOOP_COMPLETE")
18
+ * MOCK_ACP_KIRO_NOISE when "1", emits a provider-private `_kiro.dev/...`
19
+ * notification that ACP clients must filter out
20
+ */
21
+ export {};
@@ -0,0 +1,139 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Deterministic mock ACP stdio server for autoloop integration tests.
4
+ *
5
+ * Speaks newline-delimited JSON-RPC 2.0 on stdin/stdout, implementing the
6
+ * minimal agent surface the autoloop ACP client exercises:
7
+ *
8
+ * initialize → echoes the requested protocol version
9
+ * session/new → returns a fixed session id
10
+ * session/set_mode → records the mode id
11
+ * session/set_model → records the model id
12
+ * session/prompt → requests tool permission from the client, streams an
13
+ * agent_message_chunk that reports the recorded
14
+ * mode/model/permission outcome, then ends the turn
15
+ *
16
+ * Environment knobs:
17
+ * MOCK_ACP_RESPONSE extra response text (defaults to "LOOP_COMPLETE")
18
+ * MOCK_ACP_KIRO_NOISE when "1", emits a provider-private `_kiro.dev/...`
19
+ * notification that ACP clients must filter out
20
+ */
21
+ let modeId = "none";
22
+ let modelId = "none";
23
+ let nextOutboundId = 1000;
24
+ const pendingOutbound = new Map();
25
+ function send(message) {
26
+ process.stdout.write(`${JSON.stringify({ jsonrpc: "2.0", ...message })}\n`);
27
+ }
28
+ function respond(id, result) {
29
+ send({ id, result });
30
+ }
31
+ function notify(method, params) {
32
+ send({ method, params });
33
+ }
34
+ function request(method, params) {
35
+ const id = nextOutboundId++;
36
+ return new Promise((resolve) => {
37
+ pendingOutbound.set(id, resolve);
38
+ send({ id, method, params });
39
+ });
40
+ }
41
+ async function handlePrompt(msg) {
42
+ const params = msg.params;
43
+ const permission = (await request("session/request_permission", {
44
+ sessionId: params.sessionId,
45
+ toolCall: {
46
+ toolCallId: "mock-tool-1",
47
+ title: "mock tool",
48
+ kind: "execute",
49
+ status: "pending",
50
+ },
51
+ options: [
52
+ { optionId: "allow", name: "Allow always", kind: "allow_always" },
53
+ { optionId: "reject", name: "Reject", kind: "reject_once" },
54
+ ],
55
+ }));
56
+ const outcome = permission?.outcome?.outcome ?? "none";
57
+ const optionId = permission?.outcome?.optionId ?? "none";
58
+ if (process.env.MOCK_ACP_KIRO_NOISE === "1") {
59
+ notify("_kiro.dev/heartbeat", { noise: true });
60
+ }
61
+ const extra = process.env.MOCK_ACP_RESPONSE ?? "LOOP_COMPLETE";
62
+ const text = `mock-acp mode=${modeId} model=${modelId} permission=${outcome}:${optionId}\n${extra}`;
63
+ notify("session/update", {
64
+ sessionId: params.sessionId,
65
+ update: {
66
+ sessionUpdate: "agent_message_chunk",
67
+ content: { type: "text", text },
68
+ },
69
+ });
70
+ if (msg.id !== undefined)
71
+ respond(msg.id, { stopReason: "end_turn" });
72
+ }
73
+ function handleMessage(msg) {
74
+ // Response to one of our outbound requests (permission flow)
75
+ if (msg.id !== undefined && msg.method === undefined) {
76
+ const resolve = pendingOutbound.get(msg.id);
77
+ if (resolve) {
78
+ pendingOutbound.delete(msg.id);
79
+ resolve(msg.result);
80
+ }
81
+ return;
82
+ }
83
+ switch (msg.method) {
84
+ case "initialize": {
85
+ const params = msg.params;
86
+ if (msg.id !== undefined)
87
+ respond(msg.id, {
88
+ protocolVersion: params?.protocolVersion ?? 1,
89
+ agentCapabilities: {},
90
+ });
91
+ return;
92
+ }
93
+ case "session/new":
94
+ if (msg.id !== undefined)
95
+ respond(msg.id, { sessionId: "mock-acp-session" });
96
+ return;
97
+ case "session/set_mode": {
98
+ modeId = String(msg.params?.modeId ?? "none");
99
+ if (msg.id !== undefined)
100
+ respond(msg.id, {});
101
+ return;
102
+ }
103
+ case "session/set_model": {
104
+ modelId = String(msg.params?.modelId ?? "none");
105
+ if (msg.id !== undefined)
106
+ respond(msg.id, {});
107
+ return;
108
+ }
109
+ case "session/prompt":
110
+ void handlePrompt(msg);
111
+ return;
112
+ default:
113
+ // Unknown request → empty result; notifications (e.g. session/cancel) ignored
114
+ if (msg.id !== undefined)
115
+ respond(msg.id, {});
116
+ return;
117
+ }
118
+ }
119
+ let buffer = "";
120
+ process.stdin.setEncoding("utf-8");
121
+ process.stdin.on("data", (chunk) => {
122
+ buffer += chunk;
123
+ const lines = buffer.split("\n");
124
+ buffer = lines.pop() ?? "";
125
+ for (const line of lines) {
126
+ const trimmed = line.trim();
127
+ if (!trimmed)
128
+ continue;
129
+ try {
130
+ handleMessage(JSON.parse(trimmed));
131
+ }
132
+ catch {
133
+ /* skip malformed input */
134
+ }
135
+ }
136
+ });
137
+ process.stdin.on("end", () => process.exit(0));
138
+ export {};
139
+ //# sourceMappingURL=mock-acp-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-acp-server.js","sourceRoot":"","sources":["../../src/testing/mock-acp-server.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mobrienv/autoloop",
3
- "version": "0.7.4",
3
+ "version": "0.8.0",
4
4
  "license": "MIT",
5
5
  "description": "Simpler, opinionated loop harness for inspectable long-running agent workflows",
6
6
  "type": "module",
@@ -90,9 +90,9 @@
90
90
  "vitest": "^3.0.0"
91
91
  },
92
92
  "dependencies": {
93
- "@mobrienv/autoloop-cli": "0.7.4",
94
- "@mobrienv/autoloop-core": "0.7.4",
95
- "@mobrienv/autoloop-harness": "0.7.4",
96
- "@mobrienv/autoloop-presets": "0.7.4"
93
+ "@mobrienv/autoloop-cli": "0.8.0",
94
+ "@mobrienv/autoloop-core": "0.8.0",
95
+ "@mobrienv/autoloop-harness": "0.8.0",
96
+ "@mobrienv/autoloop-presets": "0.8.0"
97
97
  }
98
98
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autoloop",
3
- "version": "0.7.4",
3
+ "version": "0.8.0",
4
4
  "description": "Run and operate autoloops — start runs, monitor progress, inject guidance, manage worktrees, inspect artifacts, and clean up.",
5
5
  "homepage": "https://github.com/mikeyobrien/autoloop-ts",
6
6
  "repository": "https://github.com/mikeyobrien/autoloop-ts",
@@ -32,7 +32,7 @@ autoloop run --chain autocode,autoqa "objective" --worktree
32
32
 
33
33
  **When to use `--automerge`:** When the user wants hands-off operation. Appends an automerge step that squash-merges on completion.
34
34
 
35
- **Backend override:** Use `-b claude` or `-b pi` or `-b kiro` to switch backends.
35
+ **Backend override:** Use `-b claude`, `-b pi`, `-b kiro`, `-b claude-agent-acp`, or `-b acp:<provider>:<command>` to switch backends.
36
36
 
37
37
  Use `run_in_background: true` on the Bash tool when starting runs so the user isn't blocked.
38
38