@tangle-network/sandbox-cli 0.22.24 → 0.22.25

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.
Files changed (2) hide show
  1. package/dist/index.mjs +1 -1
  2. package/package.json +6 -6
package/dist/index.mjs CHANGED
@@ -151,7 +151,7 @@ Re-run with --json to print the full token value.`))})}function Xs(){let e=new x
151
151
  `});if(T)throw T;let ne=D.exit;if(!ne.success||!x||!C||C.operationId!==h||C.command!==`shutdown`)throw new V(`BRAID_RPC_FAILED`,`Braid graph analysis did not finish`,ne.spawnError??ne.timeoutDiagnostic??ne.stdinError??ne.processGroupError??ne.captureError??ne.terminateError??(D.stderr||`Exit ${ne.code}, signal ${ne.signal}; complete shutdown acknowledgement: ${!!C}`),`npm install --global @tangle-network/braid`);let re=mc.parse(x),ie=re.view.capabilities[`analysis.recipe`];if(t.dryRun)return{source:a,statePath:u,configuration:re.state.runConfiguration,capability:ie};if(!S||S.operationId!==m||S.command!==`analyze`)throw Error(`Braid did not acknowledge this analysis operation`);let ae=hc.parse(S.result);if(!i.schema.safeParse(ae.source.runtimeRun).success)throw new V(`BRAID_RPC_FAILED`,`Braid returned analysis of another source`,`Its saved run or immutable checkpoint identity does not match this request`,`tangle graph inspect ${e}`);return{...ae,statePath:u}})},`tangle graph analyze ${e}`)}function yc(e){e.command(`analyze <run>`).description(`Analyze retained graph evidence through Braid`).addHelpText(`after`,`
152
152
  Requires the Braid companion CLI: npm install --global @tangle-network/braid.
153
153
  BRAID_BIN may select its installed executable. Select an existing canonical AgentProfile with --profile. Analysis uses the owned subscription-only Bridge.
154
- `).requiredOption(`--profile <name|path>`,`Use a canonical analysis AgentProfile, distinct from the Tangle account profile`).option(`--dry-run`,`Inspect Braid's selected analysis configuration without dispatch`).option(`--timeout <ms>`,`Bound the analysis process in milliseconds`,Number,3e5).action(async(e,t)=>{let n=await vc(e,t);I(n),`status`in n&&n.status!==`completed`&&(process.exitCode=1)})}const bc=1e3;function xc(e,t){return[...t,e]}function Sc(e,t=`input`,n){if(n===void 0&&!e?.length)return;let r={};if(n!==void 0)try{if(r=JSON.parse(k(n,`utf8`)),!r||typeof r!=`object`||Array.isArray(r)||Object.entries(r).some(([e,t])=>!e.length||typeof t!=`string`))throw Error(`Invalid input map`)}catch{throw new V(`WORKFLOW_INPUT_FILE_INVALID`,`Could not load --input-file '${n}'`,`Expected a readable JSON object with nonempty keys and string values`,`tangle workflows run --help`)}let i=Object.entries(r);for(let n of e??[]){let e=n.indexOf(`=`);if(e<=0)throw Error(`Invalid --${t} "${n}": expected key=value (e.g. pull_request.number=123)`);i.push([n.slice(0,e),n.slice(e+1)])}return Object.fromEntries(i)}function Cc(e){F(e.map(e=>({id:e.id,status:e.status,attempts:e.attempts,created:e.createdAt,completed:e.completedAt??``})),[{key:`id`,header:`Run ID`},{key:`status`,header:`Status`},{key:`attempts`,header:`Attempts`},{key:`created`,header:`Created`},{key:`completed`,header:`Completed`}])}function wc(e){if(B({"Run ID":e.id,Workflow:e.workflowId,Status:e.status,Error:e.error??``,Attempts:e.attempts,Created:e.createdAt,Started:e.startedAt??``,Completed:e.completedAt??``}),e.actionResults.length>0){R(`Actions`);let t=e.actionResults.some(e=>e.nodeId!==void 0||e.visit!==void 0);F(e.actionResults.map(e=>({index:e.index,...t?{node:e.nodeId??``,visit:e.visit??``}:{},kind:e.kind,status:e.status,error:e.error??``,cost:e.costUsd??``})),[{key:`index`,header:`#`},...t?[{key:`node`,header:`Node`},{key:`visit`,header:`Visit`}]:[],{key:`kind`,header:`Kind`},{key:`status`,header:`Status`},{key:`error`,header:`Error`},{key:`cost`,header:`Cost (USD)`}])}e.graph&&(R(`Graph`),B({Nodes:e.graph.nodes.length,Edges:e.graph.edges.length,"Max node visits":e.graph.maxNodeVisits}),F(e.graph.edges.map(e=>({from:e.from,to:e.to,guard:e.when===void 0?``:JSON.stringify(e.when)})),[{key:`from`,header:`From`},{key:`to`,header:`To`},{key:`guard`,header:`Guard`}])),e.edgeVerdicts&&e.edgeVerdicts.length>0&&(R(`Edge verdicts`),F(e.edgeVerdicts.flatMap(e=>Object.entries(e.verdicts).map(([t,n])=>({node:e.nodeId,edge:t,verdict:n?`taken`:`not taken`}))),[{key:`node`,header:`Node`},{key:`edge`,header:`Edge`},{key:`verdict`,header:`Verdict`}]))}function Tc(e){if(e.status===`cancelled`){R(`Cancelled run ${e.runId}.`);return}if(e.signalled){R(`Cancelling run ${e.runId} — signalled the worker; it will stop shortly.`);return}R(`Requested cancellation of run ${e.runId}, but couldn't confirm it — the run may have just finished, or it's running on another instance and will stop on its own.`)}function Ec(e){switch(e.type){case`snapshot`:R(`snapshot: ${e.run.status} (${e.run.actionResults.length} action(s))`);break;case`action.started`:console.log(`→ action[${e.index}] ${e.kind} started`);break;case`action.finished`:{let t=e.status===`succeeded`?`✓`:e.status===`skipped`?`⊘`:`✗`;console.log(`${t} action[${e.index}] ${e.kind} ${e.status}${e.error?`: ${e.error}`:``}`);break}case`iteration.started`:console.log(` · action[${e.actionIndex}] iteration ${e.iterationIndex} started`);break;case`iteration.ended`:console.log(` · action[${e.actionIndex}] iteration ${e.iterationIndex} ${e.status}`);break;case`token`:typeof e.delta==`string`&&process.stdout.write(e.delta);break;case`ping`:break;case`run.done`:console.log(`\nrun.done: ${e.status}${e.error?`: ${e.error}`:``}`);break;case`run.waiting`:console.log(`\nrun.waiting: parked on a human decision (${e.decisionId}) — resolve it to resume`);break}}function Dc(e){e.command(`run`).description(`Trigger an immediate run of a workflow`).argument(`<id>`,`Workflow ID`).option(`--input-file <file>`,`Trigger inputs from a flat JSON string map; --input overrides its keys`).option(`--input <key=value>`,`Trigger input as key=value (e.g. pull_request.number=123). Repeatable.`,xc,[]).option(`--wait`,`Wait for the run to finish and print its result`).option(`--timeout <ms>`,`With --wait, fail if the run has not finished after this many milliseconds`).option(`--poll-interval <ms>`,`With --wait, how often to poll for completion (default 2000)`).action(async(e,t)=>{let n=Sc(t.input,`input`,t.inputFile),r=t.timeout===void 0?void 0:ro(t.timeout,`--timeout`),i=t.pollInterval===void 0?void 0:ro(t.pollInterval,`--poll-interval`),a=await Z(t),{runId:o}=await a.workflows.run(e,n);if(!t.wait){if(t.json)return I({runId:o});R(`Started run ${o} for workflow ${e}.`),R(`Inspect it with: tangle workflows run-detail ${e} ${o}`);return}let s=t.json?null:z(`Waiting for run ${o} to finish...`).start(),c;try{c=await a.workflows.waitForRun(e,o,{...r===void 0?{}:{timeoutMs:r},...i===void 0?{}:{pollIntervalMs:i}})}finally{s?.stop()}if(c.status===`failed`&&(process.exitCode=1),t.json)return I(c);wc(c)}),e.command(`runs`).description(`List a workflow's run history (newest first)`).argument(`<id>`,`Workflow ID`).option(`--limit <n>`,`Runs per page (1-100)`).option(`--cursor <cursor>`,`Pagination cursor from a previous page`).option(`--all`,`Fetch every page, not just the first`).action(async(e,t)=>{let n=await Z(t),r=t.limit===void 0?void 0:ro(t.limit,`--limit`);if(t.all){let i=[],a=t.cursor,o=0,s=null;for(;;){let c=await n.workflows.listRuns(e,{...r===void 0?{}:{limit:r},...a===void 0?{}:{cursor:a}});if(i.push(...c.runs),o+=1,!c.nextCursor)break;if(o>=bc){s=c.nextCursor,t.json||R(`Stopped after ${bc} pages (${i.length} runs); more may exist — resume with --cursor ${c.nextCursor}`);break}a=c.nextCursor}if(t.json)return I({runs:i,nextCursor:s});Cc(i);return}let i=await n.workflows.listRuns(e,{...r===void 0?{}:{limit:r},...t.cursor===void 0?{}:{cursor:t.cursor}});if(t.json)return I(i);Cc(i.runs),i.nextCursor&&R(`More runs available — next page: --cursor ${i.nextCursor}`)}),e.command(`run-detail`).description(`Show a single run's detail (per-action output and errors)`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID`).action(async(e,t,n)=>{let r=await(await Z(n)).workflows.getRun(e,t);if(n.json)return I(r);wc(r)}),e.command(`cancel`).description(`Cancel a queued or in-progress run`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID`).action(async(e,t,n)=>{let r=await(await Z(n)).workflows.cancel(e,t);if(n.json)return I(r);Tc(r)}),e.command(`retry`).description(`Re-run a failed or cancelled run (a fresh run with the same trigger context)`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID of the failed/cancelled run to retry`).option(`--wait`,`Wait for the new run to finish and print its result`).option(`--timeout <ms>`,`With --wait, fail if the run has not finished after this many milliseconds`).option(`--poll-interval <ms>`,`With --wait, how often to poll for completion (default 2000)`).action(async(e,t,n)=>{let r=n.timeout===void 0?void 0:ro(n.timeout,`--timeout`),i=n.pollInterval===void 0?void 0:ro(n.pollInterval,`--poll-interval`),a=await Z(n),o=await a.workflows.retryRun(e,t);if(!n.wait){if(n.json)return I(o);R(`Started run ${o.runId} (retry of ${t}).`),R(`Inspect it with: tangle workflows run-detail ${e} ${o.runId}`);return}let s=n.json?null:z(`Waiting for run ${o.runId} to finish...`).start(),c;try{c=await a.workflows.waitForRun(e,o.runId,{...r===void 0?{}:{timeoutMs:r},...i===void 0?{}:{pollIntervalMs:i}})}finally{s?.stop()}if(c.status===`failed`&&(process.exitCode=1),n.json)return I(c);wc(c)}),e.command(`events`).description(`Tail a run's live progress events until it finishes`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID`).action(async(e,t,n)=>{let r=await Z(n);for await(let i of r.workflows.watchRun(e,t))n.json?console.log(JSON.stringify(i)):Ec(i),i.type===`run.done`&&i.status===`failed`&&(process.exitCode=1)})}async function Oc(e,t,n,r){let i=await Z({...n,signal:r}),a=Pl(t),o=Sc(n.param,`param`),s={name:pe(e).replace(/(\.graph)?\.(yaml|yml|json)$/i,``),...o?{params:o}:{},...n.seat?{seat:n.seat}:{}},c,l;if(n.resume){let e=await i.workflows.getRun(n.resume,{signal:r});e.id!==n.resume&&X(`The cloud owner returned another run identity`);let t=e.execution;if((t?.kind!==`graph`||t.sourceSha!==a)&&X(`The authored cloud graph source changed`),Object.entries(o??{}).some(([e,n])=>!Object.hasOwn(t.params,e)||ie(t.params[e])!==ie(n))&&X(`A recorded cloud graph parameter changed`),n.seat!==void 0&&n.seat!==t.deploy.seat&&X(`The recorded cloud seat override changed`),n.dryRun)return{run:e};if((e.status===`cancelled`||e.status===`failed`)&&X(`The cloud run is terminal: ${e.status}`),e.status===`succeeded`)return e.graphResult||X(`The settled cloud run has no retained Runtime result`),{workflowId:e.workflowId,runId:e.id,sourceDigest:a,result:e.graphResult,replayed:!0,viewerUrl:`${i.baseUrl}/app/workflows/${encodeURIComponent(e.workflowId)}/runs/${encodeURIComponent(e.id)}`};if(e.recovery?.state===`running`)return{workflowId:e.workflowId,runId:e.id,sourceDigest:a,run:e,attached:!0,viewerUrl:`${i.baseUrl}/app/workflows/${encodeURIComponent(e.workflowId)}/runs/${encodeURIComponent(e.id)}`};e.recovery?.state!==`queued`&&e.recovery?.state!==`recoverable`&&X(`The owner has not confirmed recovery eligibility`),c=e.workflowId,l=e.revUsed??X(`The captured cloud revision is unavailable`)}else{if(n.dryRun)return i.workflows.validate(t,{format:`graph`,deploy:s});let e=await i.workflows.create(t,{format:`graph`,deploy:s});c=e.id,l=e.headRev??X(`The cloud workflow has no retained revision`)}r.throwIfAborted();let u=await i.workflows.run(c,void 0,{signal:r,sourceSha:a,expectedRev:l,...n.resume?{resumeRunId:n.resume}:{}});return n.resume&&u.runId!==n.resume&&X(`The cloud owner recovered another run identity`),{...u,workflowId:c,sourceDigest:a,viewerUrl:`${i.baseUrl}/app/workflows/${encodeURIComponent(c)}/runs/${encodeURIComponent(u.runId)}`}}const kc=[`claude-code`,`opencode`,`codex`,`kimi-code`,`prime`],Ac=process.env.TANGLE_ROUTER_URL??`https://router.tangle.tools/v1`;function jc(e){let t=ae(e);if(t)return t;throw Error(`--model '${e}' has no provider. Pass a canonical id, for example 'google/${e}'.`)}function Mc(e){let t=e.harness??`claude-code`;if(!kc.includes(t))throw Error(`Unknown --harness '${t}'. Supported: ${kc.join(`, `)}.`);let n=Nc(e.maxTokens,2e5,`--max-tokens`),r=Nc(e.maxIterations,50,`--max-iterations`),i=e.maxWorkers===void 0?void 0:Nc(e.maxWorkers,0,`--max-workers`);return{harness:t,...e.model?{model:e.model}:{},maxTokens:n,maxIterations:r,...i===void 0?{}:{maxWorkers:i}}}function Nc(e,t,n){if(e===void 0)return t;let r=Number.parseInt(e,10);if(!Number.isFinite(r)||r<=0)throw Error(`${n} must be a positive integer, got '${e}'.`);return r}function Pc(e){let t={maxTokens:e.maxTokens,maxIterations:e.maxIterations};return e.maxWorkers===void 0?{budget:t}:{budget:t,perWorker:{maxTokens:Math.max(1,Math.floor(e.maxTokens/e.maxWorkers)),maxIterations:Math.max(1,Math.floor(e.maxIterations/e.maxWorkers))}}}function Fc(e){let t=e.spentTotal,n=e.kind===`no-winner`?``:e.out;return{text:typeof n==`string`?n:typeof n==`object`&&n&&`content`in n&&typeof n.content==`string`?n.content:JSON.stringify(n),tokenUsage:{input:t.tokensKnown===!1?null:t.tokens.input,output:t.tokensKnown===!1?null:t.tokens.output},costUsd:t.usdKnown===!1?null:t.usd,status:e.kind===`no-winner`?`no-winner (${e.reason})`:`winner`}}let Ic=null;function Lc(){return Ic??=import(`@tangle-network/agent-runtime/kernel`).catch(e=>{throw Ic=null,e?.code===`ERR_MODULE_NOT_FOUND`?new V(`RUNTIME_UNAVAILABLE`,`Agent Runtime could not be loaded`,e.message,`npm install -g @tangle-network/sandbox-cli@latest`):e}),Ic}async function Rc(e,t,n){let r=W({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=G(r),{budget:a,perWorker:o}=Pc(t),s=[],c={async create(e){let{TANGLE_API_KEY:t,...r}=e?.env??{},a=await i.create(e?.env?{...e,env:r}:e);return s.push({id:a.id,status:`spawned`}),n.json||R(`worker ${a.id} spawned`),a}},l=await Lc(),u=l.workerFromBackend({backend:`sandbox`,sandboxClient:c,maxIterations:1}),d=(e,n)=>u({...e,harness:t.harness},n),f={name:`supervisor`,harness:`cli-base`,model:{default:t.model??`gemini-2.5-flash-lite`,provider:t.model?jc(t.model):`google`},tools:{agent_runtime_coordination_spawn_worker:!0,agent_runtime_coordination_observe_agent:!0}},p={routerBaseUrl:Ac,routerKey:r.apiKey},m=Fc(await l.supervise(f,{goal:e},{budget:a,makeWorkerAgent:d,router:p,...o?{perWorker:o}:{},...t.model?{allowedModels:[t.model]}:{},runId:`supervise-${w()}`})),h=m.status===`winner`?`settled`:`failed`;for(let e of s)e.status=h,n.json||R(`worker ${e.id} settled (${e.status})`);return{task:e,result:m.text,workers:s,tokenUsage:m.tokenUsage,costUsd:m.costUsd,status:m.status}}function zc(){let e=new x(`supervise`);return e.description(`Run a budget-conserving supervisor that spawns isolated sandbox workers to deliver a task.`).argument(`<task>`,`What the supervisor should accomplish`).option(`--harness <name>`,`Worker harness (${kc.join(` | `)})`,`claude-code`).option(`--model <id>`,`Model for the supervisor + workers`).option(`--max-tokens <n>`,`Conserved token budget for the whole run`,`200000`).option(`--max-iterations <n>`,`Conserved iteration budget for the whole run`,`50`).option(`--max-workers <n>`,`Bound the fanout; each worker reserves floor(max-tokens / n) tokens and floor(max-iterations / n) iterations`).action(async(e,t)=>{let n=Mc({harness:t.harness,model:t.model,maxTokens:t.maxTokens,maxIterations:t.maxIterations,maxWorkers:t.maxWorkers});t.json||(R(`Supervising: ${e}`),R(`harness=${n.harness} budget=${n.maxTokens}tok/${n.maxIterations}it`),console.log());let r=await Rc(e,n,{apiKey:t.apiKey,baseUrl:t.baseUrl,json:t.json});if(t.json){I(r),r.status!==`winner`&&(process.exitCode=1);return}if(console.log(),r.result&&(console.log(r.result),console.log()),B({"Input Tokens":r.tokenUsage.input??`Unavailable`,"Output Tokens":r.tokenUsage.output??`Unavailable`,Cost:r.costUsd===null?`Unavailable`:`$${r.costUsd.toFixed(4)}`}),r.status!==`winner`)throw Error(`Supervisor finished without a winner: ${r.status}`)}),e}const Bc=Ye(Se),Vc=async(e,...t)=>{let n={maxBuffer:1<<28};return(await Bc(`bash`,[`-ec`,e,`graph-improve`,...t],n)).stdout.trim()},Hc=j(M(),`.tangle`,`graph-improve`,`v5`),Uc=j(M(),`.tangle`,`workspaces`),Wc=e=>!!e&&e.owner!==`work`&&e.code!==`budget-exhausted`;async function Gc(e){await Ve(j(e,`bin`)),await Ve(j(e,`remotes`));let t=`#!/bin/sh
154
+ `).requiredOption(`--profile <name|path>`,`Use a canonical analysis AgentProfile, distinct from the Tangle account profile`).option(`--dry-run`,`Inspect Braid's selected analysis configuration without dispatch`).option(`--timeout <ms>`,`Bound the analysis process in milliseconds`,Number,3e5).action(async(e,t)=>{let n=await vc(e,t);I(n),`status`in n&&n.status!==`completed`&&(process.exitCode=1)})}const bc=1e3;function xc(e,t){return[...t,e]}function Sc(e,t=`input`,n){if(n===void 0&&!e?.length)return;let r={};if(n!==void 0)try{if(r=JSON.parse(k(n,`utf8`)),!r||typeof r!=`object`||Array.isArray(r)||Object.entries(r).some(([e,t])=>!e.length||typeof t!=`string`))throw Error(`Invalid input map`)}catch{throw new V(`WORKFLOW_INPUT_FILE_INVALID`,`Could not load --input-file '${n}'`,`Expected a readable JSON object with nonempty keys and string values`,`tangle workflows run --help`)}let i=Object.entries(r);for(let n of e??[]){let e=n.indexOf(`=`);if(e<=0)throw Error(`Invalid --${t} "${n}": expected key=value (e.g. pull_request.number=123)`);i.push([n.slice(0,e),n.slice(e+1)])}return Object.fromEntries(i)}function Cc(e){F(e.map(e=>({id:e.id,status:e.status,attempts:e.attempts,created:e.createdAt,completed:e.completedAt??``})),[{key:`id`,header:`Run ID`},{key:`status`,header:`Status`},{key:`attempts`,header:`Attempts`},{key:`created`,header:`Created`},{key:`completed`,header:`Completed`}])}function wc(e){if(B({"Run ID":e.id,Workflow:e.workflowId,Status:e.status,Error:e.error??``,Attempts:e.attempts,Created:e.createdAt,Started:e.startedAt??``,Completed:e.completedAt??``}),e.actionResults.length>0){R(`Actions`);let t=e.actionResults.some(e=>e.nodeId!==void 0||e.visit!==void 0);F(e.actionResults.map(e=>({index:e.index,...t?{node:e.nodeId??``,visit:e.visit??``}:{},kind:e.kind,status:e.status,error:e.error??``,cost:e.costUsd??``})),[{key:`index`,header:`#`},...t?[{key:`node`,header:`Node`},{key:`visit`,header:`Visit`}]:[],{key:`kind`,header:`Kind`},{key:`status`,header:`Status`},{key:`error`,header:`Error`},{key:`cost`,header:`Cost (USD)`}])}e.graph&&(R(`Graph`),B({Nodes:e.graph.nodes.length,Edges:e.graph.edges.length,"Max node visits":e.graph.maxNodeVisits}),F(e.graph.edges.map(e=>({from:e.from,to:e.to,guard:e.when===void 0?``:JSON.stringify(e.when)})),[{key:`from`,header:`From`},{key:`to`,header:`To`},{key:`guard`,header:`Guard`}])),e.edgeVerdicts&&e.edgeVerdicts.length>0&&(R(`Edge verdicts`),F(e.edgeVerdicts.flatMap(e=>Object.entries(e.verdicts).map(([t,n])=>({node:e.nodeId,edge:t,verdict:n?`taken`:`not taken`}))),[{key:`node`,header:`Node`},{key:`edge`,header:`Edge`},{key:`verdict`,header:`Verdict`}]))}function Tc(e){if(e.status===`cancelled`){R(`Cancelled run ${e.runId}.`);return}if(e.signalled){R(`Cancelling run ${e.runId} — signalled the worker; it will stop shortly.`);return}R(`Requested cancellation of run ${e.runId}, but couldn't confirm it — the run may have just finished, or it's running on another instance and will stop on its own.`)}function Ec(e){switch(e.type){case`snapshot`:R(`snapshot: ${e.run.status} (${e.run.actionResults.length} action(s))`);break;case`action.started`:console.log(`→ action[${e.index}] ${e.kind} started`);break;case`action.finished`:{let t=e.status===`succeeded`?`✓`:e.status===`skipped`?`⊘`:`✗`;console.log(`${t} action[${e.index}] ${e.kind} ${e.status}${e.error?`: ${e.error}`:``}`);break}case`iteration.started`:console.log(` · action[${e.actionIndex}] iteration ${e.iterationIndex} started`);break;case`iteration.ended`:console.log(` · action[${e.actionIndex}] iteration ${e.iterationIndex} ${e.status}`);break;case`token`:typeof e.delta==`string`&&process.stdout.write(e.delta);break;case`ping`:break;case`run.done`:console.log(`\nrun.done: ${e.status}${e.error?`: ${e.error}`:``}`);break;case`run.waiting`:console.log(`\nrun.waiting: parked on a human decision (${e.decisionId}) — resolve it to resume`);break}}function Dc(e){e.command(`run`).description(`Trigger an immediate run of a workflow`).argument(`<id>`,`Workflow ID`).option(`--input-file <file>`,`Trigger inputs from a flat JSON string map; --input overrides its keys`).option(`--input <key=value>`,`Trigger input as key=value (e.g. pull_request.number=123). Repeatable.`,xc,[]).option(`--wait`,`Wait for the run to finish and print its result`).option(`--timeout <ms>`,`With --wait, fail if the run has not finished after this many milliseconds`).option(`--poll-interval <ms>`,`With --wait, how often to poll for completion (default 2000)`).action(async(e,t)=>{let n=Sc(t.input,`input`,t.inputFile),r=t.timeout===void 0?void 0:ro(t.timeout,`--timeout`),i=t.pollInterval===void 0?void 0:ro(t.pollInterval,`--poll-interval`),a=await Z(t),{runId:o}=await a.workflows.run(e,n);if(!t.wait){if(t.json)return I({runId:o});R(`Started run ${o} for workflow ${e}.`),R(`Inspect it with: tangle workflows run-detail ${e} ${o}`);return}let s=t.json?null:z(`Waiting for run ${o} to finish...`).start(),c;try{c=await a.workflows.waitForRun(e,o,{...r===void 0?{}:{timeoutMs:r},...i===void 0?{}:{pollIntervalMs:i}})}finally{s?.stop()}if(c.status===`failed`&&(process.exitCode=1),t.json)return I(c);wc(c)}),e.command(`runs`).description(`List a workflow's run history (newest first)`).argument(`<id>`,`Workflow ID`).option(`--limit <n>`,`Runs per page (1-100)`).option(`--cursor <cursor>`,`Pagination cursor from a previous page`).option(`--all`,`Fetch every page, not just the first`).action(async(e,t)=>{let n=await Z(t),r=t.limit===void 0?void 0:ro(t.limit,`--limit`);if(t.all){let i=[],a=t.cursor,o=0,s=null;for(;;){let c=await n.workflows.listRuns(e,{...r===void 0?{}:{limit:r},...a===void 0?{}:{cursor:a}});if(i.push(...c.runs),o+=1,!c.nextCursor)break;if(o>=bc){s=c.nextCursor,t.json||R(`Stopped after ${bc} pages (${i.length} runs); more may exist — resume with --cursor ${c.nextCursor}`);break}a=c.nextCursor}if(t.json)return I({runs:i,nextCursor:s});Cc(i);return}let i=await n.workflows.listRuns(e,{...r===void 0?{}:{limit:r},...t.cursor===void 0?{}:{cursor:t.cursor}});if(t.json)return I(i);Cc(i.runs),i.nextCursor&&R(`More runs available — next page: --cursor ${i.nextCursor}`)}),e.command(`run-detail`).description(`Show a single run's detail (per-action output and errors)`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID`).action(async(e,t,n)=>{let r=await(await Z(n)).workflows.getRun(e,t);if(n.json)return I(r);wc(r)}),e.command(`cancel`).description(`Cancel a queued or in-progress run`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID`).action(async(e,t,n)=>{let r=await(await Z(n)).workflows.cancel(e,t);if(n.json)return I(r);Tc(r)}),e.command(`retry`).description(`Re-run a failed or cancelled run (a fresh run with the same trigger context)`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID of the failed/cancelled run to retry`).option(`--wait`,`Wait for the new run to finish and print its result`).option(`--timeout <ms>`,`With --wait, fail if the run has not finished after this many milliseconds`).option(`--poll-interval <ms>`,`With --wait, how often to poll for completion (default 2000)`).action(async(e,t,n)=>{let r=n.timeout===void 0?void 0:ro(n.timeout,`--timeout`),i=n.pollInterval===void 0?void 0:ro(n.pollInterval,`--poll-interval`),a=await Z(n),o=await a.workflows.retryRun(e,t);if(!n.wait){if(n.json)return I(o);R(`Started run ${o.runId} (retry of ${t}).`),R(`Inspect it with: tangle workflows run-detail ${e} ${o.runId}`);return}let s=n.json?null:z(`Waiting for run ${o.runId} to finish...`).start(),c;try{c=await a.workflows.waitForRun(e,o.runId,{...r===void 0?{}:{timeoutMs:r},...i===void 0?{}:{pollIntervalMs:i}})}finally{s?.stop()}if(c.status===`failed`&&(process.exitCode=1),n.json)return I(c);wc(c)}),e.command(`events`).description(`Tail a run's live progress events until it finishes`).argument(`<id>`,`Workflow ID`).argument(`<runId>`,`Run ID`).action(async(e,t,n)=>{let r=await Z(n);for await(let i of r.workflows.watchRun(e,t))n.json?console.log(JSON.stringify(i)):Ec(i),i.type===`run.done`&&i.status===`failed`&&(process.exitCode=1)})}async function Oc(e,t,n,r){let i=await Z({...n,signal:r}),a=Pl(t),o=Sc(n.param,`param`),s={name:pe(e).replace(/(\.graph)?\.(yaml|yml|json)$/i,``),...o?{params:o}:{},...n.seat?{seat:n.seat}:{}},c,l;if(n.resume){let e=await i.workflows.getRun(n.resume,{signal:r});e.id!==n.resume&&X(`The cloud owner returned another run identity`);let t=e.execution;if((t?.kind!==`graph`||t.sourceSha!==a)&&X(`The authored cloud graph source changed`),Object.entries(o??{}).some(([e,n])=>!Object.hasOwn(t.params,e)||ie(t.params[e])!==ie(n))&&X(`A recorded cloud graph parameter changed`),n.seat!==void 0&&n.seat!==t.deploy.seat&&X(`The recorded cloud seat override changed`),n.dryRun)return{run:e};if((e.status===`cancelled`||e.status===`failed`)&&X(`The cloud run is terminal: ${e.status}`),e.status===`succeeded`)return e.graphResult||X(`The settled cloud run has no retained Runtime result`),{workflowId:e.workflowId,runId:e.id,sourceDigest:a,result:e.graphResult,replayed:!0,viewerUrl:`${i.baseUrl}/app/workflows/${encodeURIComponent(e.workflowId)}/runs/${encodeURIComponent(e.id)}`};if(e.recovery?.state===`running`)return{workflowId:e.workflowId,runId:e.id,sourceDigest:a,run:e,attached:!0,viewerUrl:`${i.baseUrl}/app/workflows/${encodeURIComponent(e.workflowId)}/runs/${encodeURIComponent(e.id)}`};e.recovery?.state!==`queued`&&e.recovery?.state!==`recoverable`&&X(`The owner has not confirmed recovery eligibility`),c=e.workflowId,l=e.revUsed??X(`The captured cloud revision is unavailable`)}else{if(n.dryRun)return i.workflows.validate(t,{format:`graph`,deploy:s});let e=await i.workflows.create(t,{format:`graph`,deploy:s});c=e.id,l=e.headRev??X(`The cloud workflow has no retained revision`)}r.throwIfAborted();let u=await i.workflows.run(c,void 0,{signal:r,sourceSha:a,expectedRev:l,...n.resume?{resumeRunId:n.resume}:{}});return n.resume&&u.runId!==n.resume&&X(`The cloud owner recovered another run identity`),{...u,workflowId:c,sourceDigest:a,viewerUrl:`${i.baseUrl}/app/workflows/${encodeURIComponent(c)}/runs/${encodeURIComponent(u.runId)}`}}const kc=[`claude-code`,`opencode`,`codex`,`kimi-code`,`prime`],Ac=process.env.TANGLE_ROUTER_URL??`https://router.tangle.tools/v1`;function jc(e){let t=ae(e);if(t)return t;throw Error(`--model '${e}' has no provider. Pass a canonical id, for example 'google/${e}'.`)}function Mc(e){let t=e.harness??`claude-code`;if(!kc.includes(t))throw Error(`Unknown --harness '${t}'. Supported: ${kc.join(`, `)}.`);let n=Nc(e.maxTokens,2e5,`--max-tokens`),r=Nc(e.maxIterations,50,`--max-iterations`),i=e.maxWorkers===void 0?void 0:Nc(e.maxWorkers,0,`--max-workers`);return{harness:t,...e.model?{model:e.model}:{},maxTokens:n,maxIterations:r,...i===void 0?{}:{maxWorkers:i}}}function Nc(e,t,n){if(e===void 0)return t;let r=Number.parseInt(e,10);if(!Number.isFinite(r)||r<=0)throw Error(`${n} must be a positive integer, got '${e}'.`);return r}function Pc(e){let t={maxTokens:e.maxTokens,maxIterations:e.maxIterations};return e.maxWorkers===void 0?{budget:t}:{budget:t,perWorker:{maxTokens:Math.max(1,Math.floor(e.maxTokens/e.maxWorkers)),maxIterations:Math.max(1,Math.floor(e.maxIterations/e.maxWorkers))}}}function Fc(e){let t=e.spentTotal,n=e.kind===`no-winner`?``:e.out;return{text:typeof n==`string`?n:typeof n==`object`&&n&&`content`in n&&typeof n.content==`string`?n.content:JSON.stringify(n),tokenUsage:{input:t.tokensKnown===!1?null:t.tokens.input,output:t.tokensKnown===!1?null:t.tokens.output},costUsd:t.usdKnown===!1?null:t.usd,status:e.kind===`no-winner`?`no-winner (${e.reason})`:`winner`}}let Ic=null;function Lc(){return Ic??=import(`@tangle-network/agent-runtime/kernel`).catch(e=>{throw Ic=null,e?.code===`ERR_MODULE_NOT_FOUND`?new V(`RUNTIME_UNAVAILABLE`,`Agent Runtime could not be loaded`,e.message,`npm install -g @tangle-network/sandbox-cli@latest`):e}),Ic}async function Rc(e,t,n){let r=W({apiKey:n.apiKey,baseUrl:n.baseUrl}),i=G(r),{budget:a,perWorker:o}=Pc(t),s=[],c={async create(e){let{TANGLE_API_KEY:t,...r}=e?.env??{},a=await i.create(e?.env?{...e,env:r}:e);return s.push({id:a.id,status:`spawned`}),n.json||R(`worker ${a.id} spawned`),a}},l=await Lc(),u=l.workerFromBackend({backend:`sandbox`,sandboxClient:c,maxIterations:1}),d=(e,n)=>u({...e,harness:t.harness},n),f={name:`supervisor`,harness:`cli-base`,model:{default:t.model??`gemini-3.5-flash-lite`,provider:t.model?jc(t.model):`google`},tools:{agent_runtime_coordination_spawn_worker:!0,agent_runtime_coordination_observe_agent:!0}},p={routerBaseUrl:Ac,routerKey:r.apiKey},m=Fc(await l.supervise(f,{goal:e},{budget:a,makeWorkerAgent:d,router:p,...o?{perWorker:o}:{},...t.model?{allowedModels:[t.model]}:{},runId:`supervise-${w()}`})),h=m.status===`winner`?`settled`:`failed`;for(let e of s)e.status=h,n.json||R(`worker ${e.id} settled (${e.status})`);return{task:e,result:m.text,workers:s,tokenUsage:m.tokenUsage,costUsd:m.costUsd,status:m.status}}function zc(){let e=new x(`supervise`);return e.description(`Run a budget-conserving supervisor that spawns isolated sandbox workers to deliver a task.`).argument(`<task>`,`What the supervisor should accomplish`).option(`--harness <name>`,`Worker harness (${kc.join(` | `)})`,`claude-code`).option(`--model <id>`,`Model for the supervisor + workers`).option(`--max-tokens <n>`,`Conserved token budget for the whole run`,`200000`).option(`--max-iterations <n>`,`Conserved iteration budget for the whole run`,`50`).option(`--max-workers <n>`,`Bound the fanout; each worker reserves floor(max-tokens / n) tokens and floor(max-iterations / n) iterations`).action(async(e,t)=>{let n=Mc({harness:t.harness,model:t.model,maxTokens:t.maxTokens,maxIterations:t.maxIterations,maxWorkers:t.maxWorkers});t.json||(R(`Supervising: ${e}`),R(`harness=${n.harness} budget=${n.maxTokens}tok/${n.maxIterations}it`),console.log());let r=await Rc(e,n,{apiKey:t.apiKey,baseUrl:t.baseUrl,json:t.json});if(t.json){I(r),r.status!==`winner`&&(process.exitCode=1);return}if(console.log(),r.result&&(console.log(r.result),console.log()),B({"Input Tokens":r.tokenUsage.input??`Unavailable`,"Output Tokens":r.tokenUsage.output??`Unavailable`,Cost:r.costUsd===null?`Unavailable`:`$${r.costUsd.toFixed(4)}`}),r.status!==`winner`)throw Error(`Supervisor finished without a winner: ${r.status}`)}),e}const Bc=Ye(Se),Vc=async(e,...t)=>{let n={maxBuffer:1<<28};return(await Bc(`bash`,[`-ec`,e,`graph-improve`,...t],n)).stdout.trim()},Hc=j(M(),`.tangle`,`graph-improve`,`v5`),Uc=j(M(),`.tangle`,`workspaces`),Wc=e=>!!e&&e.owner!==`work`&&e.code!==`budget-exhausted`;async function Gc(e){await Ve(j(e,`bin`)),await Ve(j(e,`remotes`));let t=`#!/bin/sh
155
155
  [ "$1 $2" = "issue view" ] || { echo "gh: offline replay; only 'gh issue view <n>' answers" >&2; exit 1; }
156
156
  shift 2; n=; repo=; prev=
157
157
  for a; do
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/sandbox-cli",
3
- "version": "0.22.24",
3
+ "version": "0.22.25",
4
4
  "description": "CLI for Tangle Sandbox operations",
5
5
  "type": "module",
6
6
  "engines": {
@@ -25,7 +25,7 @@
25
25
  "@tangle-network/agent-runtime": "^8.0.0",
26
26
  "@tangle-network/harness-sessions": "^1.0.2",
27
27
  "@tangle-network/hub-sdk": "1.0.21",
28
- "@tangle-network/sandbox": "^1.0.292",
28
+ "@tangle-network/sandbox": "^1.0.294",
29
29
  "@tangle-network/sdk-cli-runner": "^0.3.10",
30
30
  "chalk": "^5.6.2",
31
31
  "cli-bridge": "https://github.com/drewstone/cli-bridge/archive/5798fd5297b8705f367f95ab966df7d8e2f875d2.tar.gz",
@@ -57,8 +57,8 @@
57
57
  "tsx": "^4.23.15",
58
58
  "typescript": "^6.0.3",
59
59
  "vitest": "^4.1.12",
60
- "@tangle-network/runtime-contracts": "0.20.0",
61
- "@repo/shared": "0.0.0"
60
+ "@repo/shared": "0.0.0",
61
+ "@tangle-network/runtime-contracts": "0.20.0"
62
62
  },
63
63
  "publishConfig": {
64
64
  "access": "public",
@@ -78,8 +78,8 @@
78
78
  "author": "Tangle Network",
79
79
  "license": "MIT",
80
80
  "tangleRelease": {
81
- "sourceSha": "b3c75836f8f156429c954cb348cb4a2280b551f8",
82
- "mergedAt": "2026-11-07T20:25:53-08:00"
81
+ "sourceSha": "a13c0ca3f56a29296456df8b3d3a53ca891361c3",
82
+ "mergedAt": "2026-11-07T21:01:52-08:00"
83
83
  },
84
84
  "scripts": {
85
85
  "build": "tsdown",