@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
package/dist/cli.js ADDED
@@ -0,0 +1,322 @@
1
+ #!/usr/bin/env node
2
+ import path from "node:path";
3
+ import readline from "node:readline/promises";
4
+ import { stdin, stdout } from "node:process";
5
+ import { StateStore } from "./lib/state.js";
6
+ import { AuditLog } from "./lib/audit.js";
7
+ import { snapshotSession } from "./lib/jsonl_tail.js";
8
+ import { loadConfig, saveConfig, CONFIG_PATH } from "./telegram/config.js";
9
+ import { TelegramApi } from "./telegram/api.js";
10
+ import { runBot } from "./telegram/bot.js";
11
+ /**
12
+ * orqlaude CLI — read-only inspection of state + audit log + Telegram setup
13
+ * and run.
14
+ *
15
+ * Subcommands:
16
+ * orqlaude list / status / show / history — read-only project inspection
17
+ * orqlaude tg setup / whitelist / start / test / show / help
18
+ */
19
+ const STATE_DIR = process.env.ORQLAUDE_STATE_DIR ?? path.join(process.cwd(), ".orqlaude");
20
+ async function main() {
21
+ const [cmd, ...rest] = process.argv.slice(2);
22
+ switch (cmd) {
23
+ case undefined:
24
+ case "help":
25
+ case "-h":
26
+ case "--help":
27
+ printHelp();
28
+ return 0;
29
+ case "list":
30
+ return await cmdList();
31
+ case "status":
32
+ return await cmdStatus(rest[0]);
33
+ case "show":
34
+ return await cmdShow(rest[0]);
35
+ case "history":
36
+ return await cmdHistory(parseLimit(rest));
37
+ case "tg":
38
+ return await cmdTg(rest);
39
+ default:
40
+ console.error(`Unknown subcommand: ${cmd}`);
41
+ printHelp();
42
+ return 1;
43
+ }
44
+ }
45
+ function printHelp() {
46
+ console.log(`orqlaude — multi-agent orchestrator for Claude Code
47
+
48
+ Inspection:
49
+ orqlaude list List every plan in this project
50
+ orqlaude status <plan_id> Refreshed status of one plan
51
+ orqlaude show <plan_id> Raw plan JSON
52
+ orqlaude history [--limit N] Tail the audit log (default 30)
53
+
54
+ Telegram:
55
+ orqlaude tg setup Configure bot token (interactive)
56
+ orqlaude tg whitelist <id> [--owner] [--label NAME]
57
+ orqlaude tg unwhitelist <id>
58
+ orqlaude tg show Show current Telegram config
59
+ orqlaude tg test <chat_id> Send a test message
60
+ orqlaude tg start Run the bot (foreground, monitors this project)
61
+ orqlaude tg help Telegram-specific help
62
+
63
+ State dir: ${STATE_DIR}`);
64
+ }
65
+ // ---- read-only inspection -------------------------------------------------
66
+ async function cmdList() {
67
+ const store = new StateStore(STATE_DIR);
68
+ const plans = await store.read((s) => Object.values(s.plans).sort((a, b) => b.createdAt - a.createdAt));
69
+ if (plans.length === 0) {
70
+ console.log("No plans yet in this project.");
71
+ return 0;
72
+ }
73
+ for (const p of plans) {
74
+ const done = p.tasks.filter((t) => t.status === "done").length;
75
+ const running = p.tasks.filter((t) => t.status === "running" || t.status === "dispatched").length;
76
+ console.log(`${p.id} ${p.status.padEnd(22)} ${done}/${p.tasks.length} done${running ? `, ${running} running` : ""} — ${truncate(p.rootTask, 60)}`);
77
+ }
78
+ return 0;
79
+ }
80
+ async function cmdStatus(planId) {
81
+ if (!planId) {
82
+ console.error("usage: orqlaude status <plan_id>");
83
+ return 2;
84
+ }
85
+ const store = new StateStore(STATE_DIR);
86
+ let plan;
87
+ try {
88
+ plan = await store.read((s) => requirePlan(s.plans, planId));
89
+ }
90
+ catch (err) {
91
+ console.error(err.message);
92
+ return 1;
93
+ }
94
+ console.log(`Plan ${plan.id}`);
95
+ console.log(` status: ${plan.status}`);
96
+ console.log(` root_task: ${plan.rootTask}`);
97
+ console.log(` budget: ${plan.budgetCapTokens.toLocaleString()} tokens (${Math.round(plan.budgetCapTokens / 1000)}k)`);
98
+ console.log(` created: ${new Date(plan.createdAt).toISOString()}`);
99
+ if (plan.approvedAt)
100
+ console.log(` approved: ${new Date(plan.approvedAt).toISOString()}`);
101
+ console.log(` tasks:`);
102
+ let totalTokens = 0;
103
+ for (const t of plan.tasks) {
104
+ let extra = "";
105
+ if (t.spawnedSessionId) {
106
+ const snap = await snapshotSession(process.cwd(), t.spawnedSessionId);
107
+ totalTokens += snap.totalEffectiveTokens;
108
+ extra = ` ${snap.totalEffectiveTokens.toLocaleString()}t ${snap.terminated ? "✓" : snap.lastToolUse?.name ?? snap.lastEventType ?? ""}`;
109
+ }
110
+ console.log(` [${t.status.padEnd(10)}] ${t.title.padEnd(50)}${extra}`);
111
+ if (t.prUrl)
112
+ console.log(` PR: ${t.prUrl}`);
113
+ }
114
+ console.log(` used: ${totalTokens.toLocaleString()} tokens`);
115
+ if (plan.notes.length > 0)
116
+ console.log(` notes: ${plan.notes.length}`);
117
+ if (plan.claims.length > 0)
118
+ console.log(` claims: ${plan.claims.length}`);
119
+ return 0;
120
+ }
121
+ async function cmdShow(planId) {
122
+ if (!planId) {
123
+ console.error("usage: orqlaude show <plan_id>");
124
+ return 2;
125
+ }
126
+ const store = new StateStore(STATE_DIR);
127
+ try {
128
+ const plan = await store.read((s) => requirePlan(s.plans, planId));
129
+ console.log(JSON.stringify(plan, null, 2));
130
+ return 0;
131
+ }
132
+ catch (err) {
133
+ console.error(err.message);
134
+ return 1;
135
+ }
136
+ }
137
+ async function cmdHistory(limit) {
138
+ const audit = new AuditLog(STATE_DIR);
139
+ const events = await audit.tail(limit);
140
+ if (events.length === 0) {
141
+ console.log("(no audit events yet)");
142
+ return 0;
143
+ }
144
+ for (const e of events) {
145
+ const ts = new Date(e.ts).toISOString().slice(11, 19);
146
+ const ok = e.ok ? " ok" : "ERR ";
147
+ const id = e.planId ? ` plan=${e.planId.slice(0, 8)}` : e.sessionId ? ` sess=${e.sessionId.slice(0, 8)}` : "";
148
+ console.log(`${ts} ${ok} ${e.tool.padEnd(18)} ${e.durationMs.toString().padStart(4)}ms${id} ${e.resultSummary ? truncate(e.resultSummary, 80) : e.error ?? ""}`);
149
+ }
150
+ return 0;
151
+ }
152
+ // ---- Telegram subcommands -------------------------------------------------
153
+ async function cmdTg(args) {
154
+ const [sub, ...rest] = args;
155
+ switch (sub) {
156
+ case undefined:
157
+ case "help":
158
+ printTgHelp();
159
+ return 0;
160
+ case "setup":
161
+ return await cmdTgSetup();
162
+ case "show":
163
+ return await cmdTgShow();
164
+ case "whitelist":
165
+ return await cmdTgWhitelist(rest);
166
+ case "unwhitelist":
167
+ return await cmdTgUnwhitelist(rest);
168
+ case "test":
169
+ return await cmdTgTest(rest);
170
+ case "start":
171
+ return await cmdTgStart();
172
+ default:
173
+ console.error(`Unknown tg subcommand: ${sub}`);
174
+ printTgHelp();
175
+ return 1;
176
+ }
177
+ }
178
+ function printTgHelp() {
179
+ console.log(`orqlaude tg — Telegram bot
180
+
181
+ Commands:
182
+ orqlaude tg setup Interactively set the bot token
183
+ orqlaude tg show Show current config
184
+ orqlaude tg whitelist <user_id> [--owner] [--label NAME]
185
+ orqlaude tg unwhitelist <user_id>
186
+ orqlaude tg test <chat_id> Send a test message
187
+ orqlaude tg start Run the bot (foreground)
188
+
189
+ Config file: ${CONFIG_PATH}
190
+ The bot watches the current working directory's .orqlaude state.`);
191
+ }
192
+ async function cmdTgSetup() {
193
+ const rl = readline.createInterface({ input: stdin, output: stdout });
194
+ const cfg = await loadConfig();
195
+ console.log("Telegram bot setup.\n");
196
+ console.log("1. Open Telegram, search for @BotFather, create a new bot, and copy the token.\n");
197
+ const token = (await rl.question(`Bot token${cfg.botToken ? " (press enter to keep existing)" : ""}: `)).trim();
198
+ if (token)
199
+ cfg.botToken = token;
200
+ rl.close();
201
+ if (!cfg.botToken) {
202
+ console.error("No token provided.");
203
+ return 1;
204
+ }
205
+ // Verify
206
+ const api = new TelegramApi(cfg.botToken);
207
+ try {
208
+ const me = await api.getMe();
209
+ console.log(`\n✓ Verified: @${me.username} (bot id ${me.id})`);
210
+ }
211
+ catch (err) {
212
+ console.error(`Failed to verify token: ${err.message}`);
213
+ return 1;
214
+ }
215
+ await saveConfig(cfg);
216
+ console.log(`Saved to ${CONFIG_PATH} (mode 600).`);
217
+ console.log("\nNext: message your bot with /start in Telegram. It'll print your user id. Then run:");
218
+ console.log(` orqlaude tg whitelist <your_user_id> --owner`);
219
+ console.log(`Then:`);
220
+ console.log(` orqlaude tg start`);
221
+ return 0;
222
+ }
223
+ async function cmdTgShow() {
224
+ const cfg = await loadConfig();
225
+ console.log(`Telegram config (${CONFIG_PATH}):`);
226
+ console.log(` bot token: ${cfg.botToken ? cfg.botToken.slice(0, 8) + "…" : "(not set)"}`);
227
+ console.log(` owner: ${cfg.ownerId ?? "(none)"}`);
228
+ console.log(` whitelist: ${cfg.whitelist.length} user(s)`);
229
+ for (const w of cfg.whitelist) {
230
+ console.log(` - ${w.userId}${w.label ? ` (${w.label})` : ""}`);
231
+ }
232
+ return 0;
233
+ }
234
+ async function cmdTgWhitelist(args) {
235
+ const userId = Number(args[0]);
236
+ if (!Number.isFinite(userId)) {
237
+ console.error("usage: orqlaude tg whitelist <user_id> [--owner] [--label NAME]");
238
+ return 2;
239
+ }
240
+ const isOwner = args.includes("--owner");
241
+ const labelIdx = args.indexOf("--label");
242
+ const label = labelIdx !== -1 ? args[labelIdx + 1] : undefined;
243
+ const cfg = await loadConfig();
244
+ if (!cfg.whitelist.some((w) => w.userId === userId)) {
245
+ cfg.whitelist.push({ userId, chatId: userId, label });
246
+ }
247
+ if (isOwner)
248
+ cfg.ownerId = userId;
249
+ await saveConfig(cfg);
250
+ console.log(`✓ Whitelisted ${userId}${label ? ` (${label})` : ""}${isOwner ? " — set as owner" : ""}.`);
251
+ return 0;
252
+ }
253
+ async function cmdTgUnwhitelist(args) {
254
+ const userId = Number(args[0]);
255
+ if (!Number.isFinite(userId)) {
256
+ console.error("usage: orqlaude tg unwhitelist <user_id>");
257
+ return 2;
258
+ }
259
+ const cfg = await loadConfig();
260
+ cfg.whitelist = cfg.whitelist.filter((w) => w.userId !== userId);
261
+ if (cfg.ownerId === userId)
262
+ cfg.ownerId = null;
263
+ await saveConfig(cfg);
264
+ console.log(`✓ Removed ${userId}.`);
265
+ return 0;
266
+ }
267
+ async function cmdTgTest(args) {
268
+ const chatId = Number(args[0]);
269
+ if (!Number.isFinite(chatId)) {
270
+ console.error("usage: orqlaude tg test <chat_id>");
271
+ return 2;
272
+ }
273
+ const cfg = await loadConfig();
274
+ if (!cfg.botToken) {
275
+ console.error("No bot token. Run `orqlaude tg setup` first.");
276
+ return 1;
277
+ }
278
+ const api = new TelegramApi(cfg.botToken);
279
+ try {
280
+ await api.sendMessage(chatId, "🦾 orqlaude bot test — if you see this, the wiring works.");
281
+ console.log("✓ Sent.");
282
+ return 0;
283
+ }
284
+ catch (err) {
285
+ console.error(`Failed: ${err.message}`);
286
+ return 1;
287
+ }
288
+ }
289
+ async function cmdTgStart() {
290
+ try {
291
+ await runBot(process.cwd());
292
+ return 0;
293
+ }
294
+ catch (err) {
295
+ console.error(err.message);
296
+ return 1;
297
+ }
298
+ }
299
+ // ---- helpers --------------------------------------------------------------
300
+ function requirePlan(plans, planId) {
301
+ const full = plans[planId] ?? Object.values(plans).find((p) => p.id.startsWith(planId));
302
+ if (!full)
303
+ throw new Error(`Plan not found: ${planId}`);
304
+ return full;
305
+ }
306
+ function parseLimit(args) {
307
+ const idx = args.indexOf("--limit");
308
+ if (idx === -1 || !args[idx + 1])
309
+ return 30;
310
+ const n = parseInt(args[idx + 1], 10);
311
+ return Number.isFinite(n) && n > 0 ? n : 30;
312
+ }
313
+ function truncate(s, n) {
314
+ if (s.length <= n)
315
+ return s;
316
+ return s.slice(0, n - 1) + "…";
317
+ }
318
+ main().then((code) => process.exit(code), (err) => {
319
+ console.error(err);
320
+ process.exit(1);
321
+ });
322
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Append-only audit log. Every MCP tool invocation writes one line.
3
+ *
4
+ * Format: JSONL — each line is { ts, tool, args, result_summary, ok, error?, plan_id? }
5
+ *
6
+ * Why append-only and not part of the state file: the state file is small and
7
+ * frequently rewritten (atomic temp+rename); folding audit events into it
8
+ * would churn it constantly. The audit log is unbounded and naturally
9
+ * line-oriented, so JSONL append is the right primitive.
10
+ *
11
+ * Inspect with: `tail -f .orqlaude/audit.jsonl | jq`
12
+ */
13
+ export interface AuditEvent {
14
+ ts: number;
15
+ tool: string;
16
+ args: unknown;
17
+ ok: boolean;
18
+ durationMs: number;
19
+ resultSummary?: string;
20
+ error?: string;
21
+ planId?: string;
22
+ sessionId?: string;
23
+ }
24
+ export declare class AuditLog {
25
+ private filePath;
26
+ private writeQueue;
27
+ constructor(stateDir: string);
28
+ append(evt: AuditEvent): Promise<void>;
29
+ /**
30
+ * Wrap a tool handler so every invocation is audited. Captures duration,
31
+ * success/failure, and a short summary of the result.
32
+ */
33
+ wrap<TArgs extends Record<string, unknown>, TResult>(toolName: string, handler: (args: TArgs) => Promise<TResult>, extractIds?: (args: TArgs, result?: TResult) => {
34
+ planId?: string;
35
+ sessionId?: string;
36
+ }): (args: TArgs) => Promise<TResult>;
37
+ tail(limit?: number): Promise<AuditEvent[]>;
38
+ }
@@ -0,0 +1,108 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ export class AuditLog {
4
+ filePath;
5
+ writeQueue = Promise.resolve();
6
+ constructor(stateDir) {
7
+ this.filePath = path.join(stateDir, "audit.jsonl");
8
+ }
9
+ async append(evt) {
10
+ // Serialize writes to avoid interleaving from concurrent tool calls.
11
+ this.writeQueue = this.writeQueue.then(async () => {
12
+ try {
13
+ await fs.mkdir(path.dirname(this.filePath), { recursive: true });
14
+ await fs.appendFile(this.filePath, JSON.stringify(evt) + "\n");
15
+ }
16
+ catch (err) {
17
+ // Best-effort: never fail a tool call because audit logging failed.
18
+ // The error message goes to stderr where MCP debug surfaces it.
19
+ process.stderr.write(`[orqlaude audit] write failed: ${err.message}\n`);
20
+ }
21
+ });
22
+ return this.writeQueue;
23
+ }
24
+ /**
25
+ * Wrap a tool handler so every invocation is audited. Captures duration,
26
+ * success/failure, and a short summary of the result.
27
+ */
28
+ wrap(toolName, handler, extractIds) {
29
+ return async (args) => {
30
+ const started = Date.now();
31
+ try {
32
+ const result = await handler(args);
33
+ const ids = extractIds?.(args, result) ?? {};
34
+ await this.append({
35
+ ts: started,
36
+ tool: toolName,
37
+ args: redactSecrets(args),
38
+ ok: true,
39
+ durationMs: Date.now() - started,
40
+ resultSummary: summarize(result),
41
+ ...ids,
42
+ });
43
+ return result;
44
+ }
45
+ catch (err) {
46
+ const ids = extractIds?.(args) ?? {};
47
+ await this.append({
48
+ ts: started,
49
+ tool: toolName,
50
+ args: redactSecrets(args),
51
+ ok: false,
52
+ durationMs: Date.now() - started,
53
+ error: err instanceof Error ? err.message : String(err),
54
+ ...ids,
55
+ });
56
+ throw err;
57
+ }
58
+ };
59
+ }
60
+ async tail(limit = 50) {
61
+ try {
62
+ const raw = await fs.readFile(this.filePath, "utf8");
63
+ const lines = raw.split("\n").filter((l) => l.trim());
64
+ return lines
65
+ .slice(-limit)
66
+ .map((l) => {
67
+ try {
68
+ return JSON.parse(l);
69
+ }
70
+ catch {
71
+ return null;
72
+ }
73
+ })
74
+ .filter((e) => e !== null);
75
+ }
76
+ catch (err) {
77
+ if (err.code === "ENOENT")
78
+ return [];
79
+ throw err;
80
+ }
81
+ }
82
+ }
83
+ function summarize(result) {
84
+ // MCP tool results are typically { content: [{ type: "text", text: "..." }] }.
85
+ // Extract the first text and truncate.
86
+ try {
87
+ const r = result;
88
+ const text = r?.content?.[0]?.text;
89
+ if (typeof text === "string")
90
+ return text.slice(0, 200);
91
+ return JSON.stringify(result).slice(0, 200);
92
+ }
93
+ catch {
94
+ return String(result).slice(0, 200);
95
+ }
96
+ }
97
+ function redactSecrets(args) {
98
+ // Approval tokens are single-use, but we still don't want them sitting in
99
+ // the audit log forever.
100
+ if (args && typeof args === "object") {
101
+ const clone = { ...args };
102
+ if ("approval_token" in clone)
103
+ clone.approval_token = "<redacted>";
104
+ return clone;
105
+ }
106
+ return args;
107
+ }
108
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/lib/audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import { MODEL_PRICING } from "./pricing.js";
2
+ export interface DailyTokenUsage {
3
+ date: string;
4
+ tokens: number;
5
+ }
6
+ /**
7
+ * Read the Desktop app's daily token tally. Returns null if file missing
8
+ * (e.g. user isn't on the Desktop app). Safe to call repeatedly; the file is
9
+ * small and disk-resident.
10
+ */
11
+ export declare function readDailyTokenUsage(): Promise<DailyTokenUsage | null>;
12
+ /**
13
+ * Rough token estimate per agent for a sub-task. Mirrors estimateAgentCost
14
+ * but in tokens so it composes with a token-based cap.
15
+ *
16
+ * Baselines tuned to one Haiku probe (~30k cache-creation + ~50k cache-read
17
+ * over the run + ~4k output ≈ 84k effective tokens). Tune as we collect more.
18
+ */
19
+ export declare function estimateAgentTokens(effortMultiplier?: number): {
20
+ inputTokens: number;
21
+ cacheReadTokens: number;
22
+ cacheCreationTokens: number;
23
+ outputTokens: number;
24
+ totalEffective: number;
25
+ };
26
+ /** Convenience: compute USD cost AND token estimate together. */
27
+ export declare function estimateAgent(model: string, effortMultiplier?: number): {
28
+ tokens: {
29
+ inputTokens: number;
30
+ cacheReadTokens: number;
31
+ cacheCreationTokens: number;
32
+ outputTokens: number;
33
+ totalEffective: number;
34
+ };
35
+ costUsd: number;
36
+ };
37
+ export { MODEL_PRICING };
@@ -0,0 +1,67 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ import { MODEL_PRICING, estimateAgentCost } from "./pricing.js";
4
+ /**
5
+ * Token-based budgeting (Max x20 friendly).
6
+ *
7
+ * Max-plan users care about their daily/weekly token bucket, not USD. The
8
+ * Claude Desktop app already tracks daily usage in:
9
+ * ~/Library/Application Support/Claude/buddy-tokens.json
10
+ *
11
+ * We read that file to surface remaining quota, and we let the user set a
12
+ * per-fleet token cap. USD remains tracked (and shown informationally) because
13
+ * it's a useful sanity check for runaway agents and because not every user is
14
+ * on Max — but token caps are the primary enforcement mechanism.
15
+ */
16
+ const HOME = process.env.HOME ?? process.env.USERPROFILE ?? "";
17
+ const BUDDY_TOKENS_PATH = path.join(HOME, "Library", "Application Support", "Claude", "buddy-tokens.json");
18
+ /**
19
+ * Read the Desktop app's daily token tally. Returns null if file missing
20
+ * (e.g. user isn't on the Desktop app). Safe to call repeatedly; the file is
21
+ * small and disk-resident.
22
+ */
23
+ export async function readDailyTokenUsage() {
24
+ try {
25
+ const raw = await fs.readFile(BUDDY_TOKENS_PATH, "utf8");
26
+ const parsed = JSON.parse(raw);
27
+ return parsed["tokens-today"] ?? null;
28
+ }
29
+ catch (err) {
30
+ if (err.code === "ENOENT")
31
+ return null;
32
+ throw err;
33
+ }
34
+ }
35
+ /**
36
+ * Rough token estimate per agent for a sub-task. Mirrors estimateAgentCost
37
+ * but in tokens so it composes with a token-based cap.
38
+ *
39
+ * Baselines tuned to one Haiku probe (~30k cache-creation + ~50k cache-read
40
+ * over the run + ~4k output ≈ 84k effective tokens). Tune as we collect more.
41
+ */
42
+ export function estimateAgentTokens(effortMultiplier = 1) {
43
+ const e = effortMultiplier;
44
+ const cacheCreate = Math.round(30_000 * e);
45
+ const input = Math.round(1_000 * e);
46
+ const cacheRead = Math.round(50_000 * e);
47
+ const output = Math.round(4_000 * e);
48
+ // "totalEffective" approximates buddy-bucket consumption; the Desktop app's
49
+ // accounting collapses everything into one counter so we sum without
50
+ // weighting.
51
+ return {
52
+ inputTokens: input,
53
+ cacheReadTokens: cacheRead,
54
+ cacheCreationTokens: cacheCreate,
55
+ outputTokens: output,
56
+ totalEffective: input + cacheRead + cacheCreate + output,
57
+ };
58
+ }
59
+ /** Convenience: compute USD cost AND token estimate together. */
60
+ export function estimateAgent(model, effortMultiplier = 1) {
61
+ return {
62
+ tokens: estimateAgentTokens(effortMultiplier),
63
+ costUsd: estimateAgentCost(model, effortMultiplier),
64
+ };
65
+ }
66
+ export { MODEL_PRICING };
67
+ //# sourceMappingURL=budgeting.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budgeting.js","sourceRoot":"","sources":["../../src/lib/budgeting.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEhE;;;;;;;;;;;GAWG;AAEH,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC;AAC/D,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CACjC,IAAI,EACJ,SAAS,EACT,qBAAqB,EACrB,QAAQ,EACR,mBAAmB,CACpB,CAAC;AAOF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACvC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;QACzD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAyC,CAAC;QACvE,OAAO,MAAM,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC;IACxC,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACvC,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,gBAAgB,GAAG,CAAC;IAOtD,MAAM,CAAC,GAAG,gBAAgB,CAAC;IAC3B,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACrC,4EAA4E;IAC5E,qEAAqE;IACrE,aAAa;IACb,OAAO;QACL,WAAW,EAAE,KAAK;QAClB,eAAe,EAAE,SAAS;QAC1B,mBAAmB,EAAE,WAAW;QAChC,YAAY,EAAE,MAAM;QACpB,cAAc,EAAE,KAAK,GAAG,SAAS,GAAG,WAAW,GAAG,MAAM;KACzD,CAAC;AACJ,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,gBAAgB,GAAG,CAAC;IAC/D,OAAO;QACL,MAAM,EAAE,mBAAmB,CAAC,gBAAgB,CAAC;QAC7C,OAAO,EAAE,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,CAAC;KACpD,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,aAAa,EAAE,CAAC"}
@@ -0,0 +1,46 @@
1
+ import type { SessionSnapshot } from "./jsonl_tail.js";
2
+ /**
3
+ * Hallucination detection for spawned agents.
4
+ *
5
+ * Two cheap deterministic checks, summed into a score. Higher score = more
6
+ * concerning. The primary Claude is told the score in `status()` and can
7
+ * decide whether to send a STOP message or just nudge.
8
+ *
9
+ * Check 1 — path-existence: scan recent tool_use entries for file paths
10
+ * (Read/Edit/Write inputs). If many reference paths that don't exist in the
11
+ * worktree, the agent is editing imaginary files.
12
+ *
13
+ * Check 2 — tool-pattern sanity:
14
+ * • commits without any prior Read on the file being edited
15
+ * • Write of a file that already exists (often an Edit-vs-Write mistake)
16
+ * • repeated identical tool calls (loop)
17
+ * • Edit failing repeatedly with "no match" (agent is guessing at the content)
18
+ *
19
+ * These are deliberately conservative. False positives are acceptable; we
20
+ * surface concerns, we don't kill agents automatically.
21
+ *
22
+ * NOTE: a future iteration can add a Check 3 — second-model cross-validation:
23
+ * periodically have a Haiku read the recent activity and flag suspect turns.
24
+ * That costs tokens though, so it's opt-in.
25
+ */
26
+ export interface HallucinationReport {
27
+ score: number;
28
+ level: "clean" | "minor" | "moderate" | "severe";
29
+ concerns: string[];
30
+ }
31
+ interface ToolUseEvent {
32
+ name: string;
33
+ input: any;
34
+ }
35
+ /**
36
+ * Score a session by inspecting its full set of tool_use events (extracted by
37
+ * the caller from the JSONL). cwd is the worktree root to check paths against.
38
+ */
39
+ export declare function detectHallucination(toolUses: ToolUseEvent[], cwd: string): Promise<HallucinationReport>;
40
+ /**
41
+ * Extract all tool_use events from a JSONL-derived snapshot's raw lines.
42
+ * The snapshot module currently only keeps the *last* tool_use; for
43
+ * hallucination detection we need the full history. This re-reads the JSONL.
44
+ */
45
+ export declare function extractToolUses(jsonlPath: string): Promise<ToolUseEvent[]>;
46
+ export type { SessionSnapshot };