@zibby/agent-workflow 2.0.12 → 2.0.14
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/fetch-deadline.d.ts +9 -0
- package/dist/fetch-deadline.js +1 -1
- package/dist/graph-compiler.js +22 -22
- package/dist/graph.js +21 -21
- package/dist/in-process-subgraph.js +1 -1
- package/dist/index.js +29 -29
- package/dist/sub-graph-executor.d.ts +10 -1
- package/dist/sub-graph-executor.js +1 -1
- 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 re=()=>{},ve={debug:re,info:re,warn:(...e)=>console.warn("[workflow]",...e),error:(...e)=>console.error("[workflow]",...e)},W=Symbol.for("@zibby/agent-workflow.logger");globalThis[W]||(globalThis[W]={impl:ve});var j=globalThis[W];var T={debug:(...e)=>j.impl.debug?.(...e),info:(...e)=>j.impl.info?.(...e),warn:(...e)=>j.impl.warn?.(...e),error:(...e)=>j.impl.error?.(...e)};import{mkdirSync as Be,existsSync as B,statSync as pe,readdirSync as he,rmSync as Ce}from"node:fs";import{spawn as ue}from"node:child_process";import{join as U}from"node:path";import{pathToFileURL as De}from"node:url";import{AsyncLocalStorage as Ge}from"node:async_hooks";import{AsyncLocalStorage as Pe}from"node:async_hooks";var Y=new Pe;function D(){let e=Y.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 oe(e,t){let n=Y.getStore()||D(),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 Y.run(r,t)}var V=new Map,Z=new Map,ae=new Map;function ie(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");V.set(e,t),Z.set(e,"ready"),ae.set(e,{...n,cachedAt:Date.now()})}function se(e,t){Z.set(e,"failed"),ae.set(e,{error:t?.message||String(t),failedAt:Date.now()}),V.delete(e)}function ce(e){return Z.get(e)==="ready"?V.get(e):null}function G(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 H(e,t){return{signal:AbortSignal.timeout(e),label:`after ${e}ms (${t})`}}function L(e,t){return H(G(e,t),e)}function M(e){return e?.name==="TimeoutError"||e?.name==="AbortError"}var F=process.env.ZIBBY_SUBGRAPH_CACHE_DIR||"/tmp/zibby/subgraphs";function Le(){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 q(e,t,n,r="timeout",o=null){let i=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 i.code="SUBGRAPH_TIMEOUT",i.subgraphJobId=t,i.subgraphStatus=r,o&&(i.subgraphTransportError=o),i}function ke(e,t=process.env,n=process.uptime()){let r=Number.isFinite(e)?Number(e):null,o=Number(t&&t.MAX_WORKFLOW_DURATION_MS),i=Number.isFinite(n)&&n>0?n*1e3:0,a=Number.isFinite(o)&&o>0?o-i:null;if(r===null&&a===null)return null;let s=r===null?a:a===null?r:Math.min(r,a);return Math.max(1,s)}function Ne(e,t){let n=new AbortController,r=!1,o=null,i=()=>{n.abort(e?.reason)};return e&&(e.aborted?i():e.addEventListener("abort",i,{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",i)}}}var le=new Ge,de=Promise.resolve();async function je(e,t){let n=e&&typeof e=="object"&&!Array.isArray(e)?Object.entries(e).filter(([a,s])=>typeof a=="string"&&a&&typeof s=="string"):[];if(n.length===0)return t();let r=le.getStore()===!0,o=null;if(!r){let a=de;de=new Promise(s=>{o=s}),await a}let i=new Map;try{for(let[a,s]of n)i.set(a,Object.prototype.hasOwnProperty.call(process.env,a)?process.env[a]:void 0),process.env[a]=s;return T.debug(`[in-process subgraph] scoped ${n.length} child env var(s)${r?" (nested)":""}`),await le.run(!0,t)}finally{for(let[a,s]of i)s===void 0?delete process.env[a]:process.env[a]=s;o&&o()}}function He(){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 Fe({apiBase:e,authToken:t,body:n}){let r=L("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(a){throw new w(M(a)?"begin-timeout":"network",M(a)?`begin TIMED OUT ${r.label}`:`begin fetch failed: ${a.message}`)}let i=null;try{i=await o.json()}catch(a){if(M(a))throw new w("begin-timeout",`begin body read TIMED OUT ${r.label}`)}if(!o.ok){if(o.status===404){let a=new Error(`Sub-graph child '${n.childWorkflowType}' not found in project`);throw a.code="SUBGRAPH_NOT_FOUND",a.status=404,a}if(o.status===429){let a=i?.quotaInfo||{},s=new Error(`Sub-graph blocked by quota (${a.used??"?"}/${a.limit??"?"} on ${a.planId||"plan"})`);throw s.code="SUBGRAPH_QUOTA_EXCEEDED",s.status=429,s.quotaInfo=a,s}if(o.status===400&&i?.validationErrors){let a=new Error(`Sub-graph rejected input: ${i?.error||i?.message||"validation failed"}`);throw a.code="SUBGRAPH_INVALID_INPUT",a.status=400,a.validationErrors=i.validationErrors,a.missing=i.missing,a}throw new w("begin-status",`begin returned ${o.status}`)}return i?.data||i}async function x({apiBase:e,authToken:t,payload:n}){let r=L("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||T.warn(`[in-process subgraph] finalize returned ${o.status} for ${n.childExecutionId}`)}catch(o){T.warn(`[in-process subgraph] finalize ${M(o)?`TIMED OUT ${r.label}`:`failed: ${o.message}`}`)}}async function qe(e,t){let n=U(t,".ready"),r=U(t,"graph.mjs");if(B(n)&&B(r))return;Be(t,{recursive:!0});let o=U(t,".lock"),i=!1;try{let{openSync:a,closeSync:s}=await import("node:fs"),h=a(o,"wx");s(h),i=!0}catch(a){if(a.code!=="EEXIST")throw a}if(!i){let a=Date.now()+3e4;for(;Date.now()<a;){if(B(n)&&B(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),O=Math.ceil(G("SUBGRAPH_BUNDLE_TIMEOUT_MS",6e4)/1e3),_=ue("curl",["-fsSL","--connect-timeout",String(S),"--max-time",String(O),e],{stdio:["ignore","pipe","inherit"]}),f=ue("tar",["-xzf","-","-C",t],{stdio:["pipe","inherit","inherit"]});_.stdout.pipe(f.stdin);let y,g,v=()=>{if(y!==void 0&&g!==void 0){if(y!==0)return p(new Error(`curl exited ${y}`));if(g!==0)return p(new Error(`tar exited ${g}`));h()}};_.on("close",A=>{y=A,v()}),f.on("close",A=>{g=A,v()}),_.on("error",p),f.on("error",p)});let{writeFileSync:a,unlinkSync:s}=await import("node:fs");a(n,"");try{s(o)}catch{}}catch(a){try{let{unlinkSync:s}=await import("node:fs");s(o)}catch{}throw new w("bundle-extract-failed",a.message)}}async function ze(e){let t=U(e,"graph.mjs");if(!B(t))throw new w("entry-missing",`graph.mjs missing under ${e}`);let n;try{n=await import(De(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 ge(e,t={}){if(!e||typeof e!="string")throw new Error("runInProcessSubgraph: workflowName (string) is required");let n=D(),r;try{r=He()}catch(c){throw c}T.debug(`[in-process subgraph] begin '${e}' parent=${n.executionId||"<root>"}`);let o=await Fe({apiBase:r.apiBase,authToken:r.authToken,body:{parentExecutionId:n.executionId,childWorkflowType:e,input:t.input||{},...t.conversationId?{conversationId:t.conversationId}:{}}}),{childExecutionId:i,runtimeTag:a,bundlePresignedUrl:s,sourcesPresignedUrl:h,workflowVersion:p,workflowUuid:S,bundleReady:O,nodeConfigs:_}=o,f=Le();if(a&&a!==f)throw await x({apiBase:r.apiBase,authToken:r.authToken,payload:{childExecutionId:i,discard:!0}}),new w("runtime-mismatch",`${f} vs ${a}`);if(!O||!s)throw await x({apiBase:r.apiBase,authToken:r.authToken,payload:{childExecutionId:i,discard:!0}}),new w("no-bundle","workflow bundle not built yet");let y=ce(e);if(!y){let c=U(F,`${S}@${p||"0"}`);try{await qe(s,c);try{Xe()}catch{}}catch(d){throw d.fallback&&await x({apiBase:r.apiBase,authToken:r.authToken,payload:{childExecutionId:i,status:"failed",error:{message:d.message,code:d.reason}}}),d}try{y=await ze(c),ie(e,y,{workflowUuid:S,version:p,runtimeTag:a,cacheDir:c})}catch(d){throw se(e,d),await x({apiBase:r.apiBase,authToken:r.authToken,payload:{childExecutionId:i,status:"failed",error:{message:d.message,code:d.reason||"IMPORT_FAILED"}}}),d.fallback?d:new w("import-failed",d.message)}}let g=Date.now(),v=o.env&&typeof o.env=="object"&&!Array.isArray(o.env)?o.env:null,A=_&&typeof _=="object"&&!Array.isArray(_)&&Object.keys(_).length>0,P={...t.input||{},...A?{nodeConfigs:_}:{}},E=ke(t.timeoutMs),R=Ne(t.signal,E),b,u;try{b=await je(v,async()=>{let d=await(typeof y=="function"&&y.prototype?.buildGraph?new y:y).buildGraph();return oe({executionId:i,parentExecutionId:n.executionId,conversationId:t.conversationId!==void 0?t.conversationId:n.conversationId,dispatchMode:"inprocess"},()=>d.run(t.parentAgent,P,{signal:R.signal}))}),u=b&&typeof b=="object"&&"state"in b?b.state:b}catch(c){let d=R.timedOut();throw await x({apiBase:r.apiBase,authToken:r.authToken,payload:{childExecutionId:i,status:d?"timeout":"failed",error:d?{message:`aborted at the declared ${Math.round(E/1e3)}s sub-graph budget`,code:"SUBGRAPH_TIMEOUT"}:{message:c.message,code:c.code||"CHILD_THREW",stack:c.stack},durationMs:Date.now()-g}}),d?q(e,i,E):c}finally{R.dispose()}if(b&&typeof b=="object"&&b.stoppedExternally){let c=R.timedOut();if(await x({apiBase:r.apiBase,authToken:r.authToken,payload:{childExecutionId:i,status:c?"timeout":"canceled",finalState:u,durationMs:Date.now()-g}}),c)throw q(e,i,E);let d=new Error(`Sub-graph '${e}' canceled by parent abort`);throw d.code="SUBGRAPH_CANCELED",d.subgraphJobId=i,d}return await x({apiBase:r.apiBase,authToken:r.authToken,payload:{childExecutionId:i,status:"completed",finalState:u,durationMs:Date.now()-g}}),{finalState:u,executionId:i}}function Je(e){let t=0,n=[e];for(;n.length;){let r=n.pop(),o;try{o=pe(r)}catch{continue}if(o.isDirectory()){let i;try{i=he(r)}catch{continue}for(let a of i)n.push(U(r,a))}else t+=o.size}return t}function Xe({cap:e=Number(process.env.ZIBBY_SUBGRAPH_CACHE_CAP_BYTES||2*1024*1024*1024)}={}){try{if(!B(F))return{evicted:0,freedBytes:0};let t=he(F),n=[],r=0;for(let s of t){let h=U(F,s),p;try{p=pe(h)}catch{continue}let S=p.isDirectory()?Je(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),i=0,a=0;for(let s of n){if(r-i<=o)break;if(!B(U(s.full,".lock")))try{Ce(s.full,{recursive:!0,force:!0}),i+=s.size,a+=1}catch(h){T.debug(`[sub-graph cache] evict skip ${s.name}: ${h.message}`)}}return a>0&&T.info(`[sub-graph cache] evicted ${a} entr(y/ies), freed ${(i/1024/1024).toFixed(1)}MB`),{evicted:a,freedBytes:i,totalBytes:r-i}}catch(t){return T.debug(`[sub-graph cache] evict failed: ${t.message}`),{evicted:0,freedBytes:0}}}var We=2e3,Ye=600*1e3,Ve=new Set(["completed","failed","canceled","cancelled","timeout"]);var ye=()=>L("SUBGRAPH_TRIGGER_TIMEOUT_MS",3e4);function Ze(e){let t=G("SUBGRAPH_POLL_TIMEOUT_MS",15e3),n=Math.max(1,Math.min(t,e-Date.now()));return H(n,"SUBGRAPH_POLL_TIMEOUT_MS")}function me(){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 be(){let e=process.env.PROJECT_ID;if(!e)throw new Error("Sub-graph dispatch requires PROJECT_ID env var.");return e}function Ie(){let e=process.env.PROJECT_API_TOKEN;if(!e)throw new Error("Sub-graph dispatch requires PROJECT_API_TOKEN env var.");return e}function Te(){return process.env.EXECUTION_ID||null}function fe(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 Qe(e,t={}){if(!e||typeof e!="string")throw new Error("dispatchSubgraph: workflowName (string) is required");let n=D();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:Ye;if(process.env.ZIBBY_INPROCESS_SUBGRAPH!=="0"&&!t.async&&!t.participantBindingId)try{T.debug(`[sub-graph] trying in-process for '${e}'`);let{finalState:u}=await ge(e,{input:t.input,conversationId:t.conversationId,signal:t.signal,parentAgent:t.parentAgent,timeoutMs:o}),c=fe(u,t.output);return T.info(`[sub-graph] '${e}' completed in-process`),c}catch(u){if(u instanceof w||u?.fallback)T.info(`[sub-graph] in-process fallback for '${e}': ${u.reason||"unknown"} \u2014 using HTTP`);else throw u}let i=me(),a=be(),s=Ie(),h=Te(),p=t.participantBindingId?"participant":e,S=`${i}/projects/${encodeURIComponent(a)}/workflows/${encodeURIComponent(p)}/trigger`,O={input:t.input||{},...h?{parentExecutionId:h}:{},...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}:{}};T.info(`[sub-graph] dispatching '${e}' (${t.async?"async":"sync"}) from parent ${h||"<none>"}`);let _=ye(),f;try{f=await fetch(S,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify(O),signal:_.signal})}catch(u){if(!M(u))throw u;let c=new Error(`Sub-graph '${e}' trigger TIMED OUT ${_.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 f.json(),c=u?.error||u?.message||JSON.stringify(u)}catch{c=await f.text().catch(()=>"")}if(f.status===429){let I=u?.quotaInfo||{},m=new Error(`Sub-graph '${e}' blocked by execution quota (${I.used??"?"}/${I.limit??"?"} on plan ${I.planId||"unknown"}). Sub-workflow runs count toward the same monthly cap as user-triggered runs.`);throw m.code="SUBGRAPH_QUOTA_EXCEEDED",m.status=429,m.subgraph=e,m.quotaInfo=I,m}if(f.status===400){let I=new Error(`Sub-graph '${e}' rejected input: ${c}`);throw I.code="SUBGRAPH_INVALID_INPUT",I.status=400,I.subgraph=e,I.validationErrors=u?.validationErrors||null,I.missing=u?.missing||null,I}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 y;try{y=await f.json()}catch(u){if(!M(u))throw u;let c=new Error(`Sub-graph '${e}' trigger body read TIMED OUT ${_.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=y?.data?.jobId||y?.jobId;if(!g)throw new Error(`Sub-graph '${e}' trigger returned no jobId: ${JSON.stringify(y).slice(0,200)}`);if(t.async)return T.info(`[sub-graph] async dispatch of '${e}' \u2192 jobId=${g} (not waiting)`),{jobId:g,status:"accepted",workflow:e};let v=Number.isFinite(t.pollIntervalMs)?t.pollIntervalMs:We,A=`${i}/executions/${encodeURIComponent(g)}`,P=Date.now()+o,E="accepted",R=0,b=null;for(;Date.now()<P;){await new Promise(m=>setTimeout(m,v)),R+=1;let u=Ze(P),c;try{c=await fetch(A,{headers:{Authorization:`Bearer ${s}`},signal:u.signal})}catch(m){b=M(m)?`poll TIMED OUT ${u.label}`:m?.message||String(m),T.warn(`[sub-graph] status poll for ${g} could not reach the API (${b}), will retry`);continue}if(!c.ok){if(c.status>=500){T.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 c.json()}catch(m){b=M(m)?`poll body read TIMED OUT ${u.label}`:m?.message||String(m),T.warn(`[sub-graph] status poll for ${g} returned an unreadable body (${b}), will retry`);continue}let I=d?.data||d?.execution||d;if(E=I?.status||E,Ve.has(E)){if(E!=="completed"){let l=new Error(`Sub-graph '${e}' (${g}) ended in status '${E}'`);throw l.subgraphJobId=g,l.subgraphStatus=E,l}let m=I?.finalState||I?.state||{},J=fe(m,t.output);return T.info(`[sub-graph] '${e}' (${g}) completed after ${R} polls`),J}}try{let u=H(500,"SUBGRAPH_CANCEL_TIMEOUT_MS"),c=await fetch(`${A}/cancel`,{method:"POST",headers:{Authorization:`Bearer ${s}`},signal:u.signal});!c.ok&&c.status!==400&&c.status!==404&&T.warn(`[sub-graph] best-effort cancel for ${g} returned ${c.status}`)}catch(u){T.warn(`[sub-graph] best-effort cancel for ${g} failed: ${u?.message||String(u)}`)}throw q(e,g,o,E,b)}async function Ee(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 Qe("participant",{...t,async:t.async!==!1,participantBindingId:e})}async function _e(e){if(!e||typeof e!="string")throw new Error("listParticipants: protocolId (string) is required");let t=me(),n=be(),r=Ie(),o=Te();if(!o){let p=new Error("Collaboration requires a running parent execution");throw p.code="PARENT_EXECUTION_REQUIRED",p}let i=`${t}/projects/${encodeURIComponent(n)}/workflows/participant/trigger`,a=ye(),s;try{s=await fetch(i,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${r}`},body:JSON.stringify({participantOperation:"list",parentExecutionId:o,protocolId:e}),signal:a.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 et=8*6e4,tt=10*6e4,nt=2*6e4;function rt(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 we(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 k(e,t,n={}){return{requested:!0,required:e===!0,observed:!1,status:e===!0?"incomplete":"skipped",reason:t,contributions:[],disagreements:[],...n}}function ot(e,t,n){return{participant:e.displayName||e.id,participantId:e.id,vendor:e.vendor||null,phase:t,revision:0,contribution:n}}function Se(e,t,n){return e.find(r=>Array.isArray(r.roles)&&r.roles.includes(t))||n}var at=["claims","assumptions","risks","objections","decisions","evidenceGaps","recommendations","artifactRefs"];function it(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"&&at.every(i=>Array.isArray(r[i])))){let i=new Error(`Participant returned invalid ${t} protocol output`);throw i.code="PARTICIPANT_OUTPUT_INVALID",i}return r}async function Rt(e={},t={}){let n=String(e.protocolId||"discuss.v1"),r=String(e.objective||"").trim(),o=String(e.context||""),i=e.required===!0,a=we(e.timeoutMs,et,tt),s=we(e.reserveMs,nt,15*6e4),h=(t.remainingWorkflowTimeMs||rt)();if(!r)return k(i,"objective_missing",{protocolId:n});if(h!=null&&h<a+s)return k(i,"insufficient_execution_time",{protocolId:n,remainingMs:h,requiredMs:a+s});let p=t.listParticipants||_e,S=t.dispatchParticipant||Ee,O;try{O=await p(n)}catch(l){return k(i,l?.code||"participant_roster_unavailable",{protocolId:n,detail:l?.message||String(l)})}let _=O.filter(l=>l?.available===!1),f=O.filter(l=>l&&l.available!==!1&&l.id);if(f.length===0)return k(i,"no_available_participants",{protocolId:n,unavailable:_});let y=Date.now(),g=y+a,v=[],A=[],P=async(l,$,Q,Ae=[])=>{let X=g-Date.now();if(X<1e3){let C=new Error("Collaboration node exhausted its authored timeout");throw C.code="COLLABORATION_TIMEOUT",C}let K=Number(l?.limits?.maxExecutionMinutes)*6e4,Re=Number.isFinite(K)&&K>0?Math.min(X,K):X,ee,N=null;for(let C=0;C<2;C+=1){let ne=C===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.":"",Me=await S(l.id,{async:!1,protocolId:n,timeoutMs:Math.min(Re,Math.max(1e3,g-Date.now())),output:"contribute",input:{objective:r,protocolId:n,phase:$,role:Q,contextRevision:0,sharedContext:o,priorContributions:Ae,...e.instruction||ne?{instruction:[e.instruction,ne].filter(Boolean).join(`
|
|
2
|
-
`).slice(0,4e3)}:{}}});try{
|
|
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};
|
package/dist/fetch-deadline.d.ts
CHANGED
|
@@ -76,3 +76,12 @@ export declare function deadlineFor(knob: string, fallback: number): {
|
|
|
76
76
|
* unchanged.
|
|
77
77
|
*/
|
|
78
78
|
export declare function isTimeoutError(err: any): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Make the deadline authoritative even when the underlying transport ignores
|
|
81
|
+
* abort or removes its socket without settling the returned promise.
|
|
82
|
+
*
|
|
83
|
+
* `fetch(..., { signal })` is still required so the transport gets a chance to
|
|
84
|
+
* clean up. This second layer is the caller contract: once the signal fires,
|
|
85
|
+
* the await settles immediately and late transport completion is ignored.
|
|
86
|
+
*/
|
|
87
|
+
export declare function settleWithin<T>(promise: PromiseLike<T>, signal: AbortSignal): Promise<T>;
|
package/dist/fetch-deadline.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var T=3e4,m=15e3,u=6e4,M=1e4,a=1e3,E=12e4;function _(e,r,o=process.env){let t=Number(o[e]);return Number.isFinite(t)&&t>0?Math.min(12e4,Math.max(1e3,Math.floor(t))):r}function s(e,r){return{signal:AbortSignal.timeout(e),label:`after ${e}ms (${r})`}}function c(e,r){return s(_(e,r),e)}function O(e){return e?.name==="TimeoutError"||e?.name==="AbortError"}function S(e,r){return r.aborted?(Promise.resolve(e).catch(()=>{}),Promise.reject(r.reason)):new Promise((o,t)=>{let n=()=>t(r.reason);r.addEventListener("abort",n,{once:!0}),Promise.resolve(e).then(i=>{r.removeEventListener("abort",n),o(i)},i=>{r.removeEventListener("abort",n),t(i)})})}export{u as SUBGRAPH_BUNDLE_TIMEOUT_MS,M as SUBGRAPH_CONNECT_TIMEOUT_MS,m as SUBGRAPH_POLL_TIMEOUT_MS,T as SUBGRAPH_TRIGGER_TIMEOUT_MS,E as TIMEOUT_CEILING_MS,a as TIMEOUT_FLOOR_MS,c as deadlineFor,O as isTimeoutError,s as makeDeadline,S as settleWithin,_ as timeoutMsFrom};
|