@zibby/agent-workflow 2.0.13 → 2.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/collaboration.js +2 -2
- package/dist/graph-compiler.js +20 -20
- package/dist/graph.js +19 -19
- package/dist/index.js +28 -28
- package/dist/node.js +16 -16
- package/dist/sub-graph-executor.d.ts +7 -1
- package/dist/sub-graph-executor.js +1 -1
- package/dist/timeline.d.ts +9 -0
- package/dist/timeline.js +10 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -243,6 +243,20 @@ g.addNode('audit', {
|
|
|
243
243
|
});
|
|
244
244
|
```
|
|
245
245
|
|
|
246
|
+
When a custom fan-out node also needs to persist an exact reference to the
|
|
247
|
+
child execution, the direct dispatcher can return identity with the same
|
|
248
|
+
projected value:
|
|
249
|
+
|
|
250
|
+
```js
|
|
251
|
+
const { executionId, output } = await dispatchSubgraph('deep-audit', {
|
|
252
|
+
input: { ticketId },
|
|
253
|
+
output: 'auditResult',
|
|
254
|
+
includeExecutionMetadata: true,
|
|
255
|
+
});
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
This is synchronous-only metadata; async dispatch already returns `jobId`.
|
|
259
|
+
|
|
246
260
|
**Errors are typed** so parents can branch:
|
|
247
261
|
|
|
248
262
|
| `err.code` | When |
|
package/dist/collaboration.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var oe=()=>{},Pe={debug:oe,info:oe,warn:(...e)=>console.warn("[workflow]",...e),error:(...e)=>console.error("[workflow]",...e)},Y=Symbol.for("@zibby/agent-workflow.logger");globalThis[Y]||(globalThis[Y]={impl:Pe});var H=globalThis[Y];var E={debug:(...e)=>H.impl.debug?.(...e),info:(...e)=>H.impl.info?.(...e),warn:(...e)=>H.impl.warn?.(...e),error:(...e)=>H.impl.error?.(...e)};import{mkdirSync as Ce,existsSync as C,statSync as he,readdirSync as ge,rmSync as De}from"node:fs";import{spawn as le}from"node:child_process";import{join as x}from"node:path";import{pathToFileURL as Ge}from"node:url";import{AsyncLocalStorage as Le}from"node:async_hooks";import{AsyncLocalStorage as $e}from"node:async_hooks";var V=new $e;function G(){let e=V.getStore();return e||Object.freeze({executionId:process.env.EXECUTION_ID||null,parentExecutionId:process.env.PARENT_EXECUTION_ID||null,depth:0,conversationId:process.env.ZIBBY_CONVERSATION_ID||null,dispatchMode:process.env.DISPATCH_MODE||null,agent:null,signal:null})}function ae(e,t){let n=V.getStore()||G(),r=Object.freeze({executionId:e.executionId,parentExecutionId:e.parentExecutionId??n.executionId??null,depth:(n.depth||0)+(e.executionId!==n.executionId?1:0),conversationId:e.conversationId!==void 0?e.conversationId:n.conversationId??null,dispatchMode:e.dispatchMode??null,agent:e.agent!==void 0?e.agent:n.agent??null,signal:e.signal!==void 0?e.signal:n.signal??null});return V.run(r,t)}var Z=new Map,Q=new Map,ie=new Map;function se(e,t,n={}){if(!e||typeof e!="string")throw new Error("subgraph-registry.register: name required");if(typeof t!="function")throw new Error("subgraph-registry.register: factory must be a function");Z.set(e,t),Q.set(e,"ready"),ie.set(e,{...n,cachedAt:Date.now()})}function ce(e,t){Q.set(e,"failed"),ie.set(e,{error:t?.message||String(t),failedAt:Date.now()}),Z.delete(e)}function ue(e){return Q.get(e)==="ready"?Z.get(e):null}function L(e,t,n=process.env){let r=Number(n[e]);return Number.isFinite(r)&&r>0?Math.min(12e4,Math.max(1e3,Math.floor(r))):t}function F(e,t){return{signal:AbortSignal.timeout(e),label:`after ${e}ms (${t})`}}function k(e,t){return F(L(e,t),e)}function M(e){return e?.name==="TimeoutError"||e?.name==="AbortError"}function U(e,t){return t.aborted?(Promise.resolve(e).catch(()=>{}),Promise.reject(t.reason)):new Promise((n,r)=>{let o=()=>r(t.reason);t.addEventListener("abort",o,{once:!0}),Promise.resolve(e).then(a=>{t.removeEventListener("abort",o),n(a)},a=>{t.removeEventListener("abort",o),r(a)})})}var q=process.env.ZIBBY_SUBGRAPH_CACHE_DIR||"/tmp/zibby/subgraphs";function ke(){return`node${(process.versions?.node||"").split(".")[0]||"unknown"}-${process.platform}-${process.arch}`}var w=class extends Error{constructor(t,n){super(`in-process sub-graph fallback: ${t}${n?` (${n})`:""}`),this.fallback=!0,this.reason=t,this.detail=n||null,this.name="SubgraphFallback"}};function z(e,t,n,r="timeout",o=null){let a=new Error(`Sub-graph '${e}' (${t}) timed out after ${Math.round(n/1e3)}s (last status: ${r})`+(o?`; the status API was unreachable on the last attempt (${o})`:""));return a.code="SUBGRAPH_TIMEOUT",a.subgraphJobId=t,a.subgraphStatus=r,o&&(a.subgraphTransportError=o),a}function Ne(e,t=process.env,n=process.uptime()){let r=Number.isFinite(e)?Number(e):null,o=Number(t&&t.MAX_WORKFLOW_DURATION_MS),a=Number.isFinite(n)&&n>0?n*1e3:0,i=Number.isFinite(o)&&o>0?o-a:null;if(r===null&&i===null)return null;let s=r===null?i:i===null?r:Math.min(r,i);return Math.max(1,s)}function je(e,t){let n=new AbortController,r=!1,o=null,a=()=>{n.abort(e?.reason)};return e&&(e.aborted?a():e.addEventListener("abort",a,{once:!0})),t!=null&&!n.signal.aborted&&(o=setTimeout(()=>{r=!0,n.abort(new Error("sub-graph deadline exceeded"))},t),typeof o.unref=="function"&&o.unref()),{signal:n.signal,timedOut:()=>r,dispose(){o&&(clearTimeout(o),o=null),e&&e.removeEventListener("abort",a)}}}var de=new Le,pe=Promise.resolve();async function He(e,t){let n=e&&typeof e=="object"&&!Array.isArray(e)?Object.entries(e).filter(([i,s])=>typeof i=="string"&&i&&typeof s=="string"):[];if(n.length===0)return t();let r=de.getStore()===!0,o=null;if(!r){let i=pe;pe=new Promise(s=>{o=s}),await i}let a=new Map;try{for(let[i,s]of n)a.set(i,Object.prototype.hasOwnProperty.call(process.env,i)?process.env[i]:void 0),process.env[i]=s;return E.debug(`[in-process subgraph] scoped ${n.length} child env var(s)${r?" (nested)":""}`),await de.run(!0,t)}finally{for(let[i,s]of a)s===void 0?delete process.env[i]:process.env[i]=s;o&&o()}}function Fe(){let e=(process.env.SUBGRAPH_INTERNAL_URL||"").replace(/\/$/,""),t=(process.env.PROGRESS_API_URL||"").replace(/\/executions\/?$/,""),n=e||t,r=process.env.PROJECT_ID,o=process.env.PROJECT_API_TOKEN;if(!n||!r||!o)throw new w("env","SUBGRAPH_INTERNAL_URL/PROGRESS_API_URL/PROJECT_ID/PROJECT_API_TOKEN missing");return{apiBase:n,projectId:r,authToken:o}}async function qe({apiBase:e,authToken:t,body:n}){let r=k("SUBGRAPH_TRIGGER_TIMEOUT_MS",3e4),o;try{o=await fetch(`${e}/internal/subgraph/begin`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify(n),signal:r.signal})}catch(i){throw new w(M(i)?"begin-timeout":"network",M(i)?`begin TIMED OUT ${r.label}`:`begin fetch failed: ${i.message}`)}let a=null;try{a=await o.json()}catch(i){if(M(i))throw new w("begin-timeout",`begin body read TIMED OUT ${r.label}`)}if(!o.ok){if(o.status===404){let i=new Error(`Sub-graph child '${n.childWorkflowType}' not found in project`);throw i.code="SUBGRAPH_NOT_FOUND",i.status=404,i}if(o.status===429){let i=a?.quotaInfo||{},s=new Error(`Sub-graph blocked by quota (${i.used??"?"}/${i.limit??"?"} on ${i.planId||"plan"})`);throw s.code="SUBGRAPH_QUOTA_EXCEEDED",s.status=429,s.quotaInfo=i,s}if(o.status===400&&a?.validationErrors){let i=new Error(`Sub-graph rejected input: ${a?.error||a?.message||"validation failed"}`);throw i.code="SUBGRAPH_INVALID_INPUT",i.status=400,i.validationErrors=a.validationErrors,i.missing=a.missing,i}throw new w("begin-status",`begin returned ${o.status}`)}return a?.data||a}async function B({apiBase:e,authToken:t,payload:n}){let r=k("SUBGRAPH_TRIGGER_TIMEOUT_MS",3e4);try{let o=await fetch(`${e}/internal/subgraph/finalize`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify(n),signal:r.signal});o.ok||E.warn(`[in-process subgraph] finalize returned ${o.status} for ${n.childExecutionId}`)}catch(o){E.warn(`[in-process subgraph] finalize ${M(o)?`TIMED OUT ${r.label}`:`failed: ${o.message}`}`)}}async function ze(e,t){let n=x(t,".ready"),r=x(t,"graph.mjs");if(C(n)&&C(r))return;Ce(t,{recursive:!0});let o=x(t,".lock"),a=!1;try{let{openSync:i,closeSync:s}=await import("node:fs"),h=i(o,"wx");s(h),a=!0}catch(i){if(i.code!=="EEXIST")throw i}if(!a){let i=Date.now()+3e4;for(;Date.now()<i;){if(C(n)&&C(r))return;await new Promise(s=>setTimeout(s,100))}throw new w("bundle-extract-timeout","sibling extract did not complete within 30s")}try{await new Promise((h,p)=>{let S=Math.ceil(1e4/1e3),v=Math.ceil(L("SUBGRAPH_BUNDLE_TIMEOUT_MS",6e4)/1e3),y=le("curl",["-fsSL","--connect-timeout",String(S),"--max-time",String(v),e],{stdio:["ignore","pipe","inherit"]}),f=le("tar",["-xzf","-","-C",t],{stdio:["pipe","inherit","inherit"]});y.stdout.pipe(f.stdin);let m,g,O=()=>{if(m!==void 0&&g!==void 0){if(m!==0)return p(new Error(`curl exited ${m}`));if(g!==0)return p(new Error(`tar exited ${g}`));h()}};y.on("close",A=>{m=A,O()}),f.on("close",A=>{g=A,O()}),y.on("error",p),f.on("error",p)});let{writeFileSync:i,unlinkSync:s}=await import("node:fs");i(n,"");try{s(o)}catch{}}catch(i){try{let{unlinkSync:s}=await import("node:fs");s(o)}catch{}throw new w("bundle-extract-failed",i.message)}}async function Je(e){let t=x(e,"graph.mjs");if(!C(t))throw new w("entry-missing",`graph.mjs missing under ${e}`);let n;try{n=await import(Ge(t).href)}catch(o){throw new w("import-failed",`${o?.code||o?.name||"unknown"}: ${o.message}`)}let r=n.default||Object.values(n).find(o=>typeof o=="function"&&o.prototype?.buildGraph);if(!r)throw new w("entry-class-missing","no buildGraph() class export found");return r}async function fe(e,t={}){if(!e||typeof e!="string")throw new Error("runInProcessSubgraph: workflowName (string) is required");let n=G(),r;try{r=Fe()}catch(c){throw c}E.debug(`[in-process subgraph] begin '${e}' parent=${n.executionId||"<root>"}`);let o=await qe({apiBase:r.apiBase,authToken:r.authToken,body:{parentExecutionId:n.executionId,childWorkflowType:e,input:t.input||{},...t.conversationId?{conversationId:t.conversationId}:{}}}),{childExecutionId:a,runtimeTag:i,bundlePresignedUrl:s,sourcesPresignedUrl:h,workflowVersion:p,workflowUuid:S,bundleReady:v,nodeConfigs:y}=o,f=ke();if(i&&i!==f)throw await B({apiBase:r.apiBase,authToken:r.authToken,payload:{childExecutionId:a,discard:!0}}),new w("runtime-mismatch",`${f} vs ${i}`);if(!v||!s)throw await B({apiBase:r.apiBase,authToken:r.authToken,payload:{childExecutionId:a,discard:!0}}),new w("no-bundle","workflow bundle not built yet");let m=ue(e);if(!m){let c=x(q,`${S}@${p||"0"}`);try{await ze(s,c);try{We()}catch{}}catch(d){throw d.fallback&&await B({apiBase:r.apiBase,authToken:r.authToken,payload:{childExecutionId:a,status:"failed",error:{message:d.message,code:d.reason}}}),d}try{m=await Je(c),se(e,m,{workflowUuid:S,version:p,runtimeTag:i,cacheDir:c})}catch(d){throw ce(e,d),await B({apiBase:r.apiBase,authToken:r.authToken,payload:{childExecutionId:a,status:"failed",error:{message:d.message,code:d.reason||"IMPORT_FAILED"}}}),d.fallback?d:new w("import-failed",d.message)}}let g=Date.now(),O=o.env&&typeof o.env=="object"&&!Array.isArray(o.env)?o.env:null,A=y&&typeof y=="object"&&!Array.isArray(y)&&Object.keys(y).length>0,P={...t.input||{},...A?{nodeConfigs:y}:{}},_=Ne(t.timeoutMs),R=je(t.signal,_),I,u;try{I=await He(O,async()=>{let d=await(typeof m=="function"&&m.prototype?.buildGraph?new m:m).buildGraph();return ae({executionId:a,parentExecutionId:n.executionId,conversationId:t.conversationId!==void 0?t.conversationId:n.conversationId,dispatchMode:"inprocess"},()=>d.run(t.parentAgent,P,{signal:R.signal}))}),u=I&&typeof I=="object"&&"state"in I?I.state:I}catch(c){let d=R.timedOut();throw await B({apiBase:r.apiBase,authToken:r.authToken,payload:{childExecutionId:a,status:d?"timeout":"failed",error:d?{message:`aborted at the declared ${Math.round(_/1e3)}s sub-graph budget`,code:"SUBGRAPH_TIMEOUT"}:{message:c.message,code:c.code||"CHILD_THREW",stack:c.stack},durationMs:Date.now()-g}}),d?z(e,a,_):c}finally{R.dispose()}if(I&&typeof I=="object"&&I.stoppedExternally){let c=R.timedOut();if(await B({apiBase:r.apiBase,authToken:r.authToken,payload:{childExecutionId:a,status:c?"timeout":"canceled",finalState:u,durationMs:Date.now()-g}}),c)throw z(e,a,_);let d=new Error(`Sub-graph '${e}' canceled by parent abort`);throw d.code="SUBGRAPH_CANCELED",d.subgraphJobId=a,d}return await B({apiBase:r.apiBase,authToken:r.authToken,payload:{childExecutionId:a,status:"completed",finalState:u,durationMs:Date.now()-g}}),{finalState:u,executionId:a}}function Xe(e){let t=0,n=[e];for(;n.length;){let r=n.pop(),o;try{o=he(r)}catch{continue}if(o.isDirectory()){let a;try{a=ge(r)}catch{continue}for(let i of a)n.push(x(r,i))}else t+=o.size}return t}function We({cap:e=Number(process.env.ZIBBY_SUBGRAPH_CACHE_CAP_BYTES||2*1024*1024*1024)}={}){try{if(!C(q))return{evicted:0,freedBytes:0};let t=ge(q),n=[],r=0;for(let s of t){let h=x(q,s),p;try{p=he(h)}catch{continue}let S=p.isDirectory()?Xe(h):p.size;r+=S,n.push({name:s,full:h,size:S,mtimeMs:p.mtimeMs})}if(r<=e)return{evicted:0,freedBytes:0,totalBytes:r};n.sort((s,h)=>s.mtimeMs-h.mtimeMs);let o=Math.floor(e*.7),a=0,i=0;for(let s of n){if(r-a<=o)break;if(!C(x(s.full,".lock")))try{De(s.full,{recursive:!0,force:!0}),a+=s.size,i+=1}catch(h){E.debug(`[sub-graph cache] evict skip ${s.name}: ${h.message}`)}}return i>0&&E.info(`[sub-graph cache] evicted ${i} entr(y/ies), freed ${(a/1024/1024).toFixed(1)}MB`),{evicted:i,freedBytes:a,totalBytes:r-a}}catch(t){return E.debug(`[sub-graph cache] evict failed: ${t.message}`),{evicted:0,freedBytes:0}}}var Ye=2e3,Ve=600*1e3,Ze=new Set(["completed","failed","canceled","cancelled","timeout"]);var me=()=>k("SUBGRAPH_TRIGGER_TIMEOUT_MS",3e4);function Qe(e){let t=L("SUBGRAPH_POLL_TIMEOUT_MS",15e3),n=Math.max(1,Math.min(t,e-Date.now()));return F(n,"SUBGRAPH_POLL_TIMEOUT_MS")}function be(){let e=process.env.PROGRESS_API_URL;if(!e)throw new Error("Sub-graph dispatch requires PROGRESS_API_URL env var (set automatically on cloud runs). Sub-graphs are not supported in local in-process runs yet \u2014 deploy the parent and child to cloud.");return e.replace(/\/executions\/?$/,"")}function Ie(){let e=process.env.PROJECT_ID;if(!e)throw new Error("Sub-graph dispatch requires PROJECT_ID env var.");return e}function Te(){let e=process.env.PROJECT_API_TOKEN;if(!e)throw new Error("Sub-graph dispatch requires PROJECT_API_TOKEN env var.");return e}function Ee(){return process.env.EXECUTION_ID||null}function ye(e,t){return t==null?e:typeof t=="function"?t(e):typeof t=="string"?t.split(".").reduce((n,r)=>n==null?n:n[r],e):e}async function et(e,t={}){if(!e||typeof e!="string")throw new Error("dispatchSubgraph: workflowName (string) is required");let n=G();t.parentAgent==null&&n.agent&&(t.parentAgent=n.agent),t.signal==null&&n.signal&&(t.signal=n.signal);let r=Number(process.env.ZIBBY_SUBGRAPH_MAX_DEPTH||10);if((n.depth||0)>=r)throw new Error(`dispatchSubgraph('${e}'): sub-graph depth ${n.depth} reached cap of ${r}. Restructure the graph or raise ZIBBY_SUBGRAPH_MAX_DEPTH.`);let o=Number.isFinite(t.timeoutMs)?t.timeoutMs:Ve;if(process.env.ZIBBY_INPROCESS_SUBGRAPH!=="0"&&!t.async&&!t.participantBindingId)try{E.debug(`[sub-graph] trying in-process for '${e}'`);let{finalState:u}=await fe(e,{input:t.input,conversationId:t.conversationId,signal:t.signal,parentAgent:t.parentAgent,timeoutMs:o}),c=ye(u,t.output);return E.info(`[sub-graph] '${e}' completed in-process`),c}catch(u){if(u instanceof w||u?.fallback)E.info(`[sub-graph] in-process fallback for '${e}': ${u.reason||"unknown"} \u2014 using HTTP`);else throw u}let a=be(),i=Ie(),s=Te(),h=Ee(),p=t.participantBindingId?"participant":e,S=`${a}/projects/${encodeURIComponent(i)}/workflows/${encodeURIComponent(p)}/trigger`,v={input:t.input||{},...h?{parentExecutionId:h}:{},...!t.async&&typeof t.output=="string"&&t.output.trim()?{resultPath:t.output.trim()}:{},...t.conversationId?{conversationId:t.conversationId}:{},...typeof t.idempotencyKey=="string"&&t.idempotencyKey?{idempotencyKey:t.idempotencyKey.slice(0,200)}:{},...t.participantBindingId?{participantBindingId:t.participantBindingId}:{},...t.protocolId?{protocolId:t.protocolId}:{}};E.info(`[sub-graph] dispatching '${e}' (${t.async?"async":"sync"}) from parent ${h||"<none>"}`);let y=me(),f;try{f=await U(fetch(S,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify(v),signal:y.signal}),y.signal)}catch(u){if(!M(u))throw u;let c=new Error(`Sub-graph '${e}' trigger TIMED OUT ${y.label} \u2014 the platform never answered, so no child was dispatched and nothing needs reconciling.`);throw c.code="SUBGRAPH_TRIGGER_TIMEOUT",c.subgraph=e,c.timedOut=!0,c.cause=u,c}if(!f.ok){let u=null,c="";try{u=await U(f.json(),y.signal),c=u?.error||u?.message||JSON.stringify(u)}catch{c=await U(f.text(),y.signal).catch(()=>"")}if(f.status===429){let T=u?.quotaInfo||{},b=new Error(`Sub-graph '${e}' blocked by execution quota (${T.used??"?"}/${T.limit??"?"} on plan ${T.planId||"unknown"}). Sub-workflow runs count toward the same monthly cap as user-triggered runs.`);throw b.code="SUBGRAPH_QUOTA_EXCEEDED",b.status=429,b.subgraph=e,b.quotaInfo=T,b}if(f.status===400){let T=new Error(`Sub-graph '${e}' rejected input: ${c}`);throw T.code="SUBGRAPH_INVALID_INPUT",T.status=400,T.subgraph=e,T.validationErrors=u?.validationErrors||null,T.missing=u?.missing||null,T}let d=new Error(`Sub-graph '${e}' trigger rejected (${f.status}): ${c}`);throw d.code="SUBGRAPH_TRIGGER_FAILED",d.status=f.status,d.subgraph=e,d}let m;try{m=await U(f.json(),y.signal)}catch(u){if(!M(u))throw u;let c=new Error(`Sub-graph '${e}' trigger body read TIMED OUT ${y.label} \u2014 the platform accepted the dispatch but never finished answering, so its jobId is unknown and nothing can reconcile it.`);throw c.code="SUBGRAPH_TRIGGER_TIMEOUT",c.subgraph=e,c.timedOut=!0,c.cause=u,c}let g=m?.data?.jobId||m?.jobId;if(!g)throw new Error(`Sub-graph '${e}' trigger returned no jobId: ${JSON.stringify(m).slice(0,200)}`);if(t.async)return E.info(`[sub-graph] async dispatch of '${e}' \u2192 jobId=${g} (not waiting)`),{jobId:g,status:"accepted",workflow:e};let O=Number.isFinite(t.pollIntervalMs)?t.pollIntervalMs:Ye,A=`${a}/executions/${encodeURIComponent(g)}`,P=Date.now()+o,_="accepted",R=0,I=null;for(;Date.now()<P;){await new Promise(b=>setTimeout(b,O)),R+=1;let u=Qe(P),c;try{c=await U(fetch(A,{headers:{Authorization:`Bearer ${s}`},signal:u.signal}),u.signal)}catch(b){I=M(b)?`poll TIMED OUT ${u.label}`:b?.message||String(b),E.warn(`[sub-graph] status poll for ${g} could not reach the API (${I}), will retry`);continue}if(!c.ok){if(c.status>=500){E.warn(`[sub-graph] status poll for ${g} returned ${c.status}, will retry`);continue}throw new Error(`Sub-graph status poll failed for ${g}: ${c.status}`)}let d;try{d=await U(c.json(),u.signal)}catch(b){I=M(b)?`poll body read TIMED OUT ${u.label}`:b?.message||String(b),E.warn(`[sub-graph] status poll for ${g} returned an unreadable body (${I}), will retry`);continue}let T=d?.data||d?.execution||d;if(_=T?.status||_,Ze.has(_)){if(_!=="completed"){let l=new Error(`Sub-graph '${e}' (${g}) ended in status '${_}'`);throw l.subgraphJobId=g,l.subgraphStatus=_,l}let b=T?.finalState||T?.state||{},X=ye(b,t.output);return E.info(`[sub-graph] '${e}' (${g}) completed after ${R} polls`),X}}try{let u=F(500,"SUBGRAPH_CANCEL_TIMEOUT_MS"),c=await U(fetch(`${A}/cancel`,{method:"POST",headers:{Authorization:`Bearer ${s}`},signal:u.signal}),u.signal);!c.ok&&c.status!==400&&c.status!==404&&E.warn(`[sub-graph] best-effort cancel for ${g} returned ${c.status}`)}catch(u){E.warn(`[sub-graph] best-effort cancel for ${g} failed: ${u?.message||String(u)}`)}throw z(e,g,o,_,I)}async function _e(e,t={}){if(!e||typeof e!="string")throw new Error("dispatchParticipant: bindingId (string) is required");if(!t.protocolId||typeof t.protocolId!="string")throw new Error("dispatchParticipant: protocolId (string) is required");return et("participant",{...t,async:t.async!==!1,participantBindingId:e})}async function we(e){if(!e||typeof e!="string")throw new Error("listParticipants: protocolId (string) is required");let t=be(),n=Ie(),r=Te(),o=Ee();if(!o){let p=new Error("Collaboration requires a running parent execution");throw p.code="PARENT_EXECUTION_REQUIRED",p}let a=`${t}/projects/${encodeURIComponent(n)}/workflows/participant/trigger`,i=me(),s;try{s=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${r}`},body:JSON.stringify({participantOperation:"list",parentExecutionId:o,protocolId:e}),signal:i.signal})}catch(p){let S=new Error(`Participant roster lookup failed: ${p?.message||String(p)}`);throw S.code=M(p)?"PARTICIPANT_ROSTER_TIMEOUT":"PARTICIPANT_ROSTER_UNAVAILABLE",S.cause=p,S}let h=null;try{h=await s.json()}catch{}if(!s.ok){let p=new Error(h?.error||`Participant roster lookup rejected (${s.status})`);throw p.code=h?.code||"PARTICIPANT_ROSTER_REJECTED",p.status=s.status,p}return Array.isArray(h?.participants)?h.participants:[]}var tt=8*6e4,nt=10*6e4,rt=2*6e4;function ot(e=process.env,t=process.uptime()){let n=Number(e?.MAX_WORKFLOW_DURATION_MS);return!Number.isFinite(n)||n<=0?null:Math.max(0,n-Math.ceil(Number(t||0)*1e3))}function Se(e,t,n=Number.MAX_SAFE_INTEGER){let r=Number(e);return Number.isFinite(r)&&r>0?Math.min(Math.floor(r),n):t}function N(e,t,n={}){return{requested:!0,required:e===!0,observed:!1,status:e===!0?"incomplete":"skipped",reason:t,contributions:[],disagreements:[],...n}}function at(e,t,n){return{participant:e.displayName||e.id,participantId:e.id,vendor:e.vendor||null,phase:t,revision:0,contribution:n}}function Ae(e,t,n){return e.find(r=>Array.isArray(r.roles)&&r.roles.includes(t))||n}var it=["claims","assumptions","risks","objections","decisions","evidenceGaps","recommendations","artifactRefs"];function st(e,t,n){let r=e;if(typeof r=="string")try{r=JSON.parse(r)}catch{r=null}if(!(r&&typeof r=="object"&&!Array.isArray(r)&&r.phase===t&&r.role===n&&r.contextRevision===0&&typeof r.summary=="string"&&it.every(a=>Array.isArray(r[a])))){let a=new Error(`Participant returned invalid ${t} protocol output`);throw a.code="PARTICIPANT_OUTPUT_INVALID",a}return r}async function Mt(e={},t={}){let n=String(e.protocolId||"discuss.v1"),r=String(e.objective||"").trim(),o=String(e.context||""),a=e.required===!0,i=Se(e.timeoutMs,tt,nt),s=Se(e.reserveMs,rt,15*6e4),h=(t.remainingWorkflowTimeMs||ot)();if(!r)return N(a,"objective_missing",{protocolId:n});if(h!=null&&h<i+s)return N(a,"insufficient_execution_time",{protocolId:n,remainingMs:h,requiredMs:i+s});let p=t.listParticipants||we,S=t.dispatchParticipant||_e,v;try{v=await p(n)}catch(l){return N(a,l?.code||"participant_roster_unavailable",{protocolId:n,detail:l?.message||String(l)})}let y=v.filter(l=>l?.available===!1),f=v.filter(l=>l&&l.available!==!1&&l.id);if(f.length===0)return N(a,"no_available_participants",{protocolId:n,unavailable:y});let m=Date.now(),g=m+i,O=[],A=[],P=async(l,$,ee,Re=[])=>{let W=g-Date.now();if(W<1e3){let D=new Error("Collaboration node exhausted its authored timeout");throw D.code="COLLABORATION_TIMEOUT",D}let K=Number(l?.limits?.maxExecutionMinutes)*6e4,Me=Number.isFinite(K)&&K>0?Math.min(W,K):W,te,j=null;for(let D=0;D<2;D+=1){let re=D===1?"Your previous attempt did not satisfy the JSON protocol. Return exactly one valid JSON object with every required array field and no Markdown, preface, trailing commas, or comments.":"",ve=await S(l.id,{async:!1,protocolId:n,timeoutMs:Math.min(Me,Math.max(1e3,g-Date.now())),output:"contribute",input:{objective:r,protocolId:n,phase:$,role:ee,contextRevision:0,sharedContext:o,priorContributions:Re,...e.instruction||re?{instruction:[e.instruction,re].filter(Boolean).join(`
|
|
2
|
-
`).slice(0,4e3)}:{}}});try{te=
|
|
1
|
+
var oe=()=>{},Pe={debug:oe,info:oe,warn:(...e)=>console.warn("[workflow]",...e),error:(...e)=>console.error("[workflow]",...e)},Y=Symbol.for("@zibby/agent-workflow.logger");globalThis[Y]||(globalThis[Y]={impl:Pe});var H=globalThis[Y];var E={debug:(...e)=>H.impl.debug?.(...e),info:(...e)=>H.impl.info?.(...e),warn:(...e)=>H.impl.warn?.(...e),error:(...e)=>H.impl.error?.(...e)};import{mkdirSync as Ce,existsSync as C,statSync as he,readdirSync as ge,rmSync as De}from"node:fs";import{spawn as le}from"node:child_process";import{join as U}from"node:path";import{pathToFileURL as Ge}from"node:url";import{AsyncLocalStorage as Le}from"node:async_hooks";import{AsyncLocalStorage as $e}from"node:async_hooks";var V=new $e;function G(){let e=V.getStore();return e||Object.freeze({executionId:process.env.EXECUTION_ID||null,parentExecutionId:process.env.PARENT_EXECUTION_ID||null,depth:0,conversationId:process.env.ZIBBY_CONVERSATION_ID||null,dispatchMode:process.env.DISPATCH_MODE||null,agent:null,signal:null})}function ae(e,t){let n=V.getStore()||G(),r=Object.freeze({executionId:e.executionId,parentExecutionId:e.parentExecutionId??n.executionId??null,depth:(n.depth||0)+(e.executionId!==n.executionId?1:0),conversationId:e.conversationId!==void 0?e.conversationId:n.conversationId??null,dispatchMode:e.dispatchMode??null,agent:e.agent!==void 0?e.agent:n.agent??null,signal:e.signal!==void 0?e.signal:n.signal??null});return V.run(r,t)}var Z=new Map,Q=new Map,ie=new Map;function se(e,t,n={}){if(!e||typeof e!="string")throw new Error("subgraph-registry.register: name required");if(typeof t!="function")throw new Error("subgraph-registry.register: factory must be a function");Z.set(e,t),Q.set(e,"ready"),ie.set(e,{...n,cachedAt:Date.now()})}function ce(e,t){Q.set(e,"failed"),ie.set(e,{error:t?.message||String(t),failedAt:Date.now()}),Z.delete(e)}function ue(e){return Q.get(e)==="ready"?Z.get(e):null}function L(e,t,n=process.env){let r=Number(n[e]);return Number.isFinite(r)&&r>0?Math.min(12e4,Math.max(1e3,Math.floor(r))):t}function F(e,t){return{signal:AbortSignal.timeout(e),label:`after ${e}ms (${t})`}}function k(e,t){return F(L(e,t),e)}function v(e){return e?.name==="TimeoutError"||e?.name==="AbortError"}function x(e,t){return t.aborted?(Promise.resolve(e).catch(()=>{}),Promise.reject(t.reason)):new Promise((n,r)=>{let o=()=>r(t.reason);t.addEventListener("abort",o,{once:!0}),Promise.resolve(e).then(a=>{t.removeEventListener("abort",o),n(a)},a=>{t.removeEventListener("abort",o),r(a)})})}var q=process.env.ZIBBY_SUBGRAPH_CACHE_DIR||"/tmp/zibby/subgraphs";function ke(){return`node${(process.versions?.node||"").split(".")[0]||"unknown"}-${process.platform}-${process.arch}`}var w=class extends Error{constructor(t,n){super(`in-process sub-graph fallback: ${t}${n?` (${n})`:""}`),this.fallback=!0,this.reason=t,this.detail=n||null,this.name="SubgraphFallback"}};function z(e,t,n,r="timeout",o=null){let a=new Error(`Sub-graph '${e}' (${t}) timed out after ${Math.round(n/1e3)}s (last status: ${r})`+(o?`; the status API was unreachable on the last attempt (${o})`:""));return a.code="SUBGRAPH_TIMEOUT",a.subgraphJobId=t,a.subgraphStatus=r,o&&(a.subgraphTransportError=o),a}function Ne(e,t=process.env,n=process.uptime()){let r=Number.isFinite(e)?Number(e):null,o=Number(t&&t.MAX_WORKFLOW_DURATION_MS),a=Number.isFinite(n)&&n>0?n*1e3:0,i=Number.isFinite(o)&&o>0?o-a:null;if(r===null&&i===null)return null;let s=r===null?i:i===null?r:Math.min(r,i);return Math.max(1,s)}function je(e,t){let n=new AbortController,r=!1,o=null,a=()=>{n.abort(e?.reason)};return e&&(e.aborted?a():e.addEventListener("abort",a,{once:!0})),t!=null&&!n.signal.aborted&&(o=setTimeout(()=>{r=!0,n.abort(new Error("sub-graph deadline exceeded"))},t),typeof o.unref=="function"&&o.unref()),{signal:n.signal,timedOut:()=>r,dispose(){o&&(clearTimeout(o),o=null),e&&e.removeEventListener("abort",a)}}}var de=new Le,pe=Promise.resolve();async function He(e,t){let n=e&&typeof e=="object"&&!Array.isArray(e)?Object.entries(e).filter(([i,s])=>typeof i=="string"&&i&&typeof s=="string"):[];if(n.length===0)return t();let r=de.getStore()===!0,o=null;if(!r){let i=pe;pe=new Promise(s=>{o=s}),await i}let a=new Map;try{for(let[i,s]of n)a.set(i,Object.prototype.hasOwnProperty.call(process.env,i)?process.env[i]:void 0),process.env[i]=s;return E.debug(`[in-process subgraph] scoped ${n.length} child env var(s)${r?" (nested)":""}`),await de.run(!0,t)}finally{for(let[i,s]of a)s===void 0?delete process.env[i]:process.env[i]=s;o&&o()}}function Fe(){let e=(process.env.SUBGRAPH_INTERNAL_URL||"").replace(/\/$/,""),t=(process.env.PROGRESS_API_URL||"").replace(/\/executions\/?$/,""),n=e||t,r=process.env.PROJECT_ID,o=process.env.PROJECT_API_TOKEN;if(!n||!r||!o)throw new w("env","SUBGRAPH_INTERNAL_URL/PROGRESS_API_URL/PROJECT_ID/PROJECT_API_TOKEN missing");return{apiBase:n,projectId:r,authToken:o}}async function qe({apiBase:e,authToken:t,body:n}){let r=k("SUBGRAPH_TRIGGER_TIMEOUT_MS",3e4),o;try{o=await fetch(`${e}/internal/subgraph/begin`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify(n),signal:r.signal})}catch(i){throw new w(v(i)?"begin-timeout":"network",v(i)?`begin TIMED OUT ${r.label}`:`begin fetch failed: ${i.message}`)}let a=null;try{a=await o.json()}catch(i){if(v(i))throw new w("begin-timeout",`begin body read TIMED OUT ${r.label}`)}if(!o.ok){if(o.status===404){let i=new Error(`Sub-graph child '${n.childWorkflowType}' not found in project`);throw i.code="SUBGRAPH_NOT_FOUND",i.status=404,i}if(o.status===429){let i=a?.quotaInfo||{},s=new Error(`Sub-graph blocked by quota (${i.used??"?"}/${i.limit??"?"} on ${i.planId||"plan"})`);throw s.code="SUBGRAPH_QUOTA_EXCEEDED",s.status=429,s.quotaInfo=i,s}if(o.status===400&&a?.validationErrors){let i=new Error(`Sub-graph rejected input: ${a?.error||a?.message||"validation failed"}`);throw i.code="SUBGRAPH_INVALID_INPUT",i.status=400,i.validationErrors=a.validationErrors,i.missing=a.missing,i}throw new w("begin-status",`begin returned ${o.status}`)}return a?.data||a}async function B({apiBase:e,authToken:t,payload:n}){let r=k("SUBGRAPH_TRIGGER_TIMEOUT_MS",3e4);try{let o=await fetch(`${e}/internal/subgraph/finalize`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify(n),signal:r.signal});o.ok||E.warn(`[in-process subgraph] finalize returned ${o.status} for ${n.childExecutionId}`)}catch(o){E.warn(`[in-process subgraph] finalize ${v(o)?`TIMED OUT ${r.label}`:`failed: ${o.message}`}`)}}async function ze(e,t){let n=U(t,".ready"),r=U(t,"graph.mjs");if(C(n)&&C(r))return;Ce(t,{recursive:!0});let o=U(t,".lock"),a=!1;try{let{openSync:i,closeSync:s}=await import("node:fs"),g=i(o,"wx");s(g),a=!0}catch(i){if(i.code!=="EEXIST")throw i}if(!a){let i=Date.now()+3e4;for(;Date.now()<i;){if(C(n)&&C(r))return;await new Promise(s=>setTimeout(s,100))}throw new w("bundle-extract-timeout","sibling extract did not complete within 30s")}try{await new Promise((g,p)=>{let S=Math.ceil(1e4/1e3),R=Math.ceil(L("SUBGRAPH_BUNDLE_TIMEOUT_MS",6e4)/1e3),y=le("curl",["-fsSL","--connect-timeout",String(S),"--max-time",String(R),e],{stdio:["ignore","pipe","inherit"]}),f=le("tar",["-xzf","-","-C",t],{stdio:["pipe","inherit","inherit"]});y.stdout.pipe(f.stdin);let m,h,O=()=>{if(m!==void 0&&h!==void 0){if(m!==0)return p(new Error(`curl exited ${m}`));if(h!==0)return p(new Error(`tar exited ${h}`));g()}};y.on("close",A=>{m=A,O()}),f.on("close",A=>{h=A,O()}),y.on("error",p),f.on("error",p)});let{writeFileSync:i,unlinkSync:s}=await import("node:fs");i(n,"");try{s(o)}catch{}}catch(i){try{let{unlinkSync:s}=await import("node:fs");s(o)}catch{}throw new w("bundle-extract-failed",i.message)}}async function Je(e){let t=U(e,"graph.mjs");if(!C(t))throw new w("entry-missing",`graph.mjs missing under ${e}`);let n;try{n=await import(Ge(t).href)}catch(o){throw new w("import-failed",`${o?.code||o?.name||"unknown"}: ${o.message}`)}let r=n.default||Object.values(n).find(o=>typeof o=="function"&&o.prototype?.buildGraph);if(!r)throw new w("entry-class-missing","no buildGraph() class export found");return r}async function fe(e,t={}){if(!e||typeof e!="string")throw new Error("runInProcessSubgraph: workflowName (string) is required");let n=G(),r;try{r=Fe()}catch(c){throw c}E.debug(`[in-process subgraph] begin '${e}' parent=${n.executionId||"<root>"}`);let o=await qe({apiBase:r.apiBase,authToken:r.authToken,body:{parentExecutionId:n.executionId,childWorkflowType:e,input:t.input||{},...t.conversationId?{conversationId:t.conversationId}:{}}}),{childExecutionId:a,runtimeTag:i,bundlePresignedUrl:s,sourcesPresignedUrl:g,workflowVersion:p,workflowUuid:S,bundleReady:R,nodeConfigs:y}=o,f=ke();if(i&&i!==f)throw await B({apiBase:r.apiBase,authToken:r.authToken,payload:{childExecutionId:a,discard:!0}}),new w("runtime-mismatch",`${f} vs ${i}`);if(!R||!s)throw await B({apiBase:r.apiBase,authToken:r.authToken,payload:{childExecutionId:a,discard:!0}}),new w("no-bundle","workflow bundle not built yet");let m=ue(e);if(!m){let c=U(q,`${S}@${p||"0"}`);try{await ze(s,c);try{We()}catch{}}catch(l){throw l.fallback&&await B({apiBase:r.apiBase,authToken:r.authToken,payload:{childExecutionId:a,status:"failed",error:{message:l.message,code:l.reason}}}),l}try{m=await Je(c),se(e,m,{workflowUuid:S,version:p,runtimeTag:i,cacheDir:c})}catch(l){throw ce(e,l),await B({apiBase:r.apiBase,authToken:r.authToken,payload:{childExecutionId:a,status:"failed",error:{message:l.message,code:l.reason||"IMPORT_FAILED"}}}),l.fallback?l:new w("import-failed",l.message)}}let h=Date.now(),O=o.env&&typeof o.env=="object"&&!Array.isArray(o.env)?o.env:null,A=y&&typeof y=="object"&&!Array.isArray(y)&&Object.keys(y).length>0,P={...t.input||{},...A?{nodeConfigs:y}:{}},_=Ne(t.timeoutMs),M=je(t.signal,_),I,u;try{I=await He(O,async()=>{let l=await(typeof m=="function"&&m.prototype?.buildGraph?new m:m).buildGraph();return ae({executionId:a,parentExecutionId:n.executionId,conversationId:t.conversationId!==void 0?t.conversationId:n.conversationId,dispatchMode:"inprocess"},()=>l.run(t.parentAgent,P,{signal:M.signal}))}),u=I&&typeof I=="object"&&"state"in I?I.state:I}catch(c){let l=M.timedOut();throw await B({apiBase:r.apiBase,authToken:r.authToken,payload:{childExecutionId:a,status:l?"timeout":"failed",error:l?{message:`aborted at the declared ${Math.round(_/1e3)}s sub-graph budget`,code:"SUBGRAPH_TIMEOUT"}:{message:c.message,code:c.code||"CHILD_THREW",stack:c.stack},durationMs:Date.now()-h}}),l?z(e,a,_):c}finally{M.dispose()}if(I&&typeof I=="object"&&I.stoppedExternally){let c=M.timedOut();if(await B({apiBase:r.apiBase,authToken:r.authToken,payload:{childExecutionId:a,status:c?"timeout":"canceled",finalState:u,durationMs:Date.now()-h}}),c)throw z(e,a,_);let l=new Error(`Sub-graph '${e}' canceled by parent abort`);throw l.code="SUBGRAPH_CANCELED",l.subgraphJobId=a,l}return await B({apiBase:r.apiBase,authToken:r.authToken,payload:{childExecutionId:a,status:"completed",finalState:u,durationMs:Date.now()-h}}),{finalState:u,executionId:a}}function Xe(e){let t=0,n=[e];for(;n.length;){let r=n.pop(),o;try{o=he(r)}catch{continue}if(o.isDirectory()){let a;try{a=ge(r)}catch{continue}for(let i of a)n.push(U(r,i))}else t+=o.size}return t}function We({cap:e=Number(process.env.ZIBBY_SUBGRAPH_CACHE_CAP_BYTES||2*1024*1024*1024)}={}){try{if(!C(q))return{evicted:0,freedBytes:0};let t=ge(q),n=[],r=0;for(let s of t){let g=U(q,s),p;try{p=he(g)}catch{continue}let S=p.isDirectory()?Xe(g):p.size;r+=S,n.push({name:s,full:g,size:S,mtimeMs:p.mtimeMs})}if(r<=e)return{evicted:0,freedBytes:0,totalBytes:r};n.sort((s,g)=>s.mtimeMs-g.mtimeMs);let o=Math.floor(e*.7),a=0,i=0;for(let s of n){if(r-a<=o)break;if(!C(U(s.full,".lock")))try{De(s.full,{recursive:!0,force:!0}),a+=s.size,i+=1}catch(g){E.debug(`[sub-graph cache] evict skip ${s.name}: ${g.message}`)}}return i>0&&E.info(`[sub-graph cache] evicted ${i} entr(y/ies), freed ${(a/1024/1024).toFixed(1)}MB`),{evicted:i,freedBytes:a,totalBytes:r-a}}catch(t){return E.debug(`[sub-graph cache] evict failed: ${t.message}`),{evicted:0,freedBytes:0}}}var Ye=2e3,Ve=600*1e3,Ze=new Set(["completed","failed","canceled","cancelled","timeout"]);var me=()=>k("SUBGRAPH_TRIGGER_TIMEOUT_MS",3e4);function Qe(e){let t=L("SUBGRAPH_POLL_TIMEOUT_MS",15e3),n=Math.max(1,Math.min(t,e-Date.now()));return F(n,"SUBGRAPH_POLL_TIMEOUT_MS")}function be(){let e=process.env.PROGRESS_API_URL;if(!e)throw new Error("Sub-graph dispatch requires PROGRESS_API_URL env var (set automatically on cloud runs). Sub-graphs are not supported in local in-process runs yet \u2014 deploy the parent and child to cloud.");return e.replace(/\/executions\/?$/,"")}function Ie(){let e=process.env.PROJECT_ID;if(!e)throw new Error("Sub-graph dispatch requires PROJECT_ID env var.");return e}function Te(){let e=process.env.PROJECT_API_TOKEN;if(!e)throw new Error("Sub-graph dispatch requires PROJECT_API_TOKEN env var.");return e}function Ee(){return process.env.EXECUTION_ID||null}function et(e,t){return t==null?e:typeof t=="function"?t(e):typeof t=="string"?t.split(".").reduce((n,r)=>n==null?n:n[r],e):e}function ye(e,t,n,r){let o=et(t,n);return r?{executionId:e,output:o}:o}async function tt(e,t={}){if(!e||typeof e!="string")throw new Error("dispatchSubgraph: workflowName (string) is required");let n=G();t.parentAgent==null&&n.agent&&(t.parentAgent=n.agent),t.signal==null&&n.signal&&(t.signal=n.signal);let r=Number(process.env.ZIBBY_SUBGRAPH_MAX_DEPTH||10);if((n.depth||0)>=r)throw new Error(`dispatchSubgraph('${e}'): sub-graph depth ${n.depth} reached cap of ${r}. Restructure the graph or raise ZIBBY_SUBGRAPH_MAX_DEPTH.`);let o=Number.isFinite(t.timeoutMs)?t.timeoutMs:Ve;if(process.env.ZIBBY_INPROCESS_SUBGRAPH!=="0"&&!t.async&&!t.participantBindingId)try{E.debug(`[sub-graph] trying in-process for '${e}'`);let{finalState:u,executionId:c}=await fe(e,{input:t.input,conversationId:t.conversationId,signal:t.signal,parentAgent:t.parentAgent,timeoutMs:o}),l=ye(c,u,t.output,t.includeExecutionMetadata===!0);return E.info(`[sub-graph] '${e}' completed in-process`),l}catch(u){if(u instanceof w||u?.fallback)E.info(`[sub-graph] in-process fallback for '${e}': ${u.reason||"unknown"} \u2014 using HTTP`);else throw u}let a=be(),i=Ie(),s=Te(),g=Ee(),p=t.participantBindingId?"participant":e,S=`${a}/projects/${encodeURIComponent(i)}/workflows/${encodeURIComponent(p)}/trigger`,R={input:t.input||{},...g?{parentExecutionId:g}:{},...!t.async&&typeof t.output=="string"&&t.output.trim()?{resultPath:t.output.trim()}:{},...t.conversationId?{conversationId:t.conversationId}:{},...typeof t.idempotencyKey=="string"&&t.idempotencyKey?{idempotencyKey:t.idempotencyKey.slice(0,200)}:{},...t.participantBindingId?{participantBindingId:t.participantBindingId}:{},...t.protocolId?{protocolId:t.protocolId}:{}};E.info(`[sub-graph] dispatching '${e}' (${t.async?"async":"sync"}) from parent ${g||"<none>"}`);let y=me(),f;try{f=await x(fetch(S,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify(R),signal:y.signal}),y.signal)}catch(u){if(!v(u))throw u;let c=new Error(`Sub-graph '${e}' trigger TIMED OUT ${y.label} \u2014 the platform never answered, so no child was dispatched and nothing needs reconciling.`);throw c.code="SUBGRAPH_TRIGGER_TIMEOUT",c.subgraph=e,c.timedOut=!0,c.cause=u,c}if(!f.ok){let u=null,c="";try{u=await x(f.json(),y.signal),c=u?.error||u?.message||JSON.stringify(u)}catch{c=await x(f.text(),y.signal).catch(()=>"")}if(f.status===429){let T=u?.quotaInfo||{},b=new Error(`Sub-graph '${e}' blocked by execution quota (${T.used??"?"}/${T.limit??"?"} on plan ${T.planId||"unknown"}). Sub-workflow runs count toward the same monthly cap as user-triggered runs.`);throw b.code="SUBGRAPH_QUOTA_EXCEEDED",b.status=429,b.subgraph=e,b.quotaInfo=T,b}if(f.status===400){let T=new Error(`Sub-graph '${e}' rejected input: ${c}`);throw T.code="SUBGRAPH_INVALID_INPUT",T.status=400,T.subgraph=e,T.validationErrors=u?.validationErrors||null,T.missing=u?.missing||null,T}let l=new Error(`Sub-graph '${e}' trigger rejected (${f.status}): ${c}`);throw l.code="SUBGRAPH_TRIGGER_FAILED",l.status=f.status,l.subgraph=e,l}let m;try{m=await x(f.json(),y.signal)}catch(u){if(!v(u))throw u;let c=new Error(`Sub-graph '${e}' trigger body read TIMED OUT ${y.label} \u2014 the platform accepted the dispatch but never finished answering, so its jobId is unknown and nothing can reconcile it.`);throw c.code="SUBGRAPH_TRIGGER_TIMEOUT",c.subgraph=e,c.timedOut=!0,c.cause=u,c}let h=m?.data?.jobId||m?.jobId;if(!h)throw new Error(`Sub-graph '${e}' trigger returned no jobId: ${JSON.stringify(m).slice(0,200)}`);if(t.async)return E.info(`[sub-graph] async dispatch of '${e}' \u2192 jobId=${h} (not waiting)`),{jobId:h,status:"accepted",workflow:e};let O=Number.isFinite(t.pollIntervalMs)?t.pollIntervalMs:Ye,A=`${a}/executions/${encodeURIComponent(h)}`,P=Date.now()+o,_="accepted",M=0,I=null;for(;Date.now()<P;){await new Promise(b=>setTimeout(b,O)),M+=1;let u=Qe(P),c;try{c=await x(fetch(A,{headers:{Authorization:`Bearer ${s}`},signal:u.signal}),u.signal)}catch(b){I=v(b)?`poll TIMED OUT ${u.label}`:b?.message||String(b),E.warn(`[sub-graph] status poll for ${h} could not reach the API (${I}), will retry`);continue}if(!c.ok){if(c.status>=500){E.warn(`[sub-graph] status poll for ${h} returned ${c.status}, will retry`);continue}throw new Error(`Sub-graph status poll failed for ${h}: ${c.status}`)}let l;try{l=await x(c.json(),u.signal)}catch(b){I=v(b)?`poll body read TIMED OUT ${u.label}`:b?.message||String(b),E.warn(`[sub-graph] status poll for ${h} returned an unreadable body (${I}), will retry`);continue}let T=l?.data||l?.execution||l;if(_=T?.status||_,Ze.has(_)){if(_!=="completed"){let d=new Error(`Sub-graph '${e}' (${h}) ended in status '${_}'`);throw d.subgraphJobId=h,d.subgraphStatus=_,d}let b=T?.finalState||T?.state||{},X=ye(h,b,t.output,t.includeExecutionMetadata===!0);return E.info(`[sub-graph] '${e}' (${h}) completed after ${M} polls`),X}}try{let u=F(500,"SUBGRAPH_CANCEL_TIMEOUT_MS"),c=await x(fetch(`${A}/cancel`,{method:"POST",headers:{Authorization:`Bearer ${s}`},signal:u.signal}),u.signal);!c.ok&&c.status!==400&&c.status!==404&&E.warn(`[sub-graph] best-effort cancel for ${h} returned ${c.status}`)}catch(u){E.warn(`[sub-graph] best-effort cancel for ${h} failed: ${u?.message||String(u)}`)}throw z(e,h,o,_,I)}async function _e(e,t={}){if(!e||typeof e!="string")throw new Error("dispatchParticipant: bindingId (string) is required");if(!t.protocolId||typeof t.protocolId!="string")throw new Error("dispatchParticipant: protocolId (string) is required");return tt("participant",{...t,async:t.async!==!1,participantBindingId:e})}async function we(e){if(!e||typeof e!="string")throw new Error("listParticipants: protocolId (string) is required");let t=be(),n=Ie(),r=Te(),o=Ee();if(!o){let p=new Error("Collaboration requires a running parent execution");throw p.code="PARENT_EXECUTION_REQUIRED",p}let a=`${t}/projects/${encodeURIComponent(n)}/workflows/participant/trigger`,i=me(),s;try{s=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${r}`},body:JSON.stringify({participantOperation:"list",parentExecutionId:o,protocolId:e}),signal:i.signal})}catch(p){let S=new Error(`Participant roster lookup failed: ${p?.message||String(p)}`);throw S.code=v(p)?"PARTICIPANT_ROSTER_TIMEOUT":"PARTICIPANT_ROSTER_UNAVAILABLE",S.cause=p,S}let g=null;try{g=await s.json()}catch{}if(!s.ok){let p=new Error(g?.error||`Participant roster lookup rejected (${s.status})`);throw p.code=g?.code||"PARTICIPANT_ROSTER_REJECTED",p.status=s.status,p}return Array.isArray(g?.participants)?g.participants:[]}var nt=8*6e4,rt=10*6e4,ot=2*6e4;function at(e=process.env,t=process.uptime()){let n=Number(e?.MAX_WORKFLOW_DURATION_MS);return!Number.isFinite(n)||n<=0?null:Math.max(0,n-Math.ceil(Number(t||0)*1e3))}function Se(e,t,n=Number.MAX_SAFE_INTEGER){let r=Number(e);return Number.isFinite(r)&&r>0?Math.min(Math.floor(r),n):t}function N(e,t,n={}){return{requested:!0,required:e===!0,observed:!1,status:e===!0?"incomplete":"skipped",reason:t,contributions:[],disagreements:[],...n}}function it(e,t,n){return{participant:e.displayName||e.id,participantId:e.id,vendor:e.vendor||null,phase:t,revision:0,contribution:n}}function Ae(e,t,n){return e.find(r=>Array.isArray(r.roles)&&r.roles.includes(t))||n}var st=["claims","assumptions","risks","objections","decisions","evidenceGaps","recommendations","artifactRefs"];function ct(e,t,n){let r=e;if(typeof r=="string")try{r=JSON.parse(r)}catch{r=null}if(!(r&&typeof r=="object"&&!Array.isArray(r)&&r.phase===t&&r.role===n&&r.contextRevision===0&&typeof r.summary=="string"&&st.every(a=>Array.isArray(r[a])))){let a=new Error(`Participant returned invalid ${t} protocol output`);throw a.code="PARTICIPANT_OUTPUT_INVALID",a}return r}async function Rt(e={},t={}){let n=String(e.protocolId||"discuss.v1"),r=String(e.objective||"").trim(),o=String(e.context||""),a=e.required===!0,i=Se(e.timeoutMs,nt,rt),s=Se(e.reserveMs,ot,15*6e4),g=(t.remainingWorkflowTimeMs||at)();if(!r)return N(a,"objective_missing",{protocolId:n});if(g!=null&&g<i+s)return N(a,"insufficient_execution_time",{protocolId:n,remainingMs:g,requiredMs:i+s});let p=t.listParticipants||we,S=t.dispatchParticipant||_e,R;try{R=await p(n)}catch(d){return N(a,d?.code||"participant_roster_unavailable",{protocolId:n,detail:d?.message||String(d)})}let y=R.filter(d=>d?.available===!1),f=R.filter(d=>d&&d.available!==!1&&d.id);if(f.length===0)return N(a,"no_available_participants",{protocolId:n,unavailable:y});let m=Date.now(),h=m+i,O=[],A=[],P=async(d,$,ee,Me=[])=>{let W=h-Date.now();if(W<1e3){let D=new Error("Collaboration node exhausted its authored timeout");throw D.code="COLLABORATION_TIMEOUT",D}let K=Number(d?.limits?.maxExecutionMinutes)*6e4,ve=Number.isFinite(K)&&K>0?Math.min(W,K):W,te,j=null;for(let D=0;D<2;D+=1){let re=D===1?"Your previous attempt did not satisfy the JSON protocol. Return exactly one valid JSON object with every required array field and no Markdown, preface, trailing commas, or comments.":"",Re=await S(d.id,{async:!1,protocolId:n,timeoutMs:Math.min(ve,Math.max(1e3,h-Date.now())),output:"contribute",input:{objective:r,protocolId:n,phase:$,role:ee,contextRevision:0,sharedContext:o,priorContributions:Me,...e.instruction||re?{instruction:[e.instruction,re].filter(Boolean).join(`
|
|
2
|
+
`).slice(0,4e3)}:{}}});try{te=ct(Re,$,ee),j=null;break}catch(Oe){j=Oe}}if(j)throw j;let ne=it(d,$,te);return O.push(ne),ne},_=Ae(f,"draft",f[0]),M;try{M=await P(_,"draft","drafter")}catch(d){A.push({participantId:_.id,phase:"draft",code:d?.code||"PARTICIPANT_FAILED",detail:d?.message||String(d)});let $=N(a,"draft_failed",{protocolId:n,unavailable:y,failures:A,durationMs:Date.now()-m});return a&&($.status="incomplete"),$}let I=f.filter(d=>d.id!==_.id);I.length===0&&(I=[_]);let u=await Promise.allSettled(I.map(d=>P(d,"critique","critic",[M]))),c=[];u.forEach((d,$)=>{d.status==="fulfilled"?c.push(d.value):A.push({participantId:I[$].id,phase:"critique",code:d.reason?.code||"PARTICIPANT_FAILED",detail:d.reason?.message||String(d.reason)})});let l=Ae(f,"synthesize",_),T=null;try{T=await P(l,"synthesize","synthesizer",[M,...c])}catch(d){A.push({participantId:l.id,phase:"synthesize",code:d?.code||"PARTICIPANT_FAILED",detail:d?.message||String(d)})}let b=c.length>0&&T!=null,X=(T?.contribution?.decisions||[]).filter(d=>d?.status==="unresolved");return{requested:!0,required:a,observed:b,status:b?"completed":"incomplete",reason:b?null:"required_phase_incomplete",protocolId:n,contributions:O,synthesis:T?.contribution||null,disagreements:X,failures:A,unavailable:y,durationMs:Date.now()-m}}export{at as remainingWorkflowTimeMs,Rt as runCollaboration};
|