borgmcp 1.1.13 → 1.1.15

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.
package/dist/index.js CHANGED
@@ -1,38 +1,38 @@
1
1
  #!/usr/bin/env node
2
- import{Server as ee}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as te}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as oe,ListToolsRequestSchema as re,ListPromptsRequestSchema as ne,GetPromptRequestSchema as se}from"@modelcontextprotocol/sdk/types.js";import{assertRoleMatches as ie}from"./role-match.js";import{getCubeInfo as ae,getRoleInfo as L,getRoleInfoByName as ce,getRoster as le,readLog as de,appendLog as ue,submitReport as pe,fetchReports as me,ackLogEntry as be,recordDecision as fe,listDecisions as ge,regen as O,listCubes as he,createCube as _e,updateCube as B,deleteCube as ye,createRole as we,updateRole as xe,patchRoleSection as C,patchTaxonomyClass as F,deleteRole as $e,reassignDrone as ve,evictDrone as ke,getCube as $,checkSubscriptionStatus as Re,createBillingPortalSession as Ee,createSubscription as Ce,syncRoles as Se,applyTemplate as Te,whoami as Ie,roleRationale as qe,getValidToken as De}from"./remote-client.js";import{startHealthBeatTick as Pe}from"./health-beat.js";import{getTemplate as S,listTemplateNames as T,resolveCubeDirectiveForCreate as Ue,resolveCubeDirectiveForApply as Me,resolveMessageTaxonomyForCreate as je}from"./templates.js";import{activeCubeWithFreshRegenIdentity as H,getActiveCube as h,setActiveCube as W,findProjectRoot as I,inboxPathForDrone as q}from"./cubes.js";import{monitorStateRootForWorktree as D}from"./inbox-monitor.js";import{addSessionStartHook as Ne,addUserPromptSubmitHook as Ae}from"./config-utils.js";import{humanAgo as V,formatLogEntryMarkdown as Le,formatRegenMarkdown as z,getDronePlaybook as Oe,getDronePlaybookChapter as Be,nullTaxonomyTip as Fe,regenWakePathDroneLabel as He}from"./regen-format.js";import{startLogStream as We,getStreamStatus as P}from"./log-stream.js";import{isMcpReadinessProbe as Ve}from"./readiness-probe.js";import{runMcpStartupServices as ze}from"./startup-services.js";import{TOOL_MANIFEST as Ke}from"./tool-manifest.js";import{DOCS_SECTIONS as Je,matchDocsSections as Qe,formatDocsIndex as Ye}from"./docs-sections.js";import{renderRoleList as Ge}from"./list-roles-render.js";import{filterToolsForRole as Xe}from"./tool-scope.js";import{getPackageVersion as v,getOnDiskVersion as Ze,handleVersionFlag as et}from"./version.js";import{renderStreamStatus as tt,checkInboxMonitorHealthy as U,formatWakePathPrefix as ot,shouldShowWakePathWarning as rt}from"./stream-status.js";import{formatRoleAgentLabel as nt,formatWorkingRepoLabel as st,renderRoster as it}from"./roster-render.js";import{resolveWorkingRepo as K}from"./working-repo.js";import{resolveDroneIdByLabel as at,isUuidShape as ct}from"./evict-drone.js";import{authRecoveryMessage as lt}from"./auth-recovery.js";import{DroneEvictedError as dt,DroneFrozenError as ut,formatEvictedToolResult as pt,formatFrozenToolResult as mt}from"./drone-lifecycle.js";import{classifyInSessionAssimilate as bt,reattachOnlyRefusal as ft,reattachFailureMessage as gt}from"./assimilate-guard.js";import{gateAllowsActivation as ht,borgSessionToolNotice as _t}from"./launch-gate.js";import{renderSyncRolesResult as yt}from"./sync-roles-render.js";import{initConsolePrefix as wt,consolePrefix as k}from"./console-prefix.js";import{isCodexRemoteWakeEnabled as xt,resolveSessionAgentKind as M,probeCodexBridgeArmed as $t}from"./codex-app-wake.js";import{connectOpenCodeDrone as vt,injectOpenCodeEntry as kt,probeOpenCodeDroneArmed as Rt,computeOpenCodePort as Et}from"./opencode-drone.js";import{installBorgPlugin as Ct}from"./opencode-plugin.js";import{setModuleInjectOpenCode as St}from"./log-stream.js";import{lifecycleSignalForMessage as Tt,recordLifecycleLog as J,shouldSuppressLifecycleLog as It}from"./lifecycle-log-guard.js";import{normalizeDirectLogRecipients as qt}from"./direct-log.js";import Q from"open";import Dt from"os";function Pt(){try{const b=Dt.hostname();return b&&b.trim()?b.trim().slice(0,255):null}catch{return null}}async function Y(b,y){return await Te(b,y.name)}async function _(){const b=await h();if(!b)throw new Error("Not assimilated to a cube. Use borg_assimilate <cube-name> first.");return b}async function Ut(){et();const b=Ve();await ze(b,{sessionStartHook:()=>{Ne()},auditHook:()=>{Ae()},sseStream:()=>{We()},openCode:async()=>{Ct();const l=await h();if(l&&process.env.BORG_OPENCODE==="1"){const o=`http://127.0.0.1:${Et(l.droneId)}`;await vt({serverUrl:o,directory:process.cwd(),droneLabel:l.droneLabel,cubeName:l.name}),St(kt)}},healthBeat:()=>{Pe({getActiveCube:h,getStreamConnected:()=>P().connected,getInboxPath:l=>q(l.cubeId,l.droneId),checkMonitor:l=>U(l,D(I())),isCodexRemoteWake:xt,probeBridgeArmed:l=>$t({cubeId:l.cubeId,droneId:l.droneId}),probeOpenCodeDrone:()=>Rt(),resolveAgentKind:M,resolveHostname:Pt,resolveVersion:v,getToken:De,fetchImpl:globalThis.fetch.bind(globalThis)})}});const y=new ee({name:"borg-mcp-client",version:v()},{capabilities:{tools:{},prompts:{}}}),j=Ke;y.setRequestHandler(re,async()=>{let l=null;try{const u=await h();u&&(l={roleName:u.roleName,roleClass:u.roleClass,isHumanSeat:u.isHumanSeat})}catch{l=null}return{tools:Xe(j,l)}}),y.setRequestHandler(oe,async l=>{let{name:u,arguments:o}=l.params;if(u==="borg_describe-tool"){const e=typeof o?.name=="string"?o.name:"",t=j.find(r=>r.name===e);return t?{content:[{type:"text",text:JSON.stringify({name:t.name,description:t.description,inputSchema:t.inputSchema},null,2)}]}:{content:[{type:"text",text:`Unknown borg tool: ${e||"(none)"}. Pass { name: "<borg_tool>" }.`}],isError:!0}}if(u==="borg_tool"){const e=typeof o?.name=="string"?o.name:"";if(!e||e==="borg_tool"||e==="borg_describe-tool")return{content:[{type:"text",text:'borg_tool: pass { name: "<borg_tool>", arguments: {...} } naming a real borg tool (not the dispatcher itself).'}],isError:!0};o=o?.arguments&&typeof o.arguments=="object"?o.arguments:{},u=e}if(!ht(`tool ${u}`))return{content:[{type:"text",text:_t(u)}],isError:!0};try{switch(u){case"borg_regen":{const e=await h();if(!e)return{content:[{type:"text",text:'Not connected to a cube. Use `borg_assimilate cube_name="<name>"` to join one.'}]};const t=typeof o?.since=="string"?o.since:void 0,r=o?.mode==="lite"?"lite":"full",n=typeof o?.model=="string"?o.model:void 0,i=await O(e.sessionToken,e.apiUrl,{since:t,reportedModel:n,workingRepo:K()}),s=H(e,i);s!==e&&await W(s);const a=P(),c=q(s.cubeId,s.droneId),m=D(I()),f=M()==="claude"?U(c,m):!0,d=rt(a,f)?ot({inboxPath:c,monitorStateRoot:m,droneLabel:He(i,s.droneLabel),cubeName:s.name}):"";let g="";try{const w=v(),x=Ze();if(w!=="unknown"&&x!=="unknown"&&x!==w){const[R,N,X]=w.split(".").map(Number),[E,A,Z]=x.split(".").map(Number);(E>R||E===R&&A>N||E===R&&A===N&&Z>X)&&(g=`## \u{1F504} borgmcp ${x} installed \u2014 run /mcp and reconnect (or restart Claude Code) to apply. Currently running ${w}.
2
+ import{Server as ee}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as te}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as oe,ListToolsRequestSchema as re,ListPromptsRequestSchema as ne,GetPromptRequestSchema as se}from"@modelcontextprotocol/sdk/types.js";import{assertRoleMatches as ie}from"./role-match.js";import{getCubeInfo as ae,getRoleInfo as L,getRoleInfoByName as ce,getRoster as de,readLog as le,appendLog as ue,submitReport as pe,fetchReports as me,ackLogEntry as be,recordDecision as fe,removeDecision as ge,listDecisions as he,regen as O,listCubes as _e,createCube as ye,updateCube as B,deleteCube as we,createRole as xe,updateRole as $e,patchRoleSection as C,patchTaxonomyClass as F,deleteRole as ve,reassignDrone as ke,evictDrone as Re,getCube as $,checkSubscriptionStatus as Ee,createBillingPortalSession as Ce,createSubscription as Se,syncRoles as Te,applyTemplate as Ie,whoami as qe,roleRationale as De,getValidToken as Pe}from"./remote-client.js";import{startHealthBeatTick as Ue}from"./health-beat.js";import{getTemplate as S,listTemplateNames as T,resolveCubeDirectiveForCreate as Me,resolveCubeDirectiveForApply as Ne,resolveMessageTaxonomyForCreate as je}from"./templates.js";import{activeCubeWithFreshRegenIdentity as H,getActiveCube as _,setActiveCube as W,findProjectRoot as I,inboxPathForDrone as q}from"./cubes.js";import{monitorStateRootForWorktree as D}from"./inbox-monitor.js";import{addSessionStartHook as Ae,addUserPromptSubmitHook as Le}from"./config-utils.js";import{humanAgo as V,formatLogEntryMarkdown as Oe,formatRegenMarkdown as z,getDronePlaybook as Be,getDronePlaybookChapter as Fe,nullTaxonomyTip as He,regenWakePathDroneLabel as We}from"./regen-format.js";import{startLogStream as Ve,getStreamStatus as P}from"./log-stream.js";import{isMcpReadinessProbe as ze}from"./readiness-probe.js";import{runMcpStartupServices as Ke}from"./startup-services.js";import{TOOL_MANIFEST as Je}from"./tool-manifest.js";import{DOCS_SECTIONS as Qe,matchDocsSections as Ye,formatDocsIndex as Ge}from"./docs-sections.js";import{renderRoleList as Xe}from"./list-roles-render.js";import{filterToolsForRole as Ze}from"./tool-scope.js";import{getPackageVersion as v,getOnDiskVersion as et,handleVersionFlag as tt}from"./version.js";import{renderStreamStatus as ot,checkInboxMonitorHealthy as U,formatWakePathPrefix as rt,shouldShowWakePathWarning as nt}from"./stream-status.js";import{formatRoleAgentLabel as st,formatWorkingRepoLabel as it,renderRoster as at}from"./roster-render.js";import{resolveWorkingRepo as K}from"./working-repo.js";import{resolveDroneIdByLabel as ct,isUuidShape as dt}from"./evict-drone.js";import{authRecoveryMessage as lt}from"./auth-recovery.js";import{DroneEvictedError as ut,DroneFrozenError as pt,formatEvictedToolResult as mt,formatFrozenToolResult as bt}from"./drone-lifecycle.js";import{classifyInSessionAssimilate as ft,reattachOnlyRefusal as gt,reattachFailureMessage as ht}from"./assimilate-guard.js";import{gateAllowsActivation as _t,borgSessionToolNotice as yt}from"./launch-gate.js";import{renderSyncRolesResult as wt}from"./sync-roles-render.js";import{initConsolePrefix as xt,consolePrefix as k}from"./console-prefix.js";import{isCodexRemoteWakeEnabled as $t,resolveSessionAgentKind as M,probeCodexBridgeArmed as vt}from"./codex-app-wake.js";import{connectOpenCodeDrone as kt,injectOpenCodeEntry as Rt,probeOpenCodeDroneArmed as Et,computeOpenCodePort as Ct}from"./opencode-drone.js";import{installBorgPlugin as St}from"./opencode-plugin.js";import{setModuleInjectOpenCode as Tt}from"./log-stream.js";import{lifecycleSignalForMessage as It,recordLifecycleLog as J,shouldSuppressLifecycleLog as qt}from"./lifecycle-log-guard.js";import{normalizeDirectLogRecipients as Dt}from"./direct-log.js";import Q from"open";import Pt from"os";function Ut(){try{const b=Pt.hostname();return b&&b.trim()?b.trim().slice(0,255):null}catch{return null}}async function Y(b,y){return await Ie(b,y.name)}async function h(){const b=await _();if(!b)throw new Error("Not assimilated to a cube. Use borg_assimilate <cube-name> first.");return b}async function Mt(){tt();const b=ze();await Ke(b,{sessionStartHook:()=>{Ae()},auditHook:()=>{Le()},sseStream:()=>{Ve()},openCode:async()=>{St();const d=await _();if(d&&process.env.BORG_OPENCODE==="1"){const o=`http://127.0.0.1:${Ct(d.droneId)}`;await kt({serverUrl:o,directory:process.cwd(),droneLabel:d.droneLabel,cubeName:d.name}),Tt(Rt)}},healthBeat:()=>{Ue({getActiveCube:_,getStreamConnected:()=>P().connected,getInboxPath:d=>q(d.cubeId,d.droneId),checkMonitor:d=>U(d,D(I())),isCodexRemoteWake:$t,probeBridgeArmed:d=>vt({cubeId:d.cubeId,droneId:d.droneId}),probeOpenCodeDrone:()=>Et(),resolveAgentKind:M,resolveHostname:Ut,resolveVersion:v,getToken:Pe,fetchImpl:globalThis.fetch.bind(globalThis)})}});const y=new ee({name:"borg-mcp-client",version:v()},{capabilities:{tools:{},prompts:{}}}),N=Je;y.setRequestHandler(re,async()=>{let d=null;try{const u=await _();u&&(d={roleName:u.roleName,roleClass:u.roleClass,isHumanSeat:u.isHumanSeat})}catch{d=null}return{tools:Ze(N,d)}}),y.setRequestHandler(oe,async d=>{let{name:u,arguments:o}=d.params;if(u==="borg_describe-tool"){const e=typeof o?.name=="string"?o.name:"",t=N.find(r=>r.name===e);return t?{content:[{type:"text",text:JSON.stringify({name:t.name,description:t.description,inputSchema:t.inputSchema},null,2)}]}:{content:[{type:"text",text:`Unknown borg tool: ${e||"(none)"}. Pass { name: "<borg_tool>" }.`}],isError:!0}}if(u==="borg_tool"){const e=typeof o?.name=="string"?o.name:"";if(!e||e==="borg_tool"||e==="borg_describe-tool")return{content:[{type:"text",text:'borg_tool: pass { name: "<borg_tool>", arguments: {...} } naming a real borg tool (not the dispatcher itself).'}],isError:!0};o=o?.arguments&&typeof o.arguments=="object"?o.arguments:{},u=e}if(!_t(`tool ${u}`))return{content:[{type:"text",text:yt(u)}],isError:!0};try{switch(u){case"borg_regen":{const e=await _();if(!e)return{content:[{type:"text",text:'Not connected to a cube. Use `borg_assimilate cube_name="<name>"` to join one.'}]};const t=typeof o?.since=="string"?o.since:void 0,r=o?.mode==="lite"?"lite":"full",n=typeof o?.model=="string"?o.model:void 0,i=await O(e.sessionToken,e.apiUrl,{since:t,reportedModel:n,workingRepo:K()}),s=H(e,i);s!==e&&await W(s);const a=P(),c=q(s.cubeId,s.droneId),m=D(I()),f=M()==="claude"?U(c,m):!0,l=nt(a,f)?rt({inboxPath:c,monitorStateRoot:m,droneLabel:We(i,s.droneLabel),cubeName:s.name}):"";let g="";try{const w=v(),x=et();if(w!=="unknown"&&x!=="unknown"&&x!==w){const[R,j,X]=w.split(".").map(Number),[E,A,Z]=x.split(".").map(Number);(E>R||E===R&&A>j||E===R&&A===j&&Z>X)&&(g=`## \u{1F504} borgmcp ${x} installed \u2014 run /mcp and reconnect (or restart Claude Code) to apply. Currently running ${w}.
3
3
 
4
- `)}}catch{}return{content:[{type:"text",text:g+d+z(i,{mode:r})}]}}case"borg_subscribe":return{content:[{type:"text",text:`Complete your subscription at: ${await Ce()}`}]};case"borg_upgrade-subscription":{const e=await Ee();try{await Q(e)}catch{}return{content:[{type:"text",text:`Manage your Borg MCP subscription at: ${e}`}]}}case"borg_subscription_status":{const e=await Re();return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}case"borg_open_dashboard":{const e="https://borgmcp.ai/dashboard";return await Q(e),{content:[{type:"text",text:`\u25FC Opened dashboard in browser: ${e}`}]}}case"borg_assimilate":{const e=o?.cube_name;if(!e)throw new Error("cube_name is required");const t=await h(),r=bt(t,e);if(r.kind!=="reattach")return{content:[{type:"text",text:ft(r,e)}],isError:!0};try{const n=await O(t.sessionToken,t.apiUrl,{workingRepo:K()}),i=H(t,n);return i!==t&&await W(i),{content:[{type:"text",text:[`# Re-attached to cube: ${i.name}`,"",`**Drone label:** ${i.droneLabel}`,"**Seat:** existing identity reused \u2014 no new drone minted (gh#780)","",""].join(`
5
- `)+z(n,{mode:"full"})}]}}catch(n){const i=gt(n??{});if(!i)throw n;return{content:[{type:"text",text:i}],isError:!0}}}case"borg_version":return{content:[{type:"text",text:`borgmcp ${v()}`}]};case"borg_playbook":return{content:[{type:"text",text:Be()}]};case"borg_docs":{const e=typeof o?.topic=="string"?o.topic.trim():"",t=e?Qe(e):[],r=t.length>0?t:Je;return{content:[{type:"text",text:`${e&&t.length>0?`Best-matching docs section(s) for "${e}" \u2014 WebFetch the URL for the full page:`:e?`No exact match for "${e}". Full Borg MCP docs index \u2014 WebFetch the URL you need:`:"Borg MCP docs index \u2014 WebFetch the URL of the section you need:"}
4
+ `)}}catch{}return{content:[{type:"text",text:g+l+z(i,{mode:r})}]}}case"borg_subscribe":return{content:[{type:"text",text:`Complete your subscription at: ${await Se()}`}]};case"borg_upgrade-subscription":{const e=await Ce();try{await Q(e)}catch{}return{content:[{type:"text",text:`Manage your Borg MCP subscription at: ${e}`}]}}case"borg_subscription_status":{const e=await Ee();return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}case"borg_open_dashboard":{const e="https://borgmcp.ai/dashboard";return await Q(e),{content:[{type:"text",text:`\u25FC Opened dashboard in browser: ${e}`}]}}case"borg_assimilate":{const e=o?.cube_name;if(!e)throw new Error("cube_name is required");const t=await _(),r=ft(t,e);if(r.kind!=="reattach")return{content:[{type:"text",text:gt(r,e)}],isError:!0};try{const n=await O(t.sessionToken,t.apiUrl,{workingRepo:K()}),i=H(t,n);return i!==t&&await W(i),{content:[{type:"text",text:[`# Re-attached to cube: ${i.name}`,"",`**Drone label:** ${i.droneLabel}`,"**Seat:** existing identity reused \u2014 no new drone minted (gh#780)","",""].join(`
5
+ `)+z(n,{mode:"full"})}]}}catch(n){const i=ht(n??{});if(!i)throw n;return{content:[{type:"text",text:i}],isError:!0}}}case"borg_version":return{content:[{type:"text",text:`borgmcp ${v()}`}]};case"borg_playbook":return{content:[{type:"text",text:Fe()}]};case"borg_docs":{const e=typeof o?.topic=="string"?o.topic.trim():"",t=e?Ye(e):[],r=t.length>0?t:Qe;return{content:[{type:"text",text:`${e&&t.length>0?`Best-matching docs section(s) for "${e}" \u2014 WebFetch the URL for the full page:`:e?`No exact match for "${e}". Full Borg MCP docs index \u2014 WebFetch the URL you need:`:"Borg MCP docs index \u2014 WebFetch the URL of the section you need:"}
6
6
 
7
- ${Ye(r)}`}]}}case"borg_whoami":{const e=await _(),t=await Ie(e.sessionToken,e.apiUrl);return{content:[{type:"text",text:JSON.stringify(t,null,2)}]}}case"borg_cube":{const e=await _(),[{cube:t,roles:r}]=await Promise.all([ae(e.sessionToken,e.apiUrl),L(e.sessionToken,e.apiUrl)]),n=[];n.push(`# Cube: ${t.name}`),n.push(""),n.push("## Cube directive"),n.push(t.cube_directive||"_(none)_"),n.push("");const i=Fe(t.message_taxonomy);if(i&&(n.push(i),n.push("")),n.push("## Roles in this cube"),!r.length)n.push("_(no roles defined)_");else{for(const s of r){const a=[s.role_class==="queen"?"Queen":null,s.is_human_seat?"human-seat":null,s.is_default?"default":null,s.is_mandatory?"mandatory":null].filter(Boolean).join(", "),c=a?` (${a})`:"",m=s.short_description||"_(no description)_";n.push(`- **${s.name}**${c} \u2014 ${m}`)}n.push(""),n.push("_(Coordinator-class drones can fetch role IDs via `borg_list-roles` for use with `borg_reassign-drone`.)_")}return n.push(""),n.push(Oe()),{content:[{type:"text",text:n.join(`
8
- `)}]}}case"borg_role":{const e=await _(),t=typeof o?.role=="string"?o.role.trim():"";if(t){const{role:i}=await ce(e.sessionToken,e.apiUrl,t);return ie(t,i),{content:[{type:"text",text:[`# Role: ${i.name}`,"",i.detailed_description||"_(no detailed description set)_"].join(`
7
+ ${Ge(r)}`}]}}case"borg_whoami":{const e=await h(),t=await qe(e.sessionToken,e.apiUrl);return{content:[{type:"text",text:JSON.stringify(t,null,2)}]}}case"borg_cube":{const e=await h(),[{cube:t,roles:r}]=await Promise.all([ae(e.sessionToken,e.apiUrl),L(e.sessionToken,e.apiUrl)]),n=[];n.push(`# Cube: ${t.name}`),n.push(""),n.push("## Cube directive"),n.push(t.cube_directive||"_(none)_"),n.push("");const i=He(t.message_taxonomy);if(i&&(n.push(i),n.push("")),n.push("## Roles in this cube"),!r.length)n.push("_(no roles defined)_");else{for(const s of r){const a=[s.role_class==="queen"?"Queen":null,s.is_human_seat?"human-seat":null,s.is_default?"default":null,s.is_mandatory?"mandatory":null].filter(Boolean).join(", "),c=a?` (${a})`:"",m=s.short_description||"_(no description)_";n.push(`- **${s.name}**${c} \u2014 ${m}`)}n.push(""),n.push("_(Coordinator-class drones can fetch role IDs via `borg_list-roles` for use with `borg_reassign-drone`.)_")}return n.push(""),n.push(Be()),{content:[{type:"text",text:n.join(`
8
+ `)}]}}case"borg_role":{const e=await h(),t=typeof o?.role=="string"?o.role.trim():"";if(t){const{role:i}=await ce(e.sessionToken,e.apiUrl,t);return ie(t,i),{content:[{type:"text",text:[`# Role: ${i.name}`,"",i.detailed_description||"_(no detailed description set)_"].join(`
9
9
  `)}]}}const{role:r}=await L(e.sessionToken,e.apiUrl);return{content:[{type:"text",text:[`# Your role: ${r.name}`,"",r.detailed_description||"_(no detailed description set)_"].join(`
10
- `)}]}}case"borg_role-rationale":{const e=await _(),t=typeof o?.role=="string"?o.role:"",r=typeof o?.section=="string"?o.section:"",n=await qe(e.sessionToken,e.apiUrl,t,r);return{content:[{type:"text",text:[`# Role rationale: ${n.role} \u2014 ${n.section}`,"",n.body||"_(empty)_"].join(`
11
- `)}]}}case"borg_roster":{const e=await _(),t=typeof o?.since=="string"?o.since:void 0,{drones:r,roles:n,since:i}=await le(e.sessionToken,e.apiUrl,t);return{content:[{type:"text",text:it({cubeName:e.name,drones:r,roles:n,resolvedSince:i??null,humanAgo:V})}]}}case"borg_stream-status":{const e=P(),t=await h(),r=t?q(t.cubeId,t.droneId):null,n=t?D(I()):null,i=t&&M()!=="claude",s=t?i?!0:U(r,n):null;let a="";e.runLoopHealth==="silent-inert"&&(a=`## \u26A0 SSE stream loop silent-inert \u2014 run /mcp and reconnect to restart
10
+ `)}]}}case"borg_role-rationale":{const e=await h(),t=typeof o?.role=="string"?o.role:"",r=typeof o?.section=="string"?o.section:"",n=await De(e.sessionToken,e.apiUrl,t,r);return{content:[{type:"text",text:[`# Role rationale: ${n.role} \u2014 ${n.section}`,"",n.body||"_(empty)_"].join(`
11
+ `)}]}}case"borg_roster":{const e=await h(),t=typeof o?.since=="string"?o.since:void 0,{drones:r,roles:n,since:i}=await de(e.sessionToken,e.apiUrl,t);return{content:[{type:"text",text:at({cubeName:e.name,drones:r,roles:n,resolvedSince:i??null,humanAgo:V})}]}}case"borg_stream-status":{const e=P(),t=await _(),r=t?q(t.cubeId,t.droneId):null,n=t?D(I()):null,i=t&&M()!=="claude",s=t?i?!0:U(r,n):null;let a="";e.runLoopHealth==="silent-inert"&&(a=`## \u26A0 SSE stream loop silent-inert \u2014 run /mcp and reconnect to restart
12
12
 
13
13
  The log-stream consumer started but never connected. This drone will not receive real-time cube events.
14
14
 
15
- `);const c=tt({status:e,inboxMonitorHealthy:s,inboxPath:r,monitorStateRoot:n,droneLabel:t?.droneLabel??null,cubeName:t?.name??null,humanAgo:V});return{content:[{type:"text",text:a+c}]}}case"borg_read-log":{const e=await _(),t=typeof o?.since=="string"?o.since:void 0,r=typeof o?.limit=="number"?o.limit:void 0,n=o?.unread_only===!0||o?.unread_only==="true",{entries:i,drones:s,roles:a,behind_by:c,has_more:m}=await de(e.sessionToken,e.apiUrl,{since:t,limit:r,unreadOnly:n}),p=new Map;for(const g of s)p.set(g.id,g);const f=new Map;for(const g of a)f.set(g.id,g);const d=[];if(d.push(`# Activity log: ${e.name}`),d.push(""),!i.length)d.push("_(no entries)_");else for(const g of i)d.push(Le(g,p,f));return m===!0?(d.push(""),d.push("\u26A0 has_more: true \u2014 call `borg_read-log unread_only=true` again until has_more=false so you finish draining unread entries.")):typeof c=="number"&&c>0&&(d.push(""),d.push(`\u26A0 behind_by: ${c} more unread ${c===1?"entry":"entries"} addressed to you \u2014 call \`borg_read-log unread_only=true\` again until behind_by=0 so you don't skip messages.`)),{content:[{type:"text",text:d.join(`
16
- `)}]}}case"borg_log":{const e=o?.message;if(!e||typeof e!="string")throw new Error("message is required");const t=await h();if(!t)throw new Error("Not assimilated to a cube. Use borg_assimilate <cube-name> first.");if(Tt(e)){const d=await It(t,e);if(d.suppress)return await J(t,e),{content:[{type:"text",text:`Suppressed duplicate ${d.signal?.toUpperCase()} lifecycle log for ${t.droneLabel}; recent cube log already contains this signal.`}]}}const r=Object.prototype.hasOwnProperty.call(o??{},"to"),n=r?qt(o?.to):void 0,i=typeof o?.class=="string"?o.class:void 0,s=o?.visibility==="broadcast"||o?.visibility==="direct"?o.visibility:void 0,a={...i?{class:i}:{},...r?{to:n??[]}:{},...s?{visibility:s}:{}},c=await ue(t.sessionToken,t.apiUrl,e,a);await J(t,e);const m=c.routing?.message?`
15
+ `);const c=ot({status:e,inboxMonitorHealthy:s,inboxPath:r,monitorStateRoot:n,droneLabel:t?.droneLabel??null,cubeName:t?.name??null,humanAgo:V});return{content:[{type:"text",text:a+c}]}}case"borg_read-log":{const e=await h(),t=typeof o?.since=="string"?o.since:void 0,r=typeof o?.limit=="number"?o.limit:void 0,n=o?.unread_only===!0||o?.unread_only==="true",{entries:i,drones:s,roles:a,behind_by:c,has_more:m}=await le(e.sessionToken,e.apiUrl,{since:t,limit:r,unreadOnly:n}),p=new Map;for(const g of s)p.set(g.id,g);const f=new Map;for(const g of a)f.set(g.id,g);const l=[];if(l.push(`# Activity log: ${e.name}`),l.push(""),!i.length)l.push("_(no entries)_");else for(const g of i)l.push(Oe(g,p,f));return m===!0?(l.push(""),l.push("\u26A0 has_more: true \u2014 call `borg_read-log unread_only=true` again until has_more=false so you finish draining unread entries.")):typeof c=="number"&&c>0&&(l.push(""),l.push(`\u26A0 behind_by: ${c} more unread ${c===1?"entry":"entries"} addressed to you \u2014 call \`borg_read-log unread_only=true\` again until behind_by=0 so you don't skip messages.`)),{content:[{type:"text",text:l.join(`
16
+ `)}]}}case"borg_log":{const e=o?.message;if(!e||typeof e!="string")throw new Error("message is required");const t=await _();if(!t)throw new Error("Not assimilated to a cube. Use borg_assimilate <cube-name> first.");if(It(e)){const l=await qt(t,e);if(l.suppress)return await J(t,e),{content:[{type:"text",text:`Suppressed duplicate ${l.signal?.toUpperCase()} lifecycle log for ${t.droneLabel}; recent cube log already contains this signal.`}]}}const r=Object.prototype.hasOwnProperty.call(o??{},"to"),n=r?Dt(o?.to):void 0,i=typeof o?.class=="string"?o.class:void 0,s=o?.visibility==="broadcast"||o?.visibility==="direct"?o.visibility:void 0,a={...i?{class:i}:{},...r?{to:n??[]}:{},...s?{visibility:s}:{}},c=await ue(t.sessionToken,t.apiUrl,e,a);await J(t,e);const m=c.routing?.message?`
17
17
  ${c.routing.message}`:"",p=c.unreachableRecipients?.length?`
18
- \u26A0 ${c.unreachableRecipients.length} directed recipient(s) currently unreachable (wake-path:deaf): ${c.unreachableRecipients.map(d=>d.label).join(", ")}. Message delivered \u2014 they'll read it when they return.`:"";return{content:[{type:"text",text:`Logged to cube "${t.name}" as ${t.droneLabel}. (entry id: ${c.entry.id})${m}${p}`}]}}case"borg_report-friction":{const e=o?.message;if(!e||typeof e!="string")throw new Error("message is required");const t=await h();if(!t)throw new Error("Not assimilated to a cube. Use borg_assimilate <cube-name> first.");const r=o?.kind==="bug"?"bug":"friction",n=o?.metadata&&typeof o.metadata=="object"&&!Array.isArray(o.metadata)?o.metadata:void 0;return{content:[{type:"text",text:(await pe(t.sessionToken,t.apiUrl,{kind:r,message:e,metadata:n})).ok?"Report submitted \u2014 thank you. The borgmcp team will see it. (Write-only: you cannot read reports back.)":"Report did not submit. Try again, or raise it in the cube log."}]}}case"borg_reports":{const e=await me();if(e.forbidden)return{content:[{type:"text",text:"Reports triage is builder/dogfooder-tier only. Your account is not on the dogfooder (builder) tier, so the friction-reports store is not readable. (Server-enforced gate.)"}]};if(!e.reports.length)return{content:[{type:"text",text:"No reports yet. Submissions via borg_report-friction will appear here, newest first."}]};const t=e.reports.map(r=>{const n=r.metadata&&Object.keys(r.metadata).length?" \xB7 "+Object.entries(r.metadata).map(([s,a])=>`${s}=${a}`).join(", "):"",i=r.redacted?" \xB7 [secrets-scrubbed]":"";return`**[${r.kind}]** ${r.created_at} \xB7 ${r.reporter_email}${n}${i}
18
+ \u26A0 ${c.unreachableRecipients.length} directed recipient(s) currently unreachable (wake-path:deaf): ${c.unreachableRecipients.map(l=>l.label).join(", ")}. Message delivered \u2014 they'll read it when they return.`:"";return{content:[{type:"text",text:`Logged to cube "${t.name}" as ${t.droneLabel}. (entry id: ${c.entry.id})${m}${p}`}]}}case"borg_report-friction":{const e=o?.message;if(!e||typeof e!="string")throw new Error("message is required");const t=await _();if(!t)throw new Error("Not assimilated to a cube. Use borg_assimilate <cube-name> first.");const r=o?.kind==="bug"?"bug":"friction",n=o?.metadata&&typeof o.metadata=="object"&&!Array.isArray(o.metadata)?o.metadata:void 0;return{content:[{type:"text",text:(await pe(t.sessionToken,t.apiUrl,{kind:r,message:e,metadata:n})).ok?"Report submitted \u2014 thank you. The borgmcp team will see it. (Write-only: you cannot read reports back.)":"Report did not submit. Try again, or raise it in the cube log."}]}}case"borg_reports":{const e=await me();if(e.forbidden)return{content:[{type:"text",text:"Reports triage is builder/dogfooder-tier only. Your account is not on the dogfooder (builder) tier, so the friction-reports store is not readable. (Server-enforced gate.)"}]};if(!e.reports.length)return{content:[{type:"text",text:"No reports yet. Submissions via borg_report-friction will appear here, newest first."}]};const t=e.reports.map(r=>{const n=r.metadata&&Object.keys(r.metadata).length?" \xB7 "+Object.entries(r.metadata).map(([s,a])=>`${s}=${a}`).join(", "):"",i=r.redacted?" \xB7 [secrets-scrubbed]":"";return`**[${r.kind}]** ${r.created_at} \xB7 ${r.reporter_email}${n}${i}
19
19
  ${r.message}`});return{content:[{type:"text",text:`Reports (${e.reports.length}, newest first):
20
20
 
21
21
  ${t.join(`
22
22
 
23
23
  ---
24
24
 
25
- `)}`}]}}case"borg_ack":{const e=o?.entry_id;if(!e||typeof e!="string")throw new Error("entry_id is required");const t=o?.kind==="claim"?"claim":"ack",r=await _();return await be(r.sessionToken,r.apiUrl,e,t),{content:[{type:"text",text:t==="claim"?`Claimed entry ${e} in cube "${r.name}" (advisory \u2014 merge stays keyed on REVIEW-APPROVED).`:`Acked entry ${e} in cube "${r.name}".`}]}}case"borg_decide":{const e=o?.topic,t=o?.decision;if(!e||typeof e!="string")throw new Error("topic is required");if(!t||typeof t!="string")throw new Error("decision is required");const r=typeof o?.rationale=="string"?o.rationale:void 0,n=await _(),{decision:i}=await fe(n.sessionToken,n.apiUrl,{topic:e,decision:t,...r!==void 0?{rationale:r}:{}}),s=i?.supersedes?" (superseded the prior decision on this topic)":"";return{content:[{type:"text",text:`Recorded ratified decision on "${e}" in cube "${n.name}"${s}. Cite it via borg_decisions; it surfaces in borg_regen.`}]}}case"borg_decisions":{const e=typeof o?.topic=="string"?o.topic:void 0,t=await _(),{decisions:r}=await ge(t.sessionToken,t.apiUrl,e);return{content:[{type:"text",text:r.length===0?e?`No active ratified decision on "${e}" in cube "${t.name}".`:`No active ratified decisions in cube "${t.name}".`:r.map(i=>`**${i.topic}:** ${i.decision}${i.rationale?` \u2014 ${i.rationale}`:""}`).join(`
26
- `)}]}}case"borg_list-cubes":{const{cubes:e}=await he();if(!e.length)return{content:[{type:"text",text:"No cubes yet. Use borg_create-cube to make your first one."}]};const t=e.map(r=>`- **${r.name}** (id: ${r.id})
25
+ `)}`}]}}case"borg_ack":{const e=o?.entry_id;if(!e||typeof e!="string")throw new Error("entry_id is required");const t=o?.kind==="claim"?"claim":"ack",r=await h();return await be(r.sessionToken,r.apiUrl,e,t),{content:[{type:"text",text:t==="claim"?`Claimed entry ${e} in cube "${r.name}" (advisory \u2014 merge stays keyed on REVIEW-APPROVED).`:`Acked entry ${e} in cube "${r.name}".`}]}}case"borg_decide":{const e=o?.topic,t=o?.decision;if(!e||typeof e!="string")throw new Error("topic is required");if(!t||typeof t!="string")throw new Error("decision is required");const r=typeof o?.rationale=="string"?o.rationale:void 0,n=await h(),{decision:i}=await fe(n.sessionToken,n.apiUrl,{topic:e,decision:t,...r!==void 0?{rationale:r}:{}}),s=i?.supersedes?" (superseded the prior decision on this topic)":"";return{content:[{type:"text",text:`Recorded ratified decision on "${e}" in cube "${n.name}"${s}. Cite it via borg_decisions; it surfaces in borg_regen.`}]}}case"borg_decisions":{const e=typeof o?.topic=="string"?o.topic:void 0,t=await h(),{decisions:r}=await he(t.sessionToken,t.apiUrl,e);return{content:[{type:"text",text:r.length===0?e?`No active ratified decision on "${e}" in cube "${t.name}".`:`No active ratified decisions in cube "${t.name}".`:r.map(i=>`**${i.topic}:** ${i.decision}${i.rationale?` \u2014 ${i.rationale}`:""}`).join(`
26
+ `)}]}}case"borg_remove-decision":{const e=typeof o?.topic=="string"?o.topic:void 0,t=typeof o?.decision_id=="string"?o.decision_id:void 0;if(+(e!==void 0)+ +(t!==void 0)!=1)throw new Error("provide exactly one of topic or decision_id");const r=await h(),n=e!==void 0?{topic:e}:{decision_id:t},{decision:i}=await ge(r.sessionToken,r.apiUrl,n);return{content:[{type:"text",text:`Removed the active ratified decision on "${i.topic}" from cube "${r.name}".`}]}}case"borg_list-cubes":{const{cubes:e}=await _e();if(!e.length)return{content:[{type:"text",text:"No cubes yet. Use borg_create-cube to make your first one."}]};const t=e.map(r=>`- **${r.name}** (id: ${r.id})
27
27
  ${(r.cube_directive||"_(no directive set)_").split(`
28
28
  `)[0].slice(0,120)}`);return{content:[{type:"text",text:`Your cubes (${e.length}):
29
29
 
30
30
  ${t.join(`
31
31
 
32
- `)}`}]}}case"borg_create-cube":{const e=o?.name,t=o?.cube_directive,r=o?.template;if(!e)throw new Error("name is required");if(t===void 0)throw new Error("cube_directive is required (pass empty string if none)");let n=null;if(r&&(n=S(r),!n))throw new Error(`Unknown template "${r}". Available: ${T().join(", ")}`);const i=Ue(t,n),s=je(void 0,n),a=await _e(e,i,{message_taxonomy:s});if(n){const m=await Y(a.id,n),p=i!==t?" Template cube directive applied (operator passed empty).":"";return{content:[{type:"text",text:`Created cube **${a.name}** (id: ${a.id}) with template **${r}** applied \u2014 ${m.created} role(s) created, ${m.updated} updated.${p} Use borg_assimilate ${a.name} to join as a drone.`}]}}return{content:[{type:"text",text:`Created cube **${a.name}** (id: ${a.id}). A default "Drone" role was seeded \u2014 rename or replace it via borg_update-role / borg_create-role / borg_delete-role. Use borg_assimilate ${a.name} to join as a drone.`}]}}case"borg_update-cube":{const e=o?.cube_id;if(!e)throw new Error("cube_id is required");const t={};if(typeof o?.name=="string"&&(t.name=o.name),typeof o?.cube_directive=="string"&&(t.cube_directive=o.cube_directive),Array.isArray(o?.message_taxonomy)&&(t.message_taxonomy=o.message_taxonomy),Object.keys(t).length===0)throw new Error("Pass at least one of: name, cube_directive, message_taxonomy.");const{cube:r}=await B(e,t);return{content:[{type:"text",text:`Updated cube **${r.name}** (id: ${r.id}).`}]}}case"borg_patch-taxonomy-class":{const e=o?.cube_id;if(!e)throw new Error("cube_id is required");const t=o?.action;if(t!=="add"&&t!=="replace"&&t!=="remove")throw new Error("action must be one of: add, replace, remove.");let r,n;if(t==="remove"){const s=o?.class;if(!s)throw new Error("class is required for remove.");({cube:r}=await F(e,{action:t,class:s})),n=s}else{const s=o?.class_def;if(s==null||typeof s!="object"||Array.isArray(s))throw new Error("class_def (object) is required for add/replace.");({cube:r}=await F(e,{action:t,class_def:s})),n=String(s.class??"")}return{content:[{type:"text",text:`${t==="add"?"Added":t==="replace"?"Replaced":"Removed"} taxonomy class **${n}** in cube **${r.name}** (id: ${r.id}).`}]}}case"borg_delete-cube":{const e=o?.cube_id;if(!e)throw new Error("cube_id is required");return await ye(e),{content:[{type:"text",text:`Deleted cube ${e} (and all its roles, drones, log entries).`}]}}case"borg_create-role":{const e=o?.cube_id,t=o?.name,r=o?.short_description,n=o?.detailed_description;if(!e)throw new Error("cube_id is required");if(!t)throw new Error("name is required");if(r===void 0)throw new Error("short_description is required (pass empty string if none)");if(n===void 0)throw new Error("detailed_description is required (pass empty string if none)");const i=o?.is_default===!0,s=o?.is_mandatory===!0,a=o?.is_human_seat===!0,c=o?.can_broadcast===!0,m=o?.receives_all_direct===!0,{role:p}=await we(e,{name:t,short_description:r,detailed_description:n,is_default:i,is_mandatory:s,is_human_seat:a,can_broadcast:c,receives_all_direct:m,...typeof o?.default_model=="string"?{default_model:o.default_model}:{}}),f=[p.role_class==="queen"?"Queen":null,p.is_human_seat?"human-seat":null,p.is_default?"default":null,p.is_mandatory?"mandatory":null].filter(Boolean).join(", "),d=f?` (${f})`:"";return{content:[{type:"text",text:`Created role **${p.name}**${d} (id: ${p.id}) in cube ${e}.`}]}}case"borg_update-role":{const e=o?.role_id;if(!e)throw new Error("role_id is required");const t={};if(typeof o?.name=="string"&&(t.name=o.name),typeof o?.short_description=="string"&&(t.short_description=o.short_description),typeof o?.detailed_description=="string"&&(t.detailed_description=o.detailed_description),typeof o?.is_default=="boolean"&&(t.is_default=o.is_default),typeof o?.is_mandatory=="boolean"&&(t.is_mandatory=o.is_mandatory),typeof o?.is_human_seat=="boolean"&&(t.is_human_seat=o.is_human_seat),typeof o?.can_broadcast=="boolean"&&(t.can_broadcast=o.can_broadcast),typeof o?.receives_all_direct=="boolean"&&(t.receives_all_direct=o.receives_all_direct),typeof o?.default_model=="string"&&(t.default_model=o.default_model),Object.keys(t).length===0)throw new Error("Pass at least one of: name, short_description, detailed_description, is_default, is_mandatory, is_human_seat, can_broadcast, receives_all_direct, default_model.");const{role:r}=await xe(e,t),n=[r.role_class==="queen"?"Queen":null,r.is_human_seat?"human-seat":null,r.is_default?"default":null,r.is_mandatory?"mandatory":null].filter(Boolean).join(", "),i=n?` (${n})`:"";return{content:[{type:"text",text:`Updated role **${r.name}**${i} (id: ${r.id}).`}]}}case"borg_patch-role-section":{const e=o?.role_id;if(!e)throw new Error("role_id is required");const t=o?.action;if(t!=="replace"&&t!=="insert"&&t!=="delete")throw new Error("action must be one of: replace, insert, delete.");const r=o?.heading;if(!r)throw new Error("heading is required");let n;if(t==="delete")({role:n}=await C(e,{action:t,heading:r}));else{const s=o?.body;if(typeof s!="string")throw new Error("body is required for replace/insert (pass empty string for an empty section).");if(t==="insert"){const a=typeof o?.after=="string"?o.after:null;({role:n}=await C(e,{action:t,heading:r,body:s,after:a}))}else({role:n}=await C(e,{action:t,heading:r,body:s}))}return{content:[{type:"text",text:`${t==="replace"?"Replaced":t==="insert"?"Inserted":"Deleted"} section **${r}** in role **${n.name}** (id: ${n.id}).`}]}}case"borg_delete-role":{const e=o?.role_id;if(!e)throw new Error("role_id is required");return await $e(e),{content:[{type:"text",text:`Deleted role ${e}.`}]}}case"borg_reassign-drone":{const e=o?.drone_id,t=o?.role_id;if(!e)throw new Error("drone_id is required");if(!t)throw new Error("role_id is required");const{drone:r}=await ve(e,t);return{content:[{type:"text",text:`Reassigned drone ${r.label} (${r.id}) to role ${r.role_id}.`}]}}case"borg_evict-drone":{const e=o?.drone_id?.trim(),t=o?.label?.trim(),r=o?.cube_id?.trim();let n,i;if(e){if(!ct(e))throw new Error(`drone_id "${e}" is not a UUID \u2014 if that's a drone label, pass it as label + cube_id instead.`);n=e,i=e}else if(t){if(!r)throw new Error("cube_id is required when evicting by label");const{drones:s}=await $(r),a=at(s,t);if(!a)throw new Error(`No active drone labelled "${t}" in cube ${r} (it may already be evicted; check borg_list-drones).`);n=a.id,i=a.label}else throw new Error("Provide drone_id, or label + cube_id, to identify the drone to evict");return await ke(n),{content:[{type:"text",text:`Evicted drone ${i} (${n}). Soft-deleted: removed from the roster and freed its seat; log history preserved with anonymized attribution.`}]}}case"borg_list-drones":{const e=o?.cube_id;if(!e)throw new Error("cube_id is required");const{drones:t,roles:r}=await $(e);if(!t.length)return{content:[{type:"text",text:"No drones in this cube yet."}]};const n=new Map(r.map(s=>[s.id,s])),i=t.map(s=>{const a=n.get(s.role_id),c=nt(a?.name??"?",s.agent_kind),m=s.wake_path_alert_class&&s.wake_path_alert_class!=="independent"?` \u2014 wake-path-class: ${s.wake_path_alert_class}`:"",p=s.reported_model?` \u2014 Reported model: ${s.reported_model}`:" \u2014 Reported model: not reported",f=st(s),d=f?` \u2014 ${f}`:"";return`- **${s.label}** (id: ${s.id}) \u2014 role: ${c} (${s.role_id}) \u2014 last seen ${s.last_seen}${m}${p}${d}`});return{content:[{type:"text",text:`Drones in cube ${e} (${t.length}):
32
+ `)}`}]}}case"borg_create-cube":{const e=o?.name,t=o?.cube_directive,r=o?.template;if(!e)throw new Error("name is required");if(t===void 0)throw new Error("cube_directive is required (pass empty string if none)");let n=null;if(r&&(n=S(r),!n))throw new Error(`Unknown template "${r}". Available: ${T().join(", ")}`);const i=Me(t,n),s=je(void 0,n),a=await ye(e,i,{message_taxonomy:s});if(n){const m=await Y(a.id,n),p=i!==t?" Template cube directive applied (operator passed empty).":"";return{content:[{type:"text",text:`Created cube **${a.name}** (id: ${a.id}) with template **${r}** applied \u2014 ${m.created} role(s) created, ${m.updated} updated.${p} Use borg_assimilate ${a.name} to join as a drone.`}]}}return{content:[{type:"text",text:`Created cube **${a.name}** (id: ${a.id}). A default "Drone" role was seeded \u2014 rename or replace it via borg_update-role / borg_create-role / borg_delete-role. Use borg_assimilate ${a.name} to join as a drone.`}]}}case"borg_update-cube":{const e=o?.cube_id;if(!e)throw new Error("cube_id is required");const t={};if(typeof o?.name=="string"&&(t.name=o.name),typeof o?.cube_directive=="string"&&(t.cube_directive=o.cube_directive),Array.isArray(o?.message_taxonomy)&&(t.message_taxonomy=o.message_taxonomy),Object.keys(t).length===0)throw new Error("Pass at least one of: name, cube_directive, message_taxonomy.");const{cube:r}=await B(e,t);return{content:[{type:"text",text:`Updated cube **${r.name}** (id: ${r.id}).`}]}}case"borg_patch-taxonomy-class":{const e=o?.cube_id;if(!e)throw new Error("cube_id is required");const t=o?.action;if(t!=="add"&&t!=="replace"&&t!=="remove")throw new Error("action must be one of: add, replace, remove.");let r,n;if(t==="remove"){const s=o?.class;if(!s)throw new Error("class is required for remove.");({cube:r}=await F(e,{action:t,class:s})),n=s}else{const s=o?.class_def;if(s==null||typeof s!="object"||Array.isArray(s))throw new Error("class_def (object) is required for add/replace.");({cube:r}=await F(e,{action:t,class_def:s})),n=String(s.class??"")}return{content:[{type:"text",text:`${t==="add"?"Added":t==="replace"?"Replaced":"Removed"} taxonomy class **${n}** in cube **${r.name}** (id: ${r.id}).`}]}}case"borg_delete-cube":{const e=o?.cube_id;if(!e)throw new Error("cube_id is required");return await we(e),{content:[{type:"text",text:`Deleted cube ${e} (and all its roles, drones, log entries).`}]}}case"borg_create-role":{const e=o?.cube_id,t=o?.name,r=o?.short_description,n=o?.detailed_description;if(!e)throw new Error("cube_id is required");if(!t)throw new Error("name is required");if(r===void 0)throw new Error("short_description is required (pass empty string if none)");if(n===void 0)throw new Error("detailed_description is required (pass empty string if none)");const i=o?.is_default===!0,s=o?.is_mandatory===!0,a=o?.is_human_seat===!0,c=o?.can_broadcast===!0,m=o?.receives_all_direct===!0,{role:p}=await xe(e,{name:t,short_description:r,detailed_description:n,is_default:i,is_mandatory:s,is_human_seat:a,can_broadcast:c,receives_all_direct:m,...typeof o?.default_model=="string"?{default_model:o.default_model}:{}}),f=[p.role_class==="queen"?"Queen":null,p.is_human_seat?"human-seat":null,p.is_default?"default":null,p.is_mandatory?"mandatory":null].filter(Boolean).join(", "),l=f?` (${f})`:"";return{content:[{type:"text",text:`Created role **${p.name}**${l} (id: ${p.id}) in cube ${e}.`}]}}case"borg_update-role":{const e=o?.role_id;if(!e)throw new Error("role_id is required");const t={};if(typeof o?.name=="string"&&(t.name=o.name),typeof o?.short_description=="string"&&(t.short_description=o.short_description),typeof o?.detailed_description=="string"&&(t.detailed_description=o.detailed_description),typeof o?.is_default=="boolean"&&(t.is_default=o.is_default),typeof o?.is_mandatory=="boolean"&&(t.is_mandatory=o.is_mandatory),typeof o?.is_human_seat=="boolean"&&(t.is_human_seat=o.is_human_seat),typeof o?.can_broadcast=="boolean"&&(t.can_broadcast=o.can_broadcast),typeof o?.receives_all_direct=="boolean"&&(t.receives_all_direct=o.receives_all_direct),typeof o?.default_model=="string"&&(t.default_model=o.default_model),Object.keys(t).length===0)throw new Error("Pass at least one of: name, short_description, detailed_description, is_default, is_mandatory, is_human_seat, can_broadcast, receives_all_direct.");const{role:r}=await $e(e,t),n=[r.role_class==="queen"?"Queen":null,r.is_human_seat?"human-seat":null,r.is_default?"default":null,r.is_mandatory?"mandatory":null].filter(Boolean).join(", "),i=n?` (${n})`:"";return{content:[{type:"text",text:`Updated role **${r.name}**${i} (id: ${r.id}).`}]}}case"borg_patch-role-section":{const e=o?.role_id;if(!e)throw new Error("role_id is required");const t=o?.action;if(t!=="replace"&&t!=="insert"&&t!=="delete")throw new Error("action must be one of: replace, insert, delete.");const r=o?.heading;if(!r)throw new Error("heading is required");let n;if(t==="delete")({role:n}=await C(e,{action:t,heading:r}));else{const s=o?.body;if(typeof s!="string")throw new Error("body is required for replace/insert (pass empty string for an empty section).");if(t==="insert"){const a=typeof o?.after=="string"?o.after:null;({role:n}=await C(e,{action:t,heading:r,body:s,after:a}))}else({role:n}=await C(e,{action:t,heading:r,body:s}))}return{content:[{type:"text",text:`${t==="replace"?"Replaced":t==="insert"?"Inserted":"Deleted"} section **${r}** in role **${n.name}** (id: ${n.id}).`}]}}case"borg_delete-role":{const e=o?.role_id;if(!e)throw new Error("role_id is required");return await ve(e),{content:[{type:"text",text:`Deleted role ${e}.`}]}}case"borg_reassign-drone":{const e=o?.drone_id,t=o?.role_id;if(!e)throw new Error("drone_id is required");if(!t)throw new Error("role_id is required");const{drone:r}=await ke(e,t);return{content:[{type:"text",text:`Reassigned drone ${r.label} (${r.id}) to role ${r.role_id}.`}]}}case"borg_evict-drone":{const e=o?.drone_id?.trim(),t=o?.label?.trim(),r=o?.cube_id?.trim();let n,i;if(e){if(!dt(e))throw new Error(`drone_id "${e}" is not a UUID \u2014 if that's a drone label, pass it as label + cube_id instead.`);n=e,i=e}else if(t){if(!r)throw new Error("cube_id is required when evicting by label");const{drones:s}=await $(r),a=ct(s,t);if(!a)throw new Error(`No active drone labelled "${t}" in cube ${r} (it may already be evicted; check borg_list-drones).`);n=a.id,i=a.label}else throw new Error("Provide drone_id, or label + cube_id, to identify the drone to evict");return await Re(n),{content:[{type:"text",text:`Evicted drone ${i} (${n}). Soft-deleted: removed from the roster and freed its seat; log history preserved with anonymized attribution.`}]}}case"borg_list-drones":{const e=o?.cube_id;if(!e)throw new Error("cube_id is required");const{drones:t,roles:r}=await $(e);if(!t.length)return{content:[{type:"text",text:"No drones in this cube yet."}]};const n=new Map(r.map(s=>[s.id,s])),i=t.map(s=>{const a=n.get(s.role_id),c=st(a?.name??"?",s.agent_kind),m=s.wake_path_alert_class&&s.wake_path_alert_class!=="independent"?` \u2014 wake-path-class: ${s.wake_path_alert_class}`:"",p=s.reported_model?` \u2014 Reported model: ${s.reported_model}`:" \u2014 Reported model: not reported",f=it(s),l=f?` \u2014 ${f}`:"";return`- **${s.label}** (id: ${s.id}) \u2014 role: ${c} (${s.role_id}) \u2014 last seen ${s.last_seen}${m}${p}${l}`});return{content:[{type:"text",text:`Drones in cube ${e} (${t.length}):
33
33
 
34
34
  ${i.join(`
35
- `)}`}]}}case"borg_list-roles":{const e=o?.cube_id;if(!e)throw new Error("cube_id is required");const{roles:t}=await $(e);return{content:[{type:"text",text:Ge(t,e)}]}}case"borg_list-templates":return{content:[{type:"text",text:`Available templates:
35
+ `)}`}]}}case"borg_list-roles":{const e=o?.cube_id;if(!e)throw new Error("cube_id is required");const{roles:t}=await $(e);return{content:[{type:"text",text:Xe(t,e)}]}}case"borg_list-templates":return{content:[{type:"text",text:`Available templates:
36
36
 
37
37
  ${T().map(r=>{const n=S(r);return`- **${r}**: ${n.description}`}).join(`
38
- `)}`}]};case"borg_sync-roles":{const e=o?.cube_id,t=o?.template_name||"software-dev",r=o?.apply===!0,n=o?.decisions&&typeof o.decisions=="object"?o.decisions:void 0;if(!e)throw new Error("cube_id is required");const i=await Se(e,t,r,n);return{content:[{type:"text",text:yt(i,t)}]}}case"borg_apply-template":{const e=o?.cube_id,t=o?.template_name;if(!e)throw new Error("cube_id is required");if(!t)throw new Error("template_name is required");const r=S(t);if(!r)throw new Error(`Unknown template "${t}". Available: ${T().join(", ")}`);const n=await Y(e,r);let i="";const s=await $(e),a=Me(s.cube_directive,r);return a!==null&&(await B(e,{cube_directive:a}),i=" Template cube directive applied (cube directive was empty)."),{content:[{type:"text",text:`Applied template **${t}** to cube ${e} \u2014 ${n.created} role(s) created, ${n.updated} updated.${i}`}]}}default:throw new Error(`Unknown tool: ${u}`)}}catch(e){if(e instanceof dt)return{content:[{type:"text",text:pt(e.message)}],isError:!0};if(e instanceof ut)return{content:[{type:"text",text:mt(e.message)}],isError:!0};const t=lt(e??{});return t?{content:[{type:"text",text:t}],isError:!0}:{content:[{type:"text",text:`Error: ${e.message}`}],isError:!0}}}),y.setRequestHandler(ne,async()=>({prompts:[{name:"borg_subscribe",description:"Set up Borg MCP Cube tier subscription ($1/month per cube; each cube adds 8 pooled agent sessions + 1000 req/hr). Free tier is permanent (1 cube + 3 agent sessions + 100 req/hr); no trial."},{name:"dashboard",description:"Open Borg MCP dashboard to manage cubes"}]})),y.setRequestHandler(se,async l=>{const{name:u}=l.params;switch(u){case"borg_subscribe":return{description:"Set up Borg MCP Cube tier subscription ($1/month per cube; each cube adds 8 pooled agent sessions + 1000 req/hr). Free tier is permanent (1 cube + 3 agent sessions + 100 req/hr); no trial.",messages:[{role:"user",content:{type:"text",text:"Please help me set up a Borg MCP subscription using the subscribe tool."}}]};case"dashboard":return{description:"Open Borg MCP dashboard to manage cubes",messages:[{role:"user",content:{type:"text",text:"Please open the Borg MCP dashboard using the borg_open_dashboard tool."}}]};default:throw new Error(`Unknown prompt: ${u}`)}});const G=new te;await y.connect(G),await wt(),console.error(`${k()}\u25FC Borg MCP Client started`),console.error(`${k()}\u25FC Use borg_assimilate <cube-name> to join a cube as a drone`),console.error(`${k()}\u25FC Manage your cubes at https://borgmcp.ai/dashboard`)}Ut().catch(b=>{console.error(`${k()}Fatal error:`,b),process.exit(1)});
38
+ `)}`}]};case"borg_sync-roles":{const e=o?.cube_id,t=o?.template_name||"software-dev",r=o?.apply===!0,n=o?.decisions&&typeof o.decisions=="object"?o.decisions:void 0;if(!e)throw new Error("cube_id is required");const i=await Te(e,t,r,n);return{content:[{type:"text",text:wt(i,t)}]}}case"borg_apply-template":{const e=o?.cube_id,t=o?.template_name;if(!e)throw new Error("cube_id is required");if(!t)throw new Error("template_name is required");const r=S(t);if(!r)throw new Error(`Unknown template "${t}". Available: ${T().join(", ")}`);const n=await Y(e,r);let i="";const s=await $(e),a=Ne(s.cube_directive,r);return a!==null&&(await B(e,{cube_directive:a}),i=" Template cube directive applied (cube directive was empty)."),{content:[{type:"text",text:`Applied template **${t}** to cube ${e} \u2014 ${n.created} role(s) created, ${n.updated} updated.${i}`}]}}default:throw new Error(`Unknown tool: ${u}`)}}catch(e){if(e instanceof ut)return{content:[{type:"text",text:mt(e.message)}],isError:!0};if(e instanceof pt)return{content:[{type:"text",text:bt(e.message)}],isError:!0};const t=lt(e??{});return t?{content:[{type:"text",text:t}],isError:!0}:{content:[{type:"text",text:`Error: ${e.message}`}],isError:!0}}}),y.setRequestHandler(ne,async()=>({prompts:[{name:"borg_subscribe",description:"Set up Borg MCP Cube tier subscription ($1/month per cube; each cube adds 8 pooled agent sessions + 1000 req/hr). Free tier is permanent (1 cube + 3 agent sessions + 100 req/hr); no trial."},{name:"dashboard",description:"Open Borg MCP dashboard to manage cubes"}]})),y.setRequestHandler(se,async d=>{const{name:u}=d.params;switch(u){case"borg_subscribe":return{description:"Set up Borg MCP Cube tier subscription ($1/month per cube; each cube adds 8 pooled agent sessions + 1000 req/hr). Free tier is permanent (1 cube + 3 agent sessions + 100 req/hr); no trial.",messages:[{role:"user",content:{type:"text",text:"Please help me set up a Borg MCP subscription using the subscribe tool."}}]};case"dashboard":return{description:"Open Borg MCP dashboard to manage cubes",messages:[{role:"user",content:{type:"text",text:"Please open the Borg MCP dashboard using the borg_open_dashboard tool."}}]};default:throw new Error(`Unknown prompt: ${u}`)}});const G=new te;await y.connect(G),await xt(),console.error(`${k()}\u25FC Borg MCP Client started`),console.error(`${k()}\u25FC Use borg_assimilate <cube-name> to join a cube as a drone`),console.error(`${k()}\u25FC Manage your cubes at https://borgmcp.ai/dashboard`)}Mt().catch(b=>{console.error(`${k()}Fatal error:`,b),process.exit(1)});
@@ -4,12 +4,13 @@
4
4
  * The `borg` wrapper sets BORG_SESSION=1 in the agent's launch env
5
5
  * (claude.ts / assimilate-cmd.ts launchEnv — same pattern as
6
6
  * BORG_CODEX_REMOTE_WAKE). Claude Code spawns the borg-mcp MCP child and
7
- * runs hook commands with that env inherited, so three activation bins
7
+ * runs hook commands with that env inherited, so four activation bins
8
8
  * gate on it:
9
9
  * 1. the MCP tool surface (index.ts CallTool top) — a vanilla session
10
10
  * gets a non-silent re-launch notice per tool, never a half-success;
11
11
  * 2. the borg-regen SessionStart hook bin — exit-0 no-op;
12
- * 3. the borg-log-audit UserPromptSubmit hook bin — exit-0 no-op.
12
+ * 3. the borg-clear-rewake clear-only hook bin — exit-0 no-op;
13
+ * 4. the borg-log-audit UserPromptSubmit hook bin — exit-0 no-op.
13
14
  * Result: `claude` launched directly is vanilla Claude Code; `borg`
14
15
  * launches get the full surface.
15
16
  *
@@ -1,61 +1,15 @@
1
1
  /**
2
- * Model preset registry: parse descriptors and resolve launch env.
3
- * Descriptor format: <kind>:<model> where kind {claude, ollama}.
4
- * Parse on first colon only (model tags contain colons).
5
- *
6
- * CLIENT copy of the descriptor regex — byte-identical to the worker.
2
+ * Temporary compatibility helpers for the legacy Borg-managed Claude model
3
+ * selector. Model/provider configuration belongs to the launched agent CLI;
4
+ * the remaining selector is retired with the assimilation model wire cleanup.
7
5
  */
8
6
  export declare const MODEL_DESCRIPTOR_REGEX: RegExp;
9
- /** Default Ollama endpoint when BORG_OLLAMA_BASE_URL is unset. */
10
- export declare const OLLAMA_DEFAULT_BASE_URL = "http://localhost:11434";
11
- /**
12
- * Resolve the Ollama base URL: BORG_OLLAMA_BASE_URL override or the localhost
13
- * default. The override is trimmed, defaulted to http:// when no scheme is
14
- * given (Ollama serves plain http), and trailing slashes are stripped. Lets an
15
- * Ollama drone point at a model server on another host, e.g.
16
- * BORG_OLLAMA_BASE_URL=http://Mac-Studio.local:11434 borg assimilate ...
17
- */
18
- export declare function resolveOllamaBaseUrl(env?: Record<string, string | undefined>, storedUrl?: string | null): string;
19
- /**
20
- * Restore a drone's persisted launch model onto a base environment — the
21
- * bare-`borg` relaunch path. Returns the merged child env plus, for ollama
22
- * models, a `{ descriptor, baseUrl }` the caller can use for a best-effort
23
- * reachability warning. A null/absent stored model leaves the env untouched
24
- * (backward compatible — no stored entry ⇒ today's behavior). Base-URL
25
- * precedence is BORG_OLLAMA_BASE_URL ▸ stored ▸ default (the pairing rule: the
26
- * stored host travels with the stored descriptor).
27
- */
28
- export declare function applyOllamaLaunchEnv(baseEnv: Record<string, string | undefined>, stored: {
29
- model: string;
30
- ollamaBaseUrl?: string | null;
31
- } | null, processEnv?: Record<string, string | undefined>): {
32
- env: Record<string, string | undefined>;
33
- probe: {
34
- descriptor: string;
35
- baseUrl: string;
36
- } | null;
37
- };
38
7
  export declare function parseModel(descriptor: string): {
39
- kind: 'claude' | 'ollama';
8
+ kind: 'claude';
40
9
  model: string;
41
10
  };
42
- export declare function resolveLaunchEnv(descriptor: string | null, ollamaBaseUrl?: string): {
11
+ export declare function resolveLaunchEnv(descriptor: string | null): {
43
12
  set: Record<string, string>;
44
13
  unset: string[];
45
14
  };
46
- /**
47
- * Does `requested` match one of Ollama's pulled model names? Ollama defaults a
48
- * tagless name to `:latest`, so `qwen3-coder-next` matches a pulled
49
- * `qwen3-coder-next:latest`. Comparison is case-insensitive (Ollama lowercases
50
- * model names). Tag presence is judged on the LAST path segment so a
51
- * registry-host:port prefix colon (e.g. `localhost:5000/llama3`) is not mistaken
52
- * for a tag. NOTE: digest refs (`name@sha256:…`) can't reach here — they're
53
- * fenced out by MODEL_DESCRIPTOR_REGEX (no `@`); widening that regex would
54
- * require updating this matcher in lockstep.
55
- */
56
- export declare function ollamaModelMatches(requested: string, available: string[]): boolean;
57
- export declare function checkModelReachable(descriptor: string | null, fetchImpl: typeof fetch, ollamaBaseUrl?: string): Promise<{
58
- ok: boolean;
59
- message?: string;
60
- }>;
61
15
  //# sourceMappingURL=model-presets.d.ts.map
@@ -1 +1 @@
1
- const _=/^(claude|ollama):[A-Za-z0-9._:\/-]+$/,O="http://localhost:11434";function h(n=process.env,t){const e=n.BORG_OLLAMA_BASE_URL?.trim()||t?.trim()||"";return e?(/^[a-z][a-z0-9+.-]*:\/\//i.test(e)?e:`http://${e}`).replace(/\/+$/,""):O}function $(n,t,e=process.env){if(!t||!t.model)return{env:n,probe:null};const{kind:l}=f(t.model),o=h(e,t.ollamaBaseUrl??void 0),{set:u,unset:s}=A(t.model,o),c={...n,...u};for(const d of s)delete c[d];return{env:c,probe:l==="ollama"?{descriptor:t.model,baseUrl:o}:null}}function f(n){const t=n.indexOf(":");if(t<0)throw new Error(`invalid model descriptor: ${n} (expected <kind>:<model>)`);const e=n.substring(0,t),l=n.substring(t+1);if(e!=="claude"&&e!=="ollama")throw new Error(`invalid model kind: ${e} (expected claude or ollama)`);return{kind:e,model:l}}function A(n,t=h()){if(!n)return{set:{},unset:[]};const{kind:e,model:l}=f(n);return e==="claude"?{set:{ANTHROPIC_MODEL:l},unset:[]}:{set:{ANTHROPIC_BASE_URL:t,ANTHROPIC_MODEL:l,ANTHROPIC_AUTH_TOKEN:"ollama"},unset:["ANTHROPIC_API_KEY"]}}function L(n,t){const e=n.toLowerCase(),o=e.slice(e.lastIndexOf("/")+1).includes(":")?e:`${e}:latest`;return t.some(u=>{const s=u.toLowerCase();return s===o||s===e})}async function g(n,t,e=h()){if(!n)return{ok:!0};const{kind:l,model:o}=f(n);if(l==="claude")return{ok:!0};const s=e.includes("localhost")||e.includes("127.0.0.1")||e.includes("[::1]")?"start it (ollama serve)":"check the host is up and Ollama is listening there (OLLAMA_HOST=0.0.0.0)",c={ok:!1,message:`Ollama not reachable at ${e} \u2014 ${s}, or assimilate with a Claude model.`},d=new AbortController,p=setTimeout(()=>d.abort(),3e3);try{const m=await t(`${e}/api/tags`,{signal:d.signal});if(!m.ok)return c;let r=null;try{const i=await m.json();i&&Array.isArray(i.models)&&(r=i.models.map(a=>(a?.name??a?.model??"").replace(/[\x00-\x1F\x7F]/g,"")).filter(a=>a.length>0))}catch{}if(r&&!L(o,r)){const i=r.length===0?"no models are pulled there":`available: ${r.slice(0,12).map(a=>a.length>64?`${a.slice(0,64)}\u2026`:a).join(", ")}${r.length>12?`, \u2026 (${r.length} total)`:""}`;return{ok:!1,message:`Ollama model '${o}' not found at ${e} (${i}) \u2014 pull it with: ollama pull ${o}`}}return{ok:!0}}catch{return c}finally{clearTimeout(p)}}export{_ as MODEL_DESCRIPTOR_REGEX,O as OLLAMA_DEFAULT_BASE_URL,$ as applyOllamaLaunchEnv,g as checkModelReachable,L as ollamaModelMatches,f as parseModel,A as resolveLaunchEnv,h as resolveOllamaBaseUrl};
1
+ const t=/^claude:[A-Za-z0-9._:\/-]+$/;function l(e){if(!t.test(e))throw new Error(`invalid model descriptor: ${e} (expected claude:<model>)`);return{kind:"claude",model:e.slice(7)}}function o(e){if(!e)return{set:{},unset:[]};const{model:n}=l(e);return{set:{ANTHROPIC_MODEL:n},unset:[]}}export{t as MODEL_DESCRIPTOR_REGEX,l as parseModel,o as resolveLaunchEnv};
@@ -1 +1 @@
1
- function i(o){let a;const t={};for(let l=0;l<o.length;l+=1){const r=o[l];if(r==="--worktree"){const e=o[l+1];if(typeof e!="string"||e.length===0)return{ok:!1,error:"--worktree requires a name argument (e.g. `--worktree drone-2`)"};t.worktree=e,l+=1}else if(r==="--template"){const e=o[l+1];if(typeof e!="string"||e.length===0)return{ok:!1,error:"--template requires a name argument (e.g. `--template software-dev`)"};t.template=e,l+=1}else if(r==="--no-template")t.noTemplate=!0;else if(r==="--cube-name"){const e=o[l+1];if(typeof e!="string"||e.length===0)return{ok:!1,error:"--cube-name requires a name argument (e.g. `--cube-name my-cube`)"};t.cubeName=e,l+=1}else if(r==="--here")t.here=!0;else if(r==="--yes"||r==="-y")t.yes=!0;else if(r==="--cli"){const e=o[l+1];if(e!=="claude"&&e!=="codex"&&e!=="opencode")return{ok:!1,error:"--cli requires claude, codex, or opencode"};t.cli=e,l+=1}else if(r.startsWith("--cli=")){const e=r.slice(6);if(e!=="claude"&&e!=="codex"&&e!=="opencode")return{ok:!1,error:"--cli requires claude, codex, or opencode"};t.cli=e}else if(r==="--model"||r==="--backend"){const e=o[l+1];if(typeof e!="string"||e.length===0)return{ok:!1,error:"--model requires a descriptor (e.g. `--model claude:claude-opus-4-8` or `--model ollama:qwen3-coder-next:q4_K_M`)"};if(!e.match(/^(claude|ollama):[A-Za-z0-9._:\/-]+$/))return{ok:!1,error:`invalid model descriptor '${e}' \u2014 expected <kind>:<model> where kind is claude or ollama`};t.model=e,l+=1}else if(r.startsWith("--model=")||r.startsWith("--backend=")){const e=r.slice(r.indexOf("=")+1);if(!e.match(/^(claude|ollama):[A-Za-z0-9._:\/-]+$/))return{ok:!1,error:`invalid model descriptor '${e}' \u2014 expected <kind>:<model> where kind is claude or ollama`};t.model=e}else{if(r.startsWith("--"))return{ok:!1,error:`unknown flag: ${r}. Supported: --worktree, --template, --no-template, --cube-name, --here, --yes, --cli, --model`};if(a!==void 0)return{ok:!1,error:`unexpected extra argument: ${r} (already have role "${a}")`};a=r}}return t.template!==void 0&&t.noTemplate?{ok:!1,error:"--template and --no-template are mutually exclusive"}:{ok:!0,role:a,flags:t}}export{i as parseAssimilateArgs};
1
+ function c(o){let n;const r={};for(let l=0;l<o.length;l+=1){const t=o[l];if(t==="--worktree"){const e=o[l+1];if(typeof e!="string"||e.length===0)return{ok:!1,error:"--worktree requires a name argument (e.g. `--worktree drone-2`)"};r.worktree=e,l+=1}else if(t==="--template"){const e=o[l+1];if(typeof e!="string"||e.length===0)return{ok:!1,error:"--template requires a name argument (e.g. `--template software-dev`)"};r.template=e,l+=1}else if(t==="--no-template")r.noTemplate=!0;else if(t==="--cube-name"){const e=o[l+1];if(typeof e!="string"||e.length===0)return{ok:!1,error:"--cube-name requires a name argument (e.g. `--cube-name my-cube`)"};r.cubeName=e,l+=1}else if(t==="--here")r.here=!0;else if(t==="--yes"||t==="-y")r.yes=!0;else if(t==="--cli"){const e=o[l+1];if(e!=="claude"&&e!=="codex"&&e!=="opencode")return{ok:!1,error:"--cli requires claude, codex, or opencode"};r.cli=e,l+=1}else if(t.startsWith("--cli=")){const e=t.slice(6);if(e!=="claude"&&e!=="codex"&&e!=="opencode")return{ok:!1,error:"--cli requires claude, codex, or opencode"};r.cli=e}else if(t==="--model"||t==="--backend"){const e=o[l+1];if(typeof e!="string"||e.length===0)return{ok:!1,error:"--model requires a descriptor (e.g. `--model claude:claude-opus-4-8`)"};if(!e.match(/^claude:[A-Za-z0-9._:\/-]+$/))return{ok:!1,error:`invalid model descriptor '${e}' \u2014 expected claude:<model>; configure local models in the agent CLI`};r.model=e,l+=1}else if(t.startsWith("--model=")||t.startsWith("--backend=")){const e=t.slice(t.indexOf("=")+1);if(!e.match(/^claude:[A-Za-z0-9._:\/-]+$/))return{ok:!1,error:`invalid model descriptor '${e}' \u2014 expected claude:<model>; configure local models in the agent CLI`};r.model=e}else{if(t.startsWith("--"))return{ok:!1,error:`unknown flag: ${t}. Supported: --worktree, --template, --no-template, --cube-name, --here, --yes, --cli, --model`};if(n!==void 0)return{ok:!1,error:`unexpected extra argument: ${t} (already have role "${n}")`};n=t}}return r.template!==void 0&&r.noTemplate?{ok:!1,error:"--template and --no-template are mutually exclusive"}:{ok:!0,role:n,flags:r}}export{c as parseAssimilateArgs};
@@ -34,8 +34,9 @@ export type AgentKind = 'claude' | 'codex' | 'opencode';
34
34
  *
35
35
  * Agent-branched on the existing env-agnostic signal (BORG_SESSION-style
36
36
  * `isCodexRemoteWakeEnabled`), NOT on a mutable server-recorded field:
37
- * - claude: arm the inbox-file tail Monitor + engage `/loop` + set a ~3600s
38
- * `ScheduleWakeup` fallback heartbeat (three independent wake mechanisms).
37
+ * - claude: arm the inbox-file tail Monitor + engage `/loop` + maintain one
38
+ * adaptive `ScheduleWakeup` recovery deadline (long while the Monitor is
39
+ * healthy or indeterminate; short only while explicitly broken).
39
40
  * - codex: Borg's activity stream reaches the app-server remote-control inbox
40
41
  * channel; each wake is followed by an unread-log drain. Manual full regen
41
42
  * + drain is a degraded fallback when remote control is unavailable.
@@ -1,12 +1,13 @@
1
- import{ROLE_SCOPED_SAFETY_DISCIPLINES as _,UNIVERSAL_SAFETY_DISCIPLINES as x}from"./templates.js";import{parseRoleSections as C}from"./role-section.js";import{formatRoleAgentLabel as N}from"./roster-render.js";import{formatDroneAddressToken as T}from"./drone-address.js";import{shellEscape as f}from"./shell-escape.js";function q(e){if(!e||!e.trim())return null;try{const t=JSON.parse(e)?.source;return typeof t=="string"?t:null}catch{return null}}function D(e,o,t){return e==="codex"?["Required Codex wake path: Borg activity stream \u2192 inbox wake channel via app-server remote control.","On every wake, run `borg_read-log unread_only=true` and drain until caught up.","No additional scheduler setup is required.",'Degraded fallback (only if remote control is unavailable): on return, call `borg_regen mode="full"` and drain unread log.'].join(" "):e==="opencode"?"Wake path: OpenCode wakes via SDK-driven entry injection into the TUI session \u2014 there is no tail-Monitor or `/loop` heartbeat to arm. Cube posts are delivered directly into your active session. Use `borg_regen` at any time for fresh context.":["Arm your wake path before working:",`1. **Inbox Monitor** (wake path) \u2014 run a persistent Monitor on \`${t?`borg-inbox-monitor --state-root ${f(t)} ${f(o)}`:`borg-inbox-monitor ${f(o)}`}\` so cube posts wake you in real time.`,"2. **Engage `/loop`** (self-paced) so you keep waking to triage the cube.","3. **Fallback heartbeat** \u2014 set a ~3600s (60-min) `ScheduleWakeup` so you never go fully silent."].join(`
2
- `)}function H(e,o){return{cubeName:o?.cube?.name??e.name,droneLabel:o?.drone?.label??e.droneLabel,roleName:o?.role?.name??e.roleName??null}}function V(e){const{cubeName:o,droneLabel:t,roleName:n,inboxPath:r,monitorStateRoot:i,agentKind:s,source:c}=e,l=c==="clear"?s==="codex"?"\n_(`/clear` cleared your conversation; Codex remote-control wake remains active. Follow the required Codex wake path below.)_\n":"\n_(`/clear` cleared your conversation + session-scoped `/loop` and `ScheduleWakeup` heartbeat \u2014 re-establish them now.)_\n":"";return[`# Cube: ${o} \u2014 ${t}`,"",`**Your role:** ${n||"_(call `borg_regen` to load)_"}`,l,"You are a Borg drone \u2014 coordinate through the cube log, and never pause for the user. Blocked \u2192 escalate to your cube's coordinating role.","",D(s,r,i),"",'REQUIRED BEFORE ACTING OR POSTING: (1) call `borg_regen mode="full"`; (2) load the cube directive and conventions with `borg_cube`; (3) ensure your own role playbook/details are loaded \u2014 full regen normally supplies them, but call `borg_role` if missing or after compaction; (4) load `borg_playbook` once per session for the complete operating disciplines. Do not proceed until all four are in context. This orientation stays lean and does not inline them. If you know this session\'s model, include `model="<model-id>"` in the initial full regen for advisory roster metadata.',""].join(`
3
- `)}function E(){return"## How to operate as a Drone\n\nYou're a Drone in a Cube. Coordinate with other drones through the activity log.\n\n**User asks how Borg MCP works** \u2014 a feature, setup, pricing, or concept question? Call `borg_docs {topic}` for the documentation index, then WebFetch the matching section URL and answer from the page. Don't guess borgmcp's own behavior from memory.\n\n**Tools:**\n- `borg_regen` \u2014 refresh full state (your role, roster, unread-log COUNT, and fetch-on-demand pointers) in one call; the cube directive (\u2192 `borg_cube`), the operating-playbook detail (\u2192 `borg_playbook`), and the recent-log payload (\u2192 `borg_read-log` when count >0) are NOT inlined \u2014 fetch them on demand\n- `borg_cube` \u2014 re-read the cube directive and the role overview\n- `borg_role` \u2014 re-read your role's detailed playbook\n- `borg_roster` \u2014 see who else is connected\n- `borg_read-log unread_only=true [limit]` \u2014 drain unread log entries from your server-side cursor\n- `borg_log <message>` \u2014 append to the log\n- `borg_assimilate <cube>` \u2014 switch to a different cube\n\n**How coordination works:** the Cube gives primitives, not workflows. Your role's `detailed_description` (above) is your playbook \u2014 its conventions + signals come from there, not the system. The log is the coordination channel. Different cubes, different conventions.\n\n**Default: act autonomously, coordinate through the log.** Don't wait for user input. Need input \u2192 post the question, continue other work, other drones respond. The human supervisor is reachable through your cube's coordinating / human-seat role (the role your cube designates for direction + integration), or the Queen role when the seat is delegated to a drone \u2014 one continuous seat. Your role's `detailed_description` says when to escalate + which decisions need human input; follow it.\n\n**Operating loop \u2014 each wake, in order:**\n1. Drain unread: `borg_read-log unread_only=true` (oldest-first, repeat until `behind_by=0`) before acting. The \"Cube log\" section gives your UNREAD COUNT.\n2. Apply your role's conventions to each entry. Act on: questions you can answer; blocked peers you can unblock; unowned work you can claim; decisions affecting you.\n3. Actionable signal \u2192 act + post the convention. Don't wait to be asked.\n4. User prompt waiting \u2192 respond, informed by cube context; log substantive units (shipped changes, blockers, findings) regardless of who initiated.\n5. Nothing actionable + no prompt \u2192 done; wait for next wake.\n\n**On a `<task-notification>` wake:** the payload is a truncatable preview; the full entry is in the DB. Drain: `borg_read-log unread_only=true limit=20`, repeat until `behind_by=0`. Do NOT triage with `since=<notification timestamp>` (strict-after \u2014 skips the boundary entry) or a bare window (skips older-unread during bursts).\n\n**On first wake this session:** post one `ARRIVAL: <your-label> (<your-role>) online on <hostname> at <project-path>` (run `hostname`; use cwd for the path). One-time per session \u2014 don't repeat on later wakes; skip if already posted this session (e.g. after a `/mcp` reconnect).\n\n**When a log entry routes work to you** (a routing/assignment-class entry per your cube's conventions that names your label + asks for action, or a direct `<your-label>:` mention): call `borg_ack entry_id=<id>` within ~60s. Use the `borg_ack` TOOL, not an in-band `ACK:` post (it records a queryable flag + wakes the author's Monitor + keeps the log clean). Ack = receipt, not completion (`STARTING` / `DONE` still apply). Ack only routing-class signals \u2014 not every mention.\n\n**Claim a work item before you start it (`borg_ack ... kind=claim`):** `borg_ack` has two kinds \u2014 `ack` (receipt, the default) and `claim` (advisory ownership of a routed work item you are about to take). When a routed entry could be picked up by more than one drone, `borg_ack entry_id=<id> kind=claim` BEFORE starting \u2014 it announces you are taking it so peers skip the duplicate work, and wakes the rest of the entry's audience. If a live peer already holds the claim, skip it; if the claim is STALE (the claimant went silent past the wake-path SLA), re-claim and proceed. A claim is ADVISORY only \u2014 it NEVER substitutes for the completion or approval signal your role's conventions require; a bogus or abandoned claim can at most delay a work item, never bypass its real gate.\n\n**When stuck:** post your blocker per your role's conventions, continue other work. Escalation is per your role detail, not by stalling.\n\n**Anti-passive (lane idle = no work routed to you, no actionable signal in the log):**\n- If your work arrives via dispatch / a work queue: when your lane goes idle, post your role's availability signal (capacity clean, awaiting next assignment from your coordinating role) \u2014 once per idle period, don't spam. No assignment in ~15 min \u2192 ping your coordinating role (capacity available since <time>; any queue item to pick up?).\n- If your work is SELF-DIRECTED (not dispatch-driven): do NOT post an availability signal \u2014 proactively surface lane-substantive work per your role (reviews, audits, proposals, coherence / quality sweeps on relevant in-flight work).\n- Route work-asks through your cube's coordinating role, never directly to the human Queen.\n\n**Verify factual claims:** verify any verifiable claim \u2014 versions, code-state, prod behavior, npm state \u2014 against the SOURCE-OF-TRUTH surface (`git tag` / `git show <ref>:<path>` / grep, `curl` / `wrangler tail`, `npm view`, the live DB) BEFORE writing it; never a derivative artifact (another post, summary, or your own prior framing). The full discipline \u2014 the v1/v2/v3 sharpening levels, the per-claim-type concrete surfaces, and four-surface propagation (brainstorm / comment / review / issue-filing) \u2014 is in the operating-playbook chapter (`borg_playbook`; loaded via the session-start block in your regen).\n\n**Posting to the log:** post per your role's conventions whenever you start/finish a task, get stuck, answer a drone, or learn something others need \u2014 regardless of who initiated (a log signal, your own scan, or a user prompt). Conventions live in your role detail; the system is vocabulary-agnostic.\n\n**Routing posts \u2014 widen the directed default:** the taxonomy routes most prefixes DIRECTED to your cube's coordinating role; your `to:` / `visibility:` overrides it. Widen when a post must reach more than the coordinating role:\n- Posting a verdict / decision / result a specific drone is waiting on: add `to:[that drone]` so they're WOKEN \u2014 without it they can be left UNAWARE of their own merge or feedback. Directed governs the WAKE; it is NOT read-confidentiality: every member can read every entry \u2014 the cube is the trust boundary \u2014 so never post secrets relying on `to:[x]`.\n- Any drone posting a multi-seat DELIVERABLE (spec / security classification / review artifact 3+ seats build or gate against): pass `visibility:broadcast` (or `to:[the seats]`) EVEN IF your prefix (`DONE` etc.) is a directed status class \u2014 else only your coordinating role wakes (taxonomy routes by prefix, not payload) and the building/gating seats miss it.\n\n**Pre-commit git hygiene (universal):**\n\nAny drone that commits code: run `git diff --staged --stat` before `git commit` to verify file count + LOC direction + paths match your intent. Catches deleted files / anomalous -LOC / wrong paths pre-push. Your role may layer more git rules (code-implementing + coordinating roles typically carry the full set)."}const G=E();function J(){return'## Operating playbook \u2014 full disciplines (borg_playbook chapter)\n\nThis is the on-demand detail behind the rule-spine in your regen. Load it ONCE per session; it is static \u2014 do not re-fetch on every wake.\n\n**Verifying factual claims:**\n\nAny time you make a factual claim that could be verified \u2014 "this shipped as version Y", "function Z does W", "endpoint A returns B in prod", "package P is at version Q on npm" \u2014 verify the claim against a SOURCE-OF-TRUTH surface BEFORE writing it, not against a derivative artifact (another post, doc, summary, or your own prior framing). Three sharpening levels:\n\n- **v1 (verify against the actual surface):** check the claim against the surface it describes (e.g. a code-state claim \u2192 grep the file). Apply when the claim is about code-state.\n- **v2 (source-of-truth vs derivative artifacts):** when the verification surface itself could carry the original error chain (another post citing the same wrong claim, a doc copy-mirrored from the post you\'re checking), verify against the canonical source-of-truth: `git tag` for version-attribution, code-by-grep / direct file read for code-state, live `curl` or `wrangler tail` for prod-state, `npm view` for npm-state. Apply when version numbers, deploy timestamps, or other discrete facts are in scope.\n- **v3 (end-to-end execution path vs originating mechanism):** when verifying a live-mechanism claim ("the watchdog wakes silent drones"), verify the END-TO-END execution path, not just each isolated component \u2014 each isolated mechanism can be correct while the path between them silently breaks. Apply when live-mechanism correctness is being claimed; trace the path the wake/value/state actually takes from origin to terminal observer.\n\n**Concrete verification surfaces by claim type:**\n- Version attribution \u2192 `git tag --contains <sha>` or `git log --oneline <tag>`\n- Code state \u2192 match the grep surface to the claim surface:\n - Local uncommitted claim \u2192 `grep -n "<symbol>" <file>` or direct file read in the working tree\n - `origin/main`, PR head, branch, merge-SHA, or tag claim \u2192 `git show <ref>:<path> | grep -n "<symbol>"` (examples: `git show origin/main:workers/heartbeat.ts | grep -n "last_log_post"`; `git show origin/feat/foo:client/src/log-stream.ts | grep -n "ownDrone"`; `git show abc1234:workers/cubes.ts | grep -n "visibility"`)\n- Prod state \u2192 `curl https://<endpoint>` or `wrangler tail --env production`\n- npm registry state \u2192 `npm view <package>@<version>` or `npm view <package>@latest`\n- DB state \u2192 query through the existing `db` interface; never trust a doc claim about row counts / column values\n- Cube log state \u2192 `borg_read-log unread_only=true` for wake triage, draining until `behind_by=0`; don\'t cite from memory or from another drone\'s summary\n- Ratified cube decision \u2192 `borg_decisions {topic}` \u2014 cite the registry\'s active decision by topic; NEVER restate a ratified decision from memory (a memory restatement drifts on the axis). A ratified decision is a first-class verifiable claim type with its own source of truth: the active registry entry. Recording one is `borg_decide` (seat-holder only \u2014 recording IS the ratification act).\n\n**The discipline is universal to reviewer-class actions** (Code Reviewer formal gates + Security Auditor SR gates + PM-courtesy verifications + UX-courtesy reviews + any drone making a verification-worthy factual claim in their cube-log post). It lives in this universal playbook rather than any one role\'s text because it applies to ALL reviewers.\n\n**Four-surface propagation:**\n\nThe discipline applies at FOUR surfaces. Catches at the surface closest to origin are cheapest; catches at later surfaces have already propagated through earlier consumers:\n\n- **Surface 1 (brainstorm-proposal time)**: when a brainstorm contribution names specific code identifiers / API field names / enum values / column names / function signatures, the PROPOSING drone source-grep\'s the referenced file BEFORE composing the proposal. If the proposal cites current `origin/main` or a branch/SHA, grep that ref via `git show <ref>:<path> | grep`; working-tree grep is only for explicitly local/uncommitted claims. Cheapest catch surface; one drone catches one error.\n- **Surface 2 (comment/JSDoc/docstring writing time)**: when an implementation comment cites cross-file invariants (other modules\' thresholds, schema columns, enum values, semantic contracts), the WRITING drone source-grep\'s the referenced file BEFORE writing the comment. If the comment describes a merged/base/PR-head state, grep the named ref via `git show <ref>:<path> | grep`; don\'t let a stale local checkout stand in for the ref being described. Mid-cost catch; one drone catches one error but downstream reviewers may inherit the wrong mental model from the comment.\n- **Surface 3 (review-time verification)**: the existing review-class discipline (Code Reviewer formal gates + Security Auditor SR gates + PM/UX/QA courtesy reviews). Late catch opportunity; if the error propagated through Surfaces 1 + 2, multiple reviewers may have already trusted the framing instead of source-grepping themselves.\n- **Surface 4 (durable-tracking-artifact-writing time)**: when filing a deferred-tracking issue from a cube event payload, the FILING drone fetches the originating entry\'s full body from the cube log BEFORE composing the issue body. For routine wake triage, use `borg_read-log unread_only=true` and drain until caught up; do not rely on a truncated event preview or a `since=<same timestamp>` read, which can skip the boundary entry. Cube event previews can truncate substantive content (mid-paragraph cuts on long entries); filing from the truncated preview trusts a derivative artifact instead of the source-of-truth full entry. Most expensive surface \u2014 the filed issue becomes the cube\'s durable cross-cycle memory; correcting it requires a follow-up correction post, and later pickup drones inherit the incomplete framing if the correction is missed.\n\n**Ratified-decision drift is a four-surface drift-class.** A ratified cube decision restated from memory drifts exactly like a code-identifier claim \u2014 it propagates dispatch (Surface 1, brainstorm) \u2192 copy (Surface 2, comment) \u2192 gate (Surface 3, review), and the cheapest catch is at the brainstorm surface. At each surface, a drone restating a ratified decision source-reads `borg_decisions {topic}` FIRST: the active registry entry is the source of truth; your memory is a derivative artifact. Core rule \u2014 **cite ratified decisions by topic; never restate one from memory.**'}function I(e){const o=typeof e=="string"?new Date(e):e,t=Date.now()-o.getTime();if(!Number.isFinite(t)||t<0)return"just now";const n=Math.floor(t/1e3);if(n<60)return`${n}s ago`;const r=Math.floor(n/60);if(r<60)return`${r}m ago`;const i=Math.floor(r/60);return i<24?`${i}h ago`:`${Math.floor(i/24)}d ago`}function L(e){return e==null||Array.isArray(e)&&e.length===0?"Tip: no message taxonomy declared \u2014 set one to enable intent-based smart routing (#468). Use borg_update-cube with a taxonomy array, or add classes with borg_patch-taxonomy-class.":""}function K(e,o){return e.drone?.label??o??null}let y=!1,b=null;function Q(){y=!1,b=null}function $(e){const o=e??"",t=_.filter(n=>o.includes(n));return[...x,...t]}function P(e,o){return`rationale \u2192 borg_role-rationale ${JSON.stringify(e)} ${JSON.stringify(o)}`}function X(e){const o=e.match(/borg_role-rationale\s+("(?:(?:\\.)|[^"\\])*")\s+("(?:(?:\\.)|[^"\\])*")/);if(!o)return null;try{return{role:JSON.parse(o[1]),section:JSON.parse(o[2])}}catch{return null}}const F=[...x,..._];function U(e,o){return C(o??"").map(r=>{if(r.kind!=="label"||r.heading==null||!r.heading.trim().toLowerCase().endsWith("rationale")||F.some(l=>r.body.includes(l)))return r.body;const s=r.body.indexOf(`
4
- `);return(s===-1?r.body+`
5
- `:r.body.slice(0,s+1))+P(e,r.heading)+`
6
- `}).join("")}function Z(e,o={}){const t=o.mode??"full",n=e.roles.map(a=>`- **${a.name}**${a.is_default?" _(default)_":""} \u2014 ${a.short_description||"_(no short description)_"}`).join(`
7
- `),r=e.drones.map(a=>{const h=e.roles.find(p=>p.id===a.role_id),g=N(h?.name??"?",a.agent_kind);return`- **${a.label}** (${g}) \u2014 last seen ${I(new Date(a.last_seen))}`}).join(`
8
- `)||"_(no drones connected)_",i=typeof e.behind_by=="number"?e.behind_by:null,s=i===null?"Call `borg_read-log unread_only=true` to check for and drain any unread log entries (the log payload is not inlined in regen).":i>0?`You have **${i}** unread log ${i===1?"entry":"entries"}. Drain them with \`borg_read-log unread_only=true\` (oldest-unread first; repeat until \`behind_by=0\`). The log payload is not inlined here \u2014 fetch on demand.`:"You're caught up \u2014 **0** unread log entries. No need to read the log right now.",l=(i??0)===0&&e.drones.length<=1?["## Getting started","","Welcome to your first cube. Here's how to get going:","",'1. Post your first activity: `borg_log message="Starting work on <your task>"`',"2. Invite another agent session: open a new terminal and run `borg assimilate --worktree <name>`","3. Check who's here: `borg_roster`","","---",""].join(`
9
- `):"",v=L(e.cube.message_taxonomy),S=12,m=Array.isArray(e.decisions)?e.decisions:[],w=(()=>{if(m.length===0)return"";const a=m.slice(0,S),h=a.map(p=>`- **${p.topic}:** ${p.decision}`),g=m.length-a.length;return g>0&&h.push(`- _+${g} more \u2014 \`borg_decisions\`_`),["## Ratified decisions","Cite these by topic \u2014 do NOT restate a ratified decision from memory.",...h].join(`
10
- `)})(),d=e.role.detailed_description_hash??null,O=e.role.detailed_description?U(e.role.name,e.role.detailed_description):"_(no detailed description set)_",R="Before you post or act, load your full operating context \u2014 once per session; static, do NOT re-fetch on every wake:\n- `borg_playbook` \u2014 your full operating disciplines (verification, four-surface propagation, ack / routing / idle detail).\n- `borg_cube` \u2014 the cube directive + conventions (log vocabulary, project / git / dispatch conventions).",k=t==="full"||d==null||d!==b,A=t==="full"||!y,u=[l+`# Cube: ${e.cube.name} \u2014 ${e.drone.label}`,"",`**Your role:** ${e.role.name}`,""];return t==="lite"&&u.push('_(lite regen \u2014 the role playbook may be omitted when unchanged; your operating context (playbook + cube directive) loads via the Session-start block (borg_playbook + borg_cube). If the playbook is NOT in your current context (e.g. after a context-compaction), call `borg_regen mode="full"` to re-orient.)_',""),u.push(t==="full"?"## Session start \u2014 required before acting":"## Session start",t==="full"?R:'Operating context (playbook + cube directive) was loaded at session start \u2014 re-fetch `borg_playbook` / `borg_cube` ONLY after a context-compaction (a `mode="full"` regen), not on every wake.',"",...v?[v,""]:[],`## Your role: ${e.role.name}`,k?O:["_(role playbook unchanged since your last full/lite regen; omitted in lite mode)_","",...$(e.role.detailed_description)].join(`
11
- `),"","## Roles in this cube",n,"","## Connected drones",r,"","## Cube log",s,...w?["",w]:[]),A&&(u.push("",E()),y=!0),k&&d!=null&&(b=d),u.join(`
12
- `)}function z(e,o,t){const n=o.get(e.drone_id),r=n?t.get(n.role_id):null,i=new Date(e.created_at).toISOString(),s=typeof e.id=="string"&&e.id.length>0?` [entry_id: ${e.id}]`:"",c=typeof e.drone_id=="string"&&e.drone_id.length>0?` ${T(e.drone_id)}`:"";return`**[${i}]**${s}${c} ${n?.label??"?"} (${r?.name??"?"}): ${e.message}`}export{G as DRONE_PLAYBOOK,Q as __resetRegenSessionState,U as compressRoleText,V as formatLeanOrientation,z as formatLogEntryMarkdown,P as formatRationalePointer,Z as formatRegenMarkdown,E as getDronePlaybook,J as getDronePlaybookChapter,I as humanAgo,L as nullTaxonomyTip,q as parseHookSource,X as parseRationalePointer,K as regenWakePathDroneLabel,H as resolveLeanIdentity,D as wakePathArming};
1
+ import{ROLE_SCOPED_SAFETY_DISCIPLINES as _,UNIVERSAL_SAFETY_DISCIPLINES as x}from"./templates.js";import{parseRoleSections as C}from"./role-section.js";import{formatRoleAgentLabel as N}from"./roster-render.js";import{formatDroneAddressToken as T}from"./drone-address.js";import{shellEscape as f}from"./shell-escape.js";function q(e){if(!e||!e.trim())return null;try{const t=JSON.parse(e)?.source;return typeof t=="string"?t:null}catch{return null}}function D(e,o,t){return e==="codex"?["Required Codex wake path: Borg activity stream \u2192 inbox wake channel via app-server remote control.","On every wake, run `borg_read-log unread_only=true` and drain until caught up.","No additional scheduler setup is required.",'Degraded fallback (only if remote control is unavailable): on return, call `borg_regen mode="full"` and drain unread log.'].join(" "):e==="opencode"?"Wake path: OpenCode wakes via SDK-driven entry injection into the TUI session \u2014 there is no tail-Monitor or `/loop` heartbeat to arm. Cube posts are delivered directly into your active session. Use `borg_regen` at any time for fresh context.":["Arm your wake path before working:",`1. **Inbox Monitor** (wake path) \u2014 run a persistent Monitor on \`${t?`borg-inbox-monitor --state-root ${f(t)} ${f(o)}`:`borg-inbox-monitor ${f(o)}`}\` so cube posts wake you in real time.`,"2. **Engage `/loop`** (self-paced) so you keep waking to triage the cube.","3. **Adaptive recovery deadline** \u2014 after wake/triage, set ONE `ScheduleWakeup`: [9000, 12600]s (3h \xB130m) when Monitor is healthy or indeterminate; [720, 1080]s (15m \xB13m) only when explicitly broken. Re-arm Monitor; retry short until healthy. A real Monitor wake resets\u2014not stacks\u2014it.","4. **Recovery tick** \u2014 drain `borg_read-log unread_only=true` first. If empty, do not full-regen or make a liveness post: check wake status, set the deadline, resume prior work. This reduces client fallback churn; safety probes may still wake."].join(`
2
+ `)}function H(e,o){return{cubeName:o?.cube?.name??e.name,droneLabel:o?.drone?.label??e.droneLabel,roleName:o?.role?.name??e.roleName??null}}function V(e){const{cubeName:o,droneLabel:t,roleName:n,inboxPath:r,monitorStateRoot:i,agentKind:a,source:c}=e,l=c==="clear"?a==="codex"?"\n_(`/clear` cleared your conversation; Codex remote-control wake remains active. Follow the required Codex wake path below.)_\n":a==="claude"?["\n_(`/clear` cleared Claude's `/loop` and recovery deadline \u2014 re-arm them now.)_",'_Quiet-clear fallback: if a later turn follows silence, inspect `borg_stream-status` + `borg_roster`; call `borg_regen mode="full"`, drain `borg_read-log unread_only=true`, then re-arm Monitor, `/loop`, and the recovery deadline._\n'].join(`
3
+ `):"":"";return[`# Cube: ${o} \u2014 ${t}`,"",`**Your role:** ${n||"_(call `borg_regen` to load)_"}`,l,"You are a Borg drone \u2014 coordinate through the cube log, and never pause for the user. Blocked \u2192 escalate to your cube's coordinating role.","",D(a,r,i),"",'REQUIRED BEFORE ACTING OR POSTING: (1) call `borg_regen mode="full"`; (2) load the cube directive and conventions with `borg_cube`; (3) ensure your own role playbook/details are loaded \u2014 full regen normally supplies them, but call `borg_role` if missing or after compaction; (4) load `borg_playbook` once per session for the complete operating disciplines. Do not proceed until all four are in context. This orientation stays lean and does not inline them. If you know this session\'s model, include `model="<model-id>"` in the initial full regen for advisory roster metadata.',""].join(`
4
+ `)}function E(){return"## How to operate as a Drone\n\nYou're a Drone in a Cube. Coordinate with other drones through the activity log.\n\n**User asks how Borg MCP works** \u2014 a feature, setup, pricing, or concept question? Call `borg_docs {topic}` for the documentation index, then WebFetch the matching section URL and answer from the page. Don't guess borgmcp's own behavior from memory.\n\n**Tools:**\n- `borg_regen` \u2014 refresh full state (your role, roster, unread-log COUNT, and fetch-on-demand pointers) in one call; the cube directive (\u2192 `borg_cube`), the operating-playbook detail (\u2192 `borg_playbook`), and the recent-log payload (\u2192 `borg_read-log` when count >0) are NOT inlined \u2014 fetch them on demand\n- `borg_cube` \u2014 re-read the cube directive and the role overview\n- `borg_role` \u2014 re-read your role's detailed playbook\n- `borg_roster` \u2014 see who else is connected\n- `borg_read-log unread_only=true [limit]` \u2014 drain unread log entries from your server-side cursor\n- `borg_log <message>` \u2014 append to the log\n- `borg_assimilate <cube>` \u2014 switch to a different cube\n\n**How coordination works:** the Cube gives primitives, not workflows. Your role's `detailed_description` (above) is your playbook \u2014 its conventions + signals come from there, not the system. The log is the coordination channel. Different cubes, different conventions.\n\n**Default: act autonomously, coordinate through the log.** Don't wait for user input. Need input \u2192 post the question, continue other work, other drones respond. The human supervisor is reachable through your cube's coordinating / human-seat role (the role your cube designates for direction + integration), or the Queen role when the seat is delegated to a drone \u2014 one continuous seat. Your role's `detailed_description` says when to escalate + which decisions need human input; follow it.\n\n**Operating loop \u2014 each wake, in order:**\n1. Drain unread: `borg_read-log unread_only=true` (oldest-first, repeat until `behind_by=0`) before acting. The \"Cube log\" section gives your UNREAD COUNT.\n2. Apply your role's conventions to each entry. Act on: questions you can answer; blocked peers you can unblock; unowned work you can claim; decisions affecting you.\n3. Actionable signal \u2192 act + post the convention. Don't wait to be asked.\n4. User prompt waiting \u2192 respond, informed by cube context; log substantive units (shipped changes, blockers, findings) regardless of who initiated.\n5. Nothing actionable + no prompt \u2192 done; wait for next wake.\n\n**On a `<task-notification>` wake:** the payload is a truncatable preview; the full entry is in the DB. Drain: `borg_read-log unread_only=true limit=20`, repeat until `behind_by=0`. Do NOT triage with `since=<notification timestamp>` (strict-after \u2014 skips the boundary entry) or a bare window (skips older-unread during bursts).\n\n**On first wake this session:** post one `ARRIVAL: <your-label> (<your-role>) online on <hostname> at <project-path>` (run `hostname`; use cwd for the path). One-time per session \u2014 don't repeat on later wakes; skip if already posted this session (e.g. after a `/mcp` reconnect).\n\n**When a log entry routes work to you** (a routing/assignment-class entry per your cube's conventions that names your label + asks for action, or a direct `<your-label>:` mention): call `borg_ack entry_id=<id>` within ~60s. Use the `borg_ack` TOOL, not an in-band `ACK:` post (it records a queryable flag + wakes the author's Monitor + keeps the log clean). Ack = receipt, not completion (`STARTING` / `DONE` still apply). Ack only routing-class signals \u2014 not every mention.\n\n**Claim a work item before you start it (`borg_ack ... kind=claim`):** `borg_ack` has two kinds \u2014 `ack` (receipt, the default) and `claim` (advisory ownership of a routed work item you are about to take). When a routed entry could be picked up by more than one drone, `borg_ack entry_id=<id> kind=claim` BEFORE starting \u2014 it announces you are taking it so peers skip the duplicate work, and wakes the rest of the entry's audience. If a live peer already holds the claim, skip it; if the claim is STALE (the claimant went silent past the wake-path SLA), re-claim and proceed. A claim is ADVISORY only \u2014 it NEVER substitutes for the completion or approval signal your role's conventions require; a bogus or abandoned claim can at most delay a work item, never bypass its real gate.\n\n**When stuck:** post your blocker per your role's conventions, continue other work. Escalation is per your role detail, not by stalling.\n\n**Anti-passive (lane idle = no work routed to you, no actionable signal in the log):**\n- If your work arrives via dispatch / a work queue: when your lane goes idle, post your role's availability signal (capacity clean, awaiting next assignment from your coordinating role) \u2014 once per idle period, don't spam. No assignment in ~15 min \u2192 ping your coordinating role (capacity available since <time>; any queue item to pick up?).\n- If your work is SELF-DIRECTED (not dispatch-driven): do NOT post an availability signal \u2014 proactively surface lane-substantive work per your role (reviews, audits, proposals, coherence / quality sweeps on relevant in-flight work).\n- Route work-asks through your cube's coordinating role, never directly to the human Queen.\n\n**Verify factual claims:** verify any verifiable claim \u2014 versions, code-state, prod behavior, npm state \u2014 against the SOURCE-OF-TRUTH surface (`git tag` / `git show <ref>:<path>` / grep, `curl` / `wrangler tail`, `npm view`, the live DB) BEFORE writing it; never a derivative artifact (another post, summary, or your own prior framing). The full discipline \u2014 the v1/v2/v3 sharpening levels, the per-claim-type concrete surfaces, and four-surface propagation (brainstorm / comment / review / issue-filing) \u2014 is in the operating-playbook chapter (`borg_playbook`; loaded via the session-start block in your regen).\n\n**Posting to the log:** post per your role's conventions whenever you start/finish a task, get stuck, answer a drone, or learn something others need \u2014 regardless of who initiated (a log signal, your own scan, or a user prompt). Conventions live in your role detail; the system is vocabulary-agnostic.\n\n**Routing posts \u2014 widen the directed default:** the taxonomy routes most prefixes DIRECTED to your cube's coordinating role; your `to:` / `visibility:` overrides it. Widen when a post must reach more than the coordinating role:\n- Posting a verdict / decision / result a specific drone is waiting on: add `to:[that drone]` so they're WOKEN \u2014 without it they can be left UNAWARE of their own merge or feedback. Directed governs the WAKE; it is NOT read-confidentiality: every member can read every entry \u2014 the cube is the trust boundary \u2014 so never post secrets relying on `to:[x]`.\n- Any drone posting a multi-seat DELIVERABLE (spec / security classification / review artifact 3+ seats build or gate against): pass `visibility:broadcast` (or `to:[the seats]`) EVEN IF your prefix (`DONE` etc.) is a directed status class \u2014 else only your coordinating role wakes (taxonomy routes by prefix, not payload) and the building/gating seats miss it.\n\n**Pre-commit git hygiene (universal):**\n\nAny drone that commits code: run `git diff --staged --stat` before `git commit` to verify file count + LOC direction + paths match your intent. Catches deleted files / anomalous -LOC / wrong paths pre-push. Your role may layer more git rules (code-implementing + coordinating roles typically carry the full set)."}const G=E();function J(){return'## Operating playbook \u2014 full disciplines (borg_playbook chapter)\n\nThis is the on-demand detail behind the rule-spine in your regen. Load it ONCE per session; it is static \u2014 do not re-fetch on every wake.\n\n**Verifying factual claims:**\n\nAny time you make a factual claim that could be verified \u2014 "this shipped as version Y", "function Z does W", "endpoint A returns B in prod", "package P is at version Q on npm" \u2014 verify the claim against a SOURCE-OF-TRUTH surface BEFORE writing it, not against a derivative artifact (another post, doc, summary, or your own prior framing). Three sharpening levels:\n\n- **v1 (verify against the actual surface):** check the claim against the surface it describes (e.g. a code-state claim \u2192 grep the file). Apply when the claim is about code-state.\n- **v2 (source-of-truth vs derivative artifacts):** when the verification surface itself could carry the original error chain (another post citing the same wrong claim, a doc copy-mirrored from the post you\'re checking), verify against the canonical source-of-truth: `git tag` for version-attribution, code-by-grep / direct file read for code-state, live `curl` or `wrangler tail` for prod-state, `npm view` for npm-state. Apply when version numbers, deploy timestamps, or other discrete facts are in scope.\n- **v3 (end-to-end execution path vs originating mechanism):** when verifying a live-mechanism claim ("the watchdog wakes silent drones"), verify the END-TO-END execution path, not just each isolated component \u2014 each isolated mechanism can be correct while the path between them silently breaks. Apply when live-mechanism correctness is being claimed; trace the path the wake/value/state actually takes from origin to terminal observer.\n\n**Concrete verification surfaces by claim type:**\n- Version attribution \u2192 `git tag --contains <sha>` or `git log --oneline <tag>`\n- Code state \u2192 match the grep surface to the claim surface:\n - Local uncommitted claim \u2192 `grep -n "<symbol>" <file>` or direct file read in the working tree\n - `origin/main`, PR head, branch, merge-SHA, or tag claim \u2192 `git show <ref>:<path> | grep -n "<symbol>"` (examples: `git show origin/main:workers/heartbeat.ts | grep -n "last_log_post"`; `git show origin/feat/foo:client/src/log-stream.ts | grep -n "ownDrone"`; `git show abc1234:workers/cubes.ts | grep -n "visibility"`)\n- Prod state \u2192 `curl https://<endpoint>` or `wrangler tail --env production`\n- npm registry state \u2192 `npm view <package>@<version>` or `npm view <package>@latest`\n- DB state \u2192 query through the existing `db` interface; never trust a doc claim about row counts / column values\n- Cube log state \u2192 `borg_read-log unread_only=true` for wake triage, draining until `behind_by=0`; don\'t cite from memory or from another drone\'s summary\n- Ratified cube decision \u2192 `borg_decisions {topic}` \u2014 cite the registry\'s active decision by topic; NEVER restate a ratified decision from memory (a memory restatement drifts on the axis). A ratified decision is a first-class verifiable claim type with its own source of truth: the active registry entry. Recording one is `borg_decide` (seat-holder only \u2014 recording IS the ratification act).\n\n**The discipline is universal to reviewer-class actions** (Code Reviewer formal gates + Security Auditor SR gates + PM-courtesy verifications + UX-courtesy reviews + any drone making a verification-worthy factual claim in their cube-log post). It lives in this universal playbook rather than any one role\'s text because it applies to ALL reviewers.\n\n**Four-surface propagation:**\n\nThe discipline applies at FOUR surfaces. Catches at the surface closest to origin are cheapest; catches at later surfaces have already propagated through earlier consumers:\n\n- **Surface 1 (brainstorm-proposal time)**: when a brainstorm contribution names specific code identifiers / API field names / enum values / column names / function signatures, the PROPOSING drone source-grep\'s the referenced file BEFORE composing the proposal. If the proposal cites current `origin/main` or a branch/SHA, grep that ref via `git show <ref>:<path> | grep`; working-tree grep is only for explicitly local/uncommitted claims. Cheapest catch surface; one drone catches one error.\n- **Surface 2 (comment/JSDoc/docstring writing time)**: when an implementation comment cites cross-file invariants (other modules\' thresholds, schema columns, enum values, semantic contracts), the WRITING drone source-grep\'s the referenced file BEFORE writing the comment. If the comment describes a merged/base/PR-head state, grep the named ref via `git show <ref>:<path> | grep`; don\'t let a stale local checkout stand in for the ref being described. Mid-cost catch; one drone catches one error but downstream reviewers may inherit the wrong mental model from the comment.\n- **Surface 3 (review-time verification)**: the existing review-class discipline (Code Reviewer formal gates + Security Auditor SR gates + PM/UX/QA courtesy reviews). Late catch opportunity; if the error propagated through Surfaces 1 + 2, multiple reviewers may have already trusted the framing instead of source-grepping themselves.\n- **Surface 4 (durable-tracking-artifact-writing time)**: when filing a deferred-tracking issue from a cube event payload, the FILING drone fetches the originating entry\'s full body from the cube log BEFORE composing the issue body. For routine wake triage, use `borg_read-log unread_only=true` and drain until caught up; do not rely on a truncated event preview or a `since=<same timestamp>` read, which can skip the boundary entry. Cube event previews can truncate substantive content (mid-paragraph cuts on long entries); filing from the truncated preview trusts a derivative artifact instead of the source-of-truth full entry. Most expensive surface \u2014 the filed issue becomes the cube\'s durable cross-cycle memory; correcting it requires a follow-up correction post, and later pickup drones inherit the incomplete framing if the correction is missed.\n\n**Ratified-decision drift is a four-surface drift-class.** A ratified cube decision restated from memory drifts exactly like a code-identifier claim \u2014 it propagates dispatch (Surface 1, brainstorm) \u2192 copy (Surface 2, comment) \u2192 gate (Surface 3, review), and the cheapest catch is at the brainstorm surface. At each surface, a drone restating a ratified decision source-reads `borg_decisions {topic}` FIRST: the active registry entry is the source of truth; your memory is a derivative artifact. Core rule \u2014 **cite ratified decisions by topic; never restate one from memory.**'}function I(e){const o=typeof e=="string"?new Date(e):e,t=Date.now()-o.getTime();if(!Number.isFinite(t)||t<0)return"just now";const n=Math.floor(t/1e3);if(n<60)return`${n}s ago`;const r=Math.floor(n/60);if(r<60)return`${r}m ago`;const i=Math.floor(r/60);return i<24?`${i}h ago`:`${Math.floor(i/24)}d ago`}function L(e){return e==null||Array.isArray(e)&&e.length===0?"Tip: no message taxonomy declared \u2014 set one to enable intent-based smart routing (#468). Use borg_update-cube with a taxonomy array, or add classes with borg_patch-taxonomy-class.":""}function Q(e,o){return e.drone?.label??o??null}let y=!1,b=null;function K(){y=!1,b=null}function $(e){const o=e??"",t=_.filter(n=>o.includes(n));return[...x,...t]}function P(e,o){return`rationale \u2192 borg_role-rationale ${JSON.stringify(e)} ${JSON.stringify(o)}`}function X(e){const o=e.match(/borg_role-rationale\s+("(?:(?:\\.)|[^"\\])*")\s+("(?:(?:\\.)|[^"\\])*")/);if(!o)return null;try{return{role:JSON.parse(o[1]),section:JSON.parse(o[2])}}catch{return null}}const F=[...x,..._];function U(e,o){return C(o??"").map(r=>{if(r.kind!=="label"||r.heading==null||!r.heading.trim().toLowerCase().endsWith("rationale")||F.some(l=>r.body.includes(l)))return r.body;const a=r.body.indexOf(`
5
+ `);return(a===-1?r.body+`
6
+ `:r.body.slice(0,a+1))+P(e,r.heading)+`
7
+ `}).join("")}function Z(e,o={}){const t=o.mode??"full",n=e.roles.map(s=>`- **${s.name}**${s.is_default?" _(default)_":""} \u2014 ${s.short_description||"_(no short description)_"}`).join(`
8
+ `),r=e.drones.map(s=>{const h=e.roles.find(p=>p.id===s.role_id),g=N(h?.name??"?",s.agent_kind);return`- **${s.label}** (${g}) \u2014 last seen ${I(new Date(s.last_seen))}`}).join(`
9
+ `)||"_(no drones connected)_",i=typeof e.behind_by=="number"?e.behind_by:null,a=i===null?"Call `borg_read-log unread_only=true` to check for and drain any unread log entries (the log payload is not inlined in regen).":i>0?`You have **${i}** unread log ${i===1?"entry":"entries"}. Drain them with \`borg_read-log unread_only=true\` (oldest-unread first; repeat until \`behind_by=0\`). The log payload is not inlined here \u2014 fetch on demand.`:"You're caught up \u2014 **0** unread log entries. No need to read the log right now.",l=(i??0)===0&&e.drones.length<=1?["## Getting started","","Welcome to your first cube. Here's how to get going:","",'1. Post your first activity: `borg_log message="Starting work on <your task>"`',"2. Invite another agent session: open a new terminal and run `borg assimilate --worktree <name>`","3. Check who's here: `borg_roster`","","---",""].join(`
10
+ `):"",w=L(e.cube.message_taxonomy),R=12,m=Array.isArray(e.decisions)?e.decisions:[],v=(()=>{if(m.length===0)return"";const s=m.slice(0,R),h=s.map(p=>`- **${p.topic}:** ${p.decision}`),g=m.length-s.length;return g>0&&h.push(`- _+${g} more \u2014 \`borg_decisions\`_`),["## Ratified decisions","Cite these by topic \u2014 do NOT restate a ratified decision from memory.",...h].join(`
11
+ `)})(),d=e.role.detailed_description_hash??null,S=e.role.detailed_description?U(e.role.name,e.role.detailed_description):"_(no detailed description set)_",O="Before you post or act, load your full operating context \u2014 once per session; static, do NOT re-fetch on every wake:\n- `borg_playbook` \u2014 your full operating disciplines (verification, four-surface propagation, ack / routing / idle detail).\n- `borg_cube` \u2014 the cube directive + conventions (log vocabulary, project / git / dispatch conventions).",k=t==="full"||d==null||d!==b,A=t==="full"||!y,u=[l+`# Cube: ${e.cube.name} \u2014 ${e.drone.label}`,"",`**Your role:** ${e.role.name}`,""];return t==="lite"&&u.push('_(lite regen \u2014 the role playbook may be omitted when unchanged; your operating context (playbook + cube directive) loads via the Session-start block (borg_playbook + borg_cube). If the playbook is NOT in your current context (e.g. after a context-compaction), call `borg_regen mode="full"` to re-orient.)_',""),u.push(t==="full"?"## Session start \u2014 required before acting":"## Session start",t==="full"?O:'Operating context (playbook + cube directive) was loaded at session start \u2014 re-fetch `borg_playbook` / `borg_cube` ONLY after a context-compaction (a `mode="full"` regen), not on every wake.',"",...w?[w,""]:[],`## Your role: ${e.role.name}`,k?S:["_(role playbook unchanged since your last full/lite regen; omitted in lite mode)_","",...$(e.role.detailed_description)].join(`
12
+ `),"","## Roles in this cube",n,"","## Connected drones",r,"","## Cube log",a,...v?["",v]:[]),A&&(u.push("",E()),y=!0),k&&d!=null&&(b=d),u.join(`
13
+ `)}function z(e,o,t){const n=o.get(e.drone_id),r=n?t.get(n.role_id):null,i=new Date(e.created_at).toISOString(),a=typeof e.id=="string"&&e.id.length>0?` [entry_id: ${e.id}]`:"",c=typeof e.drone_id=="string"&&e.drone_id.length>0?` ${T(e.drone_id)}`:"";return`**[${i}]**${a}${c} ${n?.label??"?"} (${r?.name??"?"}): ${e.message}`}export{G as DRONE_PLAYBOOK,K as __resetRegenSessionState,U as compressRoleText,V as formatLeanOrientation,z as formatLogEntryMarkdown,P as formatRationalePointer,Z as formatRegenMarkdown,E as getDronePlaybook,J as getDronePlaybookChapter,I as humanAgo,L as nullTaxonomyTip,q as parseHookSource,X as parseRationalePointer,Q as regenWakePathDroneLabel,H as resolveLeanIdentity,D as wakePathArming};
@@ -208,6 +208,14 @@ export declare function recordDecision(sessionToken: string, apiUrl: string, inp
208
208
  export declare function listDecisions(sessionToken: string, apiUrl: string, topic?: string): Promise<{
209
209
  decisions: any[];
210
210
  }>;
211
+ /** Remove one active ratified decision. The worker enforces the seat gate. */
212
+ export declare function removeDecision(sessionToken: string, apiUrl: string, selector: {
213
+ topic: string;
214
+ } | {
215
+ decision_id: string;
216
+ }): Promise<{
217
+ decision: any;
218
+ }>;
211
219
  /**
212
220
  * Regen: one-shot composite of everything a drone needs to be oriented.
213
221
  *
@@ -1 +1 @@
1
- import{getIdToken as f,getRefreshToken as m,clearTokens as h}from"./config.js";import{refreshIdToken as S,RefreshTokenInvalidError as g,RefreshTransientError as T}from"./auth.js";import{consolePrefix as j}from"./console-prefix.js";import{debugLog as _}from"./debug.js";import{assertUuidShape as E}from"./evict-drone.js";import{DroneEvictedError as k,DroneFrozenError as C,DRONE_EVICTED_CODE as P,DRONE_FROZEN_CODE as O,errorCodeFromBody as I}from"./drone-lifecycle.js";import{MODEL_DESCRIPTOR_REGEX as D}from"./model-presets.js";import{canonicalizeWorkingRepoIdentity as L}from"./working-repo.js";const A=process.env.BORG_API_URL||"https://api.borgmcp.ai",G=3,M=6e4;let l=null;function b(e){return l||(l=S(e).finally(()=>{l=null}),l)}function J(e){if(e==null)return null;const t=e.trim();return/^\d+$/.test(t)?parseInt(t,10)*1e3:null}function v(e,t,n=M,o=()=>Math.random()*500){const r=e??1e3*(t+1);return Math.min(r,n)+o()}function U(e){const t=(n,o)=>`${n}${/[.:!?]$/.test(n)?"":":"} ${o}`;try{const n=JSON.parse(e);if(typeof n?.error=="string")return typeof n.details=="string"?t(n.error,n.details):n.error;if(n?.error&&typeof n.error=="object"){const o=n.error.message,r=n.error.details??n.details;if(typeof o=="string"&&typeof r=="string")return t(o,r);if(typeof o=="string")return o}if(typeof n?.message=="string"&&typeof n?.details=="string")return t(n.message,n.details);if(typeof n?.message=="string")return n.message}catch{}return e}async function q(e,t,n){const o=n.maxRetries??G;let r=e,i=0;for(;r.status===429&&i<o;){const p=v(J(r.headers.get("Retry-After")),i,n.capMs,n.jitter);n.log?.(`rate limited (429); retrying in ${Math.round(p)}ms (attempt ${i+1}/${o})`),await n.sleep(p),i++,r=await t()}return r}function B(e){return new Promise(t=>setTimeout(t,e))}async function F(){let e=await f();if(!e){const t=await m(),n=t!=null;if(t)try{await b(t),e=await f()}catch(o){if(o instanceof g)await h();else throw o instanceof T?o:new T(`Token refresh failed unexpectedly (your saved login was NOT cleared \u2014 retry; if it persists, restart the borg session): ${o?.message??"unknown"}`)}if(!e)throw new Error(n?"Authentication expired \u2014 your saved login has expired. Run: borg setup":"Authentication required \u2014 you are not signed in. Run: borg setup")}return e}async function H(){const e=await m();if(!e)return null;try{return await b(e),await f()}catch(t){if(t instanceof g&&await h(),t instanceof T)throw t;return null}}async function K(){if(await f())return"valid";const t=await m();if(!t)return"dead";try{return await b(t),await f()?"valid":"transient"}catch(n){return n instanceof g?(await h(),"dead"):"transient"}}async function s(e,t={}){let n=await F();const{droneSession:o,apiUrl:r,headers:i,...p}=t,$=r??A,y=(p.method??"GET").toUpperCase(),w=async c=>{const d={Authorization:`Bearer ${c}`,...i};o&&(d["X-Drone-Session"]=o),_(`\u2192 ${y} ${e}`);const u=await fetch(`${$}${e}`,{...p,headers:d});return _(`\u2190 ${u.status} ${y} ${e}`),u};let a=await w(n);if(a.status===401){const c=await H();c&&(n=c,a=await w(n))}if(a.status===401)throw new Error("Authentication required. Run: borg setup");if(a.status===429&&(a=await q(a,()=>w(n),{sleep:B,log:c=>console.error(`${j()}${c}`)})),!a.ok){const c=await a.text();_(`\u2717 ${a.status} ${y} ${e}: ${c}`);const d=U(c),u=I(c);if(a.status===410&&u===P)throw new k(d);if(a.status===423&&u===O)throw new C(d);if(a.status===429){const x=a.headers.get("Retry-After"),R=x?` (retry after ${x}s)`:"";throw new Error(`HTTP 429: rate limited${R}: ${d}`)}throw new Error(`HTTP ${a.status}: ${d}`)}return a}async function ee(e,t,n,o){const r={hostname:n??null};if((o==="claude"||o==="codex"||o==="opencode")&&(r.agent_kind=o),typeof e=="string")r.cube_name=e;else if(e.cube_id&&(r.cube_id=e.cube_id),e.cube_name&&(r.cube_name=e.cube_name),e.role_id&&(r.role_id=e.role_id),e.role_name&&(r.role_name=e.role_name),e.prior_drone_id&&(r.prior_drone_id=e.prior_drone_id),e.model!=null){if(!D.test(e.model))throw new Error(`Invalid model descriptor: "${e.model}" (expected "<claude|ollama>:<model>").`);r.model=e.model}return await(await s("/api/assimilate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r),apiUrl:t})).json()}async function ne(e,t){return await(await s("/api/drone/cube",{method:"GET",droneSession:e,apiUrl:t})).json()}async function te(e,t){return await(await s("/api/drone/role",{method:"GET",droneSession:e,apiUrl:t})).json()}async function oe(e,t,n){const o=new URLSearchParams({role:n});return await(await s(`/api/drone/role?${o.toString()}`,{method:"GET",droneSession:e,apiUrl:t})).json()}async function re(e,t){return await(await s("/api/drone/whoami",{method:"GET",droneSession:e,apiUrl:t})).json()}async function se(e,t,n){const o=n?`?since=${encodeURIComponent(n)}`:"";return await(await s(`/api/drone/roster${o}`,{method:"GET",droneSession:e,apiUrl:t})).json()}async function ie(e,t,n={}){const o=new URLSearchParams;n.since&&o.set("since",n.since),n.limit!==void 0&&o.set("limit",String(n.limit)),n.unreadOnly&&o.set("unread_only","true");const r=o.toString();return await(await s(`/api/drone/log${r?`?${r}`:""}`,{method:"GET",droneSession:e,apiUrl:t})).json()}async function ae(e,t,n,o="ack"){await s(`/api/drone/log/${n}/ack`,{method:"POST",body:JSON.stringify({kind:o}),droneSession:e,apiUrl:t})}async function ce(e,t,n){return await(await s("/api/drone/decide",{method:"POST",body:JSON.stringify(n),droneSession:e,apiUrl:t})).json()}async function pe(e,t,n){const o=n?`?topic=${encodeURIComponent(n)}`:"";return await(await s(`/api/drone/decisions${o}`,{method:"GET",droneSession:e,apiUrl:t})).json()}async function de(e,t,n={}){const o=new URLSearchParams;if(n.since&&o.set("since",n.since),n.reportedModel&&o.set("reported_model",n.reportedModel),n.workingRepo){o.set("working_repo_reported","1");const p=n.workingRepo.origin?L(n.workingRepo.origin):null;p&&o.set("working_repo_origin",p)}const r=o.toString();return await(await s(`/api/drone/regen${r?`?${r}`:""}`,{method:"GET",droneSession:e,apiUrl:t})).json()}async function ue(e,t,n,o){const r=new URLSearchParams({role:n,section:o});return await(await s(`/api/drone/role-rationale?${r.toString()}`,{method:"GET",droneSession:e,apiUrl:t})).json()}async function fe(e,t,n,o={}){const r={message:n,...o.visibility?{visibility:o.visibility}:{},...o.recipientDroneIds?{recipientDroneIds:o.recipientDroneIds}:{},...o.class?{class:o.class}:{},...o.to?{to:o.to}:{}};return await(await s("/api/drone/log",{method:"POST",headers:{"Content-Type":"application/json"},droneSession:e,apiUrl:t,body:JSON.stringify(r)})).json()}async function le(e,t,n){const o={kind:n.kind??"friction",message:n.message,...n.metadata?{metadata:n.metadata}:{}};return await(await s("/api/drone/report",{method:"POST",headers:{"Content-Type":"application/json"},droneSession:e,apiUrl:t,body:JSON.stringify(o)})).json()}async function ye(){const e=await s("/api/reports",{method:"GET"});if(e.status===403)return{forbidden:!0};if(!e.ok)throw new Error(`Failed to fetch reports: ${e.status}`);return{forbidden:!1,reports:(await e.json()).reports}}async function we(){return await(await s("/api/cubes",{method:"GET"})).json()}async function me(){return await(await s("/api/templates",{method:"GET"})).json()}async function he(e,t,n){const o={cube_directive:t};e&&(o.name=e),n?.template&&(o.template=n.template),n&&Object.prototype.hasOwnProperty.call(n,"message_taxonomy")&&(o.message_taxonomy=n.message_taxonomy??null);const i=await(await s("/api/cubes",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)})).json();return i.cube?{...i.cube,roles:i.roles??[],drones:i.drones??[]}:i}async function ge(e,t){return await(await s(`/api/cubes/${e}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).json()}async function Te(e,t){return await(await s(`/api/cubes/${e}/taxonomy-patch`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).json()}async function _e(e){await s(`/api/cubes/${e}`,{method:"DELETE"})}async function be(e,t){return await(await s(`/api/cubes/${e}/roles`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).json()}async function xe(e,t){return await(await s(`/api/roles/${e}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).json()}async function Ee(e,t){return await(await s(`/api/roles/${e}/section-patch`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).json()}async function $e(e){await s(`/api/roles/${e}`,{method:"DELETE"})}async function Re(e,t){return E(e,"drone_id"),await(await s(`/api/drones/${e}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({role_id:t})})).json()}async function Se(e){E(e,"drone_id"),await s(`/api/drones/${e}`,{method:"DELETE"})}async function je(e){const n=await(await s(`/api/cubes/${e}`,{method:"GET"})).json();return n.cube?{...n.cube,roles:n.roles??[],drones:n.drones??[]}:n}async function ke(e,t){return await(await s(`/api/cubes/${e}/apply-template`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({template_name:t})})).json()}async function Ce(){return await(await s("/api/subscription/status",{method:"GET"})).json()}async function Pe(e,t="software-dev",n=!1,o){return await(await s(`/api/cubes/${e}/sync-roles`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({template_name:t,apply:n,...o?{decisions:o}:{}})})).json()}async function Oe(){const t=await(await s("/api/subscribe",{method:"POST",headers:{"Content-Type":"application/json"}})).json();if(!t.checkout_url)throw new Error("No checkout URL in response");return t.checkout_url}async function Ie(){const t=await(await s("/api/subscription/portal",{method:"POST",headers:{"Content-Type":"application/json"}})).json();if(!t.portal_url)throw new Error(t.message||"No portal URL in response");return t.portal_url}export{A as API_URL,ae as ackLogEntry,fe as appendLog,ke as applyTemplate,ee as assimilate,Ce as checkSubscriptionStatus,Ie as createBillingPortalSession,he as createCube,be as createRole,Oe as createSubscription,_e as deleteCube,$e as deleteRole,Se as evictDrone,U as extractHttpErrorMessage,ye as fetchReports,je as getCube,ne as getCubeInfo,te as getRoleInfo,oe as getRoleInfoByName,se as getRoster,F as getValidToken,we as listCubes,pe as listDecisions,me as listTemplates,J as parseRetryAfterMs,Ee as patchRoleSection,Te as patchTaxonomyClass,K as probeSession,v as rateLimitWaitMs,ie as readLog,Re as reassignDrone,ce as recordDecision,de as regen,q as retryOn429,ue as roleRationale,le as submitReport,Pe as syncRoles,ge as updateCube,xe as updateRole,re as whoami};
1
+ import{getIdToken as f,getRefreshToken as m,clearTokens as h}from"./config.js";import{refreshIdToken as S,RefreshTokenInvalidError as g,RefreshTransientError as T}from"./auth.js";import{consolePrefix as j}from"./console-prefix.js";import{debugLog as E}from"./debug.js";import{assertUuidShape as x}from"./evict-drone.js";import{DroneEvictedError as k,DroneFrozenError as C,DRONE_EVICTED_CODE as P,DRONE_FROZEN_CODE as O,errorCodeFromBody as I}from"./drone-lifecycle.js";import{canonicalizeWorkingRepoIdentity as D}from"./working-repo.js";const L=/^(claude|ollama):[A-Za-z0-9._:\/-]+$/,A=process.env.BORG_API_URL||"https://api.borgmcp.ai",G=3,J=6e4;let l=null;function _(e){return l||(l=S(e).finally(()=>{l=null}),l)}function M(e){if(e==null)return null;const t=e.trim();return/^\d+$/.test(t)?parseInt(t,10)*1e3:null}function v(e,t,n=J,o=()=>Math.random()*500){const r=e??1e3*(t+1);return Math.min(r,n)+o()}function U(e){const t=(n,o)=>`${n}${/[.:!?]$/.test(n)?"":":"} ${o}`;try{const n=JSON.parse(e);if(typeof n?.error=="string")return typeof n.details=="string"?t(n.error,n.details):n.error;if(n?.error&&typeof n.error=="object"){const o=n.error.message,r=n.error.details??n.details;if(typeof o=="string"&&typeof r=="string")return t(o,r);if(typeof o=="string")return o}if(typeof n?.message=="string"&&typeof n?.details=="string")return t(n.message,n.details);if(typeof n?.message=="string")return n.message}catch{}return e}async function N(e,t,n){const o=n.maxRetries??G;let r=e,i=0;for(;r.status===429&&i<o;){const p=v(M(r.headers.get("Retry-After")),i,n.capMs,n.jitter);n.log?.(`rate limited (429); retrying in ${Math.round(p)}ms (attempt ${i+1}/${o})`),await n.sleep(p),i++,r=await t()}return r}function q(e){return new Promise(t=>setTimeout(t,e))}async function B(){let e=await f();if(!e){const t=await m(),n=t!=null;if(t)try{await _(t),e=await f()}catch(o){if(o instanceof g)await h();else throw o instanceof T?o:new T(`Token refresh failed unexpectedly (your saved login was NOT cleared \u2014 retry; if it persists, restart the borg session): ${o?.message??"unknown"}`)}if(!e)throw new Error(n?"Authentication expired \u2014 your saved login has expired. Run: borg setup":"Authentication required \u2014 you are not signed in. Run: borg setup")}return e}async function F(){const e=await m();if(!e)return null;try{return await _(e),await f()}catch(t){if(t instanceof g&&await h(),t instanceof T)throw t;return null}}async function Q(){if(await f())return"valid";const t=await m();if(!t)return"dead";try{return await _(t),await f()?"valid":"transient"}catch(n){return n instanceof g?(await h(),"dead"):"transient"}}async function s(e,t={}){let n=await B();const{droneSession:o,apiUrl:r,headers:i,...p}=t,$=r??A,y=(p.method??"GET").toUpperCase(),w=async c=>{const d={Authorization:`Bearer ${c}`,...i};o&&(d["X-Drone-Session"]=o),E(`\u2192 ${y} ${e}`);const u=await fetch(`${$}${e}`,{...p,headers:d});return E(`\u2190 ${u.status} ${y} ${e}`),u};let a=await w(n);if(a.status===401){const c=await F();c&&(n=c,a=await w(n))}if(a.status===401)throw new Error("Authentication required. Run: borg setup");if(a.status===429&&(a=await N(a,()=>w(n),{sleep:q,log:c=>console.error(`${j()}${c}`)})),!a.ok){const c=await a.text();E(`\u2717 ${a.status} ${y} ${e}: ${c}`);const d=U(c),u=I(c);if(a.status===410&&u===P)throw new k(d);if(a.status===423&&u===O)throw new C(d);if(a.status===429){const b=a.headers.get("Retry-After"),R=b?` (retry after ${b}s)`:"";throw new Error(`HTTP 429: rate limited${R}: ${d}`)}throw new Error(`HTTP ${a.status}: ${d}`)}return a}async function K(e,t,n,o){const r={hostname:n??null};if((o==="claude"||o==="codex"||o==="opencode")&&(r.agent_kind=o),typeof e=="string")r.cube_name=e;else if(e.cube_id&&(r.cube_id=e.cube_id),e.cube_name&&(r.cube_name=e.cube_name),e.role_id&&(r.role_id=e.role_id),e.role_name&&(r.role_name=e.role_name),e.prior_drone_id&&(r.prior_drone_id=e.prior_drone_id),e.model!=null){if(!L.test(e.model))throw new Error(`Invalid model descriptor: "${e.model}" (expected "<claude|ollama>:<model>").`);r.model=e.model}return await(await s("/api/assimilate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r),apiUrl:t})).json()}async function ee(e,t){return await(await s("/api/drone/cube",{method:"GET",droneSession:e,apiUrl:t})).json()}async function ne(e,t){return await(await s("/api/drone/role",{method:"GET",droneSession:e,apiUrl:t})).json()}async function te(e,t,n){const o=new URLSearchParams({role:n});return await(await s(`/api/drone/role?${o.toString()}`,{method:"GET",droneSession:e,apiUrl:t})).json()}async function oe(e,t){return await(await s("/api/drone/whoami",{method:"GET",droneSession:e,apiUrl:t})).json()}async function re(e,t,n){const o=n?`?since=${encodeURIComponent(n)}`:"";return await(await s(`/api/drone/roster${o}`,{method:"GET",droneSession:e,apiUrl:t})).json()}async function se(e,t,n={}){const o=new URLSearchParams;n.since&&o.set("since",n.since),n.limit!==void 0&&o.set("limit",String(n.limit)),n.unreadOnly&&o.set("unread_only","true");const r=o.toString();return await(await s(`/api/drone/log${r?`?${r}`:""}`,{method:"GET",droneSession:e,apiUrl:t})).json()}async function ie(e,t,n,o="ack"){await s(`/api/drone/log/${n}/ack`,{method:"POST",body:JSON.stringify({kind:o}),droneSession:e,apiUrl:t})}async function ae(e,t,n){return await(await s("/api/drone/decide",{method:"POST",body:JSON.stringify(n),droneSession:e,apiUrl:t})).json()}async function ce(e,t,n){const o=n?`?topic=${encodeURIComponent(n)}`:"";return await(await s(`/api/drone/decisions${o}`,{method:"GET",droneSession:e,apiUrl:t})).json()}async function pe(e,t,n){return await(await s("/api/drone/decisions",{method:"DELETE",body:JSON.stringify(n),droneSession:e,apiUrl:t})).json()}async function de(e,t,n={}){const o=new URLSearchParams;if(n.since&&o.set("since",n.since),n.reportedModel&&o.set("reported_model",n.reportedModel),n.workingRepo){o.set("working_repo_reported","1");const p=n.workingRepo.origin?D(n.workingRepo.origin):null;p&&o.set("working_repo_origin",p)}const r=o.toString();return await(await s(`/api/drone/regen${r?`?${r}`:""}`,{method:"GET",droneSession:e,apiUrl:t})).json()}async function ue(e,t,n,o){const r=new URLSearchParams({role:n,section:o});return await(await s(`/api/drone/role-rationale?${r.toString()}`,{method:"GET",droneSession:e,apiUrl:t})).json()}async function fe(e,t,n,o={}){const r={message:n,...o.visibility?{visibility:o.visibility}:{},...o.recipientDroneIds?{recipientDroneIds:o.recipientDroneIds}:{},...o.class?{class:o.class}:{},...o.to?{to:o.to}:{}};return await(await s("/api/drone/log",{method:"POST",headers:{"Content-Type":"application/json"},droneSession:e,apiUrl:t,body:JSON.stringify(r)})).json()}async function le(e,t,n){const o={kind:n.kind??"friction",message:n.message,...n.metadata?{metadata:n.metadata}:{}};return await(await s("/api/drone/report",{method:"POST",headers:{"Content-Type":"application/json"},droneSession:e,apiUrl:t,body:JSON.stringify(o)})).json()}async function ye(){const e=await s("/api/reports",{method:"GET"});if(e.status===403)return{forbidden:!0};if(!e.ok)throw new Error(`Failed to fetch reports: ${e.status}`);return{forbidden:!1,reports:(await e.json()).reports}}async function we(){return await(await s("/api/cubes",{method:"GET"})).json()}async function me(){return await(await s("/api/templates",{method:"GET"})).json()}async function he(e,t,n){const o={cube_directive:t};e&&(o.name=e),n?.template&&(o.template=n.template),n&&Object.prototype.hasOwnProperty.call(n,"message_taxonomy")&&(o.message_taxonomy=n.message_taxonomy??null);const i=await(await s("/api/cubes",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)})).json();return i.cube?{...i.cube,roles:i.roles??[],drones:i.drones??[]}:i}async function ge(e,t){return await(await s(`/api/cubes/${e}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).json()}async function Te(e,t){return await(await s(`/api/cubes/${e}/taxonomy-patch`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).json()}async function Ee(e){await s(`/api/cubes/${e}`,{method:"DELETE"})}async function _e(e,t){return await(await s(`/api/cubes/${e}/roles`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).json()}async function be(e,t){return await(await s(`/api/roles/${e}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).json()}async function xe(e,t){return await(await s(`/api/roles/${e}/section-patch`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).json()}async function $e(e){await s(`/api/roles/${e}`,{method:"DELETE"})}async function Re(e,t){return x(e,"drone_id"),await(await s(`/api/drones/${e}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({role_id:t})})).json()}async function Se(e){x(e,"drone_id"),await s(`/api/drones/${e}`,{method:"DELETE"})}async function je(e){const n=await(await s(`/api/cubes/${e}`,{method:"GET"})).json();return n.cube?{...n.cube,roles:n.roles??[],drones:n.drones??[]}:n}async function ke(e,t){return await(await s(`/api/cubes/${e}/apply-template`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({template_name:t})})).json()}async function Ce(){return await(await s("/api/subscription/status",{method:"GET"})).json()}async function Pe(e,t="software-dev",n=!1,o){return await(await s(`/api/cubes/${e}/sync-roles`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({template_name:t,apply:n,...o?{decisions:o}:{}})})).json()}async function Oe(){const t=await(await s("/api/subscribe",{method:"POST",headers:{"Content-Type":"application/json"}})).json();if(!t.checkout_url)throw new Error("No checkout URL in response");return t.checkout_url}async function Ie(){const t=await(await s("/api/subscription/portal",{method:"POST",headers:{"Content-Type":"application/json"}})).json();if(!t.portal_url)throw new Error(t.message||"No portal URL in response");return t.portal_url}export{A as API_URL,ie as ackLogEntry,fe as appendLog,ke as applyTemplate,K as assimilate,Ce as checkSubscriptionStatus,Ie as createBillingPortalSession,he as createCube,_e as createRole,Oe as createSubscription,Ee as deleteCube,$e as deleteRole,Se as evictDrone,U as extractHttpErrorMessage,ye as fetchReports,je as getCube,ee as getCubeInfo,ne as getRoleInfo,te as getRoleInfoByName,re as getRoster,B as getValidToken,we as listCubes,ce as listDecisions,me as listTemplates,M as parseRetryAfterMs,xe as patchRoleSection,Te as patchTaxonomyClass,Q as probeSession,v as rateLimitWaitMs,se as readLog,Re as reassignDrone,ae as recordDecision,de as regen,pe as removeDecision,N as retryOn429,ue as roleRationale,le as submitReport,Pe as syncRoles,ge as updateCube,be as updateRole,oe as whoami};
@@ -7,6 +7,16 @@ export interface Role {
7
7
  role_class?: 'queen' | 'worker';
8
8
  default_model?: string | null;
9
9
  }
10
+ export interface RoleOccupant {
11
+ role_id: string;
12
+ /** Server-derived advisory; absent remains occupied for rollout safety. */
13
+ presumed_abandoned?: boolean;
14
+ }
15
+ /**
16
+ * Build the advisory occupancy set used only by role-less assimilation.
17
+ * Explicit role selection and server authorization do not consult it.
18
+ */
19
+ export declare function occupiedRoleIdsForAutoRole(drones: readonly RoleOccupant[]): Set<string>;
10
20
  /**
11
21
  * Normalize a role-name argument or stored name into a slug used for
12
22
  * both worktree-path construction and role lookup. Single source of
@@ -35,10 +45,9 @@ export declare function matchRoleByName(roles: Role[], query: string): Role | un
35
45
  * - If every eligible worker role is occupied → the is_default role.
36
46
  * - Neither available → undefined (caller errors out).
37
47
  *
38
- * `occupiedRoleIds` is the set of role_ids that already hold an ACTIVE seat
39
- * (the caller derives it from the cube's active drones). Omitted → treated as
40
- * empty (all roles unoccupied), which degrades to "first eligible worker role,
41
- * else default".
48
+ * `occupiedRoleIds` is the set of role_ids that still hold a live-enough active
49
+ * seat for automatic selection. Omitted → treated as empty (all roles
50
+ * unoccupied), which degrades to "first eligible worker role, else default".
42
51
  */
43
52
  export declare function pickDefaultRole(roles: Role[], opts: {
44
53
  isFirstDrone: boolean;
@@ -1 +1 @@
1
- function o(n){return n.toLowerCase().replace(/[_\s]+/g,"-").replace(/[^a-z0-9-]/g,"")}function u(n,t){const i=o(t);return n.find(a=>o(a.name)===i)}function c(n,t){if(t.isFirstDrone){const e=n.find(s=>s.is_human_seat);if(e)return e}const i=t.occupiedRoleIds??new Set,a=n.filter(e=>e.is_mandatory===!0&&e.role_class!=="queen"),r=n.filter(e=>e.role_class!=="queen"&&!e.is_human_seat);return a.find(e=>!i.has(e.id))??r.find(e=>!i.has(e.id))??n.find(e=>e.is_default)}export{u as matchRoleByName,c as pickDefaultRole,o as roleSlug};
1
+ function c(n){return new Set(n.filter(t=>t.presumed_abandoned!==!0).map(t=>t.role_id))}function r(n){return n.toLowerCase().replace(/[_\s]+/g,"-").replace(/[^a-z0-9-]/g,"")}function s(n,t){const i=r(t);return n.find(o=>r(o.name)===i)}function d(n,t){if(t.isFirstDrone){const e=n.find(u=>u.is_human_seat);if(e)return e}const i=t.occupiedRoleIds??new Set,o=n.filter(e=>e.is_mandatory===!0&&e.role_class!=="queen"),a=n.filter(e=>e.role_class!=="queen"&&!e.is_human_seat);return o.find(e=>!i.has(e.id))??a.find(e=>!i.has(e.id))??n.find(e=>e.is_default)}export{s as matchRoleByName,c as occupiedRoleIdsForAutoRole,d as pickDefaultRole,r as roleSlug};