@x-otto/orchestrator 0.0.1-alpha.3 → 0.0.1-alpha.5

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,8 +1,8 @@
1
- import{checkDelegationDepth as e}from"@x-otto/orchestration-contracts";import{AgentError as t,createLogger as n}from"@x-otto/shared";import{z as r}from"zod";var i=class extends Error{constructor(e,t){super(e),this.reason=t,this.name=`TaskRejectedError`}};const a=new Set([`completed`,`failed`,`cancelled`]);var o=class{tasks=new Map;nextId=1;maxStoredTasks;constructor(e=1e3){this.maxStoredTasks=Math.max(1,e)}generateId(){return`task_${Date.now()}_${this.nextId++}`}async create(e){this.evictForCapacity();let t={id:this.generateId(),status:`pending`,sessionPolicy:e.sessionMode??`ephemeral`,attempts:0,maxAttempts:3,input:e,createdAt:Date.now()};return this.tasks.set(t.id,t),t}evictForCapacity(){let e=this.tasks.size-this.maxStoredTasks+1;if(e<=0)return;let t=[...this.tasks.values()].filter(e=>a.has(e.status)).sort((e,t)=>(e.completedAt??e.createdAt)-(t.completedAt??t.createdAt));for(let n of t){if(e<=0)break;this.tasks.delete(n.id),e--}e>0&&console.warn(`[TaskStore] capacity ${this.maxStoredTasks} exceeded by ${e} in-flight task(s); no terminal tasks left to evict — keeping in-flight tasks (will exceed cap).`)}async get(e){return this.tasks.get(e)}async list(e){let t=[...this.tasks.values()];return e?.status&&(t=t.filter(t=>t.status===e.status)),t}async update(e,t){let n=this.tasks.get(e);if(!n)throw Error(`Task not found: ${e}`);Object.assign(n,t)}async delete(e){return this.tasks.delete(e)}};const s={enabled:!0,maxAttempts:3,backoffMs:1e3,backoffMultiplier:2};var c=class e{config;constructor(e){this.config={...s,...e}}shouldRetry(e){return this.config.enabled&&e<this.config.maxAttempts}getBackoff(e){return this.config.backoffMs*this.config.backoffMultiplier**(e-1)}static fromWorkflowOptions(t){let n=t?.retry;return new e({enabled:n?.enabled??!0,maxAttempts:n?.maxAttempts??3})}};const l={enabled:!0,failureThreshold:5,resetTimeoutMs:6e4};var u=class{enabled;failureThreshold;resetTimeoutMs;consecutiveFailures=0;openedAt=null;constructor(e){let t={...l,...e};this.enabled=t.enabled,this.failureThreshold=t.failureThreshold,this.resetTimeoutMs=t.resetTimeoutMs}isOpen(){return!this.enabled||this.openedAt===null?!1:Date.now()-this.openedAt>=this.resetTimeoutMs?(this.openedAt=null,this.consecutiveFailures=0,!1):!0}success(){this.consecutiveFailures=0,this.openedAt=null}failure(){this.consecutiveFailures++,this.consecutiveFailures>=this.failureThreshold&&(this.openedAt=Date.now())}reset(){this.consecutiveFailures=0,this.openedAt=null}},d=class e{breakers=new Map;constructor(e){this.factory=e}forTarget(e){let t=this.breakers.get(e);return t||(t=this.factory(),this.breakers.set(e,t)),t}resetAll(){for(let e of this.breakers.values())e.reset()}static perTarget(t){return new e(()=>new u(t))}static single(t){return new e(()=>t)}static fromWorkflowOptions(t){let n=t?.circuitBreaker;return e.perTarget({enabled:n?.enabled??!0,failureThreshold:n?.failureThreshold??5,resetTimeoutMs:n?.timeoutMs??6e4})}};function f(){let e=Error(`Semaphore acquire aborted`);return e.name=`AbortError`,e}var p=class{active=0;waiters=[];constructor(e){this.max=e}async acquire(e){if(e?.aborted)throw f();if(this.active<this.max){this.active+=1;return}await new Promise((t,n)=>{let r=()=>{a(),t()},i=()=>{let e=this.waiters.indexOf(r);e>=0&&this.waiters.splice(e,1),a(),n(f())},a=()=>e?.removeEventListener(`abort`,i);this.waiters.push(r),e?.addEventListener(`abort`,i,{once:!0})})}release(){let e=this.waiters.shift();e?e():--this.active}async run(e,t){await this.acquire(t);try{return await e()}finally{this.release()}}get activeCount(){return this.active}get pendingCount(){return this.waiters.length}get capacity(){return this.max}},m=class{constructor(e,t,n,r,i,a,o){this.taskStore=e,this.hookRegistry=t,this.retryPolicy=n,this.circuitBreakers=r,this.runSession=i,this.semaphore=a,this.runAgentTurn=o}breakerFor(e){return this.circuitBreakers.forTarget(e??`default`)}async runTask(e,t){this.assertCircuit(e.input.subagent);let n=await this.register(e);return this.runScheduled(n.id,t)}assertCircuit(e){if(this.breakerFor(e).isOpen())throw new i(`Circuit breaker is open due to repeated failures. Try again later.`,`circuit_open`)}async runScheduled(e,t){return this.semaphore.run(()=>this.executeToCompletion(e,t),t)}async isCancelled(e){return(await this.taskStore.get(e))?.status===`cancelled`}async register(e){let t=await this.taskStore.create(e.input);return this.hookRegistry.emit(`task.created`,{task:t}),t}async startTask(e){this.assertCircuit(e.input.subagent);let t=await this.register(e);return this.runScheduled(t.id).catch(async e=>{await this.taskStore.update(t.id,{status:`failed`,error:{code:`EXECUTION_FAILED`,message:e instanceof Error?e.message:String(e),retriable:!1},completedAt:Date.now()}).catch(()=>{})}),{id:t.id}}async executeToCompletion(e,t){let n=await this.taskStore.get(e);if(!n)return{taskId:e,status:`failed`,error:{code:`TASK_NOT_FOUND`,message:`Task not found: ${e}`,retriable:!1}};let r=n.attempts+1;try{await this.taskStore.update(e,{status:`running`,attempts:r}),this.hookRegistry.emit(`task.started`,{task:{...n,status:`running`},agent:n.input.subagent??`default`});let i=n.input.kind===`swarm_member`,a;if(i&&this.runAgentTurn){let e=await this.runAgentTurn({agentId:n.input.subagent??`agent`,prompt:n.input.prompt,systemPrompt:n.input.systemPrompt,tools:n.input.tools,extraTools:n.input.extraTools,coreTools:n.input.coreTools,model:n.input.model,maxToolTurns:n.input.maxToolTurns,thinkingLevel:n.input.thinkingLevel,maxTokens:n.input.maxTokens,temperature:n.input.temperature,sessionId:n.input.sessionId,depth:n.input.depth,parentTaskId:n.input.parentTaskId,signal:t});a={text:e.text,sessionId:n.input.sessionId??``,tokenUsage:e.tokenUsage,durationMs:e.durationMs,messages:e.messages,handoff:e.handoff}}else a=await this.runSession({prompt:n.input.prompt,sessionId:n.input.sessionId,sessionMode:n.sessionPolicy,agentName:n.input.subagent,slot:n.input.slot,model:n.input.model,category:n.input.category,depth:n.input.depth,parentTaskId:n.input.parentTaskId,taskId:e,signal:t,readOnly:n.input.mode===`background`,systemPrompt:n.input.systemPrompt,tools:n.input.tools,extraTools:n.input.extraTools,thinkingLevel:n.input.thinkingLevel,maxTokens:n.input.maxTokens,temperature:n.input.temperature,maxToolTurns:n.input.maxToolTurns});if(await this.isCancelled(e))return{taskId:e,status:`cancelled`};let o={text:a.text,tokenUsage:a.tokenUsage,durationMs:a.durationMs,...a.messages?{messages:a.messages}:{},...a.handoff?{handoff:a.handoff}:{}};return await this.taskStore.update(e,{status:`completed`,output:o,sessionId:a.sessionId,completedAt:Date.now()}),this.breakerFor(n.input.subagent).success(),this.hookRegistry.emit(`task.completed`,{task:{...n,status:`completed`,output:o},result:o}),{taskId:e,status:`completed`,output:o}}catch(r){let i=r instanceof Error?r.message:String(r);if(await this.isCancelled(e))return{taskId:e,status:`cancelled`};if(this.retryPolicy.shouldRetry(n.attempts)&&!t?.aborted&&(await h(this.retryPolicy.getBackoff(n.attempts),t),!t?.aborted))return this.executeToCompletion(e,t);this.breakerFor(n.input.subagent).failure();let a={code:`EXECUTION_FAILED`,message:i,retriable:!1};return await this.taskStore.update(e,{status:`failed`,error:a,completedAt:Date.now()}),this.hookRegistry.emit(`task.failed`,{task:{...n,status:`failed`},error:a}),{taskId:e,status:`failed`,error:a}}}};function h(e,t){return t?.aborted?Promise.resolve():new Promise(n=>{let r,i=()=>{clearTimeout(r),n()};r=setTimeout(()=>{t?.removeEventListener(`abort`,i),n()},e),t?.addEventListener(`abort`,i,{once:!0})})}var g=class{constructor(e,t){this.taskStore=e,this.abortTask=t}async getOutput(e){let t=await this.taskStore.get(e);return t?t.status===`completed`?{ok:!0,value:{status:`completed`,output:t.output?.text}}:t.status===`failed`?{ok:!1,error:t.error?.message??`Task failed`}:{ok:!0,value:{status:t.status}}:{ok:!1,error:`Task not found id: ${e}`}}async cancel(e){let t=await this.taskStore.get(e);return t?t.status===`completed`||t.status===`failed`||t.status===`cancelled`?{ok:!1,error:`Task already in terminal state: ${t.status}, id: ${e}`}:(await this.taskStore.update(e,{status:`cancelled`,completedAt:Date.now()}),this.abortTask&&this.abortTask(e),{ok:!0,value:void 0}):{ok:!1,error:`Task not found id: ${e}`}}},_=class{taskStore;executor;backgroundManager;hookRegistry;circuitBreakers;delegationGate;runSession;maxDelegationDepth;fanOutCounts=new Map;maxFanOutPerParent;constructor(e){let{hookRegistry:t,runSession:n,runAgentTurn:r,abortTask:i,workflowConfig:a,maxActiveTasks:s=10,maxStoredTasks:l,maxDelegationDepth:u=1}=e;this.hookRegistry=t,this.runSession=n,this.maxDelegationDepth=u,this.maxFanOutPerParent=e.maxFanOutPerParent??Math.max(1,Math.floor(s/2)),this.taskStore=new o(l);let f=c.fromWorkflowOptions(a);this.circuitBreakers=d.fromWorkflowOptions(a),this.delegationGate=new p(s),this.executor=new m(this.taskStore,t,f,this.circuitBreakers,n,this.delegationGate,r),this.backgroundManager=new g(this.taskStore,i)}dispatchTask=async t=>{let{childDepth:n,exceeded:r}=e(t.depth,this.maxDelegationDepth);if(r)return y(`Delegation depth limit (${this.maxDelegationDepth}) reached — complete this subtask yourself instead of delegating further.`);if(t.parentTaskId!==void 0&&(this.fanOutCounts.get(t.parentTaskId)??0)>=this.maxFanOutPerParent)return y(`Fan-out limit (${this.maxFanOutPerParent}) reached for parent task "${t.parentTaskId}" — wait for in-flight subtasks to complete before delegating more.`);let i={input:{prompt:t.prompt,subagent:t.subagent,category:t.category,sessionId:t.sessionId,sessionMode:t.sessionMode??`ephemeral`,mode:t.mode,slot:t.slot,model:t.model,depth:n,...t.parentTaskId===void 0?{}:{parentTaskId:t.parentTaskId}}};if(t.mode===`background`)return b(async()=>{let{id:e}=await this.executor.startTask(i);return this.acquireFanOutSlot(t.parentTaskId),{id:e,status:`pending`,output:`Background task created: ${e}`}});this.acquireFanOutSlot(t.parentTaskId);try{let e=await this.executor.runTask(i,t.signal);return e.status===`completed`?v({id:e.taskId,status:e.status,output:e.output?.text}):y(e.error?.message??`Unknown error`)}catch(e){return y(e)}finally{this.releaseFanOutSlot(t.parentTaskId)}};callAgent=async(t,n,r)=>{let{childDepth:i,exceeded:a}=e(r?.depth,this.maxDelegationDepth);if(a)return y(`Delegation depth limit (${this.maxDelegationDepth}) reached — complete this subtask yourself instead of delegating further.`);if(r?.parentTaskId!==void 0&&(this.fanOutCounts.get(r.parentTaskId)??0)>=this.maxFanOutPerParent)return y(`Fan-out limit (${this.maxFanOutPerParent}) reached for parent task "${r.parentTaskId}" — wait for in-flight subtasks to complete before delegating more.`);let o=this.circuitBreakers.forTarget(t);return o.isOpen()?y(`Circuit breaker is open due to repeated failures. Try again later.`):(this.acquireFanOutSlot(r?.parentTaskId),this.delegationGate.run(async()=>{try{let e=await this.runSession({prompt:n,sessionMode:`ephemeral`,agentName:t,slot:r?.slot,model:r?.model,depth:i,parentTaskId:r?.parentTaskId,signal:r?.signal});return o.success(),v(e.text)}catch(e){return o.failure(),y(e)}},r?.signal).finally(()=>{this.releaseFanOutSlot(r?.parentTaskId)}))};async runAgentTurn(e){let t={input:{kind:`swarm_member`,prompt:e.prompt,subagent:e.agentId,systemPrompt:e.systemPrompt,tools:e.tools,extraTools:e.extraTools,coreTools:e.coreTools,model:e.model,maxToolTurns:e.maxToolTurns,thinkingLevel:e.thinkingLevel,maxTokens:e.maxTokens,temperature:e.temperature,sessionMode:`ephemeral`,sessionId:e.sessionId,depth:e.depth}},n=await this.executor.runTask(t,e.signal);if(n.status===`completed`)return{agentId:e.agentId,text:n.output?.text??``,messages:n.output?.messages,handoff:n.output?.handoff,tokenUsage:n.output?.tokenUsage,durationMs:n.output?.durationMs??0};throw Error(n.error?.message??`Swarm member turn failed: ${n.status}`)}getTask=async e=>{let t=await this.taskStore.get(e);return t?v({id:t.id,status:t.status,prompt:t.input.prompt,output:t.output?.text,error:t.error?.message}):y(`Task not found: ${e}`)};listTasks=async e=>{let t=e&&e!==`all`?{status:e}:void 0;return v((await this.taskStore.list(t)).map(e=>({id:e.id,prompt:e.input.prompt,status:e.status})))};updateTask=async(e,t)=>{let n=await this.taskStore.get(e);if(!n)return y(`Task not found: ${e}`);if(t===`cancel`){let t=await this.backgroundManager.cancel(e);return t.ok?(await this.hookRegistry.emit(`task.cancelled`,{taskId:e}),v(`Task ${e} cancelled`)):y(t.error)}if(t===`retry`){if(n.status!==`failed`)return y(`Can only retry failed tasks. Current status: ${n.status}`);let e=n.input.depth===void 0?void 0:n.input.depth-1,t=await this.dispatchTask({prompt:n.input.prompt,subagent:n.input.subagent,category:n.input.category,mode:n.input.mode??`sync`,sessionId:n.input.sessionId,sessionMode:n.input.sessionMode,slot:n.input.slot,model:n.input.model,depth:e});return t.ok?v(t.value.output??`Retry completed`):y(t.error)}return y(`Unknown action: ${t}`)};getBackgroundOutput=async e=>this.backgroundManager.getOutput(e);cancelBackground=async e=>{let t=await this.backgroundManager.cancel(e);if(t.ok){let t=await this.taskStore.get(e);this.releaseFanOutSlot(t?.input.parentTaskId)}return t};dispose(){this.circuitBreakers.resetAll(),this.fanOutCounts.clear()}acquireFanOutSlot(e){e!==void 0&&this.fanOutCounts.set(e,(this.fanOutCounts.get(e)??0)+1)}releaseFanOutSlot(e){if(e===void 0)return;let t=this.fanOutCounts.get(e);t!==void 0&&(t<=1?this.fanOutCounts.delete(e):this.fanOutCounts.set(e,t-1))}};function v(e){return{ok:!0,value:e}}function y(e){return{ok:!1,error:x(e)}}async function b(e){try{return v(await e())}catch(e){return y(e)}}function x(e){return e instanceof Error?e.message:String(e)}var S=class extends t{depth;maxDepth;constructor(e,t){super(`Handoff depth ${e} exceeds max ${t}`,{code:`SWARM_HANDOFF_DEPTH_EXCEEDED`}),this.name=`HandoffDepthError`,this.depth=e,this.maxDepth=t}},C=class extends t{constructor(e){super(e,{code:`SWARM_ROUTING_ERROR`}),this.name=`RoutingError`}},w=class extends t{agentId;constructor(e){super(`Agent "${e}" not found in swarm`,{code:`SWARM_AGENT_NOT_FOUND`}),this.name=`AgentNotFoundError`,this.agentId=e}},T=class extends t{constructor(e){super(e,{code:`SWARM_CONFIG_ERROR`}),this.name=`SwarmConfigError`}},E=class{config;roundRobinIndex=0;constructor(e){if(e.strategy===`llm`&&!e.customRouter)throw new C(`Routing strategy 'llm' requires a customRouter (inject the real LLM routing call).`);this.config=e}async route(e,t){let{strategy:n}=this.config;switch(n){case`rule`:return this.routeByRule(e,t);case`round-robin`:return this.routeRoundRobin(t);case`random`:return this.routeRandom(t);case`llm`:return this.routeByLlm(e,t);default:throw new C(`Unknown routing strategy: ${n}`)}}routeByRule(e,t){let n=this.config.rules??[],r=e.toLowerCase(),i=[...n].sort((e,t)=>(t.priority??0)-(e.priority??0));for(let e of i)if(this.matchRule(e,r)){let n=t.find(t=>t.id===e.agentId);if(n)return{agentId:e.agentId,reason:`Matched rule for "${n.name}"`,confidence:1}}return this.fallback(t,`No rule matched`)}routeRoundRobin(e){let t=e[this.roundRobinIndex%e.length];return this.roundRobinIndex++,{agentId:t.id,reason:`Round-robin selection (index ${this.roundRobinIndex-1})`,confidence:1}}routeRandom(e){let t=this.config.random?.random??Math.random;return{agentId:e[Math.floor(t()*e.length)].id,reason:`Random selection`,confidence:1/e.length}}async routeByLlm(e,t){let{customRouter:n}=this.config;if(!n)throw new C(`Routing strategy 'llm' requires a customRouter.`);return n(e,t)}fallback(e,t){let n=this.config.fallbackAgentId??e[0]?.id;if(!n)throw new C(`${t} and no fallback agent configured`);return{agentId:n,reason:`${t} — using fallback agent`,confidence:0}}matchRule(e,t){return e.match instanceof RegExp?e.match.test(t):e.match.some(e=>t.includes(e.toLowerCase()))}},D=class{strategies=new Map;register(e){if(this.strategies.has(e.name))throw Error(`Orchestration strategy "${e.name}" is already registered`);this.strategies.set(e.name,e)}get(e){return this.strategies.get(e)}has(e){return this.strategies.has(e)}names(){return[...this.strategies.keys()]}};function O(e,t,n){let i=t.map(e=>`- "${e.id}": ${e.name} — ${e.description}`).join(`
2
- `),a=t.map(e=>e.id);return{name:`handoff_to_agent`,description:`Transfer control to another agent in the swarm. Available targets:\n${i}`,parameters:r.object({target_agent_id:r.string().describe(`The ID of the agent to hand off to. Must be one of: ${a.join(`, `)}`),reason:r.string().describe(`Why you are transferring to this agent — will be provided as context.`),summary:r.string().optional().describe(`Optional summary of work done so far to provide context to the target agent.`)}),readonly:!0,execute:async t=>{let{target_agent_id:r,reason:i,summary:o}=t.params;return a.includes(r)?(n({from:e,to:r,reason:o?`${i}\n\nContext: ${o}`:i}),{content:[{type:`text`,text:`Handoff initiated to "${r}". Reason: ${i}`}]}):{content:[{type:`text`,text:`Invalid target agent "${r}". Available targets: ${a.join(`, `)}`}],isError:!0}}}}function k(e,t){let n=t.get(e.from),r=t.get(e.to);return n?r?n.kind===`agent`&&n.handoffTargets&&n.handoffTargets.length>0&&!n.handoffTargets.includes(e.to)?{valid:!1,error:`Agent "${e.from}" is not allowed to handoff to "${e.to}". Allowed: [${n.handoffTargets.join(`, `)}]`}:{valid:!0}:{valid:!1,error:`Target agent "${e.to}" not found`}:{valid:!1,error:`Source agent "${e.from}" not found`}}const A={name:`handoff`,async execute(e,t){let n=[...t.config.agents.values()],r=t.config.entryAgentId??n[0]?.id;if(!r)return{steps:[],output:``};let i=t.config.maxHandoffDepth??10,a=r,o=e,s=[];for(let e=0;e<i&&!t.signal.aborted;e++){let e=t.config.agents.get(a);if(!e)throw new w(a);let r=n.filter(e=>e.id!==a);if(e.kind===`agent`&&e.handoffTargets&&e.handoffTargets.length>0){let t=e.handoffTargets;r=r.filter(e=>t.includes(e.id))}let i=r.length>0?{source:`core`,name:`handoff_to_agent`,context:{from:a,allowedTargets:r.map(e=>({id:e.id,name:e.name,description:e.description}))}}:null;t.emit({type:`agent.start`,agentId:a});let c=Date.now(),l=await t.createStepRunner(e,o,{signal:t.signal,coreTools:i?[i]:void 0}),u=Date.now()-c;t.emit({type:`agent.end`,agentId:a,durationMs:u}),s.push({agentId:a,output:l,durationMs:u});let d=l.handoff;if(d){let e=d;if(!k(e,new Map(n.map(e=>[e.id,e]))).valid)return{steps:s,output:l.text};t.emit({type:`handoff`,from:e.from,to:e.to,reason:e.reason}),a=e.to,o=e.reason;continue}return{steps:s,output:l.text}}throw new S(i,i)}},j={name:`sequential`,async execute(e,t){let n=t.config.pipeline??[...t.config.agents.values()].map(e=>e.id),r=[],i=e;for(let e of n){if(t.signal.aborted)break;let n=t.config.agents.get(e);if(!n)continue;t.emit({type:`agent.start`,agentId:e});let a=Date.now(),o=await t.createStepRunner(n,i,{signal:t.signal}),s=Date.now()-a;t.emit({type:`agent.end`,agentId:e,durationMs:s}),r.push({agentId:e,output:o,durationMs:s}),i=o.text,t.emit({type:`pipeline.step`,step:r.length-1,agentId:e})}let a=r[r.length-1];return a?{steps:r,output:a.output.text}:{steps:[],output:``}}};async function M(e,t){let{cap:n,executor:r,signal:i,onProgress:a}=t;if(e.length===0)return;let o=Number.isFinite(n)&&n>=1?Math.floor(n):1,s=e.length,c=[...e],l=new Set,u=0,d=0,f,p=!1,m=new AbortController,h=()=>m.abort();i&&(i.aborted?m.abort():i.addEventListener(`abort`,h,{once:!0}));try{for(;c.length>0||l.size>0;){for((m.signal.aborted||p)&&(c.length=0);l.size<o&&c.length>0;){let e=c.shift(),t=d++,n;n=(async()=>{try{await r(e,t,m.signal)}catch(e){p||=(f=e,!0),m.abort()}finally{l.delete(n),u++,a?.(u,s)}})(),l.add(n)}l.size>0&&await Promise.race(l)}}finally{l.size>0&&await Promise.allSettled(l),i&&i.removeEventListener(`abort`,h)}if(p)throw f}const N={name:`parallel`,async execute(e,t){let n=[...t.config.agents.values()],r=t.config.maxConcurrency??n.length,i=n.map(t=>({agentId:t.id,input:e})),a=Array(i.length);t.emit({type:`parallel.fanOut`,agentIds:n.map(e=>e.id)}),await M(i,{cap:r,executor:async(e,n)=>{try{let r=t.config.agents.get(e.agentId);if(!r)return;t.emit({type:`agent.start`,agentId:e.agentId});let i=Date.now(),o=await t.createStepRunner(r,e.input,{signal:t.signal}),s=Date.now()-i;t.emit({type:`agent.end`,agentId:e.agentId,durationMs:s}),a[n]={agentId:e.agentId,output:o,durationMs:s}}catch(n){t.emit({type:`error`,agentId:e.agentId,error:n instanceof Error?n:Error(String(n))})}},signal:t.signal,onProgress:(e,n)=>{t.emit({type:`parallel.fanIn`,completedCount:e,totalCount:n})}});let o=a.filter(e=>e!==void 0);return{steps:o,output:o.map(e=>`## ${e.agentId}\n${e.output.text}`).join(`
1
+ import{checkDelegationDepth as e}from"@x-otto/orchestration-contracts";import{AgentError as t,createLogger as n}from"@x-otto/shared";import{z as r}from"zod";const i={now:()=>Date.now()};var a=class extends Error{constructor(e,t){super(e),this.reason=t,this.name=`TaskRejectedError`}};const o=new Set([`completed`,`failed`,`cancelled`]);var s=class{tasks=new Map;events=[];nextId=1;_maxStoredTasks;clock;constructor(e=1e3,t=i){this._maxStoredTasks=Math.max(1,e),this.clock=t}get maxStoredTasks(){return this._maxStoredTasks}get eventHistory(){return this.events}recordEvent(e){this.events.push(e)}generateId(){return`task_${this.clock.now()}_${this.nextId++}`}async create(e,t=3){this.evictForCapacity();let n={id:this.generateId(),status:`pending`,sessionPolicy:e.sessionMode??`ephemeral`,attempts:0,maxAttempts:t,input:e,createdAt:this.clock.now()};return this.tasks.set(n.id,n),this.recordEvent({type:`task.created`,taskId:n.id,at:n.createdAt}),n}async clear(){this.tasks.clear(),this.events.length=0}evictForCapacity(){let e=this.tasks.size-this.maxStoredTasks+1;if(e<=0)return;let t=[...this.tasks.values()].filter(e=>o.has(e.status)).sort((e,t)=>(e.completedAt??e.createdAt)-(t.completedAt??t.createdAt));for(let n of t){if(e<=0)break;this.tasks.delete(n.id);let t=this.clock.now();for(let e=this.events.length-1;e>=0;e--)this.events[e].taskId===n.id&&this.events.splice(e,1);this.recordEvent({type:`task.evicted`,taskId:n.id,at:t}),e--}e>0&&console.warn(`[TaskStore] capacity ${this.maxStoredTasks} exceeded by ${e} in-flight task(s); no terminal tasks left to evict — keeping in-flight tasks (will exceed cap).`)}async get(e){return this.tasks.get(e)}async list(e){let t=[...this.tasks.values()];return e?.status&&(t=t.filter(t=>t.status===e.status)),t}async update(e,t){let n=this.tasks.get(e);if(!n)throw Error(`Task not found: ${e}`);let r=n.status,i=t.output!==void 0;Object.assign(n,t);let a=this.clock.now();t.status!==void 0&&t.status!==r&&this.recordEvent({type:`task.status-changed`,taskId:e,from:r,to:t.status,at:a}),i&&this.recordEvent({type:`task.output-written`,taskId:e,at:a})}async delete(e){let t=this.tasks.delete(e);if(t)for(let t=this.events.length-1;t>=0;t--)this.events[t].taskId===e&&this.events.splice(t,1);return t}};const c={enabled:!0,maxAttempts:3,backoffMs:1e3,backoffMultiplier:2};var l=class e{config;constructor(e){this.config={...c,...e}}shouldRetry(e){return this.config.enabled&&e<this.config.maxAttempts}get maxAttempts(){return this.config.maxAttempts}getBackoff(e){return this.config.backoffMs*this.config.backoffMultiplier**(e-1)}static fromWorkflowOptions(t){let n=t?.retry;return new e({enabled:n?.enabled??!0,maxAttempts:n?.maxAttempts??3,backoffMs:n?.backoffMs,backoffMultiplier:n?.backoffMultiplier})}};const u={enabled:!0,failureThreshold:5,resetTimeoutMs:6e4};var d=class{enabled;failureThreshold;resetTimeoutMs;clock;consecutiveFailures=0;openedAt=null;constructor(e,t=i){let n={...u,...e};this.enabled=n.enabled,this.failureThreshold=n.failureThreshold,this.resetTimeoutMs=n.resetTimeoutMs,this.clock=t}isOpen(){return!this.enabled||this.openedAt===null?!1:this.clock.now()-this.openedAt>=this.resetTimeoutMs?(this.openedAt=null,this.consecutiveFailures=0,!1):!0}success(){this.consecutiveFailures=0,this.openedAt=null}failure(){this.consecutiveFailures++,this.consecutiveFailures>=this.failureThreshold&&(this.openedAt=this.clock.now())}reset(){this.consecutiveFailures=0,this.openedAt=null}},f=class e{breakers=new Map;constructor(e){this.factory=e}forTarget(e){let t=this.breakers.get(e);return t||(t=this.factory(),this.breakers.set(e,t)),t}resetAll(){for(let e of this.breakers.values())e.reset()}static perTarget(t,n=i){return new e(()=>new d(t,n))}static single(t){return new e(()=>t)}static fromWorkflowOptions(t,n=i){let r=t?.circuitBreaker;return e.perTarget({enabled:r?.enabled??!0,failureThreshold:r?.failureThreshold??5,resetTimeoutMs:r?.timeoutMs??6e4},n)}};function p(){let e=Error(`Semaphore acquire aborted`);return e.name=`AbortError`,e}var m=class{active=0;waiters=[];constructor(e){this.max=e}async acquire(e){if(e?.aborted)throw p();if(this.active<this.max){this.active+=1;return}await new Promise((t,n)=>{let r={resolve:t,reject:n,cleanup:()=>{}},i=()=>{let e=this.waiters.indexOf(r);e>=0&&this.waiters.splice(e,1),r.cleanup(),n(p())};r.cleanup=()=>e?.removeEventListener(`abort`,i),this.waiters.push(r),e?.addEventListener(`abort`,i,{once:!0})})}release(){let e=this.waiters.shift();e?(e.cleanup(),e.resolve()):--this.active}drain(){let e=this.waiters.splice(0);for(let t of e)t.cleanup(),t.reject(p())}async run(e,t){await this.acquire(t);try{return await e()}finally{this.release()}}get activeCount(){return this.active}get pendingCount(){return this.waiters.length}get capacity(){return this.max}},h=class{constructor(e,t,n,r,a,o,s,c=i){this.taskStore=e,this.hookRegistry=t,this.retryPolicy=n,this.circuitBreakers=r,this.runSession=a,this.semaphore=o,this.runAgentTurn=s,this.clock=c}breakerFor(e){return this.circuitBreakers.forTarget(e??`default`)}async runTask(e,t){this.assertCircuit(e.input.subagent);let n=await this.register(e);return this.runScheduled(n.id,t)}assertCircuit(e){if(this.breakerFor(e).isOpen())throw new a(`Circuit breaker is open due to repeated failures. Try again later.`,`circuit_open`)}async runScheduled(e,t){return this.semaphore.run(()=>this.executeToCompletion(e,t),t)}async isCancelled(e){return(await this.taskStore.get(e))?.status===`cancelled`}async register(e){let t=await this.taskStore.create(e.input,this.retryPolicy.maxAttempts);return this.hookRegistry.emit(`task.created`,{task:t}),t}async startTask(e){this.assertCircuit(e.input.subagent);let t=await this.register(e),n=this.runScheduled(t.id).then(()=>void 0,async e=>{await this.taskStore.update(t.id,{status:`failed`,error:{code:`EXECUTION_FAILED`,message:e instanceof Error?e.message:String(e),retriable:!1},completedAt:this.clock.now()}).catch(()=>{})});return{id:t.id,completion:n}}async executeToCompletion(e,t){let n=await this.taskStore.get(e);if(!n)return{taskId:e,status:`failed`,error:{code:`TASK_NOT_FOUND`,message:`Task not found: ${e}`,retriable:!1}};let r=n.attempts+1;try{await this.taskStore.update(e,{status:`running`,attempts:r}),this.hookRegistry.emit(`task.started`,{task:{...n,status:`running`},agent:n.input.subagent??`default`});let i;if(n.input.kind===`swarm_member`&&this.runAgentTurn){let e=n.input,r=await this.runAgentTurn({agentId:e.subagent??`agent`,prompt:e.prompt,systemPrompt:e.systemPrompt,tools:e.tools,coreTools:e.coreTools,model:e.model,maxToolTurns:e.maxToolTurns,thinkingLevel:e.thinkingLevel,maxTokens:e.maxTokens,temperature:e.temperature,sessionId:e.sessionId,depth:e.depth,parentTaskId:e.parentTaskId,signal:t});i={text:r.text,sessionId:n.input.sessionId??``,tokenUsage:r.tokenUsage,durationMs:r.durationMs,messages:r.messages,handoff:r.handoff}}else{let r=n.input;i=await this.runSession({prompt:r.prompt,sessionId:r.sessionId,sessionMode:n.sessionPolicy,agentName:r.subagent,slot:r.slot,model:r.model,category:r.category,depth:r.depth,parentTaskId:r.parentTaskId,taskId:e,signal:t,readOnly:r.mode===`background`,systemPrompt:r.systemPrompt,tools:r.tools,thinkingLevel:r.thinkingLevel,maxTokens:r.maxTokens,temperature:r.temperature,maxToolTurns:r.maxToolTurns})}if(await this.isCancelled(e))return{taskId:e,status:`cancelled`};let a={text:i.text,tokenUsage:i.tokenUsage,durationMs:i.durationMs,...i.messages?{messages:i.messages}:{},...i.handoff?{handoff:i.handoff}:{}};return await this.taskStore.update(e,{status:`completed`,output:a,sessionId:i.sessionId,completedAt:this.clock.now()}),this.breakerFor(n.input.subagent).success(),this.hookRegistry.emit(`task.completed`,{task:{...n,status:`completed`,output:a},result:a}),{taskId:e,status:`completed`,output:a}}catch(r){let i=r instanceof Error?r.message:String(r);if(await this.isCancelled(e))return{taskId:e,status:`cancelled`};if(this.retryPolicy.shouldRetry(n.attempts)&&!t?.aborted&&(await g(this.retryPolicy.getBackoff(n.attempts),t),!t?.aborted))return this.executeToCompletion(e,t);this.breakerFor(n.input.subagent).failure();let a={code:`EXECUTION_FAILED`,message:i,retriable:!1};return await this.taskStore.update(e,{status:`failed`,error:a,completedAt:this.clock.now()}),this.hookRegistry.emit(`task.failed`,{task:{...n,status:`failed`},error:a}),{taskId:e,status:`failed`,error:a}}}};function g(e,t){return t?.aborted?Promise.resolve():new Promise(n=>{let r,i=()=>{clearTimeout(r),n()};r=setTimeout(()=>{t?.removeEventListener(`abort`,i),n()},e),t?.addEventListener(`abort`,i,{once:!0})})}var _=class{constructor(e,t,n=i){this.taskStore=e,this.abortTask=t,this.clock=n}async getOutput(e){let t=await this.taskStore.get(e);return t?t.status===`completed`?{ok:!0,value:{status:`completed`,output:t.output?.text}}:t.status===`failed`?{ok:!1,error:t.error?.message??`Task failed`}:{ok:!0,value:{status:t.status}}:{ok:!1,error:`Task not found id: ${e}`}}async cancel(e){let t=await this.taskStore.get(e);return t?t.status===`completed`||t.status===`failed`||t.status===`cancelled`?{ok:!1,error:`Task already in terminal state: ${t.status}, id: ${e}`}:(await this.taskStore.update(e,{status:`cancelled`,completedAt:this.clock.now()}),this.abortTask&&this.abortTask(e),{ok:!0,value:void 0}):{ok:!1,error:`Task not found id: ${e}`}}},v=class{taskStore;executor;backgroundManager;hookRegistry;circuitBreakers;delegationGate;runSession;maxDelegationDepth;abortTask;fanOutCounts=new Map;maxFanOutPerParent;heldBackgroundSlots=new Set;constructor(e){let{hookRegistry:t,runSession:n,runAgentTurn:r,abortTask:a,workflowConfig:o}=e,c=o?.orchestration,u=e.maxActiveTasks??c?.maxActiveTasks??10,d=e.maxStoredTasks??c?.maxStoredTasks,p=e.maxDelegationDepth??c?.maxDelegationDepth??1;this.hookRegistry=t,this.runSession=n,this.maxDelegationDepth=p,this.maxFanOutPerParent=e.maxFanOutPerParent??c?.maxFanOutPerParent??Math.max(1,Math.floor(u/2)),this.abortTask=a;let g=e.clock??i;this.taskStore=new s(d,g);let v=l.fromWorkflowOptions(o);this.circuitBreakers=f.fromWorkflowOptions(o,g),this.delegationGate=new m(u),this.executor=new h(this.taskStore,t,v,this.circuitBreakers,n,this.delegationGate,r,g),this.backgroundManager=new _(this.taskStore,a,g)}dumpConfig(){return{maxActiveTasks:this.delegationGate.capacity,maxFanOutPerParent:this.maxFanOutPerParent,maxDelegationDepth:this.maxDelegationDepth,maxStoredTasks:this.taskStore.maxStoredTasks}}dispatchTask=async t=>{let{childDepth:n,exceeded:r}=e(t.depth,this.maxDelegationDepth);if(r)return b(`Delegation depth limit (${this.maxDelegationDepth}) reached — complete this subtask yourself instead of delegating further.`);let i={prompt:t.prompt,subagent:t.subagent,category:t.category,sessionId:t.sessionId,sessionMode:t.sessionMode??`ephemeral`,mode:t.mode,slot:t.slot,model:t.model,depth:n,...t.parentTaskId===void 0?{}:{parentTaskId:t.parentTaskId}};return this.dispatchUnit(i,t.signal)};async dispatchUnit(e,t){let n={input:e};if((e.mode??`sync`)===`background`){let t=e.parentTaskId;if(t!==void 0&&!this.acquireFanOutSlot(t))return b(`Fan-out limit (${this.maxFanOutPerParent}) reached for parent task "${t}" — wait for in-flight subtasks to complete before delegating more.`);let r=t!==void 0;return x(async()=>{let{id:e,completion:i}=await this.executor.startTask(n).catch(e=>{throw r&&this.releaseFanOutSlot(t),e});return r&&(this.heldBackgroundSlots.add(e),i.finally(()=>{this.releaseBackgroundSlot(e,t)})),{id:e,status:`pending`,output:`Background task created: ${e}`}})}if(!this.acquireFanOutSlot(e.parentTaskId))return b(`Fan-out limit (${this.maxFanOutPerParent}) reached for parent task "${e.parentTaskId}" — wait for in-flight subtasks to complete before delegating more.`);try{let e=await this.executor.runTask(n,t);return e.status===`completed`?y({id:e.taskId,status:e.status,output:e.output?.text}):b(e.error?.message??`Unknown error`)}catch(e){return b(e)}finally{this.releaseFanOutSlot(e.parentTaskId)}}callAgent=async(t,n,r)=>{let{childDepth:i,exceeded:a}=e(r?.depth,this.maxDelegationDepth);if(a)return b(`Delegation depth limit (${this.maxDelegationDepth}) reached — complete this subtask yourself instead of delegating further.`);let o=this.circuitBreakers.forTarget(t);return o.isOpen()?b(`Circuit breaker is open due to repeated failures. Try again later.`):this.acquireFanOutSlot(r?.parentTaskId)?this.delegationGate.run(async()=>{try{let e=await this.runSession({prompt:n,sessionMode:`ephemeral`,agentName:t,slot:r?.slot,model:r?.model,depth:i,parentTaskId:r?.parentTaskId,signal:r?.signal});return o.success(),y(e.text)}catch(e){return o.failure(),b(e)}},r?.signal).finally(()=>{this.releaseFanOutSlot(r?.parentTaskId)}):b(`Fan-out limit (${this.maxFanOutPerParent}) reached for parent task "${r?.parentTaskId}" — wait for in-flight subtasks to complete before delegating more.`)};async runAgentTurn(e){let t={input:{kind:`swarm_member`,prompt:e.prompt,subagent:e.agentId,systemPrompt:e.systemPrompt,tools:e.tools,coreTools:e.coreTools,model:e.model,maxToolTurns:e.maxToolTurns,thinkingLevel:e.thinkingLevel,maxTokens:e.maxTokens,temperature:e.temperature,sessionMode:`ephemeral`,sessionId:e.sessionId,depth:e.depth,parentTaskId:e.parentTaskId}},n=await this.executor.runTask(t,e.signal);if(n.status===`completed`)return{agentId:e.agentId,text:n.output?.text??``,messages:n.output?.messages,handoff:n.output?.handoff,tokenUsage:n.output?.tokenUsage,durationMs:n.output?.durationMs??0};throw Error(n.error?.message??`Swarm member turn failed: ${n.status}`)}getTask=async e=>{let t=await this.taskStore.get(e);return t?y({id:t.id,status:t.status,prompt:t.input.prompt,output:t.output?.text,error:t.error?.message}):b(`Task not found: ${e}`)};listTasks=async e=>{let t=e&&e!==`all`?{status:e}:void 0;return y((await this.taskStore.list(t)).map(e=>({id:e.id,prompt:e.input.prompt,status:e.status})))};updateTask=async(e,t)=>{let n=await this.taskStore.get(e);if(!n)return b(`Task not found: ${e}`);if(t===`cancel`){let t=await this.backgroundManager.cancel(e);return t.ok?(await this.hookRegistry.emit(`task.cancelled`,{taskId:e}),y(`Task ${e} cancelled`)):b(t.error)}if(t===`retry`){if(n.status!==`failed`)return b(`Can only retry failed tasks. Current status: ${n.status}`);let e=await this.dispatchUnit(n.input);return e.ok?y(e.value.output??`Retry completed`):b(e.error)}return b(`Unknown action: ${t}`)};getBackgroundOutput=async e=>this.backgroundManager.getOutput(e);cancelBackground=async e=>{let t=await this.backgroundManager.cancel(e);if(t.ok){let t=await this.taskStore.get(e);this.releaseBackgroundSlot(e,t?.input.parentTaskId)}return t};async dispose(){this.circuitBreakers.resetAll(),this.fanOutCounts.clear(),this.heldBackgroundSlots.clear();let e=await this.taskStore.list();for(let t of e)if(t.status===`pending`||t.status===`running`)try{this.abortTask?.(t.id)}catch{}await this.taskStore.clear(),this.delegationGate.drain()}acquireFanOutSlot(e){if(e===void 0)return!0;let t=this.fanOutCounts.get(e)??0;return t>=this.maxFanOutPerParent?!1:(this.fanOutCounts.set(e,t+1),!0)}releaseFanOutSlot(e){if(e===void 0)return;let t=this.fanOutCounts.get(e);t!==void 0&&(t<=1?this.fanOutCounts.delete(e):this.fanOutCounts.set(e,t-1))}releaseBackgroundSlot(e,t){this.heldBackgroundSlots.delete(e)&&this.releaseFanOutSlot(t)}};function y(e){return{ok:!0,value:e}}function b(e){return{ok:!1,error:S(e)}}async function x(e){try{return y(await e())}catch(e){return b(e)}}function S(e){return e instanceof Error?e.message:String(e)}var C=class extends t{depth;maxDepth;constructor(e,t){super(`Handoff depth ${e} exceeds max ${t}`,{code:`SWARM_HANDOFF_DEPTH_EXCEEDED`}),this.name=`HandoffDepthError`,this.depth=e,this.maxDepth=t}},w=class extends t{constructor(e){super(e,{code:`SWARM_ROUTING_ERROR`}),this.name=`RoutingError`}},T=class extends t{agentId;constructor(e){super(`Agent "${e}" not found in swarm`,{code:`SWARM_AGENT_NOT_FOUND`}),this.name=`AgentNotFoundError`,this.agentId=e}},E=class extends t{constructor(e){super(e,{code:`SWARM_CONFIG_ERROR`}),this.name=`SwarmConfigError`}},D=class{config;roundRobinIndex=0;constructor(e){if(e.strategy===`llm`&&!e.customRouter)throw new w(`Routing strategy 'llm' requires a customRouter (inject the real LLM routing call).`);this.config=e}async route(e,t){let{strategy:n}=this.config;switch(n){case`rule`:return this.routeByRule(e,t);case`round-robin`:return this.routeRoundRobin(t);case`random`:return this.routeRandom(t);case`llm`:return this.routeByLlm(e,t);default:throw new w(`Unknown routing strategy: ${n}`)}}routeByRule(e,t){let n=this.config.rules??[],r=e.toLowerCase(),i=[...n].sort((e,t)=>(t.priority??0)-(e.priority??0));for(let e of i)if(this.matchRule(e,r)){let n=t.find(t=>t.id===e.agentId);if(n)return{agentId:e.agentId,reason:`Matched rule for "${n.name}"`,confidence:1}}return this.fallback(t,`No rule matched`)}routeRoundRobin(e){let t=e[this.roundRobinIndex%e.length];return this.roundRobinIndex++,{agentId:t.id,reason:`Round-robin selection (index ${this.roundRobinIndex-1})`,confidence:1}}routeRandom(e){let t=this.config.random?.random??Math.random;return{agentId:e[Math.floor(t()*e.length)].id,reason:`Random selection`,confidence:1/e.length}}async routeByLlm(e,t){let{customRouter:n}=this.config;if(!n)throw new w(`Routing strategy 'llm' requires a customRouter.`);return n(e,t)}fallback(e,t){let n=this.config.fallbackAgentId??e[0]?.id;if(!n)throw new w(`${t} and no fallback agent configured`);return{agentId:n,reason:`${t} — using fallback agent`,confidence:0}}matchRule(e,t){return e.match instanceof RegExp?e.match.test(t):e.match.some(e=>t.includes(e.toLowerCase()))}};async function O(e,t,n,r,i){e.emit({type:`agent.start`,agentId:t});let a=e.clock.now();try{let o=await e.createStepRunner(e.config.agents.get(t),n,i),s=e.clock.now()-a;return e.emit({type:`agent.end`,agentId:t,durationMs:s}),r.push({agentId:t,output:o,durationMs:s}),o}catch(n){throw e.emit({type:`error`,agentId:t,error:n instanceof Error?n:Error(String(n))}),n}}var k=class{strategies=new Map;register(e){if(this.strategies.has(e.name))throw Error(`Orchestration strategy "${e.name}" is already registered`);return this.strategies.set(e.name,e),()=>{this.strategies.delete(e.name)}}unregister(e){return this.strategies.delete(e)}get(e){return this.strategies.get(e)}has(e){return this.strategies.has(e)}names(){return[...this.strategies.keys()]}};function A(e,t,n){let i=t.map(e=>`- "${e.id}": ${e.name} — ${e.description}`).join(`
2
+ `),a=t.map(e=>e.id);return{name:`handoff_to_agent`,description:`Transfer control to another agent in the swarm. Available targets:\n${i}`,parameters:r.object({target_agent_id:r.string().describe(`The ID of the agent to hand off to. Must be one of: ${a.join(`, `)}`),reason:r.string().describe(`Why you are transferring to this agent — will be provided as context.`),summary:r.string().optional().describe(`Optional summary of work done so far to provide context to the target agent.`)}),execute:async t=>{let{target_agent_id:r,reason:i,summary:o}=t.params;return a.includes(r)?(n({from:e,to:r,reason:o?`${i}\n\nContext: ${o}`:i}),{content:[{type:`text`,text:`Handoff initiated to "${r}". Reason: ${i}`}]}):{content:[{type:`text`,text:`Invalid target agent "${r}". Available targets: ${a.join(`, `)}`}],isError:!0}}}}function j(e,t){let n=t.get(e.from),r=t.get(e.to);return n?r?n.kind===`agent`&&n.handoffTargets&&n.handoffTargets.length>0&&!n.handoffTargets.includes(e.to)?{valid:!1,error:`Agent "${e.from}" is not allowed to handoff to "${e.to}". Allowed: [${n.handoffTargets.join(`, `)}]`}:{valid:!0}:{valid:!1,error:`Target agent "${e.to}" not found`}:{valid:!1,error:`Source agent "${e.from}" not found`}}const M={name:`handoff`,async execute(e,t){let n=[...t.config.agents.values()],r=t.config.entryAgentId??n[0]?.id;if(!r)return{steps:[],output:``};let i=t.config.maxHandoffDepth??10,a=r,o=e,s=[];for(let e=0;e<i&&!t.signal.aborted;e++){let e=t.config.agents.get(a);if(!e)throw new T(a);let r=n.filter(e=>e.id!==a);if(e.kind===`agent`&&e.handoffTargets&&e.handoffTargets.length>0){let t=e.handoffTargets;r=r.filter(e=>t.includes(e.id))}let i=r.length>0?{source:`core`,name:`handoff_to_agent`,context:{from:a,allowedTargets:r.map(e=>({id:e.id,name:e.name,description:e.description}))}}:null,c=await O(t,a,o,s,{signal:t.signal,coreTools:i?[i]:void 0}),l=c.handoff;if(l){let e=l;if(!j(e,new Map(n.map(e=>[e.id,e]))).valid)return{steps:s,output:c.text};t.emit({type:`handoff`,from:e.from,to:e.to,reason:e.reason}),a=e.to,o=e.reason;continue}return{steps:s,output:c.text}}throw new C(i,i)}},N={name:`sequential`,async execute(e,t){let n=t.config.pipeline??[...t.config.agents.values()].map(e=>e.id),r=[],i=e;for(let e of n){if(t.signal.aborted)break;t.config.agents.has(e)&&(i=(await O(t,e,i,r,{signal:t.signal})).text,t.emit({type:`pipeline.step`,step:r.length-1,agentId:e}))}let a=r[r.length-1];return a?{steps:r,output:a.output.text}:{steps:[],output:``}}};async function P(e,t){let{cap:n,executor:r,signal:i,onProgress:a}=t;if(e.length===0)return;let o=Number.isFinite(n)&&n>=1?Math.floor(n):1,s=e.length,c=[...e],l=new Set,u=0,d=0,f,p=!1,m=new AbortController,h=()=>m.abort();i&&(i.aborted?m.abort():i.addEventListener(`abort`,h,{once:!0}));try{for(;c.length>0||l.size>0;){for((m.signal.aborted||p)&&(c.length=0);l.size<o&&c.length>0;){let e=c.shift(),t=d++,n;n=(async()=>{try{await r(e,t,m.signal)}catch(e){p||=(f=e,!0),m.abort()}finally{l.delete(n),u++,a?.(u,s)}})(),l.add(n)}l.size>0&&await Promise.race(l)}}finally{l.size>0&&await Promise.allSettled(l),i&&i.removeEventListener(`abort`,h)}if(p)throw f}const F={name:`parallel`,async execute(e,t){let n=[...t.config.agents.values()],r=t.config.maxConcurrency??n.length,i=n.map(t=>({agentId:t.id,input:e})),a=Array(i.length);t.emit({type:`parallel.fanOut`,agentIds:n.map(e=>e.id)}),await P(i,{cap:r,executor:async(e,n)=>{if(!t.config.agents.has(e.agentId))return;let r=[];await O(t,e.agentId,e.input,r,{signal:t.signal}),a[n]=r[0]},signal:t.signal,onProgress:(e,n)=>{t.emit({type:`parallel.fanIn`,completedCount:e,totalCount:n})}});let o=a.filter(e=>e!==void 0);return{steps:o,output:o.map(e=>`## ${e.agentId}\n${e.output.text}`).join(`
3
3
 
4
- `)}}},P=n(`otto:orchestrator:strategies:hierarchical`),F=r.object({description:r.string(),assignedTo:r.string().optional()});function I(e){if(!Array.isArray(e))return[];let t=[];for(let n of e){let e=F.safeParse(n);e.success&&t.push(e.data)}return t}function L(e){try{let t=I(JSON.parse(e.trim()));if(t.length>0)return t}catch{}let t=e.match(/\[[\s\S]*\]/);if(t)try{let e=I(JSON.parse(t[0]));if(e.length>0)return e}catch{}return P.warn({textPreview:e.slice(0,200)},`hierarchical supervisor plan parse failed, degrading to single task`),[]}const R={name:`hierarchical`,async execute(e,t){let n=t.config.supervisorId??[...t.config.agents.values()][0]?.id??``;if(!n)return{steps:[],output:``};let r=t.config.agents.get(n);if(!r||r.kind!==`agent`)return{steps:[],output:``};t.emit({type:`agent.start`,agentId:n}),t.emit({type:`hierarchy.delegate`,supervisor:n,worker:``,task:`planning`});let i=Date.now(),a=await t.createStepRunner(r,`You are the supervisor. Break down this task into subtasks:\n\n${e}\n\nFor each subtask, specify:\n1. A clear description\n2. Which agent should execute it (choose from: ${[...t.config.agents.values()].map(e=>e.id).join(`, `)})\n\nRespond with a JSON array of {description, assignedTo}. Output only the JSON array, no other text.`,{signal:t.signal}),o=Date.now()-i;t.emit({type:`agent.end`,agentId:n,durationMs:o});let s=[{agentId:n,output:a,durationMs:o}],c=L(a.text);c.length===0&&(c=[{description:e,assignedTo:void 0}]);let l=[],u=[...t.config.agents.values()].find(e=>e.id!==n)?.id??n,d=c.map(e=>({...e,agentId:e.assignedTo??u}));await M(d,{cap:t.config.maxConcurrency??d.length,executor:async e=>{try{let r=t.config.agents.get(e.agentId);if(!r)return;t.emit({type:`agent.start`,agentId:e.agentId}),t.emit({type:`hierarchy.delegate`,supervisor:n,worker:e.agentId,task:e.description});let i=Date.now(),a=await t.createStepRunner(r,e.description,{signal:t.signal}),o=Date.now()-i;t.emit({type:`agent.end`,agentId:e.agentId,durationMs:o}),l.push({task:e,agentId:e.agentId,output:a,durationMs:o})}catch(n){t.emit({type:`error`,agentId:e.agentId,error:n instanceof Error?n:Error(String(n))})}},signal:t.signal});for(let e of l)s.push({agentId:e.agentId,output:e.output,durationMs:e.durationMs});let f=l.map(e=>`## ${e.task.description} (by ${e.agentId})\n${e.output.text}`),p=[`The subtasks have been completed. Here are the results:`,``,...f,``,`Synthesize these results into a final coherent response for the original task:`,e].join(`
4
+ `)}}},I=n(`otto:orchestrator:strategies:hierarchical`),L=r.object({description:r.string(),assignedTo:r.string().optional()});function R(e){if(!Array.isArray(e))return[];let t=[];for(let n of e){let e=L.safeParse(n);e.success&&t.push(e.data)}return t}function z(e){try{let t=R(JSON.parse(e.trim()));if(t.length>0)return t}catch{}let t=e.match(/\[[\s\S]*\]/);if(t)try{let e=R(JSON.parse(t[0]));if(e.length>0)return e}catch{}return I.warn({textPreview:e.slice(0,200)},`hierarchical supervisor plan parse failed, degrading to single task`),[]}const B={name:`hierarchical`,async execute(e,t){let n=t.config.supervisorId??[...t.config.agents.values()][0]?.id??``;if(!n)return{steps:[],output:``};let r=t.config.agents.get(n);if(!r||r.kind!==`agent`)return{steps:[],output:``};t.emit({type:`agent.start`,agentId:n}),t.emit({type:`hierarchy.delegate`,supervisor:n,worker:``,task:`planning`});let i=t.clock.now(),a=await t.createStepRunner(r,`You are the supervisor. Break down this task into subtasks:\n\n${e}\n\nFor each subtask, specify:\n1. A clear description\n2. Which agent should execute it (choose from: ${[...t.config.agents.values()].map(e=>e.id).join(`, `)})\n\nRespond with a JSON array of {description, assignedTo}. Output only the JSON array, no other text.`,{signal:t.signal}),o=t.clock.now()-i;t.emit({type:`agent.end`,agentId:n,durationMs:o});let s=[{agentId:n,output:a,durationMs:o}],c=z(a.text);c.length===0&&(c=[{description:e,assignedTo:void 0}]);let l=[],u=[...t.config.agents.values()].find(e=>e.id!==n)?.id??n,d=c.map(e=>({...e,agentId:e.assignedTo??u}));await P(d,{cap:t.config.maxConcurrency??d.length,executor:async e=>{try{let r=t.config.agents.get(e.agentId);if(!r)return;t.emit({type:`agent.start`,agentId:e.agentId}),t.emit({type:`hierarchy.delegate`,supervisor:n,worker:e.agentId,task:e.description});let i=t.clock.now(),a=await t.createStepRunner(r,e.description,{signal:t.signal}),o=t.clock.now()-i;t.emit({type:`agent.end`,agentId:e.agentId,durationMs:o}),l.push({task:e,agentId:e.agentId,output:a,durationMs:o})}catch(n){throw t.emit({type:`error`,agentId:e.agentId,error:n instanceof Error?n:Error(String(n))}),n}},signal:t.signal});for(let e of l)s.push({agentId:e.agentId,output:e.output,durationMs:e.durationMs});let f=l.map(e=>`## ${e.task.description} (by ${e.agentId})\n${e.output.text}`),p=[`The subtasks have been completed. Here are the results:`,``,...f,``,`Synthesize these results into a final coherent response for the original task:`,e].join(`
5
5
  `);if(t.signal.aborted)return{steps:s,output:f.join(`
6
6
 
7
- `)};t.emit({type:`agent.start`,agentId:n});let m=Date.now(),h=await t.createStepRunner(r,p,{signal:t.signal}),g=Date.now()-m;return t.emit({type:`agent.end`,agentId:n,durationMs:g}),s.push({agentId:n,output:h,durationMs:g}),{steps:s,output:h.text}}},z={name:`router`,async execute(e,t){if(!t.router)throw new T(`Router not configured`);let n=[...t.config.agents.values()],r=await t.router.route(e,n),i=t.config.agents.get(r.agentId);if(!i)throw new w(r.agentId);t.emit({type:`routing.decision`,decision:r}),t.emit({type:`agent.start`,agentId:r.agentId});let a=Date.now(),o=await t.createStepRunner(i,e,{signal:t.signal}),s=Date.now()-a;return t.emit({type:`agent.end`,agentId:r.agentId,durationMs:s}),{steps:[{agentId:r.agentId,output:o,durationMs:s}],output:o.text}}};function B(){let e=new D;return e.register(A),e.register(j),e.register(N),e.register(R),e.register(z),e}var V=class{config;agents;router;registry;constructor(e){this.validateConfig(e),this.config=e,this.agents=new Map;for(let t of e.agents)this.agents.set(t.id,t);this.router=e.router?new E(e.router):null,this.registry=B()}getAgents(){return[...this.agents.values()]}getAgent(e){return this.agents.get(e)}registerStrategy(e){this.registry.register(e)}getStrategyNames(){return this.registry.names()}emitEvent(e){if(this.config.onSwarmEvent?.(e),this.config.hookRegistry){let t={id:`swarm:${this.config.runId??this.config.name}`,status:`running`,attempts:0,maxAttempts:1,createdAt:Date.now(),swarmEvent:e};this.config.hookRegistry.emit(`task.started`,{task:t,agent:`swarm:${this.config.name}`})}}buildStrategyContext(e){return{createStepRunner:(t,n,r)=>this.executeMemberTurn(t,n,r?.signal??e,r?.coreTools),emit:e=>this.emitEvent(e),router:this.router,signal:e,config:{name:this.config.name,agents:this.agents,entryAgentId:this.config.entryAgentId,pipeline:this.config.pipeline,supervisorId:this.config.supervisorId,maxHandoffDepth:this.config.maxHandoffDepth,maxConcurrency:this.config.maxConcurrency}}}async run(e,t){let n=t?.signal??new AbortController().signal,r=Date.now(),i=e=>this.emitEvent(e);i({type:`swarm.start`,pattern:this.config.pattern,agentCount:this.agents.size});let a=this.registry.get(this.config.pattern);if(!a)throw new T(`Unknown pattern: "${this.config.pattern}". Registered strategies: ${this.registry.names().join(`, `)}`);try{let t=this.buildStrategyContext(n),o=await a.execute(e,t);return i({type:`swarm.end`,totalDurationMs:Date.now()-r}),{pattern:this.config.pattern,steps:o.steps,output:o.output,durationMs:Date.now()-r}}catch(e){throw i({type:`swarm.end`,totalDurationMs:Date.now()-r}),e}}async executeMemberTurn(e,t,n,r){if(e.kind===`team`)return r?.length&&this.config.onSwarmEvent?.({type:`error`,agentId:e.id,error:Error(`Nested team "${e.id}" does not support core tools (${r.map(e=>e.name).join(`, `)}); descriptors dropped`)}),e.runNested(t,n);let i=e,a=await this.config.runAgentTurn({agentId:i.id,prompt:t,systemPrompt:i.systemPrompt,tools:i.tools,coreTools:r,model:i.model?.id,maxToolTurns:i.maxToolTurns,thinkingLevel:i.thinkingLevel,maxTokens:i.maxTokens,temperature:i.temperature,sessionId:this.config.runId,depth:this.config.depth,signal:n});return{agentId:a.agentId,messages:a.messages??[],text:a.text,handoff:a.handoff,tokenUsage:a.tokenUsage??{input:0,output:0,cacheRead:0},durationMs:a.durationMs}}validateConfig(e){if(!e.name)throw new T(`Swarm name is required`);if(!e.agents||e.agents.length===0)throw new T(`At least one agent is required`);if(!e.runAgentTurn)throw new T(`runAgentTurn factory is required`);let t=new Set;for(let n of e.agents){if(t.has(n.id))throw new T(`Duplicate agent ID: "${n.id}"`);t.add(n.id)}for(let n of e.agents)if(n.kind===`agent`&&n.handoffTargets){for(let e of n.handoffTargets)if(!t.has(e))throw new T(`Agent "${n.id}" references unknown handoff target "${e}"`)}}};function H(e){return new V(e)}export{w as AgentNotFoundError,u as CircuitBreaker,A as HANDOFF_STRATEGY,R as HIERARCHICAL_STRATEGY,S as HandoffDepthError,_ as Orchestrator,N as PARALLEL_STRATEGY,z as ROUTER_STRATEGY,c as RetryPolicy,C as RoutingError,j as SEQUENTIAL_STRATEGY,D as StrategyRegistry,V as Swarm,T as SwarmConfigError,E as SwarmRouter,i as TaskRejectedError,o as TaskStore,B as createDefaultStrategyRegistry,O as createHandoffTool,H as createSwarm,M as runConcurrent,k as validateHandoff};
7
+ `)};t.emit({type:`agent.start`,agentId:n});let m=t.clock.now(),h=await t.createStepRunner(r,p,{signal:t.signal}),g=t.clock.now()-m;return t.emit({type:`agent.end`,agentId:n,durationMs:g}),s.push({agentId:n,output:h,durationMs:g}),{steps:s,output:h.text}}},V={name:`router`,async execute(e,t){if(!t.router)throw new E(`Router not configured`);let n=[...t.config.agents.values()],r=await t.router.route(e,n);if(!t.config.agents.has(r.agentId))throw new T(r.agentId);t.emit({type:`routing.decision`,decision:r});let i=[];return{steps:i,output:(await O(t,r.agentId,e,i,{signal:t.signal})).text}}};function H(){let e=new k;return e.register(M),e.register(N),e.register(F),e.register(B),e.register(V),e}var U=class{config;agents;router;registry;clock;constructor(e){this.validateConfig(e),this.config=e,this.agents=new Map,this.clock=e.clock??i;for(let t of e.agents)this.agents.set(t.id,t);this.router=e.router?new D(e.router):null,this.registry=H()}getAgents(){return[...this.agents.values()]}getAgent(e){return this.agents.get(e)}registerStrategy(e){return this.registry.register(e)}getStrategyNames(){return this.registry.names()}emitEvent(e){this.config.onSwarmEvent?.(e)}buildStrategyContext(e){return{createStepRunner:(t,n,r)=>this.executeMemberTurn(t,n,r?.signal??e,r?.coreTools),emit:e=>this.emitEvent(e),router:this.router,signal:e,clock:this.clock,config:{name:this.config.name,agents:this.agents,entryAgentId:this.config.entryAgentId,pipeline:this.config.pipeline,supervisorId:this.config.supervisorId,maxHandoffDepth:this.config.maxHandoffDepth,maxConcurrency:this.config.maxConcurrency}}}async run(e,t){let n=t?.signal??new AbortController().signal,r=this.clock.now(),i=e=>this.emitEvent(e);i({type:`swarm.start`,pattern:this.config.pattern,agentCount:this.agents.size});let a=this.registry.get(this.config.pattern);if(!a)throw new E(`Unknown pattern: "${this.config.pattern}". Registered strategies: ${this.registry.names().join(`, `)}`);try{let t=this.buildStrategyContext(n),o=await a.execute(e,t);return i({type:`swarm.end`,totalDurationMs:this.clock.now()-r}),{pattern:this.config.pattern,steps:o.steps,output:o.output,durationMs:this.clock.now()-r}}catch(e){throw i({type:`swarm.end`,totalDurationMs:this.clock.now()-r}),e}}async executeMemberTurn(e,t,n,r){if(e.kind===`team`)return r?.length&&this.config.onSwarmEvent?.({type:`error`,agentId:e.id,error:Error(`Nested team "${e.id}" does not support core tools (${r.map(e=>e.name).join(`, `)}); descriptors dropped`)}),e.runNested(t,n);let i=e,a=await this.config.runAgentTurn({agentId:i.id,prompt:t,systemPrompt:i.systemPrompt,tools:i.tools,coreTools:r,model:i.model?.id,maxToolTurns:i.maxToolTurns,thinkingLevel:i.thinkingLevel,maxTokens:i.maxTokens,temperature:i.temperature,sessionId:this.config.runId,depth:this.config.depth,parentTaskId:this.config.parentTaskId,signal:n});return{agentId:a.agentId,messages:a.messages??[],text:a.text,handoff:a.handoff,tokenUsage:a.tokenUsage??{input:0,output:0,cacheRead:0},durationMs:a.durationMs}}validateConfig(e){if(!e.name)throw new E(`Swarm name is required`);if(!e.agents||e.agents.length===0)throw new E(`At least one agent is required`);if(!e.runAgentTurn)throw new E(`runAgentTurn factory is required`);let t=new Set;for(let n of e.agents){if(t.has(n.id))throw new E(`Duplicate agent ID: "${n.id}"`);t.add(n.id)}for(let n of e.agents)if(n.kind===`agent`&&n.handoffTargets){for(let e of n.handoffTargets)if(!t.has(e))throw new E(`Agent "${n.id}" references unknown handoff target "${e}"`)}}};function W(e){return new U(e)}export{T as AgentNotFoundError,d as CircuitBreaker,M as HANDOFF_STRATEGY,B as HIERARCHICAL_STRATEGY,C as HandoffDepthError,v as Orchestrator,F as PARALLEL_STRATEGY,V as ROUTER_STRATEGY,l as RetryPolicy,w as RoutingError,N as SEQUENTIAL_STRATEGY,k as StrategyRegistry,U as Swarm,E as SwarmConfigError,D as SwarmRouter,a as TaskRejectedError,s as TaskStore,H as createDefaultStrategyRegistry,A as createHandoffTool,W as createSwarm,P as runConcurrent,j as validateHandoff};
8
8
  //# sourceMappingURL=index.js.map