@damn-dev/cli 0.14.0 → 0.15.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.
@@ -5579,6 +5579,31 @@ var require_openclawBindings = __commonJS({
5579
5579
  }
5580
5580
  });
5581
5581
 
5582
+ // apps/backend/dist/lib/organigramUpdate.js
5583
+ var require_organigramUpdate = __commonJS({
5584
+ "apps/backend/dist/lib/organigramUpdate.js"(exports2) {
5585
+ "use strict";
5586
+ Object.defineProperty(exports2, "__esModule", { value: true });
5587
+ exports2.OrganigramUpdateSchema = void 0;
5588
+ var zod_12 = require("zod");
5589
+ var ReparentOpSchema = zod_12.z.object({
5590
+ op: zod_12.z.literal("reparent"),
5591
+ child: zod_12.z.string().min(1),
5592
+ // null = make child a root (reports to no one)
5593
+ parent: zod_12.z.string().min(1).nullable()
5594
+ });
5595
+ var SetRoleOpSchema = zod_12.z.object({
5596
+ op: zod_12.z.literal("set-role"),
5597
+ agentId: zod_12.z.string().min(1),
5598
+ role: zod_12.z.string().min(1).max(140)
5599
+ });
5600
+ exports2.OrganigramUpdateSchema = zod_12.z.object({
5601
+ operations: zod_12.z.array(zod_12.z.union([ReparentOpSchema, SetRoleOpSchema])).min(1),
5602
+ reason: zod_12.z.string().max(280).optional()
5603
+ });
5604
+ }
5605
+ });
5606
+
5582
5607
  // apps/backend/dist/lib/delegationSecurity.js
5583
5608
  var require_delegationSecurity = __commonJS({
5584
5609
  "apps/backend/dist/lib/delegationSecurity.js"(exports2) {
@@ -7742,7 +7767,8 @@ var require_approvalRules = __commonJS({
7742
7767
  "git_pr",
7743
7768
  "cron_config",
7744
7769
  "workspace_guide",
7745
- "channel_binding"
7770
+ "channel_binding",
7771
+ "organigram_update"
7746
7772
  ]);
7747
7773
  function derivePattern(type, payloadRaw) {
7748
7774
  if (exports2.BLOCKED_TYPES.has(type))
@@ -7785,6 +7811,10 @@ var require_approvalRules = __commonJS({
7785
7811
  const bindingAgentId = typeof payload.bindingAgentId === "string" ? payload.bindingAgentId : "*";
7786
7812
  return { pattern: `binding:${plugin}:${bindingAgentId}`, ruleType: "channel_binding" };
7787
7813
  }
7814
+ case "organigram_update": {
7815
+ const op = typeof payload.op === "string" ? payload.op : "*";
7816
+ return { pattern: `organigram:${op}`, ruleType: "organigram_update" };
7817
+ }
7788
7818
  default:
7789
7819
  return null;
7790
7820
  }
@@ -9686,6 +9716,340 @@ ${conflictDiffs}`,
9686
9716
  }
9687
9717
  });
9688
9718
 
9719
+ // apps/backend/dist/lib/organigramParse.js
9720
+ var require_organigramParse = __commonJS({
9721
+ "apps/backend/dist/lib/organigramParse.js"(exports2) {
9722
+ "use strict";
9723
+ Object.defineProperty(exports2, "__esModule", { value: true });
9724
+ exports2.parseOrganigram = parseOrganigram;
9725
+ var SECTION_TITLE_RE = /^(reporting|org\s*chart|structure|reporting\s+structure|hierarchy)\b/i;
9726
+ function extractSection(md, title) {
9727
+ const lines = md.split("\n");
9728
+ let start = -1;
9729
+ let end = lines.length;
9730
+ for (let i = 0; i < lines.length; i++) {
9731
+ const m = /^##\s+(.+?)\s*$/.exec(lines[i]);
9732
+ if (!m)
9733
+ continue;
9734
+ if (start === -1 && title.test(m[1])) {
9735
+ start = i + 1;
9736
+ continue;
9737
+ }
9738
+ if (start !== -1) {
9739
+ end = i;
9740
+ break;
9741
+ }
9742
+ }
9743
+ if (start === -1)
9744
+ return null;
9745
+ return lines.slice(start, end).join("\n");
9746
+ }
9747
+ function extractRef(rawLine) {
9748
+ let s = rawLine.replace(/[│├└─]/g, " ").replace(/^[\s\-*•◦]+/, "").replace(/[\[\]]/g, "").trim();
9749
+ if (!s)
9750
+ return null;
9751
+ const asciiStart = s.search(/[A-Za-z]/);
9752
+ if (asciiStart > 0)
9753
+ s = s.slice(asciiStart).trim();
9754
+ const idMatch = /\(([A-Za-z0-9][A-Za-z0-9_-]*)\)\s*$/.exec(s);
9755
+ const idHint = idMatch ? idMatch[1].toLowerCase() : null;
9756
+ const display = (idMatch ? s.slice(0, idMatch.index) : s).trim();
9757
+ if (!display && !idHint)
9758
+ return null;
9759
+ return { display, idHint };
9760
+ }
9761
+ function resolveAgent(ref, agents) {
9762
+ if (!ref)
9763
+ return null;
9764
+ if (ref.idHint) {
9765
+ const byId = agents.find((a) => a.id.toLowerCase() === ref.idHint);
9766
+ if (byId)
9767
+ return byId.id;
9768
+ }
9769
+ if (/^(human|user|owner|you|me)$/i.test(ref.display))
9770
+ return null;
9771
+ const norm = ref.display.toLowerCase().replace(/\s+/g, " ").trim();
9772
+ if (!norm)
9773
+ return null;
9774
+ const byName = agents.find((a) => a.name.toLowerCase() === norm);
9775
+ if (byName)
9776
+ return byName.id;
9777
+ const byPrefix = agents.find((a) => a.name.toLowerCase().startsWith(norm) || norm.startsWith(a.name.toLowerCase()));
9778
+ return byPrefix?.id ?? null;
9779
+ }
9780
+ function indentLevel(line) {
9781
+ const m = /^([\s│├└─]*)/.exec(line);
9782
+ if (!m)
9783
+ return 0;
9784
+ return Math.floor(m[1].length / 2);
9785
+ }
9786
+ function buildFlatFallback(agents) {
9787
+ const coo = agents.find((a) => a.id === "coo" || a.name.toLowerCase() === "coo");
9788
+ if (!coo)
9789
+ return [];
9790
+ const out = [{ parent: null, child: coo.id }];
9791
+ for (const a of agents) {
9792
+ if (a.id === coo.id)
9793
+ continue;
9794
+ out.push({ parent: coo.id, child: a.id });
9795
+ }
9796
+ return out;
9797
+ }
9798
+ function parseOrganigram(md, agents) {
9799
+ if (agents.length === 0)
9800
+ return [];
9801
+ const section = md ? extractSection(md, SECTION_TITLE_RE) : null;
9802
+ if (!section)
9803
+ return buildFlatFallback(agents);
9804
+ const lines = section.split("\n").filter((l) => l.trim().length > 0);
9805
+ if (lines.length === 0)
9806
+ return buildFlatFallback(agents);
9807
+ const edges = [];
9808
+ const stack = [];
9809
+ const seenChildren = /* @__PURE__ */ new Set();
9810
+ for (const raw of lines) {
9811
+ const level = indentLevel(raw);
9812
+ const ref = extractRef(raw);
9813
+ if (!ref)
9814
+ continue;
9815
+ const id = resolveAgent(ref, agents);
9816
+ const isHumanRoot = /^(human|user|owner|you|me)$/i.test(ref.display) && !ref.idHint;
9817
+ if (!id && !isHumanRoot)
9818
+ continue;
9819
+ while (stack.length > 0 && stack[stack.length - 1].level >= level)
9820
+ stack.pop();
9821
+ const parentNode = stack[stack.length - 1] ?? null;
9822
+ const parent = parentNode?.id ?? null;
9823
+ if (id) {
9824
+ if (!seenChildren.has(id)) {
9825
+ edges.push({ parent, child: id });
9826
+ seenChildren.add(id);
9827
+ }
9828
+ stack.push({ id, level });
9829
+ } else {
9830
+ stack.push({ id: null, level });
9831
+ }
9832
+ }
9833
+ if (edges.length === 0)
9834
+ return buildFlatFallback(agents);
9835
+ const flatHintMatch = /flat\s+delegation|all\s+(workspace\s+)?agents/i.test(section);
9836
+ if (flatHintMatch) {
9837
+ const rootAgentId = edges.find((e) => e.parent === null)?.child ?? (agents.find((a) => a.id === "coo")?.id ?? null);
9838
+ if (rootAgentId) {
9839
+ for (const a of agents) {
9840
+ if (seenChildren.has(a.id) || a.id === rootAgentId)
9841
+ continue;
9842
+ edges.push({ parent: rootAgentId, child: a.id });
9843
+ seenChildren.add(a.id);
9844
+ }
9845
+ }
9846
+ }
9847
+ return edges;
9848
+ }
9849
+ }
9850
+ });
9851
+
9852
+ // apps/backend/dist/lib/organigramWrite.js
9853
+ var require_organigramWrite = __commonJS({
9854
+ "apps/backend/dist/lib/organigramWrite.js"(exports2) {
9855
+ "use strict";
9856
+ Object.defineProperty(exports2, "__esModule", { value: true });
9857
+ exports2.applyOrganigramOperations = applyOrganigramOperations;
9858
+ exports2.applyReparent = applyReparent;
9859
+ var promises_12 = require("fs/promises");
9860
+ var path_12 = require("path");
9861
+ var os_12 = require("os");
9862
+ var intelligence_12 = require_intelligence();
9863
+ var organigramParse_1 = require_organigramParse();
9864
+ var db_12 = require_db();
9865
+ var ORGANIGRAM_PATH = (0, path_12.join)((0, os_12.homedir)(), ".openclaw", "agents", "coo", "ORGANIGRAM.md");
9866
+ var SECTION_TITLE_RE = /^##\s+(reporting|org\s*chart|structure|reporting\s+structure|hierarchy)\s*$/i;
9867
+ function splitOnReportingSection(md) {
9868
+ const lines = md.split("\n");
9869
+ let sectionStart = -1;
9870
+ let sectionEnd = lines.length;
9871
+ for (let i = 0; i < lines.length; i++) {
9872
+ if (sectionStart === -1) {
9873
+ if (SECTION_TITLE_RE.test(lines[i])) {
9874
+ sectionStart = i;
9875
+ continue;
9876
+ }
9877
+ } else {
9878
+ if (/^##\s+/.test(lines[i])) {
9879
+ sectionEnd = i;
9880
+ break;
9881
+ }
9882
+ }
9883
+ }
9884
+ if (sectionStart === -1) {
9885
+ return { prefix: lines, body: [], suffix: [], synthesised: true };
9886
+ }
9887
+ return {
9888
+ prefix: lines.slice(0, sectionStart),
9889
+ body: lines.slice(sectionStart + 1, sectionEnd),
9890
+ suffix: lines.slice(sectionEnd),
9891
+ synthesised: false
9892
+ };
9893
+ }
9894
+ function applyReparentOps(current, ops) {
9895
+ const byChild = /* @__PURE__ */ new Map();
9896
+ for (const e of current)
9897
+ byChild.set(e.child, e.parent);
9898
+ const rejected = [];
9899
+ for (const op of ops) {
9900
+ if (op.child === op.parent) {
9901
+ rejected.push(op);
9902
+ continue;
9903
+ }
9904
+ if (op.parent !== null) {
9905
+ let cur = op.parent;
9906
+ const seen = /* @__PURE__ */ new Set();
9907
+ let cycle = false;
9908
+ while (cur !== null && cur !== void 0 && !seen.has(cur)) {
9909
+ if (cur === op.child) {
9910
+ cycle = true;
9911
+ break;
9912
+ }
9913
+ seen.add(cur);
9914
+ cur = byChild.get(cur) ?? null;
9915
+ }
9916
+ if (cycle) {
9917
+ rejected.push(op);
9918
+ continue;
9919
+ }
9920
+ }
9921
+ byChild.set(op.child, op.parent);
9922
+ }
9923
+ return {
9924
+ edges: Array.from(byChild.entries()).map(([child, parent]) => ({ parent, child })),
9925
+ rejected
9926
+ };
9927
+ }
9928
+ function renderReportingSection(edges, agents) {
9929
+ const childrenOf = /* @__PURE__ */ new Map();
9930
+ for (const e of edges) {
9931
+ if (e.parent === null)
9932
+ continue;
9933
+ const arr = childrenOf.get(e.parent) ?? [];
9934
+ arr.push(e.child);
9935
+ childrenOf.set(e.parent, arr);
9936
+ }
9937
+ const nameById = new Map(agents.map((a) => [a.id, a.name]));
9938
+ for (const arr of childrenOf.values()) {
9939
+ arr.sort((a, b) => (nameById.get(a) ?? a).localeCompare(nameById.get(b) ?? b));
9940
+ }
9941
+ const roots = edges.filter((e) => e.parent === null).map((e) => e.child);
9942
+ roots.sort((a, b) => (nameById.get(a) ?? a).localeCompare(nameById.get(b) ?? b));
9943
+ const out = [];
9944
+ function emit(id, depth) {
9945
+ const indent = " ".repeat(depth);
9946
+ const a = agents.find((x) => x.id === id);
9947
+ const label = a ? `${a.emoji} ${a.name} (${a.id})` : id;
9948
+ out.push(`${indent}- ${label}`);
9949
+ const kids = childrenOf.get(id) ?? [];
9950
+ for (const k of kids)
9951
+ emit(k, depth + 1);
9952
+ }
9953
+ for (const r of roots)
9954
+ emit(r, 0);
9955
+ return out;
9956
+ }
9957
+ function reassemble(split, renderedBody) {
9958
+ if (split.synthesised) {
9959
+ const out2 = [...split.prefix];
9960
+ if (out2.length > 0 && out2[out2.length - 1].trim() !== "")
9961
+ out2.push("");
9962
+ out2.push("## Reporting Structure", ...renderedBody);
9963
+ return out2.join("\n");
9964
+ }
9965
+ const out = [...split.prefix, "## Reporting Structure", ...renderedBody, ...split.suffix];
9966
+ return out.join("\n");
9967
+ }
9968
+ async function archivePrevious() {
9969
+ try {
9970
+ const prev = await (0, promises_12.readFile)(ORGANIGRAM_PATH, "utf-8");
9971
+ if (prev.trim().length === 0)
9972
+ return;
9973
+ await (0, intelligence_12.atomicWrite)((0, path_12.join)((0, path_12.dirname)(ORGANIGRAM_PATH), "ORGANIGRAM.prev.md"), prev);
9974
+ } catch {
9975
+ }
9976
+ }
9977
+ async function applyOrganigramOperations(ops, workspaceId) {
9978
+ if (ops.length === 0)
9979
+ return { ok: false, error: "no operations" };
9980
+ const agents = await db_12.db.agent.findMany({
9981
+ where: { workspaceId },
9982
+ select: { id: true, name: true, emoji: true }
9983
+ });
9984
+ const agentRefs = agents.map((a) => ({ id: a.id, name: a.name, emoji: a.emoji }));
9985
+ const agentIds = new Set(agents.map((a) => a.id));
9986
+ for (const op of ops) {
9987
+ if (op.op === "reparent") {
9988
+ if (!agentIds.has(op.child))
9989
+ return { ok: false, error: `unknown agent: ${op.child}` };
9990
+ if (op.parent !== null && !agentIds.has(op.parent))
9991
+ return { ok: false, error: `unknown agent: ${op.parent}` };
9992
+ } else if (op.op === "set-role") {
9993
+ if (!agentIds.has(op.agentId))
9994
+ return { ok: false, error: `unknown agent: ${op.agentId}` };
9995
+ }
9996
+ }
9997
+ let md = "";
9998
+ try {
9999
+ md = await (0, promises_12.readFile)(ORGANIGRAM_PATH, "utf-8");
10000
+ } catch {
10001
+ md = "";
10002
+ }
10003
+ const split = splitOnReportingSection(md);
10004
+ const currentEdges = (0, organigramParse_1.parseOrganigram)(md, agentRefs);
10005
+ const reparentOps = ops.filter((o) => o.op === "reparent");
10006
+ const { edges: mutatedEdges, rejected: rejectedReparent } = applyReparentOps(currentEdges, reparentOps);
10007
+ let prefixLines = [...split.prefix];
10008
+ const setRoleRejected = [];
10009
+ for (const op of ops) {
10010
+ if (op.op !== "set-role")
10011
+ continue;
10012
+ const agent = agents.find((a) => a.id === op.agentId);
10013
+ if (!agent) {
10014
+ setRoleRejected.push(op);
10015
+ continue;
10016
+ }
10017
+ const heading = new RegExp(`^####\\s+.*\\(${op.agentId}\\)\\s*$`);
10018
+ let i = prefixLines.findIndex((l) => heading.test(l));
10019
+ if (i < 0) {
10020
+ setRoleRejected.push(op);
10021
+ continue;
10022
+ }
10023
+ let replaced = false;
10024
+ for (let j = i + 1; j < Math.min(i + 7, prefixLines.length); j++) {
10025
+ if (/^Role:\s*/i.test(prefixLines[j])) {
10026
+ prefixLines[j] = `Role: ${op.role}`;
10027
+ replaced = true;
10028
+ break;
10029
+ }
10030
+ if (/^####?\s+/.test(prefixLines[j]) || /^##\s+/.test(prefixLines[j]))
10031
+ break;
10032
+ }
10033
+ if (!replaced)
10034
+ prefixLines.splice(i + 1, 0, `Role: ${op.role}`);
10035
+ }
10036
+ const renderedBody = renderReportingSection(mutatedEdges, agentRefs);
10037
+ const newMd = reassemble({ ...split, prefix: prefixLines }, renderedBody);
10038
+ await archivePrevious();
10039
+ await (0, intelligence_12.atomicWrite)(ORGANIGRAM_PATH, newMd);
10040
+ return { ok: true, content: newMd, rejected: [...rejectedReparent, ...setRoleRejected] };
10041
+ }
10042
+ async function applyReparent(child, parent, workspaceId) {
10043
+ const r = await applyOrganigramOperations([{ op: "reparent", child, parent }], workspaceId);
10044
+ if (!r.ok)
10045
+ return r;
10046
+ if (r.rejected.length > 0)
10047
+ return { ok: false, error: "cycle would be created \u2014 reparent refused" };
10048
+ return { ok: true, content: r.content };
10049
+ }
10050
+ }
10051
+ });
10052
+
9689
10053
  // apps/backend/dist/routers/approvals.js
9690
10054
  var require_approvals = __commonJS({
9691
10055
  "apps/backend/dist/routers/approvals.js"(exports2) {
@@ -10065,6 +10429,32 @@ var require_approvals = __commonJS({
10065
10429
  console.error("[channel_binding approval] apply error:", err);
10066
10430
  }
10067
10431
  })();
10432
+ } else if (decision === "approved" && message.approval?.type === "organigram_update" && message.approval.payload) {
10433
+ const orgPayload = JSON.parse(message.approval.payload);
10434
+ void (async () => {
10435
+ try {
10436
+ const { applyOrganigramOperations } = await Promise.resolve().then(() => __importStar2(require_organigramWrite()));
10437
+ const result = await applyOrganigramOperations(orgPayload.operations, workspace.id);
10438
+ const ok = result.ok;
10439
+ const summaryLine = ok ? `Organigram updated (${orgPayload.operations.length} op${orgPayload.operations.length === 1 ? "" : "s"}).${result.rejected.length > 0 ? ` ${result.rejected.length} rejected (cycle).` : ""}` : `Organigram update failed: ${result.error}`;
10440
+ const sysMsg = await db_12.db.message.create({
10441
+ data: {
10442
+ channelId: message.channelId,
10443
+ senderType: "system",
10444
+ senderId: "system",
10445
+ senderName: "System",
10446
+ senderColor: null,
10447
+ content: summaryLine
10448
+ },
10449
+ include: messages_12.messageInclude
10450
+ });
10451
+ (0, ws_12.broadcastToChannel)(message.channelId, { type: "message.new", payload: (0, messages_12.toMessage)(sysMsg) });
10452
+ (0, ws_12.broadcastToWorkspace)(workspace.id, { type: "delegationGraph.changed", payload: { workspaceId: workspace.id } });
10453
+ await (0, triggerAgent_12.triggerAgentDm)(agentId, message.channelId, ok ? `[Approval granted] ${summaryLine}` : `[Approval granted but apply failed] ${result.error}`, workspace.id);
10454
+ } catch (err) {
10455
+ console.error("[organigram_update approval] apply error:", err);
10456
+ }
10457
+ })();
10068
10458
  } else if (decision === "approved" && message.approval?.type !== "shell_exec") {
10069
10459
  await (0, triggerAgent_12.triggerAgentDm)(agentId, message.channelId, "[Approval granted] Your pending action has been approved. Proceed.", workspace.id);
10070
10460
  } else if (decision === "rejected") {
@@ -13433,7 +13823,7 @@ var require_governance = __commonJS({
13433
13823
  const approvalsCreated = [];
13434
13824
  const delegationsInitiated = [];
13435
13825
  let memoryWritten = false;
13436
- const { extractMemoryUpdate, extractContextUpdate, extractSoulUpdate, extractHeartbeatUpdate, extractIdentityUpdate, extractSkillInstallProposal, extractSkillWriteProposal, extractCronWriteProposal, extractWorkspaceGuideUpdate, extractChannelBinding, extractDelegationRules, extractTaskInput, extractGitCommit, extractGitPR, extractGitMerge, extractDelegateBlock, extractDelegateChain, extractDelegateParallel, extractChannelPost, extractChannelUpdate, extractSkillToolCall, applyContextUpdate, applyHeartbeatUpdate, createFileEditApproval, executeSkillToolCall } = await Promise.resolve().then(() => __importStar2(require_triggerAgent()));
13826
+ const { extractMemoryUpdate, extractContextUpdate, extractSoulUpdate, extractHeartbeatUpdate, extractIdentityUpdate, extractSkillInstallProposal, extractSkillWriteProposal, extractCronWriteProposal, extractWorkspaceGuideUpdate, extractChannelBinding, extractDelegationRules, extractOrganigramUpdate, extractTaskInput, extractGitCommit, extractGitPR, extractGitMerge, extractDelegateBlock, extractDelegateChain, extractDelegateParallel, extractChannelPost, extractChannelUpdate, extractSkillToolCall, applyContextUpdate, applyHeartbeatUpdate, createFileEditApproval, executeSkillToolCall } = await Promise.resolve().then(() => __importStar2(require_triggerAgent()));
13437
13827
  const { content: afterMemory, memoryAppend } = extractMemoryUpdate(responseText);
13438
13828
  if (memoryAppend) {
13439
13829
  blocksFound.push("memory-update");
@@ -13533,7 +13923,20 @@ var require_governance = __commonJS({
13533
13923
  });
13534
13924
  }
13535
13925
  }
13536
- const { content: afterTaskInput, taskInput } = extractTaskInput(afterDelegationRules);
13926
+ const { content: afterOrganigramUpdate, organigramUpdate } = extractOrganigramUpdate(afterDelegationRules);
13927
+ if (organigramUpdate) {
13928
+ blocksFound.push("organigram-update");
13929
+ void createOrganigramUpdateApproval({
13930
+ agentId,
13931
+ channelId,
13932
+ workspaceId,
13933
+ update: organigramUpdate,
13934
+ agentName: agentName ?? agentId,
13935
+ agentColor: agentColor ?? null,
13936
+ externalSource
13937
+ });
13938
+ }
13939
+ const { content: afterTaskInput, taskInput } = extractTaskInput(afterOrganigramUpdate);
13537
13940
  if (taskInput) {
13538
13941
  blocksFound.push("task-input");
13539
13942
  const { handleTaskInput } = await Promise.resolve().then(() => __importStar2(require_delegation()));
@@ -14046,6 +14449,86 @@ _${binding.reason}_`,
14046
14449
  console.error("[channel-binding] approval creation failed:", err);
14047
14450
  }
14048
14451
  }
14452
+ async function createOrganigramUpdateApproval(opts) {
14453
+ const { agentId, channelId, workspaceId, update, agentName, agentColor, externalSource } = opts;
14454
+ try {
14455
+ const { toMessage, messageInclude } = await Promise.resolve().then(() => __importStar2(require_messages()));
14456
+ const { broadcastToChannel } = await Promise.resolve().then(() => __importStar2(require_ws()));
14457
+ const { getActiveModel } = await Promise.resolve().then(() => __importStar2(require_triggerAgent()));
14458
+ const { maybeAutoApprove } = await Promise.resolve().then(() => __importStar2(require_approvals()));
14459
+ const agent = await db_12.db.agent.findUnique({ where: { id: agentId }, select: { defaultModel: true } });
14460
+ const opCounts = {};
14461
+ for (const op of update.operations)
14462
+ opCounts[op.op] = (opCounts[op.op] ?? 0) + 1;
14463
+ const summary = `Organigram update: ${Object.entries(opCounts).map(([op, n]) => `${n}\xD7 ${op}`).join(", ")}`;
14464
+ const approvalMsg = await db_12.db.message.create({
14465
+ data: {
14466
+ channelId,
14467
+ senderType: "agent",
14468
+ senderId: agentId,
14469
+ senderName: agentName,
14470
+ senderColor: agentColor,
14471
+ content: `${summary}${update.reason ? `
14472
+
14473
+ _${update.reason}_` : ""}`,
14474
+ status: "pending_approval",
14475
+ modelUsed: await getActiveModel(agentId, agent?.defaultModel ?? "unknown"),
14476
+ metadata: JSON.stringify({ organigramUpdate: update })
14477
+ },
14478
+ include: messageInclude
14479
+ });
14480
+ const firstOp = update.operations[0];
14481
+ const approvalPayload = {
14482
+ operations: update.operations,
14483
+ reason: update.reason ?? "",
14484
+ // surface a stable pattern for derivePattern
14485
+ op: firstOp.op
14486
+ };
14487
+ await db_12.db.approval.create({
14488
+ data: {
14489
+ messageId: approvalMsg.id,
14490
+ type: "organigram_update",
14491
+ payload: JSON.stringify(approvalPayload)
14492
+ }
14493
+ });
14494
+ const msgForBroadcast = await db_12.db.message.findUnique({ where: { id: approvalMsg.id }, include: messageInclude });
14495
+ broadcastToChannel(channelId, { type: "message.new", payload: toMessage(msgForBroadcast) });
14496
+ const autoApproved = await maybeAutoApprove({
14497
+ messageId: approvalMsg.id,
14498
+ agentId,
14499
+ workspaceId,
14500
+ approvalType: "organigram_update",
14501
+ payload: approvalPayload
14502
+ });
14503
+ if (autoApproved)
14504
+ return;
14505
+ if (externalSource) {
14506
+ const { sendTelegramApprovalNotification } = await Promise.resolve().then(() => __importStar2(require_telegramBridge()));
14507
+ void sendTelegramApprovalNotification({
14508
+ agentId,
14509
+ chatId: externalSource.externalChatId,
14510
+ approvalId: approvalMsg.id,
14511
+ messageId: approvalMsg.id,
14512
+ approvalType: "organigram_update",
14513
+ description: summary
14514
+ });
14515
+ }
14516
+ const admins = await db_12.db.workspaceMember.findMany({
14517
+ where: { workspaceId, role: { in: ["owner", "admin"] } },
14518
+ select: { userId: true }
14519
+ });
14520
+ const { sendPush } = await Promise.resolve().then(() => __importStar2(require_pushNotifications()));
14521
+ for (const a of admins) {
14522
+ void sendPush(a.userId, {
14523
+ title: "Action requires approval",
14524
+ body: summary.slice(0, 100),
14525
+ data: { type: "approval", approvalId: approvalMsg.id, channelId }
14526
+ });
14527
+ }
14528
+ } catch (err) {
14529
+ console.error("[organigram-update] approval creation failed:", err);
14530
+ }
14531
+ }
14049
14532
  async function createDelegationRuleApproval(opts) {
14050
14533
  const { agentId, channelId, workspaceId, rule, agentName, agentColor, externalSource } = opts;
14051
14534
  try {
@@ -14196,6 +14679,7 @@ var require_triggerAgent = __commonJS({
14196
14679
  exports2.extractCronWriteProposal = extractCronWriteProposal;
14197
14680
  exports2.extractWorkspaceGuideUpdate = extractWorkspaceGuideUpdate;
14198
14681
  exports2.extractChannelBinding = extractChannelBinding;
14682
+ exports2.extractOrganigramUpdate = extractOrganigramUpdate;
14199
14683
  exports2.applyMemoryUpdate = applyMemoryUpdate;
14200
14684
  exports2.applyContextUpdate = applyContextUpdate;
14201
14685
  exports2.extractHeartbeatUpdate = extractHeartbeatUpdate;
@@ -14233,6 +14717,7 @@ var require_triggerAgent = __commonJS({
14233
14717
  var cronStore_1 = require_cronStore();
14234
14718
  var workspaceGuide_1 = require_workspaceGuide();
14235
14719
  var openclawBindings_1 = require_openclawBindings();
14720
+ var organigramUpdate_1 = require_organigramUpdate();
14236
14721
  var execFileAsync2 = (0, util_12.promisify)(child_process_12.execFile);
14237
14722
  function isFenceLine(line) {
14238
14723
  if (line.startsWith("````"))
@@ -14379,6 +14864,7 @@ ${newBlock}`;
14379
14864
  var CRON_WRITE_RE = /````cron-write\n([\s\S]*?)````|```cron-write\n([\s\S]*?)```/;
14380
14865
  var WORKSPACE_GUIDE_UPDATE_RE = /````workspace-guide-update\n([\s\S]*?)````|```workspace-guide-update\n([\s\S]*?)```/;
14381
14866
  var CHANNEL_BINDING_RE = /````channel-binding\n([\s\S]*?)````|```channel-binding\n([\s\S]*?)```/;
14867
+ var ORGANIGRAM_UPDATE_RE = /````organigram-update\n([\s\S]*?)````|```organigram-update\n([\s\S]*?)```/;
14382
14868
  function extractGitPR(raw) {
14383
14869
  const match = raw.match(GIT_PR_RE);
14384
14870
  if (!match)
@@ -15031,6 +15517,10 @@ ${sectionHeader}`);
15031
15517
  const { content, proposal } = extractJsonBlock(raw, CHANNEL_BINDING_RE, openclawBindings_1.ChannelBindingSchema, "channel-binding");
15032
15518
  return { content, channelBinding: proposal };
15033
15519
  }
15520
+ function extractOrganigramUpdate(raw) {
15521
+ const { content, proposal } = extractJsonBlock(raw, ORGANIGRAM_UPDATE_RE, organigramUpdate_1.OrganigramUpdateSchema, "organigram-update");
15522
+ return { content, organigramUpdate: proposal };
15523
+ }
15034
15524
  async function applyMemoryUpdate(agentId, memoryAppend, source = "gateway") {
15035
15525
  const memoryDir = path_12.default.join(OPENCLAW_AGENTS_DIR, agentId, "memory");
15036
15526
  try {
@@ -15462,6 +15952,31 @@ Pattern syntax: \`shell:<argv0>\` (or \`shell:<argv0> <subcommand>\`), \`delegat
15462
15952
  You may emit MULTIPLE \`delegation-rule\` blocks in a single message (one per pattern). Each becomes its own approval card.
15463
15953
 
15464
15954
  Each rule requires human approval before it takes effect. Never propose delegation rules for: delete, publish, send, transfer, post (public write), or any destructive action without explicit user instruction. Never propose auto-approve for items in \`BLOCKED_TYPES\` (file_edit, skill_install, trust_config, git_merge, delegation_rule itself) \u2014 those are principled "always human-in-the-loop" types.`;
15955
+ var ORGANIGRAM_UPDATE_PATCH = `
15956
+
15957
+ ## Organigram Update Protocol
15958
+ You can mutate the workspace organigram (reporting structure + role labels) via the structured \`organigram-update\` block. Prefer this over rewriting Organigram.md prose directly \u2014 the structured block is parser-stable, atomic, and the canvas reflects changes within ~1s.
15959
+
15960
+ **Cardinal rule:** the fenced \`\`\`organigram-update\`\`\` block IS the proposal. Narrating "I've moved Birdie under Marketing" / "Reporting updated" without the fenced block does nothing.
15961
+
15962
+ Operations:
15963
+ - \`reparent\` \u2014 change who an agent reports to. \`parent\` is an agent slug, OR \`null\` to make the child a root (reports to no one).
15964
+ - \`set-role\` \u2014 change an agent's role label in the Agent Directory section.
15965
+
15966
+ Example: move Birdie under Marketing Lead and update Claw's role:
15967
+ \`\`\`organigram-update
15968
+ {
15969
+ "operations": [
15970
+ {"op": "reparent", "child": "birdie", "parent": "marketing-lead"},
15971
+ {"op": "set-role", "agentId": "claw", "role": "Brand Voice Lead"}
15972
+ ],
15973
+ "reason": "Birdie now reports to the new Marketing Lead; Claw's title shortened for clarity on the canvas."
15974
+ }
15975
+ \`\`\`
15976
+
15977
+ Cycle protection is enforced server-side \u2014 proposing a reparent that would create a loop (X under one of X's descendants) is rejected at apply time; safe to attempt. The COO retains full freedom to edit Organigram.md prose (Agent Directory descriptions, narrative sections) via the workspace dialog or the existing setOrganigram flow; only structural mutations should flow through this block.
15978
+
15979
+ \`organigram-update\` is auto-approvable by default (low risk \u2014 metadata only). The owner can gate it via the rule pattern \`organigram:reparent\` / \`organigram:set-role\` / \`organigram:*\`.`;
15465
15980
  var OPS_PROTOCOL_PATCH = `
15466
15981
 
15467
15982
  ## Workspace Operations Protocol
@@ -15654,9 +16169,11 @@ You have three coordination mechanisms:
15654
16169
  out = patchSection(out, "## Channel Update Protocol", CHANNEL_UPDATE_PATCH.trim());
15655
16170
  out = patchSection(out, "## Delegation Rule Protocol", DELEGATION_RULE_PATCH.trim());
15656
16171
  out = patchSection(out, "## Workspace Operations Protocol", OPS_PROTOCOL_PATCH.trim());
16172
+ out = patchSection(out, "## Organigram Update Protocol", ORGANIGRAM_UPDATE_PATCH.trim());
15657
16173
  } else {
15658
16174
  out = out.replace(/\n*## Delegation Rule Protocol\n[\s\S]*?(?=\n## |\n# |$)/, "");
15659
16175
  out = out.replace(/\n*## Workspace Operations Protocol\n[\s\S]*?(?=\n## |\n# |$)/, "");
16176
+ out = out.replace(/\n*## Organigram Update Protocol\n[\s\S]*?(?=\n## |\n# |$)/, "");
15660
16177
  }
15661
16178
  if (flags.heartbeatChecklist) {
15662
16179
  const block = `## Heartbeat Checklist
@@ -20538,6 +21055,7 @@ var require_onboarding = __commonJS({
20538
21055
  var modelId_1 = require_modelId();
20539
21056
  var skills_12 = require_skills();
20540
21057
  var gateways_12 = require_gateways();
21058
+ var ws_12 = require_ws();
20541
21059
  var triggerAgent_2 = require_triggerAgent();
20542
21060
  var execAsync = (0, util_12.promisify)(child_process_12.exec);
20543
21061
  var OPENROUTER_URL = "https://openrouter.ai/api/v1";
@@ -21436,7 +21954,7 @@ Or just tell me what you're working on \u2014 I'll figure out the rest.`
21436
21954
  return "";
21437
21955
  }
21438
21956
  }),
21439
- setOrganigram: trpc_12.protectedProcedure.input(zod_12.z.object({ content: zod_12.z.string() })).mutation(async ({ input }) => {
21957
+ setOrganigram: trpc_12.protectedProcedure.input(zod_12.z.object({ content: zod_12.z.string() })).mutation(async ({ ctx, input }) => {
21440
21958
  await promises_12.default.mkdir(path_12.default.dirname(ORGANIGRAM_PATH), { recursive: true });
21441
21959
  try {
21442
21960
  const prev = await promises_12.default.readFile(ORGANIGRAM_PATH, "utf-8");
@@ -21447,6 +21965,7 @@ Or just tell me what you're working on \u2014 I'll figure out the rest.`
21447
21965
  }
21448
21966
  await atomicWrite(ORGANIGRAM_PATH, input.content);
21449
21967
  reloadCoo();
21968
+ (0, ws_12.broadcastToWorkspace)(ctx.workspaceId, { type: "delegationGraph.changed", payload: { workspaceId: ctx.workspaceId } });
21450
21969
  return { ok: true };
21451
21970
  }),
21452
21971
  hasGoogleAuth: trpc_12.publicProcedure.query(() => {
@@ -22093,6 +22612,29 @@ You may emit MULTIPLE \`delegation-rule\` blocks in a single message (one per pa
22093
22612
 
22094
22613
  The human approves each rule before it takes effect. Never propose delegation rules for: delete, publish, send, transfer, post (public write), or any destructive action without explicit user instruction. Never propose auto-approve for items in \`BLOCKED_TYPES\` (file_edit, skill_install, trust_config, git_merge, delegation_rule itself) \u2014 those are principled "always human-in-the-loop" types.
22095
22614
 
22615
+ ## Organigram Update Protocol
22616
+
22617
+ You can mutate the workspace organigram (reporting structure + role labels) via the structured \`organigram-update\` block. Prefer this over rewriting Organigram.md prose directly \u2014 the structured block is parser-stable, atomic, and the canvas reflects changes within ~1s.
22618
+
22619
+ **Cardinal rule:** the fenced \`\`\`organigram-update\`\`\` block IS the proposal. Narrating "I've moved Birdie under Marketing" / "Reporting updated" without the fenced block does nothing.
22620
+
22621
+ Operations:
22622
+ - \`reparent\` \u2014 change who an agent reports to. \`parent\` is an agent slug, OR \`null\` to make the child a root (reports to no one).
22623
+ - \`set-role\` \u2014 change an agent's role label in the Agent Directory section.
22624
+
22625
+ Example: move Birdie under Marketing Lead and update Claw's role:
22626
+ \`\`\`organigram-update
22627
+ {
22628
+ "operations": [
22629
+ {"op": "reparent", "child": "birdie", "parent": "marketing-lead"},
22630
+ {"op": "set-role", "agentId": "claw", "role": "Brand Voice Lead"}
22631
+ ],
22632
+ "reason": "Birdie now reports to the new Marketing Lead; Claw's title shortened for clarity on the canvas."
22633
+ }
22634
+ \`\`\`
22635
+
22636
+ Cycle protection is enforced server-side \u2014 proposing a reparent that would create a loop is rejected at apply time; safe to attempt. The COO retains full freedom to edit Organigram.md prose (Agent Directory descriptions, narrative sections) via the workspace dialog or the existing setOrganigram flow; only structural mutations should flow through this block. \`organigram-update\` is auto-approvable by default (low risk \u2014 metadata only); the owner can gate it via the rule pattern \`organigram:reparent\` / \`organigram:set-role\` / \`organigram:*\`.
22637
+
22096
22638
  ## Federation (Cross-Instance Delegation)
22097
22639
 
22098
22640
  This workspace may have federated nodes \u2014 separate damn.dev instances running in isolated Docker containers. They appear in the "Federation Nodes" section of your runtime context below (live state, refreshed every turn). Each node has a current \`delegationPolicy\` that governs who on THIS hub may dispatch tasks INTO it:
@@ -23165,6 +23707,9 @@ ${historyLines.join("\n\n")}
23165
23707
  } catch {
23166
23708
  }
23167
23709
  }
23710
+ const { content: afterOrganigramUpdate, organigramUpdate } = (0, triggerAgent_12.extractOrganigramUpdate)(cleanContent);
23711
+ if (organigramUpdate)
23712
+ cleanContent = afterOrganigramUpdate;
23168
23713
  const { content: afterMissionPlan, missionPlan } = extractMissionPlan(cleanContent);
23169
23714
  if (missionPlan)
23170
23715
  cleanContent = afterMissionPlan;
@@ -23246,6 +23791,9 @@ ${historyLines.join("\n\n")}
23246
23791
  if (delegationRulePayload) {
23247
23792
  msgMetadata.delegationRule = delegationRulePayload;
23248
23793
  }
23794
+ if (organigramUpdate) {
23795
+ msgMetadata.organigramUpdate = organigramUpdate;
23796
+ }
23249
23797
  if (missionPlan) {
23250
23798
  msgMetadata.missionPlan = missionPlan;
23251
23799
  }
@@ -23268,7 +23816,7 @@ ${historyLines.join("\n\n")}
23268
23816
  (0, approvalPolicy_12.logDelegatedApproval)({ agentId: "coo", channelId: "chan_coo", action: skillAction });
23269
23817
  }
23270
23818
  }
23271
- const needsApproval = !trustDelegated && !!trustUpdatePayload || !skillDelegated && !!skillWriteProposal || !!delegationRulePayload || !!missionPlan || !!nodeSpawnProposalId || remoteAgentProposals.length > 0;
23819
+ const needsApproval = !trustDelegated && !!trustUpdatePayload || !skillDelegated && !!skillWriteProposal || !!delegationRulePayload || !!organigramUpdate || !!missionPlan || !!nodeSpawnProposalId || remoteAgentProposals.length > 0;
23272
23820
  const cooMsg = await db_12.db.message.create({
23273
23821
  data: {
23274
23822
  channelId: "chan_coo",
@@ -23335,6 +23883,26 @@ ${historyLines.join("\n\n")}
23335
23883
  (0, ws_12.broadcast)({ type: "approval.created", payload: { approvalId: cooMsg.id, agentId: "coo", channelId: "chan_coo", priority: delPrio } });
23336
23884
  (0, ws_12.broadcastToWorkspace)(ctx.workspaceId, { type: "delegationGraph.changed", payload: { workspaceId: ctx.workspaceId } });
23337
23885
  }
23886
+ if (organigramUpdate) {
23887
+ const firstOp = organigramUpdate.operations[0];
23888
+ const orgPayload = {
23889
+ operations: organigramUpdate.operations,
23890
+ reason: organigramUpdate.reason ?? "",
23891
+ op: firstOp.op
23892
+ };
23893
+ const orgPrio = (0, approvalPolicy_12.classifyPriority)(`organigram:${firstOp.op}`, "organigram_update");
23894
+ const orgExpiry = (0, approvalPolicy_12.computeExpiresAt)(orgPrio);
23895
+ await db_12.db.approval.create({
23896
+ data: {
23897
+ messageId: cooMsg.id,
23898
+ type: "organigram_update",
23899
+ payload: JSON.stringify(orgPayload),
23900
+ priority: orgPrio,
23901
+ expiresAt: orgExpiry
23902
+ }
23903
+ });
23904
+ (0, ws_12.broadcast)({ type: "approval.created", payload: { approvalId: cooMsg.id, agentId: "coo", channelId: "chan_coo", priority: orgPrio } });
23905
+ }
23338
23906
  (0, ws_12.broadcastToChannel)("chan_coo", { type: "message.new", payload: (0, messages_12.toMessage)(cooMsg) });
23339
23907
  if (dispatchBlock) {
23340
23908
  const plan = {
@@ -25219,139 +25787,6 @@ var require_shellExec2 = __commonJS({
25219
25787
  }
25220
25788
  });
25221
25789
 
25222
- // apps/backend/dist/lib/organigramParse.js
25223
- var require_organigramParse = __commonJS({
25224
- "apps/backend/dist/lib/organigramParse.js"(exports2) {
25225
- "use strict";
25226
- Object.defineProperty(exports2, "__esModule", { value: true });
25227
- exports2.parseOrganigram = parseOrganigram;
25228
- var SECTION_TITLE_RE = /^(reporting|org\s*chart|structure|reporting\s+structure|hierarchy)\b/i;
25229
- function extractSection(md, title) {
25230
- const lines = md.split("\n");
25231
- let start = -1;
25232
- let end = lines.length;
25233
- for (let i = 0; i < lines.length; i++) {
25234
- const m = /^##\s+(.+?)\s*$/.exec(lines[i]);
25235
- if (!m)
25236
- continue;
25237
- if (start === -1 && title.test(m[1])) {
25238
- start = i + 1;
25239
- continue;
25240
- }
25241
- if (start !== -1) {
25242
- end = i;
25243
- break;
25244
- }
25245
- }
25246
- if (start === -1)
25247
- return null;
25248
- return lines.slice(start, end).join("\n");
25249
- }
25250
- function extractRef(rawLine) {
25251
- let s = rawLine.replace(/[│├└─]/g, " ").replace(/^[\s\-*•◦]+/, "").replace(/[\[\]]/g, "").trim();
25252
- if (!s)
25253
- return null;
25254
- const asciiStart = s.search(/[A-Za-z]/);
25255
- if (asciiStart > 0)
25256
- s = s.slice(asciiStart).trim();
25257
- const idMatch = /\(([A-Za-z0-9][A-Za-z0-9_-]*)\)\s*$/.exec(s);
25258
- const idHint = idMatch ? idMatch[1].toLowerCase() : null;
25259
- const display = (idMatch ? s.slice(0, idMatch.index) : s).trim();
25260
- if (!display && !idHint)
25261
- return null;
25262
- return { display, idHint };
25263
- }
25264
- function resolveAgent(ref, agents) {
25265
- if (!ref)
25266
- return null;
25267
- if (ref.idHint) {
25268
- const byId = agents.find((a) => a.id.toLowerCase() === ref.idHint);
25269
- if (byId)
25270
- return byId.id;
25271
- }
25272
- if (/^(human|user|owner|you|me)$/i.test(ref.display))
25273
- return null;
25274
- const norm = ref.display.toLowerCase().replace(/\s+/g, " ").trim();
25275
- if (!norm)
25276
- return null;
25277
- const byName = agents.find((a) => a.name.toLowerCase() === norm);
25278
- if (byName)
25279
- return byName.id;
25280
- const byPrefix = agents.find((a) => a.name.toLowerCase().startsWith(norm) || norm.startsWith(a.name.toLowerCase()));
25281
- return byPrefix?.id ?? null;
25282
- }
25283
- function indentLevel(line) {
25284
- const m = /^([\s│├└─]*)/.exec(line);
25285
- if (!m)
25286
- return 0;
25287
- return Math.floor(m[1].length / 2);
25288
- }
25289
- function buildFlatFallback(agents) {
25290
- const coo = agents.find((a) => a.id === "coo" || a.name.toLowerCase() === "coo");
25291
- if (!coo)
25292
- return [];
25293
- const out = [{ parent: null, child: coo.id }];
25294
- for (const a of agents) {
25295
- if (a.id === coo.id)
25296
- continue;
25297
- out.push({ parent: coo.id, child: a.id });
25298
- }
25299
- return out;
25300
- }
25301
- function parseOrganigram(md, agents) {
25302
- if (agents.length === 0)
25303
- return [];
25304
- const section = md ? extractSection(md, SECTION_TITLE_RE) : null;
25305
- if (!section)
25306
- return buildFlatFallback(agents);
25307
- const lines = section.split("\n").filter((l) => l.trim().length > 0);
25308
- if (lines.length === 0)
25309
- return buildFlatFallback(agents);
25310
- const edges = [];
25311
- const stack = [];
25312
- const seenChildren = /* @__PURE__ */ new Set();
25313
- for (const raw of lines) {
25314
- const level = indentLevel(raw);
25315
- const ref = extractRef(raw);
25316
- if (!ref)
25317
- continue;
25318
- const id = resolveAgent(ref, agents);
25319
- const isHumanRoot = /^(human|user|owner|you|me)$/i.test(ref.display) && !ref.idHint;
25320
- if (!id && !isHumanRoot)
25321
- continue;
25322
- while (stack.length > 0 && stack[stack.length - 1].level >= level)
25323
- stack.pop();
25324
- const parentNode = stack[stack.length - 1] ?? null;
25325
- const parent = parentNode?.id ?? null;
25326
- if (id) {
25327
- if (!seenChildren.has(id)) {
25328
- edges.push({ parent, child: id });
25329
- seenChildren.add(id);
25330
- }
25331
- stack.push({ id, level });
25332
- } else {
25333
- stack.push({ id: null, level });
25334
- }
25335
- }
25336
- if (edges.length === 0)
25337
- return buildFlatFallback(agents);
25338
- const flatHintMatch = /flat\s+delegation|all\s+(workspace\s+)?agents/i.test(section);
25339
- if (flatHintMatch) {
25340
- const rootAgentId = edges.find((e) => e.parent === null)?.child ?? (agents.find((a) => a.id === "coo")?.id ?? null);
25341
- if (rootAgentId) {
25342
- for (const a of agents) {
25343
- if (seenChildren.has(a.id) || a.id === rootAgentId)
25344
- continue;
25345
- edges.push({ parent: rootAgentId, child: a.id });
25346
- seenChildren.add(a.id);
25347
- }
25348
- }
25349
- }
25350
- return edges;
25351
- }
25352
- }
25353
- });
25354
-
25355
25790
  // apps/backend/dist/routers/delegations.js
25356
25791
  var require_delegations = __commonJS({
25357
25792
  "apps/backend/dist/routers/delegations.js"(exports2) {
@@ -25366,6 +25801,7 @@ var require_delegations = __commonJS({
25366
25801
  var path_12 = require("path");
25367
25802
  var os_12 = require("os");
25368
25803
  var organigramParse_1 = require_organigramParse();
25804
+ var organigramWrite_1 = require_organigramWrite();
25369
25805
  var ORGANIGRAM_PATH = (0, path_12.join)((0, os_12.homedir)(), ".openclaw", "agents", "coo", "ORGANIGRAM.md");
25370
25806
  async function readOrganigramSafe() {
25371
25807
  try {
@@ -25479,6 +25915,38 @@ var require_delegations = __commonJS({
25479
25915
  await db_12.db.delegationRule.delete({ where: { id: input.id } });
25480
25916
  (0, ws_12.broadcastToWorkspace)(ctx.workspaceId, { type: "delegationGraph.changed", payload: { workspaceId: ctx.workspaceId } });
25481
25917
  }),
25918
+ // Canvas drag-to-reparent — the human IS the approver, so this is a
25919
+ // plain `protectedProcedure` mutation. Atomic-rewrites ORGANIGRAM.md's
25920
+ // `## Reporting Structure` section; preserves the rest of the file
25921
+ // verbatim. Broadcasts so all canvases refetch.
25922
+ reparent: trpc_12.protectedProcedure.input(zod_12.z.object({
25923
+ childAgentId: zod_12.z.string().min(1),
25924
+ // null = make child a root (reports to no one). Useful to demote a
25925
+ // sub-tree to root level or remove a reporting relationship.
25926
+ parentAgentId: zod_12.z.string().nullable()
25927
+ })).mutation(async ({ ctx, input }) => {
25928
+ const childAgent = await db_12.db.agent.findFirst({
25929
+ where: { id: input.childAgentId, workspaceId: ctx.workspaceId },
25930
+ select: { id: true }
25931
+ });
25932
+ if (!childAgent)
25933
+ throw new Error("Child agent not in this workspace");
25934
+ if (input.parentAgentId) {
25935
+ const parentAgent = await db_12.db.agent.findFirst({
25936
+ where: { id: input.parentAgentId, workspaceId: ctx.workspaceId },
25937
+ select: { id: true }
25938
+ });
25939
+ if (!parentAgent)
25940
+ throw new Error("Parent agent not in this workspace");
25941
+ if (input.parentAgentId === input.childAgentId)
25942
+ throw new Error("An agent cannot report to itself");
25943
+ }
25944
+ const result = await (0, organigramWrite_1.applyReparent)(input.childAgentId, input.parentAgentId, ctx.workspaceId);
25945
+ if (!result.ok)
25946
+ throw new Error(result.error);
25947
+ (0, ws_12.broadcastToWorkspace)(ctx.workspaceId, { type: "delegationGraph.changed", payload: { workspaceId: ctx.workspaceId } });
25948
+ return { ok: true };
25949
+ }),
25482
25950
  // Team Canvas projection. Workspace-scoped read of the delegation/trust
25483
25951
  // topology. Parses `delegate:*` patterns into edge / starburst / halo /
25484
25952
  // full-mesh shapes server-side — the canvas layer never sees raw patterns.
@@ -31285,7 +31753,7 @@ var require_package = __commonJS({
31285
31753
  module2.exports = {
31286
31754
  name: "backend",
31287
31755
  private: true,
31288
- version: "0.14.0",
31756
+ version: "0.15.1",
31289
31757
  scripts: {
31290
31758
  dev: "tsx watch src/server.ts",
31291
31759
  build: "tsc && rm -rf dist/resources && cp -r resources dist/resources",