@zibby/skills 2.0.3 → 2.0.5
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/index.js +85 -85
- package/dist/jira.d.ts +100 -9
- package/dist/jira.js +9 -9
- package/dist/package.json +4 -4
- package/dist/trackers/index.js +12 -12
- package/dist/trackers/jira-adapter.js +11 -11
- package/docs/recipes/index.md +5 -6
- package/docs/templates/catalog-metadata.md +101 -0
- package/docs/templates/composition.md +97 -0
- package/docs/templates/deploy-time-config.md +228 -0
- package/docs/templates/index.md +138 -0
- package/docs/templates/models.md +157 -0
- package/docs/templates/node-declarations.md +149 -0
- package/docs/templates/remote-mcp.md +105 -0
- package/docs/templates/requires.md +183 -0
- package/docs/templates/sidecars.md +163 -0
- package/docs/templates/stores.md +113 -0
- package/docs/templates/surfaces.md +184 -0
- package/docs/templates/update-behavior.md +108 -0
- package/package.json +4 -4
- package/docs/recipes/bug-autofix.md +0 -85
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 9
|
|
3
|
+
title: Deploy-time configuration
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Deploy-time configuration
|
|
7
|
+
|
|
8
|
+
Four declarations that shape what happens **when somebody installs your agent**:
|
|
9
|
+
what they are asked, what is seeded for them, and what starts a run.
|
|
10
|
+
|
|
11
|
+
| Block | Writes to | Asked at |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| [`selections`](#selections) | the agent's Env bag — **configuration** | deploy |
|
|
14
|
+
| [`deployInput`](#deployinput) | the agent's default trigger payload | deploy |
|
|
15
|
+
| [`defaultSchedule`](#defaultschedule) | the agent's schedules | deploy (once) |
|
|
16
|
+
| [`workflow.json` → `triggers`](#triggers) | the agent's trigger settings | every deploy |
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## `selections`
|
|
21
|
+
|
|
22
|
+
### What it is
|
|
23
|
+
|
|
24
|
+
A value the operator must **pick from a live list some integration owns** —
|
|
25
|
+
which board this agent works, which space it writes to — rather than dig an id
|
|
26
|
+
out of a URL and type it into an environment variable.
|
|
27
|
+
|
|
28
|
+
One declaration renders a dropdown of the real options the connected system
|
|
29
|
+
returns, and the answer lands in the agent's encrypted Env bag.
|
|
30
|
+
|
|
31
|
+
### Syntax
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
spec: {
|
|
35
|
+
selections: [{
|
|
36
|
+
id: 'board',
|
|
37
|
+
label: 'Board',
|
|
38
|
+
from: 'board_tracker', // a provider, or a one-of group
|
|
39
|
+
providerKey: 'BOARD_TRACKER', // records WHICH provider was chosen
|
|
40
|
+
keys: {
|
|
41
|
+
vikunja: 'BOARD_VIKUNJA_PROJECT_ID',
|
|
42
|
+
jira: 'BOARD_JIRA_PROJECT_KEY',
|
|
43
|
+
},
|
|
44
|
+
required: true,
|
|
45
|
+
help: 'The board this agent polls for tickets marked ready.',
|
|
46
|
+
}],
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Properties
|
|
51
|
+
|
|
52
|
+
| Name | Type | Required | Allowed values | Default | Update behavior |
|
|
53
|
+
|---|---|---|---|---|---|
|
|
54
|
+
| `id` | string | **Yes** | lowercase, starts with a letter, ≤ 41 chars, unique in the array | — | Seeded once |
|
|
55
|
+
| `label` | string | **Yes** | non-empty — it is what the picker is called | — | Card only |
|
|
56
|
+
| `from` | string | **Yes** | a provider id, or a one-of group the platform already knows | — | Seeded once |
|
|
57
|
+
| `keys` | object | **Yes** | `{ provider: 'ENV_KEY' }` — every provider must belong to `from` | — | Seeded once |
|
|
58
|
+
| `providerKey` | string | **Yes** when the group offers more than one provider | a writable Env key | none | Seeded once |
|
|
59
|
+
| `required` | boolean | No | | `true` | Seeded once |
|
|
60
|
+
| `help` | string | No | | none | Card only |
|
|
61
|
+
|
|
62
|
+
### Why `keys` is a map
|
|
63
|
+
|
|
64
|
+
The value written is a different *kind* of thing per provider: Vikunja addresses
|
|
65
|
+
a board by a numeric project id, Jira by a project key (`"PROJ"`). One
|
|
66
|
+
environment variable cannot express that, and collapsing them would leave the
|
|
67
|
+
reader guessing which system's id it holds.
|
|
68
|
+
|
|
69
|
+
### Gotchas
|
|
70
|
+
|
|
71
|
+
**A one-of group with more than one option needs a `providerKey`.** A choice
|
|
72
|
+
that records nothing deploys an agent that cannot tell which system it was
|
|
73
|
+
pointed at — so the declaration is refused.
|
|
74
|
+
|
|
75
|
+
**A provider with no key in `keys` is simply not offered.** That is legitimate:
|
|
76
|
+
some systems are reached another way and have no list to show.
|
|
77
|
+
|
|
78
|
+
**It is seeded once.** A value you have already set survives every Update, and
|
|
79
|
+
the deploy tells you what it kept.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## `deployInput`
|
|
84
|
+
|
|
85
|
+
### What it is
|
|
86
|
+
|
|
87
|
+
A field of your agent's input schema, annotated so the deploy asks for it once
|
|
88
|
+
and stores it as the agent's default trigger payload.
|
|
89
|
+
|
|
90
|
+
### Syntax
|
|
91
|
+
|
|
92
|
+
```js
|
|
93
|
+
// state.js
|
|
94
|
+
repoUrl: z.string().url()
|
|
95
|
+
.meta({ deployInput: {
|
|
96
|
+
required: true,
|
|
97
|
+
source: 'github-repo',
|
|
98
|
+
label: 'GitHub repo',
|
|
99
|
+
help: 'Pick the repository whose flaky tests this agent should fix.',
|
|
100
|
+
}}),
|
|
101
|
+
|
|
102
|
+
circleProjectSlug: z.string().optional()
|
|
103
|
+
.meta({ deployInput: {
|
|
104
|
+
source: 'circleci-project',
|
|
105
|
+
deriveFrom: 'repoUrl', // resolved automatically, never typed
|
|
106
|
+
label: 'CircleCI project',
|
|
107
|
+
}}),
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Properties
|
|
111
|
+
|
|
112
|
+
| Name | Type | Required | Allowed values | Default | Update behavior |
|
|
113
|
+
|---|---|---|---|---|---|
|
|
114
|
+
| `required` | boolean | No | gates the deploy in the dashboard, the CLI **and** over MCP | `false` | Seeded once |
|
|
115
|
+
| `source` | string | No | which control renders it, or which resolver fills it | none | Seeded once |
|
|
116
|
+
| `deriveFrom` | string | No | another field's name — auto-resolved, never user-entered, never required of the user | none | Seeded once |
|
|
117
|
+
| `label` / `help` | string | No | | the field name | Card only |
|
|
118
|
+
|
|
119
|
+
### Gotchas
|
|
120
|
+
|
|
121
|
+
`deployInput.required` is the **deploy gate**, which is separate from whether
|
|
122
|
+
the field is optional in your schema.
|
|
123
|
+
|
|
124
|
+
Use `selections` instead when the value is agent *configuration* your code reads
|
|
125
|
+
from the environment — not part of a run's input.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## `defaultSchedule`
|
|
130
|
+
|
|
131
|
+
### What it is
|
|
132
|
+
|
|
133
|
+
A cron-driven agent does nothing until something wakes it. `defaultSchedule` is
|
|
134
|
+
the floor: deploy installs it as the agent's ordinary schedule **when the agent
|
|
135
|
+
has no schedule at all**.
|
|
136
|
+
|
|
137
|
+
### Syntax
|
|
138
|
+
|
|
139
|
+
```js
|
|
140
|
+
spec: {
|
|
141
|
+
defaultSchedule: { cron: '*/15 * * * *', name: 'Board tick' },
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Properties
|
|
146
|
+
|
|
147
|
+
| Name | Type | Required | Allowed values | Default | Update behavior |
|
|
148
|
+
|---|---|---|---|---|---|
|
|
149
|
+
| `cron` | string | **Yes** | a **5-field** Unix cron (`m h dom mon dow`) | — | Seeded once |
|
|
150
|
+
| `timezone` | string | No | an IANA zone | `UTC` | Seeded once |
|
|
151
|
+
| `name` | string | No | | none | Seeded once |
|
|
152
|
+
| `input` | object | No | the trigger body | `{}` | Seeded once |
|
|
153
|
+
|
|
154
|
+
A one-shot date is refused — a template cannot know a date.
|
|
155
|
+
|
|
156
|
+
### Gotchas
|
|
157
|
+
|
|
158
|
+
**Any existing entry blocks the seed.** One you created, the seeded one after
|
|
159
|
+
you edited it, a paused one (pausing is a disable, not a delete), even a
|
|
160
|
+
one-shot that already fired. So an Update can never resurrect a schedule you
|
|
161
|
+
paused, and never re-points one you changed.
|
|
162
|
+
|
|
163
|
+
**One entry, not an array.** The declaration is a floor, and a floor is one
|
|
164
|
+
cadence. An agent that needs several schedules has an operator who set them, and
|
|
165
|
+
seed-once means those are never touched.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## `triggers`
|
|
170
|
+
|
|
171
|
+
### What it is
|
|
172
|
+
|
|
173
|
+
An optional `workflow.json` beside your graph, declaring how runs start. It is
|
|
174
|
+
re-applied on **every** deploy, including an Update.
|
|
175
|
+
|
|
176
|
+
### Syntax
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{ "triggers": { "events": ["github.pr.opened", "github.pr.updated"] } }
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{ "triggers": { "api": true } }
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Properties
|
|
187
|
+
|
|
188
|
+
| Name | Type | Required | Allowed values | Default | Update behavior |
|
|
189
|
+
|---|---|---|---|---|---|
|
|
190
|
+
| `api` | boolean | No | `true` ships the inbound webhook on by default | off (opt-in) | Re-applied |
|
|
191
|
+
| `events` | string[] | No | `github.pr.opened`, `github.pr.updated`, `github.pr.commented`, `github.pr.mentioned`, and the `gitlab.mr.*` equivalents | `[]` | Re-applied |
|
|
192
|
+
|
|
193
|
+
Inbound webhooks are routed **by subscription, not by agent name**: a delivery is
|
|
194
|
+
normalised to an event, the repository is resolved to a project, and every
|
|
195
|
+
subscribed agent gets its own independent run.
|
|
196
|
+
|
|
197
|
+
### Gotchas
|
|
198
|
+
|
|
199
|
+
**A member of a fleet is suppressed from event routing while it is a member.**
|
|
200
|
+
Otherwise one pull request fires both the fleet — which runs the member itself —
|
|
201
|
+
and the standalone member, giving you two reviews. Deploy the member on its own
|
|
202
|
+
and it starts responding again.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Sample trigger bodies
|
|
207
|
+
|
|
208
|
+
Two card-only fields that make the Trigger dialog usable:
|
|
209
|
+
|
|
210
|
+
```js
|
|
211
|
+
spec: {
|
|
212
|
+
triggerExample: { ticketKey: 'PROJ-123' },
|
|
213
|
+
triggerExamples: [
|
|
214
|
+
{ label: 'board tick', body: '{}' },
|
|
215
|
+
{ label: 'one ticket', body: '{"ticketKey":"PROJ-123"}' },
|
|
216
|
+
{ label: 'plan from a PRD', body: '{"prd":"# Feature\\n\\nAs a user I want …"}' },
|
|
217
|
+
],
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
`triggerExample` pre-fills the editable body. `triggerExamples` renders one
|
|
222
|
+
click-to-use reference per source below it. Both must be **valid JSON** — JSON
|
|
223
|
+
has no comments.
|
|
224
|
+
|
|
225
|
+
## See also
|
|
226
|
+
|
|
227
|
+
- [Update behavior](./update-behavior.md)
|
|
228
|
+
- [Composition](./composition.md)
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 1
|
|
3
|
+
title: The declaration model
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Agent templates — the declaration model
|
|
7
|
+
|
|
8
|
+
An **agent template** is the source of a reusable agent: its graph, its state
|
|
9
|
+
schema, and a **declaration block** that says what the platform must do when
|
|
10
|
+
somebody installs it.
|
|
11
|
+
|
|
12
|
+
If you have written CloudFormation, you already know the shape:
|
|
13
|
+
|
|
14
|
+
| CloudFormation | Zibby |
|
|
15
|
+
|---|---|
|
|
16
|
+
| stack template | the template |
|
|
17
|
+
| `Resources` | the declaration blocks (`models`, `stores`, `requires`, …) |
|
|
18
|
+
| `create-stack` | deploying the template from the catalog |
|
|
19
|
+
| `Ref` / logical names | a declared **name** the platform binds at deploy |
|
|
20
|
+
| *Update requires: Replacement* | fields frozen at deploy — see [Update behavior](./update-behavior.md) |
|
|
21
|
+
|
|
22
|
+
The rule the whole system is built around:
|
|
23
|
+
|
|
24
|
+
> **Adding a new agent means writing only a declaration.** No new UI branch, no
|
|
25
|
+
> new gate, no code that knows this agent's name.
|
|
26
|
+
|
|
27
|
+
Everything the platform does for your agent — which model keys it asks you for,
|
|
28
|
+
which stores it provisions, which service containers it pulls, which endpoints
|
|
29
|
+
it publishes, which other agents it installs alongside — is computed from the
|
|
30
|
+
declaration. One renderer, one provisioner, one deploy path, for every agent
|
|
31
|
+
that exists or ever will.
|
|
32
|
+
|
|
33
|
+
## Where a declaration lives
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
my-agent/
|
|
37
|
+
├── graph.mjs ← the graph; NODES carry their own declarations
|
|
38
|
+
├── state.js ← the input schema; fields can carry deploy-time annotations
|
|
39
|
+
├── workflow.json ← optional: which events start a run
|
|
40
|
+
├── package.json ← the versions this agent will be FROZEN at
|
|
41
|
+
└── icon.png ← optional
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
plus the **`spec` block** that declares everything else:
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
spec: {
|
|
48
|
+
slug: 'my-agent',
|
|
49
|
+
tagline: 'One clamped line that IS the card header.',
|
|
50
|
+
tags: ['Coding'],
|
|
51
|
+
capabilities: [
|
|
52
|
+
'Each bullet is one thing, starting with a verb',
|
|
53
|
+
],
|
|
54
|
+
models: [ /* … */ ],
|
|
55
|
+
surfaces: ['cli', 'webhook', 'schedule', 'mcp'],
|
|
56
|
+
releaseNotes: 'What changed in THIS version.',
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
A template with no `spec` block is not in the catalog at all. It is still
|
|
61
|
+
usable as a local scaffold, but nothing syncs, nothing renders, nothing deploys.
|
|
62
|
+
|
|
63
|
+
:::note `marketplace:` is the same block
|
|
64
|
+
Older templates spell this block `marketplace:`. That spelling still works and
|
|
65
|
+
always will — `spec:` is simply the neutral name for the same thing. You never
|
|
66
|
+
need to rename an existing template.
|
|
67
|
+
:::
|
|
68
|
+
|
|
69
|
+
## What happens at deploy
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
your declaration
|
|
73
|
+
│
|
|
74
|
+
▼
|
|
75
|
+
┌────────────────────┐
|
|
76
|
+
│ the catalog card │ models → credential slots
|
|
77
|
+
└────────────────────┘ surfaces → which sections the page shows
|
|
78
|
+
│ requires → "installs a team of N"
|
|
79
|
+
▼
|
|
80
|
+
you press Deploy
|
|
81
|
+
│
|
|
82
|
+
┌───────────┼───────────┬──────────────┬─────────────┐
|
|
83
|
+
▼ ▼ ▼ ▼ ▼
|
|
84
|
+
stores sidecars member remote MCP selections
|
|
85
|
+
created pulled + agents servers written to
|
|
86
|
+
+ bound verified installed bound the Env bag
|
|
87
|
+
+ started + attached
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Every step is idempotent. Deploying the same version twice changes nothing;
|
|
91
|
+
deploying a newer version re-runs the steps and keeps what you configured by
|
|
92
|
+
hand.
|
|
93
|
+
|
|
94
|
+
## Backward compatibility by synthesis
|
|
95
|
+
|
|
96
|
+
A block you do not declare is not "missing" — the platform **synthesises the
|
|
97
|
+
equivalent**, so an old template behaves exactly as it always did:
|
|
98
|
+
|
|
99
|
+
- no `models` → one ordinary bring-your-own-key model block;
|
|
100
|
+
- no `surfaces` → the four standard channels (CLI, webhook, schedule, MCP);
|
|
101
|
+
- no `entryPoints` → the platform serves each surface itself.
|
|
102
|
+
|
|
103
|
+
:::tip Empty is not the same as absent
|
|
104
|
+
`models: []` is a **real declaration**: "this agent runs no model at all". The
|
|
105
|
+
deploy will not ask you for an AI key. Leaving `models` out entirely means
|
|
106
|
+
something different — "give me the normal one".
|
|
107
|
+
:::
|
|
108
|
+
|
|
109
|
+
## Names, not addresses
|
|
110
|
+
|
|
111
|
+
No template ever writes down a concrete id, host or URL. It declares a **name**;
|
|
112
|
+
the platform binds that name to a real resource at deploy; and whatever consumes
|
|
113
|
+
it resolves the name late — differently on the cloud and on your own box.
|
|
114
|
+
|
|
115
|
+
That is why the same template works in both places, and why nothing breaks when
|
|
116
|
+
a resource is re-created.
|
|
117
|
+
|
|
118
|
+
:::danger Never put a credential in a declaration
|
|
119
|
+
A declaration is public data that travels with the card. Tokens, keys and
|
|
120
|
+
passwords reach an agent **only** through its encrypted Env bag — never through
|
|
121
|
+
a template, a prompt, a trigger payload or a chat message.
|
|
122
|
+
:::
|
|
123
|
+
|
|
124
|
+
## The blocks
|
|
125
|
+
|
|
126
|
+
| Block | What it declares |
|
|
127
|
+
|---|---|
|
|
128
|
+
| [`models`](./models.md) | which model(s) the agent runs, and who supplies the key |
|
|
129
|
+
| [`stores`](./stores.md) | durable storage, provisioned at deploy and injected by name |
|
|
130
|
+
| [`requires`](./requires.md) | an endpoint another agent publishes, installed and attached for you |
|
|
131
|
+
| [`dispatchesWorkflow` / `kind`](./composition.md) | other agents this one runs as sub-graphs — a fleet |
|
|
132
|
+
| [`surfaces` / `entryPoints`](./surfaces.md) | which channels the agent offers, and where each one lives |
|
|
133
|
+
| [`sidecars` / `sidecarSpecs`](./sidecars.md) | a long-lived service container the agent needs |
|
|
134
|
+
| [`remoteMcp`](./remote-mcp.md) | a hosted third-party MCP server the agent is for |
|
|
135
|
+
| [deploy-time configuration](./deploy-time-config.md) | pickers, inputs, a default schedule, trigger events |
|
|
136
|
+
| [node declarations](./node-declarations.md) | skills, integrations and vendor pins, per node |
|
|
137
|
+
| [catalog metadata](./catalog-metadata.md) | slug, tagline, tags, release notes, availability |
|
|
138
|
+
| [Update behavior](./update-behavior.md) | what changes on re-deploy, what is seeded once, what is frozen |
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 2
|
|
3
|
+
title: models
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# `models`
|
|
7
|
+
|
|
8
|
+
## What it is
|
|
9
|
+
|
|
10
|
+
At deploy the platform has to decide two things: which model credentials to ask
|
|
11
|
+
you for, and whether a missing one should block the install. `models` is the
|
|
12
|
+
declaration those decisions are computed from.
|
|
13
|
+
|
|
14
|
+
Each block says **what kind** of model, **which node** it belongs to, **where**
|
|
15
|
+
the key is consumed, and **whether you supply it**. The deploy dialog and the
|
|
16
|
+
model disc on the agent's graph are generic renderings of this array — there is
|
|
17
|
+
no per-agent special case anywhere.
|
|
18
|
+
|
|
19
|
+
## Syntax
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
spec: {
|
|
23
|
+
models: [
|
|
24
|
+
{
|
|
25
|
+
kind: 'llm', // 'llm' | 'embedding'
|
|
26
|
+
target: 'agent', // 'agent' | 'sidecar'
|
|
27
|
+
role: 'review', // the graph NODE this block belongs to
|
|
28
|
+
label: 'Review', // the row heading in the deploy dialog
|
|
29
|
+
byok: true, // true → a real picker; false → a read-only row
|
|
30
|
+
options: ['claude', 'codex', 'gemini'],
|
|
31
|
+
required: true, // does a missing key block the deploy?
|
|
32
|
+
note: 'Reads the diff and writes the review.',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
kind: 'llm', target: 'agent', role: 'triage', label: 'Triage',
|
|
36
|
+
byok: false, required: true,
|
|
37
|
+
followsVendorOf: 'review', // tracks another block's vendor
|
|
38
|
+
fixedModelByVendor: { // …and shows what it then runs
|
|
39
|
+
claude: 'haiku-4.5',
|
|
40
|
+
codex: 'gpt-4o-mini',
|
|
41
|
+
gemini: 'gemini-3-flash',
|
|
42
|
+
},
|
|
43
|
+
note: 'Fixed — a cheap fast-tier model decides how much review depth the change needs.',
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Properties
|
|
50
|
+
|
|
51
|
+
| Name | Type | Required | Allowed values | Default | Update behavior |
|
|
52
|
+
|---|---|---|---|---|---|
|
|
53
|
+
| `kind` | string | No | `llm`, `embedding` | `llm` | Re-applied on the next deploy |
|
|
54
|
+
| `target` | string | No | `agent` (the run container), `sidecar` (a service reads it per request) | `agent` | Re-applied |
|
|
55
|
+
| `role` | string | No | a node id in this agent's graph | none | Re-applied |
|
|
56
|
+
| `label` | string | No | any | the vendor name | Card only |
|
|
57
|
+
| `byok` | boolean | No | `true`, `false` | `true` | Re-applied |
|
|
58
|
+
| `provider` | string | No | e.g. `openai` | none | Re-applied |
|
|
59
|
+
| `options` | string[] | No | vendor ids for `kind: 'llm'`; **model ids** for `kind: 'embedding'` | `[]` | Re-applied |
|
|
60
|
+
| `default` | string | No | one of `options` | none | Re-applied |
|
|
61
|
+
| `required` | boolean | No | `true`, `false` | `true` | Re-applied — **this is the deploy gate** |
|
|
62
|
+
| `followsVendorOf` | string | No | another block's `role` | none | Re-applied |
|
|
63
|
+
| `fixedModelByVendor` | object | No | `{ vendor: modelId }` | none | Re-applied |
|
|
64
|
+
| `note` | string | No | one honest sentence | none | Card only |
|
|
65
|
+
|
|
66
|
+
## The three decisions
|
|
67
|
+
|
|
68
|
+
**`target` — where the key is consumed.**
|
|
69
|
+
`agent` puts it in the run container's environment. `sidecar` routes it to a
|
|
70
|
+
service container that reads it **per request**, from the declaring agent's own
|
|
71
|
+
encrypted Env bag. One shared service, many isolated tenants.
|
|
72
|
+
|
|
73
|
+
**`byok` — what the dialog renders.**
|
|
74
|
+
`true` gives you a real picker that collects a key. `false` gives you an honest,
|
|
75
|
+
read-only line describing what actually runs. It is never a text box that does
|
|
76
|
+
nothing.
|
|
77
|
+
|
|
78
|
+
**`required` — whether a missing key blocks the install.**
|
|
79
|
+
`required: false` is how an agent says "I need no AI key". Use it for a block
|
|
80
|
+
that names an identity rather than adding a dependency — a deterministic node
|
|
81
|
+
that never invokes a model.
|
|
82
|
+
|
|
83
|
+
## Use cases
|
|
84
|
+
|
|
85
|
+
| You want | Declare |
|
|
86
|
+
|---|---|
|
|
87
|
+
| An ordinary bring-your-own-key agent | nothing — it is synthesised for you |
|
|
88
|
+
| An agent that runs no model at all | `models: []` |
|
|
89
|
+
| A different model per node, chosen by the operator | one block per model-running node, each with its own `role` |
|
|
90
|
+
| A cheap fixed tier behind an operator-chosen one | `byok: false` + `followsVendorOf` + `fixedModelByVendor` |
|
|
91
|
+
| An embedding model used by a knowledge-base service | `kind: 'embedding', target: 'sidecar', provider: 'openai', options: [<model ids>]` |
|
|
92
|
+
| A locked identity badge with no key | `byok: false, required: false` |
|
|
93
|
+
|
|
94
|
+
### Example — one block per model-running node
|
|
95
|
+
|
|
96
|
+
The shipped **Product Owner** agent runs three separate judgements, each on its
|
|
97
|
+
own node, each with its own picker:
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
models: [
|
|
101
|
+
{ kind: 'llm', target: 'agent', role: 'plan', label: 'Planner',
|
|
102
|
+
byok: true, options: ['claude','codex','gemini'], required: true,
|
|
103
|
+
note: 'Reads the PRD and the repository, then writes the tickets.' },
|
|
104
|
+
{ kind: 'llm', target: 'agent', role: 'review', label: 'Plan reviewer',
|
|
105
|
+
byok: true, options: ['claude','codex','gemini'], required: true,
|
|
106
|
+
note: 'The second opinion that fails a weak plan back to the planner.' },
|
|
107
|
+
{ kind: 'llm', target: 'agent', role: 'judge', label: 'Acceptance judge',
|
|
108
|
+
byok: true, options: ['claude','codex','gemini'], required: true,
|
|
109
|
+
note: 'Judges a pull request against the ticket\'s acceptance criteria.' },
|
|
110
|
+
]
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Example — an embedding model consumed by a service
|
|
114
|
+
|
|
115
|
+
```js
|
|
116
|
+
models: [{
|
|
117
|
+
kind: 'embedding',
|
|
118
|
+
role: 'kb',
|
|
119
|
+
target: 'sidecar',
|
|
120
|
+
byok: true,
|
|
121
|
+
provider: 'openai',
|
|
122
|
+
options: ['text-embedding-3-small', 'text-embedding-3-large'],
|
|
123
|
+
default: 'text-embedding-3-small',
|
|
124
|
+
required: true,
|
|
125
|
+
}]
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Gotchas
|
|
129
|
+
|
|
130
|
+
**`options` means different things per `kind`.** For `kind: 'llm'` they are
|
|
131
|
+
vendor ids. For `kind: 'embedding'` they are model ids, and the picker offers
|
|
132
|
+
them directly.
|
|
133
|
+
|
|
134
|
+
**`role` must match a node id exactly.** A block whose `role` names no node
|
|
135
|
+
binds to nothing: the pinned tier stops applying, and the node renders a free
|
|
136
|
+
picker whose choice the node's own code then ignores. If your node ids live in
|
|
137
|
+
a constant, import it — do not retype the string.
|
|
138
|
+
|
|
139
|
+
**A composite agent collects its children's keys too.** If your agent dispatches
|
|
140
|
+
another agent, that child's model blocks are appended to yours automatically,
|
|
141
|
+
marked non-blocking and labelled with whose they are. You do not declare them.
|
|
142
|
+
|
|
143
|
+
**Probe a model id before you declare it.** An id that does not exist makes the
|
|
144
|
+
vendor's API reject the call, and the turn comes back empty rather than with an
|
|
145
|
+
error.
|
|
146
|
+
|
|
147
|
+
**Credentials are one per vendor.** The key in the "Codex · OpenAI" slot is the
|
|
148
|
+
same key OpenAI embeddings use. At run time the order is: a key supplied for
|
|
149
|
+
this agent, then the project's credential for that vendor, then (self-hosted
|
|
150
|
+
only) the box's own environment. Nobody should ever paste the same key twice.
|
|
151
|
+
|
|
152
|
+
## See also
|
|
153
|
+
|
|
154
|
+
- [Node declarations](./node-declarations.md) — a node's `agent:` pin is what
|
|
155
|
+
*locks* a vendor; `models` describes it.
|
|
156
|
+
- [Sidecars](./sidecars.md) — `target: 'sidecar'` needs a service container.
|
|
157
|
+
- [Update behavior](./update-behavior.md)
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 10
|
|
3
|
+
title: Node declarations
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Node declarations
|
|
7
|
+
|
|
8
|
+
Some declarations live on a **graph node**, not in the `spec` block,
|
|
9
|
+
because they are about what that node needs.
|
|
10
|
+
|
|
11
|
+
| Field | Declares |
|
|
12
|
+
|---|---|
|
|
13
|
+
| `skills` | the tools this node gets, and the integrations the agent requires |
|
|
14
|
+
| `optionalSkills` | integrations that improve the node but never block a deploy |
|
|
15
|
+
| `agent` | a vendor this node is locked to |
|
|
16
|
+
| `stores` | durable storage — see [`stores`](./stores.md) |
|
|
17
|
+
| `dispatchesWorkflow` | a member agent — see [Composition](./composition.md) |
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## `skills` and `optionalSkills`
|
|
22
|
+
|
|
23
|
+
### What it is
|
|
24
|
+
|
|
25
|
+
A node's single "what this node needs" list. It drives four things at once:
|
|
26
|
+
|
|
27
|
+
1. the MCP tools the model gets **at that node**;
|
|
28
|
+
2. the skill's prompt fragment, appended to the node's prompt;
|
|
29
|
+
3. the required/optional integrations shown on the card and enforced at deploy;
|
|
30
|
+
4. for a no-connection skill, the on/off switch in the agent's Settings.
|
|
31
|
+
|
|
32
|
+
### Syntax
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
import { SKILLS } from '@zibby/core';
|
|
36
|
+
|
|
37
|
+
graph.addNode('review', {
|
|
38
|
+
prompt: …,
|
|
39
|
+
outputSchema: Review,
|
|
40
|
+
skills: [SKILLS.GITHUB, SKILLS.ARTIFACT],
|
|
41
|
+
optionalSkills: [SKILLS.SENTRY],
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Required vs optional
|
|
46
|
+
|
|
47
|
+
| Declared in | Surfaces as | Blocks Deploy? |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| `skills`, and the skill needs a connection | **required** | **Yes** — the Deploy button is blocked until it is connected |
|
|
50
|
+
| `skills`, and the skill is marked optional | optional | No |
|
|
51
|
+
| `optionalSkills` | optional | **Never** |
|
|
52
|
+
| the same skill in **both** arrays on one node | optional **for that node only** | No |
|
|
53
|
+
|
|
54
|
+
That last row is the useful one. A skill can be required by one node and merely
|
|
55
|
+
nice-to-have on another; listing it in a node's `optionalSkills` demotes it for
|
|
56
|
+
**that node** and cannot cancel a different node's requirement.
|
|
57
|
+
|
|
58
|
+
The card shows required and optional in separate groups — "Connect" versus
|
|
59
|
+
"Connect (optional)" — and only the required group can stop an install.
|
|
60
|
+
|
|
61
|
+
### One-of groups
|
|
62
|
+
|
|
63
|
+
Some entries in the array are not skills at all: they are markers that resolve
|
|
64
|
+
to *any one of* a set of providers.
|
|
65
|
+
|
|
66
|
+
| Marker | Satisfied by |
|
|
67
|
+
|---|---|
|
|
68
|
+
| `board_tracker` | Jira **or** Vikunja |
|
|
69
|
+
| `doc_source` | Google Docs **or** Notion **or** Lark Docs |
|
|
70
|
+
| `chat_notify` | Slack **or** Lark |
|
|
71
|
+
|
|
72
|
+
Connect any member and the requirement is met. Entries the platform does not
|
|
73
|
+
recognise are skipped with a warning, so an unknown id costs you nothing.
|
|
74
|
+
|
|
75
|
+
### Toggleable skills
|
|
76
|
+
|
|
77
|
+
A few skills need no connection at all — codebase memory, code scanning,
|
|
78
|
+
artifacts. They render as a simple on/off switch, default on, and the operator
|
|
79
|
+
can turn them off per agent.
|
|
80
|
+
|
|
81
|
+
:::danger Bind a toggleable skill on `skills`, not `optionalSkills`
|
|
82
|
+
The switch will appear either way — but **the tools will not**. Skills reach the
|
|
83
|
+
model only through a node's `skills`; `optionalSkills` is an integration-surface
|
|
84
|
+
declaration that the engine never reads. Put a toggleable skill in
|
|
85
|
+
`optionalSkills` and you ship a switch that controls nothing.
|
|
86
|
+
:::
|
|
87
|
+
|
|
88
|
+
### Gotchas
|
|
89
|
+
|
|
90
|
+
**`optionalSkills` is not part of the serialised graph.** It is read from your
|
|
91
|
+
source when the catalog is built, so it is visible on the card — but anything
|
|
92
|
+
that re-derives from a stored graph will not see it. Treat it as a card-level
|
|
93
|
+
declaration, never as a runtime one.
|
|
94
|
+
|
|
95
|
+
**A store skill is a precondition for [`stores`](./stores.md).** Without it, a
|
|
96
|
+
`stores` block on that node provisions nothing, silently.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## `agent` — pinning a node to a vendor
|
|
101
|
+
|
|
102
|
+
### What it is
|
|
103
|
+
|
|
104
|
+
Locks one node to one coding-agent vendor, regardless of what the operator picks
|
|
105
|
+
elsewhere.
|
|
106
|
+
|
|
107
|
+
### Syntax
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
graph.addNode('plan', { prompt, outputSchema: Plan, agent: 'claude' });
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Properties
|
|
114
|
+
|
|
115
|
+
| Name | Type | Required | Allowed values | Default | Update behavior |
|
|
116
|
+
|---|---|---|---|---|---|
|
|
117
|
+
| `agent` | string | No | `claude`, `codex`, `gemini` | unpinned — the operator's choice applies | Frozen into the published graph; the credential slots are re-applied |
|
|
118
|
+
|
|
119
|
+
### What it does at deploy
|
|
120
|
+
|
|
121
|
+
The distinct set of pins across your graph becomes the agent's vendor
|
|
122
|
+
requirement:
|
|
123
|
+
|
|
124
|
+
| Pins found | Deploy asks for |
|
|
125
|
+
|---|---|
|
|
126
|
+
| none | any vendor — the picker offers all of them |
|
|
127
|
+
| one | that vendor only |
|
|
128
|
+
| several | one credential per vendor |
|
|
129
|
+
|
|
130
|
+
Children you dispatch contribute their pins too, so a parent on Claude that
|
|
131
|
+
dispatches a Codex child collects both keys.
|
|
132
|
+
|
|
133
|
+
### Gotchas
|
|
134
|
+
|
|
135
|
+
**A pinned node is invisible to "apply this model to every node".** It is
|
|
136
|
+
*locked* — that is the point — so it never receives an operator's model choice.
|
|
137
|
+
If every model-running node is locked, there is nothing left for the operator to
|
|
138
|
+
pick.
|
|
139
|
+
|
|
140
|
+
**There is no node-level `model:` declaration.** A model comes from one of three
|
|
141
|
+
places: the operator's per-node pick, a literal in your node's own code, or —
|
|
142
|
+
best — [`models[].fixedModelByVendor`](./models.md) joined to the node by
|
|
143
|
+
`role`. Only the third is visible on the card and the canvas. Prefer it.
|
|
144
|
+
|
|
145
|
+
## See also
|
|
146
|
+
|
|
147
|
+
- [`models`](./models.md), [`stores`](./stores.md),
|
|
148
|
+
[Composition](./composition.md)
|
|
149
|
+
- [Skills](../concepts/skills.md)
|