@zibby/skills 0.1.95 → 0.2.0

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 (78) hide show
  1. package/README.md +1 -1
  2. package/dist/artifact.d.ts +1 -115
  3. package/dist/browser.d.ts +10 -19
  4. package/dist/chartRender.d.ts +46 -57
  5. package/dist/chartRender.js +1 -1
  6. package/dist/chat-memory.d.ts +26 -330
  7. package/dist/chat-notify.d.ts +30 -409
  8. package/dist/chat-notify.js +3 -3
  9. package/dist/chatProgress.d.ts +28 -47
  10. package/dist/code-scan.d.ts +52 -58
  11. package/dist/codeStats.d.ts +36 -136
  12. package/dist/codeStats.js +1 -1
  13. package/dist/codebaseMemory.d.ts +52 -32
  14. package/dist/codebaseMemory.js +2 -2
  15. package/dist/core-tools.d.ts +10 -131
  16. package/dist/datasetStore.d.ts +52 -179
  17. package/dist/datasetStore.js +12 -3
  18. package/dist/discord.d.ts +32 -68
  19. package/dist/figma.d.ts +5 -408
  20. package/dist/function-skill.d.ts +23 -135
  21. package/dist/function-skill.js +1 -1
  22. package/dist/gbrain.d.ts +46 -114
  23. package/dist/git-write.d.ts +45 -137
  24. package/dist/git-write.js +6 -6
  25. package/dist/git.d.ts +2 -73
  26. package/dist/github.d.ts +2 -1202
  27. package/dist/gitlab.d.ts +55 -1059
  28. package/dist/gitlab.js +2 -2
  29. package/dist/googleDocs.d.ts +39 -175
  30. package/dist/hubspot.d.ts +1 -381
  31. package/dist/index.d.ts +19 -12
  32. package/dist/index.js +157 -148
  33. package/dist/integrations.d.ts +2 -2
  34. package/dist/jira.d.ts +2 -532
  35. package/dist/jira.js +4 -4
  36. package/dist/kvMemory.d.ts +47 -78
  37. package/dist/lark.d.ts +2 -161
  38. package/dist/lark.js +1 -1
  39. package/dist/larkDocs.d.ts +35 -236
  40. package/dist/linear.d.ts +36 -343
  41. package/dist/linkedin.d.ts +10 -120
  42. package/dist/llm-billing.d.ts +92 -180
  43. package/dist/llm-billing.js +1 -1
  44. package/dist/memory.d.ts +11 -137
  45. package/dist/notion.d.ts +28 -276
  46. package/dist/notion.js +4 -4
  47. package/dist/opendesign.d.ts +24 -202
  48. package/dist/opendesign.js +2 -2
  49. package/dist/package.json +3 -2
  50. package/dist/plane.d.ts +40 -24
  51. package/dist/report.d.ts +153 -110
  52. package/dist/review-dedup.d.ts +8 -8
  53. package/dist/review.d.ts +12 -2
  54. package/dist/reviewMemoryIo.d.ts +37 -3
  55. package/dist/reviewRecord.d.ts +41 -47
  56. package/dist/sentry.d.ts +25 -22
  57. package/dist/skill-installer.d.ts +11 -86
  58. package/dist/slack.d.ts +1 -284
  59. package/dist/socialCard.d.ts +35 -89
  60. package/dist/test-runner.d.ts +1 -220
  61. package/dist/trackers/github-adapter.d.ts +39 -94
  62. package/dist/trackers/index.d.ts +25 -18
  63. package/dist/trackers/jira-adapter.d.ts +20 -88
  64. package/dist/trackers/linear-adapter.d.ts +24 -87
  65. package/dist/trackers/plane-adapter.d.ts +29 -85
  66. package/dist/trackers/plane-adapter.js +1 -1
  67. package/dist/trackers/types.d.ts +1 -186
  68. package/dist/triggerAgent.d.ts +26 -42
  69. package/dist/triggerAgent.js +1 -1
  70. package/dist/workflow-builder.d.ts +11 -245
  71. package/docs/cli-reference.md +33 -0
  72. package/docs/concepts/designing-agents.md +201 -0
  73. package/docs/self-host/backup-restore.md +52 -0
  74. package/docs/self-host/index.md +75 -0
  75. package/docs/self-host/storage.md +50 -0
  76. package/docs/self-host/troubleshooting.md +48 -0
  77. package/docs/self-host/upgrade.md +81 -0
  78. package/package.json +3 -2
@@ -1,245 +1,11 @@
1
- export namespace workflowBuilderSkill {
2
- export let id: string;
3
- export let description: string;
4
- export let envKeys: any[];
5
- export { PROMPT_FRAGMENT as promptFragment };
6
- export let tools: ({
7
- name: string;
8
- description: string;
9
- input_schema: {
10
- type: string;
11
- properties: {
12
- name: {
13
- type: string;
14
- description: string;
15
- };
16
- description: {
17
- type: string;
18
- description: string;
19
- };
20
- nodes: {
21
- type: string;
22
- items: {
23
- type: string;
24
- properties: {
25
- name: {
26
- type: string;
27
- description: string;
28
- };
29
- description: {
30
- type: string;
31
- description: string;
32
- };
33
- inputFields: {
34
- type: string;
35
- items: {
36
- type: string;
37
- };
38
- description: string;
39
- };
40
- outputFields: {
41
- type: string;
42
- items: {
43
- type: string;
44
- };
45
- description: string;
46
- };
47
- };
48
- required: string[];
49
- };
50
- description: string;
51
- };
52
- edges: {
53
- type: string;
54
- items: {
55
- type: string;
56
- properties: {
57
- from: {
58
- type: string;
59
- description: string;
60
- };
61
- to: {
62
- type: string;
63
- description: string;
64
- };
65
- condition: {
66
- type: string;
67
- description: string;
68
- };
69
- };
70
- required: string[];
71
- };
72
- description: string;
73
- };
74
- spec?: undefined;
75
- workflowName?: undefined;
76
- nodeName?: undefined;
77
- inputFields?: undefined;
78
- outputFields?: undefined;
79
- projectId?: undefined;
80
- topic?: undefined;
81
- };
82
- required: string[];
83
- };
84
- } | {
85
- name: string;
86
- description: string;
87
- input_schema: {
88
- type: string;
89
- properties: {
90
- name: {
91
- type: string;
92
- description: string;
93
- };
94
- spec: {
95
- type: string;
96
- description: string;
97
- properties: {
98
- name: {
99
- type: string;
100
- };
101
- description: {
102
- type: string;
103
- };
104
- nodes: {
105
- type: string;
106
- items: {
107
- type: string;
108
- };
109
- };
110
- edges: {
111
- type: string;
112
- items: {
113
- type: string;
114
- };
115
- };
116
- };
117
- };
118
- description?: undefined;
119
- nodes?: undefined;
120
- edges?: undefined;
121
- workflowName?: undefined;
122
- nodeName?: undefined;
123
- inputFields?: undefined;
124
- outputFields?: undefined;
125
- projectId?: undefined;
126
- topic?: undefined;
127
- };
128
- required: string[];
129
- };
130
- } | {
131
- name: string;
132
- description: string;
133
- input_schema: {
134
- type: string;
135
- properties: {
136
- workflowName: {
137
- type: string;
138
- description: string;
139
- };
140
- nodeName: {
141
- type: string;
142
- description: string;
143
- };
144
- description: {
145
- type: string;
146
- description: string;
147
- };
148
- inputFields: {
149
- type: string;
150
- items: {
151
- type: string;
152
- };
153
- description: string;
154
- };
155
- outputFields: {
156
- type: string;
157
- items: {
158
- type: string;
159
- };
160
- description: string;
161
- };
162
- name?: undefined;
163
- nodes?: undefined;
164
- edges?: undefined;
165
- spec?: undefined;
166
- projectId?: undefined;
167
- topic?: undefined;
168
- };
169
- required: string[];
170
- };
171
- } | {
172
- name: string;
173
- description: string;
174
- input_schema: {
175
- type: string;
176
- properties: {
177
- name: {
178
- type: string;
179
- description: string;
180
- };
181
- projectId: {
182
- type: string;
183
- description: string;
184
- };
185
- description?: undefined;
186
- nodes?: undefined;
187
- edges?: undefined;
188
- spec?: undefined;
189
- workflowName?: undefined;
190
- nodeName?: undefined;
191
- inputFields?: undefined;
192
- outputFields?: undefined;
193
- topic?: undefined;
194
- };
195
- required: string[];
196
- };
197
- } | {
198
- name: string;
199
- description: string;
200
- input_schema: {
201
- type: string;
202
- properties: {
203
- name?: undefined;
204
- description?: undefined;
205
- nodes?: undefined;
206
- edges?: undefined;
207
- spec?: undefined;
208
- workflowName?: undefined;
209
- nodeName?: undefined;
210
- inputFields?: undefined;
211
- outputFields?: undefined;
212
- projectId?: undefined;
213
- topic?: undefined;
214
- };
215
- required?: undefined;
216
- };
217
- } | {
218
- name: string;
219
- description: string;
220
- input_schema: {
221
- type: string;
222
- properties: {
223
- topic: {
224
- type: string;
225
- description: string;
226
- };
227
- name?: undefined;
228
- description?: undefined;
229
- nodes?: undefined;
230
- edges?: undefined;
231
- spec?: undefined;
232
- workflowName?: undefined;
233
- nodeName?: undefined;
234
- inputFields?: undefined;
235
- outputFields?: undefined;
236
- projectId?: undefined;
237
- };
238
- required?: undefined;
239
- };
240
- })[];
241
- export function handleToolCall(name: any, args: any, context: any): Promise<string>;
242
- export function resolve(): any;
243
- }
244
- declare const PROMPT_FRAGMENT: "## Workflow Builder\n\nYou can help users build custom AI workflows using the Zibby workflow framework.\n\n### What makes Zibby workflows different\nEach node invokes a **real AI agent** (Cursor, Claude, Codex, or Gemini) \u2014 not a thin LLM API wrapper.\nThat means every node has full agent capabilities: tool use, MCP servers (browser, GitHub, Jira, Slack),\nmulti-turn reasoning, and structured output validation via Zod schemas.\n\nKey differentiators:\n- **Agent-powered nodes** \u2014 each step runs a full AI agent (cursor-agent, claude, codex, gemini CLI) with tool access and MCP skills, not a simple chat completion call.\n- **Structured output** \u2014 every node declares a Zod schema; the framework validates and parses the agent's response automatically.\n- **Conditional routing** \u2014 edges can branch on agent-produced fields (e.g., `state.triage.priority === 'critical'`), enabling intelligent decision graphs.\n- **MCP skill injection** \u2014 nodes declare `skills: [SKILLS.BROWSER, SKILLS.GITHUB]` and the framework spins up the right MCP servers automatically.\n- **Deploy anywhere** \u2014 `zibby deploy` pushes to Zibby Cloud with an API trigger; or self-host with `zibby start`.\n- **State accumulation** \u2014 each node's validated output is stored under its name in `state` (e.g., `state.classify_ticket`), so downstream nodes can reference upstream results.\n\n### What is a workflow?\nA directed graph of nodes (AI agent steps) connected by edges. Each node has:\n- `name` \u2014 unique identifier (snake_case)\n- `prompt` \u2014 function that receives state and returns the prompt string sent to the agent\n- `outputSchema` \u2014 Zod schema defining the structured output the agent must return\n- `skills` (optional) \u2014 array of MCP skill IDs the node needs (e.g., `SKILLS.BROWSER`, `SKILLS.GITHUB`)\n- `timeout` (optional) \u2014 max execution time in ms (default: 300000)\n- `model` (optional) \u2014 override the model for this node (e.g., `'claude-opus-4'`)\n\n### File structure\n```\n.zibby/workflows/<name>/\n\u251C\u2500\u2500 graph.mjs \u2014 WorkflowAgent subclass with buildGraph()\n\u251C\u2500\u2500 nodes/\n\u2502 \u251C\u2500\u2500 index.mjs \u2014 barrel export for all nodes\n\u2502 \u2514\u2500\u2500 <node>.mjs \u2014 one file per node\n\u2514\u2500\u2500 workflow.json \u2014 manifest (name, description, triggers)\n```\n\n### Node pattern\n```javascript\nimport { z, SKILLS } from '@zibby/core';\n\nconst OutputSchema = z.object({\n summary: z.string().describe('Brief summary'),\n items: z.array(z.string()).describe('List of extracted items'),\n needsReview: z.boolean().describe('Whether a human should review this'),\n});\n\nexport const myNode = {\n name: 'my_node',\n skills: [SKILLS.GITHUB], // optional \u2014 framework injects MCP servers\n timeout: 120000, // optional \u2014 2 min timeout\n prompt: (state) => \\`You are analyzing a pull request.\n\nInput:\n\\${JSON.stringify(state.input || {}, null, 2)}\n\nReturn a JSON object matching the schema.\\`,\n outputSchema: OutputSchema,\n};\n```\n\n### Graph pattern\n```javascript\nimport { WorkflowAgent, WorkflowGraph } from '@zibby/core';\nimport { classifyNode, routeNode } from './nodes/index.mjs';\n\nexport class MyWorkflow extends WorkflowAgent {\n buildGraph() {\n const graph = new WorkflowGraph();\n graph.addNode('classify', classifyNode);\n graph.addNode('route', routeNode);\n graph.setEntryPoint('classify');\n graph.addEdge('classify', 'route');\n graph.addEdge('route', 'END');\n return graph;\n }\n\n async onComplete(result) {\n // Post-execution hook \u2014 save artifacts, notify, etc.\n console.log('Workflow complete:', result.success);\n }\n}\n```\n\nConditional edges: `graph.addConditionalEdges('node', (state) => state.node.priority === 'high' ? 'escalate' : 'notify')`\n\n### Available SKILLS constants\nImport from `@zibby/core`: `SKILLS.BROWSER`, `SKILLS.MEMORY`, `SKILLS.GITHUB`, `SKILLS.JIRA`, `SKILLS.SLACK`, `SKILLS.RUNNER`\n\n### Deep documentation\nCall `explore_framework_docs` to read detailed framework docs on demand. Use it for:\n- Advanced patterns (middleware, parallel nodes, state schemas)\n- Deployment & cloud triggers\n- CLI commands reference\n- Integration details (Jira, GitHub, etc.)\nCall with no arguments to see all available topics.\n\n### How to use the builder tools\n1. For complex workflows, call `explore_framework_docs(\"custom-workflows\")` first to learn advanced patterns.\n2. Ask the user what their workflow should do, what input it receives, and what steps are needed.\n3. Call `design_workflow` with the structured spec for the user to review.\n4. Once approved, call `build_workflow` to generate real code on disk (uses the configured agent for high-quality code generation).\n5. Remind the user: `zibby start <name>` to test locally, `zibby deploy <name> --project <id>` to deploy to cloud, `zibby logs --workflow <name>` to tail logs.\n\n### Important\n- Each node prompt should be detailed and specific \u2014 tell the AI agent exactly what to do and what format to return.\n- Zod schemas MUST use .describe() on every field so the agent knows what each field means.\n- Node names must be snake_case (e.g., classify_ticket, generate_report).\n- Workflow names must be kebab-case (e.g., ticket-triage, pr-review).\n- State flows through: each node's validated output is stored under its name in state (e.g., state.classify_ticket).\n- Downstream nodes reference upstream outputs in their prompt function (e.g., \\`\\${JSON.stringify(state.classify_ticket, null, 2)}\\`).\n- Nodes can declare skills to get MCP tool access \u2014 the framework handles server lifecycle automatically.";
245
- export {};
1
+ /**
2
+ * Workflow Builder Skill
3
+ *
4
+ * Guides users through designing and building custom AI workflows
5
+ * via conversation. The chat agent (assistant) handles the dialog;
6
+ * this skill provides tools that generate real workflow code by
7
+ * delegating to the user's configured agent (cursor/claude/codex/gemini).
8
+ *
9
+ * No MCP server needed — all handlers run locally.
10
+ */
11
+ export declare const workflowBuilderSkill: any;
@@ -20,6 +20,7 @@ Every command lives under `zibby agent <verb>` for consistency. The bare top-lev
20
20
  | [`zibby agent download <uuid>`](#agent-download) | Pull a deployed agent back to local. Edit + redeploy. |
21
21
  | [`zibby agent delete <uuid>`](#agent-delete) | Delete a deployed agent. |
22
22
  | [`zibby agent start <name>`](#agent-start) | Long-lived dev server (Studio integration). Most users want `run`. |
23
+ | [`zibby agent update <uuid>`](#agent-update) | One-shot edit of a deployed agent: model, review @mention, trigger events, name, max runtime. |
23
24
  | [`zibby agent env <verb>`](#agent-env) | Manage per-agent encrypted env vars: `list`, `set`, `unset`, `push`. |
24
25
 
25
26
  Plus the test recipe + memory + project setup:
@@ -149,6 +150,38 @@ Options:
149
150
 
150
151
  **Storage & retention.** Live logs are kept in CloudWatch for 30 days. Beyond that, the per-run session folder (uploaded to S3 at the end of every execution) is the long-term archive — pull it back with `zibby agent download <uuid>`.
151
152
 
153
+ ## agent update {#agent-update}
154
+
155
+ ```bash
156
+ zibby agent update <uuid> --model claude:sonnet-4.6 # switch vendor:model
157
+ zibby agent update <uuid> --mention @zibby # review @mention token
158
+ zibby agent update <uuid> --triggers mention,comment # WHICH events fire a review
159
+ zibby agent update <uuid> --name "FE Review Bot" --max-runtime 30
160
+ ```
161
+
162
+ The ONE consolidated editor for per-agent settings that used to be
163
+ dashboard-only. Flags can be combined; the command echoes the updated
164
+ settings so you can confirm what changed.
165
+
166
+ `--triggers` takes friendly names and controls **when a review agent runs**:
167
+
168
+ | Name | Fires on |
169
+ |---|---|
170
+ | `opened` | a new PR/MR is opened |
171
+ | `commit` | new commits are pushed to an open PR/MR |
172
+ | `mention` | someone @-mentions the agent, or replies in one of its threads |
173
+
174
+ `--triggers mention` is the cost-saving mode — the agent reviews **only when
175
+ asked**. New review agents deploy mention-only by default; existing agents keep
176
+ their current behaviour until you change it.
177
+
178
+ Driving Zibby from an AI editor instead of a shell? The MCP twin is
179
+ **`zibby_update_agent`** with the same fields (`model`, `mentionToken`,
180
+ `triggerEvents`, `displayName`, `maxRuntimeMinutes`).
181
+
182
+ Env vars, custom MCP servers and stores are separate surfaces:
183
+ `zibby agent env …`, `zibby_add_mcp`, `zibby_set_node_stores`.
184
+
152
185
  ## agent env {#agent-env}
153
186
 
154
187
  Per-agent encrypted env vars — KMS-stored on the agent record, injected into the Fargate task at trigger time. Agent env wins over project secrets on conflict.
@@ -0,0 +1,201 @@
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
+ ---
168
+
169
+ ## Pattern 3 — event-driven agents
170
+
171
+ A single agent can subscribe to **several** events and handle each differently.
172
+ The shipped **github-code-review** / **gitlab-code-review** agents are one agent
173
+ that reacts to *PR opened*, *new commits pushed*, *a comment reply*, and an
174
+ *`@mention`* — the entry router branches on which event fired. The owner even
175
+ chooses **which** events trigger a review (cost control), because the whole thing
176
+ is just an event-subscription subset feeding the same router.
177
+
178
+ The takeaway: **you rarely need a second agent for a new trigger.** Add an event
179
+ to the subscription, add a branch, extend the router.
180
+
181
+ ---
182
+
183
+ ## Putting it together — a design checklist
184
+
185
+ When you design a new agent, ask:
186
+
187
+ 1. **How many ways will this be triggered?** More than one → **Pattern 1**: a
188
+ code `route` node at the entry, one agent, branches per scenario.
189
+ 2. **What here is deterministic?** Fetch / transform / query / persist → **code
190
+ nodes** (Pattern 2), not LLM nodes. Bind the Stores those nodes need.
191
+ 3. **What actually needs judgment?** Only *those* steps get an LLM node
192
+ (`agent: 'claude' | 'codex' | …` + a `prompt` + an `outputSchema`).
193
+ 4. **Where does the data live?** Pick a Store type per data shape (blob → file,
194
+ relational → sqlite, append-only → dataset; cursor → kv-memory).
195
+ 5. **Is the split write-side vs. read-side?** Keep it **one agent** — a nightly
196
+ `collect` branch that writes the Store and an on-demand `report` branch that
197
+ reads it, selected by the router. Not two agents.
198
+
199
+ Then read the source of **sentry-triage** and **engineering-insights** and map
200
+ each pattern onto what you see. That is the fastest way to learn what Zibby can
201
+ do.
@@ -0,0 +1,52 @@
1
+ ---
2
+ sidebar_position: 3
3
+ title: Backup & restore
4
+ ---
5
+
6
+ # Backup & restore
7
+
8
+ `backup.sh` and `restore.sh` ship next to the compose file in your install directory (the installer drops them there). Together they let a dead VM — or a fat-fingered `docker compose down -v` — never mean "start over and reconnect 50 repos".
9
+
10
+ ## What a backup contains
11
+
12
+ One timestamped tarball holds the three things that together **are** your install:
13
+
14
+ 1. **ScyllaDB volume** (`scylla-data`) — all metadata: integrations + their encrypted connect tokens, deployed agents (graph/env/schedule/prompts), repo→agent links, projects, members, access tokens, review-memory, execution history, stores.
15
+ 2. **Object-store volume** — `seaweed-data` (SeaweedFS, the default) or `minio-data` on a legacy box. Run artifacts + per-node logs, datasets, marketplace icons. The active store is detected from `.env` (`ZIBBY_OBJECT_STORE`) / volume presence; `restore.sh` handles either.
16
+ 3. **`.env`** — **critical**. It holds `ENCRYPTION_KEY`, without which every encrypted token/secret in ScyllaDB is unrecoverable, plus `JWT_SECRET`, the object-store root creds, and integration tokens. A ScyllaDB restore without the matching `ENCRYPTION_KEY` is useless, so `.env` travels **inside** the backup — which is also why the backup is highly sensitive.
17
+
18
+ ## Take a backup
19
+
20
+ ```bash
21
+ cd zibby-selfhosted
22
+ ./backup.sh # → ./backups/zibby-selfhosted-<timestamp>.tar.gz
23
+ ./backup.sh --out /mnt/backups # write elsewhere
24
+ ./backup.sh --encrypt # encrypt at rest with age or gpg (prompts for a passphrase)
25
+ ./backup.sh --s3 s3://mybucket/zibby # also upload with YOUR aws creds (nothing goes to Zibby)
26
+ ```
27
+
28
+ Consistency + downtime: for a single-VM box the safe, simple approach wins — the script briefly **stops the writers** (control-plane + datastores), `nodetool flush`es Scylla so the commitlog is on disk, tars the volumes read-only from a throwaway helper container, then restarts everything. Downtime is typically seconds to a few minutes (plus Scylla's ~1–2 min cold-boot on restart). A trap guarantees the stack restarts even if the backup errors out. Volume names are compose-project-prefixed and **discovered at runtime**, so it works regardless of your install-dir name.
29
+
30
+ Copy the tarball **off the box** — it is your only recovery path, and it contains the encryption key.
31
+
32
+ :::tip
33
+ An upgrade takes its own safety snapshot into `<install-dir>/backups/upgrade-<id>/` before touching anything, and there is an `auto-backup.sh` you can cron for scheduled snapshots.
34
+ :::
35
+
36
+ ## Restore
37
+
38
+ Onto the same box after data loss, or onto a brand-new VM:
39
+
40
+ ```bash
41
+ ./restore.sh zibby-selfhosted-<timestamp>.tar.gz # interactive confirm
42
+ ./restore.sh backup.tar.gz --dir /opt/zibby --force # scripted
43
+ ./restore.sh backup.age # decrypts first (needs age/gpg)
44
+ ```
45
+
46
+ `restore.sh` restores **both** data volumes and the `.env` (so the restored, encrypted data is readable). Volume names are discovered at runtime, or created if this is a fresh box.
47
+
48
+ :::danger Restore wipes current data
49
+ Restoring **overwrites** this install's volumes. The script refuses to run without an explicit confirmation (interactive `y/N`, or `--force`).
50
+ :::
51
+
52
+ A MinIO-era bundle restored onto a current (post-SeaweedFS-flip) release comes up on the legacy MinIO override; the next installer run auto-migrates it to SeaweedFS — see [Storage](./storage).
@@ -0,0 +1,75 @@
1
+ ---
2
+ sidebar_position: 1
3
+ title: Self-host overview
4
+ ---
5
+
6
+ # Operating a self-hosted box
7
+
8
+ Run the entire Zibby agent platform on **one host**, inside your own VPC, with `docker compose`. This section is the **operator runbook** — for the person who holds the box's owner/admin token and is responsible for install, upgrade, backup, and storage. It is not the agent-authoring guide (that's the rest of these docs, and `zibby init` in your repo).
9
+
10
+ A self-hosted box is the **same codebase as cloud**, assembled differently: the control plane is `backend/server.js` (the Express server that wraps every Lambda handler, including the MCP server), the run executor is the host Docker engine instead of Fargate, and the datastore / object store are ScyllaDB Alternator + SeaweedFS instead of DynamoDB + S3.
11
+
12
+ ## Install
13
+
14
+ One command, on a Linux/macOS host with **Docker Engine + Compose v2**:
15
+
16
+ ```bash
17
+ curl -fsSL https://dl.zibby.app/selfhosted/latest/install.sh | bash
18
+ ```
19
+
20
+ The installer downloads a signed release (a `docker save` image tarball + compose file + the marketplace-template bundle), **verifies it** (an Ed25519 signature over `SHA256SUMS`, then a per-file checksum — it fails closed on any mismatch and never runs unverified bytes), `docker load`s the images, and `docker compose up -d`s the stack. It is idempotent — re-running it is how you upgrade in place, and cached downloads are checksum-verified.
21
+
22
+ :::warning Compose v2 is required
23
+ `apt install docker.io` alone gives you the engine but **not** Compose v2. Install both — the installer aborts with a link if `docker compose version` fails. On a headless box also make sure `openssl` is present (the installer needs it to verify the release signature).
24
+ :::
25
+
26
+ ### What comes up
27
+
28
+ The stack is a handful of containers on the `zibby-selfhosted` docker network:
29
+
30
+ | Container | Role |
31
+ |---|---|
32
+ | `zibby-control-plane` | the long-lived API + `/mcp` + `/hooks/*` server + the dashboard; drives the host Docker engine as the run executor over the mounted `docker.sock` |
33
+ | `zibby-scylla` | **ScyllaDB Alternator** (`:8000`) — speaks the DynamoDB API, so the backend is unchanged. All metadata: integrations + encrypted tokens, deployed agents, projects, members, executions, stores. |
34
+ | `zibby-minio` | the **object store** (`:9000`) for run sources + artifacts + logs. As of 0.1.126 this is **SeaweedFS** (the service keeps the historical name `minio` so every `S3_ENDPOINT` consumer is unchanged). Legacy boxes still run MinIO until they auto-migrate — see [Storage](./storage). |
35
+ | `zibby-bootstrap` | one-shot: provisions tables + buckets, syncs all marketplace templates, seeds the default project + token, then exits. |
36
+ | run containers | one ephemeral `zibby-agent` container per run, destroyed on exit — the same per-run isolation Fargate gives cloud. |
37
+
38
+ The control plane mounts `/var/run/docker.sock` — that socket **is** the run executor (no docker-in-docker).
39
+
40
+ ### After install
41
+
42
+ The installer prints the access info and the seeded credential:
43
+
44
+ - **Dashboard** — `http://<host>:3001/` (and `/ui`). The self-host dashboard is a single vanilla HTML page served by the control plane; the cloud React app is not shipped on-prem.
45
+ - **API + CLI** — `http://<host>:3001`. Set `ZIBBY_API_URL` + `ZIBBY_API_KEY=<apiToken>` (the seeded `zby_…` project token printed at the end, or recover it with `docker logs zibby-bootstrap`).
46
+ - **MCP** — `http://<host>:3001/mcp`, `Authorization: Bearer <PAT>`. MCP needs a **user PAT** (`zby_pat_…`), minted with `zibby self-host token` — not the project token.
47
+ - **Health** — `http://<host>:3001/health`.
48
+
49
+ AI model keys are **per-project**, set in the dashboard (Settings → AI); the deploy modal collects a missing one. The box comes up keyless — there is deliberately no box-global model credential.
50
+
51
+ ## Install knobs
52
+
53
+ Set these as environment variables or flags on the install command (they persist into `.env`, which is the runtime source of truth and is **reused untouched on upgrade**):
54
+
55
+ | Knob | Default | Effect |
56
+ |---|---|---|
57
+ | `--dir <path>` / `ZIBBY_DIR` | `./zibby-selfhosted` | install + data directory |
58
+ | `--url <u>` / `ZIBBY_DIST_URL` | `https://dl.zibby.app/selfhosted/latest` | release base URL |
59
+ | `--reconfigure` | — | re-run the external-URL / domain prompt |
60
+ | `CONTROL_PLANE_PORT` (`.env`) | `3001` | host port the control plane binds |
61
+ | `CONTROL_PLANE_BIND` (`.env`) | `0.0.0.0` | set to `127.0.0.1` to restrict the dashboard/API to loopback and front it with the bundled Caddy TLS proxy |
62
+ | `ZIBBY_EXTERNAL_URL` (`.env`/prompt) | unset | a domain → automatic Let's Encrypt HTTPS; an IP → self-signed HTTPS; `http://…` → plain HTTP via Caddy. Blank → plain HTTP on `CONTROL_PLANE_PORT`. |
63
+ | `MAX_CONCURRENT_RUNS` (`.env`) | `5` | run containers executing in parallel; the rest queue |
64
+ | `ZIBBY_SKIP_OBJECT_STORE_MIGRATION` | `0` | `1` opts a legacy MinIO box out of the auto-migration to SeaweedFS |
65
+
66
+ :::warning Plain-HTTP default binds in cleartext
67
+ With no external URL the dashboard/API bind to `0.0.0.0:3001` unencrypted — the operator token and login passcode cross the network in the clear. Either set `ZIBBY_EXTERNAL_URL` (→ auto-TLS via Caddy), firewall the port to trusted IPs, or set `CONTROL_PLANE_BIND=127.0.0.1`.
68
+ :::
69
+
70
+ ## The runbook
71
+
72
+ - **[Upgrade & rollback](./upgrade)** — check the running version, the owner-only remote upgrade, and how auto-rollback works.
73
+ - **[Backup & restore](./backup-restore)** — snapshot the whole install to one tarball; restore onto the same or a fresh box.
74
+ - **[Storage](./storage)** — the object store, and the automatic MinIO → SeaweedFS migration.
75
+ - **[Troubleshooting](./troubleshooting)** — reading logs and the failure signatures worth recognizing.