@kybernesis/create 0.7.1 → 0.7.3
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/arcana.d.ts +8 -0
- package/dist/arcana.js +99 -0
- package/dist/cli.js +21 -0
- package/dist/deploy.d.ts +4 -0
- package/dist/deploy.js +169 -0
- package/dist/init.js +6 -0
- package/dist/register.d.ts +5 -0
- package/dist/register.js +110 -0
- package/dist/skills.js +13 -1
- package/package.json +1 -1
- package/skills/fde-engagement/references/playbook.md +11 -0
- package/skills/kybernesis-packages/SKILL.md +7 -3
- package/skills/self-hosting/SKILL.md +13 -3
- package/skills/.claude/skills/certification/SKILL.md +0 -75
- package/skills/.claude/skills/connect-agents/SKILL.md +0 -119
- package/skills/.claude/skills/control-plane/SKILL.md +0 -80
- package/skills/.claude/skills/eve-building/SKILL.md +0 -106
- package/skills/.claude/skills/fde-engagement/SKILL.md +0 -35
- package/skills/.claude/skills/fde-engagement/references/playbook.md +0 -2312
- package/skills/.claude/skills/kybernesis-packages/SKILL.md +0 -148
- package/skills/.claude/skills/self-hosting/SKILL.md +0 -206
- package/skills/.claude/skills/source-of-truth/SKILL.md +0 -60
|
@@ -1,2312 +0,0 @@
|
|
|
1
|
-
# Kybernesis Forward-Deployment Playbook
|
|
2
|
-
|
|
3
|
-
**Audience:** a Kybernesis forward-deployed engineer (FDE) running a client pilot
|
|
4
|
-
engagement. Assumes you are a competent engineer who has **never seen this stack**.
|
|
5
|
-
Everything you need is either in this document or behind a link in it.
|
|
6
|
-
|
|
7
|
-
**Canonical location:** this Obsidian vault. An older, shorter copy may still exist at
|
|
8
|
-
`~/Desktop/kybernesis-engagement-playbook.md` — that one is stale; this is the one.
|
|
9
|
-
|
|
10
|
-
**Companion docs**
|
|
11
|
-
- [[kybernesis-system-overview]] — what the whole system is and why. Read it once
|
|
12
|
-
before your first engagement; you do not need it again during one.
|
|
13
|
-
- [[kybernesis-architecture-and-studio-signin]] — the auth contract in full plus the
|
|
14
|
-
Eve Studio sign-in brief.
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
## ⚡ The fast path: `kyb` (read this first, then use the rest as reference)
|
|
19
|
-
|
|
20
|
-
Everything in §3–§4 of this playbook — scaffold, registry, all four packages,
|
|
21
|
-
department subagents, memory mounts, eval wiring — is automated by
|
|
22
|
-
**`@kybernesis/create`**, our CLI (`kyb`). The manual sections below remain the
|
|
23
|
-
canonical reference for *what the CLI writes and why*, and for fixing anything by
|
|
24
|
-
hand; but a normal engagement starts here.
|
|
25
|
-
|
|
26
|
-
### Starting from zero on the client's computer
|
|
27
|
-
|
|
28
|
-
You will often be on a machine that has nothing on it. The complete bootstrap, in
|
|
29
|
-
order — nothing else is required before the first command:
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
# 1. Node.js 24 LTS (the only true prerequisite; installer from nodejs.org, or:)
|
|
33
|
-
# macOS: brew install node@24 Windows: winget install OpenJS.NodeJS.LTS
|
|
34
|
-
node -v # must be >= 24
|
|
35
|
-
|
|
36
|
-
# 2. Scaffold the agent — no install step needed; npm fetches the CLI itself:
|
|
37
|
-
npm create @kybernesis acme-atlas
|
|
38
|
-
# …or, when the client wants an agent that BUILDS software (the engineer layer):
|
|
39
|
-
npm create @kybernesis acme-atlas -- --engineer
|
|
40
|
-
# Prompts: display name · department subagents · control-plane issuer.
|
|
41
|
-
# (Non-interactive/CI: defaults apply when stdin is not a terminal.)
|
|
42
|
-
|
|
43
|
-
# …or, when the client will NOT use Vercel — their own VM, or their own
|
|
44
|
-
# ChatGPT/LLM subscription paying for inference. See section 11:
|
|
45
|
-
npm create @kybernesis acme-atlas -- --host=exe --engineer
|
|
46
|
-
|
|
47
|
-
# …or, when the client wants the desktop app (KYBER Studio) — see section 12:
|
|
48
|
-
npm create @kybernesis acme-atlas -- --studio
|
|
49
|
-
|
|
50
|
-
# 3. (Optional, for repeated use) put `kyb` on the PATH for the whole engagement:
|
|
51
|
-
npm install -g @kybernesis/create
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
That's it: `npm create @kybernesis <name>` downloads and runs the scaffolder in one
|
|
55
|
-
step — the npm registry is the distribution channel, so a fresh laptop needs only
|
|
56
|
-
Node. The Vercel CLI installs itself the first time an `npx vercel …` command runs
|
|
57
|
-
(or `npm i -g vercel` if you prefer it resident).
|
|
58
|
-
|
|
59
|
-
### What `kyb init` leaves you with
|
|
60
|
-
|
|
61
|
-
A complete agent directory — governed (`enterprise`), remembering (`arcana`),
|
|
62
|
-
multiplayer Slack (`multiplayer`), self-testing (`evals`), with one generated
|
|
63
|
-
subagent per department you named (routing description, instructions, its own
|
|
64
|
-
Arcana connection, the three memory skills) — already typechecked and
|
|
65
|
-
discovery-clean. It ends by printing **the human-steps checklist**, which is
|
|
66
|
-
exactly §§2, 5, 6, and 7 of this playbook:
|
|
67
|
-
|
|
68
|
-
1. Arcana workspaces + scoped `kb_` keys → fill `.env.local` from `.env.example`
|
|
69
|
-
2. `vercel link` (the **client's** team) + envs (prod/preview Sensitive)
|
|
70
|
-
3. The Slack connector browser flow (§5.1)
|
|
71
|
-
4. Control-plane registration (▲ eve) + pilot-cohort grants (§6)
|
|
72
|
-
5. `npm run eval` → green → `npx eve deploy` → Slack smoke + the revoke demo (§7)
|
|
73
|
-
|
|
74
|
-
### The `kyb` command reference
|
|
75
|
-
|
|
76
|
-
| Command | When | What it does |
|
|
77
|
-
| --- | --- | --- |
|
|
78
|
-
| `npm create @kybernesis <name>` | Day 1, once | Same as `kyb init <name>` without installing anything first |
|
|
79
|
-
| `kyb init <name>` | Day 1, once | eve scaffold (pinned version) + registry + all four packages + generated departments + eval wiring + env template + checklist |
|
|
80
|
-
| `kyb init <name> --engineer` | When the pilot includes building software | Everything above PLUS the engineer layer: workshop sandbox (Playwright baked into the template), the vision screenshot tool, build/ship skills, and the official limbs (agent-browser, github-tools, vercel connection) |
|
|
81
|
-
| `kyb doctor` | **Constantly** — after every human step, before every deploy, whenever anything is weird | Live preflight: every Arcana key↔workspace pair validated against the API (with the specific fix per failure: wrong-key 403 vs missing-workspace 404), issuer JWKS reachability, `KYBERNESIS_AGENT`, Slack connector env, `eve info` discovery, port-2000 conflict. Exit 1 on failure → usable in CI |
|
|
82
|
-
| `kyb upgrade` | Maintenance visits / after Kybernesis ships a package update | Compares installed `@kybernesis/*` against npm **and eve against the Kybernesis-certified version** (never blindly npm-latest — we certify eve releases in the platform repo first), installs what's behind, typechecks (+ `eve info` after a framework bump), then **runs the eval suite as the gate** — tells you to deploy only on green |
|
|
83
|
-
| `kyb upgrade --skip-eval` | Never for production changes | Same, without the gate |
|
|
84
|
-
|
|
85
|
-
**Habit to build:** `kyb doctor` is the debugging you would otherwise do by hand
|
|
86
|
-
with curl — run it before asking why something doesn't work. Green doctor + green
|
|
87
|
-
`npm run eval` = safe to deploy.
|
|
88
|
-
|
|
89
|
-
---
|
|
90
|
-
|
|
91
|
-
## 0. What you are actually doing
|
|
92
|
-
|
|
93
|
-
You walk into a client company for roughly a week and leave behind:
|
|
94
|
-
|
|
95
|
-
1. **A company agent** — an [eve](https://eve.dev) agent, running on **the client's own
|
|
96
|
-
Vercel team**, answering in **the client's own Slack workspace**.
|
|
97
|
-
2. **Department subagents** — finance, engineering, marketing, support, whatever their
|
|
98
|
-
org chart says — each with its own long-term memory and its own tools.
|
|
99
|
-
3. **Governance** — the client's admin can invite an employee, grant them the agent,
|
|
100
|
-
and revoke it, from a web UI, with revocation taking effect inside the token TTL.
|
|
101
|
-
4. **An eval suite** — the QA deliverable. It is how you and the client both know the
|
|
102
|
-
agent still works after a change.
|
|
103
|
-
|
|
104
|
-
The commercial shape of this matters to how you build it. Our pitch is **"your source →
|
|
105
|
-
your runtime → your data → your token."** Everything above lives in *the client's*
|
|
106
|
-
accounts. Kybernesis operates exactly one thing on the client's behalf — the **control
|
|
107
|
-
plane** at `https://agent.kybernesis.ai` — and maintains the npm packages that make the
|
|
108
|
-
rest possible. Do not create client resources inside Kybernesis accounts. If you catch
|
|
109
|
-
yourself doing so, stop and fix it before it becomes the handover conversation.
|
|
110
|
-
|
|
111
|
-
### Placeholders used throughout
|
|
112
|
-
|
|
113
|
-
| Placeholder | Meaning | Example |
|
|
114
|
-
| --- | --- | --- |
|
|
115
|
-
| `ACME` | the client company / control-plane org | Northwind |
|
|
116
|
-
| `acme` | the client slug, lowercase | `northwind` |
|
|
117
|
-
| `atlas` | the agent's name — whatever the client wants | `nora` |
|
|
118
|
-
| `acme-atlas` | the repo / Vercel project name | `northwind-nora` |
|
|
119
|
-
| `<dept>` | a department subagent | `finance` |
|
|
120
|
-
| `slack/atlas` | the Vercel Connect connector UID | `slack/nora` |
|
|
121
|
-
|
|
122
|
-
The agent's name is client-chosen and appears in three places that **must agree**: the
|
|
123
|
-
`KYBERNESIS_AGENT` env var, the agent's registered name in the control plane, and the
|
|
124
|
-
Slack app's display name (the last one is cosmetic but confusing if it differs).
|
|
125
|
-
|
|
126
|
-
### Timeline for a standard pilot
|
|
127
|
-
|
|
128
|
-
| Day | Work |
|
|
129
|
-
| --- | --- |
|
|
130
|
-
| Day 0 (remote) | Phase 1 pre-engagement checklist |
|
|
131
|
-
| Day 1 morning | Phase 2 discovery conversation |
|
|
132
|
-
| Day 1 afternoon | Phase 3 environment setup, agent scaffolded and running locally |
|
|
133
|
-
| Day 2–3 | Phase 4 build: instructions, subagents, memory, evals |
|
|
134
|
-
| Day 3 | Phase 5 deploy + Phase 6 control-plane wiring |
|
|
135
|
-
| Day 4 | Phase 7 pilot onboarding — the humans |
|
|
136
|
-
| Day 5 | Phase 8 acceptance demo, Phase 9 handover |
|
|
137
|
-
|
|
138
|
-
Pull days 2–3 longer if they have real proprietary systems to integrate; that work is a
|
|
139
|
-
custom eve extension and is the billable heart of the engagement.
|
|
140
|
-
|
|
141
|
-
---
|
|
142
|
-
|
|
143
|
-
## 1. Pre-engagement checklist (do this remotely, ~1 hour, before you travel)
|
|
144
|
-
|
|
145
|
-
Nothing here needs the client in the room, but several items need them to have clicked
|
|
146
|
-
something. Chase them a week out — an unprovisioned Vercel team on Day 1 costs you a day.
|
|
147
|
-
|
|
148
|
-
- [ ] **Client has a Vercel team.** Any plan tier works for a pilot; Pro if they want
|
|
149
|
-
preview protection off the shelf. You need a member seat on it, or a named client
|
|
150
|
-
engineer who can run `vercel` commands next to you.
|
|
151
|
-
- [ ] **You have `node 24.x`, `npm`, and the Vercel CLI** on the machine you will build
|
|
152
|
-
on. Verify:
|
|
153
|
-
```bash
|
|
154
|
-
node -v # expect v24.x
|
|
155
|
-
npm -v
|
|
156
|
-
npm install -g vercel@latest
|
|
157
|
-
vercel whoami
|
|
158
|
-
```
|
|
159
|
-
- [ ] **You can log in to the control plane** at <https://agent.kybernesis.ai> and you
|
|
160
|
-
have permission to create an org there. If ACME is self-hosting the control plane
|
|
161
|
-
instead, confirm their issuer URL now — it changes `KYBERNESIS_ISSUER` everywhere.
|
|
162
|
-
- [ ] **Create the control-plane org for ACME** and invite yourself as an admin of it.
|
|
163
|
-
Do not build inside the Kybernesis org.
|
|
164
|
-
- [ ] **Provision Arcana workspaces** — one per brain. The naming convention is
|
|
165
|
-
`acme-company` for the shared root brain, `acme-<dept>` per subagent, and
|
|
166
|
-
`acme-eval` for hermetic eval runs. Create each at
|
|
167
|
-
<https://arcana.kybernesis.ai> and mint a **workspace-scoped `kb_` key** for each.
|
|
168
|
-
- [ ] **Validate every key before you leave.** Keys are workspace-scoped: a key for one
|
|
169
|
-
workspace returns `403` on any other, and finding that out during a live demo is
|
|
170
|
-
avoidable. Read-only check, one per workspace:
|
|
171
|
-
```bash
|
|
172
|
-
curl -s -o /dev/null -w "HTTP %{http_code}\n" \
|
|
173
|
-
-H "Authorization: Bearer kb_REPLACE_ME" \
|
|
174
|
-
-H "X-Kyberagent-Agent: acme-company" \
|
|
175
|
-
"https://api.arcana.kybernesis.ai/brain/acme-company/timeline?limit=1"
|
|
176
|
-
# expect HTTP 200
|
|
177
|
-
```
|
|
178
|
-
- [ ] **Confirm who at ACME is the Slack workspace admin.** Creating the Slack connector
|
|
179
|
-
requires someone who can approve a Slack app install. If that person is on holiday
|
|
180
|
-
your Day 2 is Slack-less.
|
|
181
|
-
- [ ] **Read the reference implementation.** `~/kyber` is our own production agent and
|
|
182
|
-
the canonical example of everything in Phase 4. Skim `agent/agent.ts`,
|
|
183
|
-
`agent/instructions/`, `agent/subagents/finance/`, and `evals/`.
|
|
184
|
-
- [ ] **Confirm package versions you will pin.** As of 2026-08-06:
|
|
185
|
-
`@kybernesis/arcana@0.1.1`, `@kybernesis/enterprise@0.1.2`,
|
|
186
|
-
`@kybernesis/multiplayer@0.1.0`, `@kybernesis/evals@0.2.1`,
|
|
187
|
-
`@kybernesis/create@0.1.4`, `@kybernesis/engineer@0.2.0`, and
|
|
188
|
-
`eve@0.30.8` (the Kybernesis-certified version). All public on npm;
|
|
189
|
-
`kyb doctor` checks the wiring.
|
|
190
|
-
|
|
191
|
-
---
|
|
192
|
-
|
|
193
|
-
## 2. The discovery conversation (Day 1 morning, 90 minutes, with the client)
|
|
194
|
-
|
|
195
|
-
This is a working session, not a requirements-gathering ritual. Your goal is to leave the
|
|
196
|
-
room able to run Phase 3 without asking anyone anything. Bring a laptop and fill in the
|
|
197
|
-
table below live.
|
|
198
|
-
|
|
199
|
-
### 2.1 The agent itself
|
|
200
|
-
|
|
201
|
-
- **"What is it called?"** They pick. It appears in Slack, so it should be something
|
|
202
|
-
people will actually type. Write it down as `atlas`.
|
|
203
|
-
- **"What is its voice?"** Terse and factual, or warm? This becomes
|
|
204
|
-
`agent/instructions/identity.md`. Ask for two or three examples of a good answer and a
|
|
205
|
-
bad answer — those examples become evals.
|
|
206
|
-
- **"What should it refuse to do?"** Anything they name here becomes an instruction and,
|
|
207
|
-
if it matters, a hard guard in code rather than a prompt.
|
|
208
|
-
|
|
209
|
-
### 2.2 Departments
|
|
210
|
-
|
|
211
|
-
- **"Which departments would ask this thing questions?"** Aim for three to five
|
|
212
|
-
subagents in a pilot. More is a scoping conversation, not a build.
|
|
213
|
-
- For each: **what does it know that nobody else does**, and **what systems does it need
|
|
214
|
-
to read?** The first answer sizes its memory workspace; the second is either an
|
|
215
|
-
off-the-shelf eve connection or a custom extension you will write.
|
|
216
|
-
- Watch for a department that is really a *separate agent* (in our own setup, GTM was —
|
|
217
|
-
it already had its own agent). Splitting is cheaper than untangling later.
|
|
218
|
-
|
|
219
|
-
### 2.3 Surfaces — don't assume Slack
|
|
220
|
-
|
|
221
|
-
- **"Where do you actually talk?"** eve ships channels for Slack, iMessage
|
|
222
|
-
(Photon), Telegram, Discord, Teams, SMS/phone (Twilio), GitHub, Linear, and
|
|
223
|
-
a web chat — the agent can live on several at once (§4.3c has the table and
|
|
224
|
-
install commands). Slack gets the richest treatment (our multiplayer group
|
|
225
|
-
semantics); the rest are 1:1 surfaces today. Pick with the client, then ask
|
|
226
|
-
the Slack questions below only if Slack made the list.
|
|
227
|
-
|
|
228
|
-
### 2.3a Slack specifics
|
|
229
|
-
|
|
230
|
-
- **Workspace name and admin contact.**
|
|
231
|
-
- **Which channels does the agent join?** For a pilot, one shared channel is usually
|
|
232
|
-
right. Ask explicitly whether they want it in a channel with sensitive content.
|
|
233
|
-
- **Do they want thread-following?** (People keep talking to the agent in a thread
|
|
234
|
-
without re-mentioning it.) This is the default in our multiplayer package and it is
|
|
235
|
-
what makes the agent feel like a colleague, but it needs extra Slack scopes —
|
|
236
|
-
`message.channels` + `channels:history`, and the `groups` pair for private channels.
|
|
237
|
-
Get scope approval in the same conversation as the app install.
|
|
238
|
-
- **Do they want DMs?** DMs are a per-person surface with a separate memory workspace.
|
|
239
|
-
Almost everyone says yes.
|
|
240
|
-
|
|
241
|
-
### 2.4 The pilot cohort
|
|
242
|
-
|
|
243
|
-
- **"Name the five to ten people who will use this in week one."** Get names, emails, and
|
|
244
|
-
Slack handles into a table. You will invite exactly these people in Phase 7.
|
|
245
|
-
- **Who is the client-side admin?** They get the `manage` grant level and they are the
|
|
246
|
-
person you train on invite/grant/revoke. Ideally two people, so a holiday does not
|
|
247
|
-
block off-boarding.
|
|
248
|
-
- **Who is the internal champion** who will answer "what do I even ask it?" for the
|
|
249
|
-
cohort after you leave?
|
|
250
|
-
|
|
251
|
-
### 2.5 Data sensitivities — ask these out loud, take notes, put the answers in the doc
|
|
252
|
-
|
|
253
|
-
- **What must never enter the agent's memory?** Credentials, obviously. But also: salary
|
|
254
|
-
data, customer PII, unannounced M&A, health information. The memory instructions carry
|
|
255
|
-
a no-secrets rule, but a rule in a prompt is not a control — if something must not be
|
|
256
|
-
stored, do not connect the system it lives in.
|
|
257
|
-
- **Which department brains must not read each other?** Each subagent gets its own
|
|
258
|
-
Arcana workspace with its own scoped key, so this is free — but you need to know the
|
|
259
|
-
boundaries to name the workspaces correctly.
|
|
260
|
-
- **Is the shared channel brain readable by everyone in the workspace?** Today, yes:
|
|
261
|
-
anyone in the Slack workspace who can see the bot can talk to it, and public-channel
|
|
262
|
-
memory is shared. Say this plainly. If they need per-person gating on the Slack door,
|
|
263
|
-
that is a known gap (§11) and you should scope the pilot around it.
|
|
264
|
-
- **Where does their data physically live and does that matter?** Agent runtime and
|
|
265
|
-
session data: their Vercel account. Long-term memory: Arcana, our SaaS. Identity and
|
|
266
|
-
grants: the control plane. Model traffic: the AI Gateway provider their eve project is
|
|
267
|
-
configured for. If any of those three placements is a problem, surface it now — the
|
|
268
|
-
control plane is self-hostable and that is a different (and larger) engagement.
|
|
269
|
-
|
|
270
|
-
### 2.6 Leave the room with this table filled in
|
|
271
|
-
|
|
272
|
-
| Field | Value |
|
|
273
|
-
| --- | --- |
|
|
274
|
-
| Agent name (`atlas`) | |
|
|
275
|
-
| Repo / Vercel project name | |
|
|
276
|
-
| Vercel team | |
|
|
277
|
-
| Slack workspace + admin | |
|
|
278
|
-
| Slack channels | |
|
|
279
|
-
| Thread-following? DMs? | |
|
|
280
|
-
| Departments (`<dept>` list) | |
|
|
281
|
-
| Arcana workspaces + keys | |
|
|
282
|
-
| Control-plane org | |
|
|
283
|
-
| Pilot cohort (name / email / Slack id) | |
|
|
284
|
-
| Client-side admins (`manage` grant) | |
|
|
285
|
-
| Never-store list | |
|
|
286
|
-
| Custom systems to integrate | |
|
|
287
|
-
|
|
288
|
-
---
|
|
289
|
-
|
|
290
|
-
## 3. Environment setup (Day 1 afternoon, ~45 minutes)
|
|
291
|
-
|
|
292
|
-
> ⚡ **Automated by `kyb init`** (see the fast path at the top). Read this section to
|
|
293
|
-
> understand what the CLI wrote, or to do it by hand.
|
|
294
|
-
|
|
295
|
-
Everything below runs from a working directory of your choosing. Use an absolute path
|
|
296
|
-
you will remember; this playbook writes `~/work/acme-atlas`.
|
|
297
|
-
|
|
298
|
-
### 3.1 Scaffold the agent
|
|
299
|
-
|
|
300
|
-
```bash
|
|
301
|
-
mkdir -p ~/work && cd ~/work
|
|
302
|
-
npx eve@latest init acme-atlas
|
|
303
|
-
cd ~/work/acme-atlas
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
`eve init` creates the project, installs dependencies, and initializes git. You now have
|
|
307
|
-
an `agent/` directory. Confirm eve sees it:
|
|
308
|
-
|
|
309
|
-
```bash
|
|
310
|
-
npx eve info
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
`eve info` prints the resolved application — every tool, skill, subagent, schedule,
|
|
314
|
-
channel, and route eve discovered, plus discovery diagnostics. **Run this whenever
|
|
315
|
-
something behaves unexpectedly.** It is much faster than booting the dev server and it
|
|
316
|
-
answers the single most common question ("did eve even find my file?").
|
|
317
|
-
|
|
318
|
-
### 3.2 Link it to the client's Vercel team
|
|
319
|
-
|
|
320
|
-
```bash
|
|
321
|
-
cd ~/work/acme-atlas
|
|
322
|
-
npx eve link
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
Pick **the client's team**, then create a project named `acme-atlas`. This also pulls an
|
|
326
|
-
AI Gateway credential (`VERCEL_OIDC_TOKEN` or `AI_GATEWAY_API_KEY`) into `.env.local`, so
|
|
327
|
-
the agent can call a model.
|
|
328
|
-
|
|
329
|
-
In CI or any non-interactive context, `eve link` will not work — use
|
|
330
|
-
`vercel link --project acme-atlas --yes --non-interactive` instead. If your active Vercel
|
|
331
|
-
scope is not the client's team, every subsequent `vercel` call needs
|
|
332
|
-
`--scope <client-team-slug>`.
|
|
333
|
-
|
|
334
|
-
### 3.3 Register the Kybernesis registry
|
|
335
|
-
|
|
336
|
-
```bash
|
|
337
|
-
cd ~/work/acme-atlas
|
|
338
|
-
npx eve registry add @kybernesis=https://registry.kybernesis.ai/r/{name}.json
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
Type `{name}` literally — it is a placeholder eve substitutes per item. This writes the
|
|
342
|
-
mapping into `package.json#registries`. Confirm:
|
|
343
|
-
|
|
344
|
-
```bash
|
|
345
|
-
npx eve registry list --registry @kybernesis
|
|
346
|
-
npx eve registry view @kybernesis/arcana
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
### 3.4 Pin your versions
|
|
350
|
-
|
|
351
|
-
Before you install anything else, decide and record the versions this engagement pins.
|
|
352
|
-
Put them in the repo README. Pin `eve@0.30.8` — the **Kybernesis-certified** version
|
|
353
|
-
(certification run 2026-08-06: full suite green, zero code changes). Never pin blind
|
|
354
|
-
npm-latest; `kyb upgrade` carries a client to the certified pin behind their own eval
|
|
355
|
-
gate, and that upgrade is a **deliberate, eval-gated step**, never something that
|
|
356
|
-
happens by accident mid-pilot.
|
|
357
|
-
|
|
358
|
-
---
|
|
359
|
-
|
|
360
|
-
## 4. Build (Days 2–3)
|
|
361
|
-
|
|
362
|
-
> ⚡ **The package installs and department scaffolds here are automated by `kyb init`.**
|
|
363
|
-
> What remains genuinely manual in this section: tailoring instructions to the client's
|
|
364
|
-
> voice, client-specific tools/connections, and any custom extension work.
|
|
365
|
-
|
|
366
|
-
Build in this order — model, channel, memory, instructions, subagents, schedules,
|
|
367
|
-
evals. Each step is testable on its own, and the order avoids the one real trap
|
|
368
|
-
(installing the governance package *after* hand-authoring the file it overwrites).
|
|
369
|
-
|
|
370
|
-
### 4.0 Do the whole phase WITH Claude Code — this is the method, not a tip
|
|
371
|
-
|
|
372
|
-
Open Claude Code in the agent repo and keep it open for all of Phase 4. The
|
|
373
|
-
scaffold already carries the **FDE skill suite** in `.claude/skills/` (seeded
|
|
374
|
-
by `kyb init`; refresh with `kyb skills`) — Claude loads the engagement flow,
|
|
375
|
-
eve recipes, package gotchas, control-plane wiring, and eval discipline on
|
|
376
|
-
demand, so it knows what Kybernesis is doing before you say a word. The
|
|
377
|
-
scaffold's `AGENTS.md` additionally points it at the installed eve docs
|
|
378
|
-
(`node_modules/eve/docs/` — the source of truth for the pinned version), so it
|
|
379
|
-
authors against the real framework instead of guessing. The working rhythm:
|
|
380
|
-
|
|
381
|
-
1. **Tell it what you're building, paste the discovery table (§2.6).** "This
|
|
382
|
-
agent is called Atlas, lives on Slack + Telegram, needs read access to
|
|
383
|
-
their Postgres and their internal wiki, three departments."
|
|
384
|
-
2. **Make it read before it writes.** For anything eve-specific: "read
|
|
385
|
-
`node_modules/eve/docs/channels/telegram.mdx`, then wire the channel."
|
|
386
|
-
Every channel, connection type, and config surface has a doc page; the
|
|
387
|
-
pattern `read the doc → write the file → npx eve info → test in eve dev`
|
|
388
|
-
is the whole game.
|
|
389
|
-
3. **You review diffs and run the credential steps** (anything with a browser
|
|
390
|
-
login or a client secret is yours); Claude writes files, runs `eve
|
|
391
|
-
registry` searches, and iterates on eval failures.
|
|
392
|
-
4. **Never accept a claim without the check**: `npm run typecheck`, `npx eve
|
|
393
|
-
info` (0 diagnostics), a turn in `eve dev` (§4.4b), evals green (§4.8).
|
|
394
|
-
|
|
395
|
-
The eve CLI you'll both be living in:
|
|
396
|
-
|
|
397
|
-
| Command | What it does |
|
|
398
|
-
| --- | --- |
|
|
399
|
-
| `npx eve dev` | boots the local runtime + opens the chat TUI (test turns here) |
|
|
400
|
-
| `npx eve info` | compile + discovery truth: agents, tools, skills, diagnostics |
|
|
401
|
-
| `npx eve registry list` / `search <term>` / `view <item>` | discover integrations before writing one |
|
|
402
|
-
| `npx eve add <item>` | install a registry item (files + deps; may offer an interactive setup flow — rerun later with `--skip-install`) |
|
|
403
|
-
| `npm run eval` | the hermetic suite (§4.8) — kill the dev server first |
|
|
404
|
-
| `npx eve build` | production build locally (what the Vercel deploy runs) |
|
|
405
|
-
| `vercel deploy --prod --yes` | ship it (§5) |
|
|
406
|
-
|
|
407
|
-
### 4.0b Pick and pin the model
|
|
408
|
-
|
|
409
|
-
The model is agent config, not an env var: `agent/agent.ts` calls
|
|
410
|
-
`defineAgent`. With **no** `agent.ts`, eve defaults to
|
|
411
|
-
`anthropic/claude-sonnet-5`; the moment the file exists, `model` is required —
|
|
412
|
-
so pin it deliberately and record it in the repo README with the §3.4 pins:
|
|
413
|
-
|
|
414
|
-
```ts
|
|
415
|
-
// agent/agent.ts
|
|
416
|
-
import { defineAgent } from "eve";
|
|
417
|
-
|
|
418
|
-
export default defineAgent({
|
|
419
|
-
model: "anthropic/claude-opus-4.8",
|
|
420
|
-
});
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
What to know when choosing:
|
|
424
|
-
|
|
425
|
-
- The string form is a **Vercel AI Gateway id** (`provider/model` with a dot
|
|
426
|
-
version — `anthropic/claude-opus-4.8`) — routed, no provider key handling.
|
|
427
|
-
This is the default choice for client deploys.
|
|
428
|
-
- Direct provider wiring exists when a client requires it (their own
|
|
429
|
-
Anthropic account, data-processing terms): `npm install @ai-sdk/anthropic`,
|
|
430
|
-
then `model: anthropic("claude-opus-4-8")` (note the provider's hyphenated
|
|
431
|
-
id format) with the provider API key in env.
|
|
432
|
-
- **Dynamic selection** is supported (`defineDynamic` with a `fallback` and a
|
|
433
|
-
`session.started` handler — e.g. a bigger model for an enterprise-plan
|
|
434
|
-
principal). Prefer `session.started` over per-turn switching: prompt caches
|
|
435
|
-
are per model, and every switch re-ingests the conversation at uncached
|
|
436
|
-
prices. Resolver failures degrade to the fallback, never fail the turn.
|
|
437
|
-
- Kyber runs `anthropic/claude-opus-4.8` via the gateway; our eval judges are
|
|
438
|
-
configured separately in `evals/evals.config.ts` and must **never** be the
|
|
439
|
-
model under test.
|
|
440
|
-
|
|
441
|
-
### 4.1 Install `@kybernesis/enterprise` (governance) — do this FIRST
|
|
442
|
-
|
|
443
|
-
```bash
|
|
444
|
-
cd ~/work/acme-atlas
|
|
445
|
-
npx eve add @kybernesis/enterprise
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
This installs the npm package and **writes `agent/channels/eve.ts`, replacing the
|
|
449
|
-
scaffold's version.** That is why it goes first: if you hand-edit `agent/channels/eve.ts`
|
|
450
|
-
and then run this, your edits are gone.
|
|
451
|
-
|
|
452
|
-
The file it writes:
|
|
453
|
-
|
|
454
|
-
```ts
|
|
455
|
-
// agent/channels/eve.ts
|
|
456
|
-
import { eveChannel } from "eve/channels/eve";
|
|
457
|
-
import { kybernesisAuth } from "@kybernesis/enterprise";
|
|
458
|
-
|
|
459
|
-
export default eveChannel({
|
|
460
|
-
auth: [
|
|
461
|
-
kybernesisAuth({
|
|
462
|
-
issuer: process.env.KYBERNESIS_ISSUER!,
|
|
463
|
-
agent: process.env.KYBERNESIS_AGENT!,
|
|
464
|
-
}),
|
|
465
|
-
],
|
|
466
|
-
});
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
What this does: every request to the agent's HTTP surface (`/eve/v1/*`) must carry a
|
|
470
|
-
Kybernesis identity token **and** a policy bundle. The package verifies both **offline**
|
|
471
|
-
against the control plane's JWKS, cross-checks that the bundle belongs to the token's
|
|
472
|
-
user and org, and then requires the bundle's `agentGrants` to contain *this agent's*
|
|
473
|
-
registered name.
|
|
474
|
-
|
|
475
|
-
- no credentials, or invalid, or expired → **401**
|
|
476
|
-
- valid credentials but no grant for this agent → **403 `agent_not_granted`**
|
|
477
|
-
|
|
478
|
-
Note what is *not* in that auth array: the eve scaffold ships `localDev()` and
|
|
479
|
-
`placeholderAuth()`. The governed file drops both. That is intentional — it fails closed
|
|
480
|
-
— but it means the `eve dev` TUI cannot reach the HTTP door of a governed agent without
|
|
481
|
-
credentials. You will still drive the agent locally through the TUI's own session; you
|
|
482
|
-
just cannot `curl` it without a real token.
|
|
483
|
-
|
|
484
|
-
### 4.2 Install `@kybernesis/arcana` (memory)
|
|
485
|
-
|
|
486
|
-
```bash
|
|
487
|
-
cd ~/work/acme-atlas
|
|
488
|
-
npx eve add @kybernesis/arcana
|
|
489
|
-
```
|
|
490
|
-
|
|
491
|
-
This installs the package and writes `agent/extensions/arcana.ts`. **The filename is the
|
|
492
|
-
mount namespace** — leave it as `arcana.ts` unless you have a reason not to, because
|
|
493
|
-
tool names derive from it.
|
|
494
|
-
|
|
495
|
-
Edit it to use the client's workspace naming and, if they want the DM/channel memory
|
|
496
|
-
split, a `resolveWorkspace` override:
|
|
497
|
-
|
|
498
|
-
```ts
|
|
499
|
-
// agent/extensions/arcana.ts
|
|
500
|
-
import arcana from "@kybernesis/arcana";
|
|
501
|
-
|
|
502
|
-
export default arcana({
|
|
503
|
-
apiKey: process.env.ARCANA_API_KEY!,
|
|
504
|
-
workspace: process.env.ARCANA_COMPANY_WORKSPACE ?? "acme-company",
|
|
505
|
-
|
|
506
|
-
// Public channels use the shared company brain; DMs use a personal workspace.
|
|
507
|
-
// `surface` is a VERIFIED principal attribute stamped by the multiplayer Slack
|
|
508
|
-
// channel — never anything the model can influence.
|
|
509
|
-
resolveWorkspace: (ctx) =>
|
|
510
|
-
ctx.session.auth.current?.attributes.surface === "dm"
|
|
511
|
-
? (process.env.ARCANA_DM_WORKSPACE ?? "acme-dm")
|
|
512
|
-
: undefined,
|
|
513
|
-
});
|
|
514
|
-
```
|
|
515
|
-
|
|
516
|
-
What you get from the mount: an MCP connection to `https://mcp.arcana.kybernesis.ai/mcp`
|
|
517
|
-
(with the required `X-Kyberagent-Agent: <workspace>` header), three skills
|
|
518
|
-
(`recall`, `remember`, `brain-note`), and always-on memory instructions — recall-first
|
|
519
|
-
lookups, never claim ignorance without searching, proactive fact storage, no secrets in
|
|
520
|
-
memory. Those instruction rules are not decoration; each of them exists because an eval
|
|
521
|
-
caught the agent doing the opposite.
|
|
522
|
-
|
|
523
|
-
### 4.3 Install `@kybernesis/multiplayer` (Slack)
|
|
524
|
-
|
|
525
|
-
```bash
|
|
526
|
-
cd ~/work/acme-atlas
|
|
527
|
-
npx eve add @kybernesis/multiplayer
|
|
528
|
-
```
|
|
529
|
-
|
|
530
|
-
This writes `agent/channels/slack.ts` and `agent/instructions/multiplayer.md`, and
|
|
531
|
-
declares the `SLACK_CONNECTOR_UID` env var.
|
|
532
|
-
|
|
533
|
-
> **Status as of 2026-08-05:** the registry item is **live** (`eve add` resolves and
|
|
534
|
-
> writes both files), and the package is dogfooded in `~/kyber` — but
|
|
535
|
-
> `@kybernesis/multiplayer@0.1.0` is **not yet published to npm**, so the dependency
|
|
536
|
-
> install step will fail until it is. Check first:
|
|
537
|
-
> ```bash
|
|
538
|
-
> npm view @kybernesis/multiplayer version # E404 means the publish hasn't landed
|
|
539
|
-
> ```
|
|
540
|
-
> If it 404s, install from the workspace checkout at `~/kyber/packages/multiplayer` (or
|
|
541
|
-
> vendor the two files by hand — they are shown below and in
|
|
542
|
-
> `agent/instructions/multiplayer.md`) and revisit once the publish lands.
|
|
543
|
-
|
|
544
|
-
The whole Slack integration is one file:
|
|
545
|
-
|
|
546
|
-
```ts
|
|
547
|
-
// agent/channels/slack.ts
|
|
548
|
-
import { connectSlackCredentials } from "@vercel/connect/eve";
|
|
549
|
-
import { multiplayerSlackChannel } from "@kybernesis/multiplayer/slack";
|
|
550
|
-
|
|
551
|
-
export default multiplayerSlackChannel({
|
|
552
|
-
credentials: connectSlackCredentials(process.env.SLACK_CONNECTOR_UID!),
|
|
553
|
-
});
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
Defaults give you the full multiplayer behavior. What that means concretely:
|
|
557
|
-
|
|
558
|
-
- **A thread is one shared session with many verified speakers.** Every message
|
|
559
|
-
re-authenticates: `auth.current` is *that message's* verified sender; `auth.initiator`
|
|
560
|
-
stays pinned to whoever started the thread.
|
|
561
|
-
- **Attributed thread context.** Messages between agent replies are injected with stable
|
|
562
|
-
per-speaker Slack ids, so the model reads a real multi-party transcript.
|
|
563
|
-
- **No re-mentions needed.** Once the agent is active in a thread, anyone can keep
|
|
564
|
-
talking to it.
|
|
565
|
-
- **Dual surface.** Channel sessions carry a verified `surface: "channel"` principal
|
|
566
|
-
attribute; DMs carry `surface: "dm"`. Gate tools on it.
|
|
567
|
-
- **`/new` in a DM** retires the session and starts fresh.
|
|
568
|
-
|
|
569
|
-
Options, if the client asked for something different:
|
|
570
|
-
|
|
571
|
-
| Option | Default | Change it when |
|
|
572
|
-
| --- | --- | --- |
|
|
573
|
-
| `continuation` | `"subscribed-threads"` | Set `"mention-only"` if they did not approve the history scopes, or find thread-following too chatty |
|
|
574
|
-
| `dmReset` | `"/new"` | They want a different command, or `false` to disable |
|
|
575
|
-
| `threadContext` | `"incremental"` | `"full"` for whole-thread-every-mention; `false` for triggering message only (no history scope needed) |
|
|
576
|
-
| `events` | — | Passed through to the underlying eve Slack channel for custom handlers |
|
|
577
|
-
|
|
578
|
-
Use the surface helpers to gate anything personal. This is the pattern for every
|
|
579
|
-
"only in a DM" capability:
|
|
580
|
-
|
|
581
|
-
```ts
|
|
582
|
-
import { defineTool } from "eve/tools";
|
|
583
|
-
import { requireDm } from "@kybernesis/multiplayer";
|
|
584
|
-
import { z } from "zod";
|
|
585
|
-
|
|
586
|
-
export default defineTool({
|
|
587
|
-
description: "Read the caller's personal task list.",
|
|
588
|
-
inputSchema: z.object({}),
|
|
589
|
-
async execute(_input, ctx) {
|
|
590
|
-
requireDm(ctx.session); // throws a model-visible refusal on the channel surface
|
|
591
|
-
// ...
|
|
592
|
-
},
|
|
593
|
-
});
|
|
594
|
-
```
|
|
595
|
-
|
|
596
|
-
`requireDm` **fails closed** and the thrown message is visible to the model, so the agent
|
|
597
|
-
relays "DM me for that" naturally. `sessionSurface(ctx.session)` returns
|
|
598
|
-
`"channel" | "dm" | null` if you want to branch rather than refuse; the `eve dev` local
|
|
599
|
-
principal counts as `"dm"` so you can exercise personal capabilities locally.
|
|
600
|
-
|
|
601
|
-
**Enforce surface rules in code, never in the prompt.** A prompt is a suggestion; a
|
|
602
|
-
throwing guard is a control.
|
|
603
|
-
|
|
604
|
-
### 4.3b Install `@kybernesis/engineer` (optional — when the agent should BUILD software)
|
|
605
|
-
|
|
606
|
-
```bash
|
|
607
|
-
cd ~/work/acme-atlas
|
|
608
|
-
npx eve add @kybernesis/engineer
|
|
609
|
-
```
|
|
610
|
-
|
|
611
|
-
Writes TWO files: `agent/extensions/engineer.ts` (the mount: screenshot tool +
|
|
612
|
-
six build/ship skills + engineering-conduct instructions) and
|
|
613
|
-
`agent/sandbox/sandbox.ts` (the **workshop**: Playwright + Chromium baked into
|
|
614
|
-
the sandbox template, domain allowlist on deployed sessions). Pair it with the
|
|
615
|
-
official limbs if not using `kyb init --engineer`:
|
|
616
|
-
`npx eve add extension/agent-browser extension/github-tools connection/vercel`
|
|
617
|
-
(run individually).
|
|
618
|
-
|
|
619
|
-
**Wire the Vercel connection** (preview deploys + a clickable link for every
|
|
620
|
-
build — proven live 2026-08-06). After `vercel link` in the agent dir:
|
|
621
|
-
|
|
622
|
-
```bash
|
|
623
|
-
vercel connect create mcp.vercel.com --name vercel
|
|
624
|
-
vercel connect attach mcp.vercel.com/vercel --yes
|
|
625
|
-
```
|
|
626
|
-
|
|
627
|
-
Then make `agent/connections/vercel.ts` use the **UID, not the short name**:
|
|
628
|
-
`connect("mcp.vercel.com/vercel")`. The auth is user-scoped: the FIRST Vercel
|
|
629
|
-
tool call posts a "Connect with…" OAuth link in the Slack thread, the turn
|
|
630
|
-
parks, and it resumes after the click. Grant **All projects** (the agent
|
|
631
|
-
creates new projects on deploy; a fixed project list can't cover them). To
|
|
632
|
-
**narrow the grant later** there is no dashboard or CLI grant editor, and
|
|
633
|
-
`revoke-tokens` does not kill provider-side tokens — the working recipe is
|
|
634
|
-
`vercel connect detach` + `remove` + `create` (same UID) + `attach`: the
|
|
635
|
-
authorization dies with the connector. Then trigger the re-auth from a
|
|
636
|
-
**fresh session/thread** (existing sessions hold stale auth state and error
|
|
637
|
-
without re-prompting) and select only the project(s) that now exist.
|
|
638
|
-
|
|
639
|
-
> **Scoping — the client story:** the natural boundary is the CLIENT'S
|
|
640
|
-
> VERCEL TEAM. The agent, connector, and OAuth grant all live in the
|
|
641
|
-
> client's team, so "All projects" means all of *that client's* projects —
|
|
642
|
-
> usually exactly right, and it also covers the new projects the agent
|
|
643
|
-
> creates on deploy. What we verified live: an All-projects grant really
|
|
644
|
-
> does reach every team project (the agent will happily enumerate them),
|
|
645
|
-
> and there is no post-hoc grant editor — changing scope means the
|
|
646
|
-
> connector reset above. If a client wants a boundary *tighter than their
|
|
647
|
-
> team* (e.g. agent may touch only its own projects), do not promise the
|
|
648
|
-
> consent picker — enforce it on our side with a policy wrapper on the
|
|
649
|
-
> connection (allowlist of project names/IDs checked against tool
|
|
650
|
-
> arguments). That wrapper is the auditable answer in a security review. The deploy tool
|
|
651
|
-
takes an **inline file tree**, so the agent ships straight from its sandbox —
|
|
652
|
-
no git remote and no token ever inside the VM. Previews sit behind Vercel
|
|
653
|
-
Authentication by default; the agent posts a `?_vercel_share=` bypass link
|
|
654
|
-
(~24 h) and must ASK before changing protection settings.
|
|
655
|
-
|
|
656
|
-
**Wire file delivery** (the `deliver` tool — documents, exports, artifacts
|
|
657
|
-
the client can open in a browser or download):
|
|
658
|
-
|
|
659
|
-
```bash
|
|
660
|
-
vercel blob create-store acme-atlas-deliverables --access public --yes
|
|
661
|
-
```
|
|
662
|
-
|
|
663
|
-
One command: creates the store, links the project, injects
|
|
664
|
-
`BLOB_READ_WRITE_TOKEN`. Without it the tool fails with instructions rather
|
|
665
|
-
than silently degrading to a memory note.
|
|
666
|
-
|
|
667
|
-
Know before demoing:
|
|
668
|
-
|
|
669
|
-
- **The sandbox template bakes at DEPLOY time** (Playwright + Chromium) — a
|
|
670
|
-
broken bootstrap fails the Vercel build loudly instead of surfacing mid-demo.
|
|
671
|
-
Deploys that rebuild the template take minutes; unchanged templates are
|
|
672
|
-
cached and fast.
|
|
673
|
-
- **The allowlist is the client's security posture** — deployed sessions can
|
|
674
|
-
only reach the domains listed in `agent/sandbox/sandbox.ts`. A blocked host
|
|
675
|
-
fails loudly; extend the list deliberately, and treat every addition as a
|
|
676
|
-
security decision to note in the handover. The template ships the proven v5
|
|
677
|
-
egress set: Ubuntu mirrors (the base image is Ubuntu), https-rewritten apt,
|
|
678
|
-
and `storage.googleapis.com` (Chromium's CDN).
|
|
679
|
-
- **Production promotion is human-approved by design** (the ship skill).
|
|
680
|
-
Never soften this for demo convenience — the approval moment IS the demo.
|
|
681
|
-
- The agent's projects live in `/workspace` and persist across sessions and
|
|
682
|
-
redeploys — a build started Tuesday continues Thursday.
|
|
683
|
-
- **Coach the agent in prose, not shell.** Slack messages containing raw
|
|
684
|
-
shell syntax can be eaten by Cloudflare's WAF before they reach the agent.
|
|
685
|
-
|
|
686
|
-
### 4.3c Channels — put the agent on every surface the client uses
|
|
687
|
-
|
|
688
|
-
Everything above wires *our* layer. This step and the two after it are where
|
|
689
|
-
the agent becomes **the client's agent**. All of it assumes zero prior eve
|
|
690
|
-
knowledge; every claim here is expandable by reading the named doc page in
|
|
691
|
-
`node_modules/eve/docs/` — which is exactly what you tell Claude Code to do.
|
|
692
|
-
|
|
693
|
-
A channel is one file under `agent/channels/`; the filename is the channel id.
|
|
694
|
-
eve normalizes every surface into the same runtime — instructions, tools, and
|
|
695
|
-
memory don't change per channel, so adding a second surface never means
|
|
696
|
-
re-teaching the agent. What ships:
|
|
697
|
-
|
|
698
|
-
| The client wants… | Channel | Get it |
|
|
699
|
-
| --- | --- | --- |
|
|
700
|
-
| Slack (mentions, DMs, threads, buttons) | Slack | our `@kybernesis/multiplayer` (§4.3) — group semantics, dual surface |
|
|
701
|
-
| **iMessage** | Photon | `eve add channel/photon-imessage` |
|
|
702
|
-
| Telegram bot | Telegram | `eve add channel/telegram` (worked example below) |
|
|
703
|
-
| Discord (slash commands, components) | Discord | `eve add channel/discord` |
|
|
704
|
-
| Microsoft Teams (+ Adaptive Cards) | Teams | `eve add channel/teams` |
|
|
705
|
-
| SMS / phone (speech-transcribed) | Twilio | `eve add channel/twilio` |
|
|
706
|
-
| GitHub @mentions, PR review | GitHub | `eve add channel/github` |
|
|
707
|
-
| Linear issue delegation | Linear | `eve add channel/linear-agent` |
|
|
708
|
-
| Web app / browser chat | eve HTTP + `useEveAgent` | built-in (route auth via enterprise) |
|
|
709
|
-
|
|
710
|
-
Every channel's doc page (`node_modules/eve/docs/channels/<name>.mdx`) carries
|
|
711
|
-
its **complete** setup: the file to write, the env vars, the webhook/app
|
|
712
|
-
registration on the provider side, HITL behavior, and auth verification. The
|
|
713
|
-
flow is always the same three steps — worked example, Telegram:
|
|
714
|
-
|
|
715
|
-
**1. The channel file** (`eve add channel/telegram` writes it, or author it):
|
|
716
|
-
|
|
717
|
-
```ts
|
|
718
|
-
// agent/channels/telegram.ts
|
|
719
|
-
import { telegramChannel } from "eve/channels/telegram";
|
|
720
|
-
|
|
721
|
-
export default telegramChannel({
|
|
722
|
-
botUsername: "acme_atlas_bot",
|
|
723
|
-
});
|
|
724
|
-
```
|
|
725
|
-
|
|
726
|
-
**2. The provider-side credentials** (this part is always yours, not Claude's):
|
|
727
|
-
create the bot with @BotFather, then set both envs (local `.env.local` AND
|
|
728
|
-
Vercel, Sensitive):
|
|
729
|
-
|
|
730
|
-
```bash
|
|
731
|
-
TELEGRAM_BOT_TOKEN=123456:... # from BotFather
|
|
732
|
-
TELEGRAM_WEBHOOK_SECRET_TOKEN=... # any secret you generate
|
|
733
|
-
```
|
|
734
|
-
|
|
735
|
-
**3. Point the provider at the deployed agent.** Each channel mounts a route
|
|
736
|
-
(`POST /eve/v1/telegram` here); Telegram needs the webhook registered by hand:
|
|
737
|
-
|
|
738
|
-
```bash
|
|
739
|
-
curl -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook" \
|
|
740
|
-
-H "Content-Type: application/json" \
|
|
741
|
-
-d '{"url":"https://<deployed-app>/eve/v1/telegram",
|
|
742
|
-
"secret_token":"'"$TELEGRAM_WEBHOOK_SECRET_TOKEN"'",
|
|
743
|
-
"allowed_updates":["message","callback_query"]}'
|
|
744
|
-
```
|
|
745
|
-
|
|
746
|
-
That pattern — file, credentials, point-the-provider-at-the-route — is every
|
|
747
|
-
channel. Slack's version is the connector create/attach in §4.3; Discord/Teams
|
|
748
|
-
have their own app-portal steps in their doc pages. Good group behavior to
|
|
749
|
-
know: Telegram groups only wake the bot on a command, an @mention, or a reply
|
|
750
|
-
to the bot; HITL renders as inline-keyboard buttons; replies over 4096 chars
|
|
751
|
-
split automatically.
|
|
752
|
-
|
|
753
|
-
Two honest caveats to state to the client: **multiplayer's group semantics
|
|
754
|
-
(shared threads, speaker attribution, no-re-mention) are Slack-only today** —
|
|
755
|
-
other surfaces are stock channels, excellent for 1:1; and each surface has its
|
|
756
|
-
own provider terms and data flow — sensitive-data review (§2.5) is per
|
|
757
|
-
channel, not per agent.
|
|
758
|
-
|
|
759
|
-
### 4.3d Connections — wire the client's actual systems
|
|
760
|
-
|
|
761
|
-
A connection turns an external system into tools the model can call. Rule one:
|
|
762
|
-
**search before you write** — most requests already exist as registry items:
|
|
763
|
-
|
|
764
|
-
```bash
|
|
765
|
-
npx eve registry list # official catalog + our @kybernesis source
|
|
766
|
-
npx eve registry search postgres # capability search (also searches skills.sh)
|
|
767
|
-
npx eve registry view connection/linear # ALWAYS inspect before installing
|
|
768
|
-
npx eve add linear # multi-part items let you pick components
|
|
769
|
-
```
|
|
770
|
-
|
|
771
|
-
`eve add` installs dependencies and writes the connection file; official items
|
|
772
|
-
may then offer an **interactive setup flow** (accounts, OAuth clients) — run
|
|
773
|
-
it, or resume a skipped one later with `eve add <item> --skip-install`.
|
|
774
|
-
|
|
775
|
-
When the registry has nothing (a client's internal service), you write one
|
|
776
|
-
file. Two shapes, pick by what the service exposes:
|
|
777
|
-
|
|
778
|
-
- **MCP server** → `defineMcpClientConnection` (the server publishes tools).
|
|
779
|
-
- **OpenAPI 3.x document** → `defineOpenAPIConnection` (each operation
|
|
780
|
-
becomes a tool; filter the operations you actually want).
|
|
781
|
-
|
|
782
|
-
And four auth modes, pick by who the agent acts as:
|
|
783
|
-
|
|
784
|
-
| Mode | When | Shape |
|
|
785
|
-
| --- | --- | --- |
|
|
786
|
-
| **Static token** | org service accounts, internal systems — the pilot default | `auth: { getToken: async () => ({ token: process.env.X_TOKEN! }) }` |
|
|
787
|
-
| **Vercel Connect, user-scoped** | the agent acts as *the person* (their Linear, their calendar) | `auth: connect("<connector-uid>")` — first use posts an OAuth link in-thread, turn parks + resumes (§4.3b showed this live) |
|
|
788
|
-
| **Vercel Connect, app-scoped** | the agent acts as *itself* against an OAuth service | `connect({ connector: "<uid>", principalType: "app" })` — non-interactive |
|
|
789
|
-
| **None** | public/read-only APIs | omit `auth` |
|
|
790
|
-
|
|
791
|
-
The full static-token example (an internal MCP service):
|
|
792
|
-
|
|
793
|
-
```ts
|
|
794
|
-
// agent/connections/wiki.ts
|
|
795
|
-
import { defineMcpClientConnection } from "eve/connections";
|
|
796
|
-
|
|
797
|
-
export default defineMcpClientConnection({
|
|
798
|
-
url: "https://wiki.internal.acme.com/mcp",
|
|
799
|
-
description:
|
|
800
|
-
"Acme's internal wiki: search pages, read content, list owners.",
|
|
801
|
-
auth: {
|
|
802
|
-
getToken: async () => {
|
|
803
|
-
const token = process.env.ACME_WIKI_TOKEN;
|
|
804
|
-
if (!token) throw new Error("ACME_WIKI_TOKEN is not set.");
|
|
805
|
-
return { token };
|
|
806
|
-
},
|
|
807
|
-
},
|
|
808
|
-
});
|
|
809
|
-
```
|
|
810
|
-
|
|
811
|
-
Decide three things per connection, at install time, and write them down:
|
|
812
|
-
|
|
813
|
-
1. **Auth scope** (table above). Subagents have **no user principal** — inside
|
|
814
|
-
them only static-token or app-scoped Connect works.
|
|
815
|
-
2. **Surface gating.** Personal tools get `requireDm`-style fail-closed checks
|
|
816
|
-
(§4.4's surface rules only exist if connections enforce them).
|
|
817
|
-
3. **Approval.** Destructive or spend-shaped tools get an `approval` gate —
|
|
818
|
-
per-connection HITL, rendered as buttons on Slack/Telegram.
|
|
819
|
-
|
|
820
|
-
The `description` matters more than it looks: it's how the model decides to
|
|
821
|
-
reach for the connection at all. Write it like a capability, name the systems.
|
|
822
|
-
|
|
823
|
-
### 4.3e Skills — teach the client's procedures (and mine skills.sh)
|
|
824
|
-
|
|
825
|
-
A skill is a markdown procedure the model loads **on demand** (eve advertises
|
|
826
|
-
each skill's description; the model calls `load_skill` when a turn matches).
|
|
827
|
-
Anything the client's team does "the same way every time" — their release
|
|
828
|
-
checklist, escalation path, report format — is a skill, not an always-on
|
|
829
|
-
instruction. Keep always-on for identity and rules; skills for procedures.
|
|
830
|
-
|
|
831
|
-
Three authoring forms, in order of reach:
|
|
832
|
-
|
|
833
|
-
```md
|
|
834
|
-
<!-- 1. Flat file: agent/skills/escalation.md — smallest possible skill.
|
|
835
|
-
First body line doubles as the routing description. -->
|
|
836
|
-
Use when an incident needs escalation: who to page, in what order, and what
|
|
837
|
-
the first Slack message must contain.
|
|
838
|
-
...procedure...
|
|
839
|
-
```
|
|
840
|
-
|
|
841
|
-
```md
|
|
842
|
-
<!-- 2. Packaged: agent/skills/weekly-report/SKILL.md + references/ dir.
|
|
843
|
-
description frontmatter is REQUIRED here. -->
|
|
844
|
-
---
|
|
845
|
-
description: Use when someone asks for the weekly ops report.
|
|
846
|
-
---
|
|
847
|
-
Pull the numbers in this order... (see references/template.md)
|
|
848
|
-
```
|
|
849
|
-
|
|
850
|
-
TypeScript (`defineSkill` from `eve/skills`) is the third form, only for
|
|
851
|
-
generated content or typed sibling files — start with markdown.
|
|
852
|
-
|
|
853
|
-
The rules that make skills actually fire:
|
|
854
|
-
|
|
855
|
-
- The `description` is a **routing hint, not a label** — write it as the
|
|
856
|
-
triggering task ("Use when…"), and test it in `eve dev` by asking the
|
|
857
|
-
question *without naming the skill* (§4.4b).
|
|
858
|
-
- Skills are **scoped per agent** — subagents can't see the root's skills;
|
|
859
|
-
copy what each needs (or use the subagent-local extension mounts that ship
|
|
860
|
-
them, §4.5).
|
|
861
|
-
- Loading a skill adds instructions, never tools — typed behavior is a tool.
|
|
862
|
-
|
|
863
|
-
**skills.sh** — the community skills marketplace — is built into eve's search
|
|
864
|
-
as the `@skills` source:
|
|
865
|
-
|
|
866
|
-
```bash
|
|
867
|
-
npx eve registry search "react best practices" # hits skills.sh too
|
|
868
|
-
npx eve add @skills/vercel-labs/agent-skills/vercel-react-best-practices
|
|
869
|
-
```
|
|
870
|
-
|
|
871
|
-
Community skills are third-party project files: **read the source and the
|
|
872
|
-
diff before running the agent**, same as any dependency. For client work,
|
|
873
|
-
prefer authoring the client's own procedures; pull from skills.sh for generic
|
|
874
|
-
craft (framework best practices, review checklists) after review.
|
|
875
|
-
|
|
876
|
-
### 4.3f Install `@kybernesis/dispatch` (optional — when the client runs MORE THAN ONE agent)
|
|
877
|
-
|
|
878
|
-
When the client has (or grows into) a second deployed agent — an ops agent
|
|
879
|
-
next to the company assistant, a specialist per business unit — they will ask
|
|
880
|
-
for the agents to talk to each other. Dispatch is the governed way: one
|
|
881
|
-
declared **edge** per direction, human identity carried across the hop.
|
|
882
|
-
|
|
883
|
-
The concept in one breath: the caller mounts the peer as a remote subagent
|
|
884
|
-
(`remotePeer` under `agent/subagents/` — eve's `defineRemoteAgent` underneath,
|
|
885
|
-
durable park→callback dispatch, so a reply comes back on the SAME edge); the
|
|
886
|
-
receiver authors `agent/channels/eve.ts` with `dispatchChannel({ trustedPeers })`,
|
|
887
|
-
which feeds one peer list into BOTH the OIDC subjects allowlist and
|
|
888
|
-
`trustedForwarders`. Forwarding is on by default: the receiving agent runs as
|
|
889
|
-
the human who asked, so Arcana scoping, per-user connections, and PostHog
|
|
890
|
-
attribution compose across the hop unchanged (`eve:forwarded-by` records the
|
|
891
|
-
edge for audit).
|
|
892
|
-
|
|
893
|
-
**Don't hand-wire it — use the `connect-agents` Claude Code skill** (in the
|
|
894
|
-
seeded `.claude/skills/`): tell Claude "connect <agent A> to <agent B>" and it
|
|
895
|
-
reads both repos, writes the edge with a routing description derived from the
|
|
896
|
-
callee's REAL capabilities, sets the URL env var, and walks the deploy+verify
|
|
897
|
-
steps. `kyb doctor` then checks the edges (env var set, no `() => true`
|
|
898
|
-
trust, forwardPrincipal present).
|
|
899
|
-
|
|
900
|
-
Client-conversation rules of thumb:
|
|
901
|
-
|
|
902
|
-
- One edge = ask-and-answer in one direction. Mirror-image edge only if the
|
|
903
|
-
other agent should also INITIATE. Quote them separately.
|
|
904
|
-
- **Both ends must run compatible eve versions** — an old receiver silently
|
|
905
|
-
drops principal forwarding and runs the session as the calling app's
|
|
906
|
-
service identity. Upgrade edges as a unit (`kyb upgrade` both repos).
|
|
907
|
-
- Peers are pinned to production deployments of named Vercel projects.
|
|
908
|
-
Previews never get trust implicitly. The client's Vercel team is still the
|
|
909
|
-
outer boundary, same as §4.3b.
|
|
910
|
-
- Cross-ORG edges (client agent ↔ another company's agent) are a different
|
|
911
|
-
product conversation — purpose-scoped grants, §2.5 disclosures. Don't wire
|
|
912
|
-
one as if it were internal.
|
|
913
|
-
|
|
914
|
-
**Governed mode (dispatch ≥0.2.1 + enterprise ≥0.2.0 + the client's control
|
|
915
|
-
plane) — the preferred form.** Edges become GRANTS in the admin instead of
|
|
916
|
-
code: register both agents (/agents, OPEN production alias, health 200), grant
|
|
917
|
-
the edge on the CALLEE's panel (caller + purpose + optional expiry), mint each
|
|
918
|
-
agent's credential (shown once) into KYBERNESIS_AGENT_CREDENTIAL on its
|
|
919
|
-
deployment. Code shrinks to remotePeer({ callee: "<EXACT registered name —
|
|
920
|
-
case-sensitive>", governed: { issuer }, envVar, fallbackUrl }) and
|
|
921
|
-
dispatchChannel({ governed: { issuer, agent } }). Outbound auth is a 300 s A2A
|
|
922
|
-
token minted per edge; the callee URL comes from the registry (discovery), env
|
|
923
|
-
var still wins. THE DEMO: revoke the edge in the admin → the caller is refused
|
|
924
|
-
(edge_not_granted) within 5 minutes, no redeploy; re-grant → restored. Run it
|
|
925
|
-
for the client — it's the whole governance story in one minute. Full lifecycle
|
|
926
|
-
proven live 2026-08-07 (kyber ↔ eve-gtm). Budget note: the deployed agent and
|
|
927
|
-
local eval runs share the project's AI Gateway budget — size it for both.
|
|
928
|
-
|
|
929
|
-
### 4.4 Author the agent's identity and instructions
|
|
930
|
-
|
|
931
|
-
How instructions work in eve (30 seconds of mechanics): a flat
|
|
932
|
-
`agent/instructions.md` is the whole prompt; an `agent/instructions/`
|
|
933
|
-
**directory** combines entries alphabetically (root file first) and accepts
|
|
934
|
-
both `.md` and `.ts` files — a `.ts` entry wraps `defineInstructions` (built
|
|
935
|
-
once at compile time) or `defineDynamic` (resolved per session, like the
|
|
936
|
-
surface.ts example below). Keep always-on instructions to identity, tone, and
|
|
937
|
-
standing rules; procedures belong in skills (§4.3e) — the model loads those on
|
|
938
|
-
demand instead of paying for them every turn.
|
|
939
|
-
|
|
940
|
-
Crib the structure from `~/kyber/agent/instructions/identity.md`, which has
|
|
941
|
-
three sections worth copying:
|
|
942
|
-
|
|
943
|
-
1. **Identity** — who the agent is, and *how to write for Slack*: short paragraphs,
|
|
944
|
-
bullets, no headings unless the answer is genuinely long. Slack is a chat surface, not
|
|
945
|
-
a document editor. Agents default to essay mode; say otherwise explicitly.
|
|
946
|
-
2. **Delegation** — name each subagent and say when to route to it. Critically:
|
|
947
|
-
*"a specialist sees none of this conversation, so pack everything it needs into the
|
|
948
|
-
message."* Subagents do not inherit context. Without this line the agent delegates
|
|
949
|
-
half-briefed and the specialist answers badly.
|
|
950
|
-
3. **Surfaces** — what public channels mean versus DMs, and what to say when someone
|
|
951
|
-
asks for something personal in a channel.
|
|
952
|
-
|
|
953
|
-
For per-session context, `defineDynamic` on `session.started` lets you inject
|
|
954
|
-
surface-specific instructions. `~/kyber/agent/instructions/surface.ts` is a working
|
|
955
|
-
example: it greets a DM session by the caller's verified name and reminds a channel
|
|
956
|
-
session that everything it posts is public.
|
|
957
|
-
|
|
958
|
-
**Author these with Claude Code (§4.0), and judge drafts by test, not by
|
|
959
|
-
reading** — paste the discovery notes, have it draft identity.md and the
|
|
960
|
-
client skills, then run §4.4b and the evals. Instructions are prompts under
|
|
961
|
-
test: every rule in our own identity.md exists because an eval or a live turn
|
|
962
|
-
caught the opposite behavior.
|
|
963
|
-
|
|
964
|
-
### 4.4b Test-drive in `eve dev` — before any connector exists
|
|
965
|
-
|
|
966
|
-
You do not need Slack, credentials, or a deploy to exercise the agent:
|
|
967
|
-
|
|
968
|
-
```bash
|
|
969
|
-
cd ~/work/acme-atlas
|
|
970
|
-
npm run dev # boots the local runtime and opens the dev TUI
|
|
971
|
-
```
|
|
972
|
-
|
|
973
|
-
Talk to it in the TUI and walk the behaviors you just authored, in roughly
|
|
974
|
-
this order — each line catches a different class of wiring mistake:
|
|
975
|
-
|
|
976
|
-
1. **Identity**: "who are you, what can you do?" — does the persona match
|
|
977
|
-
identity.md, and does it write chat-length answers?
|
|
978
|
-
2. **Skill routing**: ask something a skill covers *without naming the skill*
|
|
979
|
-
— watch for the `load_skill` call. If it doesn't fire, the skill's
|
|
980
|
-
`description` isn't written as a triggering task.
|
|
981
|
-
3. **Delegation**: ask a department question — watch the subagent call and
|
|
982
|
-
check the answer came back briefed (§4.4's "pack everything" rule).
|
|
983
|
-
4. **Memory**: "remember that X" then ask for it back — the recall-first rule
|
|
984
|
-
in action against the real (eval-workspace!) Arcana.
|
|
985
|
-
5. **Engineer layer** (if installed): ask for a small page + screenshot — the
|
|
986
|
-
first local sandbox turn proves the hosted-sandbox credentials work.
|
|
987
|
-
|
|
988
|
-
Notes that save an afternoon: the TUI's local principal counts as a **DM**
|
|
989
|
-
surface (so `requireDm` tools are reachable locally); `npx eve info` is the
|
|
990
|
-
compile/discovery truth (0 diagnostics before moving on); **kill the dev
|
|
991
|
-
server before `npm run eval`** — a running instance makes evals attach to it
|
|
992
|
-
(§4.8); and the TUI is NOT the deployed agent — Slack runs the deployed build,
|
|
993
|
-
redeploy after every change (§11).
|
|
994
|
-
|
|
995
|
-
### 4.5 Build the department subagents
|
|
996
|
-
|
|
997
|
-
One directory per department under `agent/subagents/<dept>/`:
|
|
998
|
-
|
|
999
|
-
```
|
|
1000
|
-
agent/subagents/finance/
|
|
1001
|
-
├── agent.ts
|
|
1002
|
-
├── instructions.md
|
|
1003
|
-
├── connections/
|
|
1004
|
-
│ └── arcana.ts
|
|
1005
|
-
└── skills/
|
|
1006
|
-
├── recall/SKILL.md
|
|
1007
|
-
├── remember/SKILL.md
|
|
1008
|
-
└── brain-note/SKILL.md
|
|
1009
|
-
```
|
|
1010
|
-
|
|
1011
|
-
`agent.ts` — **the `description` is the routing signal.** The root agent sees only this
|
|
1012
|
-
string when deciding whether to delegate. Write it as a list of the nouns people
|
|
1013
|
-
actually say:
|
|
1014
|
-
|
|
1015
|
-
```ts
|
|
1016
|
-
import { defineAgent } from "eve";
|
|
1017
|
-
|
|
1018
|
-
export default defineAgent({
|
|
1019
|
-
description:
|
|
1020
|
-
"Finance specialist: financials, budgets, spend, revenue, invoices, runway, and " +
|
|
1021
|
-
"financial reporting. Keeps the finance team's own memory workspace. Delegate any " +
|
|
1022
|
-
"finance-shaped task, question, or reporting request here.",
|
|
1023
|
-
model: "anthropic/claude-sonnet-5",
|
|
1024
|
-
});
|
|
1025
|
-
```
|
|
1026
|
-
|
|
1027
|
-
**Two hard constraints on subagents, both of which will bite you:**
|
|
1028
|
-
|
|
1029
|
-
1. **Subagents inherit nothing — give each its own mount.** On eve ≥0.30 a
|
|
1030
|
-
declared subagent mounts extensions locally: drop
|
|
1031
|
-
`agent/subagents/<dept>/extensions/arcana.ts` (an `arcana({ apiKey,
|
|
1032
|
-
workspace })` mount with that department's scoped key) and only that
|
|
1033
|
-
subagent gets the connection + skills + instructions. This is the default
|
|
1034
|
-
pattern now. The plain-connection alternative below still works (it's what
|
|
1035
|
-
pre-0.30 required, and what you'll find in older kyber subagents) when you
|
|
1036
|
-
want the connection without the shipped skills:
|
|
1037
|
-
|
|
1038
|
-
```ts
|
|
1039
|
-
// agent/subagents/finance/connections/arcana.ts
|
|
1040
|
-
import { arcanaBrain } from "../../../lib/arcana";
|
|
1041
|
-
|
|
1042
|
-
export default arcanaBrain({
|
|
1043
|
-
description:
|
|
1044
|
-
"The finance team's long-term memory (Arcana): remember, recall, search, " +
|
|
1045
|
-
"timeline, and brain notes for financial work.",
|
|
1046
|
-
defaultWorkspace: "acme-finance",
|
|
1047
|
-
workspaceEnvVar: "ARCANA_FINANCE_WORKSPACE",
|
|
1048
|
-
keyEnvVar: "ARCANA_FINANCE_API_KEY",
|
|
1049
|
-
});
|
|
1050
|
-
```
|
|
1051
|
-
|
|
1052
|
-
The factory resolves the key in this order: the eval key when the workspace has been
|
|
1053
|
-
overridden to `acme-eval`, then the department's own key, then the root
|
|
1054
|
-
`ARCANA_API_KEY` as a fallback. That ordering is what makes hermetic eval runs work
|
|
1055
|
-
without a second copy of every file.
|
|
1056
|
-
|
|
1057
|
-
2. **Subagents have no user principal, so Vercel Connect OAuth is impossible in them.**
|
|
1058
|
-
Any connection a subagent needs must use a static token from an env var — an org
|
|
1059
|
-
service account, not a per-user grant. Plan the client's integrations accordingly.
|
|
1060
|
-
|
|
1061
|
-
### 4.6 Schedules (optional)
|
|
1062
|
-
|
|
1063
|
-
`agent/schedules/*.ts` for anything recurring — a Monday pipeline summary, a Friday
|
|
1064
|
-
financial report. **Schedules live on the root agent only**; a scheduled root turn
|
|
1065
|
-
delegates to the subagent that owns the work. `~/kyber/agent/schedules/friday-financials.ts`
|
|
1066
|
-
is the working example: it fires Friday 02:00 UTC, delegates to `finance`, and DMs the
|
|
1067
|
-
result to a configured Slack user. Note that DMing a user from a schedule needs the
|
|
1068
|
-
`im:write` scope on the Slack connector — add it during Phase 5 or the first run fails
|
|
1069
|
-
silently at the last step.
|
|
1070
|
-
|
|
1071
|
-
### 4.6b Observability — evlog → PostHog (the Operate-phase deliverable)
|
|
1072
|
-
|
|
1073
|
-
One hook file gives the agent per-turn structured telemetry — who talked,
|
|
1074
|
-
which tools/subagents fired, timings, token usage, outcome — with message
|
|
1075
|
-
text redacted and tool-failure turns always kept:
|
|
1076
|
-
|
|
1077
|
-
```ts
|
|
1078
|
-
// agent/hooks/evlog.ts
|
|
1079
|
-
import { defineEvlogHook } from "evlog/eve";
|
|
1080
|
-
import { createPostHogDrain } from "evlog/posthog";
|
|
1081
|
-
|
|
1082
|
-
export default defineEvlogHook({
|
|
1083
|
-
init: { env: { service: "acme-atlas" } },
|
|
1084
|
-
// mode "events" is REQUIRED for dashboards: the default "logs" mode
|
|
1085
|
-
// ships OTLP to the separate PostHog Logs product — invisible to
|
|
1086
|
-
// Activity/insights, and it looks exactly like "no events arriving".
|
|
1087
|
-
drain: createPostHogDrain({ mode: "events" }),
|
|
1088
|
-
redactMessage: true,
|
|
1089
|
-
});
|
|
1090
|
-
```
|
|
1091
|
-
|
|
1092
|
-
Env: `POSTHOG_API_KEY` = the **project** key (`phc_…`, ingestion-only — a
|
|
1093
|
-
`phx_…` personal key is account-privileged and wrong here). The default host
|
|
1094
|
-
is `https://us.i.posthog.com`; EU-hosted projects need
|
|
1095
|
-
`POSTHOG_HOST=https://eu.i.posthog.com` or events silently vanish. To verify
|
|
1096
|
-
region + key in one shot, curl a test event at each region's `/batch/` and
|
|
1097
|
-
see which appears in Activity. Turns then land as `evlog_wide_event` — build
|
|
1098
|
-
the starter insights on its properties: turns/day by surface, tool failure
|
|
1099
|
-
rate, delegation mix, p50/p95 duration.
|
|
1100
|
-
|
|
1101
|
-
**Person attribution (optional — a DISCLOSURE item, §2.5):** wide events carry no
|
|
1102
|
-
userId by default, so PostHog sees one anonymous actor named after the service. To
|
|
1103
|
-
attribute turns to the verified speaker, add a sibling hook that stamps the
|
|
1104
|
-
per-message-authenticated principal via evlog's `useLogger` — on `step.started`, not
|
|
1105
|
-
`turn.started`, so evlog's turn state exists regardless of hook ordering (crib
|
|
1106
|
-
`~/kyber/agent/hooks/attribution.ts`). Then a one-time $identify per person maps ids
|
|
1107
|
-
to names. Per-employee telemetry must be a deliberate, disclosed choice at a client.
|
|
1108
|
-
|
|
1109
|
-
### 4.7 Environment variables
|
|
1110
|
-
|
|
1111
|
-
Two places must agree: `.env.local` for local development, and the Vercel project's
|
|
1112
|
-
environment for anything deployed.
|
|
1113
|
-
|
|
1114
|
-
```bash
|
|
1115
|
-
# .env.local (never commit; .gitignore already covers it)
|
|
1116
|
-
KYBERNESIS_ISSUER=https://agent.kybernesis.ai
|
|
1117
|
-
KYBERNESIS_AGENT=atlas
|
|
1118
|
-
|
|
1119
|
-
SLACK_CONNECTOR_UID=slack/atlas
|
|
1120
|
-
|
|
1121
|
-
ARCANA_API_KEY=kb_... # root / company brain key
|
|
1122
|
-
ARCANA_COMPANY_WORKSPACE=acme-company
|
|
1123
|
-
ARCANA_DM_WORKSPACE=acme-dm
|
|
1124
|
-
|
|
1125
|
-
ARCANA_FINANCE_API_KEY=kb_...
|
|
1126
|
-
ARCANA_FINANCE_WORKSPACE=acme-finance
|
|
1127
|
-
ARCANA_ENGINEERING_API_KEY=kb_...
|
|
1128
|
-
ARCANA_ENGINEERING_WORKSPACE=acme-engineering
|
|
1129
|
-
|
|
1130
|
-
ARCANA_EVAL_API_KEY=kb_... # the acme-eval workspace key
|
|
1131
|
-
```
|
|
1132
|
-
|
|
1133
|
-
Push them to Vercel (repeat per environment; mark secrets **Sensitive**):
|
|
1134
|
-
|
|
1135
|
-
```bash
|
|
1136
|
-
cd ~/work/acme-atlas
|
|
1137
|
-
vercel env add ARCANA_API_KEY production
|
|
1138
|
-
vercel env add ARCANA_API_KEY preview
|
|
1139
|
-
vercel env add ARCANA_API_KEY development
|
|
1140
|
-
```
|
|
1141
|
-
|
|
1142
|
-
> **Read this twice.** `eve deploy` runs a `vercel env pull` afterwards and
|
|
1143
|
-
> **overwrites `.env.local`**. The Vercel project environment is the source of truth. If
|
|
1144
|
-
> you add a variable locally and then deploy, your local edit is gone. Always add to
|
|
1145
|
-
> Vercel first, then pull.
|
|
1146
|
-
|
|
1147
|
-
### 4.8 Evals — the QA deliverable
|
|
1148
|
-
|
|
1149
|
-
The baseline suite is a package. Do **not** hand-write or copy eval files:
|
|
1150
|
-
|
|
1151
|
-
```bash
|
|
1152
|
-
cd ~/work/acme-atlas
|
|
1153
|
-
eve add @kybernesis/evals
|
|
1154
|
-
```
|
|
1155
|
-
|
|
1156
|
-
That installs `@kybernesis/evals` and writes two files: `evals/evals.config.ts`
|
|
1157
|
-
(judge model — **never the model under test** — generous timeout, gentle
|
|
1158
|
-
concurrency) and `evals/kybernesis.eval.ts`, which you configure for this agent:
|
|
1159
|
-
|
|
1160
|
-
```ts
|
|
1161
|
-
// evals/kybernesis.eval.ts
|
|
1162
|
-
import { kybernesisBaseline } from "@kybernesis/evals";
|
|
1163
|
-
|
|
1164
|
-
export default kybernesisBaseline({
|
|
1165
|
-
agentDisplayName: "Atlas",
|
|
1166
|
-
// engineer: true, // when the engineer layer is installed — adds the vision-loop eval
|
|
1167
|
-
routing: [
|
|
1168
|
-
{ subagent: "finance" },
|
|
1169
|
-
{ subagent: "marketing" },
|
|
1170
|
-
{ subagent: "engineering" },
|
|
1171
|
-
],
|
|
1172
|
-
});
|
|
1173
|
-
```
|
|
1174
|
-
|
|
1175
|
-
You get: a **smoke** eval (boots, replies, identifies itself), five **memory**
|
|
1176
|
-
evals (greeting must NOT thrash memory; explicit remember never refused;
|
|
1177
|
-
proactive store of company decisions; brain-note write+index two-step, in
|
|
1178
|
-
order; a fact stored in one session recalled **unprompted** from a brand-new
|
|
1179
|
-
session), and one **routing** eval per department you list. Every fixture
|
|
1180
|
-
encodes a hardening lesson we paid for in production (in-test nonces, per-run
|
|
1181
|
-
unique keys, company-general wording, suffix-based tool matching, realistic
|
|
1182
|
-
delegation timeouts) — the package README explains each one. **Do not "clean
|
|
1183
|
-
up" the fixture patterns**; each odd-looking choice fixes a real bug.
|
|
1184
|
-
|
|
1185
|
-
Wire the hermetic run into `package.json` so every Arcana workspace is forced
|
|
1186
|
-
to `acme-eval` and evals never write into a real brain:
|
|
1187
|
-
|
|
1188
|
-
```jsonc
|
|
1189
|
-
"scripts": {
|
|
1190
|
-
"eval": "ARCANA_COMPANY_WORKSPACE=acme-eval ARCANA_DM_WORKSPACE=acme-eval ARCANA_FINANCE_WORKSPACE=acme-eval ARCANA_ENGINEERING_WORKSPACE=acme-eval eve eval",
|
|
1191
|
-
"typecheck": "tsc"
|
|
1192
|
-
}
|
|
1193
|
-
```
|
|
1194
|
-
|
|
1195
|
-
Run them:
|
|
1196
|
-
|
|
1197
|
-
```bash
|
|
1198
|
-
cd ~/work/acme-atlas
|
|
1199
|
-
npm run eval
|
|
1200
|
-
```
|
|
1201
|
-
|
|
1202
|
-
Certification-run hygiene (each of these ate a real run on 2026-08-06):
|
|
1203
|
-
|
|
1204
|
-
- **Kill any stale `eve dev` first** — a leftover dev server makes `eve eval`
|
|
1205
|
-
attach to the old instance and silently run stale code (or refuse to run).
|
|
1206
|
-
- **Do not touch the repo while the suite runs.** The dev runtime watches
|
|
1207
|
-
`agent/`; an edit mid-run breaks the rebuild and kills the remaining evals.
|
|
1208
|
-
- **No Docker anywhere.** The workshop backend is pinned to Vercel Sandbox —
|
|
1209
|
-
local eval runs create hosted sandboxes with the linked project's
|
|
1210
|
-
credentials (`vercel link` + `vercel env pull` first; `kyb doctor` checks
|
|
1211
|
-
the token) and reuse the deploy-prewarmed template (engineer eval ≈3–4 min
|
|
1212
|
-
warm, measured). If you eval BEFORE the first deploy, the first run bakes
|
|
1213
|
-
the hosted template — evals ≥0.2.1 budget 20 minutes for that.
|
|
1214
|
-
- If eve complains about a sandbox migration or templates re-bake every run,
|
|
1215
|
-
the local cache is stale from a framework version hop:
|
|
1216
|
-
`rm -rf .eve/sandbox-cache .eve/dev-runtime` and rerun.
|
|
1217
|
-
|
|
1218
|
-
Client-specific evals go in separate files beside `kybernesis.eval.ts`, built
|
|
1219
|
-
from the same primitives the package exports (`isResultFrom`,
|
|
1220
|
-
`MEMORY_READ_SUFFIXES`, …). Two traps the package can NOT absorb for you — see
|
|
1221
|
-
§10: a running dev server on port 2000 makes `eve eval` exit early, and
|
|
1222
|
-
governed agents (`@kybernesis/enterprise`) need `localDev()` in their auth walk
|
|
1223
|
-
for local eval runs (the registry's enterprise template includes it — don't
|
|
1224
|
-
remove it).
|
|
1225
|
-
|
|
1226
|
-
---
|
|
1227
|
-
|
|
1228
|
-
## 5. Deploy (Day 3, ~30 minutes)
|
|
1229
|
-
|
|
1230
|
-
### 5.1 Slack connector (needs the client's Slack admin present)
|
|
1231
|
-
|
|
1232
|
-
```bash
|
|
1233
|
-
cd ~/work/acme-atlas
|
|
1234
|
-
vercel connect create slack --triggers --name atlas
|
|
1235
|
-
```
|
|
1236
|
-
|
|
1237
|
-
This opens a browser flow against **the client's Slack workspace**. The display name you
|
|
1238
|
-
set here is what employees see. Two things to do inside that flow:
|
|
1239
|
-
|
|
1240
|
-
- Open **Advanced** and add `message.channels` under **Trigger Event Types** and
|
|
1241
|
-
`channels:history` under **Bot Scopes** — these are what make thread-following work.
|
|
1242
|
-
Private channels also need `message.groups` and `groups:history`. Add `im:write` if you
|
|
1243
|
-
built a schedule that DMs someone.
|
|
1244
|
-
- Note the connector UID it gives you (e.g. `slack/atlas`) and put it in
|
|
1245
|
-
`SLACK_CONNECTOR_UID`.
|
|
1246
|
-
|
|
1247
|
-
Then re-point the trigger at eve's Slack route. eve does not serve Connect's default
|
|
1248
|
-
path, so this detach/attach pair is mandatory:
|
|
1249
|
-
|
|
1250
|
-
```bash
|
|
1251
|
-
vercel connect detach slack/atlas --yes
|
|
1252
|
-
vercel connect attach slack/atlas --triggers --trigger-path /eve/v1/slack --yes
|
|
1253
|
-
```
|
|
1254
|
-
|
|
1255
|
-
### 5.2 Pre-flight, then deploy
|
|
1256
|
-
|
|
1257
|
-
```bash
|
|
1258
|
-
cd ~/work/acme-atlas
|
|
1259
|
-
npm run typecheck # 0 errors
|
|
1260
|
-
npx eve info # 0 discovery diagnostics
|
|
1261
|
-
npx eve deploy
|
|
1262
|
-
```
|
|
1263
|
-
|
|
1264
|
-
Treat "0 errors, 0 warnings" as a gate, not a goal. `eve info` warnings are almost always
|
|
1265
|
-
a file eve did not discover the way you thought.
|
|
1266
|
-
|
|
1267
|
-
### 5.3 Verify the deployment
|
|
1268
|
-
|
|
1269
|
-
```bash
|
|
1270
|
-
curl -s https://acme-atlas.vercel.app/eve/v1/health
|
|
1271
|
-
# expect {"ok":true}
|
|
1272
|
-
```
|
|
1273
|
-
|
|
1274
|
-
Substitute the real deployment URL that `eve deploy` printed. Keep that URL — Phase 6
|
|
1275
|
-
needs it.
|
|
1276
|
-
|
|
1277
|
-
> **The single most common failure mode in this entire playbook:** you change something,
|
|
1278
|
-
> test it in `eve dev`, it works, you try it in Slack, and it does not. **Slack talks to
|
|
1279
|
-
> the deployed build.** Every change needs `npx eve deploy` before Slack sees it. Say this
|
|
1280
|
-
> out loud to yourself once per day.
|
|
1281
|
-
|
|
1282
|
-
### 5.4 Green eval baseline
|
|
1283
|
-
|
|
1284
|
-
```bash
|
|
1285
|
-
cd ~/work/acme-atlas
|
|
1286
|
-
npm run eval
|
|
1287
|
-
```
|
|
1288
|
-
|
|
1289
|
-
A green suite is the deliverable you hand the client. Wire it into their CI before you
|
|
1290
|
-
leave (a GitHub Action running `npm run typecheck && npm run eval` on pull requests,
|
|
1291
|
-
with the Arcana eval key and gateway credential as repository secrets).
|
|
1292
|
-
|
|
1293
|
-
---
|
|
1294
|
-
|
|
1295
|
-
## 6. Control-plane wiring (Day 3, ~15 minutes, in the browser)
|
|
1296
|
-
|
|
1297
|
-
Go to <https://agent.kybernesis.ai> and switch to the **ACME org**.
|
|
1298
|
-
|
|
1299
|
-
### 6.1 Register the agent
|
|
1300
|
-
|
|
1301
|
-
**Agents → Register agent.**
|
|
1302
|
-
|
|
1303
|
-
- **Name:** `atlas` — this **must** exactly equal `KYBERNESIS_AGENT` in the deployed
|
|
1304
|
-
environment. A mismatch produces `403 agent_not_granted` for a user who genuinely has
|
|
1305
|
-
the grant, and it is a maddening thing to debug at a demo.
|
|
1306
|
-
- **Runtime:** **▲ eve deployment** (the default).
|
|
1307
|
-
- **Deployment URL:** the URL from §5.3.
|
|
1308
|
-
|
|
1309
|
-
The row now shows an **▲ eve** badge, the deployment URL, and a live health dot the
|
|
1310
|
-
control plane probes from `<deploymentUrl>/eve/v1/health`. If the dot is red, the control
|
|
1311
|
-
plane cannot reach the deployment — check Vercel deployment protection before you check
|
|
1312
|
-
anything else.
|
|
1313
|
-
|
|
1314
|
-
### 6.2 Invite the humans
|
|
1315
|
-
|
|
1316
|
-
**Users → Invite.** Invite the client's admins first, then the pilot cohort from your
|
|
1317
|
-
discovery table. Give the client admins `manage`; everyone else gets `use`.
|
|
1318
|
-
|
|
1319
|
-
### 6.3 Grant the agent
|
|
1320
|
-
|
|
1321
|
-
**Grants.** Grant `atlas` to each pilot user (or to a team, if you created one). Level
|
|
1322
|
-
`use` for the cohort, `manage` for admins.
|
|
1323
|
-
|
|
1324
|
-
Alternatively, set the agent's access tier to `org` so every org member gets it
|
|
1325
|
-
implicitly. For a pilot, prefer explicit grants — the whole demo in §8 depends on grants
|
|
1326
|
-
being visibly individual.
|
|
1327
|
-
|
|
1328
|
-
### 6.4 The one timing rule you must internalize
|
|
1329
|
-
|
|
1330
|
-
**Grants are resolved at mint time.** The identity token and policy bundle a user holds
|
|
1331
|
-
are a snapshot of their access at the moment they signed in.
|
|
1332
|
-
|
|
1333
|
-
- Grant someone *after* they signed in → they do not have it until their session is
|
|
1334
|
-
re-minted (a refresh, or a fresh sign-in).
|
|
1335
|
-
- Revoke someone → their **current** token keeps working until it expires. Default TTL is
|
|
1336
|
-
1 hour (`IDENTITY_TOKEN_TTL_SECONDS`). **That TTL is the off-boarding SLA. Tell the
|
|
1337
|
-
client the number.** If an hour is too long for them, tune it — the cost is more
|
|
1338
|
-
frequent refreshes.
|
|
1339
|
-
- **Suspend** someone → minting refuses entirely. That is the immediate lever, and it is
|
|
1340
|
-
the one to use for a real off-boarding.
|
|
1341
|
-
|
|
1342
|
-
---
|
|
1343
|
-
|
|
1344
|
-
## 7. Pilot onboarding — the humans (Day 4)
|
|
1345
|
-
|
|
1346
|
-
The technical work is done. This day decides whether the pilot succeeds.
|
|
1347
|
-
|
|
1348
|
-
### 7.1 Set up the shared channel
|
|
1349
|
-
|
|
1350
|
-
Invite the agent to the client's chosen channel (`/invite @atlas`). Post a short pinned
|
|
1351
|
-
message the champion can point at:
|
|
1352
|
-
|
|
1353
|
-
> `@atlas` is our company agent. Mention it in this channel to ask something, and it will
|
|
1354
|
-
> keep replying in that thread without needing another mention — so you can just talk.
|
|
1355
|
-
> DM it for anything personal; DMs use a separate memory from this channel. Type `/new` in
|
|
1356
|
-
> a DM to start over. It remembers what you tell it, so telling it something once is
|
|
1357
|
-
> enough.
|
|
1358
|
-
|
|
1359
|
-
### 7.2 Run a 30-minute group session with the cohort
|
|
1360
|
-
|
|
1361
|
-
Do this live, in the shared channel, with everyone watching. Demonstrate, in order:
|
|
1362
|
-
|
|
1363
|
-
1. **Mention it and ask a real question.** Use something from their actual work, not a
|
|
1364
|
-
toy.
|
|
1365
|
-
2. **Have a second person reply in the same thread without mentioning it.** This is the
|
|
1366
|
-
moment the room understands it is not a chatbot. Point out that the agent knows who
|
|
1367
|
-
said what.
|
|
1368
|
-
3. **Tell it a fact** — "our Q3 board meeting is on the 12th" — then start a **fresh
|
|
1369
|
-
thread** and ask about it. Cross-session recall is the second moment.
|
|
1370
|
-
4. **Ask a department question** and let it delegate. Show that the finance specialist
|
|
1371
|
-
has its own knowledge.
|
|
1372
|
-
5. **Ask something personal in the channel** and let it refuse and redirect to a DM. Then
|
|
1373
|
-
do it in a DM. This teaches the surface model better than any explanation.
|
|
1374
|
-
|
|
1375
|
-
### 7.3 Train the client admins separately (15 minutes)
|
|
1376
|
-
|
|
1377
|
-
Walk the two admins through the control plane themselves — do not do it for them:
|
|
1378
|
-
|
|
1379
|
-
- Invite a user.
|
|
1380
|
-
- Grant `atlas`.
|
|
1381
|
-
- Revoke it, and watch what happens (§8.3).
|
|
1382
|
-
- Suspend a user, and watch minting refuse.
|
|
1383
|
-
|
|
1384
|
-
Tell them the timing rule from §6.4 in these words: *"Suspend is immediate. Revoke takes
|
|
1385
|
-
effect within an hour. Grant takes effect the next time the person signs in."*
|
|
1386
|
-
|
|
1387
|
-
### 7.4 Set expectations honestly
|
|
1388
|
-
|
|
1389
|
-
Say these four things to the cohort, in plain language:
|
|
1390
|
-
|
|
1391
|
-
- Anyone in this Slack workspace can talk to the agent. Per-person access control on the
|
|
1392
|
-
Slack door is not built yet; the control-plane grants govern the HTTP/desktop door.
|
|
1393
|
-
- Approval buttons in a shared thread can be clicked by anyone in that thread. Do not use
|
|
1394
|
-
the agent for anything destructive in a shared channel yet.
|
|
1395
|
-
- Everything it learns in a public channel is shared with everyone in that channel.
|
|
1396
|
-
- It is a week old at your company. It will be wrong. Tell the champion when it is, and
|
|
1397
|
-
those corrections become instructions and evals.
|
|
1398
|
-
|
|
1399
|
-
---
|
|
1400
|
-
|
|
1401
|
-
## 8. Acceptance demo script (Day 5, ~20 minutes, in front of the client's sponsor)
|
|
1402
|
-
|
|
1403
|
-
Run this as a script. Rehearse it once alone first. Every step below has been executed
|
|
1404
|
-
against production.
|
|
1405
|
-
|
|
1406
|
-
### 8.1 Slack — the agent works (5 min)
|
|
1407
|
-
|
|
1408
|
-
- [ ] Mention `@atlas` in the shared channel with a real question → coherent reply in
|
|
1409
|
-
thread.
|
|
1410
|
-
- [ ] A second person replies in the thread with no mention → agent continues, and its
|
|
1411
|
-
answer reflects who is speaking.
|
|
1412
|
-
- [ ] DM `@atlas` → it responds as a personal assistant.
|
|
1413
|
-
- [ ] In the DM, type `/new` → "Started a fresh conversation."
|
|
1414
|
-
|
|
1415
|
-
### 8.2 Memory — it actually learns (5 min)
|
|
1416
|
-
|
|
1417
|
-
- [ ] In the channel: tell it a company fact.
|
|
1418
|
-
- [ ] Start a **fresh thread**: ask about that fact → it recalls it.
|
|
1419
|
-
- [ ] Ask a department question → it delegates, and the answer comes back synthesized.
|
|
1420
|
-
- [ ] Show the client their own memory, in their own workspace:
|
|
1421
|
-
```bash
|
|
1422
|
-
curl -s -H "Authorization: Bearer $ARCANA_API_KEY" \
|
|
1423
|
-
-H "X-Kyberagent-Agent: acme-company" \
|
|
1424
|
-
"https://api.arcana.kybernesis.ai/brain/acme-company/timeline?limit=5"
|
|
1425
|
-
```
|
|
1426
|
-
|
|
1427
|
-
### 8.3 Governance — the demo that sells the product (10 min)
|
|
1428
|
-
|
|
1429
|
-
This is the part the sponsor remembers. Do it live; do not screenshot it.
|
|
1430
|
-
|
|
1431
|
-
**Step 1 — sign in as a granted employee.** Start the device flow:
|
|
1432
|
-
|
|
1433
|
-
```bash
|
|
1434
|
-
curl -s -X POST https://agent.kybernesis.ai/api/oauth/device \
|
|
1435
|
-
-H 'content-type: application/json' \
|
|
1436
|
-
-d '{"deviceId":"fde-demo","deviceLabel":"FDE laptop"}'
|
|
1437
|
-
```
|
|
1438
|
-
|
|
1439
|
-
You get back `device_code`, `user_code`, `verification_uri`,
|
|
1440
|
-
`verification_uri_complete`, `expires_in: 600`, `interval: 5`. Open
|
|
1441
|
-
`verification_uri_complete` in a browser and approve as the employee.
|
|
1442
|
-
|
|
1443
|
-
**Step 2 — exchange the device code for a session:**
|
|
1444
|
-
|
|
1445
|
-
```bash
|
|
1446
|
-
curl -s -X POST https://agent.kybernesis.ai/api/oauth/token \
|
|
1447
|
-
-H 'content-type: application/json' \
|
|
1448
|
-
-d '{"device_code":"PASTE_DEVICE_CODE"}'
|
|
1449
|
-
```
|
|
1450
|
-
|
|
1451
|
-
Before approval this returns RFC 8628 error codes (`authorization_pending`, and so on).
|
|
1452
|
-
After approval it returns an **IdentitySession** plus a `refresh_token`:
|
|
1453
|
-
|
|
1454
|
-
```json
|
|
1455
|
-
{ "issuer": "https://agent.kybernesis.ai",
|
|
1456
|
-
"token": "<compact JWS — iss, sub (userId), org, email, org_name, exp>",
|
|
1457
|
-
"bundle": "<compact JWS — user, org, agentGrants:[{agent,level}], allowedAdapters, exp>",
|
|
1458
|
-
"jwks": { "keys": [ "...public ES256 keys..." ] } }
|
|
1459
|
-
```
|
|
1460
|
-
|
|
1461
|
-
**Step 3 — call the agent with the two headers:**
|
|
1462
|
-
|
|
1463
|
-
```bash
|
|
1464
|
-
TOKEN=... # the "token" field
|
|
1465
|
-
BUNDLE=... # the "bundle" field
|
|
1466
|
-
|
|
1467
|
-
curl -s -i -X POST https://acme-atlas.vercel.app/eve/v1/sessions \
|
|
1468
|
-
-H "authorization: Bearer $TOKEN" \
|
|
1469
|
-
-H "x-kybernesis-bundle: $BUNDLE" \
|
|
1470
|
-
-H 'content-type: application/json' \
|
|
1471
|
-
-d '{"input":"Hello"}'
|
|
1472
|
-
```
|
|
1473
|
-
|
|
1474
|
-
The session streams. Say out loud what just happened: *the agent verified that token
|
|
1475
|
-
offline, against a public key, with no call back to us.*
|
|
1476
|
-
|
|
1477
|
-
**Step 4 — show the failure mode first.** Call the same endpoint with no headers:
|
|
1478
|
-
|
|
1479
|
-
```bash
|
|
1480
|
-
curl -s -o /dev/null -w "%{http_code}\n" -X POST https://acme-atlas.vercel.app/eve/v1/sessions
|
|
1481
|
-
# 401
|
|
1482
|
-
```
|
|
1483
|
-
|
|
1484
|
-
**Step 5 — the revoke.** In the admin UI, revoke that employee's grant for `atlas`. Then
|
|
1485
|
-
have them sign in again (repeat steps 1–2) and call the agent with the **fresh** token:
|
|
1486
|
-
|
|
1487
|
-
```
|
|
1488
|
-
HTTP/1.1 403 Forbidden
|
|
1489
|
-
{"error":"agent_not_granted", ...}
|
|
1490
|
-
```
|
|
1491
|
-
|
|
1492
|
-
**Step 6 — the suspend.** Suspend the user in the admin UI. Try to mint again — the
|
|
1493
|
-
control plane refuses to issue a session at all. This is the off-boarding lever.
|
|
1494
|
-
|
|
1495
|
-
**Step 7 — restore.** Re-activate and re-grant, sign in once more, and show access
|
|
1496
|
-
returning. Leave the client's world as you found it.
|
|
1497
|
-
|
|
1498
|
-
Then state the SLA plainly: *"Suspension is immediate. A revoked employee's already-issued
|
|
1499
|
-
token stops working within the token TTL — one hour by default, tunable."*
|
|
1500
|
-
|
|
1501
|
-
### 8.4 Sign-off checklist
|
|
1502
|
-
|
|
1503
|
-
- [ ] Slack: mention, thread continuation, DM, `/new`
|
|
1504
|
-
- [ ] Memory: store, cross-session recall, delegation, visible in their Arcana workspace
|
|
1505
|
-
- [ ] Governance: 401 → grant → 200 → revoke → 403 → suspend → mint refused → restored
|
|
1506
|
-
- [ ] Health: `<deployment>/eve/v1/health` returns `{"ok":true}` and the control plane's
|
|
1507
|
-
health dot is green
|
|
1508
|
-
- [ ] Evals: `npm run eval` green, running in their CI
|
|
1509
|
-
- [ ] Admins have done an invite, a grant, and a revoke **with their own hands**
|
|
1510
|
-
|
|
1511
|
-
---
|
|
1512
|
-
|
|
1513
|
-
## 9. Handover (Day 5)
|
|
1514
|
-
|
|
1515
|
-
### 9.1 What the client owns and receives
|
|
1516
|
-
|
|
1517
|
-
- **The repo** — in their GitHub org, if they want it. Push it there and add their
|
|
1518
|
-
engineers. It is their source.
|
|
1519
|
-
- **The Vercel project** — theirs already. Confirm their team owns it, not your personal
|
|
1520
|
-
scope, and that at least two of their people have deploy access.
|
|
1521
|
-
- **The Slack app** — installed in their workspace, owned by their admin.
|
|
1522
|
-
- **Their Arcana workspaces** and the scoped keys. Hand these over through a password
|
|
1523
|
-
manager, not Slack.
|
|
1524
|
-
- **Control-plane admin access** — invite, grant, revoke, suspend is their entire
|
|
1525
|
-
operational surface, and after §7.3 they know how to use it.
|
|
1526
|
-
- **The eval suite**, running in their CI.
|
|
1527
|
-
- **Optionally Eve Studio** for employees who do not live in Slack — note that Studio
|
|
1528
|
-
sign-in against the control plane is specced but **not built yet** (§11).
|
|
1529
|
-
|
|
1530
|
-
### 9.2 What Kybernesis keeps doing
|
|
1531
|
-
|
|
1532
|
-
- **Operating the control plane** at `agent.kybernesis.ai` — issuer, keys, grants,
|
|
1533
|
-
audit. (Unless they self-host, in which case we support their instance.)
|
|
1534
|
-
- **Maintaining and versioning the packages** — `@kybernesis/arcana`,
|
|
1535
|
-
`@kybernesis/enterprise`, `@kybernesis/multiplayer`. Version bumps are our work; the
|
|
1536
|
-
client's eval suite is the gate that proves an upgrade is safe.
|
|
1537
|
-
- **Operating Arcana** — the memory SaaS their brains live in.
|
|
1538
|
-
- **Per-client eve version pins** (`eve_agent_deployment.eve_version_pin`) so a framework
|
|
1539
|
-
release never surprises a client mid-quarter.
|
|
1540
|
-
- **The maintenance retainer** — which is what all of the above justifies. Frame it that
|
|
1541
|
-
way in the handover conversation: they own the agent, we own the platform underneath it.
|
|
1542
|
-
|
|
1543
|
-
### 9.3 Leave-behind document
|
|
1544
|
-
|
|
1545
|
-
Write a one-page README in their repo covering: the deployment URL, the control-plane
|
|
1546
|
-
org URL, which Arcana workspace maps to which subagent, the env var list (names only,
|
|
1547
|
-
never values), how to run the evals, and — in bold — **redeploy after every change,
|
|
1548
|
-
because Slack runs the deployed build.**
|
|
1549
|
-
|
|
1550
|
-
---
|
|
1551
|
-
|
|
1552
|
-
## 10. Troubleshooting appendix
|
|
1553
|
-
|
|
1554
|
-
Grouped by where the pain shows up. Every entry here cost someone real time.
|
|
1555
|
-
|
|
1556
|
-
### Environment and deployment
|
|
1557
|
-
|
|
1558
|
-
**My `.env.local` changes disappeared.**
|
|
1559
|
-
`eve deploy` runs `vercel env pull` afterwards and overwrites `.env.local`. The Vercel
|
|
1560
|
-
project environment is the source of truth. Add to Vercel first, then pull.
|
|
1561
|
-
|
|
1562
|
-
**I changed something and Slack still does the old thing.**
|
|
1563
|
-
Slack talks to the **deployed** build. Run `npx eve deploy`. This is the number one
|
|
1564
|
-
support question and it will be yours too.
|
|
1565
|
-
|
|
1566
|
-
**`eve dev` won't start / `eve eval` exits immediately.**
|
|
1567
|
-
Something is already on port 2000 (eve dev's default). Find and kill it:
|
|
1568
|
-
```bash
|
|
1569
|
-
lsof -ti tcp:2000 | xargs kill
|
|
1570
|
-
```
|
|
1571
|
-
`eve eval` boots its own host and exits early if the port is taken.
|
|
1572
|
-
|
|
1573
|
-
**Vercel CLI does nothing useful in a script.**
|
|
1574
|
-
Non-interactive `vercel` calls need the team: add `--scope <team-slug>`. `eve link` is
|
|
1575
|
-
interactive-only; in CI use `vercel link --project <name> --yes --non-interactive`.
|
|
1576
|
-
|
|
1577
|
-
**The control plane's health dot is red but `curl` works for me.**
|
|
1578
|
-
Vercel deployment protection. A protected preview or production deployment rejects the
|
|
1579
|
-
control plane's unauthenticated health probe. Check the project's Deployment Protection
|
|
1580
|
-
settings.
|
|
1581
|
-
|
|
1582
|
-
### Arcana / memory
|
|
1583
|
-
|
|
1584
|
-
**Everything Arcana returns 403.**
|
|
1585
|
-
`kb_` keys are **workspace-scoped**. A key minted for `acme-finance` gets `403` on
|
|
1586
|
-
`acme-company`. Check that the key env var and the workspace env var for that mount refer
|
|
1587
|
-
to the same workspace. Validate in isolation:
|
|
1588
|
-
```bash
|
|
1589
|
-
curl -s -o /dev/null -w "HTTP %{http_code}\n" \
|
|
1590
|
-
-H "Authorization: Bearer kb_..." \
|
|
1591
|
-
-H "X-Kyberagent-Agent: acme-finance" \
|
|
1592
|
-
"https://api.arcana.kybernesis.ai/brain/acme-finance/timeline?limit=1"
|
|
1593
|
-
```
|
|
1594
|
-
|
|
1595
|
-
**Memory tools are missing entirely from a subagent.**
|
|
1596
|
-
Subagents inherit nothing from the root — the root's arcana mount does not reach them.
|
|
1597
|
-
Give the subagent its own local extension mount
|
|
1598
|
-
(`agent/subagents/<id>/extensions/arcana.ts`, eve ≥0.30) or a plain connection file
|
|
1599
|
-
plus skill copies (§4.5).
|
|
1600
|
-
|
|
1601
|
-
**The agent says "I don't have anything stored about that" without looking.**
|
|
1602
|
-
The `@kybernesis/arcana` instructions carry a never-claim-ignorance-without-searching
|
|
1603
|
-
rule and a recall→search escalation rule (an empty entity recall does **not** mean nothing
|
|
1604
|
-
is stored). If you overrode or trimmed the instructions, you dropped those rules. Put
|
|
1605
|
-
them back — an eval caught this exact failure.
|
|
1606
|
-
|
|
1607
|
-
**Arcana MCP tool names don't match the docs.**
|
|
1608
|
-
Tool names are qualified by the mount namespace. Mounted as `agent/extensions/arcana.ts`,
|
|
1609
|
-
`arcana_remember` is addressed as `arcana__memory__arcana_remember`. Run `npx eve info` to
|
|
1610
|
-
see the actual resolved names rather than guessing.
|
|
1611
|
-
|
|
1612
|
-
**Should I use Vercel Connect OAuth for Arcana?**
|
|
1613
|
-
No. Use a static `kb_` key. Connect OAuth works in the `eve dev` TUI and is broken in
|
|
1614
|
-
production (Connect beta: "couldn't find this authorization request"; grants also do not
|
|
1615
|
-
cross environments). This is settled — see the `arcana-eve` skill for the full analysis.
|
|
1616
|
-
|
|
1617
|
-
### Governance / auth
|
|
1618
|
-
|
|
1619
|
-
**A user who definitely has the grant gets `403 agent_not_granted`.**
|
|
1620
|
-
Three causes, in order of likelihood: (1) the agent's registered name in the control plane
|
|
1621
|
-
does not exactly equal `KYBERNESIS_AGENT` in the deployed environment; (2) the grant was
|
|
1622
|
-
added *after* the user signed in and their token predates it — grants resolve at mint
|
|
1623
|
-
time, so refresh or re-sign-in; (3) you are pointing at the wrong org's issuer.
|
|
1624
|
-
|
|
1625
|
-
**Everything returns 401.**
|
|
1626
|
-
Missing, malformed, or expired credentials — or only one of the two headers. Both are
|
|
1627
|
-
required: `authorization: Bearer <token>` **and** `x-kybernesis-bundle: <bundle>`. Also
|
|
1628
|
-
check `KYBERNESIS_ISSUER` matches the issuer that minted the token.
|
|
1629
|
-
|
|
1630
|
-
**A revoked employee can still use the agent.**
|
|
1631
|
-
Expected, for up to the token TTL (1h default). Grants resolve at mint; already-issued
|
|
1632
|
-
tokens are not revoked mid-flight. For immediate cut-off, **suspend** the user — minting
|
|
1633
|
-
refuses entirely. Tune `IDENTITY_TOKEN_TTL_SECONDS` if the client needs a tighter SLA.
|
|
1634
|
-
|
|
1635
|
-
**I can't `curl` my own agent locally.**
|
|
1636
|
-
The governed `agent/channels/eve.ts` drops `localDev()` and `placeholderAuth()` — it fails
|
|
1637
|
-
closed by design. Drive it through the `eve dev` TUI, or mint a real token via the device
|
|
1638
|
-
flow.
|
|
1639
|
-
|
|
1640
|
-
### Slack
|
|
1641
|
-
|
|
1642
|
-
**Slack delivers nothing.**
|
|
1643
|
-
The trigger is still on Connect's default path. eve does not serve that path. Run the
|
|
1644
|
-
detach/attach pair with `--trigger-path /eve/v1/slack` (§5.1).
|
|
1645
|
-
|
|
1646
|
-
**Only @mentions arrive; thread-following doesn't work.**
|
|
1647
|
-
Missing scopes. The connector needs the `message.channels` trigger event and the
|
|
1648
|
-
`channels:history` bot scope (plus `message.groups` / `groups:history` for private
|
|
1649
|
-
channels). `threadContext` needs the same history scopes.
|
|
1650
|
-
|
|
1651
|
-
**The agent replies to itself, or to other bots.**
|
|
1652
|
-
eve drops messages authored by the installed app before your hook runs, but *other* bots
|
|
1653
|
-
are still visible. The multiplayer package filters `message.author?.isBot`; if you hand-
|
|
1654
|
-
rolled a hook, do the same.
|
|
1655
|
-
|
|
1656
|
-
**A scheduled DM never arrives.**
|
|
1657
|
-
The connector needs `im:write`. The schedule otherwise runs fine and fails at the last
|
|
1658
|
-
step, which makes it look like the schedule did not fire.
|
|
1659
|
-
|
|
1660
|
-
### Evals
|
|
1661
|
-
|
|
1662
|
-
**Evals pass locally then fail identically twice in a row after a fix.**
|
|
1663
|
-
eve caches compiled eval modules across runs. Avoid module-level nonces (no
|
|
1664
|
-
`const id = Date.now()` at module scope) — generate per-run values inside `test()`.
|
|
1665
|
-
|
|
1666
|
-
**A routing eval times out.**
|
|
1667
|
-
Delegation does real memory work in the subagent. Routing evals need roughly six-minute
|
|
1668
|
-
timeouts. Set `timeoutMs` in `evals.config.ts` or pass `--timeout`.
|
|
1669
|
-
|
|
1670
|
-
**An eval fails because the agent refused.**
|
|
1671
|
-
Certain phrasings trip the model's own safety behavior — "canary codeword" reads as a
|
|
1672
|
-
secret-extraction attempt. Use neutral wording ("project codename").
|
|
1673
|
-
|
|
1674
|
-
**Evals wrote test data into the real brain.**
|
|
1675
|
-
The `eval` npm script must override **every** Arcana workspace env var to `acme-eval`.
|
|
1676
|
-
Miss one and that subagent writes into production memory. Check the script against your
|
|
1677
|
-
actual list of subagents.
|
|
1678
|
-
|
|
1679
|
-
**`npm run eval` looked green in CI but the job passed when it shouldn't have.**
|
|
1680
|
-
Piping eval output to `tail` (or anything else) masks the exit code. Use
|
|
1681
|
-
`set -o pipefail`, or don't pipe.
|
|
1682
|
-
|
|
1683
|
-
### Packages and the registry
|
|
1684
|
-
|
|
1685
|
-
**`eve add @kybernesis/...` 404s.**
|
|
1686
|
-
Either the registry namespace is not registered in this project (`eve registry add
|
|
1687
|
-
@kybernesis=https://registry.kybernesis.ai/r/{name}.json`), or that item is not published
|
|
1688
|
-
yet. Check what actually exists with `npx eve registry list --registry @kybernesis`, or
|
|
1689
|
-
hit the item URL directly: `curl -s -o /dev/null -w "%{http_code}\n"
|
|
1690
|
-
https://registry.kybernesis.ai/r/multiplayer.json`.
|
|
1691
|
-
|
|
1692
|
-
**`eve add` resolved the item but the npm install failed.**
|
|
1693
|
-
The registry item and the npm package are published separately, and the registry item can
|
|
1694
|
-
land first. That is exactly the state `@kybernesis/multiplayer` is in as of 2026-08-05.
|
|
1695
|
-
`npm view @kybernesis/<name> version` tells you which half is missing.
|
|
1696
|
-
|
|
1697
|
-
**`npm install @kybernesis/<something>` says the version doesn't exist, right after publish.**
|
|
1698
|
-
New packages and versions take one to three minutes to propagate to anonymous reads, even
|
|
1699
|
-
after `npm access` reports them public. Wait, then retry.
|
|
1700
|
-
|
|
1701
|
-
**Publishing a new package in the `@kybernesis` scope fails.**
|
|
1702
|
-
Only the `kybernesis` npm account can **create** packages in the scope; `ianborders` can
|
|
1703
|
-
publish new versions of existing ones. Publishing needs Ian's browser auth (npm web-login
|
|
1704
|
-
flow). This is a Kybernesis-internal step, not something to do at a client site.
|
|
1705
|
-
|
|
1706
|
-
**A published package imports fine locally but breaks on clean install.**
|
|
1707
|
-
`tsc` does not rewrite import specifiers. An extensionless ESM re-export in `dist/` works
|
|
1708
|
-
in a workspace and fails from the registry. `@kybernesis/enterprise@0.1.0` shipped with
|
|
1709
|
-
exactly this bug; `0.1.1` fixed it with explicit `.js` extensions. Always test a package
|
|
1710
|
-
change with a **clean install into a scratch project**, never only from the workspace.
|
|
1711
|
-
|
|
1712
|
-
**A script's JSON output is polluted with warnings.**
|
|
1713
|
-
pnpm writes engine warnings to stdout. Pipe through `pnpm --silent` and, if needed,
|
|
1714
|
-
`sed -n '/^{/,$p'` to strip everything before the first JSON line. (This mostly bites in
|
|
1715
|
-
the control-plane repo's seed scripts.)
|
|
1716
|
-
|
|
1717
|
-
### General
|
|
1718
|
-
|
|
1719
|
-
**Something isn't being picked up and I can't see why.**
|
|
1720
|
-
`npx eve info` first, always. It prints exactly what eve discovered plus diagnostics, and
|
|
1721
|
-
it is far faster than booting the dev server. `npx eve logs` reads the last `eve dev`
|
|
1722
|
-
diagnostic log if you need stderr, tool failures, and rebuild lines.
|
|
1723
|
-
|
|
1724
|
-
### Self-hosted hosts (exe.dev / client infrastructure)
|
|
1725
|
-
|
|
1726
|
-
**I deployed a change and the agent still does the old thing.**
|
|
1727
|
-
The restart did not take. Check that the running process started *after* the build
|
|
1728
|
-
(`scripts/eve-server.sh` asserts this and fails loudly). A silently-failed restart is the
|
|
1729
|
-
single most expensive failure mode off Vercel, because every test you run afterwards
|
|
1730
|
-
measures the previous build. If the process is current, start a **fresh conversation** —
|
|
1731
|
-
a long-lived channel session caches the compiled agent.
|
|
1732
|
-
|
|
1733
|
-
**Every sandbox tool fails with `SandboxTemplateNotProvisionedError`.**
|
|
1734
|
-
Either Docker is installed but disabled (`sudo systemctl enable --now docker` — exeuntu
|
|
1735
|
-
ships it disabled), or the server was started as `node .output/server/index.mjs`, which
|
|
1736
|
-
skips the CLI's template prewarm. Start with `npx eve start`.
|
|
1737
|
-
|
|
1738
|
-
**The subagent's screenshots fail with `Cannot find module 'playwright'` but the root
|
|
1739
|
-
agent is fine.**
|
|
1740
|
-
Subagents own their sandbox; they do not inherit the root's. Give the subagent its own
|
|
1741
|
-
`sandbox/sandbox.ts`.
|
|
1742
|
-
|
|
1743
|
-
**My env vars are missing under `eve start`.**
|
|
1744
|
-
`eve start` does not read `.env.local` the way `eve dev` does. Use
|
|
1745
|
-
`scripts/eve-server.sh`, which exports it into the process.
|
|
1746
|
-
|
|
1747
|
-
**I killed the agent and my own SSH session at the same time.**
|
|
1748
|
-
`pkill -f <pattern>` matches the SSH command line that contains the pattern. Keep kill
|
|
1749
|
-
patterns inside a script on the host; never pass them on an `ssh` command line.
|
|
1750
|
-
|
|
1751
|
-
**A sandbox VM boots and shows `running`, but every command answers "command not found".**
|
|
1752
|
-
SSH is landing on exe.dev's lobby REPL rather than the VM. exe.dev reuses VM names, so a
|
|
1753
|
-
name deleted and recreated within a few minutes can keep resolving to the lobby, and a
|
|
1754
|
-
stale `known_hosts` entry does the same thing. Use a fresh name, and never reuse the
|
|
1755
|
-
caller's `known_hosts` for sandbox VMs.
|
|
1756
|
-
|
|
1757
|
-
**A command works when I SSH in by hand but not from the agent.**
|
|
1758
|
-
A non-interactive SSH command does not source the login profile, so toolchains installed
|
|
1759
|
-
onto the profile PATH (nvm, pyenv, cargo) are invisible. Run through `bash -lc` — but not
|
|
1760
|
-
for file I/O, whose stdout must stay byte-clean.
|
|
1761
|
-
|
|
1762
|
-
**exe.dev refuses to run commands with my API token.**
|
|
1763
|
-
It cannot. A key registered through an API token inherits that token's command scope, and
|
|
1764
|
-
shell exec is not a scoped command. Running commands needs a full-permission account key;
|
|
1765
|
-
see 11.6 for the isolation this demands.
|
|
1766
|
-
|
|
1767
|
-
### Third-party APIs and connections
|
|
1768
|
-
|
|
1769
|
-
**The agent reports that a service is down or that a resource "isn't shared" with it.**
|
|
1770
|
-
Verify before you believe it, and never change the client's permissions on an agent's
|
|
1771
|
-
say-so. An agent's error message is a hypothesis, not evidence — one real case reported an
|
|
1772
|
-
outage, then a permissions problem, while the data was reachable the entire time and the
|
|
1773
|
-
actual cause was a version header. Read the request and response yourself.
|
|
1774
|
-
|
|
1775
|
-
**My manual `curl` works but the agent's identical call fails.**
|
|
1776
|
-
It is not identical — and the difference between the two requests *is* the bug. Diff them
|
|
1777
|
-
at the first contradiction rather than proving again that the token works. A version
|
|
1778
|
-
header, a content type, or a base URL differing by one path segment will do it.
|
|
1779
|
-
|
|
1780
|
-
**Calls derived from a large OpenAPI spec fail in ways that make no sense.**
|
|
1781
|
-
Specs with ambiguous ID schemes (Notion's `database_id` vs `data_source_id` for the same
|
|
1782
|
-
object) make the model pick wrong, and the API's errors describe a different problem.
|
|
1783
|
-
Write a small purpose-built tool with the endpoint and IDs pinned; keep the generic
|
|
1784
|
-
connection for the long tail.
|
|
1785
|
-
|
|
1786
|
-
---
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
## 11. Self-hosted deployments (when the client will not use Vercel)
|
|
1790
|
-
|
|
1791
|
-
Vercel is the default path and the proven one. Take this track when the client
|
|
1792
|
-
**cannot or will not** put the agent on Vercel — procurement, data residency, an
|
|
1793
|
-
existing VM estate — or when they want inference billed to a subscription they
|
|
1794
|
-
already pay for rather than to a gateway.
|
|
1795
|
-
|
|
1796
|
-
Everything else in this playbook still applies. This section covers only what
|
|
1797
|
-
changes.
|
|
1798
|
-
|
|
1799
|
-
**The governing rule: every credential comes from the CLIENT's accounts.** If a
|
|
1800
|
-
step works only because you happen to hold a token, that step is a bug in the
|
|
1801
|
-
deployment, not a shortcut — it will fail on the real engagement, in front of
|
|
1802
|
-
the client. Assume you have no Vercel connection, no blob store, and no API
|
|
1803
|
-
keys of your own.
|
|
1804
|
-
|
|
1805
|
-
### 11.1 Scaffold
|
|
1806
|
-
|
|
1807
|
-
```bash
|
|
1808
|
-
kyb init <name> --host=exe --channel=<imessage|slack|telegram|none> --engineer
|
|
1809
|
-
cd <name> && kyb doctor
|
|
1810
|
-
```
|
|
1811
|
-
|
|
1812
|
-
`--host=exe` swaps the host bindings; everything else is the same product —
|
|
1813
|
-
same memory, same evals, same control-plane wiring, same engineer layer.
|
|
1814
|
-
`kyb doctor` knows every failure mode below and fails loudly on each.
|
|
1815
|
-
|
|
1816
|
-
### 11.2 What Vercel gives you that a client host does not
|
|
1817
|
-
|
|
1818
|
-
| Capability | On Vercel | Self-hosted replacement |
|
|
1819
|
-
| --- | --- | --- |
|
|
1820
|
-
| Model access | AI Gateway | exe.dev LLM integration (`exeModel`) — managed, BYO key, or a **ChatGPT subscription** |
|
|
1821
|
-
| Slack / Photon / Linear credentials | Vercel Connect | **static credentials the client issues** |
|
|
1822
|
-
| Sandbox | `vercel()` | `docker()` on the host, or `exeSandbox()` per-task VMs |
|
|
1823
|
-
| File delivery | Vercel Blob | Blob **or** `DELIVER_DIR` + `DELIVER_BASE_URL` |
|
|
1824
|
-
| Public URLs | deployments | a deploy target, or an account-gated preview |
|
|
1825
|
-
| Secrets | Vercel env | host env + the platform's own secret injection |
|
|
1826
|
-
|
|
1827
|
-
**Vercel Connect does not work off-Vercel — at all.** It authenticates via
|
|
1828
|
-
Vercel OIDC, which does not exist on another host. That applies to Slack, the
|
|
1829
|
-
Vercel MCP connection, Linear, everything. Each becomes a static credential
|
|
1830
|
-
someone must issue and rotate, and `kyb doctor` fails if a `@vercel/connect`
|
|
1831
|
-
import survives into a self-hosted agent.
|
|
1832
|
-
|
|
1833
|
-
### 11.3 Host setup (exe.dev)
|
|
1834
|
-
|
|
1835
|
-
```bash
|
|
1836
|
-
ssh exe.dev new --name <agent>
|
|
1837
|
-
ssh exe.dev share port <agent> 8000 && ssh exe.dev share set-public <agent>
|
|
1838
|
-
```
|
|
1839
|
-
|
|
1840
|
-
Make the host public **before** registering any webhook — webhooks need
|
|
1841
|
-
anonymous access, and a provider that fails verification at registration time
|
|
1842
|
-
usually will not tell you why.
|
|
1843
|
-
|
|
1844
|
-
Then, on the VM: Node 24, `npm ci`, `npx eve build`, and start through
|
|
1845
|
-
`scripts/eve-server.sh` from `@kybernesis/exe`.
|
|
1846
|
-
|
|
1847
|
-
Three things that will cost you an afternoon if you skip them:
|
|
1848
|
-
|
|
1849
|
-
- **`eve start` does not read `.env.local`** the way `eve dev` does. The
|
|
1850
|
-
supervision script exports it into the process for you.
|
|
1851
|
-
- **Start via `npx eve start`, not `node .output/server/index.mjs`.** Sandbox
|
|
1852
|
-
template prewarm lives in the CLI, not the built server. Starting the server
|
|
1853
|
-
directly gives you cleaner logs and no prewarm, so every sandbox tool then
|
|
1854
|
-
fails with `SandboxTemplateNotProvisionedError`.
|
|
1855
|
-
- **Docker ships disabled on some images**, exeuntu among them. `docker
|
|
1856
|
-
--version` answers happily while nothing can actually run. Fix with `sudo
|
|
1857
|
-
systemctl enable --now docker`.
|
|
1858
|
-
|
|
1859
|
-
### 11.4 Model: billing inference to the client's subscription
|
|
1860
|
-
|
|
1861
|
-
exe.dev brokers the model, so no provider key sits on the host:
|
|
1862
|
-
|
|
1863
|
-
```bash
|
|
1864
|
-
ssh exe.dev integrations setup chatgpt --name work # once, device-code flow
|
|
1865
|
-
ssh exe.dev integrations edit llm --openai=chatgpt --openai-account=work
|
|
1866
|
-
```
|
|
1867
|
-
|
|
1868
|
-
Then `exeModel()` from `@kybernesis/exe` points the agent at it. A Codex-backed
|
|
1869
|
-
subscription requires `store: false` on every request; `exeModel` forces it.
|
|
1870
|
-
|
|
1871
|
-
This is the single biggest commercial difference in the self-hosted track: a
|
|
1872
|
-
client with an existing ChatGPT or Claude subscription pays no incremental
|
|
1873
|
-
inference cost for the pilot. Say the number out loud in the discovery
|
|
1874
|
-
conversation — it changes the shape of the deal.
|
|
1875
|
-
|
|
1876
|
-
**Grok, on a SuperGrok or X Premium+ subscription.** Same arrangement, without
|
|
1877
|
-
the broker: xAI's Grok Build CLI does a device login and writes a credential
|
|
1878
|
-
that is a valid bearer for `https://api.x.ai/v1`. Proven in production on Sid —
|
|
1879
|
-
twelve evals, twenty-nine gates, green on the subscription.
|
|
1880
|
-
|
|
1881
|
-
```bash
|
|
1882
|
-
# on the host, as the unix user the agent runs as
|
|
1883
|
-
curl -fsSL https://x.ai/cli/install.sh | bash
|
|
1884
|
-
grok login # device flow → ~/.grok/auth.json
|
|
1885
|
-
```
|
|
1886
|
-
|
|
1887
|
-
```ts title="agent/agent.ts"
|
|
1888
|
-
import { createOpenAI } from "@ai-sdk/openai";
|
|
1889
|
-
import { grokSubscription } from "@kybernesis/exe";
|
|
1890
|
-
|
|
1891
|
-
export default defineAgent({
|
|
1892
|
-
model: grokSubscription({ model: "grok-4.6", createOpenAI }),
|
|
1893
|
-
modelContextWindowTokens: 400_000,
|
|
1894
|
-
});
|
|
1895
|
-
```
|
|
1896
|
-
|
|
1897
|
-
Three things to know before you promise it to a client:
|
|
1898
|
-
|
|
1899
|
-
- The credential is **per-machine and per-user**. It lives in a home directory.
|
|
1900
|
-
A different unix user cannot see it; a new host needs its own login.
|
|
1901
|
-
- It **expires in six hours** and the CLI refreshes it in place, so the agent
|
|
1902
|
-
must re-read the file per request. `grokSubscription` does this in a `fetch`
|
|
1903
|
-
wrapper. (Do not reach for a Proxy around the model object — the AI SDK's
|
|
1904
|
-
methods depend on their own `this` and every call dies inside the SDK.)
|
|
1905
|
-
- **Unattended refresh over days is unverified.** If nobody runs `grok` on that
|
|
1906
|
-
host for a week, it is an open question, and it would present to the client as
|
|
1907
|
-
the agent breaking for no reason.
|
|
1908
|
-
|
|
1909
|
-
**The model will lie about which model it is.** Sid, running Grok, stated it was
|
|
1910
|
-
"Claude Opus 4.6, Anthropic" and attributed it to an instruction that exists
|
|
1911
|
-
nowhere in its context. Verify from the host — the configured model id and the
|
|
1912
|
-
credential in use — never by asking the agent. Expect a client to ask it in a
|
|
1913
|
-
demo, and have the real answer ready.
|
|
1914
|
-
|
|
1915
|
-
### 11.5 Third-party APIs: broker the credential, pin the version
|
|
1916
|
-
|
|
1917
|
-
Do not put a client's API token on the agent host. Put it in an exe.dev
|
|
1918
|
-
http-proxy integration, which injects headers server-side:
|
|
1919
|
-
|
|
1920
|
-
```bash
|
|
1921
|
-
ssh exe.dev "integrations add http-proxy --name notion \
|
|
1922
|
-
--target https://api.notion.com \
|
|
1923
|
-
--header 'Authorization:Bearer <token>' \
|
|
1924
|
-
--header 'Notion-Version:2025-09-03' \
|
|
1925
|
-
--attach vm:<agent>"
|
|
1926
|
-
```
|
|
1927
|
-
|
|
1928
|
-
The agent then calls `https://notion.int.exe.xyz` with no credential at all, and
|
|
1929
|
-
rotation happens in one place the client controls.
|
|
1930
|
-
|
|
1931
|
-
Two rules here, both learned expensively:
|
|
1932
|
-
|
|
1933
|
-
**Pin the API version the SPEC describes, not the one in a doc example.** A
|
|
1934
|
-
version header copied from documentation while the agent's OpenAPI spec
|
|
1935
|
-
describes a newer API produces errors that describe the wrong problem entirely.
|
|
1936
|
-
One real case cost most of a day: `Notion-Version: 2022-06-28` against a spec
|
|
1937
|
-
using `/v1/data_sources/...` returned `invalid_request_url`, intermittent 503s
|
|
1938
|
-
from search, and "not shared with the integration" — three different messages,
|
|
1939
|
-
none of them about the version mismatch, two of which look like a permissions
|
|
1940
|
-
or availability problem you can waste hours "fixing" on the client's side.
|
|
1941
|
-
|
|
1942
|
-
**Use `--header` for the token, never `--bearer=-`.** The stdin form mangles the
|
|
1943
|
-
value and the API answers 401 "token is invalid".
|
|
1944
|
-
|
|
1945
|
-
**Large specs with ambiguous ID schemes need a purpose-built tool, not a raw
|
|
1946
|
-
connection.** Notion's spec is ~1.2MB and splits `database_id` from
|
|
1947
|
-
`data_source_id` for the same object; a model deriving calls from it picks
|
|
1948
|
-
wrong. Pin the endpoint and the IDs in a small tool under `agent/tools/`, keep
|
|
1949
|
-
the generic connection for the long tail, and point the instructions at the
|
|
1950
|
-
tool. Reads went from unreliable to deterministic with about sixty lines.
|
|
1951
|
-
|
|
1952
|
-
### 11.6 The engineer layer, self-hosted
|
|
1953
|
-
|
|
1954
|
-
`--engineer` scaffolds a **builder subagent** that owns the build capability, so
|
|
1955
|
-
the root agent never gets a shell. It comes with the full production loop —
|
|
1956
|
-
workshop sandbox, Playwright, screenshots, visual verification, delivery — not a
|
|
1957
|
-
reduced version of the Vercel one.
|
|
1958
|
-
|
|
1959
|
-
- **Subagents own their sandbox; they do NOT inherit the root's.** A builder
|
|
1960
|
-
without its own `sandbox/sandbox.ts` gets a bare template and every screenshot
|
|
1961
|
-
fails with `Cannot find module 'playwright'` while the root's template is
|
|
1962
|
-
fine. `kyb doctor` checks for this.
|
|
1963
|
-
- Conversely, if the root agent runs no code, it needs **no** root sandbox.
|
|
1964
|
-
Leaving one there costs a full template prewarm on every deploy for nothing.
|
|
1965
|
-
|
|
1966
|
-
**Choosing a sandbox backend:**
|
|
1967
|
-
|
|
1968
|
-
`docker()` on the agent's own VM is the default and the safer choice. Reach for
|
|
1969
|
-
`exeSandbox()` — a whole exe.dev VM per session, with Docker available *inside*
|
|
1970
|
-
it and its own public URL — only when the work needs real isolation or
|
|
1971
|
-
per-session compute.
|
|
1972
|
-
|
|
1973
|
-
`exeSandbox()` carries one hard constraint you must raise with the client before
|
|
1974
|
-
choosing it: **it needs a full-permission account SSH key.** exe.dev keys
|
|
1975
|
-
registered through an API token inherit that token's command scope and cannot
|
|
1976
|
-
open a shell at all, so there is no scoped credential that can run commands. The
|
|
1977
|
-
key it does need grants shell to **every VM on that exe.dev account**. The
|
|
1978
|
-
backend therefore refuses to start unless the account is dedicated to this agent
|
|
1979
|
-
(its own VM plus its sandboxes), naming any foreign VMs it finds. Overriding
|
|
1980
|
-
that with `allowSharedAccount: true` is a decision for the client to make in
|
|
1981
|
-
writing, not for you to make on their behalf. VM lifecycle stays on a separately
|
|
1982
|
-
scoped token that cannot exec, so neither credential is sufficient alone.
|
|
1983
|
-
|
|
1984
|
-
### 11.7 Showing the client what the agent built
|
|
1985
|
-
|
|
1986
|
-
- **Vercel Blob refuses to serve HTML inline** — it forces a download. Use it
|
|
1987
|
-
for documents and exports, never to show a web page.
|
|
1988
|
-
- **exe.dev forwards ports 3000–9999** to `https://<vm>.exe.xyz:<port>/`, but a
|
|
1989
|
-
VM has exactly **one public port** and the agent's webhook already owns it.
|
|
1990
|
-
Alternate ports are account-gated: fine for the client reviewing work, not for
|
|
1991
|
-
the public.
|
|
1992
|
-
- **Anything genuinely public needs a deploy target** — the client's own Vercel
|
|
1993
|
-
token, or their hosting. Treat "public" as a deploy step, not a toggle, and
|
|
1994
|
-
price it into the scope.
|
|
1995
|
-
- A sandbox is a container: its ports are not reachable from the host, so a dev
|
|
1996
|
-
server inside it cannot be previewed directly. Copy the artifact out (the
|
|
1997
|
-
`preview` tool in `@kybernesis/exe`) or deploy it.
|
|
1998
|
-
|
|
1999
|
-
### 11.8 Prove the restart, every time
|
|
2000
|
-
|
|
2001
|
-
There is no deployment pipeline here to tell you a release landed. A restart
|
|
2002
|
-
that silently fails leaves the agent serving a stale build — new connections,
|
|
2003
|
-
tools, and instructions never appear — and **every test you run afterwards
|
|
2004
|
-
measures yesterday's agent.** One session lost an hour to exactly this.
|
|
2005
|
-
|
|
2006
|
-
`scripts/eve-server.sh` asserts the running process started *after* the build it
|
|
2007
|
-
should be serving, and fails loudly when it did not. Use it rather than
|
|
2008
|
-
`pkill` + `npx eve start` by hand. Related: `pkill -f <pattern>` over SSH kills
|
|
2009
|
-
your own session when the pattern appears in the SSH command line — and can take
|
|
2010
|
-
the agent down with it. Use the pidfile.
|
|
2011
|
-
|
|
2012
|
-
Also: a long-lived channel session caches the compiled agent. After changing
|
|
2013
|
-
capabilities, start a **fresh conversation** before deciding the change did not
|
|
2014
|
-
work.
|
|
2015
|
-
|
|
2016
|
-
**A restart script must do two more things, and both were learned from a
|
|
2017
|
-
stranded user.**
|
|
2018
|
-
|
|
2019
|
-
*Serialize restarts.* `@kybernesis/manage` fires one 20s after any change, and
|
|
2020
|
-
you will also run one by hand. Two overlapping runs both finish killing before
|
|
2021
|
-
either starts, and you end up with **two supervisors and two servers writing to
|
|
2022
|
-
one durable store** — two executors racing over the same runs. That is not a
|
|
2023
|
-
slow agent, it is a corrupt one. Take a `flock` at the top of the script, and
|
|
2024
|
-
assert exactly one server process at the bottom.
|
|
2025
|
-
|
|
2026
|
-
*Wait for in-flight turns.* eve does **not** resume a step killed mid-flight.
|
|
2027
|
-
Restart into a live turn and that turn never emits another event, the session
|
|
2028
|
-
never parks, and every later message queues behind a turn that will never
|
|
2029
|
-
finish. The user watches a spinner forever, and no further restart fixes it,
|
|
2030
|
-
because the session is stranded rather than stuck. Poll
|
|
2031
|
-
`.eve/.workflow-data/runs/*.json` for a `turnWorkflow` in `running` state and
|
|
2032
|
-
wait for it to clear — with a cap, so a wedged turn cannot block the restart
|
|
2033
|
-
that would clear it.
|
|
2034
|
-
|
|
2035
|
-
*And build before you restart.* Proving the process started after the BUILD says
|
|
2036
|
-
nothing about whether the build reflects the SOURCE. A production agent ran for a
|
|
2037
|
-
day on a build ten hours older than its files, reporting "OK: serving the current
|
|
2038
|
-
build" every time. Worse, `@kybernesis/manage` calls the restart script after
|
|
2039
|
-
writing files — so **every capability installed from Studio reported success and
|
|
2040
|
-
changed nothing.** Build when the source has moved, and refuse to restart into a
|
|
2041
|
-
build that failed.
|
|
2042
|
-
|
|
2043
|
-
*And measure it correctly.* `pgrep -f 'server/index.mjs'` run over ssh matches
|
|
2044
|
-
**the shell running the pgrep** — the pattern is in its own command line — so it
|
|
2045
|
-
reports two servers when there is one. An entire investigation went into hunting
|
|
2046
|
-
a phantom supervisor that a `ps -eo pid,ppid,args` would have dismissed in
|
|
2047
|
-
thirty seconds. Same family as `pkill -f` killing its caller. Inside a script
|
|
2048
|
-
file it is safe (the script's command line is `bash restart.sh`); typed at a
|
|
2049
|
-
shell or sent over ssh it is not. When a process count surprises you, **list the
|
|
2050
|
-
matches before believing the number.**
|
|
2051
|
-
|
|
2052
|
-
The escape from an already-stranded session is a **session reset**
|
|
2053
|
-
(`ClientSession.reset()`, or Reset in Studio's agent settings), which releases
|
|
2054
|
-
the durable owner so the next message starts a fresh conversation. Cancelling
|
|
2055
|
-
often does not help: the executor that would honour the cancel is the one that
|
|
2056
|
-
died.
|
|
2057
|
-
|
|
2058
|
-
### 11.9 Credential checklist — collect ALL of these from the client
|
|
2059
|
-
|
|
2060
|
-
Nothing here can be borrowed from another agent or another account.
|
|
2061
|
-
|
|
2062
|
-
1. **Host** — VM/server, plus the platform token if the agent provisions anything
|
|
2063
|
-
2. **Model source** — their LLM API key, gateway allocation, or connected subscription
|
|
2064
|
-
3. **Channel app** — their Slack app (bot + app token) / Photon project / bot token
|
|
2065
|
-
4. **Arcana** — workspaces + scoped `kb_` keys (one per brain, plus `-eval`)
|
|
2066
|
-
5. **Storage for deliverables** — their blob store, or a served host directory
|
|
2067
|
-
6. **Deploy target** — their Vercel token or hosting, if the agent ships sites
|
|
2068
|
-
7. **Sandbox credentials** — only if using `exeSandbox()` (see 11.6)
|
|
2069
|
-
8. **Control plane** — agent registered and the pilot cohort granted
|
|
2070
|
-
|
|
2071
|
-
### 11.10 Before calling it done
|
|
2072
|
-
|
|
2073
|
-
`kyb doctor` green (or every warning consciously accepted), the eval suite green
|
|
2074
|
-
against the client's `-eval` workspace, and a live turn on the real surface —
|
|
2075
|
-
sent from the client's own device, not yours.
|
|
2076
|
-
|
|
2077
|
-
## 12. KYBER Studio — the desktop surface
|
|
2078
|
-
|
|
2079
|
-
Slack and iMessage reach an agent where the client already works. KYBER Studio
|
|
2080
|
-
is the third door: a desktop app for people who do not live in a chat tool, and
|
|
2081
|
-
the only surface where an agent can work on the user's own files.
|
|
2082
|
-
|
|
2083
|
-
Reach for it when the client says any of: *"not everyone here uses Slack"*,
|
|
2084
|
-
*"I want it on my laptop"*, *"can it look at our repo"*, or when the pilot
|
|
2085
|
-
involves someone technical who will hand the agent real work.
|
|
2086
|
-
|
|
2087
|
-
### 12.1 What it is
|
|
2088
|
-
|
|
2089
|
-
- **The same agent.** Studio does not run anything. It talks to the agent you
|
|
2090
|
-
deployed — same memory, same tools, same subagents. Nothing to deploy twice.
|
|
2091
|
-
- **Governed by the same grants.** Sign-in is control-plane device flow, so
|
|
2092
|
-
desktop access is the grant you already manage. Revoke it and the desktop goes
|
|
2093
|
-
with it.
|
|
2094
|
-
- **Optionally hands and eyes.** With `@kybernesis/local` the agent can search,
|
|
2095
|
-
read, edit, write, and run commands on the user's machine, with consent.
|
|
2096
|
-
- **Optionally self-modifying.** With `@kybernesis/manage` the client can
|
|
2097
|
-
install capabilities and write routines from the app instead of asking you.
|
|
2098
|
-
|
|
2099
|
-
### 12.2 The two packages, and why they are separate
|
|
2100
|
-
|
|
2101
|
-
| | What it lets happen | Installed on |
|
|
2102
|
-
| --- | --- | --- |
|
|
2103
|
-
| `@kybernesis/local` | The agent acts on the USER's machine | the agent |
|
|
2104
|
-
| `@kybernesis/manage` | A client changes THE AGENT — deps and source | the agent |
|
|
2105
|
-
|
|
2106
|
-
Different blast radius, so they are separate items an engagement chooses
|
|
2107
|
-
independently. A reporting agent might want `local` and never `manage`. Neither
|
|
2108
|
-
is installed by default, because both let a client reach further than chat does.
|
|
2109
|
-
|
|
2110
|
-
```bash
|
|
2111
|
-
kyb init acme-agent --host=exe --studio # both, at scaffold time
|
|
2112
|
-
npx eve add local # or either one, later
|
|
2113
|
-
npx eve add manage
|
|
2114
|
-
```
|
|
2115
|
-
|
|
2116
|
-
`kyb doctor` checks both: the relay secret for local, and `KYBERNESIS_AGENT` for
|
|
2117
|
-
manage, since it cannot check a grant for a name it does not know.
|
|
2118
|
-
|
|
2119
|
-
### 12.3 Prerequisites, in order
|
|
2120
|
-
|
|
2121
|
-
1. **The agent is registered in the control plane** and the pilot users are
|
|
2122
|
-
granted. Studio lists exactly what a user has a grant for — an agent that is
|
|
2123
|
-
registered but ungranted is invisible, which is the correct behaviour and a
|
|
2124
|
-
confusing one if you forget you did it.
|
|
2125
|
-
2. **The agent has a URL on file.** Studio reads `/api/me/agents`; an agent with
|
|
2126
|
-
no deployment URL appears as unreachable rather than silently missing.
|
|
2127
|
-
3. **For `manage`: a writable working copy.** Installing edits the repo and
|
|
2128
|
-
rebuilds, so it works on a VM and refuses on a read-only serverless bundle,
|
|
2129
|
-
with that reason. Set `restartCommand` in `agent/channels/kyb.ts` or an
|
|
2130
|
-
install completes without taking effect.
|
|
2131
|
-
4. **For `local`: nothing to configure.** Setup is one switch in Studio — the
|
|
2132
|
-
agent's settings, *Work on this computer*. Behind it, Studio mints the
|
|
2133
|
-
agent's credential from the control plane, installs it over the manage
|
|
2134
|
-
channel, and records a standing grant for that machine; the agent restarts
|
|
2135
|
-
once to load it. Never hand anyone a credential to paste into an env file.
|
|
2136
|
-
The admin UI's "mint agent credential (shown once)" button remains for
|
|
2137
|
-
recovery and is not the path: a setup step that asks someone to carry a
|
|
2138
|
-
secret between two screens gets done wrong or skipped.
|
|
2139
|
-
|
|
2140
|
-
### 12.4 What consent looks like for the user
|
|
2141
|
-
|
|
2142
|
-
Studio asks per **effect** — run a command, read a file, write a file, list a
|
|
2143
|
-
directory — not per tool, and not per turn. Approving `read-file` once covers
|
|
2144
|
-
every tool that reads a file out, which is why adding a tool later cannot dodge
|
|
2145
|
-
a decision the user already made.
|
|
2146
|
-
|
|
2147
|
-
The default is ask. A working folder can be set, but it is a starting directory
|
|
2148
|
-
rather than a fence: permission to act on the machine is granted once, and the
|
|
2149
|
-
agent may work wherever it is asked to. Whether it builds in its own sandbox or
|
|
2150
|
-
on the user's files is decided by the ask, not by a mode — the same way a
|
|
2151
|
-
colleague knows "build me a demo" from "look at my repo".
|
|
2152
|
-
|
|
2153
|
-
### 12.5 State this plainly to the client
|
|
2154
|
-
|
|
2155
|
-
- **Two things gate a laptop, and they fail differently.** *Identity* is the
|
|
2156
|
-
agent's signed credential — "the local-execution relay rejected my
|
|
2157
|
-
credentials" means that. *Consent* is a standing per-device grant — "you have
|
|
2158
|
-
not allowed this agent to work on this computer" means that. Neither alone
|
|
2159
|
-
reaches anything. The grant is permanent on purpose: "always allow" means
|
|
2160
|
-
always, from a chat window, a schedule, or a message sent from a phone, and it
|
|
2161
|
-
ends on revoke, device removal, or disabling the agent.
|
|
2162
|
-
- **Reaching a desktop is still not its own revocable capability.** "May talk to
|
|
2163
|
-
this agent" and "may run commands on my laptop" remain one decision, taken
|
|
2164
|
-
when the person allows the machine. Say so at a client who would treat it as a
|
|
2165
|
-
surprise.
|
|
2166
|
-
- **Installing the credential restarts the agent**, and a turn in flight during
|
|
2167
|
-
that restart is lost for good — eve does not resume a step killed mid-flight.
|
|
2168
|
-
It reads as a spinner that never resolves, often alongside a "credential is
|
|
2169
|
-
unset" error from the process that was replaced. Send a new message, and reset
|
|
2170
|
-
the conversation if the session itself is stranded. §11.8 is why a restart
|
|
2171
|
-
script must wait for in-flight turns.
|
|
2172
|
-
- **Reading a file sends it to the model.** Execution is local; the reasoning is
|
|
2173
|
-
not. Fine for most work, and a conversation to have before a Studio points at
|
|
2174
|
-
a regulated repository.
|
|
2175
|
-
- **Management routes let a client change the agent.** That is the point, and it
|
|
2176
|
-
means the repository is no longer only yours. Agree who reviews what Studio
|
|
2177
|
-
writes — routines land as source files, so a normal review works.
|
|
2178
|
-
|
|
2179
|
-
### 12.7 Connectors — the apps library
|
|
2180
|
-
|
|
2181
|
-
The Apps tab in Studio is a shelf of services a person connects in one click:
|
|
2182
|
-
Gmail, Calendar, Drive, Slack, Notion, Linear, GitHub, Attio, Outlook, HubSpot.
|
|
2183
|
-
Connecting one makes its tools appear in that person's next session.
|
|
2184
|
-
|
|
2185
|
-
**Setup is one field, and it belongs to the client.** The org's own Composio key
|
|
2186
|
-
goes in their control plane at **Settings → Connectors**, set by an owner, the
|
|
2187
|
-
same way SSO is. It is never a deployment env var and never ours: each control
|
|
2188
|
-
plane belongs to one company, and nobody's people connect their mailboxes under
|
|
2189
|
-
another org's account. Direct them to composio.dev → Settings → API Keys.
|
|
2190
|
-
|
|
2191
|
-
**What makes it one click** is that Composio has already registered the OAuth
|
|
2192
|
-
app for each service. Without a broker, every client has to create a developer
|
|
2193
|
-
app per provider — which is exactly the hour lost to Notion on the first
|
|
2194
|
-
deployment, version pin and all.
|
|
2195
|
-
|
|
2196
|
-
**Two things on every card, because both are load-bearing:**
|
|
2197
|
-
|
|
2198
|
-
*Connects as you* versus *for the company*. A user-scoped connection cannot fire
|
|
2199
|
-
from a schedule — a routine at 8am has no signed-in person. Anything a briefing
|
|
2200
|
-
depends on must be the company's connection.
|
|
2201
|
-
|
|
2202
|
-
*An admin must approve*. True for Slack, Notion, and Google Workspace. Say it on
|
|
2203
|
-
the card; a client who discovers it at the end of a redirect chain reads the
|
|
2204
|
-
product as broken.
|
|
2205
|
-
|
|
2206
|
-
**How tools reach the agent.** `@kybernesis/connectors` mounts a dynamic
|
|
2207
|
-
resolver in `agent/tools/connectors.ts`. It resolves per session from the
|
|
2208
|
-
principal on the turn, asks the control plane what that person has connected,
|
|
2209
|
-
and calls back through it to execute. The agent never holds the broker key — it
|
|
2210
|
-
proves which agent it is with its own credential, and the control plane decides
|
|
2211
|
-
whose account the call runs against.
|
|
2212
|
-
|
|
2213
|
-
Resolution is per session, not per turn: a tool set is part of the prompt, and
|
|
2214
|
-
rebuilding it every turn re-ingests the conversation at uncached prices. Pass
|
|
2215
|
-
`perTurn: true` where people connect things mid-conversation and expect them to
|
|
2216
|
-
work immediately.
|
|
2217
|
-
|
|
2218
|
-
**Say this to the client.** Their Composio account holds refresh tokens for
|
|
2219
|
-
their Google Workspace and Slack — a fourth party alongside the model provider,
|
|
2220
|
-
the host, and us. Most will not blink; a regulated one will, and the answer for
|
|
2221
|
-
them is `eve-connect`, native eve connections with no broker. That is why every
|
|
2222
|
-
card carries a `provider`.
|
|
2223
|
-
|
|
2224
|
-
**And watch the bill.** Composio prices per action. An agent in a loop is a very
|
|
2225
|
-
different cost profile from a person clicking, and that belongs in the pricing
|
|
2226
|
-
conversation before the first invoice, not after.
|
|
2227
|
-
|
|
2228
|
-
### 12.8 MCP servers — the client's own tools, local and remote
|
|
2229
|
-
|
|
2230
|
-
The MCP tab is the escape hatch from the shelf: anything with an MCP server
|
|
2231
|
-
becomes agent tools, whether it runs on the person's laptop or on a URL.
|
|
2232
|
-
|
|
2233
|
-
**Local** — a command Studio runs on that machine (`npx -y @acme/mcp`, with env
|
|
2234
|
-
vars if the server needs them). Studio keeps it alive, and the deployed agent
|
|
2235
|
-
reaches it through the same relay as local execution. This is how a client's
|
|
2236
|
-
internal tooling — the CLI nobody will ever expose to the internet — becomes
|
|
2237
|
-
something the agent can use, without opening a port.
|
|
2238
|
-
|
|
2239
|
-
**Remote** — a URL and optional headers. Studio runs the handshake before
|
|
2240
|
-
saving, so a bad URL fails at the moment someone types it rather than in the
|
|
2241
|
-
middle of a demo.
|
|
2242
|
-
|
|
2243
|
-
Consent is **per server**, and approving one does not approve the next. The
|
|
2244
|
-
discovery call (listing what a server offers) is exempt — otherwise a person is
|
|
2245
|
-
asked to approve something before they can see what it is.
|
|
2246
|
-
|
|
2247
|
-
The things that cost real sessions here:
|
|
2248
|
-
|
|
2249
|
-
- **The command in a vendor's README is often the installer**, not the server.
|
|
2250
|
-
Plaud's documented line runs an `install` subcommand and exits; the stdio
|
|
2251
|
-
server is the bare command. If a server "connects" and never answers, check
|
|
2252
|
-
that you are running the server.
|
|
2253
|
-
- **A server declares its arguments and you must honour them.** Studio passes
|
|
2254
|
-
the published `inputSchema` through to the model (`@kybernesis/local` ≥0.5.0).
|
|
2255
|
-
Before that it did not, and watching the result is the best argument for the
|
|
2256
|
-
fix: nine consecutive calls guessing the name of an argument the server had
|
|
2257
|
-
documented, steered only by error strings.
|
|
2258
|
-
- **Discovery must have a deadline.** These resolvers run before a turn and
|
|
2259
|
-
reach across a network to a laptop that might be shut. Budgeted at 6s with a
|
|
2260
|
-
five-minute cache; without that, one closed lid makes every turn hang.
|
|
2261
|
-
|
|
2262
|
-
## 13. Known gaps — state these plainly, do not sell around them
|
|
2263
|
-
|
|
2264
|
-
Being straight about these is a feature. Clients have met vendors who were not.
|
|
2265
|
-
|
|
2266
|
-
1. **Slack access is workspace membership, not a grant.** Anyone in the client's Slack
|
|
2267
|
-
workspace who can see the bot can talk to it. Control-plane grants govern the HTTP and
|
|
2268
|
-
desktop doors, not the Slack door. The fix is a planned
|
|
2269
|
-
`governedSlackChannel()` module in `@kybernesis/enterprise` plus `external_identity`
|
|
2270
|
-
mapping (the schema exists; the module does not). Scope pilots to shared channels where
|
|
2271
|
-
workspace membership is an acceptable boundary.
|
|
2272
|
-
|
|
2273
|
-
2. **HITL approvals are session-scoped, not person-scoped.** eve renders approval buttons
|
|
2274
|
-
in the thread, and any thread member can click them. Do not gate destructive actions on
|
|
2275
|
-
thread-visible approvals in a shared channel. Person-scoped approvals — only the
|
|
2276
|
-
requester or a `manage`-grant holder may approve — are the planned governance half in
|
|
2277
|
-
`@kybernesis/enterprise`.
|
|
2278
|
-
|
|
2279
|
-
3. **The desktop door is built** — KYBER Studio, signed and notarized, with device-flow
|
|
2280
|
-
sign-in and in-app updates. What is NOT built is a second consent system talking to
|
|
2281
|
-
the first: the control plane holds the standing per-device grant, Studio holds
|
|
2282
|
-
per-effect permissions in a local file, and revoking in one does not revoke the
|
|
2283
|
-
other. An off-boarding story that says "we revoke access centrally" must be qualified
|
|
2284
|
-
at any client that asks the follow-up question.
|
|
2285
|
-
|
|
2286
|
-
4. **Off-boarding SLA equals the token TTL** (1h default) for already-minted sessions.
|
|
2287
|
-
Suspension is immediate; revocation is not. Tune `IDENTITY_TOKEN_TTL_SECONDS` to the
|
|
2288
|
-
client's appetite and tell them the number.
|
|
2289
|
-
|
|
2290
|
-
5. **Multiplayer is Slack-only in v1.** The `/discord` and `/whatsapp` subpaths are
|
|
2291
|
-
reserved for the same core with thin adapters, but nothing is built. Also: one turn at
|
|
2292
|
-
a time per session — simultaneous speakers resolve in arrival order, with mid-turn
|
|
2293
|
-
messages folded into the next turn best-effort.
|
|
2294
|
-
|
|
2295
|
-
6. **Per-user OAuth and local-file work are BUILT** — §12.6 and §12.7, both proven end
|
|
2296
|
-
to end. The remaining edge is the one that bites unattended: anything without a
|
|
2297
|
-
signed-in person (a schedule, a subagent) has no user principal, so a user-scoped
|
|
2298
|
-
connection is not available to it. A morning briefing built on someone's personal
|
|
2299
|
-
Gmail connection does not fail loudly — it quietly has no tools. Company-scoped
|
|
2300
|
-
connections are the answer, and that path has not yet been exercised in production.
|
|
2301
|
-
|
|
2302
|
-
Two more, worth saying because a client will meet them:
|
|
2303
|
-
|
|
2304
|
-
- **Tool volume is unmanaged.** Gmail and Calendar alone are 51 tool definitions in
|
|
2305
|
-
every prompt. Real tokens per turn, and measurably worse tool selection as a client
|
|
2306
|
-
connects more. Curation is designed, not shipped — connect what the pilot needs.
|
|
2307
|
-
- **Local MCP servers are per-machine.** A person's second laptop silently has a
|
|
2308
|
-
different set, and nothing in the UI says which machine a server is on.
|
|
2309
|
-
|
|
2310
|
-
7. **DM memory is per-workspace, not per-employee, unless you build it.** Splitting DMs
|
|
2311
|
-
into one Arcana workspace per person needs a Slack-user-id → workspace-slug map in the
|
|
2312
|
-
header resolver plus workspace provisioning. Doable; not shipped as a package.
|