@ra3orblade/swarm 0.14.0 → 0.15.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.
@@ -100,168 +100,268 @@ var init_adapters = __esm(() => {
100
100
  init_updates();
101
101
  init_db();
102
102
  });
103
+ // packages/core/src/prompt.ts
104
+ var init_prompt = () => {};
103
105
 
104
106
  // packages/core/src/adapters/claude-code/hooks.ts
105
- var init_hooks = () => {};
106
-
107
- // packages/core/src/art.ts
108
- var ROBOT, HEAD;
109
- var init_art = __esm(() => {
110
- ROBOT = [
111
- " MM MM ",
112
- " LL LL ",
113
- " LL LL ",
114
- " LM MM ",
115
- " MD MS ",
116
- " LE LE ",
117
- " ME ME ",
118
- " MS MS ",
119
- " KK KK ",
120
- " SSDDDSDDDDDDDDDDDDDDDDDDDDDDDDDDDDS ",
121
- " SSDSDDDDDDDDDDDDDDDDDDDDDDDDDDDDDSS ",
122
- " SSDOOOOOOOOOOOOOOOOOOOOOOOOOOOOOKSS ",
123
- " SSOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMOSS ",
124
- " DDKMMLLLMLLMMMMMMMMMMMMMMMMMMMMMODD ",
125
- " DDOMMLMMMMMMMMMMMMMMMMMMMMMMMMMMODD ",
126
- " SSSSODDOMMLMMMMMMMMMMMMMMMMMMMMMMMMMMODDOSSSS ",
127
- " SSSSODDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODDOSSSS ",
128
- " SMMMMODDOMMLMMMMMMMMMMMMMMMMMMMMMMMMMMODDOSMMMS ",
129
- " DMSSSODDOMMMMOOOOOOMMMMMMMMMOOOOOOMMMMODDOSSSED ",
130
- " DSSSSODDOMMMMODDDDDMMMMMMMMMODDDDDMMMMODDOSSSSK ",
131
- " DSMMSODDOMMMMDDDDDDMMMMMMMMMKDDDDDMMMMODDOSMMSK ",
132
- " DSSSSODDOMMMMDDDDDDMMMMMMMMMDDDDDDMMMMODDOSSSSK ",
133
- " DSSSSODDOMMMMDDDDDDMMMMMMMMMDDDDDDMMMMODDODSSSK ",
134
- " DSSSSODDOMMMMDDDDDDMMMMMMMMMDDDDDDMMMMODDODSSSK ",
135
- " DSSSKODDOMMMMDDDDDDMMMMMMMMMDDDDDDMMMMODDODSSSD ",
136
- " OKKKKODDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODDOKKKKO ",
137
- " KKKKODDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODDOOKKK ",
138
- " KKKOODDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODDOOKKK ",
139
- " DDOMMMMDDDDDDDDDDDDDDDDDDDDDMMMMODD ",
140
- " DDOMMMMDDDDDDDDDDDDDDDDDDDDDMMMMODD ",
141
- " DDOMMMMDDDDDDDDDDDDDDDDDDDDDMMMMODD ",
142
- " DDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODD ",
143
- " DDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODD ",
144
- " DDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODD ",
145
- " DDDOOOOOOOOOOOOOOOOOOOOOOOOOOOOOKDD ",
146
- " DDDDDDDDDDDDDDDDDDDDDDDDDDDDDKDDDDD ",
147
- " DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ",
148
- " OOOOOOOOOOO ",
149
- " OODDDDDKKOO ",
150
- " DSMMMMMMSDD ",
151
- " OOOOOOOOOOO ",
152
- " KDSMMMMMESSKK ",
153
- " MMMMMMSSKOOKDSMMMMMSSSDKOODSEMMMMMM ",
154
- " SSSSDO MMMMMMSSKKOOOOOOOOOOOOOOOOOKSSSMMMMMM ODSSSS ",
155
- " EEESSSSOOLLLLMMMMMESSSSSSSSSSSSSSSSSSSSMMMMMLLLLOOSSSSMME ",
156
- " SMMSSSSKOEELLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLEEOOSSSSEMS ",
157
- " DSSSSSSSOOSEEKSMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMSOEEDOOSSSSSSSK ",
158
- " DSSSSSSDOOSEOSKEMMMMMMMMMMMMMMMMMMMMMMMMMMMMMEOSDEDOODSSSSSSK ",
159
- " KSSSSSSKOODSSOSEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEESOEEDOOKSSSSSSK ",
160
- " DKSSSSKKOODSMEEMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMSEMSDOOKKSSSSDK ",
161
- " OKDDKDKKOOKSMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMSKOOKKKKKKKO ",
162
- " OOOOOKKKOOKSMMMMESSSSSSSSSSSSSMMMMEEEEEESSSSEMMMMSKOOKKKOOOOO ",
163
- " OOOOOOKKOOKSMMMSSKKKKKKKKKKKKKKMMMDKOOOOOOOODDMMMSKOOKKOOOOOO ",
164
- " OKSSKOOOOOKSMMMKKMLLLLLLLLLLLMKMMMDDSSSSSSSSDDMMMSKOOOOODSSKO ",
165
- " SDOKKK KSMMMKDLLLLLLLLLLLLLKMMMKDKKKDDKKKDKMMMSK KKKKDS ",
166
- " DMLLMKO KSMMMKDLLLLLLLLLLLLLKMMMDDDDDDDDDSKDMMMSK ODMLLMD ",
167
- " DELLMSD KSMMMKKMLLLLLLLLLLLLKMMMDKKKKKKKKKDDMMMSK DSMLLEO ",
168
- " KSKKKKDO KSMMMEDDDDKKDDDKKKDKSMMMDDDDDDDDDSDDMMMSK ODDKKKSK ",
169
- " EMLLMDO KSMMMMLLLLLLLLLLLLLLLMMMDOOOOOOOOODDMMMSK ODMLLME ",
170
- " KMLLMSD KSMMMMMMMMMMMMMMMMMMMMMMDDSSDDSSSSDDMMMSK SSMLLEK ",
171
- " KSKKKKSO KSMMMMMOOMMOOMKOEMOOMMMMDOOOOOOOOODDMMMSK OSKKKKSO ",
172
- " SMLLMDO KSMMMMMKKMMKKMDKEMKKMMMMKDDDDSSSSDDDMMMSK ODMLLMS ",
173
- " SMMMMSD KSMMMMMEEMMEEMEEMMEEMMMMDSOOOOOOOODDMMSSK DSMMMMD ",
174
- " OOOOOOO KSSMMMMMMMMMMMMMMMMMMMMMDDDDDDDDDDDDMSSSK OOOOOOO ",
175
- " KSMLLLMMSKO KSSSSMMMMMMMMMMMMMMMMMMMSOOOOOOOOOOSSSSSK OKSMLLLLMSO ",
176
- " DSMMMEEESKK KSSKSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSOSSK KDSMMMMMESD ",
177
- " DSMMMMMESKK KSOOKSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSOOKSK KKSMMMMMMSD ",
178
- " DSMMMMMESKK OKSKSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSDSKO KDSMMMMMMSD ",
179
- " KDDDDDDDDKK KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKDDDDDDDDO ",
180
- " OOOOOOOOOOO OOOOOOOOO OOOOOOOOO OOOOOOOOOOO ",
181
- "DSDO OSSO ODKKKKKKK OKKKKKKKO KEDO ODEK",
182
- "SMDO OSMD KDSSSSSSK KDSSSSSKO SMDO ODMS",
183
- "DEKO OOED OOKKKOOO OOOKKOOO SSOO OKMS",
184
- "DEKO OOED SEMMMMSK OSMMMMED SSKO OKES",
185
- "DSSO OESK SEMMMMSK OSMMMMED KSSO OSSK",
186
- "KSMS EEDO OOKKKKKO OOKKKKKO OSEE EMSO",
187
- " DSS SSK OSEEESDO ODSEEESO KSS SSD ",
188
- " OOO OO SEMMMMSK OSMMMMED OO OOO ",
189
- " KSSSSSDO OKSSESSK ",
190
- " DKKDDDDDDKKDK KDKDDDDDDDKDK ",
191
- " SSMLLMMMMMMLMSK DSSMMMMMMMLLMSS ",
192
- " SMLMMMMMMMMLLED SEMMMMMMMMMMLMS ",
193
- " DMMMMMMMMMMMMMMD SMMMMMMMMMMMMMSK ",
194
- " ODSSSSSSSSSSSSSSDO ODSSSSSSSSSSSSSSKO ",
195
- " OOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOO ",
196
- " OKSSSSSSSSSSSSSSK KSSSSSSSSSSSSSSKO ",
197
- " OKDDDDDDDDDDDDDDK KDDDDDDDDDDDDDDOO "
198
- ];
199
- HEAD = ROBOT.slice(0, 37);
200
- });
201
-
202
- // packages/core/src/audit.ts
203
- var AUDIT_TYPES, AUDIT_TYPES_SQL;
204
- var init_audit = __esm(() => {
205
- AUDIT_TYPES = new Set([
206
- "session.started",
207
- "session.ended",
208
- "tool.denied",
209
- "claim.acquired",
210
- "claim.renewed",
211
- "claim.released",
212
- "claim.expired",
213
- "claim.orphaned",
214
- "claim.denied",
215
- "rules.changed",
216
- "worktree.reclaimed",
217
- "worktree.created",
218
- "worktree.removed",
219
- "worktree.bootstrapped",
220
- "pr.opened",
221
- "question.asked",
222
- "question.answered",
223
- "dispatch.queued",
224
- "dispatch.started",
225
- "dispatch.finished",
226
- "resource.acquired",
227
- "resource.released",
228
- "resource.reaped",
229
- "process.started",
230
- "process.exited",
231
- "gate.recorded",
232
- "gate.blocked",
233
- "codify.applied",
234
- "handoff.recorded",
235
- "permission.requested",
236
- "permission.resolved",
237
- "incident.opened",
238
- "incident.acked",
239
- "run.result",
240
- "workflow.started",
241
- "workflow.finished"
107
+ var OWN_SUMMARY;
108
+ var init_hooks = __esm(() => {
109
+ init_prompt();
110
+ OWN_SUMMARY = new Set([
111
+ "Bash",
112
+ "Read",
113
+ "Edit",
114
+ "Write",
115
+ "MultiEdit",
116
+ "NotebookEdit",
117
+ "Glob",
118
+ "Grep",
119
+ "Agent",
120
+ "Task",
121
+ "WebFetch",
122
+ "WebSearch",
123
+ "AskUserQuestion"
242
124
  ]);
243
- AUDIT_TYPES_SQL = [...AUDIT_TYPES].map((t) => `'${t}'`).join(", ");
244
125
  });
245
126
 
246
- // packages/core/src/budget.ts
247
- var BUDGET_ASK_TOOLS;
248
- var init_budget = __esm(() => {
249
- BUDGET_ASK_TOOLS = new Set(["Bash", "Edit", "Write", "MultiEdit", "NotebookEdit"]);
250
- });
251
- // packages/core/src/workflows.ts
252
- var init_workflows = () => {};
253
-
254
- // packages/core/src/config.ts
255
- var init_config = __esm(() => {
256
- init_audit();
257
- init_workflows();
127
+ // packages/core/src/guards.ts
128
+ function expandHome(s, home) {
129
+ return s.replace(/(^|[\s="'`:])(?:~|\$HOME|\$\{HOME\})(?=\/|$|[\s"'`])/g, `$1${home}`);
130
+ }
131
+ function segments(cmd) {
132
+ return cmd.split(/\s*(?:&&|\|\||;|\n)\s*/).map((s) => s.trim()).filter(Boolean);
133
+ }
134
+ function words(seg) {
135
+ return (seg.match(/"[^"]*"|'[^']*'|\S+/g) ?? []).map((w) => w.replace(/^["']|["']$/g, ""));
136
+ }
137
+ function dangerousTarget(target, home, toplevel) {
138
+ if (/^\$\{?\w+\}?(\/|$)/.test(target) && !target.startsWith(home))
139
+ return `starts with ${target.match(/^\$\{?\w+\}?/)?.[0]} \u2014 if that is ever unset, it is /`;
140
+ const t = trimSlash(target.replace(/\/\*$/, "/").replace(/\/\.$/, "/"));
141
+ if (t === "*" || target === "/*")
142
+ return "removes everything the glob matches, from the top";
143
+ if (t === "." || t === ".." || t.startsWith("../"))
144
+ return "removes the working directory or its parent";
145
+ if (t === home || t === trimSlash(home))
146
+ return "removes the home directory";
147
+ if (!t.startsWith("/"))
148
+ return null;
149
+ if (SYSTEM_DIRS.includes(t))
150
+ return `removes ${t}`;
151
+ if (t.split("/").length <= 3 && /^\/(Users|home)\//.test(t))
152
+ return `removes the home directory ${t}`;
153
+ if (SCRATCH.test(t))
154
+ return null;
155
+ if (toplevel && !(t === toplevel || t.startsWith(`${trimSlash(toplevel)}/`)))
156
+ return `removes ${t}, outside this repository`;
157
+ return null;
158
+ }
159
+ function destructiveFs(cmd, home, toplevel) {
160
+ const c = expandHome(cmd, home);
161
+ for (const seg of segments(c)) {
162
+ const w = words(seg.replace(/^sudo\s+/, ""));
163
+ if (w[0] === "rm") {
164
+ const flags = w.filter((x) => x.startsWith("-"));
165
+ const recursive = flags.some((f) => /^-[a-zA-Z]*[rR]/.test(f) || f === "--recursive");
166
+ if (!recursive)
167
+ continue;
168
+ for (const target of w.slice(1).filter((x) => !x.startsWith("-"))) {
169
+ const why = dangerousTarget(target, home, toplevel);
170
+ if (why)
171
+ return { what: `\`rm -r ${target}\` ${why}` };
172
+ }
173
+ }
174
+ if (w[0] === "find" && (w.includes("-delete") || /-exec\s+rm\b/.test(seg))) {
175
+ const root = w[1] ?? ".";
176
+ const why = dangerousTarget(root, home, toplevel);
177
+ if (why && root !== ".")
178
+ return { what: `\`find ${root} \u2026 -delete\` ${why}` };
179
+ }
180
+ if (/^(mkfs(\.\w+)?|newfs\w*|wipefs)$/.test(w[0] ?? ""))
181
+ return { what: `\`${w[0]}\` formats a disk` };
182
+ if (w[0] === "dd" && w.some((x) => /^of=\/dev\//.test(x)))
183
+ return { what: "`dd` writes over a device" };
184
+ if (w[0] === "diskutil" && /^(erase|zero|secureErase|reformat)/i.test(w[1] ?? ""))
185
+ return { what: `\`diskutil ${w[1]}\` erases a disk` };
186
+ if ((w[0] === "chmod" || w[0] === "chown") && w.some((x) => /^-[a-zA-Z]*R/.test(x))) {
187
+ const target = w[w.length - 1] ?? "";
188
+ if (target.startsWith("/") || target === home) {
189
+ const why = dangerousTarget(target, home, toplevel);
190
+ if (why)
191
+ return { what: `\`${w[0]} -R ${target}\` ${why.replace(/^removes/, "changes")}` };
192
+ }
193
+ }
194
+ }
195
+ return null;
196
+ }
197
+ function destructiveInfra(cmd) {
198
+ for (const r of INFRA)
199
+ if (r.re.test(cmd) && (!r.needs || r.needs.test(cmd)))
200
+ return { what: r.what };
201
+ return null;
202
+ }
203
+ function pipeToShell(cmd) {
204
+ if (new RegExp(`${FETCHER}[^|;&]*\\|\\s*(${INTERP})\\b`).test(cmd))
205
+ return { what: "pipes a downloaded script straight into an interpreter" };
206
+ if (/\b((ba|z)?sh|source|\.)\s+<\(\s*(curl|wget)\b/.test(cmd))
207
+ return { what: "runs a downloaded script via process substitution" };
208
+ if (/\b(ba|z)?sh\s+-c\s+["']?\$\(\s*(curl|wget)\b/.test(cmd))
209
+ return { what: "runs a downloaded script via command substitution" };
210
+ return null;
211
+ }
212
+ function secretFileIn(text) {
213
+ return SECRET_FILES.find((s) => s.re.test(text))?.what ?? null;
214
+ }
215
+ function secretCommand(cmd) {
216
+ if (/\bsecurity\s+find-(generic|internet)-password\b[^|;&]*\s-[a-z]*w/.test(cmd))
217
+ return { what: "prints a password from the macOS keychain" };
218
+ if (/\b(printenv|env)\s*(\||>|$)/.test(cmd) && /\|\s*(curl|nc|ncat)\b/.test(cmd))
219
+ return { what: "sends the environment over the network" };
220
+ for (const seg of segments(cmd)) {
221
+ if (!READERS.test(seg))
222
+ continue;
223
+ const w = words(seg);
224
+ const copy = /^(cp|scp|rsync)$/.test(w[0] ?? "");
225
+ const text = copy ? w.slice(1, -1).filter((x) => !x.startsWith("-")).join(" ") : seg;
226
+ const what = secretFileIn(text);
227
+ if (what)
228
+ return { what: `${copy ? "copies" : "reads"} ${what}` };
229
+ }
230
+ return null;
231
+ }
232
+ function secretFile(tool, path) {
233
+ const what = secretFileIn(path);
234
+ if (!what)
235
+ return null;
236
+ if (tool === "Read")
237
+ return { what: `reads ${what}` };
238
+ if (/\.env(\.[\w-]+)?$|\.(pem|p12|pfx|key)$/i.test(path))
239
+ return { what: `writes ${what}` };
240
+ return null;
241
+ }
242
+ function guardConfigFile(path, home) {
243
+ const p = expandHome(path, home);
244
+ const h = trimSlash(home);
245
+ if (/(^|\/)\.claude\/settings(\.local)?\.json$/.test(p))
246
+ return "Claude Code settings (hooks and permissions)";
247
+ if (p === `${h}/.claude.json`)
248
+ return "Claude Code's MCP registration";
249
+ if (p.startsWith(`${h}/.swarm/`) || p === `${h}/.swarm`)
250
+ return "Swarm's config and ledger";
251
+ if (/(^|\/)\.swarm\.toml$/.test(p))
252
+ return "this repo's Swarm rules";
253
+ if (p === `${h}/.codex/config.toml` || p === `${h}/.gemini/settings.json` || p === `${h}/.cursor/hooks.json`)
254
+ return "another agent's hook config";
255
+ return null;
256
+ }
257
+ function configTamperCommand(cmd, home) {
258
+ if (/(^|[\s;&|])swarm\s+uninstall\b/.test(cmd))
259
+ return { what: "uninstalls Swarm's hooks" };
260
+ for (const seg of segments(expandHome(cmd, home))) {
261
+ if (!WRITERS.test(seg))
262
+ continue;
263
+ for (const w of words(seg)) {
264
+ const target = w.replace(/^>+/, "");
265
+ if (!target.includes("/") && !target.startsWith("."))
266
+ continue;
267
+ const what = guardConfigFile(target, home);
268
+ if (what)
269
+ return { what: `changes ${what} (${target})` };
270
+ }
271
+ }
272
+ return null;
273
+ }
274
+ var trimSlash = (p) => p.length > 1 ? p.replace(/\/+$/, "") : p, SYSTEM_DIRS, SCRATCH, DB_CLIENT, INFRA, FETCHER, INTERP, SECRET_FILES, READERS, WRITERS;
275
+ var init_guards = __esm(() => {
276
+ SYSTEM_DIRS = [
277
+ "/",
278
+ "/bin",
279
+ "/boot",
280
+ "/dev",
281
+ "/etc",
282
+ "/home",
283
+ "/lib",
284
+ "/opt",
285
+ "/private",
286
+ "/root",
287
+ "/sbin",
288
+ "/System",
289
+ "/Library",
290
+ "/Applications",
291
+ "/Users",
292
+ "/usr",
293
+ "/var"
294
+ ];
295
+ SCRATCH = /^(\/tmp|\/private\/tmp|\/var\/folders|\/private\/var\/folders)(\/|$)/;
296
+ DB_CLIENT = /\b(psql|mysql|mariadb|sqlite3|sqlcmd|clickhouse(-client)?|cockroach|mongo(sh)?|duckdb)\b/i;
297
+ INFRA = [
298
+ {
299
+ what: "`terraform destroy` tears the stack down",
300
+ re: /\b(terraform|tofu)\s+(\S+\s+)*destroy\b/
301
+ },
302
+ { what: "`terraform state rm` forgets live resources", re: /\b(terraform|tofu)\s+state\s+rm\b/ },
303
+ {
304
+ what: "`kubectl delete` of a namespace or everything",
305
+ re: /\bkubectl\s+(\S+\s+)*delete\s+(\S+\s+)*(ns|namespaces?|pvc?|persistentvolume(claim)?s?|--all\b|-A\b|--all-namespaces\b)/
306
+ },
307
+ { what: "`kubectl drain` evicts a node's workloads", re: /\bkubectl\s+(\S+\s+)*drain\b/ },
308
+ { what: "`helm uninstall` removes a release", re: /\bhelm\s+(\S+\s+)*(uninstall|delete)\b/ },
309
+ {
310
+ what: "`aws s3 rm --recursive` empties a bucket path",
311
+ re: /\baws\s+s3\s+rm\b[^|;&]*--recursive/
312
+ },
313
+ { what: "`aws s3 rb` deletes a bucket", re: /\baws\s+s3\s+rb\b/ },
314
+ {
315
+ what: "an AWS delete / terminate call",
316
+ re: /\baws\s+[\w-]+\s+(terminate-instances|delete-(db-instance|db-cluster|stack|table|bucket|cluster|function|user|role|repository))\b/
317
+ },
318
+ { what: "a `gcloud \u2026 delete`", re: /\bgcloud\s+(\S+\s+)+delete\b/ },
319
+ { what: "an `az \u2026 delete`", re: /\baz\s+(\S+\s+)+delete\b/ },
320
+ {
321
+ what: "`docker system prune` removes images and volumes",
322
+ re: /\bdocker\s+system\s+prune\b[^|;&]*(-a\b|--all\b|--volumes\b)/
323
+ },
324
+ { what: "`docker volume rm/prune` deletes data volumes", re: /\bdocker\s+volume\s+(rm|prune)\b/ },
325
+ { what: "a SQL `DROP`", re: /\bdrop\s+(database|schema|table)\b/i, needs: DB_CLIENT },
326
+ { what: "a SQL `TRUNCATE`", re: /\btruncate\s+(table\s+)?\w/i, needs: DB_CLIENT },
327
+ {
328
+ what: "an unbounded SQL `DELETE`",
329
+ re: /\bdelete\s+from\s+[\w."]+\s*(;|"|'|$)/i,
330
+ needs: DB_CLIENT
331
+ },
332
+ { what: "`dropdb` deletes a database", re: /(^|[\s;&|])dropdb\s/ },
333
+ {
334
+ what: "a database reset",
335
+ re: /\b(prisma\s+migrate\s+reset|rails\s+db:(drop|reset)|rake\s+db:(drop|reset)|supabase\s+db\s+reset)\b/
336
+ },
337
+ {
338
+ what: "`redis-cli FLUSHALL/FLUSHDB` wipes the store",
339
+ re: /\bredis-cli\b[^|;&]*\bflush(all|db)\b/i
340
+ },
341
+ { what: "`dropDatabase()` in the Mongo shell", re: /\bdropDatabase\s*\(/, needs: DB_CLIENT }
342
+ ];
343
+ FETCHER = String.raw`\b(curl|wget|fetch|http|xh)\b`;
344
+ INTERP = String.raw`(sudo\s+(-\S+\s+)*)?(env\s+(\w+=\S+\s+)*)?(ba|z|k|da|fi)?sh|python[0-9.]*|node|perl|ruby|php|iex`;
345
+ SECRET_FILES = [
346
+ {
347
+ what: "a .env file",
348
+ re: /(^|[/\s"'`=@])\.env(\.(?!example\b|sample\b|template\b|dist\b|defaults?\b)[\w-]+)?(?=$|[\s"'`;|&)])/
349
+ },
350
+ {
351
+ what: "an SSH private key",
352
+ re: /(^|[/\s"'`])(id_rsa|id_ed25519|id_ecdsa|id_dsa)(?=$|[\s"'`;|&)])/
353
+ },
354
+ { what: "AWS credentials", re: /\.aws\/credentials\b/ },
355
+ { what: "an npm token file", re: /(^|[/\s"'`])\.npmrc\b/ },
356
+ { what: "a kubeconfig", re: /\.kube\/config\b/ },
357
+ { what: "Google Cloud credentials", re: /gcloud\/[\w-]*credential/i },
358
+ { what: "a private key file", re: /[\w-]\.(pem|p12|pfx|key)(?=$|[\s"'`;|&)])/i },
359
+ { what: "a netrc", re: /(^|[/\s"'`])\.netrc\b/ }
360
+ ];
361
+ READERS = /(^|[\s;&|(])(cat|less|more|head|tail|bat|strings|base64|xxd|od|hexdump|grep|egrep|rg|ag|awk|nl|tac|cp|scp|rsync|openssl|gpg|pbcopy)\b|curl\b[^|;&]*(-d|--data[\w-]*|-F|--form|-T|--upload-file)\s*@/;
362
+ WRITERS = /(^|[\s;&|(])(>>?|tee|rm|mv|cp|truncate|ln|chmod|chown|unlink|shred)\b|>>?\s*\S|\bsed\s+(-[a-zA-Z]*\s+)*-i|\bperl\s+-[a-zA-Z]*i|\bsqlite3\b[^|;&]*\b(delete|update|drop|insert)\b/i;
258
363
  });
259
364
 
260
- // packages/core/src/context.ts
261
- var init_context = () => {};
262
-
263
- // packages/core/src/dag.ts
264
- var init_dag = () => {};
265
365
  // packages/core/src/rules.ts
266
366
  function otherLiveInSameTree(current, sessions, now, withinMs = LIVE_WINDOW_MS) {
267
367
  if (!current.toplevel)
@@ -312,6 +412,41 @@ function killedPorts(cmd) {
312
412
  ports.add(Number(m[1]));
313
413
  return [...ports];
314
414
  }
415
+ function familyHitsBash(cmd, home, toplevel) {
416
+ const out = [];
417
+ const add = (rule, h) => h && out.push({ rule, what: h.what });
418
+ add("destructive_fs", destructiveFs(cmd, home, toplevel));
419
+ add("destructive_infra", destructiveInfra(cmd));
420
+ add("pipe_to_shell", pipeToShell(cmd));
421
+ add("secrets", secretCommand(cmd));
422
+ add("config_tamper", configTamperCommand(cmd, home));
423
+ return out;
424
+ }
425
+ function familyHitsFile(tool, path, home) {
426
+ const out = [];
427
+ const secret = secretFile(tool, path);
428
+ if (secret)
429
+ out.push({ rule: "secrets", what: secret.what });
430
+ if (WRITE_TOOLS.has(tool)) {
431
+ const what = guardConfigFile(path, home);
432
+ if (what)
433
+ out.push({ rule: "config_tamper", what: `changes ${what} (${path})` });
434
+ }
435
+ return out;
436
+ }
437
+ function familyDecision(hits, modes) {
438
+ for (const h of hits) {
439
+ const mode = modes[h.rule] ?? "off";
440
+ if (mode !== "off")
441
+ return { action: mode, rule: h.rule, reason: FAMILY_REASON[h.rule](h.what) };
442
+ }
443
+ return null;
444
+ }
445
+ function guardFile(tool, path, modes = DEFAULT_MODES, home = process.env.HOME ?? "") {
446
+ if ((modes.secrets ?? "off") === "off" && (modes.config_tamper ?? "off") === "off")
447
+ return { action: "allow" };
448
+ return familyDecision(familyHitsFile(tool, path, home), modes) ?? { action: "allow" };
449
+ }
315
450
  function isSingleCommand(cmd) {
316
451
  return !/[&|;<>`\n]/.test(cmd) && !cmd.includes("$(");
317
452
  }
@@ -436,7 +571,7 @@ function guardBash(cmd, current, sessions, now, modes = DEFAULT_MODES, ctx = {})
436
571
  if (modes.shared_tree !== "off" && isBroadStage(cmd)) {
437
572
  const o = other();
438
573
  if (o) {
439
- const d = hit("shared_tree", `Another session (${o.id.slice(0, 8)}) is active in this same checkout. \`git add -A\` / \`git commit -a\` will sweep its uncommitted changes into your commit. Stage explicit paths (\`git add <path>\`), or give each session its own git worktree.`);
574
+ const d = hit("shared_tree", sharedTreeReason(o.id));
440
575
  if (d.action !== "allow")
441
576
  return d;
442
577
  }
@@ -449,6 +584,11 @@ function guardBash(cmd, current, sessions, now, modes = DEFAULT_MODES, ctx = {})
449
584
  return d;
450
585
  }
451
586
  }
587
+ if (FAMILY_RULES.some((r) => (modes[r] ?? "off") !== "off")) {
588
+ const d = familyDecision(familyHitsBash(cmd, ctx.home ?? process.env.HOME ?? "", current.toplevel), modes);
589
+ if (d)
590
+ return d;
591
+ }
452
592
  for (const rule of modes.custom ?? []) {
453
593
  const d = applyCustomRule(rule, cmd);
454
594
  if (d)
@@ -456,7 +596,7 @@ function guardBash(cmd, current, sessions, now, modes = DEFAULT_MODES, ctx = {})
456
596
  }
457
597
  const nv = rewriteNoVerify(cmd);
458
598
  if (nv) {
459
- const d = fix("no_verify", "Hooks and signing exist for a reason: `--no-verify` / `--no-gpg-sign` was dropped. If a hook is wrong, fix the hook.", nv);
599
+ const d = fix("no_verify", NO_VERIFY_REASON, nv);
460
600
  if (d.action !== "allow")
461
601
  return d;
462
602
  }
@@ -519,8 +659,9 @@ function guardWrite(target, current, claims, modes = DEFAULT_MODES, kind = "file
519
659
  }
520
660
  return { action: "allow" };
521
661
  }
522
- var LIVE_WINDOW_MS, GIT_GLOBAL_OPTS = "(?:\\s+(?:-C\\s+\\S+|-c\\s+\\S+|--git-dir(?:=|\\s+)\\S+|--work-tree(?:=|\\s+)\\S+|--namespace(?:=|\\s+)\\S+|--no-pager|--paginate|--bare|--literal-pathspecs|--exec-path(?:=\\S+)?))*", DEFAULT_MODES, customRe, WRITE_TOOLS;
662
+ var LIVE_WINDOW_MS, GIT_GLOBAL_OPTS = "(?:\\s+(?:-C\\s+\\S+|-c\\s+\\S+|--git-dir(?:=|\\s+)\\S+|--work-tree(?:=|\\s+)\\S+|--namespace(?:=|\\s+)\\S+|--no-pager|--paginate|--bare|--literal-pathspecs|--exec-path(?:=\\S+)?))*", DEFAULT_MODES, FAMILY_RULES, FAMILY_REASON, customRe, sharedTreeReason = (sessionId) => `Another session (${sessionId.slice(0, 8)}) is active in this same checkout. \`git add -A\` / \`git commit -a\` will sweep its uncommitted changes into your commit. Stage explicit paths (\`git add <path>\`), or give each session its own git worktree.`, NO_VERIFY_REASON = "Hooks and signing exist for a reason: `--no-verify` / `--no-gpg-sign` was dropped. If a hook is wrong, fix the hook.", WRITE_TOOLS;
523
663
  var init_rules = __esm(() => {
664
+ init_guards();
524
665
  LIVE_WINDOW_MS = 10 * 60000;
525
666
  DEFAULT_MODES = {
526
667
  shared_tree: "ask",
@@ -532,12 +673,348 @@ var init_rules = __esm(() => {
532
673
  no_verify: "off",
533
674
  dry_run_first: "off",
534
675
  custom: [],
676
+ destructive_fs: "off",
677
+ destructive_infra: "off",
678
+ pipe_to_shell: "off",
679
+ secrets: "off",
680
+ config_tamper: "off",
535
681
  protected: { ports: [] }
536
682
  };
683
+ FAMILY_RULES = [
684
+ "destructive_fs",
685
+ "destructive_infra",
686
+ "pipe_to_shell",
687
+ "secrets",
688
+ "config_tamper"
689
+ ];
690
+ FAMILY_REASON = {
691
+ destructive_fs: (w) => `This ${w}. Nothing gets that back \u2014 name the exact paths you mean, inside this repository.`,
692
+ destructive_infra: (w) => `This is ${w} \u2014 outside this checkout, and not something git can undo. Confirm with the owner first.`,
693
+ pipe_to_shell: (w) => `This ${w}. Download it to a file, read it, then run it.`,
694
+ secrets: (w) => `This ${w}. Credentials stay out of the transcript: use the variable by name, or ask the owner.`,
695
+ config_tamper: (w) => `This ${w} \u2014 the settings that decide what agents may do. Ask the owner to change them.`
696
+ };
537
697
  customRe = new WeakMap;
538
698
  WRITE_TOOLS = new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
539
699
  });
540
700
 
701
+ // packages/core/src/policy.ts
702
+ import { createHash } from "crypto";
703
+ function verifyPolicyCache(raw) {
704
+ if (!raw || typeof raw !== "object")
705
+ return null;
706
+ const c = raw;
707
+ if (c.version !== POLICY_CACHE_VERSION || typeof c.sha256 !== "string")
708
+ return null;
709
+ const { sha256, ...body } = c;
710
+ if (!body.modes || !Array.isArray(body.sessions) || !Array.isArray(body.worktrees))
711
+ return null;
712
+ return digest(body) === sha256 ? c : null;
713
+ }
714
+ function evaluateOffline(cache, raw, toplevel, now = Date.now()) {
715
+ const tool = typeof raw.tool_name === "string" ? raw.tool_name : "";
716
+ const input = raw.tool_input ?? {};
717
+ const id = typeof raw.session_id === "string" ? raw.session_id : "";
718
+ const cwd = typeof raw.cwd === "string" ? raw.cwd : "";
719
+ const isWrite = WRITE_TOOLS.has(tool) && typeof input.file_path === "string";
720
+ const cmd = tool === "Bash" && typeof input.command === "string" ? input.command : null;
721
+ const modes = cache.modes;
722
+ if (typeof input.file_path === "string" && (isWrite || tool === "Read")) {
723
+ const f = guardFile(tool, absolutePath(input.file_path, cwd), modes);
724
+ if (f.action !== "allow")
725
+ return f;
726
+ }
727
+ if (!isWrite && !cmd)
728
+ return { action: "allow" };
729
+ const current = { id, cwd, toplevel: toplevel(cwd) };
730
+ if (modes.no_foreign_worktree !== "off" || modes.claim_required_to_write !== "off") {
731
+ const target = isWrite ? absolutePath(input.file_path, cwd) : cwd;
732
+ const w = guardWrite(target, current, cache.worktrees, modes, isWrite ? "file" : "bash");
733
+ if (w.action !== "allow")
734
+ return w;
735
+ }
736
+ if (!cmd)
737
+ return { action: "allow" };
738
+ return guardBash(cmd, current, cache.sessions, now, modes);
739
+ }
740
+ var POLICY_CACHE_VERSION = 1, POLICY_CACHE_FILE = "policy.cache.json", digest = (body) => createHash("sha256").update(JSON.stringify(body)).digest("hex");
741
+ var init_policy = __esm(() => {
742
+ init_hooks();
743
+ init_rules();
744
+ });
745
+
746
+ // packages/core/src/agenthooks.ts
747
+ function detectAgent(raw) {
748
+ if (typeof raw.cursor_version === "string" || typeof raw.conversation_id === "string")
749
+ return "cursor";
750
+ if (raw.hook_event_name === "BeforeTool")
751
+ return "gemini";
752
+ if (typeof raw.turn_id === "string")
753
+ return "codex";
754
+ return "claude-code";
755
+ }
756
+ function patchPaths(patch) {
757
+ const out = [];
758
+ for (const m of patch.matchAll(/^\*\*\* (?:Add File|Update File|Delete File|Move to): (.+)$/gm)) {
759
+ const p = m[1]?.trim();
760
+ if (p && !out.includes(p))
761
+ out.push(p);
762
+ }
763
+ return out;
764
+ }
765
+ function toToolRequests(agent, raw) {
766
+ const input = raw.tool_input && typeof raw.tool_input === "object" ? raw.tool_input : {};
767
+ const tool = str(raw.tool_name) ?? "";
768
+ const cwd = str(raw.cwd) ?? "";
769
+ const sessionId = str(raw.session_id) ?? str(raw.conversation_id) ?? "";
770
+ const req = (t, i, at = cwd) => ({
771
+ tool: t,
772
+ input: i,
773
+ sessionId,
774
+ cwd: at
775
+ });
776
+ switch (agent) {
777
+ case "claude-code": {
778
+ const command = str(input.command);
779
+ const file_path = str(input.file_path);
780
+ return [req(tool, { ...command ? { command } : {}, ...file_path ? { file_path } : {} })];
781
+ }
782
+ case "codex": {
783
+ const command = str(input.command);
784
+ if (tool === "Bash" && command)
785
+ return [req("Bash", { command })];
786
+ if (tool === "apply_patch" && command)
787
+ return patchPaths(command).map((p) => req("Write", { file_path: p }));
788
+ return [];
789
+ }
790
+ case "gemini": {
791
+ const shell = str(input.command);
792
+ if (tool === "run_shell_command" && shell)
793
+ return [req("Bash", { command: shell }, str(input.dir_path) ?? cwd)];
794
+ const fp = str(input.file_path);
795
+ if (!fp)
796
+ return [];
797
+ if (tool === "read_file")
798
+ return [req("Read", { file_path: fp })];
799
+ if (tool === "write_file" || tool === "replace")
800
+ return [req("Write", { file_path: fp })];
801
+ return [];
802
+ }
803
+ case "cursor": {
804
+ const shell = str(input.command);
805
+ if (tool === "Shell" && shell)
806
+ return [req("Bash", { command: shell }, str(input.working_directory) ?? cwd)];
807
+ const fp = str(input.file_path) ?? str(input.path) ?? str(input.target_file);
808
+ if (!fp)
809
+ return [];
810
+ if (tool === "Read")
811
+ return [req("Read", { file_path: fp })];
812
+ if (tool === "Write" || tool === "Delete")
813
+ return [req("Write", { file_path: fp })];
814
+ return [];
815
+ }
816
+ }
817
+ }
818
+ function effectiveDecision(agent, d) {
819
+ if (d.action === "ask" && !CAN_ASK.has(agent))
820
+ return {
821
+ ...d,
822
+ action: "deny",
823
+ reason: `${d.reason ?? ""} (Swarm would ask the user here; ${AGENT_LABEL[agent]}'s hooks cannot ask, so the call is refused \u2014 ask the user to run it, or to turn the rule off.)`.trim()
824
+ };
825
+ if (d.action === "rewrite" && agent !== "claude-code")
826
+ return {
827
+ ...d,
828
+ action: "deny",
829
+ reason: `${d.reason ?? ""} Run this instead: ${d.command ?? ""}`.trim()
830
+ };
831
+ return d;
832
+ }
833
+ function renderAgentDecision(agent, d) {
834
+ const e = effectiveDecision(agent, d);
835
+ if (e.action === "allow")
836
+ return "{}";
837
+ const reason = `[swarm] ${e.reason ?? ""}`.trim();
838
+ if (agent === "gemini")
839
+ return JSON.stringify({ decision: "deny", reason });
840
+ return JSON.stringify({
841
+ hookSpecificOutput: {
842
+ hookEventName: "PreToolUse",
843
+ permissionDecision: e.action === "ask" ? "ask" : "deny",
844
+ permissionDecisionReason: reason
845
+ }
846
+ });
847
+ }
848
+ var AGENT_LABEL, CAN_ASK, str = (v) => typeof v === "string" && v ? v : undefined;
849
+ var init_agenthooks = __esm(() => {
850
+ init_policy();
851
+ AGENT_LABEL = {
852
+ "claude-code": "Claude Code",
853
+ codex: "Codex",
854
+ gemini: "Gemini CLI",
855
+ cursor: "Cursor"
856
+ };
857
+ CAN_ASK = new Set(["claude-code"]);
858
+ });
859
+
860
+ // packages/core/src/art.ts
861
+ var ROBOT, HEAD;
862
+ var init_art = __esm(() => {
863
+ ROBOT = [
864
+ " MM MM ",
865
+ " LL LL ",
866
+ " LL LL ",
867
+ " LM MM ",
868
+ " MD MS ",
869
+ " LE LE ",
870
+ " ME ME ",
871
+ " MS MS ",
872
+ " KK KK ",
873
+ " SSDDDSDDDDDDDDDDDDDDDDDDDDDDDDDDDDS ",
874
+ " SSDSDDDDDDDDDDDDDDDDDDDDDDDDDDDDDSS ",
875
+ " SSDOOOOOOOOOOOOOOOOOOOOOOOOOOOOOKSS ",
876
+ " SSOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMOSS ",
877
+ " DDKMMLLLMLLMMMMMMMMMMMMMMMMMMMMMODD ",
878
+ " DDOMMLMMMMMMMMMMMMMMMMMMMMMMMMMMODD ",
879
+ " SSSSODDOMMLMMMMMMMMMMMMMMMMMMMMMMMMMMODDOSSSS ",
880
+ " SSSSODDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODDOSSSS ",
881
+ " SMMMMODDOMMLMMMMMMMMMMMMMMMMMMMMMMMMMMODDOSMMMS ",
882
+ " DMSSSODDOMMMMOOOOOOMMMMMMMMMOOOOOOMMMMODDOSSSED ",
883
+ " DSSSSODDOMMMMODDDDDMMMMMMMMMODDDDDMMMMODDOSSSSK ",
884
+ " DSMMSODDOMMMMDDDDDDMMMMMMMMMKDDDDDMMMMODDOSMMSK ",
885
+ " DSSSSODDOMMMMDDDDDDMMMMMMMMMDDDDDDMMMMODDOSSSSK ",
886
+ " DSSSSODDOMMMMDDDDDDMMMMMMMMMDDDDDDMMMMODDODSSSK ",
887
+ " DSSSSODDOMMMMDDDDDDMMMMMMMMMDDDDDDMMMMODDODSSSK ",
888
+ " DSSSKODDOMMMMDDDDDDMMMMMMMMMDDDDDDMMMMODDODSSSD ",
889
+ " OKKKKODDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODDOKKKKO ",
890
+ " KKKKODDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODDOOKKK ",
891
+ " KKKOODDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODDOOKKK ",
892
+ " DDOMMMMDDDDDDDDDDDDDDDDDDDDDMMMMODD ",
893
+ " DDOMMMMDDDDDDDDDDDDDDDDDDDDDMMMMODD ",
894
+ " DDOMMMMDDDDDDDDDDDDDDDDDDDDDMMMMODD ",
895
+ " DDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODD ",
896
+ " DDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODD ",
897
+ " DDOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMODD ",
898
+ " DDDOOOOOOOOOOOOOOOOOOOOOOOOOOOOOKDD ",
899
+ " DDDDDDDDDDDDDDDDDDDDDDDDDDDDDKDDDDD ",
900
+ " DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ",
901
+ " OOOOOOOOOOO ",
902
+ " OODDDDDKKOO ",
903
+ " DSMMMMMMSDD ",
904
+ " OOOOOOOOOOO ",
905
+ " KDSMMMMMESSKK ",
906
+ " MMMMMMSSKOOKDSMMMMMSSSDKOODSEMMMMMM ",
907
+ " SSSSDO MMMMMMSSKKOOOOOOOOOOOOOOOOOKSSSMMMMMM ODSSSS ",
908
+ " EEESSSSOOLLLLMMMMMESSSSSSSSSSSSSSSSSSSSMMMMMLLLLOOSSSSMME ",
909
+ " SMMSSSSKOEELLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLEEOOSSSSEMS ",
910
+ " DSSSSSSSOOSEEKSMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMSOEEDOOSSSSSSSK ",
911
+ " DSSSSSSDOOSEOSKEMMMMMMMMMMMMMMMMMMMMMMMMMMMMMEOSDEDOODSSSSSSK ",
912
+ " KSSSSSSKOODSSOSEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEESOEEDOOKSSSSSSK ",
913
+ " DKSSSSKKOODSMEEMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMSEMSDOOKKSSSSDK ",
914
+ " OKDDKDKKOOKSMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMSKOOKKKKKKKO ",
915
+ " OOOOOKKKOOKSMMMMESSSSSSSSSSSSSMMMMEEEEEESSSSEMMMMSKOOKKKOOOOO ",
916
+ " OOOOOOKKOOKSMMMSSKKKKKKKKKKKKKKMMMDKOOOOOOOODDMMMSKOOKKOOOOOO ",
917
+ " OKSSKOOOOOKSMMMKKMLLLLLLLLLLLMKMMMDDSSSSSSSSDDMMMSKOOOOODSSKO ",
918
+ " SDOKKK KSMMMKDLLLLLLLLLLLLLKMMMKDKKKDDKKKDKMMMSK KKKKDS ",
919
+ " DMLLMKO KSMMMKDLLLLLLLLLLLLLKMMMDDDDDDDDDSKDMMMSK ODMLLMD ",
920
+ " DELLMSD KSMMMKKMLLLLLLLLLLLLKMMMDKKKKKKKKKDDMMMSK DSMLLEO ",
921
+ " KSKKKKDO KSMMMEDDDDKKDDDKKKDKSMMMDDDDDDDDDSDDMMMSK ODDKKKSK ",
922
+ " EMLLMDO KSMMMMLLLLLLLLLLLLLLLMMMDOOOOOOOOODDMMMSK ODMLLME ",
923
+ " KMLLMSD KSMMMMMMMMMMMMMMMMMMMMMMDDSSDDSSSSDDMMMSK SSMLLEK ",
924
+ " KSKKKKSO KSMMMMMOOMMOOMKOEMOOMMMMDOOOOOOOOODDMMMSK OSKKKKSO ",
925
+ " SMLLMDO KSMMMMMKKMMKKMDKEMKKMMMMKDDDDSSSSDDDMMMSK ODMLLMS ",
926
+ " SMMMMSD KSMMMMMEEMMEEMEEMMEEMMMMDSOOOOOOOODDMMSSK DSMMMMD ",
927
+ " OOOOOOO KSSMMMMMMMMMMMMMMMMMMMMMDDDDDDDDDDDDMSSSK OOOOOOO ",
928
+ " KSMLLLMMSKO KSSSSMMMMMMMMMMMMMMMMMMMSOOOOOOOOOOSSSSSK OKSMLLLLMSO ",
929
+ " DSMMMEEESKK KSSKSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSOSSK KDSMMMMMESD ",
930
+ " DSMMMMMESKK KSOOKSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSOOKSK KKSMMMMMMSD ",
931
+ " DSMMMMMESKK OKSKSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSDSKO KDSMMMMMMSD ",
932
+ " KDDDDDDDDKK KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKDDDDDDDDO ",
933
+ " OOOOOOOOOOO OOOOOOOOO OOOOOOOOO OOOOOOOOOOO ",
934
+ "DSDO OSSO ODKKKKKKK OKKKKKKKO KEDO ODEK",
935
+ "SMDO OSMD KDSSSSSSK KDSSSSSKO SMDO ODMS",
936
+ "DEKO OOED OOKKKOOO OOOKKOOO SSOO OKMS",
937
+ "DEKO OOED SEMMMMSK OSMMMMED SSKO OKES",
938
+ "DSSO OESK SEMMMMSK OSMMMMED KSSO OSSK",
939
+ "KSMS EEDO OOKKKKKO OOKKKKKO OSEE EMSO",
940
+ " DSS SSK OSEEESDO ODSEEESO KSS SSD ",
941
+ " OOO OO SEMMMMSK OSMMMMED OO OOO ",
942
+ " KSSSSSDO OKSSESSK ",
943
+ " DKKDDDDDDKKDK KDKDDDDDDDKDK ",
944
+ " SSMLLMMMMMMLMSK DSSMMMMMMMLLMSS ",
945
+ " SMLMMMMMMMMLLED SEMMMMMMMMMMLMS ",
946
+ " DMMMMMMMMMMMMMMD SMMMMMMMMMMMMMSK ",
947
+ " ODSSSSSSSSSSSSSSDO ODSSSSSSSSSSSSSSKO ",
948
+ " OOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOO ",
949
+ " OKSSSSSSSSSSSSSSK KSSSSSSSSSSSSSSKO ",
950
+ " OKDDDDDDDDDDDDDDK KDDDDDDDDDDDDDDOO "
951
+ ];
952
+ HEAD = ROBOT.slice(0, 37);
953
+ });
954
+
955
+ // packages/core/src/audit.ts
956
+ var AUDIT_TYPES, AUDIT_TYPES_SQL;
957
+ var init_audit = __esm(() => {
958
+ AUDIT_TYPES = new Set([
959
+ "session.started",
960
+ "session.ended",
961
+ "tool.denied",
962
+ "claim.acquired",
963
+ "claim.renewed",
964
+ "claim.released",
965
+ "claim.expired",
966
+ "claim.orphaned",
967
+ "claim.denied",
968
+ "rules.changed",
969
+ "worktree.reclaimed",
970
+ "worktree.created",
971
+ "worktree.removed",
972
+ "worktree.bootstrapped",
973
+ "pr.opened",
974
+ "question.asked",
975
+ "question.answered",
976
+ "dispatch.queued",
977
+ "dispatch.started",
978
+ "dispatch.finished",
979
+ "resource.acquired",
980
+ "resource.released",
981
+ "resource.reaped",
982
+ "process.started",
983
+ "process.exited",
984
+ "gate.recorded",
985
+ "gate.blocked",
986
+ "codify.applied",
987
+ "handoff.recorded",
988
+ "permission.requested",
989
+ "permission.resolved",
990
+ "incident.opened",
991
+ "incident.acked",
992
+ "run.result",
993
+ "workflow.started",
994
+ "workflow.finished"
995
+ ]);
996
+ AUDIT_TYPES_SQL = [...AUDIT_TYPES].map((t) => `'${t}'`).join(", ");
997
+ });
998
+
999
+ // packages/core/src/budget.ts
1000
+ var BUDGET_ASK_TOOLS;
1001
+ var init_budget = __esm(() => {
1002
+ BUDGET_ASK_TOOLS = new Set(["Bash", "Edit", "Write", "MultiEdit", "NotebookEdit"]);
1003
+ });
1004
+ // packages/core/src/workflows.ts
1005
+ var init_workflows = () => {};
1006
+
1007
+ // packages/core/src/config.ts
1008
+ var init_config = __esm(() => {
1009
+ init_audit();
1010
+ init_workflows();
1011
+ });
1012
+
1013
+ // packages/core/src/context.ts
1014
+ var init_context = () => {};
1015
+
1016
+ // packages/core/src/dag.ts
1017
+ var init_dag = () => {};
541
1018
  // packages/core/src/dryrun.ts
542
1019
  var init_dryrun = __esm(() => {
543
1020
  init_rules();
@@ -578,6 +1055,7 @@ var init_hygiene = __esm(() => {
578
1055
  // packages/core/src/ledger.ts
579
1056
  var EDIT_TOOLS;
580
1057
  var init_ledger = __esm(() => {
1058
+ init_prompt();
581
1059
  EDIT_TOOLS = new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
582
1060
  });
583
1061
  // packages/core/src/lessons-apply.ts
@@ -592,65 +1070,31 @@ var init_lineage = __esm(() => {
592
1070
  var init_mcphealth = __esm(() => {
593
1071
  init_gatehealth();
594
1072
  });
595
-
596
1073
  // packages/core/src/memory.ts
597
1074
  var init_memory = () => {};
1075
+ // packages/core/src/otel.ts
1076
+ var init_otel = () => {};
1077
+
598
1078
  // packages/core/src/outcomes.ts
599
1079
  var DEFAULT_BRANCHES;
600
1080
  var init_outcomes = __esm(() => {
601
1081
  DEFAULT_BRANCHES = new Set(["main", "master", "develop", "trunk"]);
602
1082
  });
603
- // packages/core/src/policy.ts
604
- import { createHash } from "crypto";
605
- function verifyPolicyCache(raw) {
606
- if (!raw || typeof raw !== "object")
607
- return null;
608
- const c = raw;
609
- if (c.version !== POLICY_CACHE_VERSION || typeof c.sha256 !== "string")
610
- return null;
611
- const { sha256, ...body } = c;
612
- if (!body.modes || !Array.isArray(body.sessions) || !Array.isArray(body.worktrees))
613
- return null;
614
- return digest(body) === sha256 ? c : null;
615
- }
616
- function evaluateOffline(cache, raw, toplevel, now = Date.now()) {
617
- const tool = typeof raw.tool_name === "string" ? raw.tool_name : "";
618
- const input = raw.tool_input ?? {};
619
- const id = typeof raw.session_id === "string" ? raw.session_id : "";
620
- const cwd = typeof raw.cwd === "string" ? raw.cwd : "";
621
- const isWrite = WRITE_TOOLS.has(tool) && typeof input.file_path === "string";
622
- const cmd = tool === "Bash" && typeof input.command === "string" ? input.command : null;
623
- if (!isWrite && !cmd)
624
- return { action: "allow" };
625
- const current = { id, cwd, toplevel: toplevel(cwd) };
626
- const modes = cache.modes;
627
- if (modes.no_foreign_worktree !== "off" || modes.claim_required_to_write !== "off") {
628
- const target = isWrite ? absolutePath(input.file_path, cwd) : cwd;
629
- const w = guardWrite(target, current, cache.worktrees, modes, isWrite ? "file" : "bash");
630
- if (w.action !== "allow")
631
- return w;
632
- }
633
- if (!cmd)
634
- return { action: "allow" };
635
- return guardBash(cmd, current, cache.sessions, now, modes);
636
- }
637
- var POLICY_CACHE_VERSION = 1, POLICY_CACHE_FILE = "policy.cache.json", digest = (body) => createHash("sha256").update(JSON.stringify(body)).digest("hex");
638
- var init_policy = __esm(() => {
639
- init_hooks();
640
- init_rules();
641
- });
642
1083
 
643
1084
  // packages/core/src/pricing.ts
644
1085
  var init_pricing = () => {};
645
1086
  // packages/core/src/provenance.ts
646
1087
  var init_provenance = () => {};
647
1088
  // packages/core/src/quota.ts
648
- var BURN_MIN_SPAN_MS;
1089
+ var BURN_MIN_SPAN_MS, BURN_LOOKBACK_MS;
649
1090
  var init_quota = __esm(() => {
650
1091
  BURN_MIN_SPAN_MS = 10 * 60000;
1092
+ BURN_LOOKBACK_MS = 60 * 60000;
651
1093
  });
652
1094
  // packages/core/src/security.ts
653
- var init_security = () => {};
1095
+ var init_security = __esm(() => {
1096
+ init_rules();
1097
+ });
654
1098
 
655
1099
  // packages/core/src/stall.ts
656
1100
  var init_stall = () => {};
@@ -749,6 +1193,7 @@ var init_src2 = __esm(() => {
749
1193
  init_actor();
750
1194
  init_adapters();
751
1195
  init_hooks();
1196
+ init_agenthooks();
752
1197
  init_art();
753
1198
  init_audit();
754
1199
  init_budget();
@@ -766,9 +1211,11 @@ var init_src2 = __esm(() => {
766
1211
  init_lineage();
767
1212
  init_mcphealth();
768
1213
  init_memory();
1214
+ init_otel();
769
1215
  init_outcomes();
770
1216
  init_policy();
771
1217
  init_pricing();
1218
+ init_prompt();
772
1219
  init_provenance();
773
1220
  init_quota();
774
1221
  init_rules();
@@ -921,7 +1368,7 @@ if (out === null && base !== fallback) {
921
1368
  out = await post(fallback);
922
1369
  } catch {}
923
1370
  }
924
- if (out === null && event === "PreToolUse")
1371
+ if (out === null && (event === "PreToolUse" || event === "BeforeTool"))
925
1372
  out = offline();
926
1373
  process.stdout.write(`${out ?? "{}"}
927
1374
  `);
@@ -934,6 +1381,18 @@ function offline() {
934
1381
  if (!cache)
935
1382
  return null;
936
1383
  const raw = input ? JSON.parse(input) : {};
1384
+ const agent = detectAgent(raw);
1385
+ if (agent !== "claude-code") {
1386
+ for (const r of toToolRequests(agent, raw)) {
1387
+ const d = evaluateOffline(cache, { tool_name: r.tool, tool_input: r.input, session_id: r.sessionId, cwd: r.cwd }, gitToplevel);
1388
+ if (d.action !== "allow")
1389
+ return renderAgentDecision(agent, {
1390
+ ...d,
1391
+ reason: `(daemon unreachable, policy-locked rule) ${d.reason}`
1392
+ });
1393
+ }
1394
+ return null;
1395
+ }
937
1396
  const d = evaluateOffline(cache, raw, gitToplevel);
938
1397
  if (d.action === "allow")
939
1398
  return null;