@zibby/skills 2.0.4 → 2.0.6
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/artifact.js +7 -7
- package/dist/chat-notify.js +4 -4
- package/dist/chatProgress.js +4 -4
- package/dist/datasetStore.js +2 -2
- package/dist/discord.js +3 -3
- package/dist/figma.js +2 -2
- package/dist/git-write.js +9 -9
- package/dist/github.js +5 -5
- package/dist/gitlab.js +5 -5
- package/dist/googleDocs.js +9 -9
- package/dist/hubspot.js +1 -1
- package/dist/index.js +179 -181
- package/dist/jira.d.ts +36 -1
- package/dist/jira.js +22 -20
- package/dist/kvMemory.js +2 -2
- package/dist/lark.js +2 -2
- package/dist/larkAttendance.js +1 -1
- package/dist/larkDocs.js +3 -3
- package/dist/lib/http-deadline.d.ts +117 -0
- package/dist/lib/http-deadline.js +1 -0
- package/dist/lib/markup.d.ts +158 -0
- package/dist/lib/markup.js +22 -0
- package/dist/linear.js +15 -15
- package/dist/linkedin.js +2 -2
- package/dist/llm-billing.js +1 -1
- package/dist/notion.js +7 -7
- package/dist/opendesign.js +2 -2
- package/dist/package.json +6 -5
- package/dist/report.d.ts +42 -42
- package/dist/review.js +4 -4
- package/dist/reviewMemoryIo.js +1 -1
- package/dist/sentry.js +2 -2
- package/dist/skill-installer.js +3 -3
- package/dist/slack.js +2 -2
- package/dist/trackers/github-adapter.js +5 -5
- package/dist/trackers/index.js +46 -44
- package/dist/trackers/jira-adapter.js +24 -22
- package/dist/trackers/linear-adapter.js +19 -19
- package/dist/trackers/plane-adapter.js +1 -1
- package/dist/triggerAgent.js +1 -1
- package/dist/vikunja.d.ts +8 -1
- package/dist/vikunja.js +22 -7
- 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 +6 -5
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 4
|
|
3
|
+
title: requires
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# `requires`
|
|
7
|
+
|
|
8
|
+
## What it is
|
|
9
|
+
|
|
10
|
+
One agent needs a capability another agent publishes — a browser, a knowledge
|
|
11
|
+
base, a bridge to an internal API. `requires` declares that need by **name**.
|
|
12
|
+
|
|
13
|
+
At deploy the platform installs the provider as one of **this agent's own
|
|
14
|
+
members** — the same cascade, the same binding record and the same delete
|
|
15
|
+
cascade a sub-graph child gets — and then attaches the provider's endpoint to
|
|
16
|
+
your agent as a managed MCP server.
|
|
17
|
+
|
|
18
|
+
Nothing concrete is written in the template: no id, no URL, no credential.
|
|
19
|
+
|
|
20
|
+
## There is no field to put a token in
|
|
21
|
+
|
|
22
|
+
This is the part people look for and do not find, so it is worth stating plainly:
|
|
23
|
+
|
|
24
|
+
- **A consumer declares WHO it uses.** Board Autopilot declares
|
|
25
|
+
`requires: [{ ref: 'endpoint:gbrain-kb/mcp', as: 'kb' }]` — and nothing else.
|
|
26
|
+
- **A provider declares WHAT IT NEEDS.** The knowledge base declares its own
|
|
27
|
+
embedding model, in its own template.
|
|
28
|
+
- **The credential that lets one call the other is in neither declaration.** The
|
|
29
|
+
platform mints it, encrypts it into the consumer's own environment, and never
|
|
30
|
+
returns it, logs it, or shows it to a model.
|
|
31
|
+
|
|
32
|
+
So there is no token field, no shared secret to distribute and nothing for you
|
|
33
|
+
to rotate. If you find yourself wanting to write a credential into a template,
|
|
34
|
+
the design has gone wrong somewhere — a declaration is public data that travels
|
|
35
|
+
with the card.
|
|
36
|
+
|
|
37
|
+
:::info Your agent installs its own members
|
|
38
|
+
A consumer never borrows a provider that happens to already exist in the
|
|
39
|
+
project. It installs its own, always. The heavy engine underneath is shared per
|
|
40
|
+
box anyway, so the extra agent row is just configuration.
|
|
41
|
+
:::
|
|
42
|
+
|
|
43
|
+
## Syntax
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
spec: {
|
|
47
|
+
requires: [{ ref: 'endpoint:browser/mcp', as: 'browser' }],
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
A bare string works too — `as` then defaults to the provider's slug:
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
requires: ['endpoint:browser/mcp'],
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Properties
|
|
58
|
+
|
|
59
|
+
| Name | Type | Required | Allowed values | Default | Update behavior |
|
|
60
|
+
|---|---|---|---|---|---|
|
|
61
|
+
| `ref` | string | **Yes** | `<kind>:<provider-slug>/<entry-name>`. All lowercase, letters/digits/hyphens; each part must start with a letter or digit. **`endpoint` is the only kind today** | — | Re-applied. The member is installed and the endpoint attached, idempotently |
|
|
62
|
+
| `as` | string | No | letters, digits, `-`, `_`; must start with a letter or digit | the provider slug | Re-applied. This is the handle your code looks the link up by |
|
|
63
|
+
|
|
64
|
+
`<entry-name>` is a surface the provider **publishes** — `mcp` for an agent that
|
|
65
|
+
serves an MCP endpoint.
|
|
66
|
+
|
|
67
|
+
## What deploy does
|
|
68
|
+
|
|
69
|
+
1. Adds the provider to this agent's member list, alongside any sub-graph
|
|
70
|
+
children.
|
|
71
|
+
2. Installs it (recursively — its own members come too).
|
|
72
|
+
3. Records the binding on your agent.
|
|
73
|
+
4. Attaches the bound provider's endpoint as a managed MCP server, tagged with
|
|
74
|
+
your alias.
|
|
75
|
+
|
|
76
|
+
Re-deploying is safe. An existing link is kept as-is; a stale one is replaced;
|
|
77
|
+
a link you added by hand to the same provider is adopted rather than duplicated.
|
|
78
|
+
|
|
79
|
+
## The two ways to consume it
|
|
80
|
+
|
|
81
|
+
This is the part worth reading twice. Both shipped examples are mechanically
|
|
82
|
+
identical to the platform — the attached endpoint is reachable from every node's
|
|
83
|
+
model either way. **What differs is the choice your template makes.**
|
|
84
|
+
|
|
85
|
+
| | **A. Model-driven** | **B. Code-driven** |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| Who calls the endpoint | the model, by calling its tools | your node's own code |
|
|
88
|
+
| What the node does to resolve it | nothing | looks the link up by its alias |
|
|
89
|
+
| What the model sees | tools in its tool list | nothing — just text in the prompt |
|
|
90
|
+
| When it is missing | the model is told to skip and say why | one log line, an empty block |
|
|
91
|
+
| Reach for it when | the work is open-ended | the retrieval must happen every run |
|
|
92
|
+
|
|
93
|
+
### A. Model-driven — the model calls the tools
|
|
94
|
+
|
|
95
|
+
The **Frontend Specialist** agent requires a browser. Its QA node does not
|
|
96
|
+
resolve anything: it simply prompts, and the model discovers `browser_navigate`,
|
|
97
|
+
`browser_take_screenshot` and the rest in its tool list.
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
// the declaration
|
|
101
|
+
requires: [{ ref: 'endpoint:browser/mcp', as: 'browser' }],
|
|
102
|
+
|
|
103
|
+
// the node — no MCP wiring at all
|
|
104
|
+
const out = await invokeAgent(prompt, { state });
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The prompt is written to degrade honestly:
|
|
108
|
+
|
|
109
|
+
> If you do **not** have browser tools, or the preview is unreachable: do not
|
|
110
|
+
> fabricate anything and do not mark checks passed. Set `skippedReason`
|
|
111
|
+
> explaining exactly what was missing.
|
|
112
|
+
|
|
113
|
+
So with no browser attached the node returns `qaRan: false` plus an honest
|
|
114
|
+
reason, and the run still completes.
|
|
115
|
+
|
|
116
|
+
**Choose this** when the work is open-ended and the model should decide how many
|
|
117
|
+
calls to make.
|
|
118
|
+
|
|
119
|
+
### B. Code-driven — your code calls it, the model never sees a tool
|
|
120
|
+
|
|
121
|
+
The **Board Autopilot** manager requires a knowledge base. Its `observe` node
|
|
122
|
+
retrieves the top few relevant records *in code* and splices the text into the
|
|
123
|
+
briefing. The model is never told a KB exists.
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
const KB_REQUIRE_AS = 'kb'; // must equal the `as` above
|
|
127
|
+
const KB_MANAGED_BY = `requires:${KB_REQUIRE_AS}`;
|
|
128
|
+
|
|
129
|
+
// 1. read this agent's own row
|
|
130
|
+
// GET {api}/projects/{PROJECT_ID}/workflows/{WORKFLOW_TYPE}
|
|
131
|
+
// 2. find the link the platform attached under our alias
|
|
132
|
+
const entry = (row.customMcp || [])
|
|
133
|
+
.find((e) => e && e.managedBy === KB_MANAGED_BY && e.id && e.url);
|
|
134
|
+
|
|
135
|
+
// 3. call it through the broker
|
|
136
|
+
// POST {api}/mcp/broker/{WORKFLOW_UUID}/{entry.id} → tools/call
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
If the link is missing, the node logs once, returns an empty block, and the tick
|
|
140
|
+
carries on.
|
|
141
|
+
|
|
142
|
+
**Choose this** when the retrieval is deterministic, must happen on every run,
|
|
143
|
+
and the model must not be able to skip it or spend a turn deciding to.
|
|
144
|
+
|
|
145
|
+
:::tip Pin the alias with a test
|
|
146
|
+
A code-driven consumer looks the link up by the literal
|
|
147
|
+
`requires:<alias>`. Assert in a test that the constant in your node and the `as`
|
|
148
|
+
in your `spec` block are the same string — nothing else checks it.
|
|
149
|
+
:::
|
|
150
|
+
|
|
151
|
+
## Gotchas
|
|
152
|
+
|
|
153
|
+
**A malformed `ref` is dropped silently.** A wrong kind, a capital letter or an
|
|
154
|
+
underscore inside a segment makes the entry vanish at normalisation, with no
|
|
155
|
+
diagnostic anywhere — the agent deploys without its capability. Copy a working
|
|
156
|
+
ref; do not retype one.
|
|
157
|
+
|
|
158
|
+
**A bad `as` degrades to the provider slug** rather than failing. That then
|
|
159
|
+
breaks a code-driven consumer looking for a different alias.
|
|
160
|
+
|
|
161
|
+
**The provider has to publish the entry you name.** If it does not, the deploy
|
|
162
|
+
fails with a message naming the ref.
|
|
163
|
+
|
|
164
|
+
**Dangling refs fail the deploy loudly.** Provider not bound, endpoint not
|
|
165
|
+
published, link not buildable — each returns an error naming the ref and telling
|
|
166
|
+
you to fix it and re-deploy. The cascade is idempotent, so re-deploying
|
|
167
|
+
re-attaches.
|
|
168
|
+
|
|
169
|
+
**At run time nothing throws.** Both patterns degrade: an honest skip, or an
|
|
170
|
+
empty block. That is deliberate — a missing capability should not lose the run.
|
|
171
|
+
|
|
172
|
+
:::caution Self-hosted only, today
|
|
173
|
+
`requires` attaches one agent to another over the per-agent MCP endpoint, and
|
|
174
|
+
that endpoint exists on self-hosted boxes only. A template with a `requires`
|
|
175
|
+
block cannot finish deploying on Zibby Cloud yet.
|
|
176
|
+
:::
|
|
177
|
+
|
|
178
|
+
## See also
|
|
179
|
+
|
|
180
|
+
- [`surfaces` and `entryPoints`](./surfaces.md) — what a provider must publish.
|
|
181
|
+
- [Composition](./composition.md) — the other kind of member; the two share one
|
|
182
|
+
member list.
|
|
183
|
+
- [`remoteMcp`](./remote-mcp.md) — the same idea for a third-party server.
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 7
|
|
3
|
+
title: sidecars & sidecarSpecs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# `sidecarSpecs` and `sidecars`
|
|
7
|
+
|
|
8
|
+
## What it is
|
|
9
|
+
|
|
10
|
+
Some agents need a long-lived service running beside them: a browser, a vector
|
|
11
|
+
database, a bridge that turns a REST API into tools. Those are **sidecars**.
|
|
12
|
+
|
|
13
|
+
`sidecarSpecs` carries the **whole specification** — name, a checksum-pinned
|
|
14
|
+
image on Zibby's own CDN, the port, the per-tenant config it reads — rather than
|
|
15
|
+
a name the platform must already know. That is what makes a brand-new
|
|
16
|
+
sidecar-backed agent *a template and nothing else*.
|
|
17
|
+
|
|
18
|
+
`sidecars` is the short form: a name for a sidecar the platform already ships.
|
|
19
|
+
|
|
20
|
+
At deploy the control plane fetches the image, **verifies its SHA-256**, loads it
|
|
21
|
+
and starts it on the internal network. The agent's run container never pulls
|
|
22
|
+
anything and never dials it directly.
|
|
23
|
+
|
|
24
|
+
:::caution Self-hosted only
|
|
25
|
+
Sidecar hosting is not built on Zibby Cloud yet. A template that declares one is
|
|
26
|
+
marked self-hosted-only and the cloud catalog will not deploy it. There is one
|
|
27
|
+
exception, [below](#the-one-cloud-exception).
|
|
28
|
+
:::
|
|
29
|
+
|
|
30
|
+
## Syntax
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
spec: {
|
|
34
|
+
selfHostOnly: true,
|
|
35
|
+
sidecarSpecs: [{
|
|
36
|
+
name: 'openapi-mcp',
|
|
37
|
+
version: '0.2.3',
|
|
38
|
+
s3Url: 'https://dl.zibby.app/sidecars/openapi-mcp/0.2.3.tar.gz',
|
|
39
|
+
sha256: 'caa4ebfc04acf725f3afd0afa8d0ddf942a600eea364ec821351e3f19af53b4f',
|
|
40
|
+
bytes: 2500336, // informational — shown in the dialog
|
|
41
|
+
arch: {
|
|
42
|
+
arm64: {
|
|
43
|
+
s3Url: 'https://dl.zibby.app/sidecars/openapi-mcp/0.2.3-arm64.tar.gz',
|
|
44
|
+
sha256: '38fe9ca08961eed8c2768e3d036da6deaf4ca2384543ea243deb9685a77ca566',
|
|
45
|
+
bytes: 2305415,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
port: 8080,
|
|
49
|
+
healthPath: '/health',
|
|
50
|
+
warm: true,
|
|
51
|
+
requestConfigKeys: ['OPENAPI_APIS', 'API_ROOT', 'API_TOKEN'],
|
|
52
|
+
}],
|
|
53
|
+
// NO `sidecars: ['openapi-mcp']` — a full spec implies its own name.
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Properties
|
|
58
|
+
|
|
59
|
+
| Name | Type | Required | Allowed values | Default | Update behavior |
|
|
60
|
+
|---|---|---|---|---|---|
|
|
61
|
+
| `name` | string | **Yes** | lowercase, starts with a letter, letters/digits/hyphens, 2–31 chars. May not shadow a built-in name | — | Re-applied |
|
|
62
|
+
| `port` | integer | **Yes** | 1–65535 | — | Re-applied |
|
|
63
|
+
| `sha256` | string | **Yes** | 64 hex characters | — | Re-applied — a new checksum pulls a new image |
|
|
64
|
+
| `s3Url` | string | **Yes** | an allow-listed host | — | Re-applied |
|
|
65
|
+
| `version` | string | No | a version string you own | none | Re-applied; shown in the Update dialog |
|
|
66
|
+
| `arch.<arch>` | object | No | `{ s3Url, sha256, bytes }`, validated identically | none | Re-applied |
|
|
67
|
+
| `healthPath` | string | No | a path | `/health` | Re-applied |
|
|
68
|
+
| `warm` | boolean | No | | `false` | Re-applied |
|
|
69
|
+
| `bytes` | integer | No | **informational only** — never checked against the download | none | Card only |
|
|
70
|
+
| `dataPath` | string | No | an absolute path — and it must be `/data` | none | Re-applied |
|
|
71
|
+
| `dataPurpose` | string | No | `data`, `credentials` | `data` | Re-applied — decides what uninstall deletes |
|
|
72
|
+
| `requestConfigKeys` | string[] | No | Env keys read **per request** from the declaring agent's own encrypted bag | `[]` | Re-applied |
|
|
73
|
+
| `envKeys` | string[] | No | container-wide environment | `[]` | Re-applied |
|
|
74
|
+
| `publicPaths` | string[] | No | prefixes exposed **anonymously** on your box's public origin — refused by default | none | Re-applied |
|
|
75
|
+
| `storeTypes` | string[] | No | store types this sidecar serves | `[]` | Re-applied |
|
|
76
|
+
| `instancePerAgent` | boolean | No | | `false` | Re-applied |
|
|
77
|
+
| `memoryBytes` / `pidsLimit` | integer | No | 64 MiB – 8 GiB / 16 – 4096 | platform defaults | Re-applied |
|
|
78
|
+
|
|
79
|
+
`sidecars` is simply `string[]`. A name nobody knows is skipped with a warning,
|
|
80
|
+
not an error.
|
|
81
|
+
|
|
82
|
+
## One shared service, many isolated tenants
|
|
83
|
+
|
|
84
|
+
A sidecar is **one process hosting N isolated tenants**, not one container per
|
|
85
|
+
agent. Everything that differs per agent — data *and* configuration — is
|
|
86
|
+
isolated:
|
|
87
|
+
|
|
88
|
+
- **Data** is keyed by a server-derived tenant key, so different tenants run in
|
|
89
|
+
parallel and the same tenant serialises.
|
|
90
|
+
- **Configuration and secrets** come from `requestConfigKeys`, injected **per
|
|
91
|
+
request** from the declaring agent's encrypted Env bag — never as
|
|
92
|
+
container-wide environment.
|
|
93
|
+
|
|
94
|
+
That last point is the one people get wrong. Putting an OAuth client id and
|
|
95
|
+
secret in `envKeys` means two projects with different apps collide, and the fix
|
|
96
|
+
is not "error on conflict" — it is per-request injection.
|
|
97
|
+
|
|
98
|
+
## Gotchas
|
|
99
|
+
|
|
100
|
+
:::danger A full spec implies its name
|
|
101
|
+
If you declare `sidecarSpecs`, do **not** also list that name in `sidecars`.
|
|
102
|
+
The name is taken from the spec. Repeating it is a second place to keep in sync.
|
|
103
|
+
:::
|
|
104
|
+
|
|
105
|
+
:::danger Durable state lives at `/data` — one path, every sidecar
|
|
106
|
+
The `dataPath` you declare and the path your app actually writes must be the
|
|
107
|
+
**same string**. Nothing cross-checks them, so a mismatch is silent: the data
|
|
108
|
+
lands in the container's throwaway layer and dies with it, while the volume you
|
|
109
|
+
declared sits empty. A live OAuth token store was lost exactly that way. Do not
|
|
110
|
+
"fix" a mismatch by injecting a path variable — change one of them.
|
|
111
|
+
:::
|
|
112
|
+
|
|
113
|
+
**A checksum pins bytes, not a CPU.** There is deliberately no fallback between
|
|
114
|
+
architectures: an x86 image verifies perfectly on an ARM box and then dies with
|
|
115
|
+
`exec format error`. Publish and pin each architecture, or that architecture
|
|
116
|
+
fails loudly.
|
|
117
|
+
|
|
118
|
+
**A URL with no checksum fails closed.** The image is never loaded unverified.
|
|
119
|
+
|
|
120
|
+
**A template-carried sidecar does not auto-update.** Its version belongs to you:
|
|
121
|
+
it moves when your `version`/`sha256`/`s3Url` move and the user takes the
|
|
122
|
+
Update. Platform-shipped sidecars follow a CDN channel instead.
|
|
123
|
+
|
|
124
|
+
:::danger A sidecar with no authentication of its own must not declare `publicPaths`
|
|
125
|
+
`publicPaths` exposes those prefixes **anonymously** on your box's public
|
|
126
|
+
origin, with no platform authentication added. Agents and the chat assistant
|
|
127
|
+
reach a sidecar over the internal network and do not need it.
|
|
128
|
+
|
|
129
|
+
Declare an [`entryPoints.mcp`](./surfaces.md) with `auth: 'bearer-pat'` instead,
|
|
130
|
+
and let the platform's own authenticated endpoint forward to you — it adds the
|
|
131
|
+
token check, the ownership check, and per-request tenant configuration.
|
|
132
|
+
|
|
133
|
+
It also costs you cloud deployability. See below.
|
|
134
|
+
:::
|
|
135
|
+
|
|
136
|
+
**`warm: true` is sometimes required, not an optimisation.** A sidecar is
|
|
137
|
+
started by the boot pre-warmer, by a public-path proxy, or by the store router.
|
|
138
|
+
An agent that is dialled directly as an MCP server matches none of those, so
|
|
139
|
+
without `warm` it would never be running.
|
|
140
|
+
|
|
141
|
+
**Share a spec, do not copy it.** If several templates use the same sidecar,
|
|
142
|
+
import the spec from one place. Two inlined copies at different versions will
|
|
143
|
+
fight, and the last deploy wins — which can silently downgrade the shared engine.
|
|
144
|
+
|
|
145
|
+
## The one cloud exception
|
|
146
|
+
|
|
147
|
+
A self-hosted-only template **is** deployable on Zibby Cloud when it is
|
|
148
|
+
browser-host-servable — judged from the declaration, never from the name:
|
|
149
|
+
|
|
150
|
+
- every carried spec is fully pinned (`name`, `version`, `port`, `s3Url`,
|
|
151
|
+
`sha256`), **and**
|
|
152
|
+
- no spec declares `publicPaths`, **and**
|
|
153
|
+
- the template declares an `entryPoints.mcp` pointing at one of its sidecars.
|
|
154
|
+
|
|
155
|
+
One `publicPaths` entry costs the template its cloud deployability.
|
|
156
|
+
|
|
157
|
+
## See also
|
|
158
|
+
|
|
159
|
+
- [`surfaces` and `entryPoints`](./surfaces.md)
|
|
160
|
+
- [`stores`](./stores.md) — a store type brings its own sidecar; do not name it
|
|
161
|
+
twice.
|
|
162
|
+
- [Bring your own sidecar](../self-host/custom-sidecars.md) — the operator route,
|
|
163
|
+
for a service that is not a catalog agent.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 3
|
|
3
|
+
title: stores
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# `stores`
|
|
7
|
+
|
|
8
|
+
## What it is
|
|
9
|
+
|
|
10
|
+
Durable storage the agent needs. Declared **on a node**, not in the
|
|
11
|
+
`spec` block.
|
|
12
|
+
|
|
13
|
+
At deploy the platform creates (or re-uses) one store per declaration and writes
|
|
14
|
+
its id into the agent's encrypted Env bag under the name you chose. Your code
|
|
15
|
+
never sees an id in the template — it asks for the name and gets the resource.
|
|
16
|
+
|
|
17
|
+
This is the reference implementation of the naming rule: declare a name, the
|
|
18
|
+
platform binds it, the consumer resolves it late.
|
|
19
|
+
|
|
20
|
+
## Syntax
|
|
21
|
+
|
|
22
|
+
```js
|
|
23
|
+
// nodes/ingest-node.js
|
|
24
|
+
import { SKILLS } from '@zibby/core';
|
|
25
|
+
|
|
26
|
+
export const ingestNode = {
|
|
27
|
+
name: 'ingest',
|
|
28
|
+
skills: [SKILLS.DATASET_STORE], // required — see gotchas
|
|
29
|
+
stores: [
|
|
30
|
+
{ name: 'knowledge_docs', type: 'docs', description: 'Verbatim markdown archive' },
|
|
31
|
+
{ name: 'knowledge_meta', type: 'sqlite', description: 'Per-document metadata',
|
|
32
|
+
schema: {
|
|
33
|
+
tables: {
|
|
34
|
+
knowledge_meta: {
|
|
35
|
+
ddl: 'CREATE TABLE IF NOT EXISTS knowledge_meta (id TEXT PRIMARY KEY, title TEXT)',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The minimal form is just a name and a description:
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
stores: [{ name: 'scorecards', description: 'Per-commit engineering scorecard' }],
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Properties
|
|
51
|
+
|
|
52
|
+
| Name | Type | Required | Allowed values | Default | Update behavior |
|
|
53
|
+
|---|---|---|---|---|---|
|
|
54
|
+
| `name` | string | **Yes** | lowercase, starts with a letter, letters/digits/underscore, ≤ 41 chars. **Unique across the whole agent**, not just the node | — | Re-applied (idempotent). A **new** name creates a **new** store; renaming leaves the old one behind |
|
|
55
|
+
| `type` | string | No | `dataset`, `sqlite`, `file`, `docs`, `postgres` (self-hosted only) | `dataset` | Re-applied. An unknown type fails the deploy loudly |
|
|
56
|
+
| `description` | string | No | any — it is what the agent's store catalogue shows the model | none | Re-applied |
|
|
57
|
+
| `schema` | object | No | `{ tables: { <name>: { ddl } } }` — **`sqlite` only**. Each `ddl` must begin `CREATE TABLE IF NOT EXISTS` and the table it creates must match the key | none | Re-applied, but an existing table is never altered |
|
|
58
|
+
|
|
59
|
+
## Store types
|
|
60
|
+
|
|
61
|
+
| Type | Use it for |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `dataset` | append-and-query records — the default |
|
|
64
|
+
| `sqlite` | relational data with a schema you declare |
|
|
65
|
+
| `file` | opaque blobs |
|
|
66
|
+
| `docs` | a verbatim document archive |
|
|
67
|
+
| `postgres` | a vector knowledge base (self-hosted only) |
|
|
68
|
+
|
|
69
|
+
## What deploy does
|
|
70
|
+
|
|
71
|
+
1. Computes a stable key from the agent, the node and the name, so re-deploying
|
|
72
|
+
re-uses the same store instead of creating another one.
|
|
73
|
+
2. Creates the store if it does not exist.
|
|
74
|
+
3. Writes `ZIBBY_STORE__<name> = <store id>` into the encrypted Env bag.
|
|
75
|
+
4. Creates any declared SQLite tables.
|
|
76
|
+
5. Seeds the node's store catalogue so the model can see what it has.
|
|
77
|
+
|
|
78
|
+
At run time the store skill turns those environment entries back into names, and
|
|
79
|
+
sub-graph children inherit them.
|
|
80
|
+
|
|
81
|
+
## Gotchas
|
|
82
|
+
|
|
83
|
+
:::danger A `stores` block on a node with no store skill is silently ignored
|
|
84
|
+
The same node must also declare a store skill (`dataset-store`, or `gbrain` for
|
|
85
|
+
a vector KB) in its **`skills`** array. Putting it in `optionalSkills` instead
|
|
86
|
+
provisions nothing — no store, no error, no warning.
|
|
87
|
+
:::
|
|
88
|
+
|
|
89
|
+
**Names are unique per agent, not per node.** Two nodes cannot each declare a
|
|
90
|
+
store called `results`.
|
|
91
|
+
|
|
92
|
+
**If you re-point a store by hand, that wins forever.** Setting
|
|
93
|
+
`ZIBBY_STORE__<name>` yourself to something other than the provisioned id tells
|
|
94
|
+
the platform this is deliberate: it stops re-binding the name and stops applying
|
|
95
|
+
the declared schema.
|
|
96
|
+
|
|
97
|
+
**A schema on a non-SQLite store is a hard error at deploy**, not a warning.
|
|
98
|
+
|
|
99
|
+
**Declared tables are created, never migrated.** If a table already exists it is
|
|
100
|
+
left exactly as it is, even when your `ddl` has changed. Adding a column is your
|
|
101
|
+
migration to write.
|
|
102
|
+
|
|
103
|
+
**A store type brings its own service.** `postgres` pulls the vector-KB engine
|
|
104
|
+
by itself — do not also name it under [`sidecars`](./sidecars.md).
|
|
105
|
+
|
|
106
|
+
**`postgres` cannot be deployed on Zibby Cloud today.** Templates that need it
|
|
107
|
+
are marked self-hosted-only.
|
|
108
|
+
|
|
109
|
+
## See also
|
|
110
|
+
|
|
111
|
+
- [Node declarations](./node-declarations.md) — the store skill is a
|
|
112
|
+
precondition, not a nicety.
|
|
113
|
+
- [Sidecars](./sidecars.md)
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 6
|
|
3
|
+
title: surfaces & entryPoints
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# `surfaces` and `entryPoints`
|
|
7
|
+
|
|
8
|
+
Two blocks, one subject: **which channels your agent offers, and where each one
|
|
9
|
+
lives.**
|
|
10
|
+
|
|
11
|
+
- `surfaces` — *which* channels the agent's page should show.
|
|
12
|
+
- `entryPoints` — *where* a channel lives when it is not served by the platform
|
|
13
|
+
itself, and every word the card shows.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## `surfaces`
|
|
18
|
+
|
|
19
|
+
### What it is
|
|
20
|
+
|
|
21
|
+
The deployed agent's page renders one section per interaction channel.
|
|
22
|
+
`surfaces` says which of them are meaningful, so a pure knowledge-base agent
|
|
23
|
+
does not show a Trigger button that does nothing.
|
|
24
|
+
|
|
25
|
+
### Syntax
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
spec: { surfaces: ['mcp', 'connect'] }
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Properties
|
|
32
|
+
|
|
33
|
+
| Name | Type | Required | Allowed values | Default | Update behavior |
|
|
34
|
+
|---|---|---|---|---|---|
|
|
35
|
+
| `surfaces` | string[] | No | `cli`, `webhook`, `schedule`, `mcp`, `connect` | `['cli','webhook','schedule','mcp']` | Re-applied |
|
|
36
|
+
|
|
37
|
+
| Value | Meaning |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `cli` | started from the command line |
|
|
40
|
+
| `webhook` | started by an inbound HTTP call |
|
|
41
|
+
| `schedule` | started by cron |
|
|
42
|
+
| `mcp` | a machine endpoint an MCP client attaches to |
|
|
43
|
+
| `connect` | a public page a **human opens in a browser** to link their own account — an OAuth sign-in, a device pairing, a licence activation |
|
|
44
|
+
|
|
45
|
+
`connect` is not a webhook: nothing third-party posts to it and it starts no run.
|
|
46
|
+
|
|
47
|
+
Unknown values are ignored. Declaring nothing gives you the four standard
|
|
48
|
+
channels — **not** all five, because `connect` is opt-in by design.
|
|
49
|
+
|
|
50
|
+
### Use cases
|
|
51
|
+
|
|
52
|
+
| Agent shape | Declare |
|
|
53
|
+
|---|---|
|
|
54
|
+
| An ordinary triggerable workflow | nothing |
|
|
55
|
+
| A pure store — running it directly is a no-op, it is driven over MCP | `['mcp']` |
|
|
56
|
+
| A service a person signs into, then uses from their editor | `['mcp', 'connect']` |
|
|
57
|
+
|
|
58
|
+
### Gotchas
|
|
59
|
+
|
|
60
|
+
Declaring `mcp` does not create an endpoint — it says the agent has one. The
|
|
61
|
+
endpoint still has to exist.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## `entryPoints`
|
|
66
|
+
|
|
67
|
+
### What it is
|
|
68
|
+
|
|
69
|
+
For a channel that need not be served by the platform, `entryPoints` names which
|
|
70
|
+
service serves it and at which path. **The URL itself is derived when the page
|
|
71
|
+
is read** — from the service's resolved public mount and your installation's
|
|
72
|
+
origin. No template ever writes a URL down.
|
|
73
|
+
|
|
74
|
+
For `connect` it also carries every word on the card, because the renderer has
|
|
75
|
+
none of its own.
|
|
76
|
+
|
|
77
|
+
### Syntax
|
|
78
|
+
|
|
79
|
+
An MCP endpoint the platform authenticates and forwards to a service:
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
spec: {
|
|
83
|
+
surfaces: ['mcp'],
|
|
84
|
+
entryPoints: {
|
|
85
|
+
mcp: {
|
|
86
|
+
sidecar: 'openapi-mcp',
|
|
87
|
+
path: '/mcp',
|
|
88
|
+
auth: 'bearer-pat',
|
|
89
|
+
label: 'MCP endpoint for the APIs this agent bridges',
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
A sign-in the platform drives, on an agent with no service container at all:
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
spec: {
|
|
99
|
+
surfaces: ['mcp', 'connect'],
|
|
100
|
+
entryPoints: {
|
|
101
|
+
connect: {
|
|
102
|
+
kind: 'oauth',
|
|
103
|
+
server: 'figma', // a name from this agent's own remoteMcp block
|
|
104
|
+
label: 'Connect your Figma account',
|
|
105
|
+
button: 'Connect',
|
|
106
|
+
buttonDisconnect: 'Disconnect',
|
|
107
|
+
disconnectConfirm: 'Disconnect Figma? The stored sign-in is deleted.',
|
|
108
|
+
connectedLine: 'Connected to Figma as {account}.',
|
|
109
|
+
},
|
|
110
|
+
// NO `mcp` entry — the absence IS the declaration. See below.
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Properties
|
|
116
|
+
|
|
117
|
+
Only `mcp` and `connect` can be redirected. `cli`, `webhook` and `schedule` are
|
|
118
|
+
platform mechanics with no alternative home.
|
|
119
|
+
|
|
120
|
+
| Name | Type | Required | Allowed values | Default | Update behavior |
|
|
121
|
+
|---|---|---|---|---|---|
|
|
122
|
+
| `sidecar` | string | **Yes**, except for `connect` with `kind: 'oauth'` | a service this agent declares, or one the platform ships | — (entry is dropped without it) | Re-applied |
|
|
123
|
+
| `path` | string | **Yes**, same exception | must start with `/`; `..` is refused | — | Re-applied |
|
|
124
|
+
| `auth` | string | No | `bearer-pat`, `connect-bearer`, `none` | `connect-bearer` for `mcp`, `none` for `connect` | Re-applied |
|
|
125
|
+
| `label` | string | No | any | none | Card only |
|
|
126
|
+
| `kind` (connect) | string | No | `sidecar-page`, `oauth` | `sidecar-page` | Re-applied |
|
|
127
|
+
| `server` (connect + `oauth`) | string | **Yes** for that kind | a name from `remoteMcp` | — | Re-applied |
|
|
128
|
+
| copy (`button`, `buttonDisconnect`, `disconnectConfirm`, `disconnectedLine`, `waitingLine`, `connectedLine`, `note`) | string | No | `{account}` is substituted | none — a field you omit renders nothing | Card only |
|
|
129
|
+
|
|
130
|
+
### `auth` names what the CALLER presents
|
|
131
|
+
|
|
132
|
+
| Value | The caller presents |
|
|
133
|
+
|---|---|
|
|
134
|
+
| `bearer-pat` | a Zibby access token — the platform authenticates and forwards |
|
|
135
|
+
| `connect-bearer` | the token this agent's own sign-in handed the user |
|
|
136
|
+
| `none` | nothing — the surface authenticates the caller itself |
|
|
137
|
+
|
|
138
|
+
:::warning A misspelt `auth` is silently corrected, not rejected
|
|
139
|
+
An unrecognised value falls back to the channel's default, so a typo quietly
|
|
140
|
+
describes an authentication your endpoint does not use. Spell it exactly.
|
|
141
|
+
:::
|
|
142
|
+
|
|
143
|
+
### An absent `entryPoints.mcp` is itself a declaration
|
|
144
|
+
|
|
145
|
+
Leave it out and **the platform serves the surface itself**, at the agent's own
|
|
146
|
+
authenticated MCP address, with a Zibby bearer token.
|
|
147
|
+
|
|
148
|
+
That is the right choice whenever your service has no authentication of its own.
|
|
149
|
+
Naming a source when you did not need to is how an endpoint gets taken away from
|
|
150
|
+
the sign-in that feeds it: the declared service wins the address and answers
|
|
151
|
+
every call with its own "missing token".
|
|
152
|
+
|
|
153
|
+
:::tip One agent, one MCP address
|
|
154
|
+
A declared MCP service takes the **whole** surface — other MCP servers attached
|
|
155
|
+
to the same agent are not merged into it. If you need both, you need two agents.
|
|
156
|
+
:::
|
|
157
|
+
|
|
158
|
+
### Use cases
|
|
159
|
+
|
|
160
|
+
| You want | Declare |
|
|
161
|
+
|---|---|
|
|
162
|
+
| Platform-served MCP with a Zibby token | `surfaces: ['mcp']` and nothing else |
|
|
163
|
+
| MCP forwarded to your own service, platform-authenticated | `entryPoints.mcp` with `auth: 'bearer-pat'` |
|
|
164
|
+
| A service that runs its own OAuth and issues its own bearer | `connect` with `auth: 'none'` **and** `mcp` with `auth: 'connect-bearer'` |
|
|
165
|
+
| A hosted third-party MCP the platform signs you into | `connect` with `kind: 'oauth'`, and **no** `mcp` entry |
|
|
166
|
+
|
|
167
|
+
### Gotchas
|
|
168
|
+
|
|
169
|
+
**Both halves are required.** A channel must be in `surfaces` *and* have an
|
|
170
|
+
`entryPoints` entry before anything is derived.
|
|
171
|
+
|
|
172
|
+
**A path the proxy would not serve is never advertised.** If the mount does not
|
|
173
|
+
publish it, the entry is dropped rather than shown as a dead link.
|
|
174
|
+
|
|
175
|
+
**`connect-bearer` turns off the platform's own token check** on the agent's MCP
|
|
176
|
+
address. Only declare it when your service really does authenticate.
|
|
177
|
+
|
|
178
|
+
**Service-served entries need a self-hosted box.** A `connect` entry with
|
|
179
|
+
`kind: 'oauth'` works everywhere.
|
|
180
|
+
|
|
181
|
+
## See also
|
|
182
|
+
|
|
183
|
+
- [`sidecars`](./sidecars.md), [`remoteMcp`](./remote-mcp.md),
|
|
184
|
+
[`requires`](./requires.md)
|