@zibby/skills 0.2.22 → 0.2.24
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/dist/browser.js +3 -10
- package/dist/code-scan.d.ts +1 -1
- package/dist/index.js +81 -88
- package/dist/jira.d.ts +75 -9
- package/dist/jira.js +4 -4
- package/dist/package.json +1 -1
- package/dist/report.d.ts +51 -276
- package/dist/trackers/index.js +12 -12
- package/dist/trackers/jira-adapter.js +11 -11
- package/package.json +1 -1
- package/docs/analysis.md +0 -109
- package/docs/apps/agent-ops.md +0 -130
- package/docs/apps/auth.md +0 -158
- package/docs/apps/deploy.md +0 -207
- package/docs/apps/goal-mode.md +0 -175
- package/docs/apps/index.md +0 -149
- package/docs/apps/managing.md +0 -121
- package/docs/cli-reference.md +0 -494
- package/docs/cloning-repositories.md +0 -285
- package/docs/cloud/bundles.md +0 -92
- package/docs/cloud/dedicated-egress.md +0 -140
- package/docs/cloud/editing-prompts.md +0 -79
- package/docs/cloud/env-vars.md +0 -144
- package/docs/cloud/limits.md +0 -81
- package/docs/cloud/logs.md +0 -104
- package/docs/cloud/triggering.md +0 -128
- package/docs/concepts/agents.md +0 -112
- package/docs/concepts/designing-agents.md +0 -247
- package/docs/concepts/graph.md +0 -83
- package/docs/concepts/sessions.md +0 -70
- package/docs/concepts/skills.md +0 -86
- package/docs/concepts/state.md +0 -106
- package/docs/concepts/sub-graphs.md +0 -227
- package/docs/custom-workflows.md +0 -358
- package/docs/get-started/deploy.md +0 -75
- package/docs/get-started/install.md +0 -60
- package/docs/get-started/run-locally.md +0 -94
- package/docs/get-started/trigger-and-logs.md +0 -90
- package/docs/get-started/use-from-agents.md +0 -153
- package/docs/get-started/your-first-workflow.md +0 -69
- package/docs/getting-started.md +0 -108
- package/docs/installation.md +0 -127
- package/docs/integrations/github.md +0 -73
- package/docs/integrations/gitlab.md +0 -43
- package/docs/integrations/jira.md +0 -71
- package/docs/integrations/lark.md +0 -41
- package/docs/integrations/linear.md +0 -43
- package/docs/integrations/notion.md +0 -33
- package/docs/integrations/plane.md +0 -46
- package/docs/integrations/sentry.md +0 -42
- package/docs/integrations/slack.md +0 -33
- package/docs/intro.md +0 -86
- package/docs/legacy/test-automation.md +0 -111
- package/docs/packages/agent-workflow.md +0 -88
- package/docs/packages/cli.md +0 -73
- package/docs/packages/core.md +0 -72
- package/docs/packages/mcp-browser.md +0 -110
- package/docs/packages/mcp-cli.md +0 -176
- package/docs/packages/memory.md +0 -223
- package/docs/packages/skills.md +0 -218
- package/docs/packages/ui-memory.md +0 -245
- package/docs/recipes/bug-autofix.md +0 -85
- package/docs/recipes/github-ai-scout.md +0 -61
- package/docs/recipes/index.md +0 -68
- package/docs/recipes/pipeline-supervisor.md +0 -57
- package/docs/recipes/sentry-triage.md +0 -93
- package/docs/recipes/test.md +0 -190
- package/docs/reviewing-results.md +0 -114
- package/docs/running-tests.md +0 -134
- package/docs/self-host/backup-restore.md +0 -53
- package/docs/self-host/custom-sidecars.md +0 -136
- package/docs/self-host/index.md +0 -75
- package/docs/self-host/storage.md +0 -50
- package/docs/self-host/troubleshooting.md +0 -48
- package/docs/self-host/upgrade.md +0 -81
- package/docs/skills/browser.md +0 -97
- package/docs/skills/chat-memory.md +0 -122
- package/docs/skills/core-tools.md +0 -80
- package/docs/skills/function-skill.md +0 -93
- package/docs/skills/github.md +0 -91
- package/docs/skills/index.md +0 -46
- package/docs/skills/jira.md +0 -99
- package/docs/skills/lark.md +0 -85
- package/docs/skills/memory.md +0 -92
- package/docs/skills/sentry.md +0 -80
- package/docs/skills/slack.md +0 -89
- package/docs/tests/memory.md +0 -131
- package/docs/triggering-workflows.md +0 -552
- package/docs/workflow-artifact-layout-evaluation.md +0 -119
- package/docs/workflow.md +0 -558
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
sidebar_position: 7
|
|
3
|
-
title: Designing agents
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Designing agents
|
|
7
|
-
|
|
8
|
-
Before you build an agent, learn the three patterns that make Zibby agents
|
|
9
|
-
powerful. Almost every good agent is some combination of them:
|
|
10
|
-
|
|
11
|
-
1. **One agent, many entry points** — a single deployed agent that behaves
|
|
12
|
-
differently depending on *how* it was triggered (cron vs. a human vs. a
|
|
13
|
-
webhook), selected by a code **router** node.
|
|
14
|
-
2. **Zero-LLM code nodes that read & write Stores** — deterministic work
|
|
15
|
-
(fetch, ETL, aggregate, persist) runs as plain code, no model call, and can
|
|
16
|
-
read/write a Store (SQLite / file / dataset) directly.
|
|
17
|
-
3. **Event-driven agents** — one agent subscribes to several events (PR opened,
|
|
18
|
-
comment, `@mention`) and routes each to the right branch.
|
|
19
|
-
|
|
20
|
-
These are not exotic. The shipped **sentry-triage** and **engineering-insights**
|
|
21
|
-
agents use all three at once. Study them — they are the canonical reference for
|
|
22
|
-
"how an agent should be designed."
|
|
23
|
-
|
|
24
|
-
:::tip Design rule
|
|
25
|
-
**Don't reach for an LLM node when plain code will do.** A model call costs
|
|
26
|
-
tokens, adds latency, and is non-deterministic. Fetching data, transforming
|
|
27
|
-
JSON, running a query, writing to a Store, calling an API — all of that belongs
|
|
28
|
-
in a **code node**. Reserve LLM nodes for judgment: classify, summarize, decide.
|
|
29
|
-
:::
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## Pattern 1 — one agent, many entry points
|
|
34
|
-
|
|
35
|
-
The trap beginners fall into is building *two* agents: a "collector" that runs on
|
|
36
|
-
a schedule and a "reporter" a human triggers. Don't. Build **one** agent whose
|
|
37
|
-
**first node is a code router** that inspects the trigger and sends the run down
|
|
38
|
-
the right branch. One bundle, one deployment, one set of env/Store bindings.
|
|
39
|
-
|
|
40
|
-
```js
|
|
41
|
-
import { WorkflowGraph } from '@zibby/agent-workflow';
|
|
42
|
-
import { z } from '@zibby/core';
|
|
43
|
-
|
|
44
|
-
// The router is PURE CODE — no model call. It reads the trigger input off state
|
|
45
|
-
// and returns the name of the next node.
|
|
46
|
-
function route(state) {
|
|
47
|
-
// state.trigger / state.mode / state.action come from the trigger payload,
|
|
48
|
-
// spread onto the initial state by the runner.
|
|
49
|
-
return state?.trigger === 'fix' ? 'fix_intake' : 'fetch_issues';
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const graph = new WorkflowGraph()
|
|
53
|
-
.addNode('route', {
|
|
54
|
-
_isCustomCode: true,
|
|
55
|
-
description: 'Routes on the trigger: "fix" runs the single-issue auto-fix branch, anything else runs the scheduled triage branch.',
|
|
56
|
-
outputSchema: z.object({}).passthrough(),
|
|
57
|
-
execute: async (ctx) => ctx.state.getAll(), // router only decides an edge; it forwards state
|
|
58
|
-
})
|
|
59
|
-
// …branch nodes…
|
|
60
|
-
.setEntryPoint('route')
|
|
61
|
-
// A conditional edge: run `route`, then take the edge its return value names.
|
|
62
|
-
.addConditionalEdges('route', route, {
|
|
63
|
-
labels: { fix_intake: 'auto-fix one issue', fetch_issues: 'scheduled triage' },
|
|
64
|
-
});
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
The **same agent** then gets three trigger sources, all landing on `route`:
|
|
68
|
-
|
|
69
|
-
| Trigger | Payload | Branch it takes |
|
|
70
|
-
|---|---|---|
|
|
71
|
-
| Cron (nightly) | `{ mode: 'collect' }` | collect → persist to Store |
|
|
72
|
-
| Human `zibby agent trigger` | `{ action: 'report' }` | read Store → generate → deliver |
|
|
73
|
-
| Webhook / Lark `@bot` | `{ trigger: 'fix', issueId }` | single-item fix branch |
|
|
74
|
-
|
|
75
|
-
> **Real reference:** `sentry-triage` routes `state.trigger === 'fix'` to an
|
|
76
|
-
> auto-fix branch and everything else (incl. an absent trigger — the nightly
|
|
77
|
-
> cron) to scheduled triage. `engineering-insights` routes `state.action`
|
|
78
|
-
> between `collect` (meter commits → SQLite) and `report` (aggregate → charts).
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
## Pattern 2 — code nodes read & write Stores (no LLM)
|
|
83
|
-
|
|
84
|
-
A **code node** (`_isCustomCode: true` + an `execute` function) is a first-class
|
|
85
|
-
citizen. It runs in the same container as the rest of the graph and has the same
|
|
86
|
-
environment — including any **Store** bound to the node. So a deterministic node
|
|
87
|
-
can fetch from a Store, transform, and write results back, entirely without a
|
|
88
|
-
model.
|
|
89
|
-
|
|
90
|
-
### How a node reaches a Store
|
|
91
|
-
|
|
92
|
-
When you bind a Store to a node (via `zibby_set_node_stores`, the deploy modal,
|
|
93
|
-
or the CLI), the runtime injects three env vars into that node's container:
|
|
94
|
-
|
|
95
|
-
| Env var | What it is |
|
|
96
|
-
|---|---|
|
|
97
|
-
| `ZIBBY_STORE__<name>` | the **storeId** of the Store you bound as `<name>` |
|
|
98
|
-
| `ZIBBY_ACCOUNT_API_URL` | the base URL of the datasets API |
|
|
99
|
-
| `PROJECT_API_TOKEN` | a project-scoped bearer token |
|
|
100
|
-
|
|
101
|
-
The node just `fetch`es the datasets API. No SDK required.
|
|
102
|
-
|
|
103
|
-
```js
|
|
104
|
-
// A pure-code node: read from a SQLite Store, write to a file Store. Zero LLM.
|
|
105
|
-
const BASE = process.env.ZIBBY_ACCOUNT_API_URL;
|
|
106
|
-
const TOKEN = process.env.PROJECT_API_TOKEN;
|
|
107
|
-
|
|
108
|
-
async function ds(storeId, action, body) {
|
|
109
|
-
const r = await fetch(`${BASE}/datasets/stores/${encodeURIComponent(storeId)}/${action}`, {
|
|
110
|
-
method: 'POST',
|
|
111
|
-
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${TOKEN}` },
|
|
112
|
-
body: JSON.stringify(body),
|
|
113
|
-
});
|
|
114
|
-
if (!r.ok) throw new Error(`${action} ${r.status}: ${await r.text()}`);
|
|
115
|
-
return r.json();
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
graph.addNode('build_report', {
|
|
119
|
-
_isCustomCode: true,
|
|
120
|
-
description: 'Reads metrics from a SQLite Store, writes the rendered report to a file Store. Pure code — no model call.',
|
|
121
|
-
outputSchema: z.object({ topDevs: z.array(z.any()), wrote: z.boolean() }),
|
|
122
|
-
execute: async (ctx) => {
|
|
123
|
-
const state = ctx.state.getAll();
|
|
124
|
-
const metricsDb = process.env.ZIBBY_STORE__metrics_db; // a sqlite Store
|
|
125
|
-
const reportFile = process.env.ZIBBY_STORE__report_files; // a file Store
|
|
126
|
-
|
|
127
|
-
// ── SQLite READ ── (readOnly:true → read-only guard)
|
|
128
|
-
const q = await ds(metricsDb, 'sql', {
|
|
129
|
-
sql: 'SELECT dev, added FROM commits WHERE added > ? ORDER BY added DESC LIMIT 10',
|
|
130
|
-
params: [100],
|
|
131
|
-
readOnly: true,
|
|
132
|
-
});
|
|
133
|
-
// q = { columns: ['dev','added'], rows: [['alice', 420], …] }
|
|
134
|
-
|
|
135
|
-
// ── SQLite WRITE ── (bound params; a brand-new table is created STRICT)
|
|
136
|
-
await ds(metricsDb, 'sql', {
|
|
137
|
-
sql: 'INSERT INTO runs (ran_at, top_dev) VALUES (?, ?)',
|
|
138
|
-
params: [state.ranAt, q.rows[0]?.[0] ?? null],
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
// ── file WRITE / READ ──
|
|
142
|
-
await ds(reportFile, 'put', { path: 'report.html', content: state.html });
|
|
143
|
-
const tpl = await ds(reportFile, 'get', { path: 'template.html' });
|
|
144
|
-
|
|
145
|
-
return { topDevs: q.rows, wrote: true };
|
|
146
|
-
},
|
|
147
|
-
});
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
### The Store data actions
|
|
151
|
-
|
|
152
|
-
| Store type | Actions | Notes |
|
|
153
|
-
|---|---|---|
|
|
154
|
-
| `sqlite` | `sql` `{ sql, params?, readOnly? }` | a real relational DB — tables, joins, UPDATEs. `readOnly: true` refuses any write. New tables are created **STRICT** so a column's declared type is enforced. |
|
|
155
|
-
| `file` | `put` / `get` / `list` / `delete` `{ path, content? }` | arbitrary blobs by relative path — good for whole-JSON inputs and rendered outputs. |
|
|
156
|
-
| `dataset` | `append` `{ record }` / `query` `{ select?, where?, … }` | append-only records + SQL-style aggregation — good for raw event streams you later `count`/`sum`/`group`. |
|
|
157
|
-
|
|
158
|
-
:::tip Choosing a Store type by data shape
|
|
159
|
-
- **Whole-blob** (a JSON file a script reads with `readFileSync`, a rendered
|
|
160
|
-
report) → **file**.
|
|
161
|
-
- **Rows you dedupe / update / join** (git commits keyed by SHA) → **sqlite**.
|
|
162
|
-
- **Append-only events you aggregate later** (MRs, worklogs) → **dataset**.
|
|
163
|
-
- **A tiny cursor** ("where did I get to last time") → **kv-memory** (built into
|
|
164
|
-
every agent, not a registry Store).
|
|
165
|
-
:::
|
|
166
|
-
|
|
167
|
-
### File-store size limit — and how to handle big files
|
|
168
|
-
|
|
169
|
-
A `file` store's per-file cap is **4 MiB of raw bytes** (the content travels
|
|
170
|
-
base64-inside-JSON through the API). Two ways past it:
|
|
171
|
-
|
|
172
|
-
- **A relational/append-only shape? Use `sqlite`/`dataset`, not `file`.** Those
|
|
173
|
-
aren't whole-file uploads — they take rows/records, so the 4 MiB blob cap
|
|
174
|
-
doesn't apply. This is the right move for raw event data (commits, worklogs).
|
|
175
|
-
- **A genuinely large blob** (a multi-MB `.jsonl` a script must read whole)?
|
|
176
|
-
**gzip it, transparently.** Text compresses well; store the compressed bytes
|
|
177
|
-
and decompress on read, so the generating script still sees the original file:
|
|
178
|
-
|
|
179
|
-
```js
|
|
180
|
-
// write node: gzip + base64 → store as <name>.gz.b64
|
|
181
|
-
import { gzipSync } from 'node:zlib';
|
|
182
|
-
const packed = gzipSync(Buffer.from(rawText)).toString('base64'); // 5.6MB → ~0.8MB
|
|
183
|
-
await ds(fileStore, 'put', { path: 'big.jsonl.gz.b64', content: packed });
|
|
184
|
-
|
|
185
|
-
// read node: fetch → base64-decode → gunzip → original file on disk
|
|
186
|
-
import { gunzipSync } from 'node:zlib';
|
|
187
|
-
const got = await ds(fileStore, 'get', { path: 'big.jsonl.gz.b64' });
|
|
188
|
-
const raw = gunzipSync(Buffer.from(got.content, 'base64')).toString('utf8');
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
Keep the list of "which files are compressed" as data (one array), so adding a
|
|
192
|
-
big file later is a one-line change — the read/write nodes stay generic.
|
|
193
|
-
|
|
194
|
-
:::caution Check every dependency's SIZE up front
|
|
195
|
-
When you list a store's inputs, don't glob one extension and assume — a single
|
|
196
|
-
oversized dependency (e.g. a 5.6 MB `.jsonl` hiding among small `.json`s) is what
|
|
197
|
-
trips the cap. Enumerate the real files + their bytes before wiring the store.
|
|
198
|
-
File paths accept Unicode (`报告.html` is fine); only `/ \ : ? # [ ] " < > | *`
|
|
199
|
-
and control characters are rejected.
|
|
200
|
-
:::
|
|
201
|
-
|
|
202
|
-
### Debugging a self-host run when logs look truncated
|
|
203
|
-
|
|
204
|
-
Self-host truncates long per-node step logs, so a node can report `success`
|
|
205
|
-
while a downstream step silently failed (you see all-green but empty output).
|
|
206
|
-
The robust pattern: **have each node persist its own full result JSON into an
|
|
207
|
-
output store** (e.g. `diag/<node>.json`) — best-effort, never throwing, never
|
|
208
|
-
changing the node's return value. Then read the diagnostics back with
|
|
209
|
-
`zibby_store_peek` to see exactly what each node saw (staged files + sizes,
|
|
210
|
-
stderr, the real error). This turns a silent all-green failure into a precise
|
|
211
|
-
root cause.
|
|
212
|
-
|
|
213
|
-
---
|
|
214
|
-
|
|
215
|
-
## Pattern 3 — event-driven agents
|
|
216
|
-
|
|
217
|
-
A single agent can subscribe to **several** events and handle each differently.
|
|
218
|
-
The shipped **github-code-review** / **gitlab-code-review** agents are one agent
|
|
219
|
-
that reacts to *PR opened*, *new commits pushed*, *a comment reply*, and an
|
|
220
|
-
*`@mention`* — the entry router branches on which event fired. The owner even
|
|
221
|
-
chooses **which** events trigger a review (cost control), because the whole thing
|
|
222
|
-
is just an event-subscription subset feeding the same router.
|
|
223
|
-
|
|
224
|
-
The takeaway: **you rarely need a second agent for a new trigger.** Add an event
|
|
225
|
-
to the subscription, add a branch, extend the router.
|
|
226
|
-
|
|
227
|
-
---
|
|
228
|
-
|
|
229
|
-
## Putting it together — a design checklist
|
|
230
|
-
|
|
231
|
-
When you design a new agent, ask:
|
|
232
|
-
|
|
233
|
-
1. **How many ways will this be triggered?** More than one → **Pattern 1**: a
|
|
234
|
-
code `route` node at the entry, one agent, branches per scenario.
|
|
235
|
-
2. **What here is deterministic?** Fetch / transform / query / persist → **code
|
|
236
|
-
nodes** (Pattern 2), not LLM nodes. Bind the Stores those nodes need.
|
|
237
|
-
3. **What actually needs judgment?** Only *those* steps get an LLM node
|
|
238
|
-
(`agent: 'claude' | 'codex' | …` + a `prompt` + an `outputSchema`).
|
|
239
|
-
4. **Where does the data live?** Pick a Store type per data shape (blob → file,
|
|
240
|
-
relational → sqlite, append-only → dataset; cursor → kv-memory).
|
|
241
|
-
5. **Is the split write-side vs. read-side?** Keep it **one agent** — a nightly
|
|
242
|
-
`collect` branch that writes the Store and an on-demand `report` branch that
|
|
243
|
-
reads it, selected by the router. Not two agents.
|
|
244
|
-
|
|
245
|
-
Then read the source of **sentry-triage** and **engineering-insights** and map
|
|
246
|
-
each pattern onto what you see. That is the fastest way to learn what Zibby can
|
|
247
|
-
do.
|
package/docs/concepts/graph.md
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
sidebar_position: 1
|
|
3
|
-
title: Graph & nodes
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Graph & nodes
|
|
7
|
-
|
|
8
|
-
A **graph** is a directed graph of agent invocations. You declare it in code:
|
|
9
|
-
|
|
10
|
-
```js
|
|
11
|
-
import { WorkflowGraph } from '@zibby/agent-workflow';
|
|
12
|
-
import { z } from '@zibby/core';
|
|
13
|
-
|
|
14
|
-
const graph = new WorkflowGraph()
|
|
15
|
-
.addNode('plan', { prompt: 'List 3 tasks for: {{goal}}', outputSchema: Plan, agent: 'claude' })
|
|
16
|
-
.addNode('execute', { prompt: 'Do task: {{task}}', outputSchema: Done, agent: 'cursor' })
|
|
17
|
-
.addNode('verify', { prompt: 'Verify: {{result}}', outputSchema: Status, agent: 'codex' })
|
|
18
|
-
.addEdge('plan', 'execute')
|
|
19
|
-
.addEdge('execute', 'verify')
|
|
20
|
-
.setEntryPoint('plan');
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Node config
|
|
24
|
-
|
|
25
|
-
Every node accepts:
|
|
26
|
-
|
|
27
|
-
| Field | Required | Description |
|
|
28
|
-
|---|---|---|
|
|
29
|
-
| `prompt` | yes | Either a string template (`{{state.X}}` interpolated) or a function `({ input, state }) => string`. |
|
|
30
|
-
| `outputSchema` | yes | A Zod schema. The node's output is validated against this before downstream nodes see it. Validation failure = node failure. |
|
|
31
|
-
| `agent` | no | Agent strategy override: `'cursor' | 'claude' | 'codex' | 'gemini' | 'assistant'`. Falls back to project default if omitted. |
|
|
32
|
-
| `retries` | no | Number of times to retry on failure (default: 0). |
|
|
33
|
-
| `skills` | no | Array of skill IDs to enable for this node — see [Skills](./skills). |
|
|
34
|
-
| `onComplete` | no | Async callback invoked with the node's validated output. |
|
|
35
|
-
|
|
36
|
-
## Edges
|
|
37
|
-
|
|
38
|
-
Three forms:
|
|
39
|
-
|
|
40
|
-
```js
|
|
41
|
-
// Linear: A → B
|
|
42
|
-
graph.addEdge('A', 'B');
|
|
43
|
-
|
|
44
|
-
// Conditional branching: state-driven routing
|
|
45
|
-
graph.addConditionalEdges('classify', (state) => {
|
|
46
|
-
if (state.classify.severity === 'critical') return 'pageOncall';
|
|
47
|
-
if (state.classify.severity === 'high') return 'createIncident';
|
|
48
|
-
return 'logAndExit';
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
// Multi-conditional with named labels (cleaner for many branches)
|
|
52
|
-
graph.addConditionalEdges('classify', {
|
|
53
|
-
routes: (state) => state.classify.severity,
|
|
54
|
-
labels: { critical: 'pageOncall', high: 'createIncident', _default: 'logAndExit' },
|
|
55
|
-
});
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Entry point
|
|
59
|
-
|
|
60
|
-
Exactly one node is the entry point:
|
|
61
|
-
|
|
62
|
-
```js
|
|
63
|
-
graph.setEntryPoint('plan');
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
This is the node that runs first when `graph.run(initialState)` is called.
|
|
67
|
-
|
|
68
|
-
## Running
|
|
69
|
-
|
|
70
|
-
```js
|
|
71
|
-
const { state } = await graph.run(agent, {
|
|
72
|
-
input: { goal: 'add a dark-mode toggle' },
|
|
73
|
-
agentType: 'cursor',
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
console.log(state.verify.status);
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
Each node's output lands at `state[nodeName]`, so downstream prompts can reference `{{state.plan.tasks}}` or `state.plan.tasks` from a function prompt.
|
|
80
|
-
|
|
81
|
-
## State immutability
|
|
82
|
-
|
|
83
|
-
State is append-only — every node-completion creates a new state object. Earlier values are preserved in the state's `_history` array, accessible via `state.rollback(N)` for retries that need to drop the latest N writes.
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
sidebar_position: 5
|
|
3
|
-
title: Sessions & artifacts
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Sessions & artifacts
|
|
7
|
-
|
|
8
|
-
Every agent run — local or cloud — produces a **session folder** under `.zibby/output/sessions/<sessionId>/`. The folder is the canonical record of what happened.
|
|
9
|
-
|
|
10
|
-
## What's inside
|
|
11
|
-
|
|
12
|
-
```
|
|
13
|
-
.zibby/output/sessions/1777678254943_ymcw/
|
|
14
|
-
├── result.json # final structured output (Zod-validated)
|
|
15
|
-
├── raw_stream_output.txt # every byte the agent emitted, raw
|
|
16
|
-
├── events.json # JSONL execution log: per-node start/end, validation, retries
|
|
17
|
-
├── .session-info.json # session metadata (start time, agent, model, workflow type)
|
|
18
|
-
└── nodes/
|
|
19
|
-
├── plan/
|
|
20
|
-
│ ├── prompt.txt # exact prompt sent to the agent
|
|
21
|
-
│ ├── raw_output.txt # what the agent returned, pre-validation
|
|
22
|
-
│ └── result.json # the validated output
|
|
23
|
-
├── implement/...
|
|
24
|
-
└── verify/...
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Session IDs
|
|
28
|
-
|
|
29
|
-
Format: `<unix_ms>_<random>`. Generated when the graph starts running.
|
|
30
|
-
|
|
31
|
-
You can override via:
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
zibby agent run my-agent --session 1777678254943_ymcw
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Useful for replay — re-run from a saved input/state without re-paying earlier nodes.
|
|
38
|
-
|
|
39
|
-
## Replay a session
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
zibby agent run my-agent \
|
|
43
|
-
--session 1777678254943_ymcw \
|
|
44
|
-
--node verify # only re-run the 'verify' node
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
The graph reads `state.plan` and `state.implement` from the saved session and only invokes `verify` again. Cheap iteration on the last node when debugging.
|
|
48
|
-
|
|
49
|
-
## Cloud sessions
|
|
50
|
-
|
|
51
|
-
Cloud runs land in the same `.zibby/output/sessions/` layout, just inside the ECS container's `/workspace/`. The session folder is uploaded to S3 at the end of each run; `zibby agent logs <uuid> -t` streams CloudWatch logs in real time.
|
|
52
|
-
|
|
53
|
-
To download a finished cloud session locally:
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
zibby agent download <uuid>
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
This pulls the agent source (so you can edit and redeploy) plus the most recent execution's session folder.
|
|
60
|
-
|
|
61
|
-
## Studio integration
|
|
62
|
-
|
|
63
|
-
[Zibby Studio](https://zibby.app) is a desktop UI that watches `.zibby/output/sessions/`. Anything that writes a session folder shows up in Studio automatically — pin a session, watch state evolve live, or stop an agent from the Stop button.
|
|
64
|
-
|
|
65
|
-
The protocol is documented and stable:
|
|
66
|
-
|
|
67
|
-
- `__WORKFLOW_GRAPH_LOG__` markers in stdout signal node begin/end events
|
|
68
|
-
- `.zibby-studio-stop` file is the kill switch — Studio writes it, the runtime checks for it between nodes
|
|
69
|
-
- `ZIBBY_RUN_SOURCE=studio` env var tells the runtime "you were spawned by Studio"
|
|
70
|
-
- `stoppedByStudio: true` returned from `graph.run()` when the kill switch fired
|
package/docs/concepts/skills.md
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
sidebar_position: 4
|
|
3
|
-
title: Skills
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Skills
|
|
7
|
-
|
|
8
|
-
A **skill** is a named bundle of MCP tools (and optional prompt fragments) that a node can opt into. Skills let you compose tool access per-node without giving every node every tool.
|
|
9
|
-
|
|
10
|
-
See also: [Skills reference](../skills/index.md) for per-skill docs (tools, setup, code samples).
|
|
11
|
-
|
|
12
|
-
## Built-in skills
|
|
13
|
-
|
|
14
|
-
`@zibby/skills` ships these:
|
|
15
|
-
|
|
16
|
-
| Skill ID | What it adds |
|
|
17
|
-
|---|---|
|
|
18
|
-
| `browser` | Playwright MCP — browse, click, fill, screenshot |
|
|
19
|
-
| `github` | GitHub MCP — issues, PRs, file edits, branches |
|
|
20
|
-
| `jira` | Jira MCP — tickets, comments, transitions |
|
|
21
|
-
| `slack` | Slack MCP — read/write channels, threads, DMs |
|
|
22
|
-
| `memory` | Test memory database — version-controlled (Dolt) knowledge from prior runs |
|
|
23
|
-
|
|
24
|
-
## Enabling on a node
|
|
25
|
-
|
|
26
|
-
```js
|
|
27
|
-
import { registerSkill } from '@zibby/agent-workflow';
|
|
28
|
-
import { browserSkill } from '@zibby/skills';
|
|
29
|
-
|
|
30
|
-
registerSkill(browserSkill);
|
|
31
|
-
|
|
32
|
-
graph.addNode('research', {
|
|
33
|
-
prompt: 'Find pricing for {{input.product}}',
|
|
34
|
-
outputSchema: Price,
|
|
35
|
-
agent: 'cursor',
|
|
36
|
-
skills: ['browser'],
|
|
37
|
-
});
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Two effects:
|
|
41
|
-
1. The agent gets the Browser MCP tools at this node only.
|
|
42
|
-
2. The skill's prompt fragment (telling the agent how to use the tools) is appended to the prompt.
|
|
43
|
-
|
|
44
|
-
## Custom skills
|
|
45
|
-
|
|
46
|
-
Implement the `Skill` shape:
|
|
47
|
-
|
|
48
|
-
```js
|
|
49
|
-
import { registerSkill } from '@zibby/agent-workflow';
|
|
50
|
-
|
|
51
|
-
registerSkill({
|
|
52
|
-
id: 'pdf',
|
|
53
|
-
serverName: 'pdf-mcp',
|
|
54
|
-
tools: ['pdfExtract', 'pdfRender'],
|
|
55
|
-
promptFragment: 'When working with PDFs, use the pdfExtract tool first.',
|
|
56
|
-
});
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
Now any node can opt in via `skills: ['pdf']`.
|
|
60
|
-
|
|
61
|
-
## Why per-node, not per-graph?
|
|
62
|
-
|
|
63
|
-
Two reasons:
|
|
64
|
-
|
|
65
|
-
- **Tool scoping** — a node that's planning shouldn't have Slack write access; a node that's posting status shouldn't have file-write tools. Per-node skills give you least-privilege automatically.
|
|
66
|
-
- **Prompt size** — every enabled skill adds prompt fragments. Don't pay for tools you won't use.
|
|
67
|
-
|
|
68
|
-
## Function skills (no MCP server needed)
|
|
69
|
-
|
|
70
|
-
For lightweight cases, register a function skill:
|
|
71
|
-
|
|
72
|
-
```js
|
|
73
|
-
import { registerSkill } from '@zibby/agent-workflow';
|
|
74
|
-
|
|
75
|
-
registerSkill({
|
|
76
|
-
id: 'pricing',
|
|
77
|
-
type: 'function',
|
|
78
|
-
fn: async ({ product }) => {
|
|
79
|
-
const r = await fetch(`https://my.api/price?p=${product}`);
|
|
80
|
-
return r.json();
|
|
81
|
-
},
|
|
82
|
-
description: 'Look up product pricing.',
|
|
83
|
-
});
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
The agent sees `pricing` as a callable tool, gets your function's return value back as a tool result. No MCP server to spin up.
|
package/docs/concepts/state.md
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
sidebar_position: 3
|
|
3
|
-
title: State & schema
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# State & schema
|
|
7
|
-
|
|
8
|
-
Every agent run carries one **state** object that flows from node to node. State is the only handoff mechanism — there's no shared globals, no hidden context.
|
|
9
|
-
|
|
10
|
-
## Shape
|
|
11
|
-
|
|
12
|
-
```js
|
|
13
|
-
state = {
|
|
14
|
-
input: { /* whatever the trigger passed */ },
|
|
15
|
-
plan: { tasks: ['t1', 't2'] }, // ← from node 'plan'
|
|
16
|
-
implement: { diff: '...' }, // ← from node 'implement'
|
|
17
|
-
verify: { status: 'ok' }, // ← from node 'verify'
|
|
18
|
-
}
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
When a node completes, its validated output lands at `state[nodeName]`.
|
|
22
|
-
|
|
23
|
-
## Schema-validated handoff
|
|
24
|
-
|
|
25
|
-
Every node's `outputSchema` is a Zod schema. It runs *before* downstream nodes see the output:
|
|
26
|
-
|
|
27
|
-
```js
|
|
28
|
-
import { z } from '@zibby/core';
|
|
29
|
-
|
|
30
|
-
const Plan = z.object({
|
|
31
|
-
tasks: z.array(z.string()).min(1),
|
|
32
|
-
priority: z.enum(['low', 'normal', 'high']),
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
graph.addNode('plan', {
|
|
36
|
-
prompt: 'Triage this ticket.',
|
|
37
|
-
outputSchema: Plan,
|
|
38
|
-
agent: 'claude',
|
|
39
|
-
});
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
If the agent returns malformed output, the node fails. Combined with `retries: N`, you get cheap automatic recovery from one-off LLM hallucinations.
|
|
43
|
-
|
|
44
|
-
Downstream nodes can reference plan output directly:
|
|
45
|
-
|
|
46
|
-
```js
|
|
47
|
-
graph.addNode('implement', {
|
|
48
|
-
prompt: ({ state }) => `Implement these tasks:\n${state.plan.tasks.map(t => `- ${t}`).join('\n')}`,
|
|
49
|
-
outputSchema: Implementation,
|
|
50
|
-
agent: 'cursor',
|
|
51
|
-
});
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Function vs. template prompts
|
|
55
|
-
|
|
56
|
-
Two prompt forms:
|
|
57
|
-
|
|
58
|
-
```js
|
|
59
|
-
// String template — variables interpolated from state.
|
|
60
|
-
graph.addNode('plan', { prompt: 'Plan: {{input.goal}}', ... });
|
|
61
|
-
|
|
62
|
-
// Function — full programmatic control.
|
|
63
|
-
graph.addNode('plan', {
|
|
64
|
-
prompt: ({ input, state }) => {
|
|
65
|
-
const ctx = state.context?.summary ?? '';
|
|
66
|
-
return `Plan: ${input.goal}\n\nContext:\n${ctx}`;
|
|
67
|
-
},
|
|
68
|
-
...
|
|
69
|
-
});
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Functions get `{ input, state, getAll, get }` so they can introspect state without throwing on missing keys.
|
|
73
|
-
|
|
74
|
-
## Skill hints
|
|
75
|
-
|
|
76
|
-
If a node opts into [skills](./skills), the framework appends prompt fragments that tell the agent how to use those tools. You don't write that boilerplate — register the skill once, list it on the node:
|
|
77
|
-
|
|
78
|
-
```js
|
|
79
|
-
graph.addNode('search', {
|
|
80
|
-
prompt: 'Find info about {{input.query}}',
|
|
81
|
-
outputSchema: Results,
|
|
82
|
-
agent: 'cursor',
|
|
83
|
-
skills: ['browser'], // appends Browser MCP usage instructions to the prompt
|
|
84
|
-
});
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
## Rollback
|
|
88
|
-
|
|
89
|
-
State is history-tracked. To revert the last N writes (typical use case: a node validation passes Zod but the *content* is wrong, and you want to retry with extra instructions):
|
|
90
|
-
|
|
91
|
-
```js
|
|
92
|
-
const recovered = state.rollback(2);
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
`rollback` returns a fresh state with the last 2 writes dropped. Use it inside `onComplete` callbacks or custom retry logic.
|
|
96
|
-
|
|
97
|
-
## Reading state in templates
|
|
98
|
-
|
|
99
|
-
Inside the `prompt` string, `{{x}}` resolves first against `state.x`, then against `input.x`. Dotted paths work:
|
|
100
|
-
|
|
101
|
-
```
|
|
102
|
-
{{state.plan.tasks.length}}
|
|
103
|
-
{{input.ticket}}
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
Function prompts give you the same access without the templating layer.
|