@paradow/ledge 0.28.0 → 0.29.0-snapshot.41297455946.1

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 (48) hide show
  1. package/README.md +8 -0
  2. package/dist/cli/{action-NQRSZEC5.js → action-MCR4BCSE.js} +5 -5
  3. package/dist/cli/ask-L3PSQZ2K.js +17 -0
  4. package/dist/cli/{attach-KXOSFUHO.js → attach-R7FT7LFH.js} +9 -14
  5. package/dist/cli/brain-BJTBN2ZK.js +20 -0
  6. package/dist/cli/{bridge-XCTXVWKH.js → bridge-YBHXFEFX.js} +2 -2
  7. package/dist/cli/{chunk-VKRFHDRK.js → chunk-2X56ZRK7.js} +1122 -1164
  8. package/dist/cli/{chunk-3NQNWGDX.js → chunk-6EW2FD2U.js} +3 -5
  9. package/dist/cli/chunk-DJMFCYF4.js +60 -0
  10. package/dist/cli/chunk-DLKJOK3B.js +6 -0
  11. package/dist/cli/{chunk-VMGACWNP.js → chunk-FWLT3SAN.js} +1 -1
  12. package/dist/cli/{chunk-6IYPRUQG.js → chunk-G3ST5WKR.js} +1 -1
  13. package/dist/cli/{chunk-ZI47U5CQ.js → chunk-GY35ZR5I.js} +102 -72
  14. package/dist/cli/{chunk-NLL2EQJ2.js → chunk-KBSXP63H.js} +22 -7
  15. package/dist/cli/chunk-LOXLGCGN.js +57 -0
  16. package/dist/cli/{chunk-LG3XFIPV.js → chunk-O4MUYFPH.js} +6 -7
  17. package/dist/cli/{chunk-JLUHY5AB.js → chunk-OR2NHT5O.js} +4 -4
  18. package/dist/cli/chunk-S6BZV7AY.js +57 -0
  19. package/dist/cli/{chunk-CR3LGTZQ.js → chunk-TCQ5PYIF.js} +3 -3
  20. package/dist/cli/{chunk-RYZ6FHYT.js → chunk-UMQGRTA4.js} +17 -39
  21. package/dist/cli/{chunk-3CF4FQWG.js → chunk-VQR5ME75.js} +4 -3
  22. package/dist/cli/{chunk-HOOHS3MX.js → chunk-W724IVDC.js} +1 -1
  23. package/dist/cli/{chunk-K7UEKSB2.js → chunk-WYRQZA4G.js} +2 -2
  24. package/dist/cli/chunk-XTDE2URL.js +365 -0
  25. package/dist/cli/daemon-ICC6LBCO.js +16562 -0
  26. package/dist/cli/{decide-OSSTI3FE.js → decide-JSY46QZC.js} +14 -16
  27. package/dist/cli/{handover-2G7OEWIG.js → handover-5RUSRVAD.js} +5 -5
  28. package/dist/cli/{hook-XY4E5H7O.js → hook-QQP3ENGA.js} +87 -81
  29. package/dist/cli/index.js +40 -28
  30. package/dist/cli/launch-RTBGUJSC.js +28 -0
  31. package/dist/cli/{send-RUHGKLMA.js → send-BIIH6SQW.js} +5 -5
  32. package/dist/cli/{setup-K42IVMHS.js → setup-OLVRJLJE.js} +4 -4
  33. package/dist/cli/{status-4Z2VJFID.js → status-Z3I33KZJ.js} +3 -3
  34. package/dist/cli/{tui-4IRYQXYU.js → tui-AIXCETOB.js} +6036 -3629
  35. package/dist/cli/{uninstall-4IMMR55T.js → uninstall-X65CWHIJ.js} +4 -4
  36. package/dist/cli/{version-L2Y7MRH6.js → version-QMHFVP3N.js} +1 -1
  37. package/dist/cli/who-C2VMEM35.js +12 -0
  38. package/package.json +21 -6
  39. package/dist/cli/chunk-CIQAFCX3.js +0 -118
  40. package/dist/cli/chunk-JHFKBKSO.js +0 -159
  41. package/dist/cli/chunk-OYDTU6AP.js +0 -9482
  42. package/dist/cli/chunk-QSRB3F5Y.js +0 -198
  43. package/dist/cli/chunk-UME2F7VM.js +0 -6
  44. package/dist/cli/daemon-ZUHJKLSL.js +0 -2322
  45. package/dist/cli/launch-7WOARLZM.js +0 -35
  46. package/dist/cli/task-2GESYTOL.js +0 -19
  47. package/dist/cli/team-MTSKNMAE.js +0 -205
  48. package/dist/cli/who-PSKHASMK.js +0 -12
@@ -1,26 +1,25 @@
1
1
  import {
2
2
  CODEX_HOOK_VAR
3
- } from "./chunk-LG3XFIPV.js";
3
+ } from "./chunk-O4MUYFPH.js";
4
4
  import {
5
5
  connect
6
- } from "./chunk-VMGACWNP.js";
6
+ } from "./chunk-FWLT3SAN.js";
7
7
  import {
8
- LEDGE_GIVER,
9
- TASK_TEXT_MAX
10
- } from "./chunk-VKRFHDRK.js";
11
- import "./chunk-HOOHS3MX.js";
8
+ ASK_ANSWER_MAX
9
+ } from "./chunk-2X56ZRK7.js";
10
+ import "./chunk-W724IVDC.js";
12
11
  import "./chunk-U67V476Y.js";
13
12
 
14
13
  // src/cli/decide.ts
15
14
  import { randomUUID } from "crypto";
16
15
  var DECIDE_USAGE = `ledge decide les d\xE9cisions que les sessions attendent de toi
17
16
  ledge decide <id> "<r\xE9ponse>" y r\xE9pondre : la session qui l'a demand\xE9e reprend`;
18
- var ID = /^t[0-9a-f]{6}$/;
17
+ var ID = /^a[0-9a-f]{6}$/;
19
18
  var DECIDE_MS = 4e3;
20
19
  function decideArgs(args) {
21
20
  if (!args.length) return { op: "list" };
22
21
  const [id, text] = args;
23
- return args.length === 2 && ID.test(id) && text.trim() && text.length <= TASK_TEXT_MAX ? { op: "done", task: id, text } : void 0;
22
+ return args.length === 2 && ID.test(id) && text.trim() && text.length <= ASK_ANSWER_MAX ? { op: "answer", id, text } : void 0;
24
23
  }
25
24
  async function runDecide(args, opts = {}) {
26
25
  const op = decideArgs(args);
@@ -35,15 +34,14 @@ async function runDecide(args, opts = {}) {
35
34
  }
36
35
  try {
37
36
  if (op.op === "list") {
38
- client.send({ type: "task.cmd", rid: randomUUID(), by: LEDGE_GIVER, op: "list" });
39
- const asked = ((await client.next("task.result", DECIDE_MS)).tasks ?? []).filter((t) => t.owner === LEDGE_GIVER);
40
- if (!asked.length) return { line: "aucune d\xE9cision en attente", code: 0 };
41
- return { line: asked.map((t) => [`${t.id} de ${t.giverName} ${t.title}`, ...t.text ? t.text.split("\n").map((l) => ` ${l}`) : []].join("\n")).join("\n\n"), code: 0 };
37
+ const open = (await client.next("asks", DECIDE_MS)).asks.filter((a) => a.state === "open");
38
+ if (!open.length) return { line: "aucune d\xE9cision en attente", code: 0 };
39
+ return { line: open.map((a) => [`${a.id} de ${a.fromName} ${a.question}`, ...a.options ? a.options.split("\n").map((l) => ` ${l}`) : []].join("\n")).join("\n\n"), code: 0 };
42
40
  }
43
- client.send({ type: "task.cmd", rid: randomUUID(), by: LEDGE_GIVER, op: "done", task: op.task, text: op.text });
44
- const r = await client.next("task.result", DECIDE_MS);
45
- if (r.reason !== void 0) return { line: `refus\xE9 : ${r.reason}`, code: 1 };
46
- return { line: `r\xE9ponse envoy\xE9e \xE0 ${r.task.giverName}`, code: 0 };
41
+ client.send({ type: "ask.answer", rid: randomUUID(), id: op.id, text: op.text });
42
+ const r = await client.next("ask.result", DECIDE_MS);
43
+ if (!r.ask) return { line: `refus\xE9 : ${r.reason ?? "raison inconnue"}`, code: 1 };
44
+ return { line: `r\xE9ponse envoy\xE9e \xE0 ${r.ask.fromName}`, code: 0 };
47
45
  } catch (e) {
48
46
  return { line: `pas de r\xE9ponse de Ledge : ${e.message}`, code: 1 };
49
47
  } finally {
@@ -3,11 +3,11 @@ import {
3
3
  handoverArgs,
4
4
  handoverOnClient,
5
5
  runHandover
6
- } from "./chunk-JLUHY5AB.js";
7
- import "./chunk-LG3XFIPV.js";
8
- import "./chunk-VMGACWNP.js";
9
- import "./chunk-VKRFHDRK.js";
10
- import "./chunk-HOOHS3MX.js";
6
+ } from "./chunk-OR2NHT5O.js";
7
+ import "./chunk-O4MUYFPH.js";
8
+ import "./chunk-FWLT3SAN.js";
9
+ import "./chunk-2X56ZRK7.js";
10
+ import "./chunk-W724IVDC.js";
11
11
  import "./chunk-U67V476Y.js";
12
12
  export {
13
13
  HANDOVER_USAGE,
@@ -1,53 +1,55 @@
1
1
  import {
2
2
  QUESTION_TOOLS,
3
3
  gitInfo,
4
+ mandateSummary,
4
5
  messageNote,
5
6
  readPeers,
6
- resolveRoleAddress,
7
7
  resolveTarget,
8
- seatContext,
9
- takeImages,
10
- teammates
11
- } from "./chunk-ZI47U5CQ.js";
8
+ rigSeatContext,
9
+ takeImages
10
+ } from "./chunk-GY35ZR5I.js";
12
11
  import {
13
- findRole
14
- } from "./chunk-JHFKBKSO.js";
12
+ HUMAN_ADDRESS
13
+ } from "./chunk-DJMFCYF4.js";
14
+ import {
15
+ log
16
+ } from "./chunk-G3ST5WKR.js";
15
17
  import {
16
18
  parseLedgeCommand,
17
19
  readOnClient,
18
20
  sendOnClient
19
- } from "./chunk-3CF4FQWG.js";
21
+ } from "./chunk-VQR5ME75.js";
20
22
  import {
21
- taskOnClient
22
- } from "./chunk-CIQAFCX3.js";
23
+ askOnClient
24
+ } from "./chunk-LOXLGCGN.js";
23
25
  import {
24
26
  handoverOnClient
25
- } from "./chunk-JLUHY5AB.js";
27
+ } from "./chunk-OR2NHT5O.js";
28
+ import {
29
+ brainOnClient
30
+ } from "./chunk-S6BZV7AY.js";
31
+ import "./chunk-TCQ5PYIF.js";
26
32
  import {
27
33
  CODEX_ENV_VAR,
28
34
  CODEX_HOOK_VAR,
29
35
  codexPlan
30
- } from "./chunk-LG3XFIPV.js";
36
+ } from "./chunk-O4MUYFPH.js";
31
37
  import {
32
38
  formatWho
33
- } from "./chunk-3NQNWGDX.js";
39
+ } from "./chunk-6EW2FD2U.js";
34
40
  import {
35
41
  connect,
36
42
  sendMessageAndAwaitAck
37
- } from "./chunk-VMGACWNP.js";
43
+ } from "./chunk-FWLT3SAN.js";
38
44
  import {
39
- taskClosed
40
- } from "./chunk-VKRFHDRK.js";
41
- import {
42
- log
43
- } from "./chunk-6IYPRUQG.js";
45
+ SESSION_KEY_VAR
46
+ } from "./chunk-2X56ZRK7.js";
44
47
  import {
45
48
  paths
46
- } from "./chunk-HOOHS3MX.js";
49
+ } from "./chunk-W724IVDC.js";
47
50
  import "./chunk-U67V476Y.js";
48
51
 
49
52
  // src/cli/hook.ts
50
- import { randomUUID } from "crypto";
51
53
  import { readFileSync, openSync, fstatSync, readSync, closeSync } from "fs";
52
54
  import { basename, join } from "path";
53
55
  var str = (v) => typeof v === "string" ? v : void 0;
@@ -129,6 +131,13 @@ function codexLedgeId(env = process.env, daemon = underCodexDaemon) {
129
131
  if (env[CODEX_ENV_VAR] && !daemon()) return env[CODEX_ENV_VAR];
130
132
  return void 0;
131
133
  }
134
+ var seat = (o) => o ? { openrig: o } : {};
135
+ function openrigSeat(agent, env = process.env, daemon = underCodexDaemon) {
136
+ const sessionName = env.OPENRIG_SESSION_NAME;
137
+ if (!sessionName) return void 0;
138
+ if (agent === "codex" && daemon()) return void 0;
139
+ return { sessionName, ...env.OPENRIG_NODE_ID ? { nodeId: env.OPENRIG_NODE_ID } : {} };
140
+ }
132
141
  function mapHook(agent, eventName, payload, at) {
133
142
  if (!payload || typeof payload !== "object") return null;
134
143
  const p = payload;
@@ -141,7 +150,7 @@ function mapHook(agent, eventName, payload, at) {
141
150
  if (!event) return null;
142
151
  const task = agent === "claude" && event.helperId ? helperDescription(str(p.transcript_path), event.helperId) : void 0;
143
152
  const agentSessionId = !event.helperId ? str(p.session_id) : void 0;
144
- return { id, event: { ...event, ...task ? { task } : {}, ...agentSessionId ? { agentSessionId } : {} }, agent, ...cwd ? { cwd } : {} };
153
+ return { id, event: { ...event, ...task ? { task } : {}, ...agentSessionId ? { agentSessionId } : {} }, agent, ...cwd ? { cwd } : {}, ...seat(openrigSeat(agent)) };
145
154
  }
146
155
  function helperDescription(transcriptPath, agentId) {
147
156
  if (!transcriptPath?.endsWith(".jsonl") || !/^[\w-]+$/.test(agentId)) return void 0;
@@ -285,7 +294,7 @@ function mapCursorHook(eventName, payload, at, env = process.env) {
285
294
  const roots = Array.isArray(p.workspace_roots) ? p.workspace_roots : [];
286
295
  const cwd = str(roots[0]) || void 0;
287
296
  const event = helper ? mapCursorHelperEvent(eventName, p, at, sessionId) : mapCursorEvent(eventName, p, at, !!env.LEDGE_SESSION, transcript);
288
- return event ? { id, event: helper || event.helperId ? event : { ...event, agentSessionId: sessionId }, agent: "cursor", ...cwd ? { cwd } : {} } : null;
297
+ return event ? { id, event: helper || event.helperId ? event : { ...event, agentSessionId: sessionId }, agent: "cursor", ...cwd ? { cwd } : {}, ...seat(openrigSeat("cursor", env)) } : null;
289
298
  }
290
299
  function mapCursorHelperEvent(eventName, p, at, helperId) {
291
300
  const base = { at, source: "hook", helperId };
@@ -337,32 +346,18 @@ function mapCursorEvent(eventName, p, at, launched, transcript) {
337
346
  return null;
338
347
  }
339
348
  }
340
- var TASKS_TOLD = 10;
341
- var TASK_STATE = { open: "ouverte", taken: "prise", done: "termin\xE9e", refused: "refus\xE9e", dropped: "abandonn\xE9e" };
342
- function tasksContext(tasks) {
343
- if (!tasks.length) return void 0;
344
- const shown = tasks.slice(0, TASKS_TOLD);
345
- return [
346
- "Tes t\xE2ches en cours (elles sont gard\xE9es par Ledge, pas dans ta conversation) :",
347
- ...shown.map((t) => `- ${t.id} \xB7 ${TASK_STATE[t.state]} \xB7 \xAB ${t.title} \xBB \xB7 confi\xE9e par ${t.giverName}`),
348
- ...tasks.length > shown.length ? [`- et ${tasks.length - shown.length} autre(s) : ledge task list`] : [],
349
- "Relis-en une avec `ledge task show <id>` avant de reprendre."
350
- ].join("\n");
351
- }
352
349
  function identityContext(s) {
353
350
  const where = [`projet ${s.repo ?? s.project}`, ...s.branch ? [`branche ${s.branch}`] : [], ...s.worktree ? [`worktree ${basename(s.cwd) || s.cwd}`] : []].join(", ");
354
351
  return [
355
352
  `Tu es ${s.name}, ${where} : une session lanc\xE9e par Ledge (${s.tool}). Les autres sessions Ledge te connaissent sous le nom ${s.name}.`,
356
353
  "Pour voir les autres sessions (nom, outil, \xE9tat, projet, branche, sujet), lance `ledge who`.",
357
- s.tool === "claude" ? "Pour \xE9crire \xE0 une autre session Ledge, Claude ou Codex, utilise son nom Ledge (donn\xE9 par `ledge who`) comme adresse SendMessage : Ledge le transmet. `ListAgents` montre les noms internes de Claude, pas les noms Ledge." : "Pour \xE9crire \xE0 une autre session Ledge, lance `ledge send <nom> \"<message>\"` (seul, sans && ni |). Ledge ex\xE9cute lui-m\xEAme `ledge send`, `ledge read`, `ledge task` et `ledge who` : leur r\xE9sultat t'arrive comme \xAB Command blocked by PreToolUse hook: \u2026 \xBB, c'est la r\xE9ponse, pas une erreur.",
358
- "Un long message d'une autre session t'arrive en aper\xE7u avec son id : lance `ledge read <id>` pour le lire en entier. Une `<ledge-notice>` vient de Ledge : soit un de tes messages n'a pas pu \xEAtre remis, soit un changement te concerne (ta consigne de r\xF4le, ton \xE9quipe).",
359
- 'Un message sert \xE0 pr\xE9venir ; pour confier un travail qui ne doit pas se perdre, lance plut\xF4t `ledge task give <nom> "<titre>" "<d\xE9tail>"` (`ledge task` pour le reste). Une `<ledge-task>` t\'en confie une, ou te dit comment une des tiennes a fini : quand on t\'en confie une, prends-la (`ledge task take <id>`) et termine-la toujours (`ledge task done <id> "<r\xE9sultat>"`, ou `refuse`). Une d\xE9cision qui revient \xE0 l\'utilisateur se pose avec `ledge task give ledge "<question>" "<options, ta recommandation>"` : sa r\xE9ponse te r\xE9veille, inutile d\'attendre la fin pour la poser.',
360
- ...s.seat ? [s.seat] : [],
361
- ...s.relieves ? [`Tu as pris la rel\xE8ve de ${s.relieves.name}, dont le contexte \xE9tait plein : relis son bilan avec \`ledge handover show\`.`] : [],
362
- ...s.tasks ? [tasksContext(s.tasks) ?? ""].filter(Boolean) : []
354
+ s.tool === "claude" ? "Pour \xE9crire \xE0 une autre session Ledge, Claude ou Codex, utilise son nom Ledge (donn\xE9 par `ledge who`) comme adresse SendMessage : Ledge le transmet. `ListAgents` montre les noms internes de Claude, pas les noms Ledge." : "Pour \xE9crire \xE0 une autre session Ledge, lance `ledge send <nom> \"<message>\"` (seul, sans && ni |). Ledge ex\xE9cute lui-m\xEAme `ledge send`, `ledge read`, `ledge ask` et `ledge who` : leur r\xE9sultat t'arrive comme \xAB Command blocked by PreToolUse hook: \u2026 \xBB, c'est la r\xE9ponse, pas une erreur.",
355
+ "Un long message d'une autre session t'arrive en aper\xE7u avec son id : lance `ledge read <id>` pour le lire en entier. Une `<ledge-notice>` vient de Ledge : soit un de tes messages n'a pas pu \xEAtre remis, soit un changement te concerne.",
356
+ 'Un message sert \xE0 pr\xE9venir. Une d\xE9cision qui revient \xE0 l\'utilisateur se pose avec `ledge ask "<question>" "<options, ta recommandation>"` : sa r\xE9ponse te r\xE9veille (une `<ledge-answer>`), inutile d\'attendre la fin pour la poser.',
357
+ ...s.relieves ? [`Tu as pris la rel\xE8ve de ${s.relieves.name}, dont le contexte \xE9tait plein : relis son bilan avec \`ledge handover show\`.`] : []
363
358
  ].join("\n");
364
359
  }
365
- async function identity(agent, cwd, known, deadline, live = { sessions: [], teams: [] }) {
360
+ async function identity(agent, cwd, known, deadline) {
366
361
  const name = known?.name ?? process.env.LEDGE_NAME;
367
362
  const dir = known?.cwd ?? cwd;
368
363
  if (!name || !dir) return void 0;
@@ -374,23 +369,9 @@ async function identity(agent, cwd, known, deadline, live = { sessions: [], team
374
369
  project: known?.project ?? (basename(dir) || dir),
375
370
  ...known?.repo ?? git?.repo ? { repo: known?.repo ?? git.repo } : {},
376
371
  ...known?.branch ?? git?.branch ? { branch: known?.branch ?? git.branch } : {},
377
- ...known?.worktree ?? git?.worktree ? { worktree: true } : {},
378
- ...seatOf(known, dir, live)
372
+ ...known?.worktree ?? git?.worktree ? { worktree: true } : {}
379
373
  };
380
374
  }
381
- function seatOf(known, dir, live) {
382
- const roleId = known?.role ?? process.env.LEDGE_ROLE;
383
- if (!roleId) return {};
384
- let role;
385
- try {
386
- role = findRole(roleId, dir);
387
- } catch {
388
- role = void 0;
389
- }
390
- const team = known?.team ? live.teams.find((t) => t.id === known.team && t.state !== "dissolved") : void 0;
391
- const seat = seatContext({ ...role ? { role } : {}, roleId, roleLabel: known?.roleLabel, ...team ? { team: { name: team.name, members: teammates(team, live.sessions).filter((m) => m.name !== known?.name) } } : {} });
392
- return seat ? { seat } : {};
393
- }
394
375
  var SEND_MESSAGE_MS = 1500;
395
376
  var deny = (reason) => JSON.stringify({ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: reason } });
396
377
  async function relayMessage(client, from, toolUseId, input, deadline) {
@@ -400,14 +381,7 @@ async function relayMessage(client, from, toolUseId, input, deadline) {
400
381
  const snapshot = await client.next("snapshot", left());
401
382
  const sender = snapshot.sessions.find((s) => s.id === from);
402
383
  if (sender?.source !== "launcher") return void 0;
403
- let address = to;
404
- if (to.startsWith("@")) {
405
- const teams = sender.team ? (await client.next("teams", left()).catch(() => void 0))?.teams ?? [] : [];
406
- const role = resolveRoleAddress(to, from, snapshot.sessions, teams);
407
- if ("reason" in role) return deny(`Ledge n'a pas pu transmettre le message \xE0 ${to} : ${role.reason}`);
408
- address = role.to;
409
- }
410
- const r = resolveTarget(address, snapshot.sessions, readPeers());
384
+ const r = resolveTarget(to, snapshot.sessions, readPeers());
411
385
  if (r.outcome === "unknown") return void 0;
412
386
  if (r.outcome === "refused") return deny(`Ledge n'a pas pu transmettre le message \xE0 ${to} : ${r.reason}`);
413
387
  if (r.outcome === "native") {
@@ -418,7 +392,7 @@ async function relayMessage(client, from, toolUseId, input, deadline) {
418
392
  }
419
393
  const tool = r.target.tool;
420
394
  const summary = str(input.summary);
421
- const ack = await sendMessageAndAwaitAck(client, { type: "session.message", rid: toolUseId, from, to: address, text: input.message, ...summary ? { summary } : {}, intercept: true }, left());
395
+ const ack = await sendMessageAndAwaitAck(client, { type: "session.message", rid: toolUseId, from, to, text: input.message, ...summary ? { summary } : {}, intercept: true }, left());
422
396
  const target = `${to} (${tool})`;
423
397
  const reason = ack.outcome === "accepted" ? `Message transmis par Ledge \xE0 ${target} : il le re\xE7oit maintenant.` : ack.outcome === "queued" ? `Message transmis par Ledge \xE0 ${target} : il le recevra d\xE8s qu'il sera libre.` : ack.outcome === "refused" ? `Ledge n'a pas pu transmettre le message \xE0 ${to} : ${ack.reason ?? "raison inconnue"}` : void 0;
424
398
  return reason ? deny(reason) : void 0;
@@ -426,13 +400,41 @@ async function relayMessage(client, from, toolUseId, input, deadline) {
426
400
  async function runLedgeCommand(client, cmd, from, deadline) {
427
401
  const left = () => Math.max(0, deadline - Date.now());
428
402
  let line;
429
- if (cmd.cmd === "who") line = formatWho((await client.next("snapshot", left())).sessions, from);
403
+ if (cmd.cmd === "brain") line = (await brainOnClient(client, cmd.args, from, process.env[SESSION_KEY_VAR], left())).line;
404
+ else if (cmd.cmd === "who") line = formatWho((await client.next("snapshot", left())).sessions, from);
430
405
  else if (cmd.cmd === "read") line = (await readOnClient(client, cmd.args, from, left())).line;
431
- else if (cmd.cmd === "task") line = (await taskOnClient(client, cmd.args, from, left())).line;
406
+ else if (cmd.cmd === "ask") line = (await askOnClient(client, cmd.args, from, left())).line;
432
407
  else if (cmd.cmd === "handover") line = (await handoverOnClient(client, cmd.args, from, left())).line;
433
408
  else line = (await sendOnClient(client, cmd.args, from, left())).line;
434
409
  return deny(line);
435
410
  }
411
+ var WRAPPERS = "sudo|env|exec|command|nohup|time|timeout|nice|stdbuf|watch|xargs|then|do|else|elif|if|while|until";
412
+ var WRAPPER_ARG = "(?:-\\S+(?:\\s+[^-\\s;&|]\\S*)?|\\d[\\w.]*)\\s+";
413
+ var COMMAND_AT = `(?:^|[;&|({!\`\\n])\\s*(?:(?:[A-Za-z_][A-Za-z0-9_]*=\\S*|${WRAPPERS})\\s+(?:${WRAPPER_ARG})*)*`;
414
+ var LOOPBACK = "(?<![\\w.])(?:127(?:\\.\\d+){1,3}|localhost|0\\.0\\.0\\.0|0|::1)";
415
+ var OPENRIG_API = `\\b(?:curl|wget|https?|httpie|xh|nc|ncat|netcat|telnet|socat)\\b[^;&|\\n]*?${LOOPBACK}\\]?(?::|\\s+)7433\\b|/dev/(?:tcp|udp)/${LOOPBACK}/7433\\b`;
416
+ var BRAIN_FORBIDDEN = new RegExp(`${COMMAND_AT}(?:\\S*/)?(?:rig|tmux)(?=$|[\\s;&|)\`])|${OPENRIG_API}`, "mi");
417
+ var BRAIN_REFUSAL = "Le cerveau de Ledge ne lance ni rig, ni tmux, ni l'API d'OpenRig lui-m\xEAme : passe par `ledge brain \u2026`.";
418
+ function brainRefusal(command) {
419
+ return BRAIN_FORBIDDEN.test(command) ? BRAIN_REFUSAL : null;
420
+ }
421
+ var isBrainId = (id) => id === "brain" || !!id?.startsWith("brain-");
422
+ function isBrain(agent, id, env, daemon = underCodexDaemon) {
423
+ if (isBrainId(id)) return true;
424
+ if (env.LEDGE_BRAIN !== "1") return false;
425
+ return agent !== "codex" || !env[CODEX_HOOK_VAR] && !daemon();
426
+ }
427
+ var BRAIN_CONTEXT = "Tu es Ledge, l'humain des rigs OpenRig de l'utilisateur et son seul interlocuteur. Tes consignes, ton mandat et ta m\xE9moire sont dans CLAUDE.md / AGENTS.md de ce dossier ; tu n'agis que par `ledge brain \u2026`.";
428
+ function seatStart(seat2) {
429
+ let text = null;
430
+ try {
431
+ text = readFileSync(paths().mandate, "utf8");
432
+ } catch {
433
+ }
434
+ const rig = seat2.slice(seat2.indexOf("@") + 1);
435
+ const additionalContext = rigSeatContext({ seat: seat2, rig, human: HUMAN_ADDRESS, mandate: mandateSummary(text) });
436
+ return JSON.stringify({ hookSpecificOutput: { hookEventName: "SessionStart", additionalContext } });
437
+ }
436
438
  async function runHook(eventName, stdin, opts = {}) {
437
439
  const agent = opts.tool ?? "claude";
438
440
  const label = agent === "claude" ? eventName : `${agent} ${eventName}`;
@@ -443,10 +445,9 @@ async function runHook(eventName, stdin, opts = {}) {
443
445
  let mapped = null;
444
446
  let identify = false;
445
447
  let known;
446
- let live = { sessions: [], teams: [] };
447
448
  let deadline = Date.now() + (opts.timeoutMs ?? 100);
448
449
  let relayed;
449
- let held = [];
450
+ let brain = false;
450
451
  try {
451
452
  const payload = JSON.parse(stdin || "{}");
452
453
  mapped = agent === "cursor" ? mapCursorHook(eventName, payload, (/* @__PURE__ */ new Date()).toISOString()) : mapHook(agent, eventName, payload, (/* @__PURE__ */ new Date()).toISOString());
@@ -455,22 +456,22 @@ async function runHook(eventName, stdin, opts = {}) {
455
456
  return void 0;
456
457
  }
457
458
  const launchedId = agent === "codex" ? codexLedgeId() : agent === "claude" ? process.env.LEDGE_SESSION : void 0;
458
- identify = eventName === "SessionStart" && !!launchedId && mapped.id === launchedId;
459
+ brain = agent !== "cursor" && isBrain(agent, launchedId, process.env, opts.daemon);
460
+ identify = !brain && eventName === "SessionStart" && !!launchedId && mapped.id === launchedId;
459
461
  const input = payload.tool_input ?? {};
462
+ const shell = eventName === "PreToolUse" && str(payload.tool_name) === "Bash" && typeof input.command === "string" ? input.command : void 0;
463
+ const refusal = brain && shell !== void 0 ? brainRefusal(shell) : null;
464
+ if (refusal) relayed = deny(refusal);
460
465
  const intercept = agent === "claude" && eventName === "PreToolUse" && str(payload.tool_name) === "SendMessage" && typeof input.to === "string" && typeof input.message === "string" && !!input.message;
461
- const command = agent === "codex" && eventName === "PreToolUse" && str(payload.tool_name) === "Bash" && typeof input.command === "string" ? parseLedgeCommand(input.command) : void 0;
466
+ const parsed = agent === "codex" && !refusal && shell !== void 0 ? parseLedgeCommand(shell) : void 0;
467
+ const command = parsed?.cmd === "brain" && !brain ? void 0 : parsed;
462
468
  const timeoutMs = opts.timeoutMs ?? (identify ? 1e3 : intercept || command ? SEND_MESSAGE_MS : 100);
463
469
  deadline = Date.now() + timeoutMs;
464
470
  const client = await connect({ port: opts.port, timeoutMs });
465
- client.send({ type: "session.event", id: mapped.id, event: mapped.event, agent: mapped.agent, ...mapped.cwd ? { cwd: mapped.cwd } : {} });
471
+ client.send({ type: "session.event", id: mapped.id, event: mapped.event, agent: mapped.agent, ...mapped.cwd ? { cwd: mapped.cwd } : {}, ...seat(mapped.openrig) });
466
472
  if (identify) {
467
473
  const snapshot = await client.next("snapshot", Math.max(0, deadline - Date.now())).catch(() => void 0);
468
474
  known = snapshot?.sessions.find((s) => s.id === mapped.id);
469
- const teamList = known?.team ? (await client.next("teams", Math.max(0, deadline - Date.now())).catch(() => void 0))?.teams : void 0;
470
- live = { sessions: snapshot?.sessions ?? [], teams: teamList ?? [] };
471
- client.send({ type: "task.cmd", rid: randomUUID(), by: mapped.id, op: "list" });
472
- const listed = await client.next("task.result", Math.max(0, deadline - Date.now())).catch(() => void 0);
473
- held = (listed?.tasks ?? []).filter((t) => t.owner === mapped.id && !taskClosed(t));
474
475
  }
475
476
  if (intercept) relayed = await relayMessage(client, mapped.id, str(payload.tool_use_id), input, deadline);
476
477
  if (command) relayed = await runLedgeCommand(client, command, mapped.id, deadline);
@@ -479,23 +480,28 @@ async function runHook(eventName, stdin, opts = {}) {
479
480
  log(`hook ${label} dropped: ${e.message}`);
480
481
  }
481
482
  if (relayed) return relayed;
483
+ if (brain && eventName === "SessionStart" && mapped) return JSON.stringify({ hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: BRAIN_CONTEXT } });
484
+ if (eventName === "SessionStart" && mapped?.openrig && !identify && !process.env.LEDGE_SESSION) return seatStart(mapped.openrig.sessionName);
482
485
  if (!identify || !mapped) return void 0;
483
- const found = await identity(agent, mapped.cwd, known, deadline, live);
484
- const who = found ? { ...found, ...held.length ? { tasks: held } : {}, ...known?.relieves ? { relieves: known.relieves } : {} } : void 0;
486
+ const found = await identity(agent, mapped.cwd, known, deadline);
487
+ const who = found ? { ...found, ...known?.relieves ? { relieves: known.relieves } : {} } : void 0;
485
488
  return who ? JSON.stringify({ hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: identityContext(who) } }) : void 0;
486
489
  }
487
490
  export {
491
+ BRAIN_CONTEXT,
492
+ BRAIN_FORBIDDEN,
488
493
  INPUT_MAX,
489
494
  REPLY_MAX,
495
+ brainRefusal,
490
496
  codexLedgeId,
491
497
  cursorReply,
492
498
  identityContext,
493
499
  mapClaudeHook,
494
500
  mapCursorHook,
495
501
  mapHook,
502
+ openrigSeat,
496
503
  readTranscript,
497
504
  runHook,
498
- tasksContext,
499
505
  toolOutput,
500
506
  trimInput,
501
507
  underCodexDaemon
package/dist/cli/index.js CHANGED
@@ -1,7 +1,15 @@
1
1
  #!/usr/bin/env node
2
+ import "./chunk-U67V476Y.js";
3
+
4
+ // src/cli/removed.ts
5
+ function removedCommand(cmd) {
6
+ if (cmd === "task") return `ledge task a \xE9t\xE9 retir\xE9 : pour une d\xE9cision qui revient \xE0 l'utilisateur, ledge ask "<question>" "<options, ta recommandation>"`;
7
+ if (cmd === "team") return "ledge team a \xE9t\xE9 retir\xE9";
8
+ return void 0;
9
+ }
2
10
 
3
11
  // src/cli/index.ts
4
- var USAGE = `ledge <claude|codex|cursor> [--name <bot>] [--role <id>] [-- agent args] start an agent in a tracked session (--role: .ledge/roles/<id>.md)
12
+ var USAGE = `ledge <claude|codex|cursor> [--name <bot>] [-- agent args] start an agent in a tracked session
5
13
  ledge attach [--read-only] <id> put this terminal on a running session
6
14
  ledge daemon run the daemon in the foreground
7
15
  ledge hook [codex|cursor] <Event> receive a hook (reads JSON on stdin)
@@ -11,13 +19,13 @@ ledge status list sessions
11
19
  ledge who list the live sessions (name, tool, state, project, branch, topic)
12
20
  ledge send <nom> "<message>" [--summary <texte>] write to another Ledge session
13
21
  ledge read <id> print a long message whole (the id is in its pointer)
14
- ledge task <give|list|show|take|done|refuse|pass> \u2026 give another session work, take it, close it (ledge task: details)
22
+ ledge ask "<question>" "<options, ta recommandation>" ask the user a decision; the answer wakes you
15
23
  ledge decide [<id> "<r\xE9ponse>"] the decisions sessions wait for from you; answer one
16
24
  ledge handover "<bilan>" | show the relief brief, when Ledge asks for it; show: read your predecessor's
17
- ledge team <up|list|join|leave|resume|dissolve|save> \u2026 start, list and run teams (ledge team: details; --json for agents)
18
25
  ledge close <session>\u2026 [--json] close sessions cleanly (their conversation stays resumable)
19
26
  ledge tui [--notify bell|osc9|off] [--here] follow and answer the sessions (in a Ledge tab of Windows Terminal unless --here)
20
27
  ledge bridge exchange Ledge messages over SSH stdio
28
+ ledge brain <say|decide|ask|rig|send|propose|remember|who|rigs|escalations|rules> \u2026 only from Ledge's brain session
21
29
  ledge --version print the installed version`;
22
30
  async function hookMain(args) {
23
31
  const tool = args[0] === "codex" || args[0] === "cursor" ? args[0] : void 0;
@@ -25,7 +33,7 @@ async function hookMain(args) {
25
33
  try {
26
34
  const chunks = [];
27
35
  for await (const c of process.stdin) chunks.push(c);
28
- const { runHook } = await import("./hook-XY4E5H7O.js");
36
+ const { runHook } = await import("./hook-QQP3ENGA.js");
29
37
  const out = await runHook(event, Buffer.concat(chunks).toString("utf8"), { tool });
30
38
  if (out) process.stdout.write(`${out}
31
39
  `);
@@ -37,7 +45,7 @@ async function main(argv) {
37
45
  const [cmd, ...rest] = argv;
38
46
  switch (cmd) {
39
47
  case "daemon": {
40
- const { startDaemon } = await import("./daemon-ZUHJKLSL.js");
48
+ const { startDaemon } = await import("./daemon-ICC6LBCO.js");
41
49
  await startDaemon();
42
50
  await new Promise(() => {
43
51
  });
@@ -45,17 +53,17 @@ async function main(argv) {
45
53
  }
46
54
  case "--version":
47
55
  case "version": {
48
- const { LEDGE_VERSION } = await import("./version-L2Y7MRH6.js");
56
+ const { LEDGE_VERSION } = await import("./version-QMHFVP3N.js");
49
57
  console.log(LEDGE_VERSION);
50
58
  return 0;
51
59
  }
52
60
  case "setup": {
53
- const { runSetup } = await import("./setup-K42IVMHS.js");
61
+ const { runSetup } = await import("./setup-OLVRJLJE.js");
54
62
  for (const l of await runSetup()) console.log(l);
55
63
  return 0;
56
64
  }
57
65
  case "uninstall": {
58
- const { runUninstall, uninstallArgs } = await import("./uninstall-4IMMR55T.js");
66
+ const { runUninstall, uninstallArgs } = await import("./uninstall-X65CWHIJ.js");
59
67
  const args = uninstallArgs(rest);
60
68
  if (!args) {
61
69
  console.log(USAGE);
@@ -66,76 +74,80 @@ async function main(argv) {
66
74
  return r.ok ? 0 : 1;
67
75
  }
68
76
  case "status": {
69
- const { printStatus } = await import("./status-4Z2VJFID.js");
77
+ const { printStatus } = await import("./status-Z3I33KZJ.js");
70
78
  console.log(await printStatus());
71
79
  return 0;
72
80
  }
73
81
  case "who": {
74
- const { printWho } = await import("./who-PSKHASMK.js");
82
+ const { printWho } = await import("./who-C2VMEM35.js");
75
83
  console.log(await printWho());
76
84
  return 0;
77
85
  }
78
86
  case "send": {
79
- const { sendMessage } = await import("./send-RUHGKLMA.js");
87
+ const { sendMessage } = await import("./send-BIIH6SQW.js");
80
88
  const r = await sendMessage(rest);
81
89
  console.log(r.line);
82
90
  return r.code;
83
91
  }
84
92
  case "read": {
85
- const { readMessage } = await import("./send-RUHGKLMA.js");
93
+ const { readMessage } = await import("./send-BIIH6SQW.js");
86
94
  const r = await readMessage(rest);
87
95
  console.log(r.line);
88
96
  return r.code;
89
97
  }
90
- case "task": {
91
- const { runTask } = await import("./task-2GESYTOL.js");
92
- const r = await runTask(rest);
98
+ case "ask": {
99
+ const { runAsk } = await import("./ask-L3PSQZ2K.js");
100
+ const r = await runAsk(rest);
93
101
  console.log(r.line);
94
102
  return r.code;
95
103
  }
96
104
  case "decide": {
97
- const { runDecide } = await import("./decide-OSSTI3FE.js");
105
+ const { runDecide } = await import("./decide-JSY46QZC.js");
98
106
  const r = await runDecide(rest);
99
107
  console.log(r.line);
100
108
  return r.code;
101
109
  }
102
110
  case "handover": {
103
- const { runHandover } = await import("./handover-2G7OEWIG.js");
111
+ const { runHandover } = await import("./handover-5RUSRVAD.js");
104
112
  const r = await runHandover(rest);
105
113
  console.log(r.line);
106
114
  return r.code;
107
115
  }
108
116
  case "close": {
109
- const { runClose } = await import("./action-NQRSZEC5.js");
117
+ const { runClose } = await import("./action-MCR4BCSE.js");
110
118
  const r = await runClose(rest);
111
119
  console.log(r.line);
112
120
  return r.code;
113
121
  }
114
- case "team": {
115
- const { runTeam } = await import("./team-MTSKNMAE.js");
116
- const r = await runTeam(rest);
117
- console.log(r.line);
118
- return r.code;
119
- }
120
122
  case "tui": {
121
- const { runTui } = await import("./tui-4IRYQXYU.js");
123
+ const { runTui } = await import("./tui-AIXCETOB.js");
122
124
  return runTui(rest);
123
125
  }
126
+ case "brain": {
127
+ const { runBrain } = await import("./brain-BJTBN2ZK.js");
128
+ const r = await runBrain(rest);
129
+ console.log(r.line);
130
+ return r.code;
131
+ }
124
132
  case "bridge": {
125
- const { bridge } = await import("./bridge-XCTXVWKH.js");
133
+ const { bridge } = await import("./bridge-YBHXFEFX.js");
126
134
  await bridge();
127
135
  return 0;
128
136
  }
129
137
  case "claude":
130
138
  case "codex":
131
139
  case "cursor": {
132
- const { launch } = await import("./launch-7WOARLZM.js");
140
+ const { launch } = await import("./launch-RTBGUJSC.js");
133
141
  return launch(cmd, rest);
134
142
  }
135
143
  case "attach": {
136
- const { attach } = await import("./attach-KXOSFUHO.js");
144
+ const { attach } = await import("./attach-R7FT7LFH.js");
137
145
  return attach(rest.find((a) => !a.startsWith("--")) ?? "", { readOnly: rest.includes("--read-only") });
138
146
  }
147
+ case "task":
148
+ case "team":
149
+ console.log(removedCommand(cmd));
150
+ return 1;
139
151
  default:
140
152
  console.log(USAGE);
141
153
  return cmd ? 1 : 0;
@@ -0,0 +1,28 @@
1
+ import {
2
+ DaemonError,
3
+ attachEnv,
4
+ createSession,
5
+ ensureDaemon,
6
+ launch,
7
+ parseLaunchArgs
8
+ } from "./chunk-UMQGRTA4.js";
9
+ import "./chunk-KBSXP63H.js";
10
+ import "./chunk-G3ST5WKR.js";
11
+ import {
12
+ agentEnv,
13
+ exitFile
14
+ } from "./chunk-O4MUYFPH.js";
15
+ import "./chunk-FWLT3SAN.js";
16
+ import "./chunk-2X56ZRK7.js";
17
+ import "./chunk-W724IVDC.js";
18
+ import "./chunk-U67V476Y.js";
19
+ export {
20
+ DaemonError,
21
+ agentEnv,
22
+ attachEnv,
23
+ createSession,
24
+ ensureDaemon,
25
+ exitFile,
26
+ launch,
27
+ parseLaunchArgs
28
+ };
@@ -4,11 +4,11 @@ import {
4
4
  readOnClient,
5
5
  sendMessage,
6
6
  sendOnClient
7
- } from "./chunk-3CF4FQWG.js";
8
- import "./chunk-LG3XFIPV.js";
9
- import "./chunk-VMGACWNP.js";
10
- import "./chunk-VKRFHDRK.js";
11
- import "./chunk-HOOHS3MX.js";
7
+ } from "./chunk-VQR5ME75.js";
8
+ import "./chunk-O4MUYFPH.js";
9
+ import "./chunk-FWLT3SAN.js";
10
+ import "./chunk-2X56ZRK7.js";
11
+ import "./chunk-W724IVDC.js";
12
12
  import "./chunk-U67V476Y.js";
13
13
  export {
14
14
  parseLedgeCommand,
@@ -14,10 +14,10 @@ import {
14
14
  stopDetachedDaemon,
15
15
  systemdUnit,
16
16
  writeBin
17
- } from "./chunk-K7UEKSB2.js";
18
- import "./chunk-NLL2EQJ2.js";
19
- import "./chunk-6IYPRUQG.js";
20
- import "./chunk-HOOHS3MX.js";
17
+ } from "./chunk-WYRQZA4G.js";
18
+ import "./chunk-KBSXP63H.js";
19
+ import "./chunk-G3ST5WKR.js";
20
+ import "./chunk-W724IVDC.js";
21
21
  import "./chunk-U67V476Y.js";
22
22
  export {
23
23
  BIN_MARK,
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  connect
3
- } from "./chunk-VMGACWNP.js";
4
- import "./chunk-VKRFHDRK.js";
5
- import "./chunk-HOOHS3MX.js";
3
+ } from "./chunk-FWLT3SAN.js";
4
+ import "./chunk-2X56ZRK7.js";
5
+ import "./chunk-W724IVDC.js";
6
6
  import "./chunk-U67V476Y.js";
7
7
 
8
8
  // src/cli/status.ts