agentix-cli 0.29.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/dist/{App-IEQXWJ7B.js → App-6TP7YMQ3.js} +2 -2
  2. package/dist/{ChatApp-NWOSAP5U.js → ChatApp-G2MQUBR3.js} +2 -2
  3. package/dist/{agent-LS4CZPJ6.js → agent-A5G4DGVU.js} +2 -2
  4. package/dist/{board-dashboard-6QQCKFB3.js → board-dashboard-BU6QJK6X.js} +192 -171
  5. package/dist/board-dashboard-BU6QJK6X.js.map +1 -0
  6. package/dist/{chunk-DI7FMCDK.js → chunk-2J3NV6RZ.js} +2 -2
  7. package/dist/{chunk-UCQ2N3KO.js → chunk-2TJ5L7CB.js} +2 -2
  8. package/dist/{chunk-FKBGJYWQ.js → chunk-3RHPZXCR.js} +2 -2
  9. package/dist/{chunk-SBCZLZG3.js → chunk-6DD4DA2L.js} +2 -2
  10. package/dist/{chunk-52ZPELD2.js → chunk-7CVJ4JOE.js} +2 -2
  11. package/dist/{chunk-Z2Y24ETP.js → chunk-7JWYLSFF.js} +2 -2
  12. package/dist/{chunk-OTXDKKEU.js → chunk-7MNXKITO.js} +134 -122
  13. package/dist/chunk-7MNXKITO.js.map +1 -0
  14. package/dist/chunk-COOJI5SR.js +170 -0
  15. package/dist/chunk-COOJI5SR.js.map +1 -0
  16. package/dist/{chunk-JKYMXLYL.js → chunk-DLOYTMYZ.js} +2 -2
  17. package/dist/chunk-DWADWDMU.js +96 -0
  18. package/dist/chunk-DWADWDMU.js.map +1 -0
  19. package/dist/{chunk-SC5DIKBB.js → chunk-FFDXDUVQ.js} +2 -2
  20. package/dist/{chunk-F5AEXRPR.js → chunk-JC6ZHSTY.js} +2 -2
  21. package/dist/{chunk-F5AEXRPR.js.map → chunk-JC6ZHSTY.js.map} +1 -1
  22. package/dist/{chunk-UYSNGBDT.js → chunk-JJIYFBDH.js} +2 -2
  23. package/dist/{chunk-3QVOWDHZ.js → chunk-JV52UMCI.js} +2 -2
  24. package/dist/{chunk-6F2IKJ2F.js → chunk-NZ346QCV.js} +2 -2
  25. package/dist/{chunk-M4AMWP3H.js → chunk-QLTM3F7M.js} +17 -8
  26. package/dist/{chunk-M4AMWP3H.js.map → chunk-QLTM3F7M.js.map} +1 -1
  27. package/dist/{chunk-4KXBUORV.js → chunk-R7WLFEW3.js} +2 -2
  28. package/dist/{chunk-AE2LDMZU.js → chunk-UOGDSRG3.js} +2 -2
  29. package/dist/chunk-UOGDSRG3.js.map +1 -0
  30. package/dist/{chunk-IWMW5432.js → chunk-V7FYSZSV.js} +1 -1
  31. package/dist/{chunk-IWMW5432.js.map → chunk-V7FYSZSV.js.map} +1 -1
  32. package/dist/chunk-VMDA2SX2.js +43 -0
  33. package/dist/chunk-VMDA2SX2.js.map +1 -0
  34. package/dist/{chunk-JIOLJIBM.js → chunk-W6HKWISZ.js} +2 -2
  35. package/dist/{chunk-GMNKJXTR.js → chunk-Z3RKXKE2.js} +2 -2
  36. package/dist/{chunk-GMNKJXTR.js.map → chunk-Z3RKXKE2.js.map} +1 -1
  37. package/dist/{chunk-LNWX4MPX.js → chunk-ZZQMR526.js} +130 -129
  38. package/dist/chunk-ZZQMR526.js.map +1 -0
  39. package/dist/cli.js +1 -1
  40. package/dist/cli.js.map +1 -1
  41. package/dist/client-QALLJG62.js +2 -0
  42. package/dist/config-TNNODULV.js +2 -0
  43. package/dist/config-mutator-RI77M5YT.js +2 -0
  44. package/dist/doctor-DW27MR2T.js +2 -0
  45. package/dist/index.d.ts +178 -155
  46. package/dist/index.js +1 -1
  47. package/dist/index.js.map +1 -1
  48. package/dist/processes-I3XAPHG7.js +2 -0
  49. package/dist/program-WZ3GEOXO.js +2 -0
  50. package/dist/query-7X6VO5VU.js +2 -0
  51. package/dist/registry-LUMYXZ6F.js +2 -0
  52. package/dist/request-planner-RNQB4L6F.js +2 -0
  53. package/dist/request-planner-RNQB4L6F.js.map +1 -0
  54. package/dist/{review-candidates-F3OFYTCZ.js → review-candidates-ZX4JBWQL.js} +2 -2
  55. package/dist/routing-5VGXQ4VG.js +2 -0
  56. package/dist/scheduler-W5C43ZTE.js +2 -0
  57. package/dist/seat-SXPFROXT.js +2 -0
  58. package/dist/{select-L7C52URW.js → select-GJ47QACY.js} +2 -2
  59. package/dist/{surface-usage-NS74AXSE.js → surface-usage-UREILT2E.js} +2 -2
  60. package/dist/web/activity-graph.global.js +901 -368
  61. package/dist/web/activity-graph.global.js.map +1 -1
  62. package/dist/wiki-SRH3JKI4.js +2 -0
  63. package/dist/workspace-setup-G6KGWPHY.js +2 -0
  64. package/package.json +1 -1
  65. package/dist/board-dashboard-6QQCKFB3.js.map +0 -1
  66. package/dist/chunk-4BR7D2OU.js +0 -36
  67. package/dist/chunk-4BR7D2OU.js.map +0 -1
  68. package/dist/chunk-67Q4DCPS.js +0 -54
  69. package/dist/chunk-67Q4DCPS.js.map +0 -1
  70. package/dist/chunk-AE2LDMZU.js.map +0 -1
  71. package/dist/chunk-I3SIXTJO.js +0 -44
  72. package/dist/chunk-I3SIXTJO.js.map +0 -1
  73. package/dist/chunk-LNWX4MPX.js.map +0 -1
  74. package/dist/chunk-OTXDKKEU.js.map +0 -1
  75. package/dist/chunk-XPXO5K4L.js +0 -164
  76. package/dist/chunk-XPXO5K4L.js.map +0 -1
  77. package/dist/client-5Z3URGPY.js +0 -2
  78. package/dist/config-ORSGLVRW.js +0 -2
  79. package/dist/config-mutator-JB3S2E7H.js +0 -2
  80. package/dist/doctor-FS2BX6H4.js +0 -2
  81. package/dist/processes-WU23U2OJ.js +0 -2
  82. package/dist/program-I4J63BQ7.js +0 -2
  83. package/dist/query-R7EI3VGX.js +0 -2
  84. package/dist/registry-UJIJBLUR.js +0 -2
  85. package/dist/request-planner-BKT6RZKX.js +0 -2
  86. package/dist/request-planner-BKT6RZKX.js.map +0 -1
  87. package/dist/routing-S5WSFIAD.js +0 -2
  88. package/dist/scheduler-DSAOMV4O.js +0 -2
  89. package/dist/seat-ZVHXNAEN.js +0 -2
  90. package/dist/wiki-5YQI4YKM.js +0 -2
  91. package/dist/workspace-setup-B3F3TUDE.js +0 -2
  92. /package/dist/{App-IEQXWJ7B.js.map → App-6TP7YMQ3.js.map} +0 -0
  93. /package/dist/{ChatApp-NWOSAP5U.js.map → ChatApp-G2MQUBR3.js.map} +0 -0
  94. /package/dist/{agent-LS4CZPJ6.js.map → agent-A5G4DGVU.js.map} +0 -0
  95. /package/dist/{chunk-DI7FMCDK.js.map → chunk-2J3NV6RZ.js.map} +0 -0
  96. /package/dist/{chunk-UCQ2N3KO.js.map → chunk-2TJ5L7CB.js.map} +0 -0
  97. /package/dist/{chunk-FKBGJYWQ.js.map → chunk-3RHPZXCR.js.map} +0 -0
  98. /package/dist/{chunk-SBCZLZG3.js.map → chunk-6DD4DA2L.js.map} +0 -0
  99. /package/dist/{chunk-52ZPELD2.js.map → chunk-7CVJ4JOE.js.map} +0 -0
  100. /package/dist/{chunk-Z2Y24ETP.js.map → chunk-7JWYLSFF.js.map} +0 -0
  101. /package/dist/{chunk-JKYMXLYL.js.map → chunk-DLOYTMYZ.js.map} +0 -0
  102. /package/dist/{chunk-SC5DIKBB.js.map → chunk-FFDXDUVQ.js.map} +0 -0
  103. /package/dist/{chunk-UYSNGBDT.js.map → chunk-JJIYFBDH.js.map} +0 -0
  104. /package/dist/{chunk-3QVOWDHZ.js.map → chunk-JV52UMCI.js.map} +0 -0
  105. /package/dist/{chunk-6F2IKJ2F.js.map → chunk-NZ346QCV.js.map} +0 -0
  106. /package/dist/{chunk-4KXBUORV.js.map → chunk-R7WLFEW3.js.map} +0 -0
  107. /package/dist/{chunk-JIOLJIBM.js.map → chunk-W6HKWISZ.js.map} +0 -0
  108. /package/dist/{client-5Z3URGPY.js.map → client-QALLJG62.js.map} +0 -0
  109. /package/dist/{config-ORSGLVRW.js.map → config-TNNODULV.js.map} +0 -0
  110. /package/dist/{config-mutator-JB3S2E7H.js.map → config-mutator-RI77M5YT.js.map} +0 -0
  111. /package/dist/{doctor-FS2BX6H4.js.map → doctor-DW27MR2T.js.map} +0 -0
  112. /package/dist/{processes-WU23U2OJ.js.map → processes-I3XAPHG7.js.map} +0 -0
  113. /package/dist/{program-I4J63BQ7.js.map → program-WZ3GEOXO.js.map} +0 -0
  114. /package/dist/{query-R7EI3VGX.js.map → query-7X6VO5VU.js.map} +0 -0
  115. /package/dist/{registry-UJIJBLUR.js.map → registry-LUMYXZ6F.js.map} +0 -0
  116. /package/dist/{review-candidates-F3OFYTCZ.js.map → review-candidates-ZX4JBWQL.js.map} +0 -0
  117. /package/dist/{routing-S5WSFIAD.js.map → routing-5VGXQ4VG.js.map} +0 -0
  118. /package/dist/{scheduler-DSAOMV4O.js.map → scheduler-W5C43ZTE.js.map} +0 -0
  119. /package/dist/{seat-ZVHXNAEN.js.map → seat-SXPFROXT.js.map} +0 -0
  120. /package/dist/{select-L7C52URW.js.map → select-GJ47QACY.js.map} +0 -0
  121. /package/dist/{surface-usage-NS74AXSE.js.map → surface-usage-UREILT2E.js.map} +0 -0
  122. /package/dist/{wiki-5YQI4YKM.js.map → wiki-SRH3JKI4.js.map} +0 -0
  123. /package/dist/{workspace-setup-B3F3TUDE.js.map → workspace-setup-G6KGWPHY.js.map} +0 -0
@@ -1,36 +0,0 @@
1
- import{a as u,b as o}from"./chunk-ZTEU5JVQ.js";o();function g(n,e,t=u()){return n.prepare(`
2
- INSERT INTO task_traces (
3
- task_id, agent_id, channel, chat_id, workflow_run_id, workflow_id,
4
- workflow_node_id, intent_event_id, intent_decided_by, resume_session_id,
5
- model, status, started_at, message_preview, original_message
6
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'in-flight', ?, ?, ?)
7
- `).run(t,e.agentId,e.channel??null,e.chatId??null,e.workflowRunId??null,e.workflowId??null,e.workflowNodeId??null,e.intentEventId??null,e.intentDecidedBy??null,e.resumeSessionId??null,e.model??null,Date.now(),e.messagePreview??null,e.originalMessage??null),t}function m(n,e,t){let s=Date.now();n.prepare(`
8
- UPDATE task_traces SET
9
- status = ?,
10
- final_session_id = ?,
11
- input_tokens = ?,
12
- output_tokens = ?,
13
- cache_read_tokens = ?,
14
- cache_create_tokens = ?,
15
- error = ?,
16
- final_response = COALESCE(?, final_response),
17
- finished_at = ?,
18
- duration_ms = ? - started_at
19
- WHERE task_id = ?
20
- `).run(t.status,t.finalSessionId??null,t.inputTokens??null,t.outputTokens??null,t.cacheReadTokens??null,t.cacheCreateTokens??null,t.error??null,t.finalResponse??null,s,s,e)}function _(n,e,t){let s=Date.now();return n.transaction(()=>{let l=(n.prepare("SELECT MAX(seq) AS max_seq FROM task_trace_steps WHERE task_id = ?").get(e)?.max_seq??-1)+1;return n.prepare(`
21
- INSERT INTO task_trace_steps (
22
- task_id, seq, name, action, status, input_summary, output_summary, error, ms, started_at
23
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
24
- `).run(e,l,t.name,t.action??null,t.status??null,t.inputSummary??null,t.outputSummary??null,t.error??null,t.ms??null,s),l})()}function i(n){return{taskId:n.task_id,agentId:n.agent_id,channel:n.channel??null,chatId:n.chat_id??null,workflowRunId:n.workflow_run_id??null,workflowId:n.workflow_id??null,workflowNodeId:n.workflow_node_id??null,intentEventId:n.intent_event_id??null,intentDecidedBy:n.intent_decided_by??null,resumeSessionId:n.resume_session_id??null,finalSessionId:n.final_session_id??null,model:n.model??null,status:n.status,startedAt:n.started_at,finishedAt:n.finished_at??null,durationMs:n.duration_ms??null,inputTokens:n.input_tokens??null,outputTokens:n.output_tokens??null,cacheReadTokens:n.cache_read_tokens??null,cacheCreateTokens:n.cache_create_tokens??null,error:n.error??null,messagePreview:n.message_preview??null,originalMessage:n.original_message??null,finalResponse:n.final_response??null}}function d(n){return{taskId:n.task_id,seq:n.seq,name:n.name,action:n.action??null,status:n.status??null,inputSummary:n.input_summary??null,outputSummary:n.output_summary??null,error:n.error??null,ms:n.ms??null,startedAt:n.started_at}}function p(n,e){let t=n.prepare("SELECT * FROM task_traces WHERE task_id = ?").get(e);if(!t)return null;let s=n.prepare("SELECT * FROM task_trace_steps WHERE task_id = ? ORDER BY seq").all(e);return{task:i(t),steps:s.map(d)}}function k(n){let e=Date.now();return n.prepare(`
25
- UPDATE task_traces
26
- SET status = 'canceled',
27
- error = 'daemon-restart (orphaned in-flight)',
28
- finished_at = ?
29
- WHERE status = 'in-flight'
30
- `).run(e).changes??0}function h(n,e={}){let t=[],s=[];e.agentId&&(t.push("agent_id = ?"),s.push(e.agentId)),e.channel&&(t.push("channel = ?"),s.push(e.channel)),e.chatId&&(t.push("chat_id = ?"),s.push(e.chatId)),e.workflowRunId&&(t.push("workflow_run_id = ?"),s.push(e.workflowRunId)),e.status&&(t.push("status = ?"),s.push(e.status)),e.since!==void 0&&(t.push("started_at >= ?"),s.push(e.since)),e.until!==void 0&&(t.push("started_at <= ?"),s.push(e.until));let a=Math.max(1,Math.min(e.limit??100,1e3)),r=`
31
- SELECT * FROM task_traces
32
- ${t.length>0?"WHERE "+t.join(" AND "):""}
33
- ORDER BY started_at DESC
34
- LIMIT ?
35
- `;return s.push(a),n.prepare(r).all(...s).map(i)}export{g as a,m as b,_ as c,p as d,k as e,h as f};
36
- //# sourceMappingURL=chunk-4BR7D2OU.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/storage/traces.ts"],"sourcesContent":["import type Database from \"better-sqlite3\"\nimport { newEventId } from \"@/intent/ulid\"\n\n// --- Task trace store ---\n//\n// Improvement plan #2 — per-task observability backed by SQLite. One\n// task_traces row per executeTask invocation, an append-only stream of\n// task_trace_steps inside it. The store is intentionally dumb: it stores\n// what callers give it. Callers (capture sites in src/agents/runtime.ts)\n// are responsible for byte-capping large step payloads so the daemon\n// memory stays bounded under tool-heavy turns.\n//\n// Why a separate store layer (vs writing inline in runtime.ts)?\n// - Tests can exercise the trace lifecycle against a temp SQLite without\n// spinning up the agent runtime.\n// - The HTTP endpoints (GET /task/:id/trace) read through the same\n// functions producers write through, so there's a single source of\n// truth on field names + semantics.\n// - Future capture sites (sdk tier, orchestrator tier) reuse this API\n// without copying SQL.\n//\n// Why ULID for task_id?\n// - URL-safe in HTTP routes (GET /task/:id/trace).\n// - Time-sortable, so listTraces ORDER BY started_at and ORDER BY\n// task_id agree without an explicit timestamp column index.\n// - Already in-tree (src/intent/ulid.ts); no new dep.\n//\n// Cross-DB FK note:\n// intent_event_id and intent_decided_by are logical foreign keys into\n// the intent ledger at .agentx/intent/ledger.sqlite. better-sqlite3\n// doesn't enforce cross-database FKs and we don't ATTACH the ledger\n// here on purpose — the rescue plan's append-only contract on the\n// ledger is honoured by routing all writes through src/intent/ledger.ts.\n// Joins happen at the HTTP / CLI layer when needed.\n\nexport interface TraceStartInput {\n agentId: string\n channel?: string | null\n chatId?: string | null\n messagePreview?: string | null\n /** Full untruncated user message — used by `agentx trace replay <taskId>`\n * to re-fire a recorded task against the current agent config. NULL on\n * rows from before migration v8, in which case replay falls back to\n * messagePreview (which is capped at 200 chars). */\n originalMessage?: string | null\n workflowRunId?: string | null\n workflowId?: string | null\n workflowNodeId?: string | null\n intentEventId?: string | null\n intentDecidedBy?: string | null\n resumeSessionId?: string | null\n model?: string | null\n}\n\nexport interface TraceEndInput {\n status: \"ok\" | \"error\" | \"timeout\"\n finalSessionId?: string | null\n inputTokens?: number | null\n outputTokens?: number | null\n cacheReadTokens?: number | null\n cacheCreateTokens?: number | null\n error?: string | null\n /** The agent's final reply text. Stored verbatim so `replay --diff`\n * can compare original output vs the new run's output without\n * reconstructing it from the step ledger. NULL when the response\n * wasn't captured (older rows or non-text outputs). */\n finalResponse?: string | null\n}\n\nexport interface TraceStepInput {\n /** Coarse step kind. Conventional values: \"tool_use\" | \"tool_result\" |\n * \"llm_message\" | \"session_rotation\" | \"error\" | \"preflight\". Free-form\n * strings allowed so future capture sites don't need a schema bump. */\n name: string\n /** Fine-grained name within the kind. For \"tool_use\", this is the tool\n * name (e.g. \"Bash\", \"Edit\"). Optional. */\n action?: string | null\n status?: \"ok\" | \"error\" | \"in-flight\" | null\n /** JSON-stringified input or human-readable summary. Caller-byte-capped. */\n inputSummary?: string | null\n outputSummary?: string | null\n error?: string | null\n /** Step duration when known. Optional — many step kinds are point-in-time. */\n ms?: number | null\n}\n\nexport interface TraceRecord {\n taskId: string\n agentId: string\n channel: string | null\n chatId: string | null\n workflowRunId: string | null\n workflowId: string | null\n workflowNodeId: string | null\n intentEventId: string | null\n intentDecidedBy: string | null\n resumeSessionId: string | null\n finalSessionId: string | null\n model: string | null\n status: string\n startedAt: number\n finishedAt: number | null\n durationMs: number | null\n inputTokens: number | null\n outputTokens: number | null\n cacheReadTokens: number | null\n cacheCreateTokens: number | null\n error: string | null\n messagePreview: string | null\n /** Full untruncated user message — populated for traces from migration v8\n * onward. NULL on older rows; consumers should fall back to messagePreview. */\n originalMessage: string | null\n /** Agent's final reply text — populated for traces from migration v8\n * onward when the executor records it on end. */\n finalResponse: string | null\n}\n\nexport interface TraceStepRecord {\n taskId: string\n seq: number\n name: string\n action: string | null\n status: string | null\n inputSummary: string | null\n outputSummary: string | null\n error: string | null\n ms: number | null\n startedAt: number\n}\n\nexport interface ListTracesFilters {\n agentId?: string\n channel?: string\n chatId?: string\n workflowRunId?: string\n status?: string\n /** ms epoch */\n since?: number\n /** ms epoch */\n until?: number\n /** Default 100, capped at 1000. */\n limit?: number\n}\n\n/**\n * Insert an in-flight trace row and return its ULID. Callers thread the\n * returned id through the executor and call recordTraceEnd at finish time.\n *\n * `taskId` is optional — when supplied, the caller has already allocated\n * a ULID upstream (e.g. registry.execute generating it before the bus\n * event so the runtime can capture per-step rows under the same id).\n * When omitted, a fresh ULID is allocated here.\n */\nexport function recordTraceStart(\n db: Database.Database,\n input: TraceStartInput,\n taskId: string = newEventId(),\n): string {\n db.prepare(`\n INSERT INTO task_traces (\n task_id, agent_id, channel, chat_id, workflow_run_id, workflow_id,\n workflow_node_id, intent_event_id, intent_decided_by, resume_session_id,\n model, status, started_at, message_preview, original_message\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'in-flight', ?, ?, ?)\n `).run(\n taskId,\n input.agentId,\n input.channel ?? null,\n input.chatId ?? null,\n input.workflowRunId ?? null,\n input.workflowId ?? null,\n input.workflowNodeId ?? null,\n input.intentEventId ?? null,\n input.intentDecidedBy ?? null,\n input.resumeSessionId ?? null,\n input.model ?? null,\n Date.now(),\n input.messagePreview ?? null,\n input.originalMessage ?? null,\n )\n return taskId\n}\n\n/**\n * Finalize a trace. duration_ms is computed from finished_at - started_at\n * inside the UPDATE so the two columns can never disagree. Idempotent —\n * calling twice on the same task_id overwrites the prior end values; the\n * second call's status wins. (Soft-overwrite is intentional: timeout\n * handlers may write before a late stream-completion arrives.)\n */\nexport function recordTraceEnd(db: Database.Database, taskId: string, input: TraceEndInput): void {\n const finishedAt = Date.now()\n db.prepare(`\n UPDATE task_traces SET\n status = ?,\n final_session_id = ?,\n input_tokens = ?,\n output_tokens = ?,\n cache_read_tokens = ?,\n cache_create_tokens = ?,\n error = ?,\n final_response = COALESCE(?, final_response),\n finished_at = ?,\n duration_ms = ? - started_at\n WHERE task_id = ?\n `).run(\n input.status,\n input.finalSessionId ?? null,\n input.inputTokens ?? null,\n input.outputTokens ?? null,\n input.cacheReadTokens ?? null,\n input.cacheCreateTokens ?? null,\n input.error ?? null,\n input.finalResponse ?? null,\n finishedAt,\n finishedAt,\n taskId,\n )\n}\n\n/**\n * Append a step. seq is auto-allocated as MAX(seq)+1 within a transaction\n * so concurrent step writes for the same task_id can't collide. Returns\n * the assigned seq, useful for downstream correlation.\n */\nexport function recordTraceStep(db: Database.Database, taskId: string, input: TraceStepInput): number {\n const startedAt = Date.now()\n const allocateAndInsert = db.transaction((): number => {\n const row = db\n .prepare(\"SELECT MAX(seq) AS max_seq FROM task_trace_steps WHERE task_id = ?\")\n .get(taskId) as { max_seq: number | null } | undefined\n const seq = ((row?.max_seq ?? -1) as number) + 1\n db.prepare(`\n INSERT INTO task_trace_steps (\n task_id, seq, name, action, status, input_summary, output_summary, error, ms, started_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `).run(\n taskId,\n seq,\n input.name,\n input.action ?? null,\n input.status ?? null,\n input.inputSummary ?? null,\n input.outputSummary ?? null,\n input.error ?? null,\n input.ms ?? null,\n startedAt,\n )\n return seq\n })\n return allocateAndInsert()\n}\n\nfunction rowToTrace(row: Record<string, unknown>): TraceRecord {\n return {\n taskId: row.task_id as string,\n agentId: row.agent_id as string,\n channel: (row.channel as string) ?? null,\n chatId: (row.chat_id as string) ?? null,\n workflowRunId: (row.workflow_run_id as string) ?? null,\n workflowId: (row.workflow_id as string) ?? null,\n workflowNodeId: (row.workflow_node_id as string) ?? null,\n intentEventId: (row.intent_event_id as string) ?? null,\n intentDecidedBy: (row.intent_decided_by as string) ?? null,\n resumeSessionId: (row.resume_session_id as string) ?? null,\n finalSessionId: (row.final_session_id as string) ?? null,\n model: (row.model as string) ?? null,\n status: row.status as string,\n startedAt: row.started_at as number,\n finishedAt: (row.finished_at as number) ?? null,\n durationMs: (row.duration_ms as number) ?? null,\n inputTokens: (row.input_tokens as number) ?? null,\n outputTokens: (row.output_tokens as number) ?? null,\n cacheReadTokens: (row.cache_read_tokens as number) ?? null,\n cacheCreateTokens: (row.cache_create_tokens as number) ?? null,\n error: (row.error as string) ?? null,\n messagePreview: (row.message_preview as string) ?? null,\n originalMessage: (row.original_message as string) ?? null,\n finalResponse: (row.final_response as string) ?? null,\n }\n}\n\nfunction rowToStep(row: Record<string, unknown>): TraceStepRecord {\n return {\n taskId: row.task_id as string,\n seq: row.seq as number,\n name: row.name as string,\n action: (row.action as string) ?? null,\n status: (row.status as string) ?? null,\n inputSummary: (row.input_summary as string) ?? null,\n outputSummary: (row.output_summary as string) ?? null,\n error: (row.error as string) ?? null,\n ms: (row.ms as number) ?? null,\n startedAt: row.started_at as number,\n }\n}\n\n/** Fetch a trace + its ordered steps. Returns null if no such task_id. */\nexport function getTrace(\n db: Database.Database,\n taskId: string,\n): { task: TraceRecord; steps: TraceStepRecord[] } | null {\n const row = db.prepare(\"SELECT * FROM task_traces WHERE task_id = ?\").get(taskId) as\n | Record<string, unknown>\n | undefined\n if (!row) return null\n const stepRows = db\n .prepare(\"SELECT * FROM task_trace_steps WHERE task_id = ? ORDER BY seq\")\n .all(taskId) as Record<string, unknown>[]\n return { task: rowToTrace(row), steps: stepRows.map(rowToStep) }\n}\n\n/**\n * Cancel any in-flight trace rows left over from a previous daemon\n * lifetime. Mirrors the intent ledger's startup cleanup (rescue plan\n * Phase 1 commit a8514d9): a hard-killed daemon never gets to fire\n * task:completed, so the row sits \"in-flight\" forever and contaminates\n * triage queries (`agentx trace list --status in-flight`) with stale\n * orphans. Run once at daemon boot before subscribers attach.\n *\n * Returns the number of rows updated. duration_ms is intentionally NOT\n * computed — we don't know when the task actually ended; preserving\n * NULL there is more honest than backdating to \"now\".\n */\nexport function cleanupOrphanedTraces(db: Database.Database): number {\n const now = Date.now()\n const r = db.prepare(`\n UPDATE task_traces\n SET status = 'canceled',\n error = 'daemon-restart (orphaned in-flight)',\n finished_at = ?\n WHERE status = 'in-flight'\n `).run(now)\n return (r.changes ?? 0) as number\n}\n\n/** Triage / dashboard surface — list traces newest-first, filtered. */\nexport function listTraces(db: Database.Database, filters: ListTracesFilters = {}): TraceRecord[] {\n const where: string[] = []\n const params: unknown[] = []\n if (filters.agentId) { where.push(\"agent_id = ?\"); params.push(filters.agentId) }\n if (filters.channel) { where.push(\"channel = ?\"); params.push(filters.channel) }\n if (filters.chatId) { where.push(\"chat_id = ?\"); params.push(filters.chatId) }\n if (filters.workflowRunId) { where.push(\"workflow_run_id = ?\"); params.push(filters.workflowRunId) }\n if (filters.status) { where.push(\"status = ?\"); params.push(filters.status) }\n if (filters.since !== undefined) { where.push(\"started_at >= ?\"); params.push(filters.since) }\n if (filters.until !== undefined) { where.push(\"started_at <= ?\"); params.push(filters.until) }\n\n const limit = Math.max(1, Math.min(filters.limit ?? 100, 1000))\n const sql = `\n SELECT * FROM task_traces\n ${where.length > 0 ? \"WHERE \" + where.join(\" AND \") : \"\"}\n ORDER BY started_at DESC\n LIMIT ?\n `\n params.push(limit)\n return (db.prepare(sql).all(...params) as Record<string, unknown>[]).map(rowToTrace)\n}\n"],"mappings":"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","names":["init_ulid","recordTraceStart","db","input","taskId","newEventId","recordTraceEnd","finishedAt","recordTraceStep","startedAt","seq","rowToTrace","row","rowToStep","getTrace","stepRows","cleanupOrphanedTraces","now","listTraces","filters","where","params","limit","sql"]}
@@ -1,54 +0,0 @@
1
- import{execFile as m}from"child_process";import{existsSync as d}from"fs";import{resolve as f}from"path";var R=["mcp__codegraph__codegraph_search","mcp__codegraph__codegraph_context","mcp__codegraph__codegraph_callers","mcp__codegraph__codegraph_callees","mcp__codegraph__codegraph_impact","mcp__codegraph__codegraph_node","mcp__codegraph__codegraph_status","mcp__codegraph__codegraph_files"],p={command:"codegraph",args:["serve","--mcp"]};function O(){return["## CodeGraph","","This workspace has CodeGraph initialized \u2014 a semantic code-knowledge-graph indexed via tree-sitter. Use it instead of grep/find/Read for exploration.","","**ALWAYS spawn an Explore subagent for `codegraph_explore` / `codegraph_context`** \u2014 those tools return large source sections that would otherwise bloat the main session. Include this in the Explore prompt:","","> Use `codegraph_explore` as your PRIMARY tool \u2014 one call returns full source from all relevant files. Do not re-Read files codegraph_explore already returned. Fall back to grep/glob/Read only if codegraph returned no results.","","**In the main session, use these lightweight codegraph tools directly:**","","| Tool | Use for |","|---|---|","| `codegraph_search` | Find symbols by name across the codebase |","| `codegraph_callers` / `codegraph_callees` | Trace call flow |","| `codegraph_impact` | Check what's affected before editing a symbol |","| `codegraph_node` | Get a single symbol's details |","| `codegraph_files` | Indexed file structure (faster than ls) |","| `codegraph_status` | Index health |",""].join(`
2
- `)}function P(e){let t=e.mcp??{};return!e.codegraph||t.codegraph?t:{...t,codegraph:{command:p.command,args:[...p.args]}}}var _=".codegraph",x=10*60*1e3,g=2;function y(e){let t=Date.now();return new Promise((r,n)=>{m("codegraph",["init","--index","--quiet",e],{timeout:x,maxBuffer:4*1024*1024},o=>{o?n(o):r(Date.now()-t)})})}async function k(e,t=console.error){let r=[];for(let[i,s]of Object.entries(e))s.codegraph&&(s.tier!=="claude-code"&&s.tier!=="codex-cli"||d(s.workspace)&&(d(f(s.workspace,_))||r.push({agentId:i,workspace:s.workspace})));if(r.length===0)return;t(`codegraph: indexing ${r.length} workspace(s) in background (max ${g} parallel)`);let n=0,o=[];for(let i=0;i<Math.min(g,r.length);i++)o.push((async()=>{for(;;){let s=n++;if(s>=r.length)return;let{agentId:c,workspace:h}=r[s];try{let a=await y(h);t(` codegraph: indexed ${c} in ${(a/1e3).toFixed(1)}s`)}catch(a){let u=a?.code==="ENOENT"?" (codegraph CLI not on PATH; install with `npm i -g @colbymchenry/codegraph`)":"";t(` codegraph: ${c} index failed: ${a?.message??a}${u}`)}}})());await Promise.allSettled(o)}import{readFileSync as S,existsSync as b,unlinkSync as w,readdirSync as v}from"fs";import{resolve as l}from"path";function L(e,t){let r={},n=o=>{let i=l(e,o);if(b(i))try{return S(i,"utf-8").trim()||void 0}catch{return}};if(t){let o=n(`SOUL.${t}.md`);o?(r.soul=o,r.soulProfile=t):r.soul=n("SOUL.md")}else r.soul=n("SOUL.md");if(r.identity=n("IDENTITY.md"),r.user=n("USER.md"),r.agents=n("AGENTS.md"),r.bootstrap=n("BOOTSTRAP.md"),r.bootstrap)try{w(l(e,"BOOTSTRAP.md"))}catch{}return r}function D(e){let t=[];return e.identity&&t.push(`[Identity]
3
- ${e.identity}`),e.soul&&t.push(`[Personality & Boundaries]
4
- ${e.soul}`),e.user&&t.push(`[User Profile]
5
- ${e.user}`),e.bootstrap&&t.push(`[First-Run Instructions \u2014 one time only]
6
- ${e.bootstrap}`),t.length===0?"":t.join(`
7
-
8
- `)}function F(e){return`# ${e.name}
9
-
10
- > Agent ID: \`${e.id}\` | Tier: ${e.tier||"claude-code"}
11
-
12
- ## Role
13
-
14
- ${e.role||"AI agent managed by AgentX orchestrator."}
15
-
16
- ## Setup
17
-
18
- This workspace is managed by AgentX. The agent runs via:
19
- \`\`\`bash
20
- # Direct execution
21
- agentx daemon send ${e.id} "your task here"
22
-
23
- # Or via any connected channel (Telegram, WhatsApp, GitLab)
24
- \`\`\`
25
-
26
- ## Code Style
27
-
28
- - Follow existing patterns in the codebase
29
- - Use the language and framework conventions already present
30
- - Keep changes minimal and focused
31
-
32
- ## Testing
33
-
34
- Run tests before committing:
35
- \`\`\`bash
36
- npm test # or the project's test command
37
- \`\`\`
38
-
39
- ## PR Guidelines
40
-
41
- - One logical change per commit
42
- - Reference issue numbers when applicable
43
- - Keep PR descriptions concise
44
-
45
- ## AgentX Integration
46
-
47
- This agent is part of an AgentX mesh. It can:
48
- - Receive tasks from messaging channels (Telegram, WhatsApp, Discord)
49
- - Respond to GitLab @mentions on issues and MRs
50
- - Delegate to other agents by mentioning their handles
51
- - Send cross-channel messages via the /send API
52
- - Access its wiki knowledge base for context
53
- `}function U(e){let t=e.match(/\/soul\s+(\w+)/i);return t?t[1].toLowerCase():/\/soul\s*$/i.test(e)?"default":null}export{R as a,O as b,P as c,k as d,L as e,D as f,F as g,U as h};
54
- //# sourceMappingURL=chunk-67Q4DCPS.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/agents/codegraph-bootstrap.ts","../src/agents/bootstrap.ts"],"sourcesContent":["import { execFile } from \"child_process\"\nimport { existsSync } from \"fs\"\nimport { resolve } from \"path\"\nimport type { AgentDef } from \"@/daemon/config\"\nimport type { McpServerMap } from \"./agent-mcp\"\n\n// --- CodeGraph integration: per-agent semantic code-graph index ---\n//\n// CodeGraph (https://github.com/colbymchenry/codegraph) is a local\n// tree-sitter → SQLite knowledge graph exposed to Claude Code / Codex via\n// MCP. Opt-in per agent with `codegraph: true` on the agent's config.\n//\n// This module owns three responsibilities:\n// 1. `effectiveMcpConfig(def)` — synthesize the codegraph MCP server\n// entry into the agent's MCP map. Used by the daemon's\n// installAgentMcpConfig pass so the workspace's .mcp.json picks\n// up `codegraph serve --mcp` automatically.\n// 2. `bootstrapCodegraphIndexes(agents, log)` — at daemon boot, fire\n// off `codegraph init --index --quiet` in the background for any\n// opted-in workspace whose `.codegraph/` is missing. Throttled to\n// 2 parallel jobs so a large fleet doesn't thrash the host.\n// Returns immediately; the indexes finish on their own.\n// 3. Export the constant tool/CLI names that workspace-setup needs\n// to keep CLAUDE.md and settings.json in sync.\n\n/** MCP tool names exposed by `codegraph serve --mcp`. Used to extend the\n * per-workspace `.claude/settings.json` permissions.allow so claude\n * doesn't prompt for permission on every call. Keep in sync with the\n * upstream tool surface — additions are harmless, removals leave a\n * stale allow entry (also harmless). */\nexport const CODEGRAPH_TOOLS = [\n \"mcp__codegraph__codegraph_search\",\n \"mcp__codegraph__codegraph_context\",\n \"mcp__codegraph__codegraph_callers\",\n \"mcp__codegraph__codegraph_callees\",\n \"mcp__codegraph__codegraph_impact\",\n \"mcp__codegraph__codegraph_node\",\n \"mcp__codegraph__codegraph_status\",\n \"mcp__codegraph__codegraph_files\",\n] as const\n\n/** MCP server stanza we inject into `.mcp.json` for opted-in agents. */\nexport const CODEGRAPH_MCP_SERVER: { command: string; args: string[] } = {\n command: \"codegraph\",\n args: [\"serve\", \"--mcp\"],\n}\n\n/** Markdown block appended to managed CLAUDE.md when codegraph is on.\n * Tells the agent to prefer codegraph_* over grep, and to always spawn\n * an Explore subagent for codegraph_explore so the large source-dump\n * doesn't bloat the main session context. Mirrors the upstream-\n * recommended wording from the README's \"Global Instructions\" block. */\nexport function codegraphClaudeMdSection(): string {\n return [\n \"## CodeGraph\",\n \"\",\n \"This workspace has CodeGraph initialized — a semantic code-knowledge-graph indexed via tree-sitter. Use it instead of grep/find/Read for exploration.\",\n \"\",\n \"**ALWAYS spawn an Explore subagent for `codegraph_explore` / `codegraph_context`** — those tools return large source sections that would otherwise bloat the main session. Include this in the Explore prompt:\",\n \"\",\n \"> Use `codegraph_explore` as your PRIMARY tool — one call returns full source from all relevant files. Do not re-Read files codegraph_explore already returned. Fall back to grep/glob/Read only if codegraph returned no results.\",\n \"\",\n \"**In the main session, use these lightweight codegraph tools directly:**\",\n \"\",\n \"| Tool | Use for |\",\n \"|---|---|\",\n \"| `codegraph_search` | Find symbols by name across the codebase |\",\n \"| `codegraph_callers` / `codegraph_callees` | Trace call flow |\",\n \"| `codegraph_impact` | Check what's affected before editing a symbol |\",\n \"| `codegraph_node` | Get a single symbol's details |\",\n \"| `codegraph_files` | Indexed file structure (faster than ls) |\",\n \"| `codegraph_status` | Index health |\",\n \"\",\n ].join(\"\\n\")\n}\n\n/** Return the MCP server map an agent should expose to .mcp.json,\n * layering codegraph on top of the operator-declared `def.mcp` when\n * `def.codegraph === true`. Operator-declared entries win on key\n * collision (operator can override by declaring their own\n * `mcp.codegraph` block with different args). */\nexport function effectiveMcpConfig(def: AgentDef): McpServerMap {\n const base: McpServerMap = ((def as any).mcp ?? {}) as McpServerMap\n if (!def.codegraph) return base\n if (base.codegraph) return base\n return { ...base, codegraph: { command: CODEGRAPH_MCP_SERVER.command, args: [...CODEGRAPH_MCP_SERVER.args] } }\n}\n\nconst INDEX_DIR = \".codegraph\"\nconst INIT_TIMEOUT_MS = 10 * 60 * 1000 // 10 min — Swift Compiler benchmark indexed 25k files in ~4 min\nconst MAX_PARALLEL = 2\n\n/** Spawn `codegraph init --index --quiet` for one workspace. Returns the\n * duration in ms on success, throws on failure. Caller decides whether\n * to surface the error. */\nfunction indexWorkspace(workspace: string): Promise<number> {\n const started = Date.now()\n return new Promise<number>((res, rej) => {\n execFile(\n \"codegraph\",\n [\"init\", \"--index\", \"--quiet\", workspace],\n { timeout: INIT_TIMEOUT_MS, maxBuffer: 4 * 1024 * 1024 },\n (err) => {\n if (err) rej(err)\n else res(Date.now() - started)\n },\n )\n })\n}\n\n/** Boot-time helper: kick off `codegraph init --index` for every\n * opted-in workspace whose `.codegraph/` is missing. Fire-and-forget —\n * the returned promise resolves once all background jobs settle, but\n * callers (daemon start) don't need to await it. Indexing happens\n * while the daemon is already serving traffic; cold dispatches before\n * the index lands gracefully fall through to grep (the MCP server\n * returns an empty result, and CLAUDE.md tells the agent to fall back).\n *\n * Throttled to MAX_PARALLEL concurrent jobs so a fleet boot doesn't\n * saturate CPU/disk. */\nexport async function bootstrapCodegraphIndexes(\n agents: Record<string, AgentDef>,\n log: (...args: unknown[]) => void = console.error,\n): Promise<void> {\n const pending: Array<{ agentId: string; workspace: string }> = []\n for (const [agentId, def] of Object.entries(agents)) {\n if (!def.codegraph) continue\n if (def.tier !== \"claude-code\" && def.tier !== \"codex-cli\") continue\n if (!existsSync(def.workspace)) continue\n if (existsSync(resolve(def.workspace, INDEX_DIR))) continue\n pending.push({ agentId, workspace: def.workspace })\n }\n\n if (pending.length === 0) return\n\n log(`codegraph: indexing ${pending.length} workspace(s) in background (max ${MAX_PARALLEL} parallel)`)\n\n let i = 0\n const runners: Promise<void>[] = []\n for (let slot = 0; slot < Math.min(MAX_PARALLEL, pending.length); slot++) {\n runners.push((async () => {\n while (true) {\n const idx = i++\n if (idx >= pending.length) return\n const { agentId, workspace } = pending[idx]\n try {\n const ms = await indexWorkspace(workspace)\n log(` codegraph: indexed ${agentId} in ${(ms / 1000).toFixed(1)}s`)\n } catch (err: any) {\n // codegraph binary missing surfaces as ENOENT — recoverable hint\n const hint = err?.code === \"ENOENT\"\n ? \" (codegraph CLI not on PATH; install with `npm i -g @colbymchenry/codegraph`)\"\n : \"\"\n log(` codegraph: ${agentId} index failed: ${err?.message ?? err}${hint}`)\n }\n }\n })())\n }\n await Promise.allSettled(runners)\n}\n","import { readFileSync, existsSync, unlinkSync, readdirSync } from \"fs\"\nimport { resolve } from \"path\"\n\n// --- Bootstrap Identity Files ---\n//\n// Support for structured workspace identity files (inspired by OpenClaw).\n// These files live in the agent's workspace and are auto-loaded into context.\n//\n// Supported files:\n// SOUL.md — default personality, tone, boundaries\n// SOUL.{profile}.md — named soul profiles (e.g. SOUL.finance.md, SOUL.legal.md)\n// IDENTITY.md — name, role, emoji, tagline (persistent)\n// USER.md — user profile, preferences (persistent)\n// AGENTS.md — operating rules, standing orders (persistent)\n// BOOTSTRAP.md — one-time first-run ritual (auto-deleted after first load)\n//\n// Soul switching: send \"/soul finance\" to swap to SOUL.finance.md mid-session.\n\nexport interface BootstrapFiles {\n soul?: string\n /** Active soul profile name (undefined = default SOUL.md) */\n soulProfile?: string\n identity?: string\n user?: string\n agents?: string\n bootstrap?: string\n}\n\nconst BOOTSTRAP_FILE_NAMES = [\n \"SOUL.md\",\n \"IDENTITY.md\",\n \"USER.md\",\n \"AGENTS.md\",\n \"BOOTSTRAP.md\",\n] as const\n\n/**\n * Load bootstrap identity files from an agent's workspace.\n * @param soulProfile — optional soul profile name (e.g. \"finance\" loads SOUL.finance.md)\n */\nexport function loadBootstrapFiles(workspace: string, soulProfile?: string): BootstrapFiles {\n const result: BootstrapFiles = {}\n\n const tryLoad = (filename: string): string | undefined => {\n const filePath = resolve(workspace, filename)\n if (!existsSync(filePath)) return undefined\n try {\n const content = readFileSync(filePath, \"utf-8\").trim()\n return content || undefined\n } catch {\n return undefined\n }\n }\n\n // Load soul: profile-specific if requested, fallback to default\n if (soulProfile) {\n const profileSoul = tryLoad(`SOUL.${soulProfile}.md`)\n if (profileSoul) {\n result.soul = profileSoul\n result.soulProfile = soulProfile\n } else {\n // Profile not found — fall back to default and note it\n result.soul = tryLoad(\"SOUL.md\")\n }\n } else {\n result.soul = tryLoad(\"SOUL.md\")\n }\n\n result.identity = tryLoad(\"IDENTITY.md\")\n result.user = tryLoad(\"USER.md\")\n result.agents = tryLoad(\"AGENTS.md\")\n result.bootstrap = tryLoad(\"BOOTSTRAP.md\")\n\n // BOOTSTRAP.md is one-time: delete after loading\n if (result.bootstrap) {\n try {\n unlinkSync(resolve(workspace, \"BOOTSTRAP.md\"))\n } catch {\n // Best-effort deletion\n }\n }\n\n return result\n}\n\n/**\n * Build context string from bootstrap files.\n * Returns empty string if no files found.\n */\nexport function buildBootstrapContext(files: BootstrapFiles): string {\n const sections: string[] = []\n\n if (files.identity) {\n sections.push(`[Identity]\\n${files.identity}`)\n }\n\n if (files.soul) {\n sections.push(`[Personality & Boundaries]\\n${files.soul}`)\n }\n\n // AGENTS.md is intentionally NOT injected into AgentX's --append-system-prompt\n // anymore. It's loaded natively by Claude Code via `@AGENTS.md` in the\n // workspace's CLAUDE.md (generated by workspace-setup.ts for new workspaces;\n // added to existing workspaces by the 2026-04-24 distribution). Injecting it\n // here on top of the native CLAUDE.md path would double the bytes. Keeping\n // the load in BootstrapFiles for diagnostic/listing purposes only.\n\n if (files.user) {\n sections.push(`[User Profile]\\n${files.user}`)\n }\n\n if (files.bootstrap) {\n sections.push(`[First-Run Instructions — one time only]\\n${files.bootstrap}`)\n }\n\n if (sections.length === 0) return \"\"\n\n return sections.join(\"\\n\\n\")\n}\n\n/**\n * Check which bootstrap files exist in a workspace (for status/diagnostics).\n */\nexport function listBootstrapFiles(workspace: string): string[] {\n return BOOTSTRAP_FILE_NAMES.filter((name) =>\n existsSync(resolve(workspace, name))\n )\n}\n\n/**\n * List available soul profiles in a workspace.\n * Returns profile names (e.g. [\"finance\", \"legal\", \"creative\"]).\n */\nexport function listSoulProfiles(workspace: string): string[] {\n try {\n return readdirSync(workspace)\n .filter(f => f.startsWith(\"SOUL.\") && f.endsWith(\".md\") && f !== \"SOUL.md\")\n .map(f => f.slice(5, -3)) // \"SOUL.finance.md\" → \"finance\"\n } catch {\n return []\n }\n}\n\n/**\n * Generate a standard AGENTS.md for a new agent workspace.\n * Follows the agents.md community convention (https://agents.md/).\n * Readable by Claude Code, Codex, Cursor, Amp, Gemini CLI, etc.\n */\nexport function generateAgentsMd(agent: {\n name: string\n id: string\n role?: string\n workspace: string\n tier?: string\n}): string {\n return `# ${agent.name}\n\n> Agent ID: \\`${agent.id}\\` | Tier: ${agent.tier || \"claude-code\"}\n\n## Role\n\n${agent.role || \"AI agent managed by AgentX orchestrator.\"}\n\n## Setup\n\nThis workspace is managed by AgentX. The agent runs via:\n\\`\\`\\`bash\n# Direct execution\nagentx daemon send ${agent.id} \"your task here\"\n\n# Or via any connected channel (Telegram, WhatsApp, GitLab)\n\\`\\`\\`\n\n## Code Style\n\n- Follow existing patterns in the codebase\n- Use the language and framework conventions already present\n- Keep changes minimal and focused\n\n## Testing\n\nRun tests before committing:\n\\`\\`\\`bash\nnpm test # or the project's test command\n\\`\\`\\`\n\n## PR Guidelines\n\n- One logical change per commit\n- Reference issue numbers when applicable\n- Keep PR descriptions concise\n\n## AgentX Integration\n\nThis agent is part of an AgentX mesh. It can:\n- Receive tasks from messaging channels (Telegram, WhatsApp, Discord)\n- Respond to GitLab @mentions on issues and MRs\n- Delegate to other agents by mentioning their handles\n- Send cross-channel messages via the /send API\n- Access its wiki knowledge base for context\n`\n}\n\n/**\n * Detect a /soul command in a message.\n * Returns the requested profile name, or null if no /soul command found.\n * \"/soul finance\" → \"finance\"\n * \"/soul\" or \"/soul default\" → \"default\" (reset to SOUL.md)\n */\nexport function detectSoulSwitch(message: string): string | null {\n const match = message.match(/\\/soul\\s+(\\w+)/i)\n if (match) return match[1].toLowerCase()\n if (/\\/soul\\s*$/i.test(message)) return \"default\"\n return null\n}\n"],"mappings":"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","names":["execFile","existsSync","resolve","CODEGRAPH_TOOLS","CODEGRAPH_MCP_SERVER","codegraphClaudeMdSection","effectiveMcpConfig","def","base","INDEX_DIR","INIT_TIMEOUT_MS","MAX_PARALLEL","indexWorkspace","workspace","started","res","rej","err","bootstrapCodegraphIndexes","agents","log","pending","agentId","i","runners","slot","idx","ms","hint","readFileSync","existsSync","unlinkSync","readdirSync","resolve","loadBootstrapFiles","workspace","soulProfile","result","tryLoad","filename","filePath","resolve","existsSync","readFileSync","profileSoul","unlinkSync","buildBootstrapContext","files","sections","generateAgentsMd","agent","detectSoulSwitch","message","match"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/agents/routing.ts","../src/decisions/seats/task-tier.ts"],"sourcesContent":["import { askSeat } from \"@/decisions/seat\"\nimport {\n TASK_TIER_SEAT,\n taskTierQuestions,\n taskTierState,\n needsFlagship,\n type TaskTierAnswers,\n type TaskTierInput,\n} from \"@/decisions/seats/task-tier\"\n\n// Picking a cheaper model for work that does not need an expensive one.\n//\n// The policy, in one line: keep the flagship unless the seat is ACTIVE and\n// confidently says the task is mechanical.\n//\n// Every branch that is not that leads to the flagship — seat off, seat in\n// shadow, backend unreachable, answer above the threshold, no cheap model\n// configured, a follow-up turn. That is not defensiveness for its own\n// sake. Downgrading is invisible in the output: the reply still arrives,\n// still reads fluently, and is simply worse in ways nobody checks. An\n// outage that silently made the whole fleet dumber would cost far more\n// than the tokens it saved, so the failure has to land on the expensive\n// side.\n//\n// Follow-ups are where this gets interesting, and the answer is\n// arithmetic rather than caution.\n//\n// Switching models mid-session is mechanically trivial — the session is a\n// transcript and the model is a per-invocation flag, which is exactly what\n// Claude Code's /model does. The cost is that the PROMPT CACHE IS PER\n// MODEL. Resuming on a different model means that model has no cache for\n// the transcript and reads all of it at full price.\n//\n// Published rates, opus-5 against haiku-4.5:\n//\n// opus cached read $0.50/M haiku uncached read $1.00/M\n// opus output $25.00/M haiku output $5.00/M\n//\n// So the swap costs 2x more on input and saves $20/M on output, and it is\n// cheaper only when\n//\n// session_tokens < 40 x reply_tokens\n//\n// A task worth downgrading is by definition one whose answer is short —\n// an acknowledgement, a status echo. Ten tokens of reply puts the\n// break-even at a 400-token session. On any real conversation the swap\n// LOSES, and it loses precisely on the traffic this seat exists to catch.\n//\n// That reverses once the cache is COLD. After the cache TTL there is no\n// cached read to give up: opus pays $5.00/M and haiku $1.00/M, and the\n// cheap model wins outright. So a follow-up is routable exactly when the\n// session has been idle long enough that nothing is cached any more.\n\n/** Below this P(needs the strongest model), a cheaper one will do.\n *\n * Low on purpose. This is not the point at which a small model becomes\n * adequate — it is the point past which being wrong is cheap, and the\n * asymmetry between \"saved a fraction of a cent\" and \"gave a subtly wrong\n * answer nobody caught\" is not close. */\nconst DOWNGRADE_BELOW = 0.2\n\n/** How long a cached prefix is assumed to live. Anthropic's default TTL is\n * five minutes with a one-hour option; an hour is assumed here because it\n * is the assumption whose error is cheap — see the note above. */\nconst CACHE_TTL_MS = 60 * 60 * 1000\n\n/** Resolve only models compatible with the agent's CLI engine. An omitted\n * model leaves the agent on its configured default. */\nexport function cheapModelForEngine(\n tier: string,\n routing?: { cheapModel?: string; cheapModels?: { \"claude-code\"?: string; \"codex-cli\"?: string } },\n): string | undefined {\n if (tier === \"claude-code\") {\n const model = routing?.cheapModels?.[tier] ?? routing?.cheapModel\n return model && /^(claude-|haiku$|sonnet$|opus$)/.test(model) ? model : undefined\n }\n if (tier === \"codex-cli\") {\n const model = routing?.cheapModels?.[tier]\n return model && /^(gpt-|o\\d)/.test(model) ? model : undefined\n }\n return undefined\n}\n\nexport interface RouteResult {\n /** The model to use, or undefined to leave the agent's own default. */\n model?: string\n /** True when this call moved the task off the flagship. */\n downgraded: boolean\n /** P(needs the strongest model), or null when nothing was asked. */\n needsFlagship: number | null\n reason: string\n}\n\nexport interface RouteOptions extends TaskTierInput {\n /** The model to drop to. Usually from config; no downgrade without it. */\n cheapModel?: string | null\n threshold?: number\n /** Idle time on the session being resumed, or null when starting fresh.\n * Only consulted for follow-ups. */\n sessionIdleMs?: number | null\n /** How long a cached prefix is assumed to survive. */\n cacheTtlMs?: number\n}\n\n/**\n * Decide whether this task can run on a cheaper model.\n *\n * Never throws and never blocks: a routing decision must not be able to\n * stop a task from running.\n */\nexport async function routeTaskModel(opts: RouteOptions): Promise<RouteResult> {\n const keep = (reason: string, p: number | null = null): RouteResult => ({\n downgraded: false, needsFlagship: p, reason,\n })\n\n if (opts.channel === \"voice\" || opts.channel === \"desktop\") return keep(\"desktop requests retain the configured model\")\n\n if (!opts.cheapModel) return keep(\"no cheaper model configured\")\n\n // A follow-up onto a WARM cache is the losing case above. Idle longer\n // than the TTL and there is nothing left to lose, so it is routable.\n //\n // One hour, not the five-minute default, because the assumption has to\n // be the one that fails safely: over-estimating how long the cache lives\n // means occasionally keeping the flagship when a swap would have been\n // free, while under-estimating means routinely paying double on input.\n if (opts.isFollowUp) {\n const idle = opts.sessionIdleMs ?? 0\n const ttl = opts.cacheTtlMs ?? CACHE_TTL_MS\n if (idle < ttl) {\n return keep(`follow-up on a warm cache (idle ${Math.round(idle / 1000)}s) — swapping would cost more than it saves`)\n }\n }\n\n let result\n try {\n result = await askSeat(\n TASK_TIER_SEAT,\n taskTierState(opts),\n taskTierQuestions,\n {\n // The incumbent is \"always flagship\", which is what this has to beat.\n incumbent: { needsFlagship: 1 },\n features: { agent: opts.agent, channel: opts.channel ?? \"unknown\" },\n },\n )\n } catch {\n return keep(\"routing seat threw — keeping the flagship\")\n }\n\n if (!result) return keep(\"routing seat unavailable — keeping the flagship\")\n\n const p = needsFlagship(result.answers as TaskTierAnswers)\n\n // Shadow records the answer and changes nothing. This is the whole\n // point of a soak: the rows accumulate against real traffic while the\n // incumbent stays authoritative, so promotion is a decision made on\n // evidence rather than on how good the idea sounded.\n if (result.mode !== \"active\") {\n return keep(`shadow — would ${p < (opts.threshold ?? DOWNGRADE_BELOW) ? \"downgrade\" : \"keep\"} (${p.toFixed(2)})`, p)\n }\n\n const threshold = opts.threshold ?? DOWNGRADE_BELOW\n if (p >= threshold) return keep(`needs the flagship (${p.toFixed(2)})`, p)\n\n return {\n model: opts.cheapModel,\n downgraded: true,\n needsFlagship: p,\n reason: `mechanical task (${p.toFixed(2)} below ${threshold}) → ${opts.cheapModel}`,\n }\n}\n","import { noul } from \"../questions\"\nimport type { AnswersFor, NoulAnswer, StateValue } from \"../types\"\n\n// Whether a task needs the flagship model.\n//\n// Every claude-code agent on both nodes runs claude-opus-5. Much of what\n// they actually do is not hard: acknowledging a message, reading a file\n// and saying what is in it, restating a status, routing a mention. Those\n// turns cost flagship rates for work a small model finishes correctly.\n//\n// A single Noul, not a Choice over model names, and that is deliberate.\n// The models available differ per node, per tier and per provider, and a\n// Choice whose options are a fleet's current model list is a decision that\n// silently changes meaning every time someone edits config. The question\n// that stays stable is about the TASK: does this need the strongest model\n// available. Mapping that answer onto a specific model is policy, and\n// policy belongs in code where it can be read.\n//\n// Direction of failure is the whole design. A seat is fail-open by\n// contract — askSeat returns null when it is off or unreachable — and here\n// \"open\" must mean KEEP THE FLAGSHIP. A missing answer that quietly\n// downgrades a model would turn an outage into silently worse work across\n// the fleet, which is far more expensive than the tokens it saved. So the\n// caller only ever downgrades on an explicit, confident \"no\".\n\nexport const TASK_TIER_SEAT = \"task-tier\"\n\nexport interface TaskTierInput {\n /** The task, in the words it arrived in. */\n message: string\n agent: string\n /** Where it came from — a cron heartbeat and a customer message are not\n * the same kind of work even when they read alike. */\n channel?: string | null\n /** True when this continues an existing session. A follow-up inherits\n * the difficulty of what it is following up on, and switching models\n * mid-conversation is its own hazard. */\n isFollowUp?: boolean\n /** Tools the agent could reach for. A task that will touch production\n * is not a cheap task however simply it is phrased. */\n toolsAvailable?: number\n}\n\nexport function taskTierState(input: TaskTierInput): StateValue {\n return {\n task: clip(input.message, 2000),\n agent: input.agent,\n channel: input.channel ?? null,\n continuesExistingSession: input.isFollowUp ?? false,\n ...(input.toolsAvailable ? { toolsAvailable: input.toolsAvailable } : {}),\n }\n}\n\nexport const taskTierQuestions = {\n needsFlagship: noul(\n \"This task needs the strongest available model to be done correctly.\",\n {\n true:\n \"Doing it well takes real reasoning: writing or reviewing code, diagnosing a failure, planning work across several steps, weighing a judgement, handling money, touching production or anything irreversible, following a long or ambiguous instruction, or work where being subtly wrong would not be noticed. Anything where a weaker model would produce something that LOOKS right belongs here.\",\n false:\n \"It is mechanical and its result is obvious on sight: acknowledging or confirming a message, restating or formatting something already written, a greeting, a status echo, reading one file and reporting what it says, a lookup with one right answer, a scheduled heartbeat with nothing to decide.\",\n },\n ),\n}\n\nexport type TaskTierAnswers = AnswersFor<typeof taskTierQuestions>\n\nexport function needsFlagship(answers: TaskTierAnswers): number {\n return (answers.needsFlagship as NoulAnswer).noul\n}\n\nfunction clip(s: string, max: number): string {\n return s.length > max ? `${s.slice(0, max)}…` : s\n}\n"],"mappings":"yFAAAA,ICAAC,IAyBO,IAAMC,EAAiB,YAkBvB,SAASC,EAAcC,EAAkC,CAC9D,MAAO,CACL,KAAMC,EAAKD,EAAM,QAAS,GAAI,EAC9B,MAAOA,EAAM,MACb,QAASA,EAAM,SAAW,KAC1B,yBAA0BA,EAAM,YAAc,GAC9C,GAAIA,EAAM,eAAiB,CAAE,eAAgBA,EAAM,cAAe,EAAI,CAAC,CACzE,CACF,CAEO,IAAME,EAAoB,CAC/B,cAAeC,EACb,sEACA,CACE,KACE,sYACF,MACE,sSACJ,CACF,CACF,EAIO,SAASC,EAAcC,EAAkC,CAC9D,OAAQA,EAAQ,cAA6B,IAC/C,CAEA,SAASJ,EAAKK,EAAWC,EAAqB,CAC5C,OAAOD,EAAE,OAASC,EAAM,GAAGD,EAAE,MAAM,EAAGC,CAAG,UAAOD,CAClD,CDdA,IAAME,EAAkB,GAKlBC,EAAe,GAAK,GAAK,IAIxB,SAASC,EACdC,EACAC,EACoB,CACpB,GAAID,IAAS,cAAe,CAC1B,IAAME,EAAQD,GAAS,cAAcD,CAAI,GAAKC,GAAS,WACvD,OAAOC,GAAS,kCAAkC,KAAKA,CAAK,EAAIA,EAAQ,OAE1E,GAAIF,IAAS,YAAa,CACxB,IAAME,EAAQD,GAAS,cAAcD,CAAI,EACzC,OAAOE,GAAS,cAAc,KAAKA,CAAK,EAAIA,EAAQ,OAGxD,CA6BA,eAAsBC,EAAeC,EAA0C,CAC7E,IAAMC,EAAO,CAACC,EAAgBC,EAAmB,QAAuB,CACtE,WAAY,GAAO,cAAeA,EAAG,OAAAD,CACvC,GAEA,GAAIF,EAAK,UAAY,SAAWA,EAAK,UAAY,UAAW,OAAOC,EAAK,8CAA8C,EAEtH,GAAI,CAACD,EAAK,WAAY,OAAOC,EAAK,6BAA6B,EAS/D,GAAID,EAAK,WAAY,CACnB,IAAMI,EAAOJ,EAAK,eAAiB,EAC7BK,EAAML,EAAK,YAAcN,EAC/B,GAAIU,EAAOC,EACT,OAAOJ,EAAK,mCAAmC,KAAK,MAAMG,EAAO,GAAI,mDAA8C,EAIvH,IAAIE,EACJ,GAAI,CACFA,EAAS,MAAMC,EACbC,EACAC,EAAcT,CAAI,EAClBU,EACA,CAEE,UAAW,CAAE,cAAe,CAAE,EAC9B,SAAU,CAAE,MAAOV,EAAK,MAAO,QAASA,EAAK,SAAW,SAAU,CACpE,CACF,CACF,MAAE,CACA,OAAOC,EAAK,gDAA2C,CACzD,CAEA,GAAI,CAACK,EAAQ,OAAOL,EAAK,sDAAiD,EAE1E,IAAME,EAAIQ,EAAcL,EAAO,OAA0B,EAMzD,GAAIA,EAAO,OAAS,SAClB,OAAOL,EAAK,uBAAkBE,GAAKH,EAAK,WAAaP,GAAmB,YAAc,WAAWU,EAAE,QAAQ,CAAC,KAAMA,CAAC,EAGrH,IAAMS,EAAYZ,EAAK,WAAaP,EACpC,OAAIU,GAAKS,EAAkBX,EAAK,uBAAuBE,EAAE,QAAQ,CAAC,KAAMA,CAAC,EAElE,CACL,MAAOH,EAAK,WACZ,WAAY,GACZ,cAAeG,EACf,OAAQ,oBAAoBA,EAAE,QAAQ,CAAC,WAAWS,aAAgBZ,EAAK,YACzE,CACF","names":["init_seat","init_questions","TASK_TIER_SEAT","taskTierState","input","clip","taskTierQuestions","noul","needsFlagship","answers","s","max","DOWNGRADE_BELOW","CACHE_TTL_MS","cheapModelForEngine","tier","routing","model","routeTaskModel","opts","keep","reason","p","idle","ttl","result","askSeat","TASK_TIER_SEAT","taskTierState","taskTierQuestions","needsFlagship","threshold"]}
@@ -1,44 +0,0 @@
1
- import{a as A,b as v,g as E}from"./chunk-67Q4DCPS.js";import{existsSync as l,mkdirSync as S,writeFileSync as h,readFileSync as k}from"fs";import{resolve as p}from"path";import{createHash as $}from"crypto";var f="<!-- agentx-managed: hash=",y=" -->";function R(t){return $("sha256").update(t.systemPrompt??"").update("\0").update(t.codegraph?"cg:1":"cg:0").digest("hex").slice(0,16)}function C(t){return`${f}${R(t)}${y}`}function j(t){let s=t.split(`
2
- `,1)[0]??"";return!s.startsWith(f)||!s.endsWith(y)?null:s.slice(f.length,s.length-y.length)}function D(t,s){let o=`${t} ${s.systemPrompt||""} ${s.name}`.toLowerCase();return/cod(e|ing|er)|develop|engineer|program/.test(o)?"coding":/pm|project.?manag|product|scrum/.test(o)?"pm":/devops|deploy|infra|ops|sre|ci.?cd/.test(o)?"devops":/qa|test|quality|forensic/.test(o)?"qa":"general"}function N(t,s,o){let n=D(t,s),e=[];switch(e.push(C(s)),e.push(`# ${s.name}`),e.push(""),e.push("@AGENTS.md"),e.push(""),s.systemPrompt&&s.systemPrompt.trim().length>0&&(e.push("## Role"),e.push(""),e.push(s.systemPrompt.trimEnd()),e.push("")),e.push("## Commands"),e.push(""),l(p(s.workspace,"package.json"))?(e.push("```bash"),e.push("npm install # install dependencies"),e.push("npm test # run tests"),e.push("npm run build # build project"),e.push("```")):l(p(s.workspace,"requirements.txt"))&&(e.push("```bash"),e.push("pip install -r requirements.txt"),e.push("pytest"),e.push("```")),e.push(""),e.push("## Conventions"),e.push(""),n){case"coding":e.push("- Write tests for new code before committing"),e.push("- Keep functions small and focused"),e.push("- Follow existing patterns in the codebase"),e.push("- Run tests before pushing");break;case"pm":e.push("- Keep responses concise (3-5 lines main message)"),e.push("- Reference issues with #IID and MRs with !IID"),e.push("- Update issue labels and milestones when relevant"),e.push("- Summarize decisions, don't narrate process");break;case"devops":e.push("- Always check service status before making changes"),e.push("- Never run destructive commands without confirmation"),e.push("- Log all deployment actions"),e.push("- Verify changes in staging before production");break;case"qa":e.push("- Document reproduction steps for every bug"),e.push("- Include expected vs actual behavior"),e.push("- Check both happy path and edge cases"),e.push("- Attach screenshots when reporting UI issues");break;default:e.push("- Be concise \u2014 lead with the answer, skip preamble"),e.push("- Follow existing patterns in the codebase")}return e.push(""),e.push("## Cross-Channel Messaging"),e.push(""),e.push("You can send messages to any channel proactively:"),e.push("```bash"),e.push(`curl -X POST http://localhost:${o}/send \\`),e.push(' -H "Content-Type: application/json" \\'),e.push(` -d '{"channel":"telegram","chatId":"<id>","text":"<message>","agentId":"${t}"}'`),e.push("```"),e.push("Channels: telegram, whatsapp, gitlab, discord"),e.push(""),e.push("## Typed actions \u2014 prefer these over free-form Bash"),e.push(""),e.push("Daemon-shipped typed actions cover the common needs. Each call appears in `/traces` as a structured step (input + output schema), unlike opaque `Bash`/`curl`/`Read` calls."),e.push(""),e.push("```bash"),e.push("# List available actions + their schemas"),e.push("agentx actions builtin # \u2192 catalog"),e.push("agentx actions builtin <name> --schema # \u2192 input/output schema"),e.push(""),e.push("# Invoke an action with typed JSON input"),e.push(`agentx actions builtin http.fetch --input '{"url":"https://example.com/data"}'`),e.push(`agentx actions builtin http.post --input '{"url":"https://x.tld/hook","body":{"k":"v"}}'`),e.push(`agentx actions builtin file.read_lines --input '{"path":"data/prices.json"}'`),e.push(`agentx actions builtin file.write_jsonl --input '{"path":"leads.jsonl","record":{"name":"\u2026","email":"\u2026"}}'`),e.push(`agentx actions builtin extract.structured --input '{"prompt":"<text>","schema":{"type":"object",\u2026}}'`),e.push(`agentx actions builtin rag.lexical --input '{"agentId":"${t}","query":"\u2026","limit":5}'`),e.push(`agentx actions builtin agent.call --input '{"agentId":"<local-agent>","message":"\u2026"}'`),e.push(`agentx actions builtin mesh.delegate --input '{"peer":"<peer-name>","agent":"<peer-agent>","message":"\u2026"}'`),e.push("```"),e.push(""),e.push("Rule of thumb:"),e.push("- HTTP fetch/POST \u2192 `http.fetch` / `http.post` (not `Bash curl`)"),e.push("- Reading a file you'll parse \u2192 `file.read_lines` (not `Bash cat`)"),e.push("- Appending a structured record \u2192 `file.write_jsonl` (not `Bash echo >>` or `Write` with model-formatted JSON)"),e.push("- Extracting fields from text into a typed object \u2192 `extract.structured` (not asking the model to format JSON in its reply)"),e.push("- Searching a doc corpus \u2192 `rag.lexical` (not `Read` over every file)"),e.push("- Calling another agent on this daemon \u2192 `agent.call` (fresh session by default; not composing `Bash curl` to /task)"),e.push("- Calling an agent on a peer mesh node \u2192 `mesh.delegate` (cross-machine; same fresh-session default)"),e.push(""),s.codegraph&&e.push(v()),e.join(`
3
- `)}var g=["Bash(npx prisma migrate reset*)","Bash(prisma migrate reset*)","Bash(npx prisma db push*)","Bash(prisma db push*)","Bash(dropdb*)"];function M(t,s){return`curl -s --max-time 3 -H 'Content-Type: application/json' --data-binary @- '${`http://127.0.0.1:${s}/guard/check?agent=${encodeURIComponent(t)}`}' 2>/dev/null || true`}function P(t,s){let o=M(t,s);return[{matcher:"Bash",hooks:[{type:"command",command:o,timeout:10}]},{matcher:"Write|Edit",hooks:[{type:"command",command:o,timeout:10}]}]}function B(t){return Array.isArray(t?.hooks)&&t.hooks.some(s=>typeof s?.command=="string"&&(s.command.includes("/guard/check")||s.command.includes("guard check")))}function O(t,s,o="19900"){let n={autoMemoryEnabled:!0,env:{CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS:"1"}};if(s.permissionMode==="bypassPermissions"?n.permissions={deny:[...g]}:s.permissionMode==="plan"?n.permissions={deny:[...g,"Bash(rm -rf *)","Bash(drop *)","Bash(DELETE *)"]}:n.permissions={deny:[...g,"Bash(rm -rf /)","Bash(> /dev/sda*)","Bash(mkfs*)"]},s.codegraph){let r=n.permissions||{},a=Array.isArray(r.allow)?r.allow:[];r.allow=[...new Set([...a,...A])],n.permissions=r}let e={};return e.PreToolUse=P(t,o),e.Notification=[{matcher:"",hooks:[{type:"command",command:`echo "[${t}] notification: $(jq -r '.type // "unknown"')" >> /tmp/agentx-${t}.log`}]}],e.PostToolUse=[{matcher:"Bash",hooks:[{type:"command",command:`jq -r '.tool_input.command // empty' >> /tmp/agentx-${t}-commands.log`}]}],e.SessionStart=[{matcher:"compact",hooks:[{type:"command",command:`echo "Reminder: You are ${s.name} (${t}). Follow the conventions in CLAUDE.md."`}]}],n.hooks=e,n}function U(t,s){let o=D(t,s),n=[];switch(o){case"coding":n.push({name:"testing.md",content:`---
4
- paths:
5
- - "**/*.test.{ts,tsx,js,jsx}"
6
- - "**/*.spec.{ts,tsx,js,jsx}"
7
- - "test/**/*"
8
- ---
9
-
10
- # Testing Rules
11
-
12
- - Every new function should have a corresponding test
13
- - Test both happy path and error cases
14
- - Use descriptive test names that explain what's being tested
15
- - Mock external dependencies, not internal modules
16
- `}),n.push({name:"code-quality.md",content:`# Code Quality
17
-
18
- - No console.log in production code (use a logger)
19
- - Handle errors explicitly \u2014 no empty catch blocks
20
- - Keep files under 300 lines
21
- - Extract repeated logic into shared utilities
22
- `});break;case"devops":n.push({name:"deployment-safety.md",content:`# Deployment Safety
23
-
24
- - Always verify staging before production
25
- - Check disk space and memory before deploying
26
- - Never expose secrets in logs or commits
27
- - Use rollback-safe deployment strategies
28
- `});break;case"pm":n.push({name:"communication.md",content:`# Communication Rules
29
-
30
- - Keep GitLab comments under 5 lines for the main message
31
- - Use <details> for verbose output (logs, commands, steps)
32
- - Reference issues with #IID, merge requests with !IID
33
- - Never mention Telegram handles on GitLab
34
- `});break;case"qa":n.push({name:"bug-reports.md",content:`# Bug Report Format
35
-
36
- Always include:
37
- 1. Steps to reproduce
38
- 2. Expected behavior
39
- 3. Actual behavior
40
- 4. Environment (browser, OS, version)
41
- 5. Screenshots if applicable
42
- `});break}return n}function _(t,s,o="19900",n=console.error){let e=[],r=[],a=s.workspace;if(!l(a))return n(`Workspace not found: ${a}`),{created:e,skipped:r};if(s.tier!=="claude-code"&&s.tier!=="codex-cli")return{created:e,skipped:r};let c=(i,m)=>{l(i)?r.push(i):(S(p(i,".."),{recursive:!0}),h(i,m),e.push(i))},u=(i,m,T)=>{if(!l(i)){S(p(i,".."),{recursive:!0}),h(i,m),e.push(i);return}let w;try{w=k(i,"utf8")}catch{r.push(i);return}let x=j(w);if(x===null){r.push(i);return}if(x===T){r.push(i);return}h(i,m),e.push(`${i} (refreshed)`)};if(c(p(a,"AGENTS.md"),E({name:s.name,id:t,role:s.systemPrompt?.split(`
43
- `)[0],workspace:a,tier:s.tier})),s.tier==="codex-cli")return e.length>0&&n(`[${t}] workspace setup: created ${e.length} file(s)`),{created:e,skipped:r};u(p(a,"CLAUDE.md"),N(t,s,o),R(s));let b=p(a,".claude/settings.json");c(b,JSON.stringify(O(t,s,o),null,2));let d=U(t,s);for(let i of d)c(p(a,".claude/rules",i.name),i.content);return e.length>0&&n(`[${t}] workspace setup: created ${e.length} file(s)`),{created:e,skipped:r}}function L(t,s){let o=p(t,".claude/settings.json");if(!l(o))return!1;try{let n=JSON.parse(k(o,"utf-8")),e=!1;for(let[r,a]of Object.entries(s))if(typeof a=="object"&&a!==null&&!Array.isArray(a)){n[r]||(n[r]={});for(let[c,u]of Object.entries(a))n[r][c]===void 0&&(n[r][c]=u,e=!0)}else n[r]===void 0&&(n[r]=a,e=!0);return e&&h(o,JSON.stringify(n,null,2)),e}catch{return!1}}function q(t,s,o="19900"){let n=p(t,".claude/settings.json");if(!l(n))return!1;try{let e=JSON.parse(k(n,"utf-8")),r=!1,a=e.permissions??={},c=Array.isArray(a.deny)?a.deny:a.deny=[];for(let i of g)c.includes(i)||(c.push(i),r=!0);let u=e.hooks??={},d=[...Array.isArray(u.PreToolUse)?u.PreToolUse.filter(i=>!B(i)):[],...P(s,o)];return JSON.stringify(u.PreToolUse??null)!==JSON.stringify(d)&&(u.PreToolUse=d,r=!0),r&&h(n,JSON.stringify(e,null,2)),r}catch{return!1}}function F(t,s="19900",o=console.error){let n=0,e=0,r=0;for(let[a,c]of Object.entries(t)){if(c.tier!=="claude-code"&&c.tier!=="codex-cli")continue;let u=_(a,c,s,o);n+=u.created.length,c.tier==="claude-code"&&L(c.workspace,{env:{CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS:"1"}})&&e++,c.tier==="claude-code"&&q(c.workspace,a,s)&&r++}n>0&&o(`Workspace setup: ${n} file(s) created across agent workspaces`),e>0&&o(`Workspace patch: ${e} workspace(s) updated with agent teams support`),r>0&&o(`Guardrails: ${r} workspace(s) backfilled with deny list + PreToolUse guard hook`)}export{j as a,N as b,_ as c,q as d,F as e};
44
- //# sourceMappingURL=chunk-I3SIXTJO.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/agents/workspace-setup.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync, readFileSync } from \"fs\"\nimport { resolve } from \"path\"\nimport { createHash } from \"crypto\"\nimport type { AgentDef } from \"@/daemon/config\"\nimport { generateAgentsMd } from \"./bootstrap\"\nimport { CODEGRAPH_TOOLS, codegraphClaudeMdSection } from \"./codegraph-bootstrap\"\n\n// Marker stamped into auto-generated CLAUDE.md so we can tell agentx-managed\n// files apart from user-edited ones. On daemon start, files with the marker\n// are silently regenerated when the systemPrompt hash changes; files without\n// the marker are treated as user-owned and never touched.\nconst MANAGED_MARKER_PREFIX = \"<!-- agentx-managed: hash=\"\nconst MANAGED_MARKER_SUFFIX = \" -->\"\n\n/** Hash inputs that drive the managed-CLAUDE.md content. Anything that\n * affects what `generateClaudeMd` emits should feed into this so flipping\n * the input also refreshes the file on next boot. Currently:\n * systemPrompt + codegraph flag (the codegraph instruction block is\n * conditional on it). */\nfunction managedHashInputs(def: Pick<AgentDef, \"systemPrompt\" | \"codegraph\">): string {\n return createHash(\"sha256\")\n .update(def.systemPrompt ?? \"\")\n .update(\"\\0\")\n .update(def.codegraph ? \"cg:1\" : \"cg:0\")\n .digest(\"hex\")\n .slice(0, 16)\n}\n\nfunction buildManagedMarker(def: Pick<AgentDef, \"systemPrompt\" | \"codegraph\">): string {\n return `${MANAGED_MARKER_PREFIX}${managedHashInputs(def)}${MANAGED_MARKER_SUFFIX}`\n}\n\n/**\n * Read the managed marker from an existing file's first line, if present.\n * Returns the hash (truncated sha256 hex) when found, null otherwise.\n * Files without the marker are user-managed and untouchable.\n */\nexport function readManagedHash(content: string): string | null {\n const firstLine = content.split(\"\\n\", 1)[0] ?? \"\"\n if (!firstLine.startsWith(MANAGED_MARKER_PREFIX)) return null\n if (!firstLine.endsWith(MANAGED_MARKER_SUFFIX)) return null\n return firstLine.slice(MANAGED_MARKER_PREFIX.length, firstLine.length - MANAGED_MARKER_SUFFIX.length)\n}\n\n// --- Workspace Setup: align agent workspaces with Claude Code best practices ---\n//\n// Generates on daemon start (if missing):\n// CLAUDE.md — project conventions, under 200 lines, @AGENTS.md import\n// AGENTS.md — agents.md spec for cross-tool compatibility\n// .claude/settings.json — hooks, permissions, auto memory\n// .claude/rules/ — path-specific rules based on agent role\n\n/** Detect agent role from systemPrompt or agent ID */\nfunction detectRole(agentId: string, def: AgentDef): \"coding\" | \"pm\" | \"devops\" | \"qa\" | \"general\" {\n const text = `${agentId} ${def.systemPrompt || \"\"} ${def.name}`.toLowerCase()\n if (/cod(e|ing|er)|develop|engineer|program/.test(text)) return \"coding\"\n if (/pm|project.?manag|product|scrum/.test(text)) return \"pm\"\n if (/devops|deploy|infra|ops|sre|ci.?cd/.test(text)) return \"devops\"\n if (/qa|test|quality|forensic/.test(text)) return \"qa\"\n return \"general\"\n}\n\n/** Generate CLAUDE.md for an agent workspace */\nexport function generateClaudeMd(agentId: string, def: AgentDef, daemonPort: string): string {\n const role = detectRole(agentId, def)\n const lines: string[] = []\n\n // Managed-marker on line 1 — used by setupWorkspace to detect stale\n // auto-generated files and regenerate them when systemPrompt changes.\n // User-edited files (no marker) are left alone forever.\n lines.push(buildManagedMarker(def))\n lines.push(`# ${def.name}`)\n lines.push(\"\")\n lines.push(\"@AGENTS.md\")\n lines.push(\"\")\n\n // Agent identity from systemPrompt — emitted verbatim. Truncating here would\n // silently drop instructions (e.g. multi-paragraph protocols), so we trade\n // a few extra tokens in CLAUDE.md for fidelity. The runtime path already\n // forwards the full systemPrompt via --append-system-prompt; this section\n // mirrors it for tools that read CLAUDE.md as workspace context.\n if (def.systemPrompt && def.systemPrompt.trim().length > 0) {\n lines.push(\"## Role\")\n lines.push(\"\")\n lines.push(def.systemPrompt.trimEnd())\n lines.push(\"\")\n }\n\n // Build/test commands\n lines.push(\"## Commands\")\n lines.push(\"\")\n if (existsSync(resolve(def.workspace, \"package.json\"))) {\n lines.push(\"```bash\")\n lines.push(\"npm install # install dependencies\")\n lines.push(\"npm test # run tests\")\n lines.push(\"npm run build # build project\")\n lines.push(\"```\")\n } else if (existsSync(resolve(def.workspace, \"requirements.txt\"))) {\n lines.push(\"```bash\")\n lines.push(\"pip install -r requirements.txt\")\n lines.push(\"pytest\")\n lines.push(\"```\")\n }\n lines.push(\"\")\n\n // Role-specific conventions\n lines.push(\"## Conventions\")\n lines.push(\"\")\n switch (role) {\n case \"coding\":\n lines.push(\"- Write tests for new code before committing\")\n lines.push(\"- Keep functions small and focused\")\n lines.push(\"- Follow existing patterns in the codebase\")\n lines.push(\"- Run tests before pushing\")\n break\n case \"pm\":\n lines.push(\"- Keep responses concise (3-5 lines main message)\")\n lines.push(\"- Reference issues with #IID and MRs with !IID\")\n lines.push(\"- Update issue labels and milestones when relevant\")\n lines.push(\"- Summarize decisions, don't narrate process\")\n break\n case \"devops\":\n lines.push(\"- Always check service status before making changes\")\n lines.push(\"- Never run destructive commands without confirmation\")\n lines.push(\"- Log all deployment actions\")\n lines.push(\"- Verify changes in staging before production\")\n break\n case \"qa\":\n lines.push(\"- Document reproduction steps for every bug\")\n lines.push(\"- Include expected vs actual behavior\")\n lines.push(\"- Check both happy path and edge cases\")\n lines.push(\"- Attach screenshots when reporting UI issues\")\n break\n default:\n lines.push(\"- Be concise — lead with the answer, skip preamble\")\n lines.push(\"- Follow existing patterns in the codebase\")\n }\n lines.push(\"\")\n\n // Cross-channel messaging capability\n lines.push(\"## Cross-Channel Messaging\")\n lines.push(\"\")\n lines.push(\"You can send messages to any channel proactively:\")\n lines.push(\"```bash\")\n lines.push(`curl -X POST http://localhost:${daemonPort}/send \\\\`)\n lines.push(` -H \"Content-Type: application/json\" \\\\`)\n lines.push(` -d '{\"channel\":\"telegram\",\"chatId\":\"<id>\",\"text\":\"<message>\",\"agentId\":\"${agentId}\"}'`)\n lines.push(\"```\")\n lines.push('Channels: telegram, whatsapp, gitlab, discord')\n lines.push(\"\")\n\n // Typed actions catalog — prefer these over free-form Bash/Read/Write/curl\n // when applicable. Each action's call lands in /traces as a structured\n // step (action: <name>, input: …, output: …) which is how observability\n // works in this codebase. Free-form Bash gets recorded as opaque shell\n // text, which is harder to debug and harder to compose into workflows.\n lines.push(\"## Typed actions — prefer these over free-form Bash\")\n lines.push(\"\")\n lines.push(\"Daemon-shipped typed actions cover the common needs. Each call appears in `/traces` as a structured step (input + output schema), unlike opaque `Bash`/`curl`/`Read` calls.\")\n lines.push(\"\")\n lines.push(\"```bash\")\n lines.push(\"# List available actions + their schemas\")\n lines.push(`agentx actions builtin # → catalog`)\n lines.push(`agentx actions builtin <name> --schema # → input/output schema`)\n lines.push(\"\")\n lines.push(\"# Invoke an action with typed JSON input\")\n lines.push(`agentx actions builtin http.fetch --input '{\"url\":\"https://example.com/data\"}'`)\n lines.push(`agentx actions builtin http.post --input '{\"url\":\"https://x.tld/hook\",\"body\":{\"k\":\"v\"}}'`)\n lines.push(`agentx actions builtin file.read_lines --input '{\"path\":\"data/prices.json\"}'`)\n lines.push(`agentx actions builtin file.write_jsonl --input '{\"path\":\"leads.jsonl\",\"record\":{\"name\":\"…\",\"email\":\"…\"}}'`)\n lines.push(`agentx actions builtin extract.structured --input '{\"prompt\":\"<text>\",\"schema\":{\"type\":\"object\",…}}'`)\n lines.push(`agentx actions builtin rag.lexical --input '{\"agentId\":\"${agentId}\",\"query\":\"…\",\"limit\":5}'`)\n lines.push(`agentx actions builtin agent.call --input '{\"agentId\":\"<local-agent>\",\"message\":\"…\"}'`)\n lines.push(`agentx actions builtin mesh.delegate --input '{\"peer\":\"<peer-name>\",\"agent\":\"<peer-agent>\",\"message\":\"…\"}'`)\n lines.push(\"```\")\n lines.push(\"\")\n lines.push(\"Rule of thumb:\")\n lines.push(\"- HTTP fetch/POST → `http.fetch` / `http.post` (not `Bash curl`)\")\n lines.push(\"- Reading a file you'll parse → `file.read_lines` (not `Bash cat`)\")\n lines.push(\"- Appending a structured record → `file.write_jsonl` (not `Bash echo >>` or `Write` with model-formatted JSON)\")\n lines.push(\"- Extracting fields from text into a typed object → `extract.structured` (not asking the model to format JSON in its reply)\")\n lines.push(\"- Searching a doc corpus → `rag.lexical` (not `Read` over every file)\")\n lines.push(\"- Calling another agent on this daemon → `agent.call` (fresh session by default; not composing `Bash curl` to /task)\")\n lines.push(\"- Calling an agent on a peer mesh node → `mesh.delegate` (cross-machine; same fresh-session default)\")\n lines.push(\"\")\n\n // CodeGraph instruction block — only when the agent opted in. See\n // src/agents/codegraph-bootstrap.ts for the section content; sourcing it\n // from one place keeps the wording consistent if upstream changes.\n if (def.codegraph) {\n lines.push(codegraphClaudeMdSection())\n }\n\n return lines.join(\"\\n\")\n}\n\n// Phase 0 stop-gap deny list for the destructive-action guardrails. Claude\n// Code deny globs are coarse (single trailing wildcard), so this is a blunt\n// backstop for the clearly-always-destructive prisma/dropdb classes that\n// caused the 2026-07-21 prod-wipe incident. The env-var-resolved, nuanced\n// matching (diff --shadow-database-url pointed at prod, pg_restore --clean,\n// migrate deploy) is done by the `agentx guard` PreToolUse hook, which runs\n// even under bypassPermissions (where deny rules may be skipped).\nconst GUARDRAIL_DENY = [\n \"Bash(npx prisma migrate reset*)\",\n \"Bash(prisma migrate reset*)\",\n \"Bash(npx prisma db push*)\",\n \"Bash(prisma db push*)\",\n \"Bash(dropdb*)\",\n]\n\n/**\n * The command the PreToolUse hook runs. This fires on EVERY Bash/Write/Edit\n * call, so it must be cheap: spawning `node dist/cli.js guard check` cost\n * ~300ms of interpreter + bundle boot per tool call. Instead we POST the\n * payload to the already-running daemon over loopback (~1ms) and pipe its\n * response straight back to Claude Code. `/guard/check` is loopback-only.\n *\n * The trailing `|| true` is load-bearing: a PreToolUse hook exiting 2 BLOCKS\n * the tool call, and curl uses exit 2 for its own init failures. Forcing\n * exit 0 means a curl/daemon problem can never masquerade as a policy deny.\n * If the daemon is unreachable we fail open — acceptable because the daemon\n * is what spawns the agents in the first place, so \"daemon down\" means there\n * are no agent tool calls to guard.\n */\nfunction guardHookCommand(agentId: string, daemonPort: string): string {\n const url = `http://127.0.0.1:${daemonPort}/guard/check?agent=${encodeURIComponent(agentId)}`\n return `curl -s --max-time 3 -H 'Content-Type: application/json' --data-binary @- '${url}' 2>/dev/null || true`\n}\n\n/** PreToolUse hook entries wiring the guard onto Bash + Write/Edit. */\nfunction guardPreToolUseHooks(agentId: string, daemonPort: string): unknown[] {\n const command = guardHookCommand(agentId, daemonPort)\n return [\n { matcher: \"Bash\", hooks: [{ type: \"command\", command, timeout: 10 }] },\n { matcher: \"Write|Edit\", hooks: [{ type: \"command\", command, timeout: 10 }] },\n ]\n}\n\n/** Is this hook entry one of ours? Matches both the current curl form and\n * the earlier `guard check` CLI form, so stale entries get replaced rather\n * than duplicated. */\nfunction isGuardEntry(entry: any): boolean {\n return (\n Array.isArray(entry?.hooks) &&\n entry.hooks.some(\n (h: any) =>\n typeof h?.command === \"string\" && (h.command.includes(\"/guard/check\") || h.command.includes(\"guard check\")),\n )\n )\n}\n\n/** Generate .claude/settings.json with hooks and permissions */\nfunction generateSettings(agentId: string, def: AgentDef, daemonPort: string = \"19900\"): Record<string, unknown> {\n const settings: Record<string, unknown> = {\n autoMemoryEnabled: true,\n env: {\n CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: \"1\",\n },\n }\n\n // Permission mode. The guardrail deny list is injected in ALL modes —\n // including bypassPermissions, which previously had none — as a stop-gap\n // backstop beneath the PreToolUse guard hook.\n if (def.permissionMode === \"bypassPermissions\") {\n settings[\"permissions\"] = { deny: [...GUARDRAIL_DENY] }\n } else if (def.permissionMode === \"plan\") {\n settings[\"permissions\"] = {\n deny: [...GUARDRAIL_DENY, \"Bash(rm -rf *)\", \"Bash(drop *)\", \"Bash(DELETE *)\"],\n }\n } else {\n // Default: deny destructive operations\n settings[\"permissions\"] = {\n deny: [\n ...GUARDRAIL_DENY,\n \"Bash(rm -rf /)\",\n \"Bash(> /dev/sda*)\",\n \"Bash(mkfs*)\",\n ],\n }\n }\n\n // Pre-authorize codegraph MCP tools so claude doesn't prompt for each\n // call. Even under bypassPermissions there's no harm — an explicit allow\n // list survives if the agent later switches to a stricter mode.\n if (def.codegraph) {\n const perms = (settings[\"permissions\"] as Record<string, unknown>) || {}\n const existing = Array.isArray(perms[\"allow\"]) ? (perms[\"allow\"] as string[]) : []\n perms[\"allow\"] = [...new Set([...existing, ...CODEGRAPH_TOOLS])]\n settings[\"permissions\"] = perms\n }\n\n // Hooks\n const hooks: Record<string, unknown[]> = {}\n\n // PreToolUse — destructive-action guardrails. Runs the guard on every Bash\n // and Write/Edit call; in warn mode it audits without blocking. This is the\n // primary enforcement chokepoint (works even under bypassPermissions).\n hooks[\"PreToolUse\"] = guardPreToolUseHooks(agentId, daemonPort)\n\n // Notification hook — log when agent needs input\n hooks[\"Notification\"] = [{\n matcher: \"\",\n hooks: [{\n type: \"command\",\n command: `echo \"[${agentId}] notification: $(jq -r '.type // \\\"unknown\\\"')\" >> /tmp/agentx-${agentId}.log`,\n }],\n }]\n\n // PostToolUse — log tool activity\n hooks[\"PostToolUse\"] = [{\n matcher: \"Bash\",\n hooks: [{\n type: \"command\",\n command: `jq -r '.tool_input.command // empty' >> /tmp/agentx-${agentId}-commands.log`,\n }],\n }]\n\n // SessionStart after compaction — re-inject key context\n hooks[\"SessionStart\"] = [{\n matcher: \"compact\",\n hooks: [{\n type: \"command\",\n command: `echo \"Reminder: You are ${def.name} (${agentId}). Follow the conventions in CLAUDE.md.\"`,\n }],\n }]\n\n settings[\"hooks\"] = hooks\n return settings\n}\n\n/** Generate .claude/rules/ files based on agent role */\nfunction generateRules(agentId: string, def: AgentDef): Array<{ name: string; content: string }> {\n const role = detectRole(agentId, def)\n const rules: Array<{ name: string; content: string }> = []\n\n switch (role) {\n case \"coding\":\n rules.push({\n name: \"testing.md\",\n content: `---\npaths:\n - \"**/*.test.{ts,tsx,js,jsx}\"\n - \"**/*.spec.{ts,tsx,js,jsx}\"\n - \"test/**/*\"\n---\n\n# Testing Rules\n\n- Every new function should have a corresponding test\n- Test both happy path and error cases\n- Use descriptive test names that explain what's being tested\n- Mock external dependencies, not internal modules\n`,\n })\n rules.push({\n name: \"code-quality.md\",\n content: `# Code Quality\n\n- No console.log in production code (use a logger)\n- Handle errors explicitly — no empty catch blocks\n- Keep files under 300 lines\n- Extract repeated logic into shared utilities\n`,\n })\n break\n\n case \"devops\":\n rules.push({\n name: \"deployment-safety.md\",\n content: `# Deployment Safety\n\n- Always verify staging before production\n- Check disk space and memory before deploying\n- Never expose secrets in logs or commits\n- Use rollback-safe deployment strategies\n`,\n })\n break\n\n case \"pm\":\n rules.push({\n name: \"communication.md\",\n content: `# Communication Rules\n\n- Keep GitLab comments under 5 lines for the main message\n- Use <details> for verbose output (logs, commands, steps)\n- Reference issues with #IID, merge requests with !IID\n- Never mention Telegram handles on GitLab\n`,\n })\n break\n\n case \"qa\":\n rules.push({\n name: \"bug-reports.md\",\n content: `# Bug Report Format\n\nAlways include:\n1. Steps to reproduce\n2. Expected behavior\n3. Actual behavior\n4. Environment (browser, OS, version)\n5. Screenshots if applicable\n`,\n })\n break\n }\n\n return rules\n}\n\n/**\n * Set up an agent workspace with CLI-agent best practices.\n * Only creates files that don't already exist (non-destructive).\n */\nexport function setupWorkspace(\n agentId: string,\n def: AgentDef,\n daemonPort: string = \"19900\",\n log: (...args: unknown[]) => void = console.error,\n): { created: string[]; skipped: string[] } {\n const created: string[] = []\n const skipped: string[] = []\n const workspace = def.workspace\n\n if (!existsSync(workspace)) {\n log(`Workspace not found: ${workspace}`)\n return { created, skipped }\n }\n\n // Only set up CLI-backed tier agents.\n if (def.tier !== \"claude-code\" && def.tier !== \"codex-cli\") {\n return { created, skipped }\n }\n\n const writeIfMissing = (path: string, content: string) => {\n if (existsSync(path)) {\n skipped.push(path)\n } else {\n mkdirSync(resolve(path, \"..\"), { recursive: true })\n writeFileSync(path, content)\n created.push(path)\n }\n }\n\n /**\n * Write a managed file with the agentx-managed marker. If the file already\n * exists and starts with our marker, regenerate when the embedded hash\n * doesn't match the current expected hash. Files without the marker are\n * treated as user-owned and left alone.\n */\n const writeManaged = (path: string, content: string, expectedHash: string) => {\n if (!existsSync(path)) {\n mkdirSync(resolve(path, \"..\"), { recursive: true })\n writeFileSync(path, content)\n created.push(path)\n return\n }\n let existing: string\n try {\n existing = readFileSync(path, \"utf8\")\n } catch {\n skipped.push(path)\n return\n }\n const existingHash = readManagedHash(existing)\n if (existingHash === null) {\n // User-edited (no marker) — never overwrite.\n skipped.push(path)\n return\n }\n if (existingHash === expectedHash) {\n skipped.push(path)\n return\n }\n writeFileSync(path, content)\n created.push(`${path} (refreshed)`)\n }\n\n // AGENTS.md (agents.md spec) — first-line of systemPrompt only, currently\n // not managed-marker'd. Left as writeIfMissing for now.\n writeIfMissing(\n resolve(workspace, \"AGENTS.md\"),\n generateAgentsMd({ name: def.name, id: agentId, role: def.systemPrompt?.split(\"\\n\")[0], workspace, tier: def.tier }),\n )\n\n if (def.tier === \"codex-cli\") {\n if (created.length > 0) {\n log(`[${agentId}] workspace setup: created ${created.length} file(s)`)\n }\n return { created, skipped }\n }\n\n // CLAUDE.md — managed: silently refreshed when systemPrompt OR codegraph\n // flag changes (both feed into the marker hash via managedHashInputs).\n writeManaged(\n resolve(workspace, \"CLAUDE.md\"),\n generateClaudeMd(agentId, def, daemonPort),\n managedHashInputs(def),\n )\n\n // .claude/settings.json\n const settingsPath = resolve(workspace, \".claude/settings.json\")\n writeIfMissing(settingsPath, JSON.stringify(generateSettings(agentId, def, daemonPort), null, 2))\n\n // .claude/rules/\n const rules = generateRules(agentId, def)\n for (const rule of rules) {\n writeIfMissing(resolve(workspace, \".claude/rules\", rule.name), rule.content)\n }\n\n if (created.length > 0) {\n log(`[${agentId}] workspace setup: created ${created.length} file(s)`)\n }\n\n return { created, skipped }\n}\n\n/**\n * Patch existing .claude/settings.json to add missing keys (non-destructive).\n * Used to enable new features (like agent teams) on workspaces that already have settings.\n */\nfunction patchSettings(workspace: string, patches: Record<string, unknown>): boolean {\n const settingsPath = resolve(workspace, \".claude/settings.json\")\n if (!existsSync(settingsPath)) return false\n\n try {\n const existing = JSON.parse(readFileSync(settingsPath, \"utf-8\"))\n let changed = false\n\n for (const [key, value] of Object.entries(patches)) {\n if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n // Merge objects (e.g. env: { KEY: \"value\" })\n if (!existing[key]) existing[key] = {}\n for (const [subKey, subValue] of Object.entries(value as Record<string, unknown>)) {\n if (existing[key][subKey] === undefined) {\n existing[key][subKey] = subValue\n changed = true\n }\n }\n } else if (existing[key] === undefined) {\n existing[key] = value\n changed = true\n }\n }\n\n if (changed) {\n writeFileSync(settingsPath, JSON.stringify(existing, null, 2))\n }\n return changed\n } catch {\n return false\n }\n}\n\n/**\n * Backfill the destructive-action guardrails into an EXISTING settings.json:\n * merge the stop-gap deny list into permissions.deny (dedup, no clobber) and\n * add the PreToolUse guard hook if absent. writeIfMissing never touches\n * existing files, so live workspaces need this explicit patch. Idempotent —\n * re-running is a no-op once both are present. Returns true if it changed.\n */\nexport function patchGuardrails(workspace: string, agentId: string, daemonPort: string = \"19900\"): boolean {\n const settingsPath = resolve(workspace, \".claude/settings.json\")\n if (!existsSync(settingsPath)) return false\n\n try {\n const existing = JSON.parse(readFileSync(settingsPath, \"utf-8\"))\n let changed = false\n\n // 1. deny list\n const perms = (existing.permissions ??= {})\n const deny: string[] = Array.isArray(perms.deny) ? perms.deny : (perms.deny = [])\n for (const rule of GUARDRAIL_DENY) {\n if (!deny.includes(rule)) {\n deny.push(rule)\n changed = true\n }\n }\n\n // 2. PreToolUse guard hook. Drop any previous guard entries (including\n // the older node-CLI form) and re-add the current one, so a changed\n // port or invocation style self-heals instead of duplicating.\n const hooks = (existing.hooks ??= {})\n const others = Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse.filter((e: any) => !isGuardEntry(e)) : []\n const desired = [...others, ...guardPreToolUseHooks(agentId, daemonPort)]\n if (JSON.stringify(hooks.PreToolUse ?? null) !== JSON.stringify(desired)) {\n hooks.PreToolUse = desired\n changed = true\n }\n\n if (changed) writeFileSync(settingsPath, JSON.stringify(existing, null, 2))\n return changed\n } catch {\n return false\n }\n}\n\n/**\n * Set up all agent workspaces on daemon start.\n */\nexport function setupAllWorkspaces(\n agents: Record<string, AgentDef>,\n daemonPort: string = \"19900\",\n log: (...args: unknown[]) => void = console.error,\n): void {\n let totalCreated = 0\n let totalPatched = 0\n let totalGuarded = 0\n for (const [id, def] of Object.entries(agents)) {\n if (def.tier !== \"claude-code\" && def.tier !== \"codex-cli\") continue\n const result = setupWorkspace(id, def, daemonPort, log)\n totalCreated += result.created.length\n\n // Patch existing settings with new features (agent teams, etc.)\n if (def.tier === \"claude-code\" && patchSettings(def.workspace, {\n env: { CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: \"1\" },\n })) {\n totalPatched++\n }\n\n // Backfill destructive-action guardrails (deny list + PreToolUse hook)\n // onto existing workspaces.\n if (def.tier === \"claude-code\" && patchGuardrails(def.workspace, id, daemonPort)) {\n totalGuarded++\n }\n }\n if (totalCreated > 0) {\n log(`Workspace setup: ${totalCreated} file(s) created across agent workspaces`)\n }\n if (totalPatched > 0) {\n log(`Workspace patch: ${totalPatched} workspace(s) updated with agent teams support`)\n }\n if (totalGuarded > 0) {\n log(`Guardrails: ${totalGuarded} workspace(s) backfilled with deny list + PreToolUse guard hook`)\n }\n}\n"],"mappings":"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","names":["existsSync","mkdirSync","writeFileSync","readFileSync","resolve","createHash","MANAGED_MARKER_PREFIX","MANAGED_MARKER_SUFFIX","managedHashInputs","def","createHash","buildManagedMarker","readManagedHash","content","firstLine","detectRole","agentId","text","generateClaudeMd","daemonPort","role","lines","existsSync","resolve","codegraphClaudeMdSection","GUARDRAIL_DENY","guardHookCommand","guardPreToolUseHooks","command","isGuardEntry","entry","h","generateSettings","settings","perms","existing","CODEGRAPH_TOOLS","hooks","generateRules","rules","setupWorkspace","log","created","skipped","workspace","writeIfMissing","path","mkdirSync","writeFileSync","writeManaged","expectedHash","readFileSync","existingHash","generateAgentsMd","settingsPath","rule","patchSettings","patches","changed","key","value","subKey","subValue","patchGuardrails","deny","desired","e","setupAllWorkspaces","agents","totalCreated","totalPatched","totalGuarded","id","result"]}