@synaplink/orqlaude 0.3.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 (66) hide show
  1. package/.mcp.json.template +8 -0
  2. package/README.md +239 -0
  3. package/dist/__tests__/hallucination.test.d.ts +1 -0
  4. package/dist/__tests__/hallucination.test.js +65 -0
  5. package/dist/__tests__/hallucination.test.js.map +1 -0
  6. package/dist/__tests__/state.test.d.ts +1 -0
  7. package/dist/__tests__/state.test.js +124 -0
  8. package/dist/__tests__/state.test.js.map +1 -0
  9. package/dist/cli.d.ts +2 -0
  10. package/dist/cli.js +322 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/lib/audit.d.ts +38 -0
  13. package/dist/lib/audit.js +108 -0
  14. package/dist/lib/audit.js.map +1 -0
  15. package/dist/lib/budgeting.d.ts +37 -0
  16. package/dist/lib/budgeting.js +67 -0
  17. package/dist/lib/budgeting.js.map +1 -0
  18. package/dist/lib/hallucination.d.ts +46 -0
  19. package/dist/lib/hallucination.js +154 -0
  20. package/dist/lib/hallucination.js.map +1 -0
  21. package/dist/lib/jsonl_tail.d.ts +40 -0
  22. package/dist/lib/jsonl_tail.js +126 -0
  23. package/dist/lib/jsonl_tail.js.map +1 -0
  24. package/dist/lib/pricing.d.ts +24 -0
  25. package/dist/lib/pricing.js +43 -0
  26. package/dist/lib/pricing.js.map +1 -0
  27. package/dist/lib/state.d.ts +118 -0
  28. package/dist/lib/state.js +138 -0
  29. package/dist/lib/state.js.map +1 -0
  30. package/dist/server.d.ts +2 -0
  31. package/dist/server.js +34 -0
  32. package/dist/server.js.map +1 -0
  33. package/dist/telegram/api.d.ts +45 -0
  34. package/dist/telegram/api.js +59 -0
  35. package/dist/telegram/api.js.map +1 -0
  36. package/dist/telegram/bot.d.ts +11 -0
  37. package/dist/telegram/bot.js +73 -0
  38. package/dist/telegram/bot.js.map +1 -0
  39. package/dist/telegram/commands.d.ts +22 -0
  40. package/dist/telegram/commands.js +217 -0
  41. package/dist/telegram/commands.js.map +1 -0
  42. package/dist/telegram/config.d.ts +30 -0
  43. package/dist/telegram/config.js +37 -0
  44. package/dist/telegram/config.js.map +1 -0
  45. package/dist/telegram/notifier.d.ts +14 -0
  46. package/dist/telegram/notifier.js +136 -0
  47. package/dist/telegram/notifier.js.map +1 -0
  48. package/dist/tools/broker.d.ts +15 -0
  49. package/dist/tools/broker.js +245 -0
  50. package/dist/tools/broker.js.map +1 -0
  51. package/dist/tools/dispatch.d.ts +14 -0
  52. package/dist/tools/dispatch.js +231 -0
  53. package/dist/tools/dispatch.js.map +1 -0
  54. package/dist/tools/lifecycle.d.ts +18 -0
  55. package/dist/tools/lifecycle.js +124 -0
  56. package/dist/tools/lifecycle.js.map +1 -0
  57. package/dist/tools/ping.d.ts +2 -0
  58. package/dist/tools/ping.js +26 -0
  59. package/dist/tools/ping.js.map +1 -0
  60. package/dist/tools/planning.d.ts +4 -0
  61. package/dist/tools/planning.js +160 -0
  62. package/dist/tools/planning.js.map +1 -0
  63. package/dist/tools/review.d.ts +18 -0
  64. package/dist/tools/review.js +93 -0
  65. package/dist/tools/review.js.map +1 -0
  66. package/package.json +56 -0
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "orqlaude": {
4
+ "command": "npx",
5
+ "args": ["-y", "@synaplink/orqlaude"]
6
+ }
7
+ }
8
+ }
package/README.md ADDED
@@ -0,0 +1,239 @@
1
+ # @synaplink/orqlaude
2
+
3
+ Multi-agent orchestrator for Claude Code. One primary Claude session decomposes a complex task into N parallel sub-tasks, gets a single user approval, then dispatches N child agents (each in its own session and worktree) via the Claude Desktop app's native `mcp__ccd_session__spawn_task`. Tracks cost/tokens via JSONL tails, brokers messages between agents, detects hallucination, manages PRs, and can spawn a reviewer agent per PR at the end.
4
+
5
+ The name is **orq**hestrator + **Claude**.
6
+
7
+ > Status: **v0.3.0** — 19 tools, 11 tests passing, CI green. Token-first budgets (Max-friendly), self-registering child agents, hallucination detection, file-claim broker, audit log, resumability, auto-review pipeline, and a **Telegram bot** for fleet notifications + remote control.
8
+
9
+ ## Why orqlaude exists
10
+
11
+ A single Claude agent is great at focused work but slow at multi-region refactors. You can manually spawn parallel sessions via `spawn_task`, but you lose budget oversight, cross-agent coordination, and a single place to see "what's the fleet doing right now?"
12
+
13
+ orqlaude is the thin layer that adds those things. It never spawns processes itself — the Desktop app's `spawn_task` does that — but it owns the *plan*, the *budget*, the *broker*, and the *aggregation*.
14
+
15
+ ## How it fits together
16
+
17
+ ```
18
+ ┌──────────────────────┐
19
+ │ PRIMARY CLAUDE │
20
+ └─────────┬────────────┘
21
+ ┌─── orqlaude.create_plan ──────►│
22
+ │ orqlaude.request_approval ──►│ (relays via AskUserQuestion)
23
+ │ orqlaude.confirm ────►│
24
+ │ orqlaude.next_task ────►│
25
+ │ ccd_session.spawn_task ────►├─── chip ─► ┌──────────┐
26
+ │ │ │ child #1 │ ─► auto-registers via checkin
27
+ │ │ │ session │
28
+ │ orqlaude.next_task ────►│ └────┬─────┘
29
+ │ ccd_session.spawn_task ────►├─── chip ─► │
30
+ │ │ ┌────▼─────┐
31
+ │ │ │ child #2 │
32
+ │ orqlaude.status ────►│ ◄────── claim_files, post_note
33
+ │ orqlaude.poll_notes ────►│ ◄────── PR url via post_note
34
+ │ orqlaude.send_message ────►│
35
+ │ orqlaude.collect ────►│
36
+ │ orqlaude.review_prs ────►├─── chip ─► reviewer #1
37
+ │ ├─── chip ─► reviewer #2
38
+ └────────────────────────────────┘
39
+ ```
40
+
41
+ ## Install
42
+
43
+ ```sh
44
+ npm install -g @synaplink/orqlaude # CLI + MCP server
45
+ ```
46
+
47
+ In your project, add to `.mcp.json` (or copy `.mcp.json.template`):
48
+
49
+ ```json
50
+ {
51
+ "mcpServers": {
52
+ "orqlaude": {
53
+ "command": "npx",
54
+ "args": ["-y", "@synaplink/orqlaude"]
55
+ }
56
+ }
57
+ }
58
+ ```
59
+
60
+ Restart your Claude Code session. The `mcp__orqlaude__*` tools will appear.
61
+
62
+ ## Tool reference
63
+
64
+ ### Planning (primary Claude)
65
+
66
+ | Tool | Purpose |
67
+ |---|---|
68
+ | `create_plan(root_task, tasks[], budget_cap_tokens?, model_for_estimate?, effort_multiplier?)` | Register a fleet. Returns `plan_id`. Budget is in TOKENS (Max-plan friendly); USD is informational. |
69
+ | `estimate(plan_id, model?, effort_multiplier?)` | Recompute cost/duration estimates. |
70
+ | `request_approval(plan_id)` | Returns `approval_token` and a prebuilt `ask_user_question` payload. Surfaces your daily token usage from the Desktop app's `buddy-tokens.json`. |
71
+ | `confirm(plan_id, approval_token)` | Lock the plan after user approves. |
72
+
73
+ ### Dispatch (primary Claude)
74
+
75
+ | Tool | Purpose |
76
+ |---|---|
77
+ | `next_task(plan_id)` | Pull the next pending task. Returned `prompt` embeds `plan_id` + `task_id` and instructs the agent to self-register via `checkin` on its first turn. |
78
+ | `status(plan_id)` | Per-agent live snapshot: cost, tokens, last activity, current tool, terminated yes/no, **hallucination report**. Auto-cancels and STOPs all agents if total tokens exceed the cap. |
79
+ | `collect(plan_id)` | Aggregated PR URLs, summaries, costs, exit reasons. |
80
+ | `review_prs(plan_id, auto_approve?, budget_cap_tokens?)` | Spawn a reviewer agent against each PR produced by `plan_id`. Creates a new "review plan" auto-approved by default. |
81
+ | `register_spawn(plan_id, task_id, session_id)` | Manual fallback if a child fails to self-register. Rarely needed. |
82
+
83
+ ### Broker
84
+
85
+ | Tool | Caller | Purpose |
86
+ |---|---|---|
87
+ | `checkin(session_id, task_id?)` | child agent | **First call**: pass `task_id` to self-register. Subsequent calls: pull queued messages, see STOP signals, ack state of blocking notes. |
88
+ | `post_note(session_id, text, blocking?, pr_url?)` | child agent | Share findings or report a PR URL. `blocking: true` pauses until acked. |
89
+ | `claim_files(session_id, paths[], reason?)` | child agent | Register intent to edit specific files. Conflicting claims by other agents surface to the caller. |
90
+ | `release_files(session_id, paths[])` | child agent | Release claims after finishing. |
91
+ | `poll_notes(plan_id, since_ts?, mark_acked?)` | primary Claude | Read agent notes; ack blocking ones to unblock posters. |
92
+ | `send_message(plan_id, to_session_id, text, from_task_id?, kind?)` | primary Claude | Queue a directed message. `kind: "stop"` triggers child commit-and-exit. |
93
+
94
+ ### Lifecycle
95
+
96
+ | Tool | Purpose |
97
+ |---|---|
98
+ | `kill_task(plan_id, task_id, reason)` | Queue STOP broker message; returns session_id ready for `archive_session`. Use for hallucinating/looping agents. |
99
+ | `resume_plan(plan_id)` | Pick up an in-flight plan after a Desktop-app restart or new session. Refreshes per-task status from JSONL, returns a "do this next" hint. |
100
+ | `list_plans(include_collected?)` | All plans known to orqlaude in this project, active first. |
101
+
102
+ ### Health
103
+
104
+ | Tool | Purpose |
105
+ |---|---|
106
+ | `ping(echo?)` | Returns version, cwd, node, pid. |
107
+
108
+ ## End-to-end walkthrough
109
+
110
+ User says: *"Refactor the auth system — magic-link login, update the docs, add tests."* You judge it as parallelizable.
111
+
112
+ 1. `orqlaude.create_plan` with 3 subtasks (auth-core, docs, tests), `budget_cap_tokens: 600000`.
113
+ 2. `orqlaude.request_approval` → returns `approval_token` and a prebuilt question payload showing your remaining daily quota.
114
+ 3. You call `AskUserQuestion` with that payload. User picks "Approve and spawn".
115
+ 4. `orqlaude.confirm`.
116
+ 5. Loop three times:
117
+ - `orqlaude.next_task` → returns a task with the wrapped prompt
118
+ - `mcp__ccd_session__spawn_task` with the title/prompt/tldr → user clicks the chip
119
+ - The spawned agent calls `orqlaude.checkin(session_id, task_id)` on first turn → self-registers
120
+ 6. Periodically: `orqlaude.status` (shows hallucination scores) + `orqlaude.poll_notes`. Forward cross-cutting info via `send_message`. If an agent goes off the rails, `kill_task`.
121
+ 7. Agents call `orqlaude.post_note(..., pr_url=...)` when their PR is open.
122
+ 8. `orqlaude.collect` → three PR URLs and summaries.
123
+ 9. **NEW**: `orqlaude.review_prs(plan_id)` → spawns three reviewer agents, one per PR. Each reviews, runs tests, posts findings. You aggregate the second-round notes.
124
+
125
+ ## State
126
+
127
+ State lives in `<project>/.orqlaude/`:
128
+
129
+ - `orqlaude-state.json` — plans, tasks, notes, messages, claims. Atomic write via temp+rename.
130
+ - `audit.jsonl` — append-only log of every tool call: `{ ts, tool, args (redacted), ok, durationMs, plan/session ids, summary }`. Inspect with `orqlaude history` or `tail -f .orqlaude/audit.jsonl | jq`.
131
+
132
+ Both human-readable. Both `.gitignore`d.
133
+
134
+ ## Hallucination detection
135
+
136
+ `status()` runs two deterministic checks per agent:
137
+
138
+ 1. **Path-existence**: every `file_path` referenced in `Read/Edit/Write/Grep` tool calls is checked against the worktree. >30% missing or ≥3 missing = `moderate`/`severe` flag.
139
+ 2. **Tool-pattern sanity**:
140
+ - Edit on a file that wasn't read first
141
+ - Same tool call repeated ≥3 times (loop)
142
+ - `git commit` without any prior `npm test`/`tsc`/`pytest`/etc.
143
+
144
+ Each agent gets a `hallucination_score` (0–1) and `concerns: string[]`. The aggregated `hallucination_alerts` list surfaces to the primary Claude so it can `send_message` a nudge or `kill_task`. False positives are acceptable here — we surface concerns, we don't auto-kill.
145
+
146
+ A future v0.3 can add a Check 3: periodic Haiku cross-validation of recent activity (costs tokens, opt-in).
147
+
148
+ ## CLI
149
+
150
+ ```sh
151
+ orqlaude list # every plan in this project
152
+ orqlaude status <plan_id> # refreshed status of one plan
153
+ orqlaude show <plan_id> # raw plan JSON
154
+ orqlaude history --limit 50 # tail audit log
155
+ orqlaude help
156
+ ```
157
+
158
+ Read-only. For active orchestration, use the MCP from inside Claude Code.
159
+
160
+ ## Repo layout
161
+
162
+ ```
163
+ orqlaude/
164
+ ├── package.json # @synaplink/orqlaude
165
+ ├── tsconfig.json
166
+ ├── .mcp.json # local dev wiring
167
+ ├── .mcp.json.template # production wiring (npx-based)
168
+ ├── .github/workflows/ci.yml # typecheck + build + test
169
+ ├── src/
170
+ │ ├── server.ts # MCP stdio entry
171
+ │ ├── cli.ts # `orqlaude` CLI binary
172
+ │ ├── lib/
173
+ │ │ ├── state.ts # JSON-backed ledger, schema v2
174
+ │ │ ├── budgeting.ts # token-first budget, daily quota reader
175
+ │ │ ├── pricing.ts # USD pricing table (informational)
176
+ │ │ ├── hallucination.ts # deterministic detectors
177
+ │ │ ├── jsonl_tail.ts # cached byte-offset session tail
178
+ │ │ └── audit.ts # append-only audit log
179
+ │ ├── tools/
180
+ │ │ ├── ping.ts
181
+ │ │ ├── planning.ts # create_plan, estimate, request_approval, confirm
182
+ │ │ ├── dispatch.ts # next_task, register_spawn, status, collect
183
+ │ │ ├── broker.ts # checkin, post_note, claim_files, release_files, poll_notes, send_message
184
+ │ │ ├── lifecycle.ts # kill_task, resume_plan, list_plans
185
+ │ │ └── review.ts # review_prs
186
+ │ └── __tests__/
187
+ │ ├── state.test.ts
188
+ │ └── hallucination.test.ts
189
+ └── dist/ # tsc output (published to npm)
190
+ ```
191
+
192
+ ## Telegram bot
193
+
194
+ orqlaude can notify you on Telegram when fleet events happen and accept commands from your phone.
195
+
196
+ ```sh
197
+ # One-time setup (creates ~/.orqlaude/telegram.json, mode 600)
198
+ orqlaude tg setup
199
+ # (paste your bot token from @BotFather)
200
+
201
+ # Message your bot /start in Telegram to learn your user id, then:
202
+ orqlaude tg whitelist <your_user_id> --owner --label "you"
203
+
204
+ # Run the bot (foreground; daemonize with launchctl / systemd / nohup as you prefer)
205
+ cd /path/to/your/project
206
+ orqlaude tg start
207
+ ```
208
+
209
+ **Notifications pushed to you:**
210
+ - 📋 New plan created
211
+ - ✅ Plan approved (spawn imminent)
212
+ - ✓ Task done (with PR URL)
213
+ - ❌ Task failed / 🛑 cancelled
214
+ - 📝 New agent note (with severity from `post_note`)
215
+ - 💸 Auto-cancel on budget overrun
216
+ - 🎉 Fleet collected
217
+
218
+ **Commands you can send (whitelisted users only):**
219
+ - `/plans` — active plans
220
+ - `/status <plan_id>` — refreshed task list with token usage
221
+ - `/show <plan_id>` — raw plan JSON
222
+ - `/notes <plan_id>` — recent agent notes
223
+ - `/kill <plan_id> <task_id> <reason>` — STOP a runaway agent
224
+ - `/whitelist <user_id> [label]` (owner-only) — add another user
225
+ - `/help` / `/whoami`
226
+
227
+ The bot uses raw `fetch` against Telegram's Bot API — zero extra deps. State is shared with the MCP via the same `StateStore`, so commands take effect on the next status() / checkin().
228
+
229
+ ## Known gaps (v0.3 → v0.4 roadmap)
230
+
231
+ - **Cost-learning estimates** — current baselines are tuned to a single Haiku probe. Future: write per-task realized costs to history and use moving averages.
232
+ - **N chips = N clicks** — Anthropic's `spawn_task` is per-click by design. Worth filing as feedback. Until then, batch-spawn isn't possible through that API.
233
+ - **Second-model hallucination check** — periodic Haiku cross-validation of recent activity, opt-in.
234
+ - **Multi-project Telegram bot** — currently the bot watches a single project. Multi-project watching is a small extension to the config schema.
235
+ - **Inline approve buttons in Telegram** — `/approve <plan_id>` and inline keyboards so you can confirm fleets from your phone.
236
+
237
+ ## License
238
+
239
+ MIT.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,65 @@
1
+ import { test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { promises as fs } from "node:fs";
4
+ import path from "node:path";
5
+ import os from "node:os";
6
+ import { detectHallucination } from "../lib/hallucination.js";
7
+ async function tmpRepo() {
8
+ return fs.mkdtemp(path.join(os.tmpdir(), "orqlaude-hallu-"));
9
+ }
10
+ test("clean session scores 0", async () => {
11
+ const cwd = await tmpRepo();
12
+ const real = path.join(cwd, "real.ts");
13
+ await fs.writeFile(real, "// hi");
14
+ const tools = [
15
+ { name: "Read", input: { file_path: real } },
16
+ { name: "Edit", input: { file_path: real, old_string: "hi", new_string: "hello" } },
17
+ { name: "Bash", input: { command: "npm test" } },
18
+ { name: "Bash", input: { command: "git commit -m 'fix'" } },
19
+ ];
20
+ const report = await detectHallucination(tools, cwd);
21
+ assert.equal(report.score, 0);
22
+ assert.equal(report.level, "clean");
23
+ });
24
+ test("references to nonexistent paths score moderate-or-higher", async () => {
25
+ const cwd = await tmpRepo();
26
+ const tools = [
27
+ { name: "Read", input: { file_path: path.join(cwd, "nope-a.ts") } },
28
+ { name: "Read", input: { file_path: path.join(cwd, "nope-b.ts") } },
29
+ { name: "Read", input: { file_path: path.join(cwd, "nope-c.ts") } },
30
+ { name: "Edit", input: { file_path: path.join(cwd, "nope-d.ts"), old_string: "x", new_string: "y" } },
31
+ ];
32
+ const report = await detectHallucination(tools, cwd);
33
+ assert.ok(report.score >= 0.3, `expected score >= 0.3 (moderate), got ${report.score}`);
34
+ assert.match(report.concerns[0] ?? "", /don't exist/);
35
+ });
36
+ test("edit without prior read flags concern", async () => {
37
+ const cwd = await tmpRepo();
38
+ const real = path.join(cwd, "real.ts");
39
+ await fs.writeFile(real, "x");
40
+ const tools = [{ name: "Edit", input: { file_path: real, old_string: "x", new_string: "y" } }];
41
+ const report = await detectHallucination(tools, cwd);
42
+ assert.ok(report.concerns.some((c) => /without first reading/.test(c)));
43
+ });
44
+ test("tight loop of identical tool calls flags concern", async () => {
45
+ const cwd = await tmpRepo();
46
+ const real = path.join(cwd, "real.ts");
47
+ await fs.writeFile(real, "x");
48
+ const sameCall = { name: "Read", input: { file_path: real } };
49
+ const tools = [sameCall, sameCall, sameCall, sameCall, sameCall];
50
+ const report = await detectHallucination(tools, cwd);
51
+ assert.ok(report.concerns.some((c) => /loop/i.test(c) || /Repeated/.test(c)));
52
+ });
53
+ test("commit without tests flags concern", async () => {
54
+ const cwd = await tmpRepo();
55
+ const real = path.join(cwd, "real.ts");
56
+ await fs.writeFile(real, "x");
57
+ const tools = [
58
+ { name: "Read", input: { file_path: real } },
59
+ { name: "Edit", input: { file_path: real, old_string: "x", new_string: "y" } },
60
+ { name: "Bash", input: { command: "git add -A && git commit -m 'wip'" } },
61
+ ];
62
+ const report = await detectHallucination(tools, cwd);
63
+ assert.ok(report.concerns.some((c) => /without running tests/.test(c)));
64
+ });
65
+ //# sourceMappingURL=hallucination.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hallucination.test.js","sourceRoot":"","sources":["../../src/__tests__/hallucination.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,124 @@
1
+ import { test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { promises as fs } from "node:fs";
4
+ import path from "node:path";
5
+ import os from "node:os";
6
+ import { StateStore, newPlan, findPlan, unclaimedTaskById, normalizeClaimPath, } from "../lib/state.js";
7
+ async function tmpStore() {
8
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), "orqlaude-test-"));
9
+ return { store: new StateStore(dir), dir };
10
+ }
11
+ test("create plan + persist + reload preserves shape", async () => {
12
+ const { store, dir } = await tmpStore();
13
+ const plan = await store.update((s) => {
14
+ const p = newPlan("root task", 100_000, [
15
+ { title: "T1", prompt: "p1", tldr: "tldr1" },
16
+ { title: "T2", prompt: "p2", tldr: "tldr2" },
17
+ ]);
18
+ s.plans[p.id] = p;
19
+ return p;
20
+ });
21
+ // New store instance: forces reload from disk.
22
+ const fresh = new StateStore(dir);
23
+ const reloaded = await fresh.read((s) => findPlan(s, plan.id));
24
+ assert.equal(reloaded.tasks.length, 2);
25
+ assert.equal(reloaded.budgetCapTokens, 100_000);
26
+ assert.equal(reloaded.perAgentCapTokens, 50_000);
27
+ assert.equal(reloaded.tasks[0].status, "pending");
28
+ });
29
+ test("approval token mismatch is rejected", async () => {
30
+ const { store } = await tmpStore();
31
+ const plan = await store.update((s) => {
32
+ const p = newPlan("root", 50_000, [{ title: "T", prompt: "p", tldr: "tl" }]);
33
+ p.status = "awaiting_approval";
34
+ p.approvalToken = "the-real-token";
35
+ s.plans[p.id] = p;
36
+ return p;
37
+ });
38
+ await assert.rejects(async () => {
39
+ await store.update((s) => {
40
+ const p = findPlan(s, plan.id);
41
+ if (p.approvalToken !== "wrong")
42
+ throw new Error("Approval token mismatch.");
43
+ p.status = "approved";
44
+ });
45
+ }, /Approval token mismatch/);
46
+ });
47
+ test("unclaimedTaskById finds dispatched-but-unowned task", async () => {
48
+ const { store } = await tmpStore();
49
+ let taskId = "";
50
+ await store.update((s) => {
51
+ const p = newPlan("root", 50_000, [{ title: "T", prompt: "p", tldr: "tl" }]);
52
+ p.tasks[0].status = "dispatched";
53
+ taskId = p.tasks[0].id;
54
+ s.plans[p.id] = p;
55
+ });
56
+ const found = await store.read((s) => unclaimedTaskById(s, taskId));
57
+ assert.ok(found, "should find unclaimed task");
58
+ assert.equal(found.task.id, taskId);
59
+ // After claiming, it no longer appears.
60
+ await store.update((s) => {
61
+ const f = unclaimedTaskById(s, taskId);
62
+ f.task.spawnedSessionId = "some-uuid";
63
+ });
64
+ const found2 = await store.read((s) => unclaimedTaskById(s, taskId));
65
+ assert.equal(found2, undefined);
66
+ });
67
+ test("normalizeClaimPath: relative paths resolve against cwd", () => {
68
+ const cwd = "/repo/root";
69
+ assert.equal(normalizeClaimPath("src/foo.ts", cwd), "/repo/root/src/foo.ts");
70
+ assert.equal(normalizeClaimPath("/abs/path.ts", cwd), "/abs/path.ts");
71
+ assert.equal(normalizeClaimPath("./a/../b.ts", cwd), "/repo/root/b.ts");
72
+ });
73
+ test("concurrent updates serialize correctly", async () => {
74
+ const { store } = await tmpStore();
75
+ let planId = "";
76
+ await store.update((s) => {
77
+ const p = newPlan("root", 100_000, [{ title: "T", prompt: "p", tldr: "tl" }]);
78
+ planId = p.id;
79
+ s.plans[p.id] = p;
80
+ });
81
+ // Fire 20 concurrent appends to the notes array. With serialization, all 20 land.
82
+ const writes = Array.from({ length: 20 }, (_, i) => store.update((s) => {
83
+ findPlan(s, planId).notes.push({
84
+ id: `note-${i}`,
85
+ fromSessionId: "x",
86
+ taskId: "y",
87
+ text: `n${i}`,
88
+ blocking: false,
89
+ postedAt: Date.now(),
90
+ acked: false,
91
+ });
92
+ }));
93
+ await Promise.all(writes);
94
+ const final = await store.read((s) => findPlan(s, planId));
95
+ assert.equal(final.notes.length, 20);
96
+ });
97
+ test("schema v1 migrates to v2 with token caps synthesized", async () => {
98
+ const { store, dir } = await tmpStore();
99
+ // Write a v1-shaped state file directly.
100
+ const v1 = {
101
+ schemaVersion: 1,
102
+ plans: {
103
+ "p1": {
104
+ id: "p1",
105
+ createdAt: 1,
106
+ rootTask: "old",
107
+ budgetCapUsd: 4,
108
+ perAgentCapUsd: 2,
109
+ status: "draft",
110
+ tasks: [],
111
+ notes: [],
112
+ messages: [],
113
+ },
114
+ },
115
+ };
116
+ await fs.writeFile(path.join(dir, "orqlaude-state.json"), JSON.stringify(v1));
117
+ // Reload via a new store (the test store already cached empty).
118
+ const fresh = new StateStore(dir);
119
+ const migrated = await fresh.read((s) => s);
120
+ assert.equal(migrated.schemaVersion, 2);
121
+ assert.equal(migrated.plans["p1"].budgetCapTokens, 4 * 25_000);
122
+ assert.deepEqual(migrated.plans["p1"].claims, []);
123
+ });
124
+ //# sourceMappingURL=state.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.test.js","sourceRoot":"","sources":["../../src/__tests__/state.test.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};