@alvin0/ai-agent-sdk-core 0.1.4 → 0.1.6
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/README.md +13 -1
- package/dist/agent/define/definition.d.ts +1 -1
- package/dist/agent/define/definition.js +1 -1
- package/dist/agent/define/definition.js.map +1 -1
- package/dist/agent/define/session/types.d.ts +6 -0
- package/dist/agent/define/session/types.d.ts.map +1 -1
- package/dist/agent/define/session.d.ts +39 -2
- package/dist/agent/define/session.d.ts.map +1 -1
- package/dist/agent/define/session.js +1 -1
- package/dist/agent/define/session.js.map +1 -1
- package/dist/agent/loop/admission.js +2 -0
- package/dist/agent/loop/admission.js.map +1 -0
- package/dist/agent/loop/events.d.ts +4 -0
- package/dist/agent/loop/events.d.ts.map +1 -1
- package/dist/agent/loop/program.js +2 -0
- package/dist/agent/loop/program.js.map +1 -0
- package/dist/agent/loop/run-turn.d.ts.map +1 -1
- package/dist/agent/loop/run-turn.js +1 -1
- package/dist/agent/loop/run-turn.js.map +1 -1
- package/dist/agent/loop/schedule.d.ts.map +1 -1
- package/dist/agent/loop/schedule.js +2 -2
- package/dist/agent/loop/schedule.js.map +1 -1
- package/dist/agent/loop/tool-call-support.js +2 -0
- package/dist/agent/loop/tool-call-support.js.map +1 -0
- package/dist/agent/loop/turn/hooks.js +1 -1
- package/dist/agent/loop/turn/hooks.js.map +1 -1
- package/dist/agent/loop/turn/model-request-boundary.js +2 -0
- package/dist/agent/loop/turn/model-request-boundary.js.map +1 -0
- package/dist/agent/loop/turn/model-round.js +1 -1
- package/dist/agent/loop/turn/model-round.js.map +1 -1
- package/dist/agent/loop/turn/step-projection.js +2 -0
- package/dist/agent/loop/turn/step-projection.js.map +1 -0
- package/dist/agent/loop/turn/types.d.ts +6 -0
- package/dist/agent/loop/turn/types.d.ts.map +1 -1
- package/dist/agent/memory/context-optimizer.d.ts +61 -0
- package/dist/agent/memory/context-optimizer.d.ts.map +1 -0
- package/dist/agent/memory/context-optimizer.js +4 -0
- package/dist/agent/memory/context-optimizer.js.map +1 -0
- package/dist/agent/memory/memory.js +1 -1
- package/dist/agent/memory/memory.js.map +1 -1
- package/dist/agent/mode/run-agent.d.ts +15 -1
- package/dist/agent/mode/run-agent.d.ts.map +1 -1
- package/dist/agent/mode/run-agent.js +2 -2
- package/dist/agent/mode/run-agent.js.map +1 -1
- package/dist/agent/skill/provider/snapshot.js +1 -1
- package/dist/agent/team/contracts.d.ts +5 -0
- package/dist/agent/team/contracts.d.ts.map +1 -1
- package/dist/agent/team/managed.d.ts +33 -64
- package/dist/agent/team/managed.d.ts.map +1 -1
- package/dist/agent/team/managed.js +6 -3
- package/dist/agent/team/managed.js.map +1 -1
- package/dist/agent/team/team.d.ts +2 -0
- package/dist/agent/team/team.d.ts.map +1 -1
- package/dist/agent/team/team.js +1 -1
- package/dist/agent/team/team.js.map +1 -1
- package/dist/agent/team/types.d.ts +1 -0
- package/dist/agent/team/types.d.ts.map +1 -1
- package/dist/agent/tool/action-fusion.d.ts +30 -0
- package/dist/agent/tool/action-fusion.d.ts.map +1 -0
- package/dist/agent/tool/action-fusion.js +2 -0
- package/dist/agent/tool/action-fusion.js.map +1 -0
- package/dist/agent/tool/capture.js +1 -1
- package/dist/agent/tool/capture.js.map +1 -1
- package/dist/agent/tool/definition.d.ts +8 -0
- package/dist/agent/tool/definition.d.ts.map +1 -1
- package/dist/agent/tool/definition.js.map +1 -1
- package/dist/agent/tool/evidence-reducer.d.ts +41 -0
- package/dist/agent/tool/evidence-reducer.d.ts.map +1 -0
- package/dist/agent/tool/evidence-reducer.js +7 -0
- package/dist/agent/tool/evidence-reducer.js.map +1 -0
- package/dist/agent/tool/nested.d.ts +117 -0
- package/dist/agent/tool/nested.d.ts.map +1 -0
- package/dist/agent/tool/nested.js +2 -0
- package/dist/agent/tool/nested.js.map +1 -0
- package/dist/agent/tool/output-budget.d.ts.map +1 -1
- package/dist/agent/tool/output-budget.js +2 -2
- package/dist/agent/tool/output-budget.js.map +1 -1
- package/dist/agent/tool/output-schema.js +2 -0
- package/dist/agent/tool/output-schema.js.map +1 -0
- package/dist/agent/tool/pipeline.d.ts +7 -0
- package/dist/agent/tool/pipeline.d.ts.map +1 -1
- package/dist/agent/tool/pipeline.js +2 -2
- package/dist/agent/tool/pipeline.js.map +1 -1
- package/dist/agent/tool/program-results.js +2 -0
- package/dist/agent/tool/program-results.js.map +1 -0
- package/dist/agent.d.ts +6 -2
- package/dist/agent.js +1 -1
- package/dist/composition/agent/definition.js +1 -1
- package/dist/composition/agent/options.js +1 -1
- package/dist/composition/agent/options.js.map +1 -1
- package/dist/composition/agent/session.js +1 -1
- package/dist/composition/agent/session.js.map +1 -1
- package/dist/composition/agent/types.d.ts +6 -0
- package/dist/composition/agent/types.d.ts.map +1 -1
- package/dist/composition/tool-source/snapshot.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/memory.d.ts +2 -1
- package/dist/memory.js +1 -1
- package/dist/observability/bus.js +1 -1
- package/dist/primitives/version.d.ts +1 -1
- package/dist/primitives/version.js +1 -1
- package/dist/primitives/version.js.map +1 -1
- package/dist/runtime/registry.js +1 -1
- package/dist/tools.d.ts +4 -1
- package/dist/tools.js +1 -1
- package/package.json +1 -1
package/dist/agent/team/team.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{waitForSettlement as e}from"../../async/settlement.js";import{timeoutValue as t}from"../../platform/config.js";import{AgentSdkError as n}from"../../errors/agent-sdk-error.js";import{createUserMessage as r}from"../../message/message.js";import{defineTool as i}from"../tool/definition.js";import{TEAM_TOOL_NAMES as a,abortable as o,asJson as s,boundedString as c,byteLength as l,combineSignals as u,deepCloneFreeze as d,emptyObject as f,errorMessage as p,memberName as m,messageContent as h,messageToolSchema as g,newMessageId as _,newTeamId as v,parseMessageTool as y,parseWaitTool as b,positiveInteger as x,withTimeout as S}from"./common.js";var C=class{ownRemoteTask;id;maxMembers;maxMessageBytes;maxLinkedResultBytes;maxMessages;maxMailboxBytes;maxMetadataBytes;disposeTimeoutMs;operationTimeoutMs;observerTimeoutMs;waitTimeoutMs;minWaitTimeoutMs;onEvent;onAgentEvent;roster=new Map;links=new Map;mailbox=[];mailboxBytes=0;pendingMessages=0;pendingMailboxBytes=0;lifecycle=new AbortController;waitEdges=new Map;disposed=!1;disposeTask;constructor(e={},n){this.ownRemoteTask=n,this.id=c(e.id??v(),`team id`,1024),this.maxMembers=x(e.maxMembers??8,`maxMembers`),this.maxMessageBytes=x(e.maxMessageBytes??65536,`maxMessageBytes`),this.maxLinkedResultBytes=x(e.maxLinkedResultBytes??1048576,`maxLinkedResultBytes`),this.maxMessages=x(e.maxMessages??1e4,`maxMessages`),this.maxMailboxBytes=x(e.maxMailboxBytes??67108864,`maxMailboxBytes`),this.maxMetadataBytes=x(e.maxMetadataBytes??8192,`maxMetadataBytes`),this.disposeTimeoutMs=t(e.disposeTimeoutMs??3e4),this.operationTimeoutMs=t(e.operationTimeoutMs??6e5),this.observerTimeoutMs=t(e.observerTimeoutMs??1e3),this.waitTimeoutMs=t(e.waitTimeoutMs??3e4),this.minWaitTimeoutMs=Math.min(t(e.minWaitTimeoutMs??5e3),this.waitTimeoutMs),this.onEvent=e.onEvent,this.onAgentEvent=e.onAgentEvent}attach(e,t={}){this.assertActive();let n=m(t.name??e.definition.id);this.assertAddressAvailable(n),this.assertCapacity();let r=t.role??(this.roster.size===0?`lead`:`peer`);if(r===`lead`&&[...this.roster.values()].some(e=>e.role===`lead`))throw Error(`A2A team '${this.id}' already has a lead`);let i={kind:`local`,name:n,role:r,access:t.tools===`reporting`?`reporting`:`full`,session:e,wakeRequestedSeq:0,wakeConsumedSeq:0,wakeTask:void 0,wakeController:void 0,error:void 0,outcome:void 0,pendingStart:void 0,steerController:void 0,...t.description===void 0?{}:{description:c(t.description,`member description`,this.maxMetadataBytes)},...t.instructions===void 0?{}:{instructions:c(t.instructions,`member instructions`,this.maxMetadataBytes)}};this.roster.set(n,i),this.emit({type:`member-attached`,member:this.view(i)})}linkAgent(e){this.assertActive();let t=m(e.name);if(this.assertAddressAvailable(t),this.assertCapacity(),typeof e.transport?.send!=`function`)throw TypeError(`linked agent transport must implement send()`);let n={kind:`remote`,name:t,role:`peer`,transport:e.transport,tail:Promise.resolve(),pending:0,controllers:new Set,error:void 0,...e.description===void 0?{}:{description:c(e.description,`linked agent description`,this.maxMetadataBytes)}};return this.links.set(t,n),this.emit({type:`member-linked`,member:this.view(n)}),()=>{if(this.links.get(t)===n){if(n.pending>0)throw Error(`cannot unlink running A2A member '${t}'`);this.links.delete(t)}}}detach(e){this.assertActive();let t=this.requireLocalMember(e);if(t.session.isRunning||t.wakeTask!==void 0)throw Error(`cannot detach running A2A member '${t.name}'`);this.roster.delete(t.name)}members(){return Object.freeze([...this.roster.values(),...this.links.values()].map(e=>this.view(e)))}messages(){return d(this.mailbox)}async sendMessage(e){this.assertActive(),e.signal?.throwIfAborted();let t=this.requireLocalMember(e.from),n=this.requireAddress(e.target);if(n.kind===`local`&&t===n)throw Error(`an A2A member cannot message itself`);let i=e.delivery??`quiet`;if(i!==`quiet`&&i!==`wakeup`)throw TypeError(`A2A delivery must be 'quiet' or 'wakeup'`);if(n.kind===`remote`&&i===`quiet`)throw Error(`remote A2A member '${n.name}' cannot accept quiet injection; use followup_task or delivery 'wakeup'`);let a=_(),o=h(e.message,this.maxMessageBytes),s=d([{type:`text`,text:`A2A message ${a} from ${t.name}:`},...o]),c=l(s);if(c>this.maxMessageBytes)throw Error(`A2A message exceeds the ${this.maxMessageBytes}-byte limit`);let u=n.kind===`remote`?c+this.maxLinkedResultBytes:c,f=this.reserveMailbox(u),p;try{if(n.kind===`local`){let e=r({source:{kind:`agent-message`,teamId:this.id,messageId:a,sender:t.name,senderAgentId:t.session.definition.id},content:s}),o=n.session.inject(e);i===`wakeup`&&this.scheduleWake(n,o)}else p=await this.dispatchRemote(n,{teamId:this.id,messageId:a,sender:t.name,senderAgentId:t.session.definition.id,content:s,...e.signal===void 0?{}:{signal:e.signal}});let o=d({id:a,teamId:this.id,sender:t.name,senderAgentId:t.session.definition.id,target:n.name,targetAgentId:n.kind===`local`?n.session.definition.id:n.transport.agentId,delivery:i,content:s,status:`accepted`,createdAt:new Date().toISOString(),...p===void 0?{}:{result:p}}),u=c+(p===void 0?0:l(p));return this.mailbox.push(o),this.mailboxBytes+=u,this.emit({type:`message-accepted`,message:d(o)}),d({messageId:a,status:`accepted`,delivery:i,target:n.name,...p===void 0?{}:{result:p}})}finally{f()}}followup(e,t,n,r){return this.sendMessage({from:e,target:t,message:n,delivery:`wakeup`,...r===void 0?{}:{signal:r}})}async whenIdle(e,t){let n=this.requireAddress(e);if(n.kind===`remote`){for(;;){let e=n.tail;if(await o(e,t),e===n.tail&&n.pending===0)break}return}for(;;){let e=n.wakeRequestedSeq,r=n.wakeTask,i=n.pendingStart;if(i!==void 0&&await o(i,t),r!==void 0&&await o(r,t),await n.session.whenIdle(t),n.wakeTask===void 0&&!n.session.isRunning&&n.pendingStart===void 0&&n.wakeConsumedSeq>=n.wakeRequestedSeq&&e===n.wakeRequestedSeq)return}}notifySteer(e){let t=this.requireLocalMember(e),n=t.steerController;t.steerController=void 0,n?.abort(Error(`user input steered into the active turn`))}wake(e){let t=this.requireLocalMember(e);this.scheduleWake(t,t.wakeRequestedSeq+1)}async cancel(e,t=Error(`A2A member work cancelled`)){let n=this.requireAddress(e);if(n.kind===`remote`){for(let e of n.controllers)e.abort(t);await S(n.tail,this.disposeTimeoutMs,`A2A member '${n.name}' did not cancel within ${this.disposeTimeoutMs}ms`,`TEAM_CANCELLATION_TIMEOUT`);return}n.wakeConsumedSeq=n.wakeRequestedSeq;let r=n.wakeController,i=n.wakeTask;r?.abort(t),i!==void 0&&await S(i,this.disposeTimeoutMs,`A2A member '${n.name}' did not cancel within ${this.disposeTimeoutMs}ms`,`TEAM_CANCELLATION_TIMEOUT`)}dispose(e=Error(`A2A team disposed`)){if(this.disposeTask!==void 0)return this.disposeTask;this.disposed=!0,this.lifecycle.abort(e);let t=Promise.allSettled([...[...this.roster.values()].map(t=>this.cancel(t.name,e)),...[...this.links.values()].map(t=>this.cancel(t.name,e))]).then(e=>{this.roster.clear(),this.links.clear(),this.emit({type:`team-disposed`,teamId:this.id}),this.mailbox.length=0,this.mailboxBytes=0,this.waitEdges.clear(),this.onEvent=void 0,this.onAgentEvent=void 0;let t=e.find(e=>e.status===`rejected`);if(t!==void 0)throw new n(`A2A team '${this.id}' did not dispose within ${this.disposeTimeoutMs}ms`,`TEAM_DISPOSE_TIMEOUT`,{cause:t.reason})});return this.disposeTask=S(t,this.disposeTimeoutMs,`A2A team '${this.id}' did not dispose within ${this.disposeTimeoutMs}ms`,`TEAM_DISPOSE_TIMEOUT`),this.disposeTask}toolsFor(e,n=`full`){this.assertActive();let r=m(e),o=n===`full`;return Object.freeze([i({name:a.list,budgetExempt:!0,description:`List local and remote addressable agents, their protocols, supported delivery modes, and status.`,parameters:{type:`object`,properties:{},additionalProperties:!1},parse:e=>f(e,`list_agents`),execute:()=>s(this.members()),isConcurrencySafe:()=>!0}),i({name:a.send,budgetExempt:!0,description:`Inject quiet context into ANOTHER local agent without starting it. Name a target from list_agents other than yourself; your own result already goes back to whoever started you, so reporting does not need this tool. Remote A2A peers require followup_task.`,parameters:g(`Message to add to the target context.`),parse:y,execute:async({target:e,message:t},n)=>s(await this.sendMessage({from:r,target:e,message:t,delivery:`quiet`,signal:n.signal}))}),...o?[i({name:a.followup,budgetExempt:!0,description:`Send active work to a local agent or interoperable remote A2A peer and wait for its accepted result.`,parameters:g(`Follow-up instruction the target must process.`),parse:y,execute:async({target:e,message:t},n)=>s(await this.followup(r,e,t,n.signal))})]:[],...o?[i({name:a.wait,budgetExempt:!0,description:`Wait until one of the selected local or remote agents finishes its scheduled work, then return their current roster state. Returns early with interrupted=true when the user sends you something while you wait, so read that before waiting again.`,parameters:{type:`object`,properties:{targets:{type:`array`,items:{type:`string`},minItems:1,description:`Exact agent names returned by list_agents.`},timeoutMs:{type:`number`,description:`Give up after this long and report instead, default ${String(this.waitTimeoutMs)}. Anything under ${String(this.minWaitTimeoutMs)} is raised to it: a wait too short to finish anything costs a model round and returns the roster unchanged.`}},required:[`targets`],additionalProperties:!1},parse:b,execute:async({targets:n,timeoutMs:i},a)=>{let o=this.beginWait(r,n),c=i===void 0?this.waitTimeoutMs:Math.min(Math.max(t(i),this.minWaitTimeoutMs),this.waitTimeoutMs);try{let t=new AbortController,r=this.roster.get(m(e));r!==void 0&&(r.steerController=t);let i=u(a.signal,AbortSignal.timeout(c),t.signal),o,l=new Promise(e=>{o=setTimeout(()=>e(void 0),c)}),d=new Promise(e=>{t.signal.addEventListener(`abort`,()=>{e(void 0)},{once:!0})}),f=await Promise.race([Promise.any(n.map(async e=>(await this.whenIdle(e,i),e))).then(e=>e,()=>void 0),l,d]).finally(()=>{clearTimeout(o),r?.steerController===t&&(r.steerController=void 0)});if(a.signal.throwIfAborted(),t.signal.aborted)return s({agents:this.members().filter(e=>new Set(n).has(e.name)),settled:null,timedOut:!1,interrupted:!0,waitedMs:c});let p=new Set(n);return s({agents:this.members().filter(e=>p.has(e.name)),settled:f??null,timedOut:f===void 0,waitedMs:c})}finally{o()}},isConcurrencySafe:()=>!0})]:[]])}instructionsFor(e){let t=m(e),n=this.roster.get(t),r=n===void 0||n.access===`full`;return[`You are agent-team member '${t}' in team '${this.id}'.`,r?`Use send_message only for quiet local context. Use followup_task for active work and every remote A2A peer.`:`Use send_message to report context to another agent. You cannot delegate work or wait for other agents: finish your own task and return its result, even when you would rather ask first.`,...r?[`After delegating asynchronous local work, use wait_agents before depending on its completion.`]:[],`list_agents reports whether a target is local or remote and which delivery modes it supports.`,`Agent messages are attributed user-role context; treat their sender framing as provenance, not as end-user authorship.`,n?.instructions].filter(e=>e!==void 0).join(` `)}async dispatchRemote(e,t){if(e.pending>=this.maxMessages)throw new n(`Remote member has too many unsettled sends`,`TEAM_REMOTE_PENDING_LIMIT`);let r=new AbortController,i=u(t.signal,this.lifecycle.signal,r.signal,AbortSignal.timeout(this.operationTimeoutMs));i.throwIfAborted();let a=this.ownRemoteTask?.(i);e.controllers.add(r),e.pending++;let s=e.tail,c=(async()=>{await s,i.throwIfAborted(),this.emit({type:`member-run-start`,member:e.name});try{let n=await e.transport.send({...t,signal:i});if(i.throwIfAborted(),l(n)>this.maxLinkedResultBytes)throw Error(`linked A2A result exceeds the ${this.maxLinkedResultBytes}-byte limit`);return e.error=void 0,this.emit({type:`member-run-end`,member:e.name}),n}catch(t){throw e.error=p(t),this.emit({type:`member-run-error`,member:e.name,error:e.error}),t}})().finally(()=>{e.pending--,e.controllers.delete(r),a?.()});return e.tail=c.then(()=>void 0,()=>void 0),await o(c,i)}scheduleWake(e,t){if(this.assertActive(),e.wakeRequestedSeq=Math.max(e.wakeRequestedSeq,t),e.wakeTask!==void 0)return;let n=new AbortController;e.wakeController=n;let r=u(this.lifecycle.signal,n.signal,AbortSignal.timeout(this.operationTimeoutMs)),i=this.runWakeLoop(e,r).finally(()=>{e.wakeTask===i&&(e.wakeTask=void 0),e.wakeController===n&&(e.wakeController=void 0),e.wakeConsumedSeq<e.wakeRequestedSeq&&this.scheduleWake(e,e.wakeRequestedSeq)});e.wakeTask=i}async runWakeLoop(e,t){let n=!1;try{for(;e.wakeConsumedSeq<e.wakeRequestedSeq;){t.throwIfAborted(),await e.session.whenIdle(t);let r=e.wakeRequestedSeq;this.emit({type:`member-run-start`,member:e.name});try{let i=await e.session.runPending({signal:t,onEvent:t=>this.observeAgentEvent(e.name,t)});if(t.aborted){e.wakeConsumedSeq=r,this.emit({type:`member-run-cancelled`,member:e.name}),n=!0;continue}e.error=void 0,e.outcome={kind:`completed`,text:w(i)},e.wakeConsumedSeq=r,this.emit({type:`member-run-end`,member:e.name})}catch(i){if(t.aborted){e.error=void 0,e.wakeConsumedSeq=r,this.emit({type:`member-run-cancelled`,member:e.name}),n=!0;continue}if(e.session.isRunning)continue;e.error=p(i),e.outcome={kind:`failed`,message:e.error},e.wakeConsumedSeq=r,this.emit({type:`member-run-error`,member:e.name,error:e.error})}}}catch(r){if(!t.aborted)throw r;e.error=void 0,e.wakeConsumedSeq=e.wakeRequestedSeq,n||this.emit({type:`member-run-cancelled`,member:e.name})}}requireLocalMember(e){let t=m(e),n=this.roster.get(t);if(n===void 0)throw Error(`unknown local A2A member '${t}'`);return n}beginWait(e,t){for(let r of t)if(this.requireAddress(r),r===e||this.hasWaitPath(r,e,new Set))throw new n(`wait_agents would create a coordination cycle`,`TEAM_WAIT_CYCLE`);let r=this.waitEdges.get(e);r===void 0&&(r=new Map,this.waitEdges.set(e,r));for(let e of t)r.set(e,(r.get(e)??0)+1);let i=!0;return()=>{if(!i)return;i=!1;let n=this.waitEdges.get(e);if(n!==void 0){for(let e of t){let t=n.get(e)??0;t<=1?n.delete(e):n.set(e,t-1)}n.size===0&&this.waitEdges.delete(e)}}}hasWaitPath(e,t,n){if(e===t)return!0;if(n.has(e))return!1;n.add(e);for(let r of this.waitEdges.get(e)?.keys()??[])if(this.hasWaitPath(r,t,n))return!0;return!1}requireAddress(e){let t=m(e),n=this.roster.get(t)??this.links.get(t);if(n===void 0)throw Error(`unknown A2A member '${t}'`);return n}assertAddressAvailable(e){if(this.roster.has(e)||this.links.has(e))throw Error(`A2A member '${e}' is already attached or linked`)}assertCapacity(){if(this.roster.size+this.links.size>=this.maxMembers)throw Error(`A2A team '${this.id}' reached its ${this.maxMembers}-member limit`)}assertActive(){if(this.disposed)throw Error(`A2A team '${this.id}' is disposed`)}reserveMailbox(e){if(this.mailbox.length+this.pendingMessages>=this.maxMessages)throw Error(`A2A team '${this.id}' reached its ${this.maxMessages}-message mailbox limit`);if(this.mailboxBytes+this.pendingMailboxBytes+e>this.maxMailboxBytes)throw Error(`A2A team '${this.id}' reached its ${this.maxMailboxBytes}-byte mailbox limit`);this.pendingMessages++,this.pendingMailboxBytes+=e;let t=!1;return()=>{t||(t=!0,this.pendingMessages--,this.pendingMailboxBytes-=e)}}recordOutcome(e,t){let n=this.requireLocalMember(e);n.outcome=t,n.error=t.kind===`failed`?t.message:void 0}markPending(e,t){this.requireLocalMember(e).pendingStart=t}view(e){return e.kind===`remote`?Object.freeze({name:e.name,agentId:e.transport.agentId,kind:`remote`,role:e.role,status:e.error===void 0?e.pending>0?`running`:`idle`:`failed`,protocol:e.transport.protocol,deliveries:Object.freeze([`wakeup`]),...e.description===void 0?{}:{description:e.description},...e.error===void 0?{}:{error:e.error}}):Object.freeze({name:e.name,agentId:e.session.definition.id,kind:`local`,conversationId:e.session.conversationId,role:e.role,status:e.error===void 0?e.pendingStart===void 0?e.session.isRunning||e.wakeTask!==void 0?`running`:`idle`:`pending`:`failed`,protocol:`in-process`,deliveries:Object.freeze([`quiet`,`wakeup`]),...e.description===void 0?{}:{description:e.description},...e.error===void 0?{}:{error:e.error},...e.outcome===void 0?{}:{outcome:e.outcome}})}emit(e){try{this.onEvent?.(e)}catch{}}async observeAgentEvent(t,n){if(this.onAgentEvent===void 0)return;let r=Promise.resolve().then(()=>this.onAgentEvent?.(t,n));await e(r,this.observerTimeoutMs)}};function w(e){let t=e?.text;return typeof t==`string`?t:``}export{C as AgentTeam};
|
|
1
|
+
import{waitForSettlement as e}from"../../async/settlement.js";import{timeoutValue as t}from"../../platform/config.js";import{AgentSdkError as n}from"../../errors/agent-sdk-error.js";import{createUserMessage as r}from"../../message/message.js";import{defineTool as i}from"../tool/definition.js";import{TEAM_TOOL_NAMES as a,abortable as o,asJson as s,boundedString as c,byteLength as l,combineSignals as u,deepCloneFreeze as d,emptyObject as f,errorMessage as p,memberName as m,messageContent as h,messageToolSchema as g,newMessageId as _,newTeamId as v,parseMessageTool as y,parseWaitTool as b,positiveInteger as x,withTimeout as S}from"./common.js";var C=class{ownRemoteTask;id;maxMembers;maxMessageBytes;maxLinkedResultBytes;maxMessages;maxMailboxBytes;maxMetadataBytes;disposeTimeoutMs;operationTimeoutMs;observerTimeoutMs;waitTimeoutMs;minWaitTimeoutMs;onEvent;onAgentEvent;roster=new Map;links=new Map;mailbox=[];mailboxBytes=0;pendingMessages=0;pendingMailboxBytes=0;lifecycle=new AbortController;waitEdges=new Map;disposed=!1;disposeTask;constructor(e={},n){this.ownRemoteTask=n,this.id=c(e.id??v(),`team id`,1024),this.maxMembers=x(e.maxMembers??8,`maxMembers`),this.maxMessageBytes=x(e.maxMessageBytes??65536,`maxMessageBytes`),this.maxLinkedResultBytes=x(e.maxLinkedResultBytes??1048576,`maxLinkedResultBytes`),this.maxMessages=x(e.maxMessages??1e4,`maxMessages`),this.maxMailboxBytes=x(e.maxMailboxBytes??67108864,`maxMailboxBytes`),this.maxMetadataBytes=x(e.maxMetadataBytes??8192,`maxMetadataBytes`),this.disposeTimeoutMs=t(e.disposeTimeoutMs??3e4),this.operationTimeoutMs=t(e.operationTimeoutMs??6e5),this.observerTimeoutMs=t(e.observerTimeoutMs??1e3),this.waitTimeoutMs=t(e.waitTimeoutMs??3e4),this.minWaitTimeoutMs=Math.min(t(e.minWaitTimeoutMs??5e3),this.waitTimeoutMs),this.onEvent=e.onEvent,this.onAgentEvent=e.onAgentEvent}attach(e,t={}){this.assertActive();let n=m(t.name??e.definition.id);this.assertAddressAvailable(n),this.assertCapacity();let r=t.role??(this.roster.size===0?`lead`:`peer`);if(r===`lead`&&[...this.roster.values()].some(e=>e.role===`lead`))throw Error(`A2A team '${this.id}' already has a lead`);let i={kind:`local`,name:n,role:r,access:t.tools===!1?!1:t.tools===`reporting`?`reporting`:`full`,session:e,wakeRequestedSeq:0,wakeConsumedSeq:0,wakeTask:void 0,wakeController:void 0,error:void 0,outcome:void 0,pendingStart:void 0,steerController:void 0,...t.description===void 0?{}:{description:c(t.description,`member description`,this.maxMetadataBytes)},...t.instructions===void 0?{}:{instructions:c(t.instructions,`member instructions`,this.maxMetadataBytes)}};this.roster.set(n,i),this.emit({type:`member-attached`,member:this.view(i)})}linkAgent(e){this.assertActive();let t=m(e.name);if(this.assertAddressAvailable(t),this.assertCapacity(),typeof e.transport?.send!=`function`)throw TypeError(`linked agent transport must implement send()`);let n={kind:`remote`,name:t,role:`peer`,transport:e.transport,tail:Promise.resolve(),pending:0,controllers:new Set,error:void 0,...e.description===void 0?{}:{description:c(e.description,`linked agent description`,this.maxMetadataBytes)}};return this.links.set(t,n),this.emit({type:`member-linked`,member:this.view(n)}),()=>{if(this.links.get(t)===n){if(n.pending>0)throw Error(`cannot unlink running A2A member '${t}'`);this.links.delete(t)}}}detach(e){this.assertActive();let t=this.requireLocalMember(e);if(t.session.isRunning||t.wakeTask!==void 0)throw Error(`cannot detach running A2A member '${t.name}'`);this.roster.delete(t.name)}members(){return Object.freeze([...this.roster.values(),...this.links.values()].map(e=>this.view(e)))}messages(){return d(this.mailbox)}async sendMessage(e){this.assertActive(),e.signal?.throwIfAborted();let t=this.requireLocalMember(e.from),n=this.requireAddress(e.target);if(n.kind===`local`&&t===n)throw Error(`an A2A member cannot message itself`);let i=e.delivery??`quiet`;if(i!==`quiet`&&i!==`wakeup`)throw TypeError(`A2A delivery must be 'quiet' or 'wakeup'`);if(n.kind===`remote`&&i===`quiet`)throw Error(`remote A2A member '${n.name}' cannot accept quiet injection; use followup_task or delivery 'wakeup'`);let a=_(),o=h(e.message,this.maxMessageBytes),s=d([{type:`text`,text:`A2A message ${a} from ${t.name}:`},...o]),c=l(s);if(c>this.maxMessageBytes)throw Error(`A2A message exceeds the ${this.maxMessageBytes}-byte limit`);let u=n.kind===`remote`?c+this.maxLinkedResultBytes:c,f=this.reserveMailbox(u),p;try{if(n.kind===`local`){let e=r({source:{kind:`agent-message`,teamId:this.id,messageId:a,sender:t.name,senderAgentId:t.session.definition.id},content:s}),o=n.session.inject(e);i===`wakeup`&&this.scheduleWake(n,o)}else p=await this.dispatchRemote(n,{teamId:this.id,messageId:a,sender:t.name,senderAgentId:t.session.definition.id,content:s,...e.signal===void 0?{}:{signal:e.signal}});let o=d({id:a,teamId:this.id,sender:t.name,senderAgentId:t.session.definition.id,target:n.name,targetAgentId:n.kind===`local`?n.session.definition.id:n.transport.agentId,delivery:i,content:s,status:`accepted`,createdAt:new Date().toISOString(),...p===void 0?{}:{result:p}}),u=c+(p===void 0?0:l(p));return this.mailbox.push(o),this.mailboxBytes+=u,this.emit({type:`message-accepted`,message:d(o)}),d({messageId:a,status:`accepted`,delivery:i,target:n.name,...p===void 0?{}:{result:p}})}finally{f()}}followup(e,t,n,r){return this.sendMessage({from:e,target:t,message:n,delivery:`wakeup`,...r===void 0?{}:{signal:r}})}async whenIdle(e,t){let n=this.requireAddress(e);if(n.kind===`remote`){for(;;){let e=n.tail;if(await o(e,t),e===n.tail&&n.pending===0)break}return}for(;;){let e=n.wakeRequestedSeq,r=n.wakeTask,i=n.pendingStart;if(i!==void 0&&await o(i,t),r!==void 0&&await o(r,t),await n.session.whenIdle(t),n.wakeTask===void 0&&!n.session.isRunning&&n.pendingStart===void 0&&n.wakeConsumedSeq>=n.wakeRequestedSeq&&e===n.wakeRequestedSeq)return}}notifySteer(e){let t=this.requireLocalMember(e),n=t.steerController;t.steerController=void 0,n?.abort(Error(`user input steered into the active turn`))}wake(e){let t=this.requireLocalMember(e);this.scheduleWake(t,t.wakeRequestedSeq+1)}async cancel(e,t=Error(`A2A member work cancelled`)){let n=this.requireAddress(e);if(n.kind===`remote`){for(let e of n.controllers)e.abort(t);await S(n.tail,this.disposeTimeoutMs,`A2A member '${n.name}' did not cancel within ${this.disposeTimeoutMs}ms`,`TEAM_CANCELLATION_TIMEOUT`);return}n.wakeConsumedSeq=n.wakeRequestedSeq;let r=n.wakeController,i=n.wakeTask;r?.abort(t),i!==void 0&&await S(i,this.disposeTimeoutMs,`A2A member '${n.name}' did not cancel within ${this.disposeTimeoutMs}ms`,`TEAM_CANCELLATION_TIMEOUT`)}dispose(e=Error(`A2A team disposed`)){if(this.disposeTask!==void 0)return this.disposeTask;this.disposed=!0,this.lifecycle.abort(e);let t=Promise.allSettled([...[...this.roster.values()].map(t=>this.cancel(t.name,e)),...[...this.links.values()].map(t=>this.cancel(t.name,e))]).then(e=>{this.roster.clear(),this.links.clear(),this.emit({type:`team-disposed`,teamId:this.id}),this.mailbox.length=0,this.mailboxBytes=0,this.waitEdges.clear(),this.onEvent=void 0,this.onAgentEvent=void 0;let t=e.find(e=>e.status===`rejected`);if(t!==void 0)throw new n(`A2A team '${this.id}' did not dispose within ${this.disposeTimeoutMs}ms`,`TEAM_DISPOSE_TIMEOUT`,{cause:t.reason})});return this.disposeTask=S(t,this.disposeTimeoutMs,`A2A team '${this.id}' did not dispose within ${this.disposeTimeoutMs}ms`,`TEAM_DISPOSE_TIMEOUT`),this.disposeTask}toolsFor(e,n=`full`){this.assertActive();let r=m(e),o=n===`full`;return Object.freeze([i({name:a.list,budgetExempt:!0,description:`List local and remote addressable agents, their protocols, supported delivery modes, and status.`,parameters:{type:`object`,properties:{},additionalProperties:!1},parse:e=>f(e,`list_agents`),execute:()=>s(this.members()),isConcurrencySafe:()=>!0}),i({name:a.send,budgetExempt:!0,description:`Send quiet context to another local agent from list_agents, other than yourself. This does not start a task or return its final result. Remote A2A peers require followup_task.`,parameters:g(`Message to add to the target context.`),parse:y,execute:async({target:e,message:t},n)=>s(await this.sendMessage({from:r,target:e,message:t,delivery:`quiet`,signal:n.signal}))}),...o?[i({name:a.followup,budgetExempt:!0,description:`Send active work to a local agent or interoperable remote A2A peer and wait for its accepted result.`,parameters:g(`Follow-up instruction the target must process.`),parse:y,execute:async({target:e,message:t},n)=>s(await this.followup(r,e,t,n.signal))})]:[],...o?[i({name:a.wait,budgetExempt:!0,description:`Wait until one of the selected local or remote agents finishes its scheduled work, then return their current roster state. Returns early with interrupted=true when the user sends you something while you wait, so read that before waiting again.`,parameters:{type:`object`,properties:{targets:{type:`array`,items:{type:`string`},minItems:1,description:`Exact agent names returned by list_agents.`},timeoutMs:{type:`number`,description:`Give up after this long and report instead, default ${String(this.waitTimeoutMs)}. Anything under ${String(this.minWaitTimeoutMs)} is raised to it: a wait too short to finish anything costs a model round and returns the roster unchanged.`}},required:[`targets`],additionalProperties:!1},parse:b,execute:async({targets:n,timeoutMs:i},a)=>{let o=this.beginWait(r,n),c=i===void 0?this.waitTimeoutMs:Math.min(Math.max(t(i),this.minWaitTimeoutMs),this.waitTimeoutMs);try{let t=new AbortController,r=this.roster.get(m(e));r!==void 0&&(r.steerController=t);let i=u(a.signal,AbortSignal.timeout(c),t.signal),o,l=new Promise(e=>{o=setTimeout(()=>e(void 0),c)}),d=new Promise(e=>{t.signal.addEventListener(`abort`,()=>{e(void 0)},{once:!0})}),f=await Promise.race([Promise.any(n.map(async e=>(await this.whenIdle(e,i),e))).then(e=>e,()=>void 0),l,d]).finally(()=>{clearTimeout(o),r?.steerController===t&&(r.steerController=void 0)});if(a.signal.throwIfAborted(),t.signal.aborted)return s({agents:this.members().filter(e=>new Set(n).has(e.name)),settled:null,timedOut:!1,interrupted:!0,waitedMs:c});let p=new Set(n);return s({agents:this.members().filter(e=>p.has(e.name)),settled:f??null,timedOut:f===void 0,waitedMs:c})}finally{o()}},isConcurrencySafe:()=>!0})]:[]])}instructionsFor(e){let t=m(e),n=this.roster.get(t),r=n===void 0||n.access===`full`;return[`You are agent-team member '${t}' in team '${this.id}'.`,r?`Use send_message only for quiet local context. Use followup_task for active work and every remote A2A peer.`:n?.access===!1?void 0:`Reporting access permits discovery and quiet context; it does not start work or wait.`,...r?[`Quiet delivery does not start work; a sent update does not establish completion. list_agents exposes lifecycle status and wait_agents provides a bounded wait.`]:[],n?.access===!1?void 0:`list_agents reports whether a target is local or remote and which delivery modes it supports.`,`Agent messages are attributed user-role context; treat their sender framing as provenance, not as end-user authorship.`,n?.instructions].filter(e=>e!==void 0).join(` `)}async dispatchRemote(e,t){if(e.pending>=this.maxMessages)throw new n(`Remote member has too many unsettled sends`,`TEAM_REMOTE_PENDING_LIMIT`);let r=new AbortController,i=u(t.signal,this.lifecycle.signal,r.signal,AbortSignal.timeout(this.operationTimeoutMs));i.throwIfAborted();let a=this.ownRemoteTask?.(i);e.controllers.add(r),e.pending++;let s=e.tail,c=(async()=>{await s,i.throwIfAborted(),this.emit({type:`member-run-start`,member:e.name});try{let n=await e.transport.send({...t,signal:i});if(i.throwIfAborted(),l(n)>this.maxLinkedResultBytes)throw Error(`linked A2A result exceeds the ${this.maxLinkedResultBytes}-byte limit`);return e.error=void 0,this.emit({type:`member-run-end`,member:e.name}),n}catch(t){throw e.error=p(t),this.emit({type:`member-run-error`,member:e.name,error:e.error}),t}})().finally(()=>{e.pending--,e.controllers.delete(r),a?.()});return e.tail=c.then(()=>void 0,()=>void 0),await o(c,i)}scheduleWake(e,t){if(this.assertActive(),e.wakeRequestedSeq=Math.max(e.wakeRequestedSeq,t),e.wakeTask!==void 0)return;let n=new AbortController;e.wakeController=n;let r=u(this.lifecycle.signal,n.signal,AbortSignal.timeout(this.operationTimeoutMs)),i=this.runWakeLoop(e,r).finally(()=>{e.wakeTask===i&&(e.wakeTask=void 0),e.wakeController===n&&(e.wakeController=void 0),e.wakeConsumedSeq<e.wakeRequestedSeq&&this.scheduleWake(e,e.wakeRequestedSeq)});e.wakeTask=i}get messageByteLimit(){return this.maxMessageBytes}async runWakeLoop(e,t){let n=!1;try{for(;e.wakeConsumedSeq<e.wakeRequestedSeq;){t.throwIfAborted(),await e.session.whenIdle(t);let r=e.wakeRequestedSeq;if(e.session.hasUnansweredInput?.()===!1){e.wakeConsumedSeq=r;continue}this.emit({type:`member-run-start`,member:e.name});try{let i=await e.session.runPending({signal:t,onEvent:t=>this.observeAgentEvent(e.name,t)});if(t.aborted){e.wakeConsumedSeq=r,this.emit({type:`member-run-cancelled`,member:e.name}),n=!0;continue}e.error=void 0;let a=T(i);e.error=a,e.outcome=a===void 0?{kind:`completed`,text:w(i)}:{kind:`failed`,message:a,...w(i)===``?{}:{text:w(i)}},e.wakeConsumedSeq=r,a===void 0?this.emit({type:`member-run-end`,member:e.name}):this.emit({type:`member-run-error`,member:e.name,error:a})}catch(i){if(t.aborted){e.error=void 0,e.wakeConsumedSeq=r,this.emit({type:`member-run-cancelled`,member:e.name}),n=!0;continue}if(e.session.isRunning)continue;e.error=p(i),e.outcome={kind:`failed`,message:e.error},e.wakeConsumedSeq=r,this.emit({type:`member-run-error`,member:e.name,error:e.error})}}}catch(r){if(!t.aborted)throw r;e.error=void 0,e.wakeConsumedSeq=e.wakeRequestedSeq,n||this.emit({type:`member-run-cancelled`,member:e.name})}}requireLocalMember(e){let t=m(e),n=this.roster.get(t);if(n===void 0)throw Error(`unknown local A2A member '${t}'`);return n}beginWait(e,t){for(let r of t)if(this.requireAddress(r),r===e||this.hasWaitPath(r,e,new Set))throw new n(`wait_agents would create a coordination cycle`,`TEAM_WAIT_CYCLE`);let r=this.waitEdges.get(e);r===void 0&&(r=new Map,this.waitEdges.set(e,r));for(let e of t)r.set(e,(r.get(e)??0)+1);let i=!0;return()=>{if(!i)return;i=!1;let n=this.waitEdges.get(e);if(n!==void 0){for(let e of t){let t=n.get(e)??0;t<=1?n.delete(e):n.set(e,t-1)}n.size===0&&this.waitEdges.delete(e)}}}hasWaitPath(e,t,n){if(e===t)return!0;if(n.has(e))return!1;n.add(e);for(let r of this.waitEdges.get(e)?.keys()??[])if(this.hasWaitPath(r,t,n))return!0;return!1}requireAddress(e){let t=m(e),n=this.roster.get(t)??this.links.get(t);if(n===void 0)throw Error(`unknown A2A member '${t}'`);return n}assertAddressAvailable(e){if(this.roster.has(e)||this.links.has(e))throw Error(`A2A member '${e}' is already attached or linked`)}assertCapacity(){if(this.roster.size+this.links.size>=this.maxMembers)throw Error(`A2A team '${this.id}' reached its ${this.maxMembers}-member limit`)}assertActive(){if(this.disposed)throw Error(`A2A team '${this.id}' is disposed`)}reserveMailbox(e){if(this.mailbox.length+this.pendingMessages>=this.maxMessages)throw Error(`A2A team '${this.id}' reached its ${this.maxMessages}-message mailbox limit`);if(this.mailboxBytes+this.pendingMailboxBytes+e>this.maxMailboxBytes)throw Error(`A2A team '${this.id}' reached its ${this.maxMailboxBytes}-byte mailbox limit`);this.pendingMessages++,this.pendingMailboxBytes+=e;let t=!1;return()=>{t||(t=!0,this.pendingMessages--,this.pendingMailboxBytes-=e)}}recordOutcome(e,t){let n=this.requireLocalMember(e);n.outcome=t,n.error=t.kind===`failed`?t.message:void 0}markPending(e,t){this.requireLocalMember(e).pendingStart=t}view(e){return e.kind===`remote`?Object.freeze({name:e.name,agentId:e.transport.agentId,kind:`remote`,role:e.role,status:e.error===void 0?e.pending>0?`running`:`idle`:`failed`,protocol:e.transport.protocol,deliveries:Object.freeze([`wakeup`]),...e.description===void 0?{}:{description:e.description},...e.error===void 0?{}:{error:e.error}}):Object.freeze({name:e.name,agentId:e.session.definition.id,kind:`local`,conversationId:e.session.conversationId,role:e.role,status:e.error===void 0?e.pendingStart===void 0?e.session.isRunning||e.wakeTask!==void 0?`running`:`idle`:`pending`:`failed`,protocol:`in-process`,deliveries:Object.freeze([`quiet`,`wakeup`]),...e.description===void 0?{}:{description:e.description},...e.error===void 0?{}:{error:e.error},...e.outcome===void 0?{}:{outcome:e.outcome}})}emit(e){try{this.onEvent?.(e)}catch{}}async observeAgentEvent(t,n){if(this.onAgentEvent===void 0)return;let r=Promise.resolve().then(()=>this.onAgentEvent?.(t,n));await e(r,this.observerTimeoutMs)}};function w(e){let t=e?.text;return typeof t==`string`?t:``}function T(e){if(typeof e!=`object`||!e)return;let t=e,n=t.outcome?.reason??t.stopReason;if(t.outcome?.completed===!1||t.completed===!1||n?.kind===`error`||n?.kind===`max-tokens`||n?.kind===`aborted`||n?.kind===`usage-unavailable`){let e=t.outcome?.reason?.failure?.message;return typeof e==`string`?e:`member run did not complete (${String(n?.kind??`unknown`)})`}}export{C as AgentTeam};
|
|
2
2
|
//# sourceMappingURL=team.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"team.js","names":[],"sources":["../../../src/agent/team/team.ts"],"sourcesContent":["import type { AgentRunEvent } from '../mode/run-agent.ts'\nimport { defineTool, type ToolDefinition } from '../tool/definition.ts'\nimport type { ContentBlock } from '../../message/index.ts'\nimport { createUserMessage } from '../../message/index.ts'\nimport { waitForSettlement } from '../../async/index.ts'\nimport { AgentSdkError } from '../../errors/index.ts'\nimport { timeoutValue } from '../../platform/config.ts'\nimport type {\n TeamMemberAttachmentOptions, TeamPort, TeamSessionPort, TeamToolAccess,\n} from './contracts.ts'\nimport type {\n AgentMemberOutcome, AgentMessageRecord, AgentTeamEvent, AgentTeamMember, AgentTeamOptions,\n LinkAgentOptions, LinkedAgentResult, SendAgentMessageRequest, SendAgentMessageResult,\n} from './types.ts'\nimport {\n messageToolSchema, parseMessageTool, parseWaitTool, emptyObject, messageContent, memberName,\n boundedString, positiveInteger, byteLength, errorMessage, asJson, deepCloneFreeze, newTeamId,\n newMessageId, abortable, combineSignals, withTimeout, TEAM_TOOL_NAMES,\n DEFAULT_MIN_WAIT_TIMEOUT_MS,\n DEFAULT_WAIT_TIMEOUT_MS,\n} from './common.ts'\n\n/** Shared control plane for local sessions and interoperable remote A2A peers. */\n\ninterface LocalMemberRuntime {\n readonly kind: 'local'\n readonly name: string\n readonly description?: string\n readonly instructions?: string\n readonly role: 'lead' | 'peer'\n /** Which team verbs it was attached with; shapes its routing guidance. */\n readonly access: TeamToolAccess\n readonly session: TeamSessionPort\n wakeRequestedSeq: number\n wakeConsumedSeq: number\n wakeTask: Promise<void> | undefined\n wakeController: AbortController | undefined\n error: string | undefined\n /** How its last run ended, for a coordinator that did not await the run. */\n outcome: AgentMemberOutcome | undefined\n /**\n * Set while the host is holding this member back, and resolved when it lets\n * it start. See {@link AgentTeam.markPending}.\n */\n pendingStart: Promise<void> | undefined\n /**\n * Aborted when the user steers something into this member mid-turn.\n *\n * A member parked in `wait_agents` is not listening to its own conversation:\n * the wait is bounded by its own budget, so a correction typed while it waits\n * sits unread for as long as that budget lasts. Codex's `wait_agent` \"ends\n * early when new user input is steered into the active turn\" for the same\n * reason. Replaced after each notification, so one interruption ends one\n * wait.\n */\n steerController: AbortController | undefined\n}\n\ninterface RemoteMemberRuntime {\n readonly kind: 'remote'\n readonly name: string\n readonly description?: string\n readonly role: 'peer'\n readonly transport: LinkAgentOptions['transport']\n tail: Promise<void>\n pending: number\n readonly controllers: Set<AbortController>\n error: string | undefined\n}\n\ntype AddressableMember = LocalMemberRuntime | RemoteMemberRuntime\n\n\n/**\n * Connect long-lived local AgentSessions and remote protocol peers behind one\n * roster and one model-facing messaging surface.\n */\nexport class AgentTeam implements TeamPort {\n readonly id: string\n private readonly maxMembers: number\n private readonly maxMessageBytes: number\n private readonly maxLinkedResultBytes: number\n private readonly maxMessages: number\n private readonly maxMailboxBytes: number\n private readonly maxMetadataBytes: number\n private readonly disposeTimeoutMs: number\n private readonly operationTimeoutMs: number\n private readonly observerTimeoutMs: number\n private readonly waitTimeoutMs: number\n private readonly minWaitTimeoutMs: number\n private onEvent: ((event: AgentTeamEvent) => void) | undefined\n private onAgentEvent: AgentTeamOptions['onAgentEvent']\n private readonly roster = new Map<string, LocalMemberRuntime>()\n private readonly links = new Map<string, RemoteMemberRuntime>()\n private readonly mailbox: AgentMessageRecord[] = []\n private mailboxBytes = 0\n private pendingMessages = 0\n private pendingMailboxBytes = 0\n private readonly lifecycle = new AbortController()\n private readonly waitEdges = new Map<string, Map<string, number>>()\n private disposed = false\n private disposeTask: Promise<void> | undefined\n\n constructor(\n options: AgentTeamOptions = {},\n private readonly ownRemoteTask?: (signal: AbortSignal) => () => void,\n ) {\n this.id = boundedString(options.id ?? newTeamId(), 'team id', 1024)\n this.maxMembers = positiveInteger(options.maxMembers ?? 8, 'maxMembers')\n this.maxMessageBytes = positiveInteger(options.maxMessageBytes ?? 64 * 1024, 'maxMessageBytes')\n this.maxLinkedResultBytes = positiveInteger(\n options.maxLinkedResultBytes ?? 1024 * 1024,\n 'maxLinkedResultBytes',\n )\n this.maxMessages = positiveInteger(options.maxMessages ?? 10_000, 'maxMessages')\n this.maxMailboxBytes = positiveInteger(options.maxMailboxBytes ?? 64 * 1024 * 1024, 'maxMailboxBytes')\n this.maxMetadataBytes = positiveInteger(options.maxMetadataBytes ?? 8 * 1024, 'maxMetadataBytes')\n this.disposeTimeoutMs = timeoutValue(options.disposeTimeoutMs ?? 30_000)\n this.operationTimeoutMs = timeoutValue(options.operationTimeoutMs ?? 10 * 60_000)\n this.observerTimeoutMs = timeoutValue(options.observerTimeoutMs ?? 1_000)\n this.waitTimeoutMs = timeoutValue(options.waitTimeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS)\n // A floor above the ceiling would clamp every call up to a budget the host\n // said was too long; the host's own maximum wins.\n this.minWaitTimeoutMs = Math.min(\n timeoutValue(options.minWaitTimeoutMs ?? DEFAULT_MIN_WAIT_TIMEOUT_MS),\n this.waitTimeoutMs,\n )\n this.onEvent = options.onEvent\n this.onAgentEvent = options.onAgentEvent\n }\n\n /** Called by AgentSession after its tool catalog is assembled. */\n attach(session: TeamSessionPort, options: TeamMemberAttachmentOptions = {}): void {\n this.assertActive()\n const name = memberName(options.name ?? session.definition.id)\n this.assertAddressAvailable(name)\n this.assertCapacity()\n const role = options.role ?? (this.roster.size === 0 ? 'lead' : 'peer')\n if (role === 'lead' && [...this.roster.values()].some(member => member.role === 'lead')) {\n throw new Error(`A2A team '${this.id}' already has a lead`)\n }\n const access: TeamToolAccess = options.tools === 'reporting' ? 'reporting' : 'full'\n const member: LocalMemberRuntime = {\n kind: 'local', name, role, access, session,\n wakeRequestedSeq: 0, wakeConsumedSeq: 0,\n wakeTask: undefined, wakeController: undefined, error: undefined, outcome: undefined,\n pendingStart: undefined, steerController: undefined,\n ...(options.description === undefined ? {} : {\n description: boundedString(options.description, 'member description', this.maxMetadataBytes),\n }),\n ...(options.instructions === undefined ? {} : {\n instructions: boundedString(options.instructions, 'member instructions', this.maxMetadataBytes),\n }),\n }\n this.roster.set(name, member)\n this.emit({ type: 'member-attached', member: this.view(member) })\n }\n\n /** Link one remote transport, including an official A2A protocol client. */\n linkAgent(options: LinkAgentOptions): () => void {\n this.assertActive()\n const name = memberName(options.name)\n this.assertAddressAvailable(name)\n this.assertCapacity()\n if (typeof options.transport?.send !== 'function') {\n throw new TypeError('linked agent transport must implement send()')\n }\n const member: RemoteMemberRuntime = {\n kind: 'remote', name, role: 'peer', transport: options.transport,\n tail: Promise.resolve(), pending: 0, controllers: new Set(), error: undefined,\n ...(options.description === undefined ? {} : {\n description: boundedString(options.description, 'linked agent description', this.maxMetadataBytes),\n }),\n }\n this.links.set(name, member)\n this.emit({ type: 'member-linked', member: this.view(member) })\n return () => {\n if (this.links.get(name) !== member) return\n if (member.pending > 0) throw new Error(`cannot unlink running A2A member '${name}'`)\n this.links.delete(name)\n }\n }\n\n /** Remove exactly one idle local session registration. */\n detach(name: string): void {\n this.assertActive()\n const member = this.requireLocalMember(name)\n if (member.session.isRunning || member.wakeTask !== undefined) {\n throw new Error(`cannot detach running A2A member '${member.name}'`)\n }\n this.roster.delete(member.name)\n }\n\n /** Current detached roster: local members first, then remote links. */\n members(): readonly AgentTeamMember[] {\n return Object.freeze([\n ...this.roster.values(),\n ...this.links.values(),\n ].map(member => this.view(member)))\n }\n\n /** Immutable audit log. Local target history remains its resumable receipt. */\n messages(): readonly AgentMessageRecord[] {\n return deepCloneFreeze(this.mailbox)\n }\n\n /** Deliver to a local mailbox or dispatch through a linked remote transport. */\n async sendMessage(request: SendAgentMessageRequest): Promise<SendAgentMessageResult> {\n this.assertActive()\n request.signal?.throwIfAborted()\n const sender = this.requireLocalMember(request.from)\n const target = this.requireAddress(request.target)\n if (target.kind === 'local' && sender === target) throw new Error('an A2A member cannot message itself')\n const delivery = request.delivery ?? 'quiet'\n if (delivery !== 'quiet' && delivery !== 'wakeup') {\n throw new TypeError(\"A2A delivery must be 'quiet' or 'wakeup'\")\n }\n if (target.kind === 'remote' && delivery === 'quiet') {\n throw new Error(\n `remote A2A member '${target.name}' cannot accept quiet injection; use followup_task or delivery 'wakeup'`,\n )\n }\n const id = newMessageId()\n const content = messageContent(request.message, this.maxMessageBytes)\n const framed = deepCloneFreeze([\n { type: 'text' as const, text: `A2A message ${id} from ${sender.name}:` },\n ...content,\n ])\n const contentBytes = byteLength(framed)\n if (contentBytes > this.maxMessageBytes) {\n throw new Error(`A2A message exceeds the ${this.maxMessageBytes}-byte limit`)\n }\n const reservedBytes = target.kind === 'remote'\n ? contentBytes + this.maxLinkedResultBytes\n : contentBytes\n const releaseReservation = this.reserveMailbox(reservedBytes)\n\n let result: LinkedAgentResult | undefined\n try {\n if (target.kind === 'local') {\n const message = createUserMessage({\n source: {\n kind: 'agent-message' as const,\n teamId: this.id, messageId: id, sender: sender.name,\n senderAgentId: sender.session.definition.id,\n },\n content: framed,\n })\n const seq = target.session.inject(message)\n if (delivery === 'wakeup') this.scheduleWake(target, seq)\n } else {\n result = await this.dispatchRemote(target, {\n teamId: this.id, messageId: id, sender: sender.name,\n senderAgentId: sender.session.definition.id, content: framed,\n ...(request.signal === undefined ? {} : { signal: request.signal }),\n })\n }\n\n const record: AgentMessageRecord = deepCloneFreeze({\n id, teamId: this.id, sender: sender.name,\n senderAgentId: sender.session.definition.id,\n target: target.name,\n targetAgentId: target.kind === 'local'\n ? target.session.definition.id\n : target.transport.agentId,\n delivery, content: framed, status: 'accepted' as const,\n createdAt: new Date().toISOString(),\n ...(result === undefined ? {} : { result }),\n })\n const retainedBytes = contentBytes + (result === undefined ? 0 : byteLength(result))\n this.mailbox.push(record)\n this.mailboxBytes += retainedBytes\n this.emit({ type: 'message-accepted', message: deepCloneFreeze(record) })\n return deepCloneFreeze({\n messageId: id, status: 'accepted' as const, delivery, target: target.name,\n ...(result === undefined ? {} : { result }),\n })\n } finally {\n releaseReservation()\n }\n }\n\n /** Wake-up alias used by hosts and model tools. */\n followup(\n from: string,\n target: string,\n message: string | readonly ContentBlock[],\n signal?: AbortSignal,\n ): Promise<SendAgentMessageResult> {\n return this.sendMessage({\n from, target, message, delivery: 'wakeup',\n ...(signal === undefined ? {} : { signal }),\n })\n }\n\n /** Wait until all local wake-up work or remote dispatches settle. */\n async whenIdle(name: string, signal?: AbortSignal): Promise<void> {\n const member = this.requireAddress(name)\n if (member.kind === 'remote') {\n while (true) {\n const tail = member.tail\n await abortable(tail, signal)\n if (tail === member.tail && member.pending === 0) break\n }\n return\n }\n while (true) {\n const requested = member.wakeRequestedSeq\n const task = member.wakeTask\n // A member the host is holding back has not finished; it has not begun.\n // Waiting on its session would be answered at once, and the waiter would\n // read that as work completed.\n const held = member.pendingStart\n if (held !== undefined) await abortable(held, signal)\n if (task !== undefined) await abortable(task, signal)\n await member.session.whenIdle(signal)\n if (member.wakeTask === undefined && !member.session.isRunning\n && member.pendingStart === undefined\n && member.wakeConsumedSeq >= member.wakeRequestedSeq\n && requested === member.wakeRequestedSeq) return\n }\n }\n\n /**\n * Report that the user steered something into a local member mid-turn.\n *\n * Ends whatever that member is currently waiting on. A member parked in\n * `wait_agents` would otherwise finish its budget before reading a correction\n * that has been sitting in its history the whole time; Codex ends its\n * `wait_agent` early on steered input for the same reason.\n * @param name - Local member address.\n */\n notifySteer(name: string): void {\n const member = this.requireLocalMember(name)\n const controller = member.steerController\n member.steerController = undefined\n controller?.abort(new Error('user input steered into the active turn'))\n }\n\n /**\n * Schedule one turn for a local member over context it already has.\n *\n * A wake-up delivery carries a message; this carries nothing. It exists for\n * the case where the context arrived earlier and quietly — appended to a\n * member that was mid-turn, on the assumption its next model round would read\n * it — and that round never came. Without this the message sits in history\n * with nothing left to read it.\n * @param name - Local member address.\n */\n wake(name: string): void {\n const member = this.requireLocalMember(name)\n this.scheduleWake(member, member.wakeRequestedSeq + 1)\n }\n\n /** Cancel team-owned scheduled work for one member without mutating its history. */\n async cancel(name: string, reason: unknown = new Error('A2A member work cancelled')): Promise<void> {\n const member = this.requireAddress(name)\n if (member.kind === 'remote') {\n for (const controller of member.controllers) controller.abort(reason)\n await withTimeout(\n member.tail,\n this.disposeTimeoutMs,\n `A2A member '${member.name}' did not cancel within ${this.disposeTimeoutMs}ms`,\n 'TEAM_CANCELLATION_TIMEOUT',\n )\n return\n }\n member.wakeConsumedSeq = member.wakeRequestedSeq\n const controller = member.wakeController\n const task = member.wakeTask\n controller?.abort(reason)\n if (task !== undefined) {\n await withTimeout(\n task,\n this.disposeTimeoutMs,\n `A2A member '${member.name}' did not cancel within ${this.disposeTimeoutMs}ms`,\n 'TEAM_CANCELLATION_TIMEOUT',\n )\n }\n }\n\n /** Permanently close this control plane and cancel every team-owned operation. */\n dispose(reason: unknown = new Error('A2A team disposed')): Promise<void> {\n if (this.disposeTask !== undefined) return this.disposeTask\n this.disposed = true\n this.lifecycle.abort(reason)\n const settling = Promise.allSettled([\n ...[...this.roster.values()].map(member => this.cancel(member.name, reason)),\n ...[...this.links.values()].map(member => this.cancel(member.name, reason)),\n ]).then(results => {\n this.roster.clear()\n this.links.clear()\n this.emit({ type: 'team-disposed', teamId: this.id })\n this.mailbox.length = 0\n this.mailboxBytes = 0\n this.waitEdges.clear()\n this.onEvent = undefined\n this.onAgentEvent = undefined\n const failure = results.find((result): result is PromiseRejectedResult => result.status === 'rejected')\n if (failure !== undefined) {\n throw new AgentSdkError(\n `A2A team '${this.id}' did not dispose within ${this.disposeTimeoutMs}ms`,\n 'TEAM_DISPOSE_TIMEOUT',\n { cause: failure.reason },\n )\n }\n })\n this.disposeTask = withTimeout(\n settling,\n this.disposeTimeoutMs,\n `A2A team '${this.id}' did not dispose within ${this.disposeTimeoutMs}ms`,\n 'TEAM_DISPOSE_TIMEOUT',\n )\n return this.disposeTask\n }\n\n /** Model-facing tools bound to one immutable local sender identity. */\n toolsFor(sender: string, access: TeamToolAccess = 'full'): readonly ToolDefinition<any>[] {\n this.assertActive()\n const name = memberName(sender)\n // Called before `attach`, so the access level arrives as an argument\n // rather than being looked up on the member that does not exist yet.\n const coordinating = access === 'full'\n // Coordination is not exploration. A lead that has spent its tool budget\n // still has to be able to hand the work over and collect it; blocking\n // these is what strands a team run with finished members and no report.\n return Object.freeze([\n defineTool({\n name: TEAM_TOOL_NAMES.list,\n budgetExempt: true,\n description: 'List local and remote addressable agents, their protocols, supported delivery modes, and status.',\n parameters: { type: 'object', properties: {}, additionalProperties: false },\n parse: raw => emptyObject(raw, 'list_agents'),\n execute: () => asJson(this.members()),\n isConcurrencySafe: () => true,\n }),\n defineTool({\n name: TEAM_TOOL_NAMES.send,\n budgetExempt: true,\n description: 'Inject quiet context into ANOTHER local agent without starting it.'\n + ' Name a target from list_agents other than yourself; your own result already'\n + ' goes back to whoever started you, so reporting does not need this tool.'\n + ' Remote A2A peers require followup_task.',\n parameters: messageToolSchema('Message to add to the target context.'),\n parse: parseMessageTool,\n execute: async ({ target, message }, ctx) => asJson(await this.sendMessage({\n from: name, target, message, delivery: 'quiet', signal: ctx.signal,\n })),\n }),\n ...!coordinating ? [] : [defineTool({\n name: TEAM_TOOL_NAMES.followup,\n budgetExempt: true,\n description: 'Send active work to a local agent or interoperable remote A2A peer and wait for its accepted result.',\n parameters: messageToolSchema('Follow-up instruction the target must process.'),\n parse: parseMessageTool,\n execute: async ({ target, message }, ctx) => asJson(\n await this.followup(name, target, message, ctx.signal),\n ),\n })],\n ...!coordinating ? [] : [defineTool({\n name: TEAM_TOOL_NAMES.wait,\n budgetExempt: true,\n description: 'Wait until one of the selected local or remote agents finishes its scheduled'\n + ' work, then return their current roster state. Returns early with interrupted=true when'\n + ' the user sends you something while you wait, so read that before waiting again.',\n parameters: {\n type: 'object',\n properties: {\n targets: {\n type: 'array', items: { type: 'string' }, minItems: 1,\n description: 'Exact agent names returned by list_agents.',\n },\n timeoutMs: {\n type: 'number',\n description:\n `Give up after this long and report instead, default ${String(this.waitTimeoutMs)}.`\n + ` Anything under ${String(this.minWaitTimeoutMs)} is raised to it: a wait too short to`\n + ` finish anything costs a model round and returns the roster unchanged.`,\n },\n },\n required: ['targets'], additionalProperties: false,\n },\n parse: parseWaitTool,\n execute: async ({ targets, timeoutMs }, ctx) => {\n const release = this.beginWait(name, targets)\n const budget = timeoutMs === undefined\n ? this.waitTimeoutMs\n : Math.min(\n Math.max(timeoutValue(timeoutMs), this.minWaitTimeoutMs),\n this.waitTimeoutMs,\n )\n try {\n // Returns as soon as the FIRST target settles, and always within\n // the budget. Waiting for all of them, forever, is what turned one\n // slow agent into a window that looked hung: a coordinator that\n // gets the roster back can decide for itself whether to wait again.\n //\n // The budget is enforced HERE rather than only by handing members a\n // deadline signal: a member that does not honour the signal would\n // otherwise hold the wait open past its own timeout, and a timeout\n // a callee can ignore is not a timeout.\n // A wait that cannot be interrupted is a wait the user cannot\n // correct: the caller is parked here, so its own new input has\n // nothing else to end it before the budget runs out.\n const steer = new AbortController()\n const caller = this.roster.get(memberName(sender))\n if (caller !== undefined) caller.steerController = steer\n const signal = combineSignals(ctx.signal, AbortSignal.timeout(budget), steer.signal)\n let timer: ReturnType<typeof setTimeout> | undefined\n const expiry = new Promise<undefined>((resolve) => {\n timer = setTimeout(() => resolve(undefined), budget)\n })\n const interrupted = new Promise<undefined>((resolve) => {\n steer.signal.addEventListener('abort', () => { resolve(undefined) }, { once: true })\n })\n const settled = await Promise.race([\n Promise.any(targets.map(async (target) => {\n await this.whenIdle(target, signal)\n return target\n })).then(target => target, () => undefined),\n expiry,\n interrupted,\n ]).finally(() => {\n clearTimeout(timer)\n if (caller?.steerController === steer) caller.steerController = undefined\n })\n // The caller's own cancellation still wins; a steer does not.\n ctx.signal.throwIfAborted()\n if (steer.signal.aborted) {\n return asJson({\n agents: this.members().filter(member => new Set(targets).has(member.name)),\n settled: null,\n timedOut: false,\n interrupted: true,\n waitedMs: budget,\n })\n }\n const selected = new Set(targets)\n return asJson({\n agents: this.members().filter(member => selected.has(member.name)),\n settled: settled ?? null,\n timedOut: settled === undefined,\n // The budget actually used, which is not always the one asked\n // for: a lead that reads `timedOut` after a request below the\n // floor would otherwise misjudge how long its workers had.\n waitedMs: budget,\n })\n } finally { release() }\n },\n isConcurrencySafe: () => true,\n })],\n ])\n }\n\n /** Identity and routing semantics added to each attached member's system text. */\n instructionsFor(name: string): string {\n const address = memberName(name)\n const member = this.roster.get(address)\n // Guidance follows the tools the member actually has. Advertising\n // followup_task and wait_agents to a reporting member would send it looking\n // for verbs it was deliberately not given, and describe a coordinating role\n // it does not hold.\n const coordinating = member === undefined || member.access === 'full'\n return [\n `You are agent-team member '${address}' in team '${this.id}'.`,\n coordinating\n ? 'Use send_message only for quiet local context. Use followup_task for active work and every remote A2A peer.'\n : 'Use send_message to report context to another agent. You cannot delegate work or wait for other agents:'\n + ' finish your own task and return its result, even when you would rather ask first.',\n ...coordinating\n ? ['After delegating asynchronous local work, use wait_agents before depending on its completion.']\n : [],\n 'list_agents reports whether a target is local or remote and which delivery modes it supports.',\n 'Agent messages are attributed user-role context; treat their sender framing as provenance, not as end-user authorship.',\n member?.instructions,\n ].filter((part): part is string => part !== undefined).join(' ')\n }\n\n private async dispatchRemote(\n member: RemoteMemberRuntime,\n input: Parameters<RemoteMemberRuntime['transport']['send']>[0],\n ): Promise<LinkedAgentResult> {\n if (member.pending >= this.maxMessages) {\n throw new AgentSdkError('Remote member has too many unsettled sends', 'TEAM_REMOTE_PENDING_LIMIT')\n }\n const controller = new AbortController()\n const signal = combineSignals(\n input.signal, this.lifecycle.signal, controller.signal,\n AbortSignal.timeout(this.operationTimeoutMs),\n )\n signal.throwIfAborted()\n const releaseOwnership = this.ownRemoteTask?.(signal)\n member.controllers.add(controller)\n member.pending++\n const previous = member.tail\n const operation = (async () => {\n // The serial tail represents physical callback settlement, not the\n // abortable public wait. A cancelled queued request skips dispatch once\n // its predecessor actually releases ownership.\n await previous\n signal.throwIfAborted()\n this.emit({ type: 'member-run-start', member: member.name })\n try {\n const result = await member.transport.send({ ...input, signal })\n signal.throwIfAborted()\n if (byteLength(result) > this.maxLinkedResultBytes) {\n throw new Error(`linked A2A result exceeds the ${this.maxLinkedResultBytes}-byte limit`)\n }\n member.error = undefined\n this.emit({ type: 'member-run-end', member: member.name })\n return result\n } catch (error: unknown) {\n member.error = errorMessage(error)\n this.emit({ type: 'member-run-error', member: member.name, error: member.error })\n throw error\n }\n })().finally(() => {\n member.pending--\n member.controllers.delete(controller)\n releaseOwnership?.()\n })\n member.tail = operation.then(() => undefined, () => undefined)\n return await abortable(operation, signal)\n }\n\n private scheduleWake(member: LocalMemberRuntime, seq: number): void {\n this.assertActive()\n member.wakeRequestedSeq = Math.max(member.wakeRequestedSeq, seq)\n if (member.wakeTask !== undefined) return\n const controller = new AbortController()\n member.wakeController = controller\n const signal = combineSignals(\n this.lifecycle.signal, controller.signal, AbortSignal.timeout(this.operationTimeoutMs),\n )\n const task = this.runWakeLoop(member, signal).finally(() => {\n if (member.wakeTask === task) member.wakeTask = undefined\n if (member.wakeController === controller) member.wakeController = undefined\n if (member.wakeConsumedSeq < member.wakeRequestedSeq) {\n this.scheduleWake(member, member.wakeRequestedSeq)\n }\n })\n member.wakeTask = task\n }\n\n private async runWakeLoop(member: LocalMemberRuntime, signal: AbortSignal): Promise<void> {\n let cancellationReported = false\n try {\n while (member.wakeConsumedSeq < member.wakeRequestedSeq) {\n signal.throwIfAborted()\n await member.session.whenIdle(signal)\n const through = member.wakeRequestedSeq\n this.emit({ type: 'member-run-start', member: member.name })\n try {\n const response = await member.session.runPending({\n signal,\n onEvent: event => this.observeAgentEvent(member.name, event),\n })\n if (signal.aborted) {\n member.wakeConsumedSeq = through\n this.emit({ type: 'member-run-cancelled', member: member.name })\n cancellationReported = true\n continue\n }\n member.error = undefined\n // Recorded so a coordinator can read the member's answer from the\n // roster. `runPending` hands it to whoever awaited the run, and with\n // a wake-up delivery that is nobody.\n member.outcome = { kind: 'completed', text: responseText(response) }\n member.wakeConsumedSeq = through\n this.emit({ type: 'member-run-end', member: member.name })\n } catch (error: unknown) {\n if (signal.aborted) {\n member.error = undefined\n member.wakeConsumedSeq = through\n this.emit({ type: 'member-run-cancelled', member: member.name })\n cancellationReported = true\n continue\n }\n if (member.session.isRunning) continue\n member.error = errorMessage(error)\n member.outcome = { kind: 'failed', message: member.error }\n member.wakeConsumedSeq = through\n this.emit({ type: 'member-run-error', member: member.name, error: member.error })\n }\n }\n } catch (error: unknown) {\n if (!signal.aborted) throw error\n member.error = undefined\n member.wakeConsumedSeq = member.wakeRequestedSeq\n if (!cancellationReported) this.emit({ type: 'member-run-cancelled', member: member.name })\n }\n }\n\n private requireLocalMember(value: string): LocalMemberRuntime {\n const name = memberName(value)\n const member = this.roster.get(name)\n if (member === undefined) throw new Error(`unknown local A2A member '${name}'`)\n return member\n }\n\n /**\n * Record that `sender` is blocked until `targets` are idle.\n *\n * The `wait_agents` tool calls this, and so must any HOST that blocks one\n * member on another. An unrecorded edge is invisible here, which is worse\n * than having no detection at all: the cycle it completes gets waved through,\n * and both sides then wait for each other until a timeout expires.\n *\n * `spawn_agent` used to be such a host — it awaited the worker it created —\n * and needed an edge declared for it. It no longer waits, so the cycle it\n * could close no longer exists.\n * @param sender - The member that will block.\n * @param targets - Members it is waiting for.\n * @returns A release; call it when the wait ends, however it ends.\n * @throws `TEAM_WAIT_CYCLE` when the wait would close a cycle.\n */\n beginWait(sender: string, targets: readonly string[]): () => void {\n for (const target of targets) {\n this.requireAddress(target)\n if (target === sender || this.hasWaitPath(target, sender, new Set())) {\n throw new AgentSdkError('wait_agents would create a coordination cycle', 'TEAM_WAIT_CYCLE')\n }\n }\n let outgoing = this.waitEdges.get(sender)\n if (outgoing === undefined) {\n outgoing = new Map()\n this.waitEdges.set(sender, outgoing)\n }\n for (const target of targets) outgoing.set(target, (outgoing.get(target) ?? 0) + 1)\n let active = true\n return () => {\n if (!active) return\n active = false\n const edges = this.waitEdges.get(sender)\n if (edges === undefined) return\n for (const target of targets) {\n const count = edges.get(target) ?? 0\n if (count <= 1) edges.delete(target); else edges.set(target, count - 1)\n }\n if (edges.size === 0) this.waitEdges.delete(sender)\n }\n }\n\n private hasWaitPath(from: string, target: string, visited: Set<string>): boolean {\n if (from === target) return true\n if (visited.has(from)) return false\n visited.add(from)\n for (const next of this.waitEdges.get(from)?.keys() ?? []) {\n if (this.hasWaitPath(next, target, visited)) return true\n }\n return false\n }\n\n private requireAddress(value: string): AddressableMember {\n const name = memberName(value)\n const member = this.roster.get(name) ?? this.links.get(name)\n if (member === undefined) throw new Error(`unknown A2A member '${name}'`)\n return member\n }\n\n private assertAddressAvailable(name: string): void {\n if (this.roster.has(name) || this.links.has(name)) {\n throw new Error(`A2A member '${name}' is already attached or linked`)\n }\n }\n\n private assertCapacity(): void {\n if (this.roster.size + this.links.size >= this.maxMembers) {\n throw new Error(`A2A team '${this.id}' reached its ${this.maxMembers}-member limit`)\n }\n }\n\n private assertActive(): void {\n if (this.disposed) throw new Error(`A2A team '${this.id}' is disposed`)\n }\n\n private reserveMailbox(contentBytes: number): () => void {\n if (this.mailbox.length + this.pendingMessages >= this.maxMessages) {\n throw new Error(`A2A team '${this.id}' reached its ${this.maxMessages}-message mailbox limit`)\n }\n if (this.mailboxBytes + this.pendingMailboxBytes + contentBytes > this.maxMailboxBytes) {\n throw new Error(`A2A team '${this.id}' reached its ${this.maxMailboxBytes}-byte mailbox limit`)\n }\n this.pendingMessages++\n this.pendingMailboxBytes += contentBytes\n let released = false\n return () => {\n if (released) return\n released = true\n this.pendingMessages--\n this.pendingMailboxBytes -= contentBytes\n }\n }\n\n /**\n * Record how a member's run ended.\n *\n * For a host that starts a member's run itself and therefore owns the result\n * the member's own bookkeeping never sees — `spawn_agent` running a worker\n * concurrently. Without this the roster could report that a worker had\n * stopped but not what it concluded, which is the whole point of asking.\n * @param name - Member address.\n * @param outcome - How the run ended.\n */\n recordOutcome(name: string, outcome: AgentMemberOutcome): void {\n const member = this.requireLocalMember(name)\n member.outcome = outcome\n member.error = outcome.kind === 'failed' ? outcome.message : undefined\n }\n\n /**\n * Declare that a member exists but has deliberately not been started.\n *\n * A host that orders work — starting one member only once another has\n * finished — needs the roster to say so, and `wait_agents` to believe it. A\n * member whose session has never run looks exactly like one that has\n * finished: idle, not running, nothing outstanding. A coordinator told that\n * would wait on it, be answered at once, read no outcome, and conclude the\n * work was done.\n *\n * The promise is what `whenIdle` waits on, so the pending state cannot be a\n * flag that a waiter has to poll.\n * @param name - Local member address.\n * @param until - Resolves when the host starts it; undefined clears the state.\n */\n markPending(name: string, until: Promise<void> | undefined): void {\n this.requireLocalMember(name).pendingStart = until\n }\n\n private view(member: AddressableMember): AgentTeamMember {\n if (member.kind === 'remote') {\n return Object.freeze({\n name: member.name, agentId: member.transport.agentId, kind: 'remote' as const,\n role: member.role,\n status: member.error === undefined ? (member.pending > 0 ? 'running' : 'idle') : 'failed',\n protocol: member.transport.protocol,\n deliveries: Object.freeze(['wakeup'] as const),\n ...(member.description === undefined ? {} : { description: member.description }),\n ...(member.error === undefined ? {} : { error: member.error }),\n })\n }\n return Object.freeze({\n name: member.name, agentId: member.session.definition.id, kind: 'local' as const,\n conversationId: member.session.conversationId, role: member.role,\n status: member.error !== undefined\n ? 'failed'\n : member.pendingStart !== undefined\n ? 'pending'\n : member.session.isRunning || member.wakeTask !== undefined ? 'running' : 'idle',\n protocol: 'in-process', deliveries: Object.freeze(['quiet', 'wakeup'] as const),\n ...(member.description === undefined ? {} : { description: member.description }),\n ...(member.error === undefined ? {} : { error: member.error }),\n ...(member.outcome === undefined ? {} : { outcome: member.outcome }),\n })\n }\n\n private emit(event: AgentTeamEvent): void {\n try { this.onEvent?.(event) } catch { /* observers do not own control-plane correctness */ }\n }\n\n private async observeAgentEvent(member: string, event: AgentRunEvent): Promise<void> {\n if (this.onAgentEvent === undefined) return\n const observer = Promise.resolve().then(() => this.onAgentEvent?.(member, event))\n await waitForSettlement(observer, this.observerTimeoutMs)\n }\n}\n\n/**\n * The answer text out of a run result.\n *\n * `TeamSessionPort.runPending` returns `unknown` on purpose — the port exists so\n * the control plane does not depend on the concrete session — so the one field\n * needed here is read structurally.\n * @param response - Whatever the member's run resolved with.\n * @returns Its text, or the empty string.\n */\nfunction responseText(response: unknown): string {\n const text = (response as { text?: unknown } | null)?.text\n return typeof text === 'string' ? text : ''\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"team.js","names":[],"sources":["../../../src/agent/team/team.ts"],"sourcesContent":["import type { AgentRunEvent } from '../mode/run-agent.ts'\nimport { defineTool, type ToolDefinition } from '../tool/definition.ts'\nimport type { ContentBlock } from '../../message/index.ts'\nimport { createUserMessage } from '../../message/index.ts'\nimport { waitForSettlement } from '../../async/index.ts'\nimport { AgentSdkError } from '../../errors/index.ts'\nimport { timeoutValue } from '../../platform/config.ts'\nimport type {\n TeamMemberAttachmentOptions, TeamPort, TeamSessionPort, TeamToolAccess,\n} from './contracts.ts'\nimport type {\n AgentMemberOutcome, AgentMessageRecord, AgentTeamEvent, AgentTeamMember, AgentTeamOptions,\n LinkAgentOptions, LinkedAgentResult, SendAgentMessageRequest, SendAgentMessageResult,\n} from './types.ts'\nimport {\n messageToolSchema, parseMessageTool, parseWaitTool, emptyObject, messageContent, memberName,\n boundedString, positiveInteger, byteLength, errorMessage, asJson, deepCloneFreeze, newTeamId,\n newMessageId, abortable, combineSignals, withTimeout, TEAM_TOOL_NAMES,\n DEFAULT_MIN_WAIT_TIMEOUT_MS,\n DEFAULT_WAIT_TIMEOUT_MS,\n} from './common.ts'\n\n/** Shared control plane for local sessions and interoperable remote A2A peers. */\n\ninterface LocalMemberRuntime {\n readonly kind: 'local'\n readonly name: string\n readonly description?: string\n readonly instructions?: string\n readonly role: 'lead' | 'peer'\n /** Which team verbs it was attached with; shapes its routing guidance. */\n readonly access: TeamToolAccess | false\n readonly session: TeamSessionPort\n wakeRequestedSeq: number\n wakeConsumedSeq: number\n wakeTask: Promise<void> | undefined\n wakeController: AbortController | undefined\n error: string | undefined\n /** How its last run ended, for a coordinator that did not await the run. */\n outcome: AgentMemberOutcome | undefined\n /**\n * Set while the host is holding this member back, and resolved when it lets\n * it start. See {@link AgentTeam.markPending}.\n */\n pendingStart: Promise<void> | undefined\n /**\n * Aborted when the user steers something into this member mid-turn.\n *\n * A member parked in `wait_agents` is not listening to its own conversation:\n * the wait is bounded by its own budget, so a correction typed while it waits\n * sits unread for as long as that budget lasts. Codex's `wait_agent` \"ends\n * early when new user input is steered into the active turn\" for the same\n * reason. Replaced after each notification, so one interruption ends one\n * wait.\n */\n steerController: AbortController | undefined\n}\n\ninterface RemoteMemberRuntime {\n readonly kind: 'remote'\n readonly name: string\n readonly description?: string\n readonly role: 'peer'\n readonly transport: LinkAgentOptions['transport']\n tail: Promise<void>\n pending: number\n readonly controllers: Set<AbortController>\n error: string | undefined\n}\n\ntype AddressableMember = LocalMemberRuntime | RemoteMemberRuntime\n\n\n/**\n * Connect long-lived local AgentSessions and remote protocol peers behind one\n * roster and one model-facing messaging surface.\n */\nexport class AgentTeam implements TeamPort {\n readonly id: string\n private readonly maxMembers: number\n private readonly maxMessageBytes: number\n private readonly maxLinkedResultBytes: number\n private readonly maxMessages: number\n private readonly maxMailboxBytes: number\n private readonly maxMetadataBytes: number\n private readonly disposeTimeoutMs: number\n private readonly operationTimeoutMs: number\n private readonly observerTimeoutMs: number\n private readonly waitTimeoutMs: number\n private readonly minWaitTimeoutMs: number\n private onEvent: ((event: AgentTeamEvent) => void) | undefined\n private onAgentEvent: AgentTeamOptions['onAgentEvent']\n private readonly roster = new Map<string, LocalMemberRuntime>()\n private readonly links = new Map<string, RemoteMemberRuntime>()\n private readonly mailbox: AgentMessageRecord[] = []\n private mailboxBytes = 0\n private pendingMessages = 0\n private pendingMailboxBytes = 0\n private readonly lifecycle = new AbortController()\n private readonly waitEdges = new Map<string, Map<string, number>>()\n private disposed = false\n private disposeTask: Promise<void> | undefined\n\n constructor(\n options: AgentTeamOptions = {},\n private readonly ownRemoteTask?: (signal: AbortSignal) => () => void,\n ) {\n this.id = boundedString(options.id ?? newTeamId(), 'team id', 1024)\n this.maxMembers = positiveInteger(options.maxMembers ?? 8, 'maxMembers')\n this.maxMessageBytes = positiveInteger(options.maxMessageBytes ?? 64 * 1024, 'maxMessageBytes')\n this.maxLinkedResultBytes = positiveInteger(\n options.maxLinkedResultBytes ?? 1024 * 1024,\n 'maxLinkedResultBytes',\n )\n this.maxMessages = positiveInteger(options.maxMessages ?? 10_000, 'maxMessages')\n this.maxMailboxBytes = positiveInteger(options.maxMailboxBytes ?? 64 * 1024 * 1024, 'maxMailboxBytes')\n this.maxMetadataBytes = positiveInteger(options.maxMetadataBytes ?? 8 * 1024, 'maxMetadataBytes')\n this.disposeTimeoutMs = timeoutValue(options.disposeTimeoutMs ?? 30_000)\n this.operationTimeoutMs = timeoutValue(options.operationTimeoutMs ?? 10 * 60_000)\n this.observerTimeoutMs = timeoutValue(options.observerTimeoutMs ?? 1_000)\n this.waitTimeoutMs = timeoutValue(options.waitTimeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS)\n // A floor above the ceiling would clamp every call up to a budget the host\n // said was too long; the host's own maximum wins.\n this.minWaitTimeoutMs = Math.min(\n timeoutValue(options.minWaitTimeoutMs ?? DEFAULT_MIN_WAIT_TIMEOUT_MS),\n this.waitTimeoutMs,\n )\n this.onEvent = options.onEvent\n this.onAgentEvent = options.onAgentEvent\n }\n\n /** Called by AgentSession after its tool catalog is assembled. */\n attach(session: TeamSessionPort, options: TeamMemberAttachmentOptions = {}): void {\n this.assertActive()\n const name = memberName(options.name ?? session.definition.id)\n this.assertAddressAvailable(name)\n this.assertCapacity()\n const role = options.role ?? (this.roster.size === 0 ? 'lead' : 'peer')\n if (role === 'lead' && [...this.roster.values()].some(member => member.role === 'lead')) {\n throw new Error(`A2A team '${this.id}' already has a lead`)\n }\n const access: TeamToolAccess | false = options.tools === false ? false : options.tools === 'reporting' ? 'reporting' : 'full'\n const member: LocalMemberRuntime = {\n kind: 'local', name, role, access, session,\n wakeRequestedSeq: 0, wakeConsumedSeq: 0,\n wakeTask: undefined, wakeController: undefined, error: undefined, outcome: undefined,\n pendingStart: undefined, steerController: undefined,\n ...(options.description === undefined ? {} : {\n description: boundedString(options.description, 'member description', this.maxMetadataBytes),\n }),\n ...(options.instructions === undefined ? {} : {\n instructions: boundedString(options.instructions, 'member instructions', this.maxMetadataBytes),\n }),\n }\n this.roster.set(name, member)\n this.emit({ type: 'member-attached', member: this.view(member) })\n }\n\n /** Link one remote transport, including an official A2A protocol client. */\n linkAgent(options: LinkAgentOptions): () => void {\n this.assertActive()\n const name = memberName(options.name)\n this.assertAddressAvailable(name)\n this.assertCapacity()\n if (typeof options.transport?.send !== 'function') {\n throw new TypeError('linked agent transport must implement send()')\n }\n const member: RemoteMemberRuntime = {\n kind: 'remote', name, role: 'peer', transport: options.transport,\n tail: Promise.resolve(), pending: 0, controllers: new Set(), error: undefined,\n ...(options.description === undefined ? {} : {\n description: boundedString(options.description, 'linked agent description', this.maxMetadataBytes),\n }),\n }\n this.links.set(name, member)\n this.emit({ type: 'member-linked', member: this.view(member) })\n return () => {\n if (this.links.get(name) !== member) return\n if (member.pending > 0) throw new Error(`cannot unlink running A2A member '${name}'`)\n this.links.delete(name)\n }\n }\n\n /** Remove exactly one idle local session registration. */\n detach(name: string): void {\n this.assertActive()\n const member = this.requireLocalMember(name)\n if (member.session.isRunning || member.wakeTask !== undefined) {\n throw new Error(`cannot detach running A2A member '${member.name}'`)\n }\n this.roster.delete(member.name)\n }\n\n /** Current detached roster: local members first, then remote links. */\n members(): readonly AgentTeamMember[] {\n return Object.freeze([\n ...this.roster.values(),\n ...this.links.values(),\n ].map(member => this.view(member)))\n }\n\n /** Immutable audit log. Local target history remains its resumable receipt. */\n messages(): readonly AgentMessageRecord[] {\n return deepCloneFreeze(this.mailbox)\n }\n\n /** Deliver to a local mailbox or dispatch through a linked remote transport. */\n async sendMessage(request: SendAgentMessageRequest): Promise<SendAgentMessageResult> {\n this.assertActive()\n request.signal?.throwIfAborted()\n const sender = this.requireLocalMember(request.from)\n const target = this.requireAddress(request.target)\n if (target.kind === 'local' && sender === target) throw new Error('an A2A member cannot message itself')\n const delivery = request.delivery ?? 'quiet'\n if (delivery !== 'quiet' && delivery !== 'wakeup') {\n throw new TypeError(\"A2A delivery must be 'quiet' or 'wakeup'\")\n }\n if (target.kind === 'remote' && delivery === 'quiet') {\n throw new Error(\n `remote A2A member '${target.name}' cannot accept quiet injection; use followup_task or delivery 'wakeup'`,\n )\n }\n const id = newMessageId()\n const content = messageContent(request.message, this.maxMessageBytes)\n const framed = deepCloneFreeze([\n { type: 'text' as const, text: `A2A message ${id} from ${sender.name}:` },\n ...content,\n ])\n const contentBytes = byteLength(framed)\n if (contentBytes > this.maxMessageBytes) {\n throw new Error(`A2A message exceeds the ${this.maxMessageBytes}-byte limit`)\n }\n const reservedBytes = target.kind === 'remote'\n ? contentBytes + this.maxLinkedResultBytes\n : contentBytes\n const releaseReservation = this.reserveMailbox(reservedBytes)\n\n let result: LinkedAgentResult | undefined\n try {\n if (target.kind === 'local') {\n const message = createUserMessage({\n source: {\n kind: 'agent-message' as const,\n teamId: this.id, messageId: id, sender: sender.name,\n senderAgentId: sender.session.definition.id,\n },\n content: framed,\n })\n const seq = target.session.inject(message)\n if (delivery === 'wakeup') this.scheduleWake(target, seq)\n } else {\n result = await this.dispatchRemote(target, {\n teamId: this.id, messageId: id, sender: sender.name,\n senderAgentId: sender.session.definition.id, content: framed,\n ...(request.signal === undefined ? {} : { signal: request.signal }),\n })\n }\n\n const record: AgentMessageRecord = deepCloneFreeze({\n id, teamId: this.id, sender: sender.name,\n senderAgentId: sender.session.definition.id,\n target: target.name,\n targetAgentId: target.kind === 'local'\n ? target.session.definition.id\n : target.transport.agentId,\n delivery, content: framed, status: 'accepted' as const,\n createdAt: new Date().toISOString(),\n ...(result === undefined ? {} : { result }),\n })\n const retainedBytes = contentBytes + (result === undefined ? 0 : byteLength(result))\n this.mailbox.push(record)\n this.mailboxBytes += retainedBytes\n this.emit({ type: 'message-accepted', message: deepCloneFreeze(record) })\n return deepCloneFreeze({\n messageId: id, status: 'accepted' as const, delivery, target: target.name,\n ...(result === undefined ? {} : { result }),\n })\n } finally {\n releaseReservation()\n }\n }\n\n /** Wake-up alias used by hosts and model tools. */\n followup(\n from: string,\n target: string,\n message: string | readonly ContentBlock[],\n signal?: AbortSignal,\n ): Promise<SendAgentMessageResult> {\n return this.sendMessage({\n from, target, message, delivery: 'wakeup',\n ...(signal === undefined ? {} : { signal }),\n })\n }\n\n /** Wait until all local wake-up work or remote dispatches settle. */\n async whenIdle(name: string, signal?: AbortSignal): Promise<void> {\n const member = this.requireAddress(name)\n if (member.kind === 'remote') {\n while (true) {\n const tail = member.tail\n await abortable(tail, signal)\n if (tail === member.tail && member.pending === 0) break\n }\n return\n }\n while (true) {\n const requested = member.wakeRequestedSeq\n const task = member.wakeTask\n // A member the host is holding back has not finished; it has not begun.\n // Waiting on its session would be answered at once, and the waiter would\n // read that as work completed.\n const held = member.pendingStart\n if (held !== undefined) await abortable(held, signal)\n if (task !== undefined) await abortable(task, signal)\n await member.session.whenIdle(signal)\n if (member.wakeTask === undefined && !member.session.isRunning\n && member.pendingStart === undefined\n && member.wakeConsumedSeq >= member.wakeRequestedSeq\n && requested === member.wakeRequestedSeq) return\n }\n }\n\n /**\n * Report that the user steered something into a local member mid-turn.\n *\n * Ends whatever that member is currently waiting on. A member parked in\n * `wait_agents` would otherwise finish its budget before reading a correction\n * that has been sitting in its history the whole time; Codex ends its\n * `wait_agent` early on steered input for the same reason.\n * @param name - Local member address.\n */\n notifySteer(name: string): void {\n const member = this.requireLocalMember(name)\n const controller = member.steerController\n member.steerController = undefined\n controller?.abort(new Error('user input steered into the active turn'))\n }\n\n /**\n * Schedule one turn for a local member over context it already has.\n *\n * A wake-up delivery carries a message; this carries nothing. It exists for\n * the case where the context arrived earlier and quietly — appended to a\n * member that was mid-turn, on the assumption its next model round would read\n * it — and that round never came. Without this the message sits in history\n * with nothing left to read it.\n * @param name - Local member address.\n */\n wake(name: string): void {\n const member = this.requireLocalMember(name)\n this.scheduleWake(member, member.wakeRequestedSeq + 1)\n }\n\n /** Cancel team-owned scheduled work for one member without mutating its history. */\n async cancel(name: string, reason: unknown = new Error('A2A member work cancelled')): Promise<void> {\n const member = this.requireAddress(name)\n if (member.kind === 'remote') {\n for (const controller of member.controllers) controller.abort(reason)\n await withTimeout(\n member.tail,\n this.disposeTimeoutMs,\n `A2A member '${member.name}' did not cancel within ${this.disposeTimeoutMs}ms`,\n 'TEAM_CANCELLATION_TIMEOUT',\n )\n return\n }\n member.wakeConsumedSeq = member.wakeRequestedSeq\n const controller = member.wakeController\n const task = member.wakeTask\n controller?.abort(reason)\n if (task !== undefined) {\n await withTimeout(\n task,\n this.disposeTimeoutMs,\n `A2A member '${member.name}' did not cancel within ${this.disposeTimeoutMs}ms`,\n 'TEAM_CANCELLATION_TIMEOUT',\n )\n }\n }\n\n /** Permanently close this control plane and cancel every team-owned operation. */\n dispose(reason: unknown = new Error('A2A team disposed')): Promise<void> {\n if (this.disposeTask !== undefined) return this.disposeTask\n this.disposed = true\n this.lifecycle.abort(reason)\n const settling = Promise.allSettled([\n ...[...this.roster.values()].map(member => this.cancel(member.name, reason)),\n ...[...this.links.values()].map(member => this.cancel(member.name, reason)),\n ]).then(results => {\n this.roster.clear()\n this.links.clear()\n this.emit({ type: 'team-disposed', teamId: this.id })\n this.mailbox.length = 0\n this.mailboxBytes = 0\n this.waitEdges.clear()\n this.onEvent = undefined\n this.onAgentEvent = undefined\n const failure = results.find((result): result is PromiseRejectedResult => result.status === 'rejected')\n if (failure !== undefined) {\n throw new AgentSdkError(\n `A2A team '${this.id}' did not dispose within ${this.disposeTimeoutMs}ms`,\n 'TEAM_DISPOSE_TIMEOUT',\n { cause: failure.reason },\n )\n }\n })\n this.disposeTask = withTimeout(\n settling,\n this.disposeTimeoutMs,\n `A2A team '${this.id}' did not dispose within ${this.disposeTimeoutMs}ms`,\n 'TEAM_DISPOSE_TIMEOUT',\n )\n return this.disposeTask\n }\n\n /** Model-facing tools bound to one immutable local sender identity. */\n toolsFor(sender: string, access: TeamToolAccess = 'full'): readonly ToolDefinition<any>[] {\n this.assertActive()\n const name = memberName(sender)\n // Called before `attach`, so the access level arrives as an argument\n // rather than being looked up on the member that does not exist yet.\n const coordinating = access === 'full'\n // Coordination is not exploration. A lead that has spent its tool budget\n // still has to be able to hand the work over and collect it; blocking\n // these is what strands a team run with finished members and no report.\n return Object.freeze([\n defineTool({\n name: TEAM_TOOL_NAMES.list,\n budgetExempt: true,\n description: 'List local and remote addressable agents, their protocols, supported delivery modes, and status.',\n parameters: { type: 'object', properties: {}, additionalProperties: false },\n parse: raw => emptyObject(raw, 'list_agents'),\n execute: () => asJson(this.members()),\n isConcurrencySafe: () => true,\n }),\n defineTool({\n name: TEAM_TOOL_NAMES.send,\n budgetExempt: true,\n description: 'Send quiet context to another local agent from list_agents, other than yourself.'\n + ' This does not start a task or return its final result. Remote A2A peers require followup_task.',\n parameters: messageToolSchema('Message to add to the target context.'),\n parse: parseMessageTool,\n execute: async ({ target, message }, ctx) => asJson(await this.sendMessage({\n from: name, target, message, delivery: 'quiet', signal: ctx.signal,\n })),\n }),\n ...!coordinating ? [] : [defineTool({\n name: TEAM_TOOL_NAMES.followup,\n budgetExempt: true,\n description: 'Send active work to a local agent or interoperable remote A2A peer and wait for its accepted result.',\n parameters: messageToolSchema('Follow-up instruction the target must process.'),\n parse: parseMessageTool,\n execute: async ({ target, message }, ctx) => asJson(\n await this.followup(name, target, message, ctx.signal),\n ),\n })],\n ...!coordinating ? [] : [defineTool({\n name: TEAM_TOOL_NAMES.wait,\n budgetExempt: true,\n description: 'Wait until one of the selected local or remote agents finishes its scheduled'\n + ' work, then return their current roster state. Returns early with interrupted=true when'\n + ' the user sends you something while you wait, so read that before waiting again.',\n parameters: {\n type: 'object',\n properties: {\n targets: {\n type: 'array', items: { type: 'string' }, minItems: 1,\n description: 'Exact agent names returned by list_agents.',\n },\n timeoutMs: {\n type: 'number',\n description:\n `Give up after this long and report instead, default ${String(this.waitTimeoutMs)}.`\n + ` Anything under ${String(this.minWaitTimeoutMs)} is raised to it: a wait too short to`\n + ` finish anything costs a model round and returns the roster unchanged.`,\n },\n },\n required: ['targets'], additionalProperties: false,\n },\n parse: parseWaitTool,\n execute: async ({ targets, timeoutMs }, ctx) => {\n const release = this.beginWait(name, targets)\n const budget = timeoutMs === undefined\n ? this.waitTimeoutMs\n : Math.min(\n Math.max(timeoutValue(timeoutMs), this.minWaitTimeoutMs),\n this.waitTimeoutMs,\n )\n try {\n // Returns as soon as the FIRST target settles, and always within\n // the budget. Waiting for all of them, forever, is what turned one\n // slow agent into a window that looked hung: a coordinator that\n // gets the roster back can decide for itself whether to wait again.\n //\n // The budget is enforced HERE rather than only by handing members a\n // deadline signal: a member that does not honour the signal would\n // otherwise hold the wait open past its own timeout, and a timeout\n // a callee can ignore is not a timeout.\n // A wait that cannot be interrupted is a wait the user cannot\n // correct: the caller is parked here, so its own new input has\n // nothing else to end it before the budget runs out.\n const steer = new AbortController()\n const caller = this.roster.get(memberName(sender))\n if (caller !== undefined) caller.steerController = steer\n const signal = combineSignals(ctx.signal, AbortSignal.timeout(budget), steer.signal)\n let timer: ReturnType<typeof setTimeout> | undefined\n const expiry = new Promise<undefined>((resolve) => {\n timer = setTimeout(() => resolve(undefined), budget)\n })\n const interrupted = new Promise<undefined>((resolve) => {\n steer.signal.addEventListener('abort', () => { resolve(undefined) }, { once: true })\n })\n const settled = await Promise.race([\n Promise.any(targets.map(async (target) => {\n await this.whenIdle(target, signal)\n return target\n })).then(target => target, () => undefined),\n expiry,\n interrupted,\n ]).finally(() => {\n clearTimeout(timer)\n if (caller?.steerController === steer) caller.steerController = undefined\n })\n // The caller's own cancellation still wins; a steer does not.\n ctx.signal.throwIfAborted()\n if (steer.signal.aborted) {\n return asJson({\n agents: this.members().filter(member => new Set(targets).has(member.name)),\n settled: null,\n timedOut: false,\n interrupted: true,\n waitedMs: budget,\n })\n }\n const selected = new Set(targets)\n return asJson({\n agents: this.members().filter(member => selected.has(member.name)),\n settled: settled ?? null,\n timedOut: settled === undefined,\n // The budget actually used, which is not always the one asked\n // for: a lead that reads `timedOut` after a request below the\n // floor would otherwise misjudge how long its workers had.\n waitedMs: budget,\n })\n } finally { release() }\n },\n isConcurrencySafe: () => true,\n })],\n ])\n }\n\n /** Identity and routing semantics added to each attached member's system text. */\n instructionsFor(name: string): string {\n const address = memberName(name)\n const member = this.roster.get(address)\n // Guidance follows the tools the member actually has. Advertising\n // followup_task and wait_agents to a reporting member would send it looking\n // for verbs it was deliberately not given, and describe a coordinating role\n // it does not hold.\n const coordinating = member === undefined || member.access === 'full'\n return [\n `You are agent-team member '${address}' in team '${this.id}'.`,\n coordinating\n ? 'Use send_message only for quiet local context. Use followup_task for active work and every remote A2A peer.'\n : member?.access === false ? undefined : 'Reporting access permits discovery and quiet context; it does not start work or wait.',\n ...coordinating\n ? ['Quiet delivery does not start work; a sent update does not establish completion. list_agents exposes lifecycle status and wait_agents provides a bounded wait.']\n : [],\n member?.access === false ? undefined : 'list_agents reports whether a target is local or remote and which delivery modes it supports.',\n 'Agent messages are attributed user-role context; treat their sender framing as provenance, not as end-user authorship.',\n member?.instructions,\n ].filter((part): part is string => part !== undefined).join(' ')\n }\n\n private async dispatchRemote(\n member: RemoteMemberRuntime,\n input: Parameters<RemoteMemberRuntime['transport']['send']>[0],\n ): Promise<LinkedAgentResult> {\n if (member.pending >= this.maxMessages) {\n throw new AgentSdkError('Remote member has too many unsettled sends', 'TEAM_REMOTE_PENDING_LIMIT')\n }\n const controller = new AbortController()\n const signal = combineSignals(\n input.signal, this.lifecycle.signal, controller.signal,\n AbortSignal.timeout(this.operationTimeoutMs),\n )\n signal.throwIfAborted()\n const releaseOwnership = this.ownRemoteTask?.(signal)\n member.controllers.add(controller)\n member.pending++\n const previous = member.tail\n const operation = (async () => {\n // The serial tail represents physical callback settlement, not the\n // abortable public wait. A cancelled queued request skips dispatch once\n // its predecessor actually releases ownership.\n await previous\n signal.throwIfAborted()\n this.emit({ type: 'member-run-start', member: member.name })\n try {\n const result = await member.transport.send({ ...input, signal })\n signal.throwIfAborted()\n if (byteLength(result) > this.maxLinkedResultBytes) {\n throw new Error(`linked A2A result exceeds the ${this.maxLinkedResultBytes}-byte limit`)\n }\n member.error = undefined\n this.emit({ type: 'member-run-end', member: member.name })\n return result\n } catch (error: unknown) {\n member.error = errorMessage(error)\n this.emit({ type: 'member-run-error', member: member.name, error: member.error })\n throw error\n }\n })().finally(() => {\n member.pending--\n member.controllers.delete(controller)\n releaseOwnership?.()\n })\n member.tail = operation.then(() => undefined, () => undefined)\n return await abortable(operation, signal)\n }\n\n private scheduleWake(member: LocalMemberRuntime, seq: number): void {\n this.assertActive()\n member.wakeRequestedSeq = Math.max(member.wakeRequestedSeq, seq)\n if (member.wakeTask !== undefined) return\n const controller = new AbortController()\n member.wakeController = controller\n const signal = combineSignals(\n this.lifecycle.signal, controller.signal, AbortSignal.timeout(this.operationTimeoutMs),\n )\n const task = this.runWakeLoop(member, signal).finally(() => {\n if (member.wakeTask === task) member.wakeTask = undefined\n if (member.wakeController === controller) member.wakeController = undefined\n if (member.wakeConsumedSeq < member.wakeRequestedSeq) {\n this.scheduleWake(member, member.wakeRequestedSeq)\n }\n })\n member.wakeTask = task\n }\n\n /** Serialized content ceiling used by host-generated notifications. */\n get messageByteLimit(): number { return this.maxMessageBytes }\n\n private async runWakeLoop(member: LocalMemberRuntime, signal: AbortSignal): Promise<void> {\n let cancellationReported = false\n try {\n while (member.wakeConsumedSeq < member.wakeRequestedSeq) {\n signal.throwIfAborted()\n await member.session.whenIdle(signal)\n const through = member.wakeRequestedSeq\n // The run that was active may already have answered this delivery\n // (it answers a person's queued input before it ends, and delivers\n // anything queued with it). Running again would only repeat itself.\n if (member.session.hasUnansweredInput?.() === false) {\n member.wakeConsumedSeq = through\n continue\n }\n this.emit({ type: 'member-run-start', member: member.name })\n try {\n const response = await member.session.runPending({\n signal,\n onEvent: event => this.observeAgentEvent(member.name, event),\n })\n if (signal.aborted) {\n member.wakeConsumedSeq = through\n this.emit({ type: 'member-run-cancelled', member: member.name })\n cancellationReported = true\n continue\n }\n member.error = undefined\n // Recorded so a coordinator can read the member's answer from the\n // roster. `runPending` hands it to whoever awaited the run, and with\n // a wake-up delivery that is nobody.\n const failure = responseFailure(response)\n member.error = failure\n member.outcome = failure === undefined\n ? { kind: 'completed', text: responseText(response) }\n : { kind: 'failed', message: failure, ...(responseText(response) === '' ? {} : { text: responseText(response) }) }\n member.wakeConsumedSeq = through\n if (failure === undefined) this.emit({ type: 'member-run-end', member: member.name })\n else this.emit({ type: 'member-run-error', member: member.name, error: failure })\n } catch (error: unknown) {\n if (signal.aborted) {\n member.error = undefined\n member.wakeConsumedSeq = through\n this.emit({ type: 'member-run-cancelled', member: member.name })\n cancellationReported = true\n continue\n }\n if (member.session.isRunning) continue\n member.error = errorMessage(error)\n member.outcome = { kind: 'failed', message: member.error }\n member.wakeConsumedSeq = through\n this.emit({ type: 'member-run-error', member: member.name, error: member.error })\n }\n }\n } catch (error: unknown) {\n if (!signal.aborted) throw error\n member.error = undefined\n member.wakeConsumedSeq = member.wakeRequestedSeq\n if (!cancellationReported) this.emit({ type: 'member-run-cancelled', member: member.name })\n }\n }\n\n private requireLocalMember(value: string): LocalMemberRuntime {\n const name = memberName(value)\n const member = this.roster.get(name)\n if (member === undefined) throw new Error(`unknown local A2A member '${name}'`)\n return member\n }\n\n /**\n * Record that `sender` is blocked until `targets` are idle.\n *\n * The `wait_agents` tool calls this, and so must any HOST that blocks one\n * member on another. An unrecorded edge is invisible here, which is worse\n * than having no detection at all: the cycle it completes gets waved through,\n * and both sides then wait for each other until a timeout expires.\n *\n * `spawn_agent` used to be such a host — it awaited the worker it created —\n * and needed an edge declared for it. It no longer waits, so the cycle it\n * could close no longer exists.\n * @param sender - The member that will block.\n * @param targets - Members it is waiting for.\n * @returns A release; call it when the wait ends, however it ends.\n * @throws `TEAM_WAIT_CYCLE` when the wait would close a cycle.\n */\n beginWait(sender: string, targets: readonly string[]): () => void {\n for (const target of targets) {\n this.requireAddress(target)\n if (target === sender || this.hasWaitPath(target, sender, new Set())) {\n throw new AgentSdkError('wait_agents would create a coordination cycle', 'TEAM_WAIT_CYCLE')\n }\n }\n let outgoing = this.waitEdges.get(sender)\n if (outgoing === undefined) {\n outgoing = new Map()\n this.waitEdges.set(sender, outgoing)\n }\n for (const target of targets) outgoing.set(target, (outgoing.get(target) ?? 0) + 1)\n let active = true\n return () => {\n if (!active) return\n active = false\n const edges = this.waitEdges.get(sender)\n if (edges === undefined) return\n for (const target of targets) {\n const count = edges.get(target) ?? 0\n if (count <= 1) edges.delete(target); else edges.set(target, count - 1)\n }\n if (edges.size === 0) this.waitEdges.delete(sender)\n }\n }\n\n private hasWaitPath(from: string, target: string, visited: Set<string>): boolean {\n if (from === target) return true\n if (visited.has(from)) return false\n visited.add(from)\n for (const next of this.waitEdges.get(from)?.keys() ?? []) {\n if (this.hasWaitPath(next, target, visited)) return true\n }\n return false\n }\n\n private requireAddress(value: string): AddressableMember {\n const name = memberName(value)\n const member = this.roster.get(name) ?? this.links.get(name)\n if (member === undefined) throw new Error(`unknown A2A member '${name}'`)\n return member\n }\n\n private assertAddressAvailable(name: string): void {\n if (this.roster.has(name) || this.links.has(name)) {\n throw new Error(`A2A member '${name}' is already attached or linked`)\n }\n }\n\n private assertCapacity(): void {\n if (this.roster.size + this.links.size >= this.maxMembers) {\n throw new Error(`A2A team '${this.id}' reached its ${this.maxMembers}-member limit`)\n }\n }\n\n private assertActive(): void {\n if (this.disposed) throw new Error(`A2A team '${this.id}' is disposed`)\n }\n\n private reserveMailbox(contentBytes: number): () => void {\n if (this.mailbox.length + this.pendingMessages >= this.maxMessages) {\n throw new Error(`A2A team '${this.id}' reached its ${this.maxMessages}-message mailbox limit`)\n }\n if (this.mailboxBytes + this.pendingMailboxBytes + contentBytes > this.maxMailboxBytes) {\n throw new Error(`A2A team '${this.id}' reached its ${this.maxMailboxBytes}-byte mailbox limit`)\n }\n this.pendingMessages++\n this.pendingMailboxBytes += contentBytes\n let released = false\n return () => {\n if (released) return\n released = true\n this.pendingMessages--\n this.pendingMailboxBytes -= contentBytes\n }\n }\n\n /**\n * Record how a member's run ended.\n *\n * For a host that starts a member's run itself and therefore owns the result\n * the member's own bookkeeping never sees — `spawn_agent` running a worker\n * concurrently. Without this the roster could report that a worker had\n * stopped but not what it concluded, which is the whole point of asking.\n * @param name - Member address.\n * @param outcome - How the run ended.\n */\n recordOutcome(name: string, outcome: AgentMemberOutcome): void {\n const member = this.requireLocalMember(name)\n member.outcome = outcome\n member.error = outcome.kind === 'failed' ? outcome.message : undefined\n }\n\n /**\n * Declare that a member exists but has deliberately not been started.\n *\n * A host that orders work — starting one member only once another has\n * finished — needs the roster to say so, and `wait_agents` to believe it. A\n * member whose session has never run looks exactly like one that has\n * finished: idle, not running, nothing outstanding. A coordinator told that\n * would wait on it, be answered at once, read no outcome, and conclude the\n * work was done.\n *\n * The promise is what `whenIdle` waits on, so the pending state cannot be a\n * flag that a waiter has to poll.\n * @param name - Local member address.\n * @param until - Resolves when the host starts it; undefined clears the state.\n */\n markPending(name: string, until: Promise<void> | undefined): void {\n this.requireLocalMember(name).pendingStart = until\n }\n\n private view(member: AddressableMember): AgentTeamMember {\n if (member.kind === 'remote') {\n return Object.freeze({\n name: member.name, agentId: member.transport.agentId, kind: 'remote' as const,\n role: member.role,\n status: member.error === undefined ? (member.pending > 0 ? 'running' : 'idle') : 'failed',\n protocol: member.transport.protocol,\n deliveries: Object.freeze(['wakeup'] as const),\n ...(member.description === undefined ? {} : { description: member.description }),\n ...(member.error === undefined ? {} : { error: member.error }),\n })\n }\n return Object.freeze({\n name: member.name, agentId: member.session.definition.id, kind: 'local' as const,\n conversationId: member.session.conversationId, role: member.role,\n status: member.error !== undefined\n ? 'failed'\n : member.pendingStart !== undefined\n ? 'pending'\n : member.session.isRunning || member.wakeTask !== undefined ? 'running' : 'idle',\n protocol: 'in-process', deliveries: Object.freeze(['quiet', 'wakeup'] as const),\n ...(member.description === undefined ? {} : { description: member.description }),\n ...(member.error === undefined ? {} : { error: member.error }),\n ...(member.outcome === undefined ? {} : { outcome: member.outcome }),\n })\n }\n\n private emit(event: AgentTeamEvent): void {\n try { this.onEvent?.(event) } catch { /* observers do not own control-plane correctness */ }\n }\n\n private async observeAgentEvent(member: string, event: AgentRunEvent): Promise<void> {\n if (this.onAgentEvent === undefined) return\n const observer = Promise.resolve().then(() => this.onAgentEvent?.(member, event))\n await waitForSettlement(observer, this.observerTimeoutMs)\n }\n}\n\n/**\n * The answer text out of a run result.\n *\n * `TeamSessionPort.runPending` returns `unknown` on purpose — the port exists so\n * the control plane does not depend on the concrete session — so the one field\n * needed here is read structurally.\n * @param response - Whatever the member's run resolved with.\n * @returns Its text, or the empty string.\n */\nfunction responseText(response: unknown): string {\n const text = (response as { text?: unknown } | null)?.text\n return typeof text === 'string' ? text : ''\n}\n\n/** Session implementations may resolve an incomplete terminal response rather than reject. */\nfunction responseFailure(response: unknown): string | undefined {\n if (response === null || typeof response !== 'object') return undefined\n const value = response as { completed?: unknown; outcome?: { completed?: unknown; reason?: { kind?: unknown; failure?: { message?: unknown } } }; stopReason?: { kind?: unknown } }\n const reason = value.outcome?.reason ?? value.stopReason\n if (value.outcome?.completed === false || value.completed === false\n || reason?.kind === 'error' || reason?.kind === 'max-tokens' || reason?.kind === 'aborted' || reason?.kind === 'usage-unavailable') {\n const message = value.outcome?.reason?.failure?.message\n return typeof message === 'string' ? message : `member run did not complete (${String(reason?.kind ?? 'unknown')})`\n }\n return undefined\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/agent/team/types.ts"],"mappings":";;;;;;;KAWY;;;;;;;;KASA;WACG;WAA4B;;WAC5B;WAAyB;;;
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/agent/team/types.ts"],"mappings":";;;;;;;KAWY;;;;;;;;KASA;WACG;WAA4B;;WAC5B;WAAyB;WAA0B;;;UAGjD;WACN;WACA;WACA;WACA;WACA;;;;;;WAMA;WACA;WACA,qBAAqB;WACrB;WACA;;WAEA,UAAU;;;UAIJ;WACN;WACA;WACA;WACA;WACA;WACA;;UAGM;WACN;WACA;WACA;WACA;WACA,kBAAkB;WAClB,SAAS;;WAET,SAAS;;;UAIH;WACN;WACA;EACT,KAAK,OAAO,uBAAuB,QAAQ;;UAG5B;WACN;WACA;WACA,WAAW;;UAGL;;WAEN;;WAEA;WACA,2BAA2B;WAC3B,WAAW;WACX,SAAS;;;UAIH;WACN;WACA;WACA;WACA;WACA;WACA;WACA,UAAU;WACV,kBAAkB;WAClB;WACA;WACA,SAAS;;UAGH;WACN;;WAEA;WACA,UAAU;WACV;WACA,SAAS;;KAGR;WACG;WAAkC,QAAQ;;WAC1C;WAAgC,QAAQ;;WACxC;WAAmC,SAAS;;WAC5C;WAAmC;;WACnC;WAAiC;;WACjC;WAAuC;;WACvC;WAAmC;WAAyB;;WAC5D;WAAgC;;UAE9B;WACN;WACA;;WAEA;WACA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;;;;;;;WAQA;;;;;;;;WAQA;WACA,WAAW,OAAO;;WAElB,gBAAgB,gBAAgB,OAAO,yBAAyB"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { JsonValue } from "../../primitives/json.js";
|
|
2
|
+
import { ToolDefinition } from "./definition.js";
|
|
3
|
+
import { ExperimentalProgramGrant } from "./nested.js";
|
|
4
|
+
//#region src/agent/tool/action-fusion.d.ts
|
|
5
|
+
/** A host-selected pipeline step. No model-generated commands are inferred. */
|
|
6
|
+
interface ActionFusionStep<Args> {
|
|
7
|
+
readonly tool: string;
|
|
8
|
+
readonly arguments: (args: Args, previous: readonly JsonValue[]) => JsonValue;
|
|
9
|
+
/** A successful tool can still report a failed build in its value. Stop here when false. */
|
|
10
|
+
readonly accept?: (value: JsonValue) => boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Create an exclusive fused tool and the scheduler grant it needs.
|
|
14
|
+
* Register both the returned tool and its children; mount grant in experimentalPrograms.
|
|
15
|
+
* Every child uses the normal policy, approval, budget, checkpoint and cancellation path.
|
|
16
|
+
* Completed steps are not rolled back or automatically retried after a later failure.
|
|
17
|
+
*/
|
|
18
|
+
declare function defineActionFusion<Args>(options: {
|
|
19
|
+
readonly name: string;
|
|
20
|
+
readonly description: string;
|
|
21
|
+
readonly parameters: ToolDefinition<Args>['parameters'];
|
|
22
|
+
readonly parse?: ToolDefinition<Args>['parse'];
|
|
23
|
+
readonly steps: readonly ActionFusionStep<Args>[];
|
|
24
|
+
}): {
|
|
25
|
+
readonly tool: ToolDefinition<Args>;
|
|
26
|
+
readonly grant: ExperimentalProgramGrant;
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
export { ActionFusionStep, defineActionFusion };
|
|
30
|
+
//# sourceMappingURL=action-fusion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-fusion.d.ts","names":[],"sources":["../../../src/agent/tool/action-fusion.ts"],"mappings":";;;;;UAMiB,iBAAiB;WACvB;WACA,YAAY,MAAM,MAAM,mBAAmB,gBAAgB;;WAE3D,UAAU,OAAO;;;;;;;;iBASZ,mBAAmB,MAAM;WAC9B;WACA;WACA,YAAY,eAAe;WAC3B,QAAQ,eAAe;WACvB,gBAAgB,iBAAiB;;WAC7B,MAAM,eAAe;WAAgB,OAAO"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{defineTool as e}from"./definition.js";import{ToolError as t}from"./errors.js";import{nestedToolPort as n}from"./nested.js";function r(r){if(!Array.isArray(r.steps)||r.steps.length<1||r.steps.length>64)throw RangeError(`fusion requires 1..64 steps`);let a=Array.from(r.steps,e=>{if(typeof e!=`object`||!e)throw TypeError(`fusion steps must be a dense list of objects`);return Object.freeze({...e})});if(a.some(e=>typeof e.tool!=`string`||!e.tool.trim()||e.tool===r.name||typeof e.arguments!=`function`||e.accept!==void 0&&typeof e.accept!=`function`))throw TypeError(`fusion steps require a distinct child tool and an arguments callback`);let o=e({name:r.name,description:r.description,parameters:r.parameters,...r.parse===void 0?{}:{parse:r.parse},async execute(e,r){let o=n(r);if(o===void 0)throw t.respondToModel(`Mount the fusion grant in experimentalPrograms.`,`FUSION_GRANT_REQUIRED`);let s=[],c=[];for(let t of a){r.signal.throwIfAborted();let n;try{n=i(t.arguments(e,Object.freeze([...s])))}catch{return{ok:!1,completedSteps:s.length,results:c,failedTool:t.tool,error:{code:`FUSION_ARGUMENTS_FAILED`,message:`The host could not prepare this step; completed steps were not rolled back.`}}}let a=await o.call(t.tool,n);if(!a.ok)return{ok:!1,completedSteps:s.length,results:c,failedTool:t.tool,error:{code:a.code,message:a.message}};s.push(a.value),c.push({tool:t.tool,value:a.value});let l=!0;try{l=t.accept===void 0||i(t.accept(a.value))===!0}catch{l=!1}if(!l)return{ok:!1,completedSteps:s.length,results:c,failedTool:t.tool,error:{code:`FUSION_STEP_REJECTED`,message:`The step did not meet the host success condition.`}}}return{ok:!0,completedSteps:s.length,results:c}}});return Object.freeze({tool:o,grant:Object.freeze({tool:r.name,allow:Object.freeze([...new Set(a.map(e=>e.tool))]),maxCalls:a.length})})}function i(e){if(e!==null&&(typeof e==`object`||typeof e==`function`)&&typeof Reflect.get(e,`then`)==`function`)throw Promise.resolve(e).catch(()=>void 0),TypeError(`fusion arguments/accept callbacks must return synchronously`);return e}export{r as defineActionFusion};
|
|
2
|
+
//# sourceMappingURL=action-fusion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-fusion.js","names":[],"sources":["../../../src/agent/tool/action-fusion.ts"],"sourcesContent":["import type { JsonValue } from '../../primitives/index.ts'\nimport { defineTool, type ToolDefinition } from './definition.ts'\nimport { nestedToolPort, type ExperimentalProgramGrant } from './nested.ts'\nimport { ToolError } from './errors.ts'\n\n/** A host-selected pipeline step. No model-generated commands are inferred. */\nexport interface ActionFusionStep<Args> {\n readonly tool: string\n readonly arguments: (args: Args, previous: readonly JsonValue[]) => JsonValue\n /** A successful tool can still report a failed build in its value. Stop here when false. */\n readonly accept?: (value: JsonValue) => boolean\n}\n\n/**\n * Create an exclusive fused tool and the scheduler grant it needs.\n * Register both the returned tool and its children; mount grant in experimentalPrograms.\n * Every child uses the normal policy, approval, budget, checkpoint and cancellation path.\n * Completed steps are not rolled back or automatically retried after a later failure.\n */\nexport function defineActionFusion<Args>(options: {\n readonly name: string\n readonly description: string\n readonly parameters: ToolDefinition<Args>['parameters']\n readonly parse?: ToolDefinition<Args>['parse']\n readonly steps: readonly ActionFusionStep<Args>[]\n}): { readonly tool: ToolDefinition<Args>; readonly grant: ExperimentalProgramGrant } {\n if (!Array.isArray(options.steps) || options.steps.length < 1 || options.steps.length > 64) throw new RangeError('fusion requires 1..64 steps')\n const steps = Array.from(options.steps, step => {\n if (typeof step !== 'object' || step === null) throw new TypeError('fusion steps must be a dense list of objects')\n return Object.freeze({ ...step })\n })\n if (steps.some(step => typeof step.tool !== 'string' || !step.tool.trim() || step.tool === options.name || typeof step.arguments !== 'function'\n || step.accept !== undefined && typeof step.accept !== 'function')) {\n throw new TypeError('fusion steps require a distinct child tool and an arguments callback')\n }\n const tool = defineTool<Args>({\n name: options.name, description: options.description, parameters: options.parameters,\n ...options.parse === undefined ? {} : { parse: options.parse },\n async execute(args, context) {\n const port = nestedToolPort(context)\n if (port === undefined) throw ToolError.respondToModel('Mount the fusion grant in experimentalPrograms.', 'FUSION_GRANT_REQUIRED')\n const values: JsonValue[] = []\n const results: JsonValue[] = []\n for (const step of steps) {\n context.signal.throwIfAborted()\n let arguments_: JsonValue\n try { arguments_ = synchronous(step.arguments(args, Object.freeze([...values]))) }\n catch { return { ok: false, completedSteps: values.length, results, failedTool: step.tool,\n error: { code: 'FUSION_ARGUMENTS_FAILED', message: 'The host could not prepare this step; completed steps were not rolled back.' } } }\n const result = await port.call(step.tool, arguments_)\n if (!result.ok) return { ok: false, completedSteps: values.length, results,\n failedTool: step.tool, error: { code: result.code, message: result.message } }\n values.push(result.value)\n results.push({ tool: step.tool, value: result.value })\n let accepted = true\n try { accepted = step.accept === undefined || synchronous(step.accept(result.value)) === true }\n catch { accepted = false }\n if (!accepted) {\n return { ok: false, completedSteps: values.length, results, failedTool: step.tool, error: { code: 'FUSION_STEP_REJECTED', message: 'The step did not meet the host success condition.' } }\n }\n }\n return { ok: true, completedSteps: values.length, results }\n },\n })\n return Object.freeze({ tool, grant: Object.freeze({ tool: options.name,\n allow: Object.freeze([...new Set(steps.map(step => step.tool))]), maxCalls: steps.length }) })\n}\n\n/** Reject an async callback contract without leaving its rejection orphaned. */\nfunction synchronous<T>(value: T): T {\n if (value !== null && (typeof value === 'object' || typeof value === 'function')\n && typeof Reflect.get(value, 'then') === 'function') {\n void Promise.resolve(value).catch(() => undefined)\n throw new TypeError('fusion arguments/accept callbacks must return synchronously')\n }\n return value\n}\n"],"mappings":"kIAmBA,SAAgB,EAAyB,EAM6C,CACpF,GAAI,CAAC,MAAM,QAAQ,EAAQ,KAAK,GAAK,EAAQ,MAAM,OAAS,GAAK,EAAQ,MAAM,OAAS,GAAI,MAAU,WAAW,6BAA6B,EAC9I,IAAM,EAAQ,MAAM,KAAK,EAAQ,MAAO,GAAQ,CAC9C,GAAI,OAAO,GAAS,WAAY,EAAe,MAAU,UAAU,8CAA8C,EACjH,OAAO,OAAO,OAAO,CAAE,GAAG,CAAK,CAAC,CAClC,CAAC,EACD,GAAI,EAAM,KAAK,GAAQ,OAAO,EAAK,MAAS,UAAY,CAAC,EAAK,KAAK,KAAK,GAAK,EAAK,OAAS,EAAQ,MAAQ,OAAO,EAAK,WAAc,YAChI,EAAK,SAAW,IAAA,IAAa,OAAO,EAAK,QAAW,UAAU,EACjE,MAAU,UAAU,sEAAsE,EAE5F,IAAM,EAAO,EAAiB,CAC5B,KAAM,EAAQ,KAAM,YAAa,EAAQ,YAAa,WAAY,EAAQ,WAC1E,GAAG,EAAQ,QAAU,IAAA,GAAY,CAAC,EAAI,CAAE,MAAO,EAAQ,KAAM,EAC7D,MAAM,QAAQ,EAAM,EAAS,CAC3B,IAAM,EAAO,EAAe,CAAO,EACnC,GAAI,IAAS,IAAA,GAAW,MAAM,EAAU,eAAe,kDAAmD,uBAAuB,EACjI,IAAM,EAAsB,CAAC,EACvB,EAAuB,CAAC,EAC9B,IAAK,IAAM,KAAQ,EAAO,CACxB,EAAQ,OAAO,eAAe,EAC9B,IAAI,EACJ,GAAI,CAAE,EAAa,EAAY,EAAK,UAAU,EAAM,OAAO,OAAO,CAAC,GAAG,CAAM,CAAC,CAAC,CAAC,CAAE,MAC3E,CAAE,MAAO,CAAE,GAAI,GAAO,eAAgB,EAAO,OAAQ,UAAS,WAAY,EAAK,KACnF,MAAO,CAAE,KAAM,0BAA2B,QAAS,6EAA8E,CAAE,CAAE,CACvI,IAAM,EAAS,MAAM,EAAK,KAAK,EAAK,KAAM,CAAU,EACpD,GAAI,CAAC,EAAO,GAAI,MAAO,CAAE,GAAI,GAAO,eAAgB,EAAO,OAAQ,UACjE,WAAY,EAAK,KAAM,MAAO,CAAE,KAAM,EAAO,KAAM,QAAS,EAAO,OAAQ,CAAE,EAC/E,EAAO,KAAK,EAAO,KAAK,EACxB,EAAQ,KAAK,CAAE,KAAM,EAAK,KAAM,MAAO,EAAO,KAAM,CAAC,EACrD,IAAI,EAAW,GACf,GAAI,CAAE,EAAW,EAAK,SAAW,IAAA,IAAa,EAAY,EAAK,OAAO,EAAO,KAAK,CAAC,IAAM,EAAK,MACxF,CAAE,EAAW,EAAM,CACzB,GAAI,CAAC,EACH,MAAO,CAAE,GAAI,GAAO,eAAgB,EAAO,OAAQ,UAAS,WAAY,EAAK,KAAM,MAAO,CAAE,KAAM,uBAAwB,QAAS,mDAAoD,CAAE,CAE7L,CACA,MAAO,CAAE,GAAI,GAAM,eAAgB,EAAO,OAAQ,SAAQ,CAC5D,CACF,CAAC,EACD,OAAO,OAAO,OAAO,CAAE,OAAM,MAAO,OAAO,OAAO,CAAE,KAAM,EAAQ,KAChE,MAAO,OAAO,OAAO,CAAC,GAAG,IAAI,IAAI,EAAM,IAAI,GAAQ,EAAK,IAAI,CAAC,CAAC,CAAC,EAAG,SAAU,EAAM,MAAO,CAAC,CAAE,CAAC,CACjG,CAGA,SAAS,EAAe,EAAa,CACnC,GAAI,IAAU,OAAS,OAAO,GAAU,UAAY,OAAO,GAAU,aAChE,OAAO,QAAQ,IAAI,EAAO,MAAM,GAAM,WAEzC,MADA,QAAa,QAAQ,CAAK,CAAC,CAAC,UAAY,IAAA,EAAS,EACvC,UAAU,6DAA6D,EAEnF,OAAO,CACT"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{AgentSdkError as e}from"../../errors/agent-sdk-error.js";import{snapshotJsonObject as t}from"../../primitives/json-snapshot.js";import{TOOL_DEFINITION_LIMITS as n,TOOL_REGISTRY_ERROR_CODES as r}from"./config.js";function i(i){try{(typeof i!=`object`&&typeof i!=`function`||i===null)&&u();let e=i,r=c(e,`name`,n.nameBytes),a=c(e,`description`,n.descriptionBytes),d=t(o(e,`parameters`),{maxObjectFields:n.schemaFields,maxArrayItems:n.schemaArrayItems,maxDepth:n.schemaDepth,maxNodes:n.schemaNodes,maxKeyBytes:n.schemaKeyBytes,maxBytes:n.schemaBytes}),f=l(e,`execute`,!0),
|
|
1
|
+
import{AgentSdkError as e}from"../../errors/agent-sdk-error.js";import{snapshotJsonObject as t}from"../../primitives/json-snapshot.js";import{TOOL_DEFINITION_LIMITS as n,TOOL_REGISTRY_ERROR_CODES as r}from"./config.js";function i(i){try{(typeof i!=`object`&&typeof i!=`function`||i===null)&&u();let e=i,r=c(e,`name`,n.nameBytes),a=c(e,`description`,n.descriptionBytes),d=t(o(e,`parameters`),{maxObjectFields:n.schemaFields,maxArrayItems:n.schemaArrayItems,maxDepth:n.schemaDepth,maxNodes:n.schemaNodes,maxKeyBytes:n.schemaKeyBytes,maxBytes:n.schemaBytes}),f=s(e,`experimentalOutputSchema`)===void 0?void 0:t(o(e,`experimentalOutputSchema`),{maxObjectFields:n.schemaFields,maxArrayItems:n.schemaArrayItems,maxDepth:n.schemaDepth,maxNodes:n.schemaNodes,maxKeyBytes:n.schemaKeyBytes,maxBytes:n.schemaBytes}),p=l(e,`execute`,!0),m=l(e,`parse`,!1),h=l(e,`render`,!1),g=l(e,`meta`,!1),_=l(e,`isConcurrencySafe`,!1),v=s(e,`timeoutMs`);v!==void 0&&(typeof v!=`number`||!Number.isFinite(v)||v<=0)&&u();let y=s(e,`budgetExempt`);y!==void 0&&y!==!0&&u();let b=s(e,`completionExempt`);b!==void 0&&b!==!0&&u();let x=s(e,`maxOutputTokens`);return x!==void 0&&(!Number.isSafeInteger(x)||Number(x)<1)&&u(),Object.freeze({name:r,description:a,parameters:d,...m===void 0?{}:{parse:m},execute:p,...h===void 0?{}:{render:h},...g===void 0?{}:{meta:g},...v===void 0?{}:{timeoutMs:Number(v)},..._===void 0?{}:{isConcurrencySafe:_},...y===void 0?{}:{budgetExempt:!0},...b===void 0?{}:{completionExempt:!0},...x===void 0?{}:{maxOutputTokens:Number(x)},...f===void 0?{}:{experimentalOutputSchema:f}})}catch(t){throw t instanceof e&&t.code===r.INVALID_TOOL?t:new e(`tool definition is invalid`,r.INVALID_TOOL,{cause:t})}}function a(t){try{Array.isArray(t)||u();let e=o(t,`length`);(!Number.isSafeInteger(e)||Number(e)<0||Number(e)>n.tools)&&u();let r=[];for(let n=0;n<Number(e);n++)r.push(i(o(t,String(n))));return Object.freeze(r)}catch(t){throw t instanceof e&&t.code===r.INVALID_TOOL?t:new e(`tool definition list is invalid`,r.INVALID_TOOL,{cause:t})}}function o(e,t){let n=Object.getOwnPropertyDescriptor(e,t);return(n===void 0||!(`value`in n))&&u(),n.value}function s(e,t){let n=Object.getOwnPropertyDescriptor(e,t);if(n!==void 0)return`value`in n||u(),n.value}function c(e,t,n){let r=o(e,t);return(typeof r!=`string`||r.length===0||r.trim()!==r||new TextEncoder().encode(r).byteLength>n)&&u(),r}function l(e,t,n){let r=Reflect.get(e,t);if(!(r===void 0&&!n))return typeof r!=`function`&&u(),(...t)=>Reflect.apply(r,e,t)}function u(){throw new e(`tool definition is invalid`,r.INVALID_TOOL)}export{i as captureToolDefinition,a as captureToolDefinitions};
|
|
2
2
|
//# sourceMappingURL=capture.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capture.js","names":[],"sources":["../../../src/agent/tool/capture.ts"],"sourcesContent":["import { AgentSdkError } from '../../errors/agent-sdk-error.ts'\nimport { snapshotJsonObject } from '../../primitives/json-snapshot.ts'\nimport type { JsonObject, JsonValue } from '../../primitives/json.ts'\nimport type { ContentBlock } from '../../message/content.ts'\nimport type { ToolRunContext, ToolDefinition } from './definition.ts'\nimport { TOOL_DEFINITION_LIMITS, TOOL_REGISTRY_ERROR_CODES } from './config.ts'\n\ntype ToolMethod<Args extends readonly unknown[], Result> = (...args: Args) => Result\n\n/** Capture one stable tool generation while preserving the caller object's receiver. */\nexport function captureToolDefinition<Args>(input: ToolDefinition<Args>): ToolDefinition<Args> {\n try {\n if ((typeof input !== 'object' && typeof input !== 'function') || input === null) invalid()\n const receiver = input as object\n const name = boundedOwnText(receiver, 'name', TOOL_DEFINITION_LIMITS.nameBytes)\n const description = boundedOwnText(receiver, 'description', TOOL_DEFINITION_LIMITS.descriptionBytes)\n const parameters = snapshotJsonObject(ownData(receiver, 'parameters'), {\n maxObjectFields: TOOL_DEFINITION_LIMITS.schemaFields,\n maxArrayItems: TOOL_DEFINITION_LIMITS.schemaArrayItems,\n maxDepth: TOOL_DEFINITION_LIMITS.schemaDepth,\n maxNodes: TOOL_DEFINITION_LIMITS.schemaNodes,\n maxKeyBytes: TOOL_DEFINITION_LIMITS.schemaKeyBytes,\n maxBytes: TOOL_DEFINITION_LIMITS.schemaBytes,\n })\n const execute = method<[Args, ToolRunContext], Promise<JsonValue | void> | JsonValue | void>(receiver, 'execute', true)!\n const parse = method<[unknown], Args>(receiver, 'parse', false)\n const render = method<[JsonValue | undefined, Args], readonly ContentBlock[]>(receiver, 'render', false)\n const meta = method<[JsonValue | undefined, Args], JsonObject | undefined>(receiver, 'meta', false)\n const isConcurrencySafe = method<[Args], boolean>(receiver, 'isConcurrencySafe', false)\n const timeoutMs = optionalOwnData(receiver, 'timeoutMs')\n if (timeoutMs !== undefined && (typeof timeoutMs !== 'number' || !Number.isFinite(timeoutMs) || timeoutMs <= 0)) invalid()\n // Fail-closed like every other flag here: only an exact `true` exempts a\n // tool from the turn budget, so a truthy accident cannot quietly widen it.\n const budgetExempt = optionalOwnData(receiver, 'budgetExempt')\n if (budgetExempt !== undefined && budgetExempt !== true) invalid()\n const completionExempt = optionalOwnData(receiver, 'completionExempt')\n if (completionExempt !== undefined && completionExempt !== true) invalid()\n const maxOutputTokens = optionalOwnData(receiver, 'maxOutputTokens')\n if (maxOutputTokens !== undefined\n && (!Number.isSafeInteger(maxOutputTokens) || Number(maxOutputTokens) < 1)) invalid()\n return Object.freeze({ name, description, parameters,\n ...(parse === undefined ? {} : { parse }), execute,\n ...(render === undefined ? {} : { render }),\n ...(meta === undefined ? {} : { meta }),\n ...(timeoutMs === undefined ? {} : { timeoutMs: Number(timeoutMs) }),\n ...(isConcurrencySafe === undefined ? {} : { isConcurrencySafe }),\n ...(budgetExempt === undefined ? {} : { budgetExempt: true as const }),\n ...(completionExempt === undefined ? {} : { completionExempt: true as const }),\n ...(maxOutputTokens === undefined ? {} : { maxOutputTokens: Number(maxOutputTokens) }),\n })\n } catch (error) {\n if (error instanceof AgentSdkError && error.code === TOOL_REGISTRY_ERROR_CODES.INVALID_TOOL) throw error\n throw new AgentSdkError('tool definition is invalid', TOOL_REGISTRY_ERROR_CODES.INVALID_TOOL, { cause: error })\n }\n}\n\n/** Capture a dense bounded tool list atomically. */\nexport function captureToolDefinitions(value: unknown): readonly ToolDefinition[] {\n try {\n if (!Array.isArray(value)) invalid()\n const length = ownData(value, 'length')\n if (!Number.isSafeInteger(length) || Number(length) < 0 || Number(length) > TOOL_DEFINITION_LIMITS.tools) invalid()\n const captured: ToolDefinition[] = []\n for (let index = 0; index < Number(length); index++) {\n captured.push(captureToolDefinition(ownData(value, String(index)) as ToolDefinition))\n }\n return Object.freeze(captured)\n } catch (error) {\n if (error instanceof AgentSdkError && error.code === TOOL_REGISTRY_ERROR_CODES.INVALID_TOOL) throw error\n throw new AgentSdkError('tool definition list is invalid', TOOL_REGISTRY_ERROR_CODES.INVALID_TOOL, { cause: error })\n }\n}\n\nfunction ownData(source: object, key: string): unknown {\n const descriptor = Object.getOwnPropertyDescriptor(source, key)\n if (descriptor === undefined || !('value' in descriptor)) invalid()\n return descriptor.value\n}\n\nfunction optionalOwnData(source: object, key: string): unknown {\n const descriptor = Object.getOwnPropertyDescriptor(source, key)\n if (descriptor === undefined) return undefined\n if (!('value' in descriptor)) invalid()\n return descriptor.value\n}\n\nfunction boundedOwnText(source: object, key: string, maxBytes: number): string {\n const value = ownData(source, key)\n if (typeof value !== 'string' || value.length === 0 || value.trim() !== value\n || new TextEncoder().encode(value).byteLength > maxBytes) invalid()\n return value\n}\n\nfunction method<Args extends readonly unknown[], Result>(\n receiver: object,\n key: string,\n required: boolean,\n): ToolMethod<Args, Result> | undefined {\n const value: unknown = Reflect.get(receiver, key)\n if (value === undefined && !required) return undefined\n if (typeof value !== 'function') invalid()\n return (...args: Args): Result => Reflect.apply(value, receiver, args) as Result\n}\n\nfunction invalid(): never {\n throw new AgentSdkError('tool definition is invalid', TOOL_REGISTRY_ERROR_CODES.INVALID_TOOL)\n}\n"],"mappings":"2NAUA,SAAgB,EAA4B,EAAmD,CAC7F,GAAI,EACG,OAAO,GAAU,UAAY,OAAO,GAAU,YAAe,IAAU,OAAM,EAAQ,EAC1F,IAAM,EAAW,EACX,EAAO,EAAe,EAAU,OAAQ,EAAuB,SAAS,EACxE,EAAc,EAAe,EAAU,cAAe,EAAuB,gBAAgB,EAC7F,EAAa,EAAmB,EAAQ,EAAU,YAAY,EAAG,CACrE,gBAAiB,EAAuB,aACxC,cAAe,EAAuB,iBACtC,SAAU,EAAuB,YACjC,SAAU,EAAuB,YACjC,YAAa,EAAuB,eACpC,SAAU,EAAuB,WACnC,CAAC,EACK,EAAU,EAA6E,EAAU,UAAW,EAAI,EAChH,EAAQ,EAAwB,EAAU,QAAS,EAAK,EACxD,EAAS,EAA+D,EAAU,SAAU,EAAK,EACjG,EAAO,EAA8D,EAAU,OAAQ,EAAK,EAC5F,EAAoB,EAAwB,EAAU,oBAAqB,EAAK,EAChF,EAAY,EAAgB,EAAU,WAAW,EACnD,IAAc,IAAA,KAAc,OAAO,GAAc,UAAY,CAAC,OAAO,SAAS,CAAS,GAAK,GAAa,IAAI,EAAQ,EAGzH,IAAM,EAAe,EAAgB,EAAU,cAAc,EACzD,IAAiB,IAAA,IAAa,IAAiB,IAAM,EAAQ,EACjE,IAAM,EAAmB,EAAgB,EAAU,kBAAkB,EACjE,IAAqB,IAAA,IAAa,IAAqB,IAAM,EAAQ,EACzE,IAAM,EAAkB,EAAgB,EAAU,iBAAiB,EAGnE,OAFI,IAAoB,IAAA,KAClB,CAAC,OAAO,cAAc,CAAe,GAAK,OAAO,CAAe,EAAI,IAAI,EAAQ,EAC/E,OAAO,OAAO,CAAE,OAAM,cAAa,aACxC,GAAI,IAAU,IAAA,GAAY,CAAC,EAAI,CAAE,OAAM,EAAI,UAC3C,GAAI,IAAW,IAAA,GAAY,CAAC,EAAI,CAAE,QAAO,EACzC,GAAI,IAAS,IAAA,GAAY,CAAC,EAAI,CAAE,MAAK,EACrC,GAAI,IAAc,IAAA,GAAY,CAAC,EAAI,CAAE,UAAW,OAAO,CAAS,CAAE,EAClE,GAAI,IAAsB,IAAA,GAAY,CAAC,EAAI,CAAE,mBAAkB,EAC/D,GAAI,IAAiB,IAAA,GAAY,CAAC,EAAI,CAAE,aAAc,EAAc,EACpE,GAAI,IAAqB,IAAA,GAAY,CAAC,EAAI,CAAE,iBAAkB,EAAc,EAC5E,GAAI,IAAoB,IAAA,GAAY,CAAC,EAAI,CAAE,gBAAiB,OAAO,CAAe,CAAE,
|
|
1
|
+
{"version":3,"file":"capture.js","names":[],"sources":["../../../src/agent/tool/capture.ts"],"sourcesContent":["import { AgentSdkError } from '../../errors/agent-sdk-error.ts'\nimport { snapshotJsonObject } from '../../primitives/json-snapshot.ts'\nimport type { JsonObject, JsonValue } from '../../primitives/json.ts'\nimport type { ContentBlock } from '../../message/content.ts'\nimport type { ToolRunContext, ToolDefinition } from './definition.ts'\nimport { TOOL_DEFINITION_LIMITS, TOOL_REGISTRY_ERROR_CODES } from './config.ts'\n\ntype ToolMethod<Args extends readonly unknown[], Result> = (...args: Args) => Result\n\n/** Capture one stable tool generation while preserving the caller object's receiver. */\nexport function captureToolDefinition<Args>(input: ToolDefinition<Args>): ToolDefinition<Args> {\n try {\n if ((typeof input !== 'object' && typeof input !== 'function') || input === null) invalid()\n const receiver = input as object\n const name = boundedOwnText(receiver, 'name', TOOL_DEFINITION_LIMITS.nameBytes)\n const description = boundedOwnText(receiver, 'description', TOOL_DEFINITION_LIMITS.descriptionBytes)\n const parameters = snapshotJsonObject(ownData(receiver, 'parameters'), {\n maxObjectFields: TOOL_DEFINITION_LIMITS.schemaFields,\n maxArrayItems: TOOL_DEFINITION_LIMITS.schemaArrayItems,\n maxDepth: TOOL_DEFINITION_LIMITS.schemaDepth,\n maxNodes: TOOL_DEFINITION_LIMITS.schemaNodes,\n maxKeyBytes: TOOL_DEFINITION_LIMITS.schemaKeyBytes,\n maxBytes: TOOL_DEFINITION_LIMITS.schemaBytes,\n })\n const outputSchema = optionalOwnData(receiver, 'experimentalOutputSchema') === undefined\n ? undefined\n : snapshotJsonObject(ownData(receiver, 'experimentalOutputSchema'), {\n maxObjectFields: TOOL_DEFINITION_LIMITS.schemaFields,\n maxArrayItems: TOOL_DEFINITION_LIMITS.schemaArrayItems,\n maxDepth: TOOL_DEFINITION_LIMITS.schemaDepth,\n maxNodes: TOOL_DEFINITION_LIMITS.schemaNodes,\n maxKeyBytes: TOOL_DEFINITION_LIMITS.schemaKeyBytes,\n maxBytes: TOOL_DEFINITION_LIMITS.schemaBytes,\n })\n const execute = method<[Args, ToolRunContext], Promise<JsonValue | void> | JsonValue | void>(receiver, 'execute', true)!\n const parse = method<[unknown], Args>(receiver, 'parse', false)\n const render = method<[JsonValue | undefined, Args], readonly ContentBlock[]>(receiver, 'render', false)\n const meta = method<[JsonValue | undefined, Args], JsonObject | undefined>(receiver, 'meta', false)\n const isConcurrencySafe = method<[Args], boolean>(receiver, 'isConcurrencySafe', false)\n const timeoutMs = optionalOwnData(receiver, 'timeoutMs')\n if (timeoutMs !== undefined && (typeof timeoutMs !== 'number' || !Number.isFinite(timeoutMs) || timeoutMs <= 0)) invalid()\n // Fail-closed like every other flag here: only an exact `true` exempts a\n // tool from the turn budget, so a truthy accident cannot quietly widen it.\n const budgetExempt = optionalOwnData(receiver, 'budgetExempt')\n if (budgetExempt !== undefined && budgetExempt !== true) invalid()\n const completionExempt = optionalOwnData(receiver, 'completionExempt')\n if (completionExempt !== undefined && completionExempt !== true) invalid()\n const maxOutputTokens = optionalOwnData(receiver, 'maxOutputTokens')\n if (maxOutputTokens !== undefined\n && (!Number.isSafeInteger(maxOutputTokens) || Number(maxOutputTokens) < 1)) invalid()\n return Object.freeze({ name, description, parameters,\n ...(parse === undefined ? {} : { parse }), execute,\n ...(render === undefined ? {} : { render }),\n ...(meta === undefined ? {} : { meta }),\n ...(timeoutMs === undefined ? {} : { timeoutMs: Number(timeoutMs) }),\n ...(isConcurrencySafe === undefined ? {} : { isConcurrencySafe }),\n ...(budgetExempt === undefined ? {} : { budgetExempt: true as const }),\n ...(completionExempt === undefined ? {} : { completionExempt: true as const }),\n ...(maxOutputTokens === undefined ? {} : { maxOutputTokens: Number(maxOutputTokens) }),\n ...(outputSchema === undefined ? {} : { experimentalOutputSchema: outputSchema }),\n })\n } catch (error) {\n if (error instanceof AgentSdkError && error.code === TOOL_REGISTRY_ERROR_CODES.INVALID_TOOL) throw error\n throw new AgentSdkError('tool definition is invalid', TOOL_REGISTRY_ERROR_CODES.INVALID_TOOL, { cause: error })\n }\n}\n\n/** Capture a dense bounded tool list atomically. */\nexport function captureToolDefinitions(value: unknown): readonly ToolDefinition[] {\n try {\n if (!Array.isArray(value)) invalid()\n const length = ownData(value, 'length')\n if (!Number.isSafeInteger(length) || Number(length) < 0 || Number(length) > TOOL_DEFINITION_LIMITS.tools) invalid()\n const captured: ToolDefinition[] = []\n for (let index = 0; index < Number(length); index++) {\n captured.push(captureToolDefinition(ownData(value, String(index)) as ToolDefinition))\n }\n return Object.freeze(captured)\n } catch (error) {\n if (error instanceof AgentSdkError && error.code === TOOL_REGISTRY_ERROR_CODES.INVALID_TOOL) throw error\n throw new AgentSdkError('tool definition list is invalid', TOOL_REGISTRY_ERROR_CODES.INVALID_TOOL, { cause: error })\n }\n}\n\nfunction ownData(source: object, key: string): unknown {\n const descriptor = Object.getOwnPropertyDescriptor(source, key)\n if (descriptor === undefined || !('value' in descriptor)) invalid()\n return descriptor.value\n}\n\nfunction optionalOwnData(source: object, key: string): unknown {\n const descriptor = Object.getOwnPropertyDescriptor(source, key)\n if (descriptor === undefined) return undefined\n if (!('value' in descriptor)) invalid()\n return descriptor.value\n}\n\nfunction boundedOwnText(source: object, key: string, maxBytes: number): string {\n const value = ownData(source, key)\n if (typeof value !== 'string' || value.length === 0 || value.trim() !== value\n || new TextEncoder().encode(value).byteLength > maxBytes) invalid()\n return value\n}\n\nfunction method<Args extends readonly unknown[], Result>(\n receiver: object,\n key: string,\n required: boolean,\n): ToolMethod<Args, Result> | undefined {\n const value: unknown = Reflect.get(receiver, key)\n if (value === undefined && !required) return undefined\n if (typeof value !== 'function') invalid()\n return (...args: Args): Result => Reflect.apply(value, receiver, args) as Result\n}\n\nfunction invalid(): never {\n throw new AgentSdkError('tool definition is invalid', TOOL_REGISTRY_ERROR_CODES.INVALID_TOOL)\n}\n"],"mappings":"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"}
|
|
@@ -143,6 +143,14 @@ interface ToolDefinition<Args = unknown> extends ToolSchema {
|
|
|
143
143
|
* Never sent to the model.
|
|
144
144
|
*/
|
|
145
145
|
readonly maxOutputTokens?: number;
|
|
146
|
+
/**
|
|
147
|
+
* Experimental: JSON Schema of the structured value this tool returns.
|
|
148
|
+
*
|
|
149
|
+
* Read only by programs that call tools (see `experimentalPrograms`), which
|
|
150
|
+
* validate a child's value against it before using it. Never sent to the
|
|
151
|
+
* provider. Without it, a program receives the value marked as unchecked.
|
|
152
|
+
*/
|
|
153
|
+
readonly experimentalOutputSchema?: JsonObject;
|
|
146
154
|
}
|
|
147
155
|
/** A tool call that succeeded. */
|
|
148
156
|
interface ToolSuccess {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definition.d.ts","names":[],"sources":["../../../src/agent/tool/definition.ts"],"mappings":";;;;;;KA2BK,qBAAqB,iCAAiC;EACzD,kBAAkB,MAAM,OAAO;;;UAIhB;WACN;WACA;;WAEA;;WAEA;;;UAIM,uBAAuB;;WAE7B,QAAQ;;WAER;;;;;;;WAOA,QAAQ;;WAER,SAAS;;;;;;;;EASlB;;;;;;;;EASA,WAAW,2BAA2B;;;;;;;;;KAU5B;;UAGK,eAAe,wBAAwB;;;;;;;;;;;WAW7C,QAAQ,sBAAsB,eAAe;;;;;;;WAQ7C,SAAS,sBACf,MAAM,MAAM,KAAK,iBAClB,QAAQ,oBAAoB;;;;;;;;WAUrB,SAAS,sBACf,OAAO,uBAAuB,MAAM,gBAC5B;;;;;;WAQF,OAAO,sBACb,OAAO,uBAAuB,MAAM,OACrC;;;;;;;;;WAWO;;;;;;;;;WAUA,oBAAoB,sBAAsB,MAAM;;;;;;;;;;;;;;;WAgBhD;;;;;;;;WASA;;;;;;;;;;;;WAaA;;;
|
|
1
|
+
{"version":3,"file":"definition.d.ts","names":[],"sources":["../../../src/agent/tool/definition.ts"],"mappings":";;;;;;KA2BK,qBAAqB,iCAAiC;EACzD,kBAAkB,MAAM,OAAO;;;UAIhB;WACN;WACA;;WAEA;;WAEA;;;UAIM,uBAAuB;;WAE7B,QAAQ;;WAER;;;;;;;WAOA,QAAQ;;WAER,SAAS;;;;;;;;EASlB;;;;;;;;EASA,WAAW,2BAA2B;;;;;;;;;KAU5B;;UAGK,eAAe,wBAAwB;;;;;;;;;;;WAW7C,QAAQ,sBAAsB,eAAe;;;;;;;WAQ7C,SAAS,sBACf,MAAM,MAAM,KAAK,iBAClB,QAAQ,oBAAoB;;;;;;;;WAUrB,SAAS,sBACf,OAAO,uBAAuB,MAAM,gBAC5B;;;;;;WAQF,OAAO,sBACb,OAAO,uBAAuB,MAAM,OACrC;;;;;;;;;WAWO;;;;;;;;;WAUA,oBAAoB,sBAAsB,MAAM;;;;;;;;;;;;;;;WAgBhD;;;;;;;;WASA;;;;;;;;;;;;WAaA;;;;;;;;WASA,2BAA2B;;;UAIrB;WACN;;WAEA,OAAO;;WAEP,kBAAkB;;WAElB,OAAO;;WAEP,6BAA6B;;WAE7B;;;UAIM;WACN;WACA;aACE;aACA;;;WAGF,kBAAkB;WAClB,OAAO;WACP,6BAA6B;;;;;;;WAO7B;;;KAIC,sBAAsB,cAAc;;;;;;;;;;iBAWhC,gBAAgB,OAAO,iCAAiC;;;;;;;;;;;;;;;;;;;;;;;iBA8BxD,WAAW,MAAM,YAAY,eAAe,QAAQ,eAAe;;;;;;;;;;iBAanE,gBACd,MAAM,4BACN,gBACC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definition.js","names":[],"sources":["../../../src/agent/tool/definition.ts"],"sourcesContent":["/**\n * What a tool is, what it receives, and what it produces.\n *\n * Two shape decisions worth stating up front.\n *\n * **The body returns a value, not model-facing text.** `execute` produces a\n * lossless-JSON value; `render` turns it into the blocks the model reads. Keeping\n * them apart means the value can be logged, replayed, asserted on in tests, and\n * handed to a UI, while the model-facing wording stays free to change without\n * invalidating any of that. Simple tools ignore `render` entirely and get a\n * sensible default.\n *\n * **Side channels live on the context, not in the return type.** A tool that wants\n * to end the turn or inject extra context calls `ctx.concludeTurn()` /\n * `ctx.addContext()`. That keeps the common return type trivial — a string or an\n * object — instead of forcing every tool to wrap its result in an envelope.\n *\n * @module ai-agent-sdk/agent/tool/definition\n */\n\nimport type { ToolSchema } from '../../contract/index.ts'\nimport type { ContentBlock } from '../../message/index.ts'\nimport type { ToolCallId } from '../../primitives/index.ts'\nimport type { JsonObject, JsonValue } from '../../primitives/index.ts'\nimport type { SdkLogger } from '../../logging/types.ts'\nimport { captureToolDefinition } from './capture.ts'\n\ntype ReadonlyToolBehavior<Args extends readonly unknown[], Result> = {\n bivarianceHack(...args: Args): Result\n}['bivarianceHack']\n\n/** Where a tool call sits in the run, for logging and diagnostics. */\nexport interface ToolCallPosition {\n readonly runId?: string\n readonly conversationId?: string\n /** 1-based turn number within the conversation. */\n readonly turn: number\n /** 1-based step number within the turn. */\n readonly step: number\n}\n\n/** What a tool body receives besides its arguments. */\nexport interface ToolRunContext extends ToolCallPosition {\n /** Provider-issued id of this call. */\n readonly callId: ToolCallId\n /** The tool's own name, so shared helpers can report which tool called them. */\n readonly toolName: string\n /**\n * Cancellation for this call.\n *\n * A tool that declares `timeoutMs` is ASSERTING that it forwards this signal;\n * the timeout is cooperative and cannot interrupt code that ignores it.\n */\n readonly signal: AbortSignal\n /** Always present on AgentRuntime execution paths; optional for legacy callers. */\n readonly logger?: SdkLogger\n\n /**\n * End the turn after this batch of tool calls commits.\n *\n * For a tool that IS the answer — submitting a final result, handing off to a\n * human. The loop still commits every call in the current batch first, so a\n * parallel sibling's work is never discarded.\n */\n concludeTurn(): void\n\n /**\n * Inject extra context for the next step, beyond this call's result.\n *\n * The blocks become a user message the model sees on the following request. Use\n * it for information the model needs but did not ask for — a reminder that it\n * has repeated itself, a warning that a file changed underneath it.\n */\n addContext(content: string | readonly ContentBlock[]): void\n}\n\n/**\n * How a tool call may be scheduled relative to its siblings.\n *\n * Both reference implementations default to `exclusive` and require an explicit\n * opt-in, because the failure mode of guessing wrong is silent data corruption\n * from two tools mutating the same state, not a visible error.\n */\nexport type ToolExecutionMode = 'parallel' | 'exclusive'\n\n/** A tool the model can call. */\nexport interface ToolDefinition<Args = unknown> extends ToolSchema {\n /**\n * Validate and narrow raw arguments before {@link execute} sees them.\n *\n * The hook exists so this package needs no schema library of its own: plug in\n * zod, valibot, ajv, or hand-written checks. Throwing here produces an\n * `INVALID_ARGUMENTS` result the model can correct.\n *\n * Omitting it means `execute` receives the parsed JSON UNVALIDATED, typed as\n * `Args` on trust. That is fine for a tool whose body checks its own inputs.\n */\n readonly parse?: ReadonlyToolBehavior<[raw: unknown], Args>\n\n /**\n * Do the work.\n * @param args - validated arguments.\n * @param ctx - call identity, cancellation, and the side channels.\n * @returns a lossless-JSON value, or nothing.\n */\n readonly execute: ReadonlyToolBehavior<\n [args: Args, ctx: ToolRunContext],\n Promise<JsonValue | void> | JsonValue | void\n >\n\n /**\n * Turn the returned value into what the model reads.\n *\n * Defaults to {@link renderJsonValue}: a string is passed through verbatim,\n * anything else is pretty-printed JSON. Override to give the model prose, or to\n * return an image block.\n */\n readonly render?: ReadonlyToolBehavior<\n [value: JsonValue | undefined, args: Args],\n readonly ContentBlock[]\n >\n\n /**\n * Metadata for a UI, kept out of what the model sees.\n *\n * Must be lossless JSON — it is persisted with the result.\n */\n readonly meta?: ReadonlyToolBehavior<\n [value: JsonValue | undefined, args: Args],\n JsonObject | undefined\n >\n\n /**\n * Wall-clock bound for one call, enforced cooperatively.\n *\n * NEVER sent to the model. Declaring it is a promise that {@link execute}\n * forwards `ctx.signal`, because the pipeline aborts the signal and waits — it\n * does not abandon the promise, since an orphaned tool would keep mutating state\n * behind the loop's back.\n */\n readonly timeoutMs?: number\n\n /**\n * Whether this call may run alongside its siblings.\n *\n * Fail-closed: only an exact `true` opts in. A throwing or absent classifier\n * means `exclusive`. Return `true` only when the call cannot observe or mutate\n * state another concurrent call touches — a pure read of an immutable source\n * qualifies, \"probably fine\" does not.\n */\n readonly isConcurrencySafe?: ReadonlyToolBehavior<[args: Args], boolean>\n\n /**\n * Exempt this tool from the turn's tool-call budget and its loop guards.\n *\n * For the calls that END work rather than do it: submitting a final result,\n * asking the user a blocking question, handing a task to another agent. A\n * budget exists to stop exploration, and a spent budget that also blocks the\n * only remaining useful action leaves the model with no legal move — the run\n * then dies mid-plan instead of finishing. Both reference harnesses avoid\n * that by never failing a call for a limit at all; this flag is the narrow\n * version of the same rule.\n *\n * Exempt calls still count toward the RUN-level ledger limits, so this\n * cannot be used to escape accounting — only to reach a terminal action.\n */\n readonly budgetExempt?: true\n\n /**\n * Progress/reporting only: calling this tool does not invalidate an accepted\n * deep-mode completion submission. Examples include updating a visible todo\n * list after the work has been verified. Never set this for tools that edit\n * work, obtain new evidence, delegate, or change the user's requirements.\n * This is independent of budgetExempt and is never sent to the model.\n */\n readonly completionExempt?: true\n\n /**\n * Estimated tokens of text this tool's result may put in front of the model.\n *\n * For a tool that knows its own shape: a file reader can ask for room a\n * one-line status check has no use for. The turn's budget still applies, and\n * the STRICTER of the two wins — Codex resolves a model-requested\n * `max_output_tokens` against its deployment policy the same way — so this\n * can lower a tool's share but never raise it past what the host allows.\n *\n * Never sent to the model.\n */\n readonly maxOutputTokens?: number\n}\n\n/** A tool call that succeeded. */\nexport interface ToolSuccess {\n readonly isError: false\n /** The raw value the body returned; omitted when post-policy replaces the result. */\n readonly value: JsonValue | undefined\n /** What the model reads. */\n readonly content: readonly ContentBlock[]\n /** UI metadata, never shown to the model. */\n readonly meta?: JsonObject\n /** Extra context requested via `ctx.addContext`. */\n readonly additionalContext?: readonly ContentBlock[]\n /** Set when the tool called `ctx.concludeTurn()`. */\n readonly concludesTurn?: true\n}\n\n/** A tool call that failed. */\nexport interface ToolFailure {\n readonly isError: true\n readonly error: {\n readonly message: string\n readonly code: string\n }\n /** What the model reads — the failure, phrased for the model. */\n readonly content: readonly ContentBlock[]\n readonly meta?: JsonObject\n readonly additionalContext?: readonly ContentBlock[]\n /**\n * Typed `never`: a failure must not be able to end the turn.\n *\n * Otherwise a denied or crashed tool could silently stop work the user asked\n * for, which is the opposite of what a failure should do.\n */\n readonly concludesTurn?: never\n}\n\n/** The outcome of one tool call. */\nexport type ToolExecutionResult = ToolSuccess | ToolFailure\n\n/**\n * The default {@link ToolDefinition.render}.\n *\n * A string is passed through as-is, because a tool that returns prose meant it.\n * Everything else is indented JSON, which models parse reliably and humans can\n * read in a transcript.\n * @param value - the value the body returned.\n * @returns model-facing blocks.\n */\nexport function renderJsonValue(value: JsonValue | undefined): readonly ContentBlock[] {\n if (value === undefined) return [{ type: 'text', text: '(no output)' }]\n if (typeof value === 'string') {\n return [{ type: 'text', text: value.length === 0 ? '(empty)' : value }]\n }\n return [{ type: 'text', text: JSON.stringify(value, null, 2) }]\n}\n\n/**\n * Author a tool with argument types inferred from `parse`.\n *\n * Returns a detached frozen execution view. Schema and method references are\n * captured once while method calls retain the caller object's receiver.\n *\n * ```ts\n * const getWeather = defineTool({\n * name: 'get_weather',\n * description: 'Current weather for a city.',\n * parameters: {\n * type: 'object',\n * properties: { city: { type: 'string' } },\n * required: ['city'],\n * },\n * parse: raw => WeatherArgs.parse(raw),\n * execute: async ({ city }) => ({ tempC: await lookup(city) }),\n * })\n * ```\n * @param definition - the tool.\n * @returns a new immutable definition, typed.\n */\nexport function defineTool<Args>(definition: ToolDefinition<Args>): ToolDefinition<Args> {\n return captureToolDefinition(definition)\n}\n\n/**\n * Decide how one call may be scheduled.\n *\n * Fail-closed on every uncertainty, including a classifier that throws: a tool\n * whose safety check is broken is not a tool whose safety can be assumed.\n * @param tool - the definition, or `undefined` for an unknown tool.\n * @param args - the parsed arguments.\n * @returns the scheduling mode.\n */\nexport function executionModeOf(\n tool: ToolDefinition | undefined,\n args: unknown,\n): ToolExecutionMode {\n if (tool?.isConcurrencySafe === undefined) return 'exclusive'\n try {\n return tool.isConcurrencySafe(args) === true ? 'parallel' : 'exclusive'\n } catch {\n return 'exclusive'\n }\n}\n"],"mappings":"qDA8OA,SAAgB,EAAgB,EAAuD,CAKrF,OAJI,IAAU,IAAA,GAAkB,CAAC,CAAE,KAAM,OAAQ,KAAM,aAAc,CAAC,EAClE,OAAO,GAAU,SACZ,CAAC,CAAE,KAAM,OAAQ,KAAM,EAAM,SAAW,EAAI,UAAY,CAAM,CAAC,EAEjE,CAAC,CAAE,KAAM,OAAQ,KAAM,KAAK,UAAU,EAAO,KAAM,CAAC,CAAE,CAAC,CAChE,CAwBA,SAAgB,EAAiB,EAAwD,CACvF,OAAO,EAAsB,CAAU,CACzC,CAWA,SAAgB,EACd,EACA,EACmB,CACnB,GAAI,GAAM,oBAAsB,IAAA,GAAW,MAAO,YAClD,GAAI,CACF,OAAO,EAAK,kBAAkB,CAAI,IAAM,GAAO,WAAa,WAC9D,MAAQ,CACN,MAAO,WACT,CACF"}
|
|
1
|
+
{"version":3,"file":"definition.js","names":[],"sources":["../../../src/agent/tool/definition.ts"],"sourcesContent":["/**\n * What a tool is, what it receives, and what it produces.\n *\n * Two shape decisions worth stating up front.\n *\n * **The body returns a value, not model-facing text.** `execute` produces a\n * lossless-JSON value; `render` turns it into the blocks the model reads. Keeping\n * them apart means the value can be logged, replayed, asserted on in tests, and\n * handed to a UI, while the model-facing wording stays free to change without\n * invalidating any of that. Simple tools ignore `render` entirely and get a\n * sensible default.\n *\n * **Side channels live on the context, not in the return type.** A tool that wants\n * to end the turn or inject extra context calls `ctx.concludeTurn()` /\n * `ctx.addContext()`. That keeps the common return type trivial — a string or an\n * object — instead of forcing every tool to wrap its result in an envelope.\n *\n * @module ai-agent-sdk/agent/tool/definition\n */\n\nimport type { ToolSchema } from '../../contract/index.ts'\nimport type { ContentBlock } from '../../message/index.ts'\nimport type { ToolCallId } from '../../primitives/index.ts'\nimport type { JsonObject, JsonValue } from '../../primitives/index.ts'\nimport type { SdkLogger } from '../../logging/types.ts'\nimport { captureToolDefinition } from './capture.ts'\n\ntype ReadonlyToolBehavior<Args extends readonly unknown[], Result> = {\n bivarianceHack(...args: Args): Result\n}['bivarianceHack']\n\n/** Where a tool call sits in the run, for logging and diagnostics. */\nexport interface ToolCallPosition {\n readonly runId?: string\n readonly conversationId?: string\n /** 1-based turn number within the conversation. */\n readonly turn: number\n /** 1-based step number within the turn. */\n readonly step: number\n}\n\n/** What a tool body receives besides its arguments. */\nexport interface ToolRunContext extends ToolCallPosition {\n /** Provider-issued id of this call. */\n readonly callId: ToolCallId\n /** The tool's own name, so shared helpers can report which tool called them. */\n readonly toolName: string\n /**\n * Cancellation for this call.\n *\n * A tool that declares `timeoutMs` is ASSERTING that it forwards this signal;\n * the timeout is cooperative and cannot interrupt code that ignores it.\n */\n readonly signal: AbortSignal\n /** Always present on AgentRuntime execution paths; optional for legacy callers. */\n readonly logger?: SdkLogger\n\n /**\n * End the turn after this batch of tool calls commits.\n *\n * For a tool that IS the answer — submitting a final result, handing off to a\n * human. The loop still commits every call in the current batch first, so a\n * parallel sibling's work is never discarded.\n */\n concludeTurn(): void\n\n /**\n * Inject extra context for the next step, beyond this call's result.\n *\n * The blocks become a user message the model sees on the following request. Use\n * it for information the model needs but did not ask for — a reminder that it\n * has repeated itself, a warning that a file changed underneath it.\n */\n addContext(content: string | readonly ContentBlock[]): void\n}\n\n/**\n * How a tool call may be scheduled relative to its siblings.\n *\n * Both reference implementations default to `exclusive` and require an explicit\n * opt-in, because the failure mode of guessing wrong is silent data corruption\n * from two tools mutating the same state, not a visible error.\n */\nexport type ToolExecutionMode = 'parallel' | 'exclusive'\n\n/** A tool the model can call. */\nexport interface ToolDefinition<Args = unknown> extends ToolSchema {\n /**\n * Validate and narrow raw arguments before {@link execute} sees them.\n *\n * The hook exists so this package needs no schema library of its own: plug in\n * zod, valibot, ajv, or hand-written checks. Throwing here produces an\n * `INVALID_ARGUMENTS` result the model can correct.\n *\n * Omitting it means `execute` receives the parsed JSON UNVALIDATED, typed as\n * `Args` on trust. That is fine for a tool whose body checks its own inputs.\n */\n readonly parse?: ReadonlyToolBehavior<[raw: unknown], Args>\n\n /**\n * Do the work.\n * @param args - validated arguments.\n * @param ctx - call identity, cancellation, and the side channels.\n * @returns a lossless-JSON value, or nothing.\n */\n readonly execute: ReadonlyToolBehavior<\n [args: Args, ctx: ToolRunContext],\n Promise<JsonValue | void> | JsonValue | void\n >\n\n /**\n * Turn the returned value into what the model reads.\n *\n * Defaults to {@link renderJsonValue}: a string is passed through verbatim,\n * anything else is pretty-printed JSON. Override to give the model prose, or to\n * return an image block.\n */\n readonly render?: ReadonlyToolBehavior<\n [value: JsonValue | undefined, args: Args],\n readonly ContentBlock[]\n >\n\n /**\n * Metadata for a UI, kept out of what the model sees.\n *\n * Must be lossless JSON — it is persisted with the result.\n */\n readonly meta?: ReadonlyToolBehavior<\n [value: JsonValue | undefined, args: Args],\n JsonObject | undefined\n >\n\n /**\n * Wall-clock bound for one call, enforced cooperatively.\n *\n * NEVER sent to the model. Declaring it is a promise that {@link execute}\n * forwards `ctx.signal`, because the pipeline aborts the signal and waits — it\n * does not abandon the promise, since an orphaned tool would keep mutating state\n * behind the loop's back.\n */\n readonly timeoutMs?: number\n\n /**\n * Whether this call may run alongside its siblings.\n *\n * Fail-closed: only an exact `true` opts in. A throwing or absent classifier\n * means `exclusive`. Return `true` only when the call cannot observe or mutate\n * state another concurrent call touches — a pure read of an immutable source\n * qualifies, \"probably fine\" does not.\n */\n readonly isConcurrencySafe?: ReadonlyToolBehavior<[args: Args], boolean>\n\n /**\n * Exempt this tool from the turn's tool-call budget and its loop guards.\n *\n * For the calls that END work rather than do it: submitting a final result,\n * asking the user a blocking question, handing a task to another agent. A\n * budget exists to stop exploration, and a spent budget that also blocks the\n * only remaining useful action leaves the model with no legal move — the run\n * then dies mid-plan instead of finishing. Both reference harnesses avoid\n * that by never failing a call for a limit at all; this flag is the narrow\n * version of the same rule.\n *\n * Exempt calls still count toward the RUN-level ledger limits, so this\n * cannot be used to escape accounting — only to reach a terminal action.\n */\n readonly budgetExempt?: true\n\n /**\n * Progress/reporting only: calling this tool does not invalidate an accepted\n * deep-mode completion submission. Examples include updating a visible todo\n * list after the work has been verified. Never set this for tools that edit\n * work, obtain new evidence, delegate, or change the user's requirements.\n * This is independent of budgetExempt and is never sent to the model.\n */\n readonly completionExempt?: true\n\n /**\n * Estimated tokens of text this tool's result may put in front of the model.\n *\n * For a tool that knows its own shape: a file reader can ask for room a\n * one-line status check has no use for. The turn's budget still applies, and\n * the STRICTER of the two wins — Codex resolves a model-requested\n * `max_output_tokens` against its deployment policy the same way — so this\n * can lower a tool's share but never raise it past what the host allows.\n *\n * Never sent to the model.\n */\n readonly maxOutputTokens?: number\n\n /**\n * Experimental: JSON Schema of the structured value this tool returns.\n *\n * Read only by programs that call tools (see `experimentalPrograms`), which\n * validate a child's value against it before using it. Never sent to the\n * provider. Without it, a program receives the value marked as unchecked.\n */\n readonly experimentalOutputSchema?: JsonObject\n}\n\n/** A tool call that succeeded. */\nexport interface ToolSuccess {\n readonly isError: false\n /** The raw value the body returned; omitted when post-policy replaces the result. */\n readonly value: JsonValue | undefined\n /** What the model reads. */\n readonly content: readonly ContentBlock[]\n /** UI metadata, never shown to the model. */\n readonly meta?: JsonObject\n /** Extra context requested via `ctx.addContext`. */\n readonly additionalContext?: readonly ContentBlock[]\n /** Set when the tool called `ctx.concludeTurn()`. */\n readonly concludesTurn?: true\n}\n\n/** A tool call that failed. */\nexport interface ToolFailure {\n readonly isError: true\n readonly error: {\n readonly message: string\n readonly code: string\n }\n /** What the model reads — the failure, phrased for the model. */\n readonly content: readonly ContentBlock[]\n readonly meta?: JsonObject\n readonly additionalContext?: readonly ContentBlock[]\n /**\n * Typed `never`: a failure must not be able to end the turn.\n *\n * Otherwise a denied or crashed tool could silently stop work the user asked\n * for, which is the opposite of what a failure should do.\n */\n readonly concludesTurn?: never\n}\n\n/** The outcome of one tool call. */\nexport type ToolExecutionResult = ToolSuccess | ToolFailure\n\n/**\n * The default {@link ToolDefinition.render}.\n *\n * A string is passed through as-is, because a tool that returns prose meant it.\n * Everything else is indented JSON, which models parse reliably and humans can\n * read in a transcript.\n * @param value - the value the body returned.\n * @returns model-facing blocks.\n */\nexport function renderJsonValue(value: JsonValue | undefined): readonly ContentBlock[] {\n if (value === undefined) return [{ type: 'text', text: '(no output)' }]\n if (typeof value === 'string') {\n return [{ type: 'text', text: value.length === 0 ? '(empty)' : value }]\n }\n return [{ type: 'text', text: JSON.stringify(value, null, 2) }]\n}\n\n/**\n * Author a tool with argument types inferred from `parse`.\n *\n * Returns a detached frozen execution view. Schema and method references are\n * captured once while method calls retain the caller object's receiver.\n *\n * ```ts\n * const getWeather = defineTool({\n * name: 'get_weather',\n * description: 'Current weather for a city.',\n * parameters: {\n * type: 'object',\n * properties: { city: { type: 'string' } },\n * required: ['city'],\n * },\n * parse: raw => WeatherArgs.parse(raw),\n * execute: async ({ city }) => ({ tempC: await lookup(city) }),\n * })\n * ```\n * @param definition - the tool.\n * @returns a new immutable definition, typed.\n */\nexport function defineTool<Args>(definition: ToolDefinition<Args>): ToolDefinition<Args> {\n return captureToolDefinition(definition)\n}\n\n/**\n * Decide how one call may be scheduled.\n *\n * Fail-closed on every uncertainty, including a classifier that throws: a tool\n * whose safety check is broken is not a tool whose safety can be assumed.\n * @param tool - the definition, or `undefined` for an unknown tool.\n * @param args - the parsed arguments.\n * @returns the scheduling mode.\n */\nexport function executionModeOf(\n tool: ToolDefinition | undefined,\n args: unknown,\n): ToolExecutionMode {\n if (tool?.isConcurrencySafe === undefined) return 'exclusive'\n try {\n return tool.isConcurrencySafe(args) === true ? 'parallel' : 'exclusive'\n } catch {\n return 'exclusive'\n }\n}\n"],"mappings":"qDAuPA,SAAgB,EAAgB,EAAuD,CAKrF,OAJI,IAAU,IAAA,GAAkB,CAAC,CAAE,KAAM,OAAQ,KAAM,aAAc,CAAC,EAClE,OAAO,GAAU,SACZ,CAAC,CAAE,KAAM,OAAQ,KAAM,EAAM,SAAW,EAAI,UAAY,CAAM,CAAC,EAEjE,CAAC,CAAE,KAAM,OAAQ,KAAM,KAAK,UAAU,EAAO,KAAM,CAAC,CAAE,CAAC,CAChE,CAwBA,SAAgB,EAAiB,EAAwD,CACvF,OAAO,EAAsB,CAAU,CACzC,CAWA,SAAgB,EACd,EACA,EACmB,CACnB,GAAI,GAAM,oBAAsB,IAAA,GAAW,MAAO,YAClD,GAAI,CACF,OAAO,EAAK,kBAAkB,CAAI,IAAM,GAAO,WAAa,WAC9D,MAAQ,CACN,MAAO,WACT,CACF"}
|