Run, inspect & trace a workflow
The same loop works from three places: create a workflow from YAML, run it,
read each step, and follow its events live. Pick the terminal, TypeScript, or
plain HTTP. All three use your sk-tan-… API key.
The definition
triage.yml runs one agent in a sandbox each time its webhook is called:
name: triage-note
on:
webhook: {}
do:
- agent.run:
profile:
name: triager
prompt:
systemPrompt: "You triage bug reports into severity and owner."
prompt: "Triage this report: ${trigger.text}"See the YAML reference for every trigger and action.
From the terminal
tangle workflows validate triage.yml # compile check; exits 1 when invalid
tangle workflows apply triage.yml # create, or update the workflow with this name
tangle workflows run <workflow-id> --wait \
--input text="Login button does nothing on Safari"
tangle workflows runs <workflow-id> # run history, newest first
tangle workflows run-detail <workflow-id> <run-id> # every step, its output and cost
tangle workflows events <workflow-id> <run-id> # follow a run live--wait blocks until the run finishes and prints the run. A run that reaches a
human decision step returns as waiting; resolve it with
tangle workflows decision-resolve <workflow-id> <run-id> --choice approve.
Every workflows command accepts --json for scripts.
From TypeScript
Workflows live on the Tangle platform. @tangle-network/hub-sdk is its client:
npm install @tangle-network/hub-sdkimport { readFileSync } from "node:fs";
import { HubClient } from "@tangle-network/hub-sdk";
const hub = new HubClient({
baseUrl: "https://id.tangle.tools",
apiKey: process.env.TANGLE_API_KEY!,
});
const yaml = readFileSync("triage.yml", "utf8");
const check = await hub.workflows.validate(yaml);
if (!check.valid) throw new Error(JSON.stringify(check.errors));
const workflow = await hub.workflows.create(yaml);
// Keys are the trigger paths the YAML reads, after `trigger.`.
const { runId } = await hub.workflows.run(workflow.id, {
text: "Login button does nothing on Safari",
});
for await (const event of hub.workflows.watchRun(workflow.id, runId)) {
if (event.type === "action.started") console.log("step", event.index, event.kind);
if (event.type === "token") process.stdout.write(event.delta);
if (event.type === "action.finished") console.log("\nstep", event.index, event.status, event.costUsd);
if (event.type === "run.waiting") console.log("waiting on decision", event.decisionId);
if (event.type === "run.done") console.log("run", event.status, event.error ?? "");
}
const run = await hub.workflows.getRun(workflow.id, runId);
console.log(run.status, run.actionResults);waitForRun(workflowId, runId, { timeoutMs }) polls instead of streaming. It
returns as soon as the run leaves queued or running, which includes
waiting on a decision.
Read an agent step's trace
An agent.run step records its execution timeline: messages, tool calls, and
results, paged by round.
let after: number | undefined;
do {
const page = await hub.workflows.listRunSteps(workflow.id, runId, {
action: 0, // the step's position in the run
iteration: 0, // the round within that step
after,
});
for (const step of page.steps) console.log(step);
after = page.nextCursor ?? undefined;
} while (after !== undefined);From HTTP
Base URL https://id.tangle.tools, header Authorization: Bearer $TANGLE_API_KEY.
Responses are wrapped as { "success": true, "data": … }.
| Step | Request |
|---|---|
| Validate | POST /v1/workflows/validate with { "yaml": "…" } |
| Create | POST /v1/workflows with { "yaml": "…" } |
| Run | POST /v1/workflows/{id}/run with { "inputs": { "text": "…" } }, returns { runId } |
| Run detail | GET /v1/workflows/{id}/runs/{runId} |
| Live events | GET /v1/workflows/{id}/runs/{runId}/events with Accept: text/event-stream |
| Agent steps | GET /v1/workflows/{id}/runs/{runId}/steps?action=0&iteration=0 |
| Cancel | POST /v1/workflows/{id}/runs/{runId}/cancel |
A workflow with a webhook trigger also gets its own URL. Create returns the
hook URL and its token once; the posted JSON object becomes ${trigger.*}, so
{ "text": "…" } fills ${trigger.text}:
curl -X POST "https://id.tangle.tools/v1/hooks/$WORKFLOW_ID" \
-H "Authorization: Bearer $HOOK_TOKEN" \
-H "Content-Type: application/json" \
-d '{"text": "Login button does nothing on Safari"}'Traces from sandboxes and fleets
A sandbox or fleet you drive directly keeps its own trace:
const trace = await box.trace(); // the sandbox's events and spans
await box.exportTrace({ // push the bundle to your collector
url: "https://otel.example.com/v1/traces",
format: "otel-json",
serviceName: "my-agents",
});fleet.trace() and fleet.exportTrace() do the same for a fleet.
format: "otel-json" sends OpenTelemetry JSON that any OTLP/HTTP collector
accepts.