@kybernesis/create 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1665 @@
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
+ # 3. (Optional, for repeated use) put `kyb` on the PATH for the whole engagement:
44
+ npm install -g @kybernesis/create
45
+ ```
46
+
47
+ That's it: `npm create @kybernesis <name>` downloads and runs the scaffolder in one
48
+ step — the npm registry is the distribution channel, so a fresh laptop needs only
49
+ Node. The Vercel CLI installs itself the first time an `npx vercel …` command runs
50
+ (or `npm i -g vercel` if you prefer it resident).
51
+
52
+ ### What `kyb init` leaves you with
53
+
54
+ A complete agent directory — governed (`enterprise`), remembering (`arcana`),
55
+ multiplayer Slack (`multiplayer`), self-testing (`evals`), with one generated
56
+ subagent per department you named (routing description, instructions, its own
57
+ Arcana connection, the three memory skills) — already typechecked and
58
+ discovery-clean. It ends by printing **the human-steps checklist**, which is
59
+ exactly §§2, 5, 6, and 7 of this playbook:
60
+
61
+ 1. Arcana workspaces + scoped `kb_` keys → fill `.env.local` from `.env.example`
62
+ 2. `vercel link` (the **client's** team) + envs (prod/preview Sensitive)
63
+ 3. The Slack connector browser flow (§5.1)
64
+ 4. Control-plane registration (▲ eve) + pilot-cohort grants (§6)
65
+ 5. `npm run eval` → green → `npx eve deploy` → Slack smoke + the revoke demo (§7)
66
+
67
+ ### The `kyb` command reference
68
+
69
+ | Command | When | What it does |
70
+ | --- | --- | --- |
71
+ | `npm create @kybernesis <name>` | Day 1, once | Same as `kyb init <name>` without installing anything first |
72
+ | `kyb init <name>` | Day 1, once | eve scaffold (pinned version) + registry + all four packages + generated departments + eval wiring + env template + checklist |
73
+ | `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) |
74
+ | `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 |
75
+ | `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 |
76
+ | `kyb upgrade --skip-eval` | Never for production changes | Same, without the gate |
77
+
78
+ **Habit to build:** `kyb doctor` is the debugging you would otherwise do by hand
79
+ with curl — run it before asking why something doesn't work. Green doctor + green
80
+ `npm run eval` = safe to deploy.
81
+
82
+ ---
83
+
84
+ ## 0. What you are actually doing
85
+
86
+ You walk into a client company for roughly a week and leave behind:
87
+
88
+ 1. **A company agent** — an [eve](https://eve.dev) agent, running on **the client's own
89
+ Vercel team**, answering in **the client's own Slack workspace**.
90
+ 2. **Department subagents** — finance, engineering, marketing, support, whatever their
91
+ org chart says — each with its own long-term memory and its own tools.
92
+ 3. **Governance** — the client's admin can invite an employee, grant them the agent,
93
+ and revoke it, from a web UI, with revocation taking effect inside the token TTL.
94
+ 4. **An eval suite** — the QA deliverable. It is how you and the client both know the
95
+ agent still works after a change.
96
+
97
+ The commercial shape of this matters to how you build it. Our pitch is **"your source →
98
+ your runtime → your data → your token."** Everything above lives in *the client's*
99
+ accounts. Kybernesis operates exactly one thing on the client's behalf — the **control
100
+ plane** at `https://agent.kybernesis.ai` — and maintains the npm packages that make the
101
+ rest possible. Do not create client resources inside Kybernesis accounts. If you catch
102
+ yourself doing so, stop and fix it before it becomes the handover conversation.
103
+
104
+ ### Placeholders used throughout
105
+
106
+ | Placeholder | Meaning | Example |
107
+ | --- | --- | --- |
108
+ | `ACME` | the client company / control-plane org | Northwind |
109
+ | `acme` | the client slug, lowercase | `northwind` |
110
+ | `atlas` | the agent's name — whatever the client wants | `nora` |
111
+ | `acme-atlas` | the repo / Vercel project name | `northwind-nora` |
112
+ | `<dept>` | a department subagent | `finance` |
113
+ | `slack/atlas` | the Vercel Connect connector UID | `slack/nora` |
114
+
115
+ The agent's name is client-chosen and appears in three places that **must agree**: the
116
+ `KYBERNESIS_AGENT` env var, the agent's registered name in the control plane, and the
117
+ Slack app's display name (the last one is cosmetic but confusing if it differs).
118
+
119
+ ### Timeline for a standard pilot
120
+
121
+ | Day | Work |
122
+ | --- | --- |
123
+ | Day 0 (remote) | Phase 1 pre-engagement checklist |
124
+ | Day 1 morning | Phase 2 discovery conversation |
125
+ | Day 1 afternoon | Phase 3 environment setup, agent scaffolded and running locally |
126
+ | Day 2–3 | Phase 4 build: instructions, subagents, memory, evals |
127
+ | Day 3 | Phase 5 deploy + Phase 6 control-plane wiring |
128
+ | Day 4 | Phase 7 pilot onboarding — the humans |
129
+ | Day 5 | Phase 8 acceptance demo, Phase 9 handover |
130
+
131
+ Pull days 2–3 longer if they have real proprietary systems to integrate; that work is a
132
+ custom eve extension and is the billable heart of the engagement.
133
+
134
+ ---
135
+
136
+ ## 1. Pre-engagement checklist (do this remotely, ~1 hour, before you travel)
137
+
138
+ Nothing here needs the client in the room, but several items need them to have clicked
139
+ something. Chase them a week out — an unprovisioned Vercel team on Day 1 costs you a day.
140
+
141
+ - [ ] **Client has a Vercel team.** Any plan tier works for a pilot; Pro if they want
142
+ preview protection off the shelf. You need a member seat on it, or a named client
143
+ engineer who can run `vercel` commands next to you.
144
+ - [ ] **You have `node 24.x`, `npm`, and the Vercel CLI** on the machine you will build
145
+ on. Verify:
146
+ ```bash
147
+ node -v # expect v24.x
148
+ npm -v
149
+ npm install -g vercel@latest
150
+ vercel whoami
151
+ ```
152
+ - [ ] **You can log in to the control plane** at <https://agent.kybernesis.ai> and you
153
+ have permission to create an org there. If ACME is self-hosting the control plane
154
+ instead, confirm their issuer URL now — it changes `KYBERNESIS_ISSUER` everywhere.
155
+ - [ ] **Create the control-plane org for ACME** and invite yourself as an admin of it.
156
+ Do not build inside the Kybernesis org.
157
+ - [ ] **Provision Arcana workspaces** — one per brain. The naming convention is
158
+ `acme-company` for the shared root brain, `acme-<dept>` per subagent, and
159
+ `acme-eval` for hermetic eval runs. Create each at
160
+ <https://arcana.kybernesis.ai> and mint a **workspace-scoped `kb_` key** for each.
161
+ - [ ] **Validate every key before you leave.** Keys are workspace-scoped: a key for one
162
+ workspace returns `403` on any other, and finding that out during a live demo is
163
+ avoidable. Read-only check, one per workspace:
164
+ ```bash
165
+ curl -s -o /dev/null -w "HTTP %{http_code}\n" \
166
+ -H "Authorization: Bearer kb_REPLACE_ME" \
167
+ -H "X-Kyberagent-Agent: acme-company" \
168
+ "https://api.arcana.kybernesis.ai/brain/acme-company/timeline?limit=1"
169
+ # expect HTTP 200
170
+ ```
171
+ - [ ] **Confirm who at ACME is the Slack workspace admin.** Creating the Slack connector
172
+ requires someone who can approve a Slack app install. If that person is on holiday
173
+ your Day 2 is Slack-less.
174
+ - [ ] **Read the reference implementation.** `~/kyber` is our own production agent and
175
+ the canonical example of everything in Phase 4. Skim `agent/agent.ts`,
176
+ `agent/instructions/`, `agent/subagents/finance/`, and `evals/`.
177
+ - [ ] **Confirm package versions you will pin.** As of 2026-08-06:
178
+ `@kybernesis/arcana@0.1.1`, `@kybernesis/enterprise@0.1.2`,
179
+ `@kybernesis/multiplayer@0.1.0`, `@kybernesis/evals@0.2.1`,
180
+ `@kybernesis/create@0.1.4`, `@kybernesis/engineer@0.2.0`, and
181
+ `eve@0.30.8` (the Kybernesis-certified version). All public on npm;
182
+ `kyb doctor` checks the wiring.
183
+
184
+ ---
185
+
186
+ ## 2. The discovery conversation (Day 1 morning, 90 minutes, with the client)
187
+
188
+ This is a working session, not a requirements-gathering ritual. Your goal is to leave the
189
+ room able to run Phase 3 without asking anyone anything. Bring a laptop and fill in the
190
+ table below live.
191
+
192
+ ### 2.1 The agent itself
193
+
194
+ - **"What is it called?"** They pick. It appears in Slack, so it should be something
195
+ people will actually type. Write it down as `atlas`.
196
+ - **"What is its voice?"** Terse and factual, or warm? This becomes
197
+ `agent/instructions/identity.md`. Ask for two or three examples of a good answer and a
198
+ bad answer — those examples become evals.
199
+ - **"What should it refuse to do?"** Anything they name here becomes an instruction and,
200
+ if it matters, a hard guard in code rather than a prompt.
201
+
202
+ ### 2.2 Departments
203
+
204
+ - **"Which departments would ask this thing questions?"** Aim for three to five
205
+ subagents in a pilot. More is a scoping conversation, not a build.
206
+ - For each: **what does it know that nobody else does**, and **what systems does it need
207
+ to read?** The first answer sizes its memory workspace; the second is either an
208
+ off-the-shelf eve connection or a custom extension you will write.
209
+ - Watch for a department that is really a *separate agent* (in our own setup, GTM was —
210
+ it already had its own agent). Splitting is cheaper than untangling later.
211
+
212
+ ### 2.3 Surfaces — don't assume Slack
213
+
214
+ - **"Where do you actually talk?"** eve ships channels for Slack, iMessage
215
+ (Photon), Telegram, Discord, Teams, SMS/phone (Twilio), GitHub, Linear, and
216
+ a web chat — the agent can live on several at once (§4.3c has the table and
217
+ install commands). Slack gets the richest treatment (our multiplayer group
218
+ semantics); the rest are 1:1 surfaces today. Pick with the client, then ask
219
+ the Slack questions below only if Slack made the list.
220
+
221
+ ### 2.3a Slack specifics
222
+
223
+ - **Workspace name and admin contact.**
224
+ - **Which channels does the agent join?** For a pilot, one shared channel is usually
225
+ right. Ask explicitly whether they want it in a channel with sensitive content.
226
+ - **Do they want thread-following?** (People keep talking to the agent in a thread
227
+ without re-mentioning it.) This is the default in our multiplayer package and it is
228
+ what makes the agent feel like a colleague, but it needs extra Slack scopes —
229
+ `message.channels` + `channels:history`, and the `groups` pair for private channels.
230
+ Get scope approval in the same conversation as the app install.
231
+ - **Do they want DMs?** DMs are a per-person surface with a separate memory workspace.
232
+ Almost everyone says yes.
233
+
234
+ ### 2.4 The pilot cohort
235
+
236
+ - **"Name the five to ten people who will use this in week one."** Get names, emails, and
237
+ Slack handles into a table. You will invite exactly these people in Phase 7.
238
+ - **Who is the client-side admin?** They get the `manage` grant level and they are the
239
+ person you train on invite/grant/revoke. Ideally two people, so a holiday does not
240
+ block off-boarding.
241
+ - **Who is the internal champion** who will answer "what do I even ask it?" for the
242
+ cohort after you leave?
243
+
244
+ ### 2.5 Data sensitivities — ask these out loud, take notes, put the answers in the doc
245
+
246
+ - **What must never enter the agent's memory?** Credentials, obviously. But also: salary
247
+ data, customer PII, unannounced M&A, health information. The memory instructions carry
248
+ a no-secrets rule, but a rule in a prompt is not a control — if something must not be
249
+ stored, do not connect the system it lives in.
250
+ - **Which department brains must not read each other?** Each subagent gets its own
251
+ Arcana workspace with its own scoped key, so this is free — but you need to know the
252
+ boundaries to name the workspaces correctly.
253
+ - **Is the shared channel brain readable by everyone in the workspace?** Today, yes:
254
+ anyone in the Slack workspace who can see the bot can talk to it, and public-channel
255
+ memory is shared. Say this plainly. If they need per-person gating on the Slack door,
256
+ that is a known gap (§11) and you should scope the pilot around it.
257
+ - **Where does their data physically live and does that matter?** Agent runtime and
258
+ session data: their Vercel account. Long-term memory: Arcana, our SaaS. Identity and
259
+ grants: the control plane. Model traffic: the AI Gateway provider their eve project is
260
+ configured for. If any of those three placements is a problem, surface it now — the
261
+ control plane is self-hostable and that is a different (and larger) engagement.
262
+
263
+ ### 2.6 Leave the room with this table filled in
264
+
265
+ | Field | Value |
266
+ | --- | --- |
267
+ | Agent name (`atlas`) | |
268
+ | Repo / Vercel project name | |
269
+ | Vercel team | |
270
+ | Slack workspace + admin | |
271
+ | Slack channels | |
272
+ | Thread-following? DMs? | |
273
+ | Departments (`<dept>` list) | |
274
+ | Arcana workspaces + keys | |
275
+ | Control-plane org | |
276
+ | Pilot cohort (name / email / Slack id) | |
277
+ | Client-side admins (`manage` grant) | |
278
+ | Never-store list | |
279
+ | Custom systems to integrate | |
280
+
281
+ ---
282
+
283
+ ## 3. Environment setup (Day 1 afternoon, ~45 minutes)
284
+
285
+ > ⚡ **Automated by `kyb init`** (see the fast path at the top). Read this section to
286
+ > understand what the CLI wrote, or to do it by hand.
287
+
288
+ Everything below runs from a working directory of your choosing. Use an absolute path
289
+ you will remember; this playbook writes `~/work/acme-atlas`.
290
+
291
+ ### 3.1 Scaffold the agent
292
+
293
+ ```bash
294
+ mkdir -p ~/work && cd ~/work
295
+ npx eve@latest init acme-atlas
296
+ cd ~/work/acme-atlas
297
+ ```
298
+
299
+ `eve init` creates the project, installs dependencies, and initializes git. You now have
300
+ an `agent/` directory. Confirm eve sees it:
301
+
302
+ ```bash
303
+ npx eve info
304
+ ```
305
+
306
+ `eve info` prints the resolved application — every tool, skill, subagent, schedule,
307
+ channel, and route eve discovered, plus discovery diagnostics. **Run this whenever
308
+ something behaves unexpectedly.** It is much faster than booting the dev server and it
309
+ answers the single most common question ("did eve even find my file?").
310
+
311
+ ### 3.2 Link it to the client's Vercel team
312
+
313
+ ```bash
314
+ cd ~/work/acme-atlas
315
+ npx eve link
316
+ ```
317
+
318
+ Pick **the client's team**, then create a project named `acme-atlas`. This also pulls an
319
+ AI Gateway credential (`VERCEL_OIDC_TOKEN` or `AI_GATEWAY_API_KEY`) into `.env.local`, so
320
+ the agent can call a model.
321
+
322
+ In CI or any non-interactive context, `eve link` will not work — use
323
+ `vercel link --project acme-atlas --yes --non-interactive` instead. If your active Vercel
324
+ scope is not the client's team, every subsequent `vercel` call needs
325
+ `--scope <client-team-slug>`.
326
+
327
+ ### 3.3 Register the Kybernesis registry
328
+
329
+ ```bash
330
+ cd ~/work/acme-atlas
331
+ npx eve registry add @kybernesis=https://registry.kybernesis.ai/r/{name}.json
332
+ ```
333
+
334
+ Type `{name}` literally — it is a placeholder eve substitutes per item. This writes the
335
+ mapping into `package.json#registries`. Confirm:
336
+
337
+ ```bash
338
+ npx eve registry list --registry @kybernesis
339
+ npx eve registry view @kybernesis/arcana
340
+ ```
341
+
342
+ ### 3.4 Pin your versions
343
+
344
+ Before you install anything else, decide and record the versions this engagement pins.
345
+ Put them in the repo README. Pin `eve@0.30.8` — the **Kybernesis-certified** version
346
+ (certification run 2026-08-06: full suite green, zero code changes). Never pin blind
347
+ npm-latest; `kyb upgrade` carries a client to the certified pin behind their own eval
348
+ gate, and that upgrade is a **deliberate, eval-gated step**, never something that
349
+ happens by accident mid-pilot.
350
+
351
+ ---
352
+
353
+ ## 4. Build (Days 2–3)
354
+
355
+ > ⚡ **The package installs and department scaffolds here are automated by `kyb init`.**
356
+ > What remains genuinely manual in this section: tailoring instructions to the client's
357
+ > voice, client-specific tools/connections, and any custom extension work.
358
+
359
+ Build in this order — model, channel, memory, instructions, subagents, schedules,
360
+ evals. Each step is testable on its own, and the order avoids the one real trap
361
+ (installing the governance package *after* hand-authoring the file it overwrites).
362
+
363
+ ### 4.0 Do the whole phase WITH Claude Code — this is the method, not a tip
364
+
365
+ Open Claude Code in the agent repo and keep it open for all of Phase 4. The
366
+ scaffold already carries the **FDE skill suite** in `.claude/skills/` (seeded
367
+ by `kyb init`; refresh with `kyb skills`) — Claude loads the engagement flow,
368
+ eve recipes, package gotchas, control-plane wiring, and eval discipline on
369
+ demand, so it knows what Kybernesis is doing before you say a word. The
370
+ scaffold's `AGENTS.md` additionally points it at the installed eve docs
371
+ (`node_modules/eve/docs/` — the source of truth for the pinned version), so it
372
+ authors against the real framework instead of guessing. The working rhythm:
373
+
374
+ 1. **Tell it what you're building, paste the discovery table (§2.6).** "This
375
+ agent is called Atlas, lives on Slack + Telegram, needs read access to
376
+ their Postgres and their internal wiki, three departments."
377
+ 2. **Make it read before it writes.** For anything eve-specific: "read
378
+ `node_modules/eve/docs/channels/telegram.mdx`, then wire the channel."
379
+ Every channel, connection type, and config surface has a doc page; the
380
+ pattern `read the doc → write the file → npx eve info → test in eve dev`
381
+ is the whole game.
382
+ 3. **You review diffs and run the credential steps** (anything with a browser
383
+ login or a client secret is yours); Claude writes files, runs `eve
384
+ registry` searches, and iterates on eval failures.
385
+ 4. **Never accept a claim without the check**: `npm run typecheck`, `npx eve
386
+ info` (0 diagnostics), a turn in `eve dev` (§4.4b), evals green (§4.8).
387
+
388
+ The eve CLI you'll both be living in:
389
+
390
+ | Command | What it does |
391
+ | --- | --- |
392
+ | `npx eve dev` | boots the local runtime + opens the chat TUI (test turns here) |
393
+ | `npx eve info` | compile + discovery truth: agents, tools, skills, diagnostics |
394
+ | `npx eve registry list` / `search <term>` / `view <item>` | discover integrations before writing one |
395
+ | `npx eve add <item>` | install a registry item (files + deps; may offer an interactive setup flow — rerun later with `--skip-install`) |
396
+ | `npm run eval` | the hermetic suite (§4.8) — kill the dev server first |
397
+ | `npx eve build` | production build locally (what the Vercel deploy runs) |
398
+ | `vercel deploy --prod --yes` | ship it (§5) |
399
+
400
+ ### 4.0b Pick and pin the model
401
+
402
+ The model is agent config, not an env var: `agent/agent.ts` calls
403
+ `defineAgent`. With **no** `agent.ts`, eve defaults to
404
+ `anthropic/claude-sonnet-5`; the moment the file exists, `model` is required —
405
+ so pin it deliberately and record it in the repo README with the §3.4 pins:
406
+
407
+ ```ts
408
+ // agent/agent.ts
409
+ import { defineAgent } from "eve";
410
+
411
+ export default defineAgent({
412
+ model: "anthropic/claude-opus-4.8",
413
+ });
414
+ ```
415
+
416
+ What to know when choosing:
417
+
418
+ - The string form is a **Vercel AI Gateway id** (`provider/model` with a dot
419
+ version — `anthropic/claude-opus-4.8`) — routed, no provider key handling.
420
+ This is the default choice for client deploys.
421
+ - Direct provider wiring exists when a client requires it (their own
422
+ Anthropic account, data-processing terms): `npm install @ai-sdk/anthropic`,
423
+ then `model: anthropic("claude-opus-4-8")` (note the provider's hyphenated
424
+ id format) with the provider API key in env.
425
+ - **Dynamic selection** is supported (`defineDynamic` with a `fallback` and a
426
+ `session.started` handler — e.g. a bigger model for an enterprise-plan
427
+ principal). Prefer `session.started` over per-turn switching: prompt caches
428
+ are per model, and every switch re-ingests the conversation at uncached
429
+ prices. Resolver failures degrade to the fallback, never fail the turn.
430
+ - Kyber runs `anthropic/claude-opus-4.8` via the gateway; our eval judges are
431
+ configured separately in `evals/evals.config.ts` and must **never** be the
432
+ model under test.
433
+
434
+ ### 4.1 Install `@kybernesis/enterprise` (governance) — do this FIRST
435
+
436
+ ```bash
437
+ cd ~/work/acme-atlas
438
+ npx eve add @kybernesis/enterprise
439
+ ```
440
+
441
+ This installs the npm package and **writes `agent/channels/eve.ts`, replacing the
442
+ scaffold's version.** That is why it goes first: if you hand-edit `agent/channels/eve.ts`
443
+ and then run this, your edits are gone.
444
+
445
+ The file it writes:
446
+
447
+ ```ts
448
+ // agent/channels/eve.ts
449
+ import { eveChannel } from "eve/channels/eve";
450
+ import { kybernesisAuth } from "@kybernesis/enterprise";
451
+
452
+ export default eveChannel({
453
+ auth: [
454
+ kybernesisAuth({
455
+ issuer: process.env.KYBERNESIS_ISSUER!,
456
+ agent: process.env.KYBERNESIS_AGENT!,
457
+ }),
458
+ ],
459
+ });
460
+ ```
461
+
462
+ What this does: every request to the agent's HTTP surface (`/eve/v1/*`) must carry a
463
+ Kybernesis identity token **and** a policy bundle. The package verifies both **offline**
464
+ against the control plane's JWKS, cross-checks that the bundle belongs to the token's
465
+ user and org, and then requires the bundle's `agentGrants` to contain *this agent's*
466
+ registered name.
467
+
468
+ - no credentials, or invalid, or expired → **401**
469
+ - valid credentials but no grant for this agent → **403 `agent_not_granted`**
470
+
471
+ Note what is *not* in that auth array: the eve scaffold ships `localDev()` and
472
+ `placeholderAuth()`. The governed file drops both. That is intentional — it fails closed
473
+ — but it means the `eve dev` TUI cannot reach the HTTP door of a governed agent without
474
+ credentials. You will still drive the agent locally through the TUI's own session; you
475
+ just cannot `curl` it without a real token.
476
+
477
+ ### 4.2 Install `@kybernesis/arcana` (memory)
478
+
479
+ ```bash
480
+ cd ~/work/acme-atlas
481
+ npx eve add @kybernesis/arcana
482
+ ```
483
+
484
+ This installs the package and writes `agent/extensions/arcana.ts`. **The filename is the
485
+ mount namespace** — leave it as `arcana.ts` unless you have a reason not to, because
486
+ tool names derive from it.
487
+
488
+ Edit it to use the client's workspace naming and, if they want the DM/channel memory
489
+ split, a `resolveWorkspace` override:
490
+
491
+ ```ts
492
+ // agent/extensions/arcana.ts
493
+ import arcana from "@kybernesis/arcana";
494
+
495
+ export default arcana({
496
+ apiKey: process.env.ARCANA_API_KEY!,
497
+ workspace: process.env.ARCANA_COMPANY_WORKSPACE ?? "acme-company",
498
+
499
+ // Public channels use the shared company brain; DMs use a personal workspace.
500
+ // `surface` is a VERIFIED principal attribute stamped by the multiplayer Slack
501
+ // channel — never anything the model can influence.
502
+ resolveWorkspace: (ctx) =>
503
+ ctx.session.auth.current?.attributes.surface === "dm"
504
+ ? (process.env.ARCANA_DM_WORKSPACE ?? "acme-dm")
505
+ : undefined,
506
+ });
507
+ ```
508
+
509
+ What you get from the mount: an MCP connection to `https://mcp.arcana.kybernesis.ai/mcp`
510
+ (with the required `X-Kyberagent-Agent: <workspace>` header), three skills
511
+ (`recall`, `remember`, `brain-note`), and always-on memory instructions — recall-first
512
+ lookups, never claim ignorance without searching, proactive fact storage, no secrets in
513
+ memory. Those instruction rules are not decoration; each of them exists because an eval
514
+ caught the agent doing the opposite.
515
+
516
+ ### 4.3 Install `@kybernesis/multiplayer` (Slack)
517
+
518
+ ```bash
519
+ cd ~/work/acme-atlas
520
+ npx eve add @kybernesis/multiplayer
521
+ ```
522
+
523
+ This writes `agent/channels/slack.ts` and `agent/instructions/multiplayer.md`, and
524
+ declares the `SLACK_CONNECTOR_UID` env var.
525
+
526
+ > **Status as of 2026-08-05:** the registry item is **live** (`eve add` resolves and
527
+ > writes both files), and the package is dogfooded in `~/kyber` — but
528
+ > `@kybernesis/multiplayer@0.1.0` is **not yet published to npm**, so the dependency
529
+ > install step will fail until it is. Check first:
530
+ > ```bash
531
+ > npm view @kybernesis/multiplayer version # E404 means the publish hasn't landed
532
+ > ```
533
+ > If it 404s, install from the workspace checkout at `~/kyber/packages/multiplayer` (or
534
+ > vendor the two files by hand — they are shown below and in
535
+ > `agent/instructions/multiplayer.md`) and revisit once the publish lands.
536
+
537
+ The whole Slack integration is one file:
538
+
539
+ ```ts
540
+ // agent/channels/slack.ts
541
+ import { connectSlackCredentials } from "@vercel/connect/eve";
542
+ import { multiplayerSlackChannel } from "@kybernesis/multiplayer/slack";
543
+
544
+ export default multiplayerSlackChannel({
545
+ credentials: connectSlackCredentials(process.env.SLACK_CONNECTOR_UID!),
546
+ });
547
+ ```
548
+
549
+ Defaults give you the full multiplayer behavior. What that means concretely:
550
+
551
+ - **A thread is one shared session with many verified speakers.** Every message
552
+ re-authenticates: `auth.current` is *that message's* verified sender; `auth.initiator`
553
+ stays pinned to whoever started the thread.
554
+ - **Attributed thread context.** Messages between agent replies are injected with stable
555
+ per-speaker Slack ids, so the model reads a real multi-party transcript.
556
+ - **No re-mentions needed.** Once the agent is active in a thread, anyone can keep
557
+ talking to it.
558
+ - **Dual surface.** Channel sessions carry a verified `surface: "channel"` principal
559
+ attribute; DMs carry `surface: "dm"`. Gate tools on it.
560
+ - **`/new` in a DM** retires the session and starts fresh.
561
+
562
+ Options, if the client asked for something different:
563
+
564
+ | Option | Default | Change it when |
565
+ | --- | --- | --- |
566
+ | `continuation` | `"subscribed-threads"` | Set `"mention-only"` if they did not approve the history scopes, or find thread-following too chatty |
567
+ | `dmReset` | `"/new"` | They want a different command, or `false` to disable |
568
+ | `threadContext` | `"incremental"` | `"full"` for whole-thread-every-mention; `false` for triggering message only (no history scope needed) |
569
+ | `events` | — | Passed through to the underlying eve Slack channel for custom handlers |
570
+
571
+ Use the surface helpers to gate anything personal. This is the pattern for every
572
+ "only in a DM" capability:
573
+
574
+ ```ts
575
+ import { defineTool } from "eve/tools";
576
+ import { requireDm } from "@kybernesis/multiplayer";
577
+ import { z } from "zod";
578
+
579
+ export default defineTool({
580
+ description: "Read the caller's personal task list.",
581
+ inputSchema: z.object({}),
582
+ async execute(_input, ctx) {
583
+ requireDm(ctx.session); // throws a model-visible refusal on the channel surface
584
+ // ...
585
+ },
586
+ });
587
+ ```
588
+
589
+ `requireDm` **fails closed** and the thrown message is visible to the model, so the agent
590
+ relays "DM me for that" naturally. `sessionSurface(ctx.session)` returns
591
+ `"channel" | "dm" | null` if you want to branch rather than refuse; the `eve dev` local
592
+ principal counts as `"dm"` so you can exercise personal capabilities locally.
593
+
594
+ **Enforce surface rules in code, never in the prompt.** A prompt is a suggestion; a
595
+ throwing guard is a control.
596
+
597
+ ### 4.3b Install `@kybernesis/engineer` (optional — when the agent should BUILD software)
598
+
599
+ ```bash
600
+ cd ~/work/acme-atlas
601
+ npx eve add @kybernesis/engineer
602
+ ```
603
+
604
+ Writes TWO files: `agent/extensions/engineer.ts` (the mount: screenshot tool +
605
+ six build/ship skills + engineering-conduct instructions) and
606
+ `agent/sandbox/sandbox.ts` (the **workshop**: Playwright + Chromium baked into
607
+ the sandbox template, domain allowlist on deployed sessions). Pair it with the
608
+ official limbs if not using `kyb init --engineer`:
609
+ `npx eve add extension/agent-browser extension/github-tools connection/vercel`
610
+ (run individually).
611
+
612
+ **Wire the Vercel connection** (preview deploys + a clickable link for every
613
+ build — proven live 2026-08-06). After `vercel link` in the agent dir:
614
+
615
+ ```bash
616
+ vercel connect create mcp.vercel.com --name vercel
617
+ vercel connect attach mcp.vercel.com/vercel --yes
618
+ ```
619
+
620
+ Then make `agent/connections/vercel.ts` use the **UID, not the short name**:
621
+ `connect("mcp.vercel.com/vercel")`. The auth is user-scoped: the FIRST Vercel
622
+ tool call posts a "Connect with…" OAuth link in the Slack thread, the turn
623
+ parks, and it resumes after the click. Grant **All projects** (the agent
624
+ creates new projects on deploy; a fixed project list can't cover them). To
625
+ **narrow the grant later** there is no dashboard or CLI grant editor, and
626
+ `revoke-tokens` does not kill provider-side tokens — the working recipe is
627
+ `vercel connect detach` + `remove` + `create` (same UID) + `attach`: the
628
+ authorization dies with the connector. Then trigger the re-auth from a
629
+ **fresh session/thread** (existing sessions hold stale auth state and error
630
+ without re-prompting) and select only the project(s) that now exist.
631
+
632
+ > **Scoping — the client story:** the natural boundary is the CLIENT'S
633
+ > VERCEL TEAM. The agent, connector, and OAuth grant all live in the
634
+ > client's team, so "All projects" means all of *that client's* projects —
635
+ > usually exactly right, and it also covers the new projects the agent
636
+ > creates on deploy. What we verified live: an All-projects grant really
637
+ > does reach every team project (the agent will happily enumerate them),
638
+ > and there is no post-hoc grant editor — changing scope means the
639
+ > connector reset above. If a client wants a boundary *tighter than their
640
+ > team* (e.g. agent may touch only its own projects), do not promise the
641
+ > consent picker — enforce it on our side with a policy wrapper on the
642
+ > connection (allowlist of project names/IDs checked against tool
643
+ > arguments). That wrapper is the auditable answer in a security review. The deploy tool
644
+ takes an **inline file tree**, so the agent ships straight from its sandbox —
645
+ no git remote and no token ever inside the VM. Previews sit behind Vercel
646
+ Authentication by default; the agent posts a `?_vercel_share=` bypass link
647
+ (~24 h) and must ASK before changing protection settings.
648
+
649
+ **Wire file delivery** (the `deliver` tool — documents, exports, artifacts
650
+ the client can open in a browser or download):
651
+
652
+ ```bash
653
+ vercel blob create-store acme-atlas-deliverables --access public --yes
654
+ ```
655
+
656
+ One command: creates the store, links the project, injects
657
+ `BLOB_READ_WRITE_TOKEN`. Without it the tool fails with instructions rather
658
+ than silently degrading to a memory note.
659
+
660
+ Know before demoing:
661
+
662
+ - **The sandbox template bakes at DEPLOY time** (Playwright + Chromium) — a
663
+ broken bootstrap fails the Vercel build loudly instead of surfacing mid-demo.
664
+ Deploys that rebuild the template take minutes; unchanged templates are
665
+ cached and fast.
666
+ - **The allowlist is the client's security posture** — deployed sessions can
667
+ only reach the domains listed in `agent/sandbox/sandbox.ts`. A blocked host
668
+ fails loudly; extend the list deliberately, and treat every addition as a
669
+ security decision to note in the handover. The template ships the proven v5
670
+ egress set: Ubuntu mirrors (the base image is Ubuntu), https-rewritten apt,
671
+ and `storage.googleapis.com` (Chromium's CDN).
672
+ - **Production promotion is human-approved by design** (the ship skill).
673
+ Never soften this for demo convenience — the approval moment IS the demo.
674
+ - The agent's projects live in `/workspace` and persist across sessions and
675
+ redeploys — a build started Tuesday continues Thursday.
676
+ - **Coach the agent in prose, not shell.** Slack messages containing raw
677
+ shell syntax can be eaten by Cloudflare's WAF before they reach the agent.
678
+
679
+ ### 4.3c Channels — put the agent on every surface the client uses
680
+
681
+ Everything above wires *our* layer. This step and the two after it are where
682
+ the agent becomes **the client's agent**. All of it assumes zero prior eve
683
+ knowledge; every claim here is expandable by reading the named doc page in
684
+ `node_modules/eve/docs/` — which is exactly what you tell Claude Code to do.
685
+
686
+ A channel is one file under `agent/channels/`; the filename is the channel id.
687
+ eve normalizes every surface into the same runtime — instructions, tools, and
688
+ memory don't change per channel, so adding a second surface never means
689
+ re-teaching the agent. What ships:
690
+
691
+ | The client wants… | Channel | Get it |
692
+ | --- | --- | --- |
693
+ | Slack (mentions, DMs, threads, buttons) | Slack | our `@kybernesis/multiplayer` (§4.3) — group semantics, dual surface |
694
+ | **iMessage** | Photon | `eve add channel/photon-imessage` |
695
+ | Telegram bot | Telegram | `eve add channel/telegram` (worked example below) |
696
+ | Discord (slash commands, components) | Discord | `eve add channel/discord` |
697
+ | Microsoft Teams (+ Adaptive Cards) | Teams | `eve add channel/teams` |
698
+ | SMS / phone (speech-transcribed) | Twilio | `eve add channel/twilio` |
699
+ | GitHub @mentions, PR review | GitHub | `eve add channel/github` |
700
+ | Linear issue delegation | Linear | `eve add channel/linear-agent` |
701
+ | Web app / browser chat | eve HTTP + `useEveAgent` | built-in (route auth via enterprise) |
702
+
703
+ Every channel's doc page (`node_modules/eve/docs/channels/<name>.mdx`) carries
704
+ its **complete** setup: the file to write, the env vars, the webhook/app
705
+ registration on the provider side, HITL behavior, and auth verification. The
706
+ flow is always the same three steps — worked example, Telegram:
707
+
708
+ **1. The channel file** (`eve add channel/telegram` writes it, or author it):
709
+
710
+ ```ts
711
+ // agent/channels/telegram.ts
712
+ import { telegramChannel } from "eve/channels/telegram";
713
+
714
+ export default telegramChannel({
715
+ botUsername: "acme_atlas_bot",
716
+ });
717
+ ```
718
+
719
+ **2. The provider-side credentials** (this part is always yours, not Claude's):
720
+ create the bot with @BotFather, then set both envs (local `.env.local` AND
721
+ Vercel, Sensitive):
722
+
723
+ ```bash
724
+ TELEGRAM_BOT_TOKEN=123456:... # from BotFather
725
+ TELEGRAM_WEBHOOK_SECRET_TOKEN=... # any secret you generate
726
+ ```
727
+
728
+ **3. Point the provider at the deployed agent.** Each channel mounts a route
729
+ (`POST /eve/v1/telegram` here); Telegram needs the webhook registered by hand:
730
+
731
+ ```bash
732
+ curl -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook" \
733
+ -H "Content-Type: application/json" \
734
+ -d '{"url":"https://<deployed-app>/eve/v1/telegram",
735
+ "secret_token":"'"$TELEGRAM_WEBHOOK_SECRET_TOKEN"'",
736
+ "allowed_updates":["message","callback_query"]}'
737
+ ```
738
+
739
+ That pattern — file, credentials, point-the-provider-at-the-route — is every
740
+ channel. Slack's version is the connector create/attach in §4.3; Discord/Teams
741
+ have their own app-portal steps in their doc pages. Good group behavior to
742
+ know: Telegram groups only wake the bot on a command, an @mention, or a reply
743
+ to the bot; HITL renders as inline-keyboard buttons; replies over 4096 chars
744
+ split automatically.
745
+
746
+ Two honest caveats to state to the client: **multiplayer's group semantics
747
+ (shared threads, speaker attribution, no-re-mention) are Slack-only today** —
748
+ other surfaces are stock channels, excellent for 1:1; and each surface has its
749
+ own provider terms and data flow — sensitive-data review (§2.5) is per
750
+ channel, not per agent.
751
+
752
+ ### 4.3d Connections — wire the client's actual systems
753
+
754
+ A connection turns an external system into tools the model can call. Rule one:
755
+ **search before you write** — most requests already exist as registry items:
756
+
757
+ ```bash
758
+ npx eve registry list # official catalog + our @kybernesis source
759
+ npx eve registry search postgres # capability search (also searches skills.sh)
760
+ npx eve registry view connection/linear # ALWAYS inspect before installing
761
+ npx eve add linear # multi-part items let you pick components
762
+ ```
763
+
764
+ `eve add` installs dependencies and writes the connection file; official items
765
+ may then offer an **interactive setup flow** (accounts, OAuth clients) — run
766
+ it, or resume a skipped one later with `eve add <item> --skip-install`.
767
+
768
+ When the registry has nothing (a client's internal service), you write one
769
+ file. Two shapes, pick by what the service exposes:
770
+
771
+ - **MCP server** → `defineMcpClientConnection` (the server publishes tools).
772
+ - **OpenAPI 3.x document** → `defineOpenAPIConnection` (each operation
773
+ becomes a tool; filter the operations you actually want).
774
+
775
+ And four auth modes, pick by who the agent acts as:
776
+
777
+ | Mode | When | Shape |
778
+ | --- | --- | --- |
779
+ | **Static token** | org service accounts, internal systems — the pilot default | `auth: { getToken: async () => ({ token: process.env.X_TOKEN! }) }` |
780
+ | **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) |
781
+ | **Vercel Connect, app-scoped** | the agent acts as *itself* against an OAuth service | `connect({ connector: "<uid>", principalType: "app" })` — non-interactive |
782
+ | **None** | public/read-only APIs | omit `auth` |
783
+
784
+ The full static-token example (an internal MCP service):
785
+
786
+ ```ts
787
+ // agent/connections/wiki.ts
788
+ import { defineMcpClientConnection } from "eve/connections";
789
+
790
+ export default defineMcpClientConnection({
791
+ url: "https://wiki.internal.acme.com/mcp",
792
+ description:
793
+ "Acme's internal wiki: search pages, read content, list owners.",
794
+ auth: {
795
+ getToken: async () => {
796
+ const token = process.env.ACME_WIKI_TOKEN;
797
+ if (!token) throw new Error("ACME_WIKI_TOKEN is not set.");
798
+ return { token };
799
+ },
800
+ },
801
+ });
802
+ ```
803
+
804
+ Decide three things per connection, at install time, and write them down:
805
+
806
+ 1. **Auth scope** (table above). Subagents have **no user principal** — inside
807
+ them only static-token or app-scoped Connect works.
808
+ 2. **Surface gating.** Personal tools get `requireDm`-style fail-closed checks
809
+ (§4.4's surface rules only exist if connections enforce them).
810
+ 3. **Approval.** Destructive or spend-shaped tools get an `approval` gate —
811
+ per-connection HITL, rendered as buttons on Slack/Telegram.
812
+
813
+ The `description` matters more than it looks: it's how the model decides to
814
+ reach for the connection at all. Write it like a capability, name the systems.
815
+
816
+ ### 4.3e Skills — teach the client's procedures (and mine skills.sh)
817
+
818
+ A skill is a markdown procedure the model loads **on demand** (eve advertises
819
+ each skill's description; the model calls `load_skill` when a turn matches).
820
+ Anything the client's team does "the same way every time" — their release
821
+ checklist, escalation path, report format — is a skill, not an always-on
822
+ instruction. Keep always-on for identity and rules; skills for procedures.
823
+
824
+ Three authoring forms, in order of reach:
825
+
826
+ ```md
827
+ <!-- 1. Flat file: agent/skills/escalation.md — smallest possible skill.
828
+ First body line doubles as the routing description. -->
829
+ Use when an incident needs escalation: who to page, in what order, and what
830
+ the first Slack message must contain.
831
+ ...procedure...
832
+ ```
833
+
834
+ ```md
835
+ <!-- 2. Packaged: agent/skills/weekly-report/SKILL.md + references/ dir.
836
+ description frontmatter is REQUIRED here. -->
837
+ ---
838
+ description: Use when someone asks for the weekly ops report.
839
+ ---
840
+ Pull the numbers in this order... (see references/template.md)
841
+ ```
842
+
843
+ TypeScript (`defineSkill` from `eve/skills`) is the third form, only for
844
+ generated content or typed sibling files — start with markdown.
845
+
846
+ The rules that make skills actually fire:
847
+
848
+ - The `description` is a **routing hint, not a label** — write it as the
849
+ triggering task ("Use when…"), and test it in `eve dev` by asking the
850
+ question *without naming the skill* (§4.4b).
851
+ - Skills are **scoped per agent** — subagents can't see the root's skills;
852
+ copy what each needs (or use the subagent-local extension mounts that ship
853
+ them, §4.5).
854
+ - Loading a skill adds instructions, never tools — typed behavior is a tool.
855
+
856
+ **skills.sh** — the community skills marketplace — is built into eve's search
857
+ as the `@skills` source:
858
+
859
+ ```bash
860
+ npx eve registry search "react best practices" # hits skills.sh too
861
+ npx eve add @skills/vercel-labs/agent-skills/vercel-react-best-practices
862
+ ```
863
+
864
+ Community skills are third-party project files: **read the source and the
865
+ diff before running the agent**, same as any dependency. For client work,
866
+ prefer authoring the client's own procedures; pull from skills.sh for generic
867
+ craft (framework best practices, review checklists) after review.
868
+
869
+
870
+ ### 4.4 Author the agent's identity and instructions
871
+
872
+ How instructions work in eve (30 seconds of mechanics): a flat
873
+ `agent/instructions.md` is the whole prompt; an `agent/instructions/`
874
+ **directory** combines entries alphabetically (root file first) and accepts
875
+ both `.md` and `.ts` files — a `.ts` entry wraps `defineInstructions` (built
876
+ once at compile time) or `defineDynamic` (resolved per session, like the
877
+ surface.ts example below). Keep always-on instructions to identity, tone, and
878
+ standing rules; procedures belong in skills (§4.3e) — the model loads those on
879
+ demand instead of paying for them every turn.
880
+
881
+ Crib the structure from `~/kyber/agent/instructions/identity.md`, which has
882
+ three sections worth copying:
883
+
884
+ 1. **Identity** — who the agent is, and *how to write for Slack*: short paragraphs,
885
+ bullets, no headings unless the answer is genuinely long. Slack is a chat surface, not
886
+ a document editor. Agents default to essay mode; say otherwise explicitly.
887
+ 2. **Delegation** — name each subagent and say when to route to it. Critically:
888
+ *"a specialist sees none of this conversation, so pack everything it needs into the
889
+ message."* Subagents do not inherit context. Without this line the agent delegates
890
+ half-briefed and the specialist answers badly.
891
+ 3. **Surfaces** — what public channels mean versus DMs, and what to say when someone
892
+ asks for something personal in a channel.
893
+
894
+ For per-session context, `defineDynamic` on `session.started` lets you inject
895
+ surface-specific instructions. `~/kyber/agent/instructions/surface.ts` is a working
896
+ example: it greets a DM session by the caller's verified name and reminds a channel
897
+ session that everything it posts is public.
898
+
899
+ **Author these with Claude Code (§4.0), and judge drafts by test, not by
900
+ reading** — paste the discovery notes, have it draft identity.md and the
901
+ client skills, then run §4.4b and the evals. Instructions are prompts under
902
+ test: every rule in our own identity.md exists because an eval or a live turn
903
+ caught the opposite behavior.
904
+
905
+ ### 4.4b Test-drive in `eve dev` — before any connector exists
906
+
907
+ You do not need Slack, credentials, or a deploy to exercise the agent:
908
+
909
+ ```bash
910
+ cd ~/work/acme-atlas
911
+ npm run dev # boots the local runtime and opens the dev TUI
912
+ ```
913
+
914
+ Talk to it in the TUI and walk the behaviors you just authored, in roughly
915
+ this order — each line catches a different class of wiring mistake:
916
+
917
+ 1. **Identity**: "who are you, what can you do?" — does the persona match
918
+ identity.md, and does it write chat-length answers?
919
+ 2. **Skill routing**: ask something a skill covers *without naming the skill*
920
+ — watch for the `load_skill` call. If it doesn't fire, the skill's
921
+ `description` isn't written as a triggering task.
922
+ 3. **Delegation**: ask a department question — watch the subagent call and
923
+ check the answer came back briefed (§4.4's "pack everything" rule).
924
+ 4. **Memory**: "remember that X" then ask for it back — the recall-first rule
925
+ in action against the real (eval-workspace!) Arcana.
926
+ 5. **Engineer layer** (if installed): ask for a small page + screenshot — the
927
+ first local sandbox turn proves the hosted-sandbox credentials work.
928
+
929
+ Notes that save an afternoon: the TUI's local principal counts as a **DM**
930
+ surface (so `requireDm` tools are reachable locally); `npx eve info` is the
931
+ compile/discovery truth (0 diagnostics before moving on); **kill the dev
932
+ server before `npm run eval`** — a running instance makes evals attach to it
933
+ (§4.8); and the TUI is NOT the deployed agent — Slack runs the deployed build,
934
+ redeploy after every change (§11).
935
+
936
+ ### 4.5 Build the department subagents
937
+
938
+ One directory per department under `agent/subagents/<dept>/`:
939
+
940
+ ```
941
+ agent/subagents/finance/
942
+ ├── agent.ts
943
+ ├── instructions.md
944
+ ├── connections/
945
+ │ └── arcana.ts
946
+ └── skills/
947
+ ├── recall/SKILL.md
948
+ ├── remember/SKILL.md
949
+ └── brain-note/SKILL.md
950
+ ```
951
+
952
+ `agent.ts` — **the `description` is the routing signal.** The root agent sees only this
953
+ string when deciding whether to delegate. Write it as a list of the nouns people
954
+ actually say:
955
+
956
+ ```ts
957
+ import { defineAgent } from "eve";
958
+
959
+ export default defineAgent({
960
+ description:
961
+ "Finance specialist: financials, budgets, spend, revenue, invoices, runway, and " +
962
+ "financial reporting. Keeps the finance team's own memory workspace. Delegate any " +
963
+ "finance-shaped task, question, or reporting request here.",
964
+ model: "anthropic/claude-sonnet-5",
965
+ });
966
+ ```
967
+
968
+ **Two hard constraints on subagents, both of which will bite you:**
969
+
970
+ 1. **Subagents inherit nothing — give each its own mount.** On eve ≥0.30 a
971
+ declared subagent mounts extensions locally: drop
972
+ `agent/subagents/<dept>/extensions/arcana.ts` (an `arcana({ apiKey,
973
+ workspace })` mount with that department's scoped key) and only that
974
+ subagent gets the connection + skills + instructions. This is the default
975
+ pattern now. The plain-connection alternative below still works (it's what
976
+ pre-0.30 required, and what you'll find in older kyber subagents) when you
977
+ want the connection without the shipped skills:
978
+
979
+ ```ts
980
+ // agent/subagents/finance/connections/arcana.ts
981
+ import { arcanaBrain } from "../../../lib/arcana";
982
+
983
+ export default arcanaBrain({
984
+ description:
985
+ "The finance team's long-term memory (Arcana): remember, recall, search, " +
986
+ "timeline, and brain notes for financial work.",
987
+ defaultWorkspace: "acme-finance",
988
+ workspaceEnvVar: "ARCANA_FINANCE_WORKSPACE",
989
+ keyEnvVar: "ARCANA_FINANCE_API_KEY",
990
+ });
991
+ ```
992
+
993
+ The factory resolves the key in this order: the eval key when the workspace has been
994
+ overridden to `acme-eval`, then the department's own key, then the root
995
+ `ARCANA_API_KEY` as a fallback. That ordering is what makes hermetic eval runs work
996
+ without a second copy of every file.
997
+
998
+ 2. **Subagents have no user principal, so Vercel Connect OAuth is impossible in them.**
999
+ Any connection a subagent needs must use a static token from an env var — an org
1000
+ service account, not a per-user grant. Plan the client's integrations accordingly.
1001
+
1002
+ ### 4.6 Schedules (optional)
1003
+
1004
+ `agent/schedules/*.ts` for anything recurring — a Monday pipeline summary, a Friday
1005
+ financial report. **Schedules live on the root agent only**; a scheduled root turn
1006
+ delegates to the subagent that owns the work. `~/kyber/agent/schedules/friday-financials.ts`
1007
+ is the working example: it fires Friday 02:00 UTC, delegates to `finance`, and DMs the
1008
+ result to a configured Slack user. Note that DMing a user from a schedule needs the
1009
+ `im:write` scope on the Slack connector — add it during Phase 5 or the first run fails
1010
+ silently at the last step.
1011
+
1012
+ ### 4.7 Environment variables
1013
+
1014
+ Two places must agree: `.env.local` for local development, and the Vercel project's
1015
+ environment for anything deployed.
1016
+
1017
+ ```bash
1018
+ # .env.local (never commit; .gitignore already covers it)
1019
+ KYBERNESIS_ISSUER=https://agent.kybernesis.ai
1020
+ KYBERNESIS_AGENT=atlas
1021
+
1022
+ SLACK_CONNECTOR_UID=slack/atlas
1023
+
1024
+ ARCANA_API_KEY=kb_... # root / company brain key
1025
+ ARCANA_COMPANY_WORKSPACE=acme-company
1026
+ ARCANA_DM_WORKSPACE=acme-dm
1027
+
1028
+ ARCANA_FINANCE_API_KEY=kb_...
1029
+ ARCANA_FINANCE_WORKSPACE=acme-finance
1030
+ ARCANA_ENGINEERING_API_KEY=kb_...
1031
+ ARCANA_ENGINEERING_WORKSPACE=acme-engineering
1032
+
1033
+ ARCANA_EVAL_API_KEY=kb_... # the acme-eval workspace key
1034
+ ```
1035
+
1036
+ Push them to Vercel (repeat per environment; mark secrets **Sensitive**):
1037
+
1038
+ ```bash
1039
+ cd ~/work/acme-atlas
1040
+ vercel env add ARCANA_API_KEY production
1041
+ vercel env add ARCANA_API_KEY preview
1042
+ vercel env add ARCANA_API_KEY development
1043
+ ```
1044
+
1045
+ > **Read this twice.** `eve deploy` runs a `vercel env pull` afterwards and
1046
+ > **overwrites `.env.local`**. The Vercel project environment is the source of truth. If
1047
+ > you add a variable locally and then deploy, your local edit is gone. Always add to
1048
+ > Vercel first, then pull.
1049
+
1050
+ ### 4.8 Evals — the QA deliverable
1051
+
1052
+ The baseline suite is a package. Do **not** hand-write or copy eval files:
1053
+
1054
+ ```bash
1055
+ cd ~/work/acme-atlas
1056
+ eve add @kybernesis/evals
1057
+ ```
1058
+
1059
+ That installs `@kybernesis/evals` and writes two files: `evals/evals.config.ts`
1060
+ (judge model — **never the model under test** — generous timeout, gentle
1061
+ concurrency) and `evals/kybernesis.eval.ts`, which you configure for this agent:
1062
+
1063
+ ```ts
1064
+ // evals/kybernesis.eval.ts
1065
+ import { kybernesisBaseline } from "@kybernesis/evals";
1066
+
1067
+ export default kybernesisBaseline({
1068
+ agentDisplayName: "Atlas",
1069
+ // engineer: true, // when the engineer layer is installed — adds the vision-loop eval
1070
+ routing: [
1071
+ { subagent: "finance" },
1072
+ { subagent: "marketing" },
1073
+ { subagent: "engineering" },
1074
+ ],
1075
+ });
1076
+ ```
1077
+
1078
+ You get: a **smoke** eval (boots, replies, identifies itself), five **memory**
1079
+ evals (greeting must NOT thrash memory; explicit remember never refused;
1080
+ proactive store of company decisions; brain-note write+index two-step, in
1081
+ order; a fact stored in one session recalled **unprompted** from a brand-new
1082
+ session), and one **routing** eval per department you list. Every fixture
1083
+ encodes a hardening lesson we paid for in production (in-test nonces, per-run
1084
+ unique keys, company-general wording, suffix-based tool matching, realistic
1085
+ delegation timeouts) — the package README explains each one. **Do not "clean
1086
+ up" the fixture patterns**; each odd-looking choice fixes a real bug.
1087
+
1088
+ Wire the hermetic run into `package.json` so every Arcana workspace is forced
1089
+ to `acme-eval` and evals never write into a real brain:
1090
+
1091
+ ```jsonc
1092
+ "scripts": {
1093
+ "eval": "ARCANA_COMPANY_WORKSPACE=acme-eval ARCANA_DM_WORKSPACE=acme-eval ARCANA_FINANCE_WORKSPACE=acme-eval ARCANA_ENGINEERING_WORKSPACE=acme-eval eve eval",
1094
+ "typecheck": "tsc"
1095
+ }
1096
+ ```
1097
+
1098
+ Run them:
1099
+
1100
+ ```bash
1101
+ cd ~/work/acme-atlas
1102
+ npm run eval
1103
+ ```
1104
+
1105
+ Certification-run hygiene (each of these ate a real run on 2026-08-06):
1106
+
1107
+ - **Kill any stale `eve dev` first** — a leftover dev server makes `eve eval`
1108
+ attach to the old instance and silently run stale code (or refuse to run).
1109
+ - **Do not touch the repo while the suite runs.** The dev runtime watches
1110
+ `agent/`; an edit mid-run breaks the rebuild and kills the remaining evals.
1111
+ - **No Docker anywhere.** The workshop backend is pinned to Vercel Sandbox —
1112
+ local eval runs create hosted sandboxes with the linked project's
1113
+ credentials (`vercel link` + `vercel env pull` first; `kyb doctor` checks
1114
+ the token) and reuse the deploy-prewarmed template (engineer eval ≈3–4 min
1115
+ warm, measured). If you eval BEFORE the first deploy, the first run bakes
1116
+ the hosted template — evals ≥0.2.1 budget 20 minutes for that.
1117
+ - If eve complains about a sandbox migration or templates re-bake every run,
1118
+ the local cache is stale from a framework version hop:
1119
+ `rm -rf .eve/sandbox-cache .eve/dev-runtime` and rerun.
1120
+
1121
+ Client-specific evals go in separate files beside `kybernesis.eval.ts`, built
1122
+ from the same primitives the package exports (`isResultFrom`,
1123
+ `MEMORY_READ_SUFFIXES`, …). Two traps the package can NOT absorb for you — see
1124
+ §10: a running dev server on port 2000 makes `eve eval` exit early, and
1125
+ governed agents (`@kybernesis/enterprise`) need `localDev()` in their auth walk
1126
+ for local eval runs (the registry's enterprise template includes it — don't
1127
+ remove it).
1128
+
1129
+ ---
1130
+
1131
+ ## 5. Deploy (Day 3, ~30 minutes)
1132
+
1133
+ ### 5.1 Slack connector (needs the client's Slack admin present)
1134
+
1135
+ ```bash
1136
+ cd ~/work/acme-atlas
1137
+ vercel connect create slack --triggers --name atlas
1138
+ ```
1139
+
1140
+ This opens a browser flow against **the client's Slack workspace**. The display name you
1141
+ set here is what employees see. Two things to do inside that flow:
1142
+
1143
+ - Open **Advanced** and add `message.channels` under **Trigger Event Types** and
1144
+ `channels:history` under **Bot Scopes** — these are what make thread-following work.
1145
+ Private channels also need `message.groups` and `groups:history`. Add `im:write` if you
1146
+ built a schedule that DMs someone.
1147
+ - Note the connector UID it gives you (e.g. `slack/atlas`) and put it in
1148
+ `SLACK_CONNECTOR_UID`.
1149
+
1150
+ Then re-point the trigger at eve's Slack route. eve does not serve Connect's default
1151
+ path, so this detach/attach pair is mandatory:
1152
+
1153
+ ```bash
1154
+ vercel connect detach slack/atlas --yes
1155
+ vercel connect attach slack/atlas --triggers --trigger-path /eve/v1/slack --yes
1156
+ ```
1157
+
1158
+ ### 5.2 Pre-flight, then deploy
1159
+
1160
+ ```bash
1161
+ cd ~/work/acme-atlas
1162
+ npm run typecheck # 0 errors
1163
+ npx eve info # 0 discovery diagnostics
1164
+ npx eve deploy
1165
+ ```
1166
+
1167
+ Treat "0 errors, 0 warnings" as a gate, not a goal. `eve info` warnings are almost always
1168
+ a file eve did not discover the way you thought.
1169
+
1170
+ ### 5.3 Verify the deployment
1171
+
1172
+ ```bash
1173
+ curl -s https://acme-atlas.vercel.app/eve/v1/health
1174
+ # expect {"ok":true}
1175
+ ```
1176
+
1177
+ Substitute the real deployment URL that `eve deploy` printed. Keep that URL — Phase 6
1178
+ needs it.
1179
+
1180
+ > **The single most common failure mode in this entire playbook:** you change something,
1181
+ > test it in `eve dev`, it works, you try it in Slack, and it does not. **Slack talks to
1182
+ > the deployed build.** Every change needs `npx eve deploy` before Slack sees it. Say this
1183
+ > out loud to yourself once per day.
1184
+
1185
+ ### 5.4 Green eval baseline
1186
+
1187
+ ```bash
1188
+ cd ~/work/acme-atlas
1189
+ npm run eval
1190
+ ```
1191
+
1192
+ A green suite is the deliverable you hand the client. Wire it into their CI before you
1193
+ leave (a GitHub Action running `npm run typecheck && npm run eval` on pull requests,
1194
+ with the Arcana eval key and gateway credential as repository secrets).
1195
+
1196
+ ---
1197
+
1198
+ ## 6. Control-plane wiring (Day 3, ~15 minutes, in the browser)
1199
+
1200
+ Go to <https://agent.kybernesis.ai> and switch to the **ACME org**.
1201
+
1202
+ ### 6.1 Register the agent
1203
+
1204
+ **Agents → Register agent.**
1205
+
1206
+ - **Name:** `atlas` — this **must** exactly equal `KYBERNESIS_AGENT` in the deployed
1207
+ environment. A mismatch produces `403 agent_not_granted` for a user who genuinely has
1208
+ the grant, and it is a maddening thing to debug at a demo.
1209
+ - **Runtime:** **▲ eve deployment** (the default).
1210
+ - **Deployment URL:** the URL from §5.3.
1211
+
1212
+ The row now shows an **▲ eve** badge, the deployment URL, and a live health dot the
1213
+ control plane probes from `<deploymentUrl>/eve/v1/health`. If the dot is red, the control
1214
+ plane cannot reach the deployment — check Vercel deployment protection before you check
1215
+ anything else.
1216
+
1217
+ ### 6.2 Invite the humans
1218
+
1219
+ **Users → Invite.** Invite the client's admins first, then the pilot cohort from your
1220
+ discovery table. Give the client admins `manage`; everyone else gets `use`.
1221
+
1222
+ ### 6.3 Grant the agent
1223
+
1224
+ **Grants.** Grant `atlas` to each pilot user (or to a team, if you created one). Level
1225
+ `use` for the cohort, `manage` for admins.
1226
+
1227
+ Alternatively, set the agent's access tier to `org` so every org member gets it
1228
+ implicitly. For a pilot, prefer explicit grants — the whole demo in §8 depends on grants
1229
+ being visibly individual.
1230
+
1231
+ ### 6.4 The one timing rule you must internalize
1232
+
1233
+ **Grants are resolved at mint time.** The identity token and policy bundle a user holds
1234
+ are a snapshot of their access at the moment they signed in.
1235
+
1236
+ - Grant someone *after* they signed in → they do not have it until their session is
1237
+ re-minted (a refresh, or a fresh sign-in).
1238
+ - Revoke someone → their **current** token keeps working until it expires. Default TTL is
1239
+ 1 hour (`IDENTITY_TOKEN_TTL_SECONDS`). **That TTL is the off-boarding SLA. Tell the
1240
+ client the number.** If an hour is too long for them, tune it — the cost is more
1241
+ frequent refreshes.
1242
+ - **Suspend** someone → minting refuses entirely. That is the immediate lever, and it is
1243
+ the one to use for a real off-boarding.
1244
+
1245
+ ---
1246
+
1247
+ ## 7. Pilot onboarding — the humans (Day 4)
1248
+
1249
+ The technical work is done. This day decides whether the pilot succeeds.
1250
+
1251
+ ### 7.1 Set up the shared channel
1252
+
1253
+ Invite the agent to the client's chosen channel (`/invite @atlas`). Post a short pinned
1254
+ message the champion can point at:
1255
+
1256
+ > `@atlas` is our company agent. Mention it in this channel to ask something, and it will
1257
+ > keep replying in that thread without needing another mention — so you can just talk.
1258
+ > DM it for anything personal; DMs use a separate memory from this channel. Type `/new` in
1259
+ > a DM to start over. It remembers what you tell it, so telling it something once is
1260
+ > enough.
1261
+
1262
+ ### 7.2 Run a 30-minute group session with the cohort
1263
+
1264
+ Do this live, in the shared channel, with everyone watching. Demonstrate, in order:
1265
+
1266
+ 1. **Mention it and ask a real question.** Use something from their actual work, not a
1267
+ toy.
1268
+ 2. **Have a second person reply in the same thread without mentioning it.** This is the
1269
+ moment the room understands it is not a chatbot. Point out that the agent knows who
1270
+ said what.
1271
+ 3. **Tell it a fact** — "our Q3 board meeting is on the 12th" — then start a **fresh
1272
+ thread** and ask about it. Cross-session recall is the second moment.
1273
+ 4. **Ask a department question** and let it delegate. Show that the finance specialist
1274
+ has its own knowledge.
1275
+ 5. **Ask something personal in the channel** and let it refuse and redirect to a DM. Then
1276
+ do it in a DM. This teaches the surface model better than any explanation.
1277
+
1278
+ ### 7.3 Train the client admins separately (15 minutes)
1279
+
1280
+ Walk the two admins through the control plane themselves — do not do it for them:
1281
+
1282
+ - Invite a user.
1283
+ - Grant `atlas`.
1284
+ - Revoke it, and watch what happens (§8.3).
1285
+ - Suspend a user, and watch minting refuse.
1286
+
1287
+ Tell them the timing rule from §6.4 in these words: *"Suspend is immediate. Revoke takes
1288
+ effect within an hour. Grant takes effect the next time the person signs in."*
1289
+
1290
+ ### 7.4 Set expectations honestly
1291
+
1292
+ Say these four things to the cohort, in plain language:
1293
+
1294
+ - Anyone in this Slack workspace can talk to the agent. Per-person access control on the
1295
+ Slack door is not built yet; the control-plane grants govern the HTTP/desktop door.
1296
+ - Approval buttons in a shared thread can be clicked by anyone in that thread. Do not use
1297
+ the agent for anything destructive in a shared channel yet.
1298
+ - Everything it learns in a public channel is shared with everyone in that channel.
1299
+ - It is a week old at your company. It will be wrong. Tell the champion when it is, and
1300
+ those corrections become instructions and evals.
1301
+
1302
+ ---
1303
+
1304
+ ## 8. Acceptance demo script (Day 5, ~20 minutes, in front of the client's sponsor)
1305
+
1306
+ Run this as a script. Rehearse it once alone first. Every step below has been executed
1307
+ against production.
1308
+
1309
+ ### 8.1 Slack — the agent works (5 min)
1310
+
1311
+ - [ ] Mention `@atlas` in the shared channel with a real question → coherent reply in
1312
+ thread.
1313
+ - [ ] A second person replies in the thread with no mention → agent continues, and its
1314
+ answer reflects who is speaking.
1315
+ - [ ] DM `@atlas` → it responds as a personal assistant.
1316
+ - [ ] In the DM, type `/new` → "Started a fresh conversation."
1317
+
1318
+ ### 8.2 Memory — it actually learns (5 min)
1319
+
1320
+ - [ ] In the channel: tell it a company fact.
1321
+ - [ ] Start a **fresh thread**: ask about that fact → it recalls it.
1322
+ - [ ] Ask a department question → it delegates, and the answer comes back synthesized.
1323
+ - [ ] Show the client their own memory, in their own workspace:
1324
+ ```bash
1325
+ curl -s -H "Authorization: Bearer $ARCANA_API_KEY" \
1326
+ -H "X-Kyberagent-Agent: acme-company" \
1327
+ "https://api.arcana.kybernesis.ai/brain/acme-company/timeline?limit=5"
1328
+ ```
1329
+
1330
+ ### 8.3 Governance — the demo that sells the product (10 min)
1331
+
1332
+ This is the part the sponsor remembers. Do it live; do not screenshot it.
1333
+
1334
+ **Step 1 — sign in as a granted employee.** Start the device flow:
1335
+
1336
+ ```bash
1337
+ curl -s -X POST https://agent.kybernesis.ai/api/oauth/device \
1338
+ -H 'content-type: application/json' \
1339
+ -d '{"deviceId":"fde-demo","deviceLabel":"FDE laptop"}'
1340
+ ```
1341
+
1342
+ You get back `device_code`, `user_code`, `verification_uri`,
1343
+ `verification_uri_complete`, `expires_in: 600`, `interval: 5`. Open
1344
+ `verification_uri_complete` in a browser and approve as the employee.
1345
+
1346
+ **Step 2 — exchange the device code for a session:**
1347
+
1348
+ ```bash
1349
+ curl -s -X POST https://agent.kybernesis.ai/api/oauth/token \
1350
+ -H 'content-type: application/json' \
1351
+ -d '{"device_code":"PASTE_DEVICE_CODE"}'
1352
+ ```
1353
+
1354
+ Before approval this returns RFC 8628 error codes (`authorization_pending`, and so on).
1355
+ After approval it returns an **IdentitySession** plus a `refresh_token`:
1356
+
1357
+ ```json
1358
+ { "issuer": "https://agent.kybernesis.ai",
1359
+ "token": "<compact JWS — iss, sub (userId), org, email, org_name, exp>",
1360
+ "bundle": "<compact JWS — user, org, agentGrants:[{agent,level}], allowedAdapters, exp>",
1361
+ "jwks": { "keys": [ "...public ES256 keys..." ] } }
1362
+ ```
1363
+
1364
+ **Step 3 — call the agent with the two headers:**
1365
+
1366
+ ```bash
1367
+ TOKEN=... # the "token" field
1368
+ BUNDLE=... # the "bundle" field
1369
+
1370
+ curl -s -i -X POST https://acme-atlas.vercel.app/eve/v1/sessions \
1371
+ -H "authorization: Bearer $TOKEN" \
1372
+ -H "x-kybernesis-bundle: $BUNDLE" \
1373
+ -H 'content-type: application/json' \
1374
+ -d '{"input":"Hello"}'
1375
+ ```
1376
+
1377
+ The session streams. Say out loud what just happened: *the agent verified that token
1378
+ offline, against a public key, with no call back to us.*
1379
+
1380
+ **Step 4 — show the failure mode first.** Call the same endpoint with no headers:
1381
+
1382
+ ```bash
1383
+ curl -s -o /dev/null -w "%{http_code}\n" -X POST https://acme-atlas.vercel.app/eve/v1/sessions
1384
+ # 401
1385
+ ```
1386
+
1387
+ **Step 5 — the revoke.** In the admin UI, revoke that employee's grant for `atlas`. Then
1388
+ have them sign in again (repeat steps 1–2) and call the agent with the **fresh** token:
1389
+
1390
+ ```
1391
+ HTTP/1.1 403 Forbidden
1392
+ {"error":"agent_not_granted", ...}
1393
+ ```
1394
+
1395
+ **Step 6 — the suspend.** Suspend the user in the admin UI. Try to mint again — the
1396
+ control plane refuses to issue a session at all. This is the off-boarding lever.
1397
+
1398
+ **Step 7 — restore.** Re-activate and re-grant, sign in once more, and show access
1399
+ returning. Leave the client's world as you found it.
1400
+
1401
+ Then state the SLA plainly: *"Suspension is immediate. A revoked employee's already-issued
1402
+ token stops working within the token TTL — one hour by default, tunable."*
1403
+
1404
+ ### 8.4 Sign-off checklist
1405
+
1406
+ - [ ] Slack: mention, thread continuation, DM, `/new`
1407
+ - [ ] Memory: store, cross-session recall, delegation, visible in their Arcana workspace
1408
+ - [ ] Governance: 401 → grant → 200 → revoke → 403 → suspend → mint refused → restored
1409
+ - [ ] Health: `<deployment>/eve/v1/health` returns `{"ok":true}` and the control plane's
1410
+ health dot is green
1411
+ - [ ] Evals: `npm run eval` green, running in their CI
1412
+ - [ ] Admins have done an invite, a grant, and a revoke **with their own hands**
1413
+
1414
+ ---
1415
+
1416
+ ## 9. Handover (Day 5)
1417
+
1418
+ ### 9.1 What the client owns and receives
1419
+
1420
+ - **The repo** — in their GitHub org, if they want it. Push it there and add their
1421
+ engineers. It is their source.
1422
+ - **The Vercel project** — theirs already. Confirm their team owns it, not your personal
1423
+ scope, and that at least two of their people have deploy access.
1424
+ - **The Slack app** — installed in their workspace, owned by their admin.
1425
+ - **Their Arcana workspaces** and the scoped keys. Hand these over through a password
1426
+ manager, not Slack.
1427
+ - **Control-plane admin access** — invite, grant, revoke, suspend is their entire
1428
+ operational surface, and after §7.3 they know how to use it.
1429
+ - **The eval suite**, running in their CI.
1430
+ - **Optionally Eve Studio** for employees who do not live in Slack — note that Studio
1431
+ sign-in against the control plane is specced but **not built yet** (§11).
1432
+
1433
+ ### 9.2 What Kybernesis keeps doing
1434
+
1435
+ - **Operating the control plane** at `agent.kybernesis.ai` — issuer, keys, grants,
1436
+ audit. (Unless they self-host, in which case we support their instance.)
1437
+ - **Maintaining and versioning the packages** — `@kybernesis/arcana`,
1438
+ `@kybernesis/enterprise`, `@kybernesis/multiplayer`. Version bumps are our work; the
1439
+ client's eval suite is the gate that proves an upgrade is safe.
1440
+ - **Operating Arcana** — the memory SaaS their brains live in.
1441
+ - **Per-client eve version pins** (`eve_agent_deployment.eve_version_pin`) so a framework
1442
+ release never surprises a client mid-quarter.
1443
+ - **The maintenance retainer** — which is what all of the above justifies. Frame it that
1444
+ way in the handover conversation: they own the agent, we own the platform underneath it.
1445
+
1446
+ ### 9.3 Leave-behind document
1447
+
1448
+ Write a one-page README in their repo covering: the deployment URL, the control-plane
1449
+ org URL, which Arcana workspace maps to which subagent, the env var list (names only,
1450
+ never values), how to run the evals, and — in bold — **redeploy after every change,
1451
+ because Slack runs the deployed build.**
1452
+
1453
+ ---
1454
+
1455
+ ## 10. Troubleshooting appendix
1456
+
1457
+ Grouped by where the pain shows up. Every entry here cost someone real time.
1458
+
1459
+ ### Environment and deployment
1460
+
1461
+ **My `.env.local` changes disappeared.**
1462
+ `eve deploy` runs `vercel env pull` afterwards and overwrites `.env.local`. The Vercel
1463
+ project environment is the source of truth. Add to Vercel first, then pull.
1464
+
1465
+ **I changed something and Slack still does the old thing.**
1466
+ Slack talks to the **deployed** build. Run `npx eve deploy`. This is the number one
1467
+ support question and it will be yours too.
1468
+
1469
+ **`eve dev` won't start / `eve eval` exits immediately.**
1470
+ Something is already on port 2000 (eve dev's default). Find and kill it:
1471
+ ```bash
1472
+ lsof -ti tcp:2000 | xargs kill
1473
+ ```
1474
+ `eve eval` boots its own host and exits early if the port is taken.
1475
+
1476
+ **Vercel CLI does nothing useful in a script.**
1477
+ Non-interactive `vercel` calls need the team: add `--scope <team-slug>`. `eve link` is
1478
+ interactive-only; in CI use `vercel link --project <name> --yes --non-interactive`.
1479
+
1480
+ **The control plane's health dot is red but `curl` works for me.**
1481
+ Vercel deployment protection. A protected preview or production deployment rejects the
1482
+ control plane's unauthenticated health probe. Check the project's Deployment Protection
1483
+ settings.
1484
+
1485
+ ### Arcana / memory
1486
+
1487
+ **Everything Arcana returns 403.**
1488
+ `kb_` keys are **workspace-scoped**. A key minted for `acme-finance` gets `403` on
1489
+ `acme-company`. Check that the key env var and the workspace env var for that mount refer
1490
+ to the same workspace. Validate in isolation:
1491
+ ```bash
1492
+ curl -s -o /dev/null -w "HTTP %{http_code}\n" \
1493
+ -H "Authorization: Bearer kb_..." \
1494
+ -H "X-Kyberagent-Agent: acme-finance" \
1495
+ "https://api.arcana.kybernesis.ai/brain/acme-finance/timeline?limit=1"
1496
+ ```
1497
+
1498
+ **Memory tools are missing entirely from a subagent.**
1499
+ Subagents inherit nothing from the root — the root's arcana mount does not reach them.
1500
+ Give the subagent its own local extension mount
1501
+ (`agent/subagents/<id>/extensions/arcana.ts`, eve ≥0.30) or a plain connection file
1502
+ plus skill copies (§4.5).
1503
+
1504
+ **The agent says "I don't have anything stored about that" without looking.**
1505
+ The `@kybernesis/arcana` instructions carry a never-claim-ignorance-without-searching
1506
+ rule and a recall→search escalation rule (an empty entity recall does **not** mean nothing
1507
+ is stored). If you overrode or trimmed the instructions, you dropped those rules. Put
1508
+ them back — an eval caught this exact failure.
1509
+
1510
+ **Arcana MCP tool names don't match the docs.**
1511
+ Tool names are qualified by the mount namespace. Mounted as `agent/extensions/arcana.ts`,
1512
+ `arcana_remember` is addressed as `arcana__memory__arcana_remember`. Run `npx eve info` to
1513
+ see the actual resolved names rather than guessing.
1514
+
1515
+ **Should I use Vercel Connect OAuth for Arcana?**
1516
+ No. Use a static `kb_` key. Connect OAuth works in the `eve dev` TUI and is broken in
1517
+ production (Connect beta: "couldn't find this authorization request"; grants also do not
1518
+ cross environments). This is settled — see the `arcana-eve` skill for the full analysis.
1519
+
1520
+ ### Governance / auth
1521
+
1522
+ **A user who definitely has the grant gets `403 agent_not_granted`.**
1523
+ Three causes, in order of likelihood: (1) the agent's registered name in the control plane
1524
+ does not exactly equal `KYBERNESIS_AGENT` in the deployed environment; (2) the grant was
1525
+ added *after* the user signed in and their token predates it — grants resolve at mint
1526
+ time, so refresh or re-sign-in; (3) you are pointing at the wrong org's issuer.
1527
+
1528
+ **Everything returns 401.**
1529
+ Missing, malformed, or expired credentials — or only one of the two headers. Both are
1530
+ required: `authorization: Bearer <token>` **and** `x-kybernesis-bundle: <bundle>`. Also
1531
+ check `KYBERNESIS_ISSUER` matches the issuer that minted the token.
1532
+
1533
+ **A revoked employee can still use the agent.**
1534
+ Expected, for up to the token TTL (1h default). Grants resolve at mint; already-issued
1535
+ tokens are not revoked mid-flight. For immediate cut-off, **suspend** the user — minting
1536
+ refuses entirely. Tune `IDENTITY_TOKEN_TTL_SECONDS` if the client needs a tighter SLA.
1537
+
1538
+ **I can't `curl` my own agent locally.**
1539
+ The governed `agent/channels/eve.ts` drops `localDev()` and `placeholderAuth()` — it fails
1540
+ closed by design. Drive it through the `eve dev` TUI, or mint a real token via the device
1541
+ flow.
1542
+
1543
+ ### Slack
1544
+
1545
+ **Slack delivers nothing.**
1546
+ The trigger is still on Connect's default path. eve does not serve that path. Run the
1547
+ detach/attach pair with `--trigger-path /eve/v1/slack` (§5.1).
1548
+
1549
+ **Only @mentions arrive; thread-following doesn't work.**
1550
+ Missing scopes. The connector needs the `message.channels` trigger event and the
1551
+ `channels:history` bot scope (plus `message.groups` / `groups:history` for private
1552
+ channels). `threadContext` needs the same history scopes.
1553
+
1554
+ **The agent replies to itself, or to other bots.**
1555
+ eve drops messages authored by the installed app before your hook runs, but *other* bots
1556
+ are still visible. The multiplayer package filters `message.author?.isBot`; if you hand-
1557
+ rolled a hook, do the same.
1558
+
1559
+ **A scheduled DM never arrives.**
1560
+ The connector needs `im:write`. The schedule otherwise runs fine and fails at the last
1561
+ step, which makes it look like the schedule did not fire.
1562
+
1563
+ ### Evals
1564
+
1565
+ **Evals pass locally then fail identically twice in a row after a fix.**
1566
+ eve caches compiled eval modules across runs. Avoid module-level nonces (no
1567
+ `const id = Date.now()` at module scope) — generate per-run values inside `test()`.
1568
+
1569
+ **A routing eval times out.**
1570
+ Delegation does real memory work in the subagent. Routing evals need roughly six-minute
1571
+ timeouts. Set `timeoutMs` in `evals.config.ts` or pass `--timeout`.
1572
+
1573
+ **An eval fails because the agent refused.**
1574
+ Certain phrasings trip the model's own safety behavior — "canary codeword" reads as a
1575
+ secret-extraction attempt. Use neutral wording ("project codename").
1576
+
1577
+ **Evals wrote test data into the real brain.**
1578
+ The `eval` npm script must override **every** Arcana workspace env var to `acme-eval`.
1579
+ Miss one and that subagent writes into production memory. Check the script against your
1580
+ actual list of subagents.
1581
+
1582
+ **`npm run eval` looked green in CI but the job passed when it shouldn't have.**
1583
+ Piping eval output to `tail` (or anything else) masks the exit code. Use
1584
+ `set -o pipefail`, or don't pipe.
1585
+
1586
+ ### Packages and the registry
1587
+
1588
+ **`eve add @kybernesis/...` 404s.**
1589
+ Either the registry namespace is not registered in this project (`eve registry add
1590
+ @kybernesis=https://registry.kybernesis.ai/r/{name}.json`), or that item is not published
1591
+ yet. Check what actually exists with `npx eve registry list --registry @kybernesis`, or
1592
+ hit the item URL directly: `curl -s -o /dev/null -w "%{http_code}\n"
1593
+ https://registry.kybernesis.ai/r/multiplayer.json`.
1594
+
1595
+ **`eve add` resolved the item but the npm install failed.**
1596
+ The registry item and the npm package are published separately, and the registry item can
1597
+ land first. That is exactly the state `@kybernesis/multiplayer` is in as of 2026-08-05.
1598
+ `npm view @kybernesis/<name> version` tells you which half is missing.
1599
+
1600
+ **`npm install @kybernesis/<something>` says the version doesn't exist, right after publish.**
1601
+ New packages and versions take one to three minutes to propagate to anonymous reads, even
1602
+ after `npm access` reports them public. Wait, then retry.
1603
+
1604
+ **Publishing a new package in the `@kybernesis` scope fails.**
1605
+ Only the `kybernesis` npm account can **create** packages in the scope; `ianborders` can
1606
+ publish new versions of existing ones. Publishing needs Ian's browser auth (npm web-login
1607
+ flow). This is a Kybernesis-internal step, not something to do at a client site.
1608
+
1609
+ **A published package imports fine locally but breaks on clean install.**
1610
+ `tsc` does not rewrite import specifiers. An extensionless ESM re-export in `dist/` works
1611
+ in a workspace and fails from the registry. `@kybernesis/enterprise@0.1.0` shipped with
1612
+ exactly this bug; `0.1.1` fixed it with explicit `.js` extensions. Always test a package
1613
+ change with a **clean install into a scratch project**, never only from the workspace.
1614
+
1615
+ **A script's JSON output is polluted with warnings.**
1616
+ pnpm writes engine warnings to stdout. Pipe through `pnpm --silent` and, if needed,
1617
+ `sed -n '/^{/,$p'` to strip everything before the first JSON line. (This mostly bites in
1618
+ the control-plane repo's seed scripts.)
1619
+
1620
+ ### General
1621
+
1622
+ **Something isn't being picked up and I can't see why.**
1623
+ `npx eve info` first, always. It prints exactly what eve discovered plus diagnostics, and
1624
+ it is far faster than booting the dev server. `npx eve logs` reads the last `eve dev`
1625
+ diagnostic log if you need stderr, tool failures, and rebuild lines.
1626
+
1627
+ ---
1628
+
1629
+ ## 11. Known gaps — state these plainly, do not sell around them
1630
+
1631
+ Being straight about these is a feature. Clients have met vendors who were not.
1632
+
1633
+ 1. **Slack access is workspace membership, not a grant.** Anyone in the client's Slack
1634
+ workspace who can see the bot can talk to it. Control-plane grants govern the HTTP and
1635
+ desktop doors, not the Slack door. The fix is a planned
1636
+ `governedSlackChannel()` module in `@kybernesis/enterprise` plus `external_identity`
1637
+ mapping (the schema exists; the module does not). Scope pilots to shared channels where
1638
+ workspace membership is an acceptable boundary.
1639
+
1640
+ 2. **HITL approvals are session-scoped, not person-scoped.** eve renders approval buttons
1641
+ in the thread, and any thread member can click them. Do not gate destructive actions on
1642
+ thread-visible approvals in a shared channel. Person-scoped approvals — only the
1643
+ requester or a `manage`-grant holder may approve — are the planned governance half in
1644
+ `@kybernesis/enterprise`.
1645
+
1646
+ 3. **Eve Studio sign-in is specced, not built.** Employees who do not live in Slack have
1647
+ no polished desktop door yet; HTTP access is token-by-hand via the device flow. The
1648
+ implementation brief is in [[kybernesis-architecture-and-studio-signin]].
1649
+
1650
+ 4. **Off-boarding SLA equals the token TTL** (1h default) for already-minted sessions.
1651
+ Suspension is immediate; revocation is not. Tune `IDENTITY_TOKEN_TTL_SECONDS` to the
1652
+ client's appetite and tell them the number.
1653
+
1654
+ 5. **Multiplayer is Slack-only in v1.** The `/discord` and `/whatsapp` subpaths are
1655
+ reserved for the same core with thin adapters, but nothing is built. Also: one turn at
1656
+ a time per session — simultaneous speakers resolve in arrival order, with mid-turn
1657
+ messages folded into the next turn best-effort.
1658
+
1659
+ 6. **Per-user OAuth into personal SaaS and local-file work (the device bridge) are future
1660
+ builds.** Org service accounts with static tokens cover most pilot asks. Subagents in
1661
+ particular *cannot* use per-user OAuth at all — no user principal.
1662
+
1663
+ 7. **DM memory is per-workspace, not per-employee, unless you build it.** Splitting DMs
1664
+ into one Arcana workspace per person needs a Slack-user-id → workspace-slug map in the
1665
+ header resolver plus workspace provisioning. Doable; not shipped as a package.