@kybernesis/create 0.5.2 → 0.7.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.
package/dist/cli.js CHANGED
@@ -22,6 +22,7 @@ function initOptions(rest) {
22
22
  const subs = flag(rest, 'subagents');
23
23
  return {
24
24
  engineer: rest.includes('--engineer'),
25
+ studio: rest.includes('--studio'),
25
26
  channel: flag(rest, "channel"),
26
27
  host: flag(rest, "host"),
27
28
  subagents: subs === undefined ? undefined : subs.split(',').map((s) => s.trim()).filter(Boolean),
@@ -59,6 +60,7 @@ ${bold("kyb")} — Kybernesis agent scaffolder & FDE toolkit
59
60
  --host=<kind> ${dim("vercel|exe (default: vercel)")}
60
61
  --subagents=a,b ${dim("department subagents (default: none)")}
61
62
  --engineer ${dim("add the engineer layer: workshop sandbox + vision dev loop")}
63
+ --studio ${dim("wire for KYBER Studio: local execution + management routes")}
62
64
  --yes ${dim("no prompts; take flags and defaults")}
63
65
  ${bold("kyb doctor")} preflight checks (keys, issuer, envs, discovery)
64
66
  ${bold("kyb skills")} install/refresh the FDE skill suite for Claude Code
package/dist/doctor.js CHANGED
@@ -238,6 +238,33 @@ export async function doctor() {
238
238
  }
239
239
  }
240
240
  }
241
+ // ── KYBER Studio wiring ────────────────────────────────────────────────
242
+ const hasLocal = existsSync(join(cwd, "agent/tools/local_shell.ts"));
243
+ const hasManage = existsSync(join(cwd, "agent/channels/kyb.ts"));
244
+ if (hasLocal) {
245
+ // Without a credential the tools compile, appear in the tool list, and fail
246
+ // at the moment the user asks for something — the worst time to learn a
247
+ // deployment is incomplete. This is NOT a value to go and set by hand: the
248
+ // switch in Studio installs it, and a missing one means nobody has turned
249
+ // local access on yet.
250
+ if (process.env.KYBERNESIS_AGENT_CREDENTIAL) {
251
+ add("pass", "local execution can identify this agent to the control plane");
252
+ }
253
+ else {
254
+ add("warn", "local execution is installed but this agent has no credential yet", "turn on 'Work on this computer' in the agent's settings in KYBER Studio — it mints and installs one; do not paste a credential by hand");
255
+ }
256
+ }
257
+ if (hasManage) {
258
+ // manage authorizes with the caller's control-plane grant, so it needs to
259
+ // know which agent it IS before it can check one.
260
+ if (process.env.KYBERNESIS_AGENT) {
261
+ add("pass", "management routes can resolve this agent's grants");
262
+ }
263
+ else {
264
+ add("fail", "management routes have no KYBERNESIS_AGENT", "KYBER Studio cannot install or write routines here: the agent cannot check a grant for a name it does not know");
265
+ }
266
+ add("warn", "management routes need a writable working copy", "installing edits this repo and rebuilds; on a read-only serverless bundle the routes refuse. Set restartCommand in agent/channels/kyb.ts or an install will not take effect");
267
+ }
241
268
  // ── engineer subagent (build capability scoped to a subagent) ──────────
242
269
  const builderDir = join(cwd, "agent/subagents/builder");
243
270
  if (existsSync(builderDir)) {
package/dist/init.d.ts CHANGED
@@ -1,6 +1,15 @@
1
1
  import { type ChannelKind, type HostKind } from "./templates.js";
2
2
  export interface InitOptions {
3
3
  engineer?: boolean;
4
+ /**
5
+ * Wire this agent for KYBER Studio: local execution on the user's own machine,
6
+ * and management routes so Studio can install capabilities and write routines.
7
+ *
8
+ * Off by default. Both let a client reach further than chat does — one onto
9
+ * the user's laptop, one into the agent's own repository — so they are a
10
+ * deliberate choice rather than something an engagement gets by accident.
11
+ */
12
+ studio?: boolean;
4
13
  /** Chat surface. Default "none" — add later with `kyb add channel`. */
5
14
  channel?: ChannelKind;
6
15
  /** Where the agent runs. Default "vercel". */
package/dist/init.js CHANGED
@@ -17,6 +17,7 @@ const ENGINEER_ITEMS_VERCEL = ["connection/vercel"];
17
17
  const DEFAULT_MODEL = "anthropic/claude-sonnet-5";
18
18
  export async function init(rawName, options = {}) {
19
19
  const engineer = options.engineer === true;
20
+ const studio = options.studio === true;
20
21
  const nonInteractive = options.yes === true;
21
22
  const name = slug(rawName ?? (await ask("Agent name (kebab-case)?", "acme-agent")));
22
23
  if (!name) {
@@ -70,6 +71,18 @@ export async function init(rawName, options = {}) {
70
71
  for (const item of plan.registryItems) {
71
72
  run("npx", ["eve", "add", item, "--overwrite"], { cwd: dir, allowFail: true });
72
73
  }
74
+ if (studio) {
75
+ // Two separate items on purpose: `local` lets the agent act on the USER'S
76
+ // machine (consent per effect, granted on the desktop); `manage` lets a
77
+ // client change THIS AGENT — its dependencies and its source. Different
78
+ // blast radius, so an agent can have one without the other.
79
+ console.log(bold("\n2b2 KYBER Studio: local execution + management routes …"));
80
+ for (const item of ["local", "manage"]) {
81
+ const ok = run("npx", ["eve", "add", item, "--overwrite"], { cwd: dir, allowFail: true });
82
+ if (!ok)
83
+ console.log(yellow(` ! ${item} did not install cleanly — re-run: npx eve add ${item}`));
84
+ }
85
+ }
73
86
  const engPlan = engineer ? engineerPlan(host, DEFAULT_MODEL) : null;
74
87
  if (engPlan) {
75
88
  console.log(bold("\n2c Engineer subagent: workshop sandbox + vision dev loop …"));
@@ -148,6 +161,7 @@ export async function init(rawName, options = {}) {
148
161
  "self-testing (evals)",
149
162
  channel === "none" ? null : `${channel} channel`,
150
163
  host === "exe" ? "exe.dev host" : null,
164
+ studio ? "KYBER Studio (local execution + management routes)" : null,
151
165
  engineer ? "engineer subagent (workshop + vision loop)" : null,
152
166
  depts.length ? `${depts.length} dept subagent(s)` : null,
153
167
  ].filter(Boolean);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kybernesis/create",
3
- "version": "0.5.2",
3
+ "version": "0.7.0",
4
4
  "description": "The Kybernesis agent scaffolder and FDE toolkit: one command to a governed, remembering, multiplayer, self-testing eve agent — plus doctor and upgrade.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -46,6 +46,32 @@ This exact sequence was verified against production 2026-08-05. The demo
46
46
  moment for clients is step 3→4 — access appearing and disappearing from the
47
47
  admin screen.
48
48
 
49
+ ## It also brokers connectors and the user's own machine
50
+
51
+ Governance was the first job; the plane now also holds the two things an agent
52
+ cannot hold itself.
53
+
54
+ **Connectors** (`/api/connectors`, `link`, `disconnect`, `tools`, `execute`,
55
+ `custom`, `mcp`, `mcp/test`). Each ORG holds its own broker (Composio) API key,
56
+ encrypted at rest with `SECRET_ENCRYPTION_KEY` (AES-256-GCM,
57
+ `v1:<iv>:<tag>:<ct>`), set
58
+ through the admin — never an env var, never our key used for a client. The
59
+ agent asks the plane which services the CURRENT principal has connected;
60
+ `@kybernesis/connectors` turns the answer into tools for that turn only. The
61
+ broker's entity is `<registered-agent-name>:<userId>` — the registered NAME,
62
+ not the agent's UUID.
63
+
64
+ **Local access** (`/api/local-exec/*`). A device enrolls, the user grants it
65
+ once, and that grant is STANDING — no expiry. Requests and responses are relayed
66
+ as frames; the plane never executes anything. See `@kybernesis/local`.
67
+
68
+ **A client must refresh on the earlier of the token and the bundle.** They have
69
+ independent lifetimes: a token with 57 minutes left and a bundle with 12 will
70
+ start returning 401 while every dashboard says the session is fine. This cost a
71
+ full day, presented to the user as "log out and log back in", and the fix is one
72
+ line — `Math.min(tokenExpiry, bundleExpiry)`. On a 401, force a refresh and
73
+ retry ONCE before showing a human anything.
74
+
49
75
  ## Boundaries to state plainly
50
76
 
51
77
  Control-plane grants govern the HTTP/desktop doors — NOT the Slack door
@@ -44,6 +44,9 @@ npm create @kybernesis acme-atlas -- --engineer
44
44
  # ChatGPT/LLM subscription paying for inference. See section 11:
45
45
  npm create @kybernesis acme-atlas -- --host=exe --engineer
46
46
 
47
+ # …or, when the client wants the desktop app (KYBER Studio) — see section 12:
48
+ npm create @kybernesis acme-atlas -- --studio
49
+
47
50
  # 3. (Optional, for repeated use) put `kyb` on the PATH for the whole engagement:
48
51
  npm install -g @kybernesis/create
49
52
  ```
@@ -1870,6 +1873,45 @@ client with an existing ChatGPT or Claude subscription pays no incremental
1870
1873
  inference cost for the pilot. Say the number out loud in the discovery
1871
1874
  conversation — it changes the shape of the deal.
1872
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
+
1873
1915
  ### 11.5 Third-party APIs: broker the credential, pin the version
1874
1916
 
1875
1917
  Do not put a client's API token on the agent host. Put it in an exe.dev
@@ -1971,6 +2013,40 @@ Also: a long-lived channel session caches the compiled agent. After changing
1971
2013
  capabilities, start a **fresh conversation** before deciding the change did not
1972
2014
  work.
1973
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 measure it correctly.* `pgrep -f 'server/index.mjs'` run over ssh matches
2036
+ **the shell running the pgrep** — the pattern is in its own command line — so it
2037
+ reports two servers when there is one. An entire investigation went into hunting
2038
+ a phantom supervisor that a `ps -eo pid,ppid,args` would have dismissed in
2039
+ thirty seconds. Same family as `pkill -f` killing its caller. Inside a script
2040
+ file it is safe (the script's command line is `bash restart.sh`); typed at a
2041
+ shell or sent over ssh it is not. When a process count surprises you, **list the
2042
+ matches before believing the number.**
2043
+
2044
+ The escape from an already-stranded session is a **session reset**
2045
+ (`ClientSession.reset()`, or Reset in Studio's agent settings), which releases
2046
+ the durable owner so the next message starts a fresh conversation. Cancelling
2047
+ often does not help: the executor that would honour the cancel is the one that
2048
+ died.
2049
+
1974
2050
  ### 11.9 Credential checklist — collect ALL of these from the client
1975
2051
 
1976
2052
  Nothing here can be borrowed from another agent or another account.
@@ -1990,7 +2066,192 @@ Nothing here can be borrowed from another agent or another account.
1990
2066
  against the client's `-eval` workspace, and a live turn on the real surface —
1991
2067
  sent from the client's own device, not yours.
1992
2068
 
1993
- ## 12. Known gapsstate these plainly, do not sell around them
2069
+ ## 12. KYBER Studiothe desktop surface
2070
+
2071
+ Slack and iMessage reach an agent where the client already works. KYBER Studio
2072
+ is the third door: a desktop app for people who do not live in a chat tool, and
2073
+ the only surface where an agent can work on the user's own files.
2074
+
2075
+ Reach for it when the client says any of: *"not everyone here uses Slack"*,
2076
+ *"I want it on my laptop"*, *"can it look at our repo"*, or when the pilot
2077
+ involves someone technical who will hand the agent real work.
2078
+
2079
+ ### 12.1 What it is
2080
+
2081
+ - **The same agent.** Studio does not run anything. It talks to the agent you
2082
+ deployed — same memory, same tools, same subagents. Nothing to deploy twice.
2083
+ - **Governed by the same grants.** Sign-in is control-plane device flow, so
2084
+ desktop access is the grant you already manage. Revoke it and the desktop goes
2085
+ with it.
2086
+ - **Optionally hands and eyes.** With `@kybernesis/local` the agent can search,
2087
+ read, edit, write, and run commands on the user's machine, with consent.
2088
+ - **Optionally self-modifying.** With `@kybernesis/manage` the client can
2089
+ install capabilities and write routines from the app instead of asking you.
2090
+
2091
+ ### 12.2 The two packages, and why they are separate
2092
+
2093
+ | | What it lets happen | Installed on |
2094
+ | --- | --- | --- |
2095
+ | `@kybernesis/local` | The agent acts on the USER's machine | the agent |
2096
+ | `@kybernesis/manage` | A client changes THE AGENT — deps and source | the agent |
2097
+
2098
+ Different blast radius, so they are separate items an engagement chooses
2099
+ independently. A reporting agent might want `local` and never `manage`. Neither
2100
+ is installed by default, because both let a client reach further than chat does.
2101
+
2102
+ ```bash
2103
+ kyb init acme-agent --host=exe --studio # both, at scaffold time
2104
+ npx eve add local # or either one, later
2105
+ npx eve add manage
2106
+ ```
2107
+
2108
+ `kyb doctor` checks both: the relay secret for local, and `KYBERNESIS_AGENT` for
2109
+ manage, since it cannot check a grant for a name it does not know.
2110
+
2111
+ ### 12.3 Prerequisites, in order
2112
+
2113
+ 1. **The agent is registered in the control plane** and the pilot users are
2114
+ granted. Studio lists exactly what a user has a grant for — an agent that is
2115
+ registered but ungranted is invisible, which is the correct behaviour and a
2116
+ confusing one if you forget you did it.
2117
+ 2. **The agent has a URL on file.** Studio reads `/api/me/agents`; an agent with
2118
+ no deployment URL appears as unreachable rather than silently missing.
2119
+ 3. **For `manage`: a writable working copy.** Installing edits the repo and
2120
+ rebuilds, so it works on a VM and refuses on a read-only serverless bundle,
2121
+ with that reason. Set `restartCommand` in `agent/channels/kyb.ts` or an
2122
+ install completes without taking effect.
2123
+ 4. **For `local`: nothing to configure.** Setup is one switch in Studio — the
2124
+ agent's settings, *Work on this computer*. Behind it, Studio mints the
2125
+ agent's credential from the control plane, installs it over the manage
2126
+ channel, and records a standing grant for that machine; the agent restarts
2127
+ once to load it. Never hand anyone a credential to paste into an env file.
2128
+ The admin UI's "mint agent credential (shown once)" button remains for
2129
+ recovery and is not the path: a setup step that asks someone to carry a
2130
+ secret between two screens gets done wrong or skipped.
2131
+
2132
+ ### 12.4 What consent looks like for the user
2133
+
2134
+ Studio asks per **effect** — run a command, read a file, write a file, list a
2135
+ directory — not per tool, and not per turn. Approving `read-file` once covers
2136
+ every tool that reads a file out, which is why adding a tool later cannot dodge
2137
+ a decision the user already made.
2138
+
2139
+ The default is ask. A working folder can be set, but it is a starting directory
2140
+ rather than a fence: permission to act on the machine is granted once, and the
2141
+ agent may work wherever it is asked to. Whether it builds in its own sandbox or
2142
+ on the user's files is decided by the ask, not by a mode — the same way a
2143
+ colleague knows "build me a demo" from "look at my repo".
2144
+
2145
+ ### 12.5 State this plainly to the client
2146
+
2147
+ - **Two things gate a laptop, and they fail differently.** *Identity* is the
2148
+ agent's signed credential — "the local-execution relay rejected my
2149
+ credentials" means that. *Consent* is a standing per-device grant — "you have
2150
+ not allowed this agent to work on this computer" means that. Neither alone
2151
+ reaches anything. The grant is permanent on purpose: "always allow" means
2152
+ always, from a chat window, a schedule, or a message sent from a phone, and it
2153
+ ends on revoke, device removal, or disabling the agent.
2154
+ - **Reaching a desktop is still not its own revocable capability.** "May talk to
2155
+ this agent" and "may run commands on my laptop" remain one decision, taken
2156
+ when the person allows the machine. Say so at a client who would treat it as a
2157
+ surprise.
2158
+ - **Installing the credential restarts the agent**, and a turn in flight during
2159
+ that restart is lost for good — eve does not resume a step killed mid-flight.
2160
+ It reads as a spinner that never resolves, often alongside a "credential is
2161
+ unset" error from the process that was replaced. Send a new message, and reset
2162
+ the conversation if the session itself is stranded. §11.8 is why a restart
2163
+ script must wait for in-flight turns.
2164
+ - **Reading a file sends it to the model.** Execution is local; the reasoning is
2165
+ not. Fine for most work, and a conversation to have before a Studio points at
2166
+ a regulated repository.
2167
+ - **Management routes let a client change the agent.** That is the point, and it
2168
+ means the repository is no longer only yours. Agree who reviews what Studio
2169
+ writes — routines land as source files, so a normal review works.
2170
+
2171
+ ### 12.7 Connectors — the apps library
2172
+
2173
+ The Apps tab in Studio is a shelf of services a person connects in one click:
2174
+ Gmail, Calendar, Drive, Slack, Notion, Linear, GitHub, Attio, Outlook, HubSpot.
2175
+ Connecting one makes its tools appear in that person's next session.
2176
+
2177
+ **Setup is one field, and it belongs to the client.** The org's own Composio key
2178
+ goes in their control plane at **Settings → Connectors**, set by an owner, the
2179
+ same way SSO is. It is never a deployment env var and never ours: each control
2180
+ plane belongs to one company, and nobody's people connect their mailboxes under
2181
+ another org's account. Direct them to composio.dev → Settings → API Keys.
2182
+
2183
+ **What makes it one click** is that Composio has already registered the OAuth
2184
+ app for each service. Without a broker, every client has to create a developer
2185
+ app per provider — which is exactly the hour lost to Notion on the first
2186
+ deployment, version pin and all.
2187
+
2188
+ **Two things on every card, because both are load-bearing:**
2189
+
2190
+ *Connects as you* versus *for the company*. A user-scoped connection cannot fire
2191
+ from a schedule — a routine at 8am has no signed-in person. Anything a briefing
2192
+ depends on must be the company's connection.
2193
+
2194
+ *An admin must approve*. True for Slack, Notion, and Google Workspace. Say it on
2195
+ the card; a client who discovers it at the end of a redirect chain reads the
2196
+ product as broken.
2197
+
2198
+ **How tools reach the agent.** `@kybernesis/connectors` mounts a dynamic
2199
+ resolver in `agent/tools/connectors.ts`. It resolves per session from the
2200
+ principal on the turn, asks the control plane what that person has connected,
2201
+ and calls back through it to execute. The agent never holds the broker key — it
2202
+ proves which agent it is with its own credential, and the control plane decides
2203
+ whose account the call runs against.
2204
+
2205
+ Resolution is per session, not per turn: a tool set is part of the prompt, and
2206
+ rebuilding it every turn re-ingests the conversation at uncached prices. Pass
2207
+ `perTurn: true` where people connect things mid-conversation and expect them to
2208
+ work immediately.
2209
+
2210
+ **Say this to the client.** Their Composio account holds refresh tokens for
2211
+ their Google Workspace and Slack — a fourth party alongside the model provider,
2212
+ the host, and us. Most will not blink; a regulated one will, and the answer for
2213
+ them is `eve-connect`, native eve connections with no broker. That is why every
2214
+ card carries a `provider`.
2215
+
2216
+ **And watch the bill.** Composio prices per action. An agent in a loop is a very
2217
+ different cost profile from a person clicking, and that belongs in the pricing
2218
+ conversation before the first invoice, not after.
2219
+
2220
+ ### 12.8 MCP servers — the client's own tools, local and remote
2221
+
2222
+ The MCP tab is the escape hatch from the shelf: anything with an MCP server
2223
+ becomes agent tools, whether it runs on the person's laptop or on a URL.
2224
+
2225
+ **Local** — a command Studio runs on that machine (`npx -y @acme/mcp`, with env
2226
+ vars if the server needs them). Studio keeps it alive, and the deployed agent
2227
+ reaches it through the same relay as local execution. This is how a client's
2228
+ internal tooling — the CLI nobody will ever expose to the internet — becomes
2229
+ something the agent can use, without opening a port.
2230
+
2231
+ **Remote** — a URL and optional headers. Studio runs the handshake before
2232
+ saving, so a bad URL fails at the moment someone types it rather than in the
2233
+ middle of a demo.
2234
+
2235
+ Consent is **per server**, and approving one does not approve the next. The
2236
+ discovery call (listing what a server offers) is exempt — otherwise a person is
2237
+ asked to approve something before they can see what it is.
2238
+
2239
+ The things that cost real sessions here:
2240
+
2241
+ - **The command in a vendor's README is often the installer**, not the server.
2242
+ Plaud's documented line runs an `install` subcommand and exits; the stdio
2243
+ server is the bare command. If a server "connects" and never answers, check
2244
+ that you are running the server.
2245
+ - **A server declares its arguments and you must honour them.** Studio passes
2246
+ the published `inputSchema` through to the model (`@kybernesis/local` ≥0.5.0).
2247
+ Before that it did not, and watching the result is the best argument for the
2248
+ fix: nine consecutive calls guessing the name of an argument the server had
2249
+ documented, steered only by error strings.
2250
+ - **Discovery must have a deadline.** These resolvers run before a turn and
2251
+ reach across a network to a laptop that might be shut. Budgeted at 6s with a
2252
+ five-minute cache; without that, one closed lid makes every turn hang.
2253
+
2254
+ ## 13. Known gaps — state these plainly, do not sell around them
1994
2255
 
1995
2256
  Being straight about these is a feature. Clients have met vendors who were not.
1996
2257
 
@@ -2007,9 +2268,12 @@ Being straight about these is a feature. Clients have met vendors who were not.
2007
2268
  requester or a `manage`-grant holder may approve — are the planned governance half in
2008
2269
  `@kybernesis/enterprise`.
2009
2270
 
2010
- 3. **Eve Studio sign-in is specced, not built.** Employees who do not live in Slack have
2011
- no polished desktop door yet; HTTP access is token-by-hand via the device flow. The
2012
- implementation brief is in [[kybernesis-architecture-and-studio-signin]].
2271
+ 3. **The desktop door is built** KYBER Studio, signed and notarized, with device-flow
2272
+ sign-in and in-app updates. What is NOT built is a second consent system talking to
2273
+ the first: the control plane holds the standing per-device grant, Studio holds
2274
+ per-effect permissions in a local file, and revoking in one does not revoke the
2275
+ other. An off-boarding story that says "we revoke access centrally" must be qualified
2276
+ at any client that asks the follow-up question.
2013
2277
 
2014
2278
  4. **Off-boarding SLA equals the token TTL** (1h default) for already-minted sessions.
2015
2279
  Suspension is immediate; revocation is not. Tune `IDENTITY_TOKEN_TTL_SECONDS` to the
@@ -2020,9 +2284,20 @@ Being straight about these is a feature. Clients have met vendors who were not.
2020
2284
  a time per session — simultaneous speakers resolve in arrival order, with mid-turn
2021
2285
  messages folded into the next turn best-effort.
2022
2286
 
2023
- 6. **Per-user OAuth into personal SaaS and local-file work (the device bridge) are future
2024
- builds.** Org service accounts with static tokens cover most pilot asks. Subagents in
2025
- particular *cannot* use per-user OAuth at all no user principal.
2287
+ 6. **Per-user OAuth and local-file work are BUILT** §12.6 and §12.7, both proven end
2288
+ to end. The remaining edge is the one that bites unattended: anything without a
2289
+ signed-in person (a schedule, a subagent) has no user principal, so a user-scoped
2290
+ connection is not available to it. A morning briefing built on someone's personal
2291
+ Gmail connection does not fail loudly — it quietly has no tools. Company-scoped
2292
+ connections are the answer, and that path has not yet been exercised in production.
2293
+
2294
+ Two more, worth saying because a client will meet them:
2295
+
2296
+ - **Tool volume is unmanaged.** Gmail and Calendar alone are 51 tool definitions in
2297
+ every prompt. Real tokens per turn, and measurably worse tool selection as a client
2298
+ connects more. Curation is designed, not shipped — connect what the pilot needs.
2299
+ - **Local MCP servers are per-machine.** A person's second laptop silently has a
2300
+ different set, and nothing in the UI says which machine a server is on.
2026
2301
 
2027
2302
  7. **DM memory is per-workspace, not per-employee, unless you build it.** Splitting DMs
2028
2303
  into one Arcana workspace per person needs a Slack-user-id → workspace-slug map in the
@@ -1,10 +1,10 @@
1
1
  ---
2
- description: Use when installing, configuring, or debugging any @kybernesis package — arcana (memory), enterprise (governance), multiplayer (Slack), engineer (build+ship), dispatch (agent-to-agent), evals (QA), create (kyb CLI) — or the Kybernesis registry. Includes every production-learned gotcha.
2
+ description: Use when installing, configuring, or debugging any @kybernesis package — arcana (memory), enterprise (governance), multiplayer (Slack), engineer (build+ship), dispatch (agent-to-agent), connectors (Gmail/Calendar/remote MCP), local (the user's own machine), manage (Studio→agent), exe (off-Vercel hosting), evals (QA), create (kyb CLI) — or the Kybernesis registry. Includes every production-learned gotcha.
3
3
  ---
4
4
 
5
5
  # The Kybernesis packages
6
6
 
7
- Seven packages, npm-public under `@kybernesis`, Apache-2.0, monorepo
7
+ Eleven packages, npm-public under `@kybernesis`, Apache-2.0, monorepo
8
8
  `KybernesisAI/platform`. Registry: `https://registry.kybernesis.ai`
9
9
  (`eve registry add @kybernesis=https://registry.kybernesis.ai/r/{name}.json`,
10
10
  then `eve add @kybernesis/<item>`). Each covers one axis:
@@ -60,6 +60,28 @@ then `eve add @kybernesis/<item>`). Each covers one axis:
60
60
  0.2.1 lesson: eve resolves remote URLs at BOOT — url() must degrade
61
61
  (env → discovery-if-credentialed → fallbackUrl), never throw on a missing
62
62
  credential, or the whole agent (and its evals) fails to boot.
63
+ - **connectors** — the user's SaaS accounts, brokered. `connectorTools()` is a
64
+ dynamic resolver: at turn start it asks the control plane which services THIS
65
+ principal has connected and returns those tools. Composio is the broker; the
66
+ API key is per-org, held in the control plane (never an env var, never a
67
+ client's key in our account). Tools are named `<toolkit>_<action>`. Also
68
+ exports `toolInputSchema` (broker JSON Schema → zod) and a minimal MCP client
69
+ for `mcp-direct` servers that speaks BOTH JSON and text/event-stream.
70
+ - **local** — the user's own machine, through KYBER Studio. `localShellTool`,
71
+ `localRead/List/Write/Edit/SearchTool`, plus `localMcpTools()` for MCP servers
72
+ running on that machine, relayed. Every effect is consented in Studio; the
73
+ agent never holds a shell. `LOCAL_INSTRUCTIONS` explains the arrangement to
74
+ the model — mount it or the agent will offer to do things it cannot do.
75
+ - **manage** — the other direction: `manageChannel()` lets Studio install
76
+ capabilities and write schedules onto a running agent, and `routineTools()`
77
+ turns "every morning at 8, brief me" into a real schedule file. This is how a
78
+ routine gets created from chat without anyone touching the repo.
79
+ - **exe** — running off Vercel. `exeModel()` for exe.dev's LLM integration,
80
+ `grokSubscription()` / `readGrokCredential()` for a SuperGrok or X Premium+
81
+ login (`grok login` → `~/.grok/auth.json`, a valid bearer for api.x.ai —
82
+ same shape as eve's `experimental_chatgpt()`), `hostPreflight()`, Photon
83
+ iMessage credentials, and a `/preview` tool. Subpaths: `/slack`, `/photon`,
84
+ `/sandbox`, `/preview`. See the `self-hosting` skill.
63
85
  - **evals** — QA. `kybernesisBaseline({ agentDisplayName, routing,
64
86
  engineer? })` = smoke + 5 memory + routing per dept + optional vision-loop
65
87
  eval. Judge model ≠ model under test. Hermetic runs force all workspaces to
@@ -91,6 +113,36 @@ then `eve add @kybernesis/<item>`). Each covers one axis:
91
113
  company-general wording (dept-flavored prompts delegate and hide tool
92
114
  calls), no security vocabulary ("canary" triggers refusals), long routing
93
115
  timeouts. Do not "clean up" the odd-looking patterns.
116
+ - **A per-turn dynamic resolver needs a deadline.** `connectorTools()` and
117
+ `localMcpTools()` run before every turn and reach across a network. Without a
118
+ budget (6s) and a cache (60s connectors, 5min local discovery) one unreachable
119
+ laptop makes every turn hang — the agent looks broken and nothing in the log
120
+ says why.
121
+ - **Composio: one request per toolkit.** Repeating `toolkit_slug` in a single
122
+ `/api/v3/tools` call returns an EMPTY list, so connecting a second service
123
+ silently emptied the first. The logo is at `meta.logo`, not `logo`. A 200 can
124
+ still carry `successful: false` — check the body, not the status.
125
+ - **The broker's entity is the agent's REGISTERED name**, not its UUID.
126
+ `<agent>:<userId>`. Studio knows agents by id; normalize before you ask the
127
+ broker, or a connected account looks unconnected.
128
+ - **MCP requires the handshake.** `initialize` AND `notifications/initialized`
129
+ before `tools/list`, or the server never answers. Spawn through a LOGIN shell
130
+ (a bare spawn misses the user's PATH and node) and always bind
131
+ `child.on("error")` — without it a failed spawn is an unhandled rejection
132
+ that takes the process, not a error message.
133
+ - **Translate the MCP/broker inputSchema — never pass an open object.** A tool
134
+ with no declared arguments makes the model guess: nine calls to find a
135
+ `file_id` the server had documented all along. `mcpInputSchema` (local) and
136
+ `toolInputSchema` (connectors) do this; keep them permissive where the server
137
+ says nothing.
138
+ - **Never wrap a model object in a Proxy.** The AI SDK's model methods depend on
139
+ their own `this`; intercepting them detaches it and every call dies inside the
140
+ SDK on a missing internal. To swap a credential, wrap `fetch` instead — and
141
+ re-read the credential per request: a Grok login expires in six hours and the
142
+ CLI refreshes it in place.
143
+ - **Credentials are never a user's problem.** No client ever puts a key in a
144
+ `.env` — broker keys live per-org in the control plane, encrypted at rest, set
145
+ through an admin screen. A design that ends in "paste this token" is wrong.
94
146
  - **npm**: only the `kybernesis` account creates new packages in the scope;
95
147
  publishes need the human's browser auth; new versions take 1–3 min to
96
148
  propagate to anonymous reads.
@@ -25,7 +25,7 @@ kyb init <name> --host=exe --channel=<imessage|slack|telegram|none> --engineer
25
25
 
26
26
  | Capability | On Vercel | Self-hosted replacement |
27
27
  | --- | --- | --- |
28
- | Model access | AI Gateway | exe.dev LLM integration (`exeModel`) — managed, BYO key, or a **ChatGPT subscription** |
28
+ | Model access | AI Gateway | exe.dev LLM integration (`exeModel`) — managed, BYO key, or a **ChatGPT / Grok subscription** |
29
29
  | Slack/Photon/Linear credentials | Vercel Connect | **Portable/static credentials the client issues** |
30
30
  | Sandbox | `vercel()` hosted | `docker()` on the host |
31
31
  | File delivery | Vercel Blob | Blob **or** `DELIVER_DIR` + `DELIVER_BASE_URL` |
@@ -38,6 +38,44 @@ Vercel MCP connection, Linear, everything. Each becomes a static credential
38
38
  someone must issue and rotate. `kyb doctor` fails loudly if a `@vercel/connect`
39
39
  import survives into a self-hosted agent.
40
40
 
41
+ ## Running on the client's own subscription
42
+
43
+ A client who already pays for ChatGPT Plus/Pro or SuperGrok / X Premium+ can
44
+ run the agent on it instead of on metered API billing. Both work the same way:
45
+ a CLI performs a device login on the host, writes a credential to the home
46
+ directory, and that credential is a valid bearer for an OpenAI-compatible
47
+ endpoint. eve ships `experimental_chatgpt()` for the first;
48
+ `@kybernesis/exe` ships `grokSubscription()` for the second.
49
+
50
+ ```bash
51
+ # on the host, as the user the agent runs as
52
+ curl -fsSL https://x.ai/cli/install.sh | bash
53
+ grok login # device flow → ~/.grok/auth.json
54
+ ```
55
+
56
+ ```ts title="agent/agent.ts"
57
+ import { createOpenAI } from "@ai-sdk/openai";
58
+ import { grokSubscription } from "@kybernesis/exe";
59
+
60
+ export default defineAgent({
61
+ model: grokSubscription({ model: "grok-4.6", createOpenAI }),
62
+ modelContextWindowTokens: 400_000,
63
+ });
64
+ ```
65
+
66
+ What this arrangement costs you, and it is worth saying to the client:
67
+
68
+ - **It is per-machine and per-user.** The login belongs to the host's home
69
+ directory. Moving the agent means logging in again; running it as a different
70
+ unix user means it cannot see the credential at all.
71
+ - **The token expires in hours** (Grok: six) and the CLI refreshes it in place.
72
+ Read it per request, never once at boot, or the agent works all afternoon and
73
+ starts failing authentication at dinner for no reason a user can see.
74
+ - **Nobody has proven unattended refresh over days.** If no one runs `grok` on
75
+ that host, whether the refresh keeps happening is an open question — and it
76
+ presents as the agent "breaking".
77
+ - **Ask the vendor's terms question before a client demo**, not after.
78
+
41
79
  ## The failure modes, each of which cost a real session
42
80
 
43
81
  - **Docker ships disabled on some images.** exe.dev's exeuntu runs
@@ -144,3 +182,20 @@ yesterday's agent. Assert the process started AFTER the build it should serve
144
182
  (`scripts/eve-server.sh` and the restart pattern in `@kybernesis/exe` do this).
145
183
  Related: a long-lived channel session caches the compiled agent, so start a
146
184
  fresh conversation after changing capabilities.
185
+
186
+ A restart script also has to **serialize** (`flock`, released by the child with
187
+ `9>&-`) and **wait for in-flight turns** — eve does not resume a step killed
188
+ mid-flight, and restarting into a live turn strands the session behind a turn
189
+ that will never finish.
190
+
191
+ Run restarts **detached** from your ssh connection —
192
+ `setsid nohup bash restart.sh >/tmp/r.log 2>&1 </dev/null &` — or a dropped
193
+ connection SIGHUPs the script halfway through and leaves exactly the mess it
194
+ exists to prevent.
195
+
196
+ **And measure it correctly.** `pgrep -f 'server/index.mjs'` typed over ssh
197
+ matches the shell running it: the pattern is in that shell's own command line,
198
+ so it reports two servers when there is one. A whole investigation went into a
199
+ phantom "second server" that `ps -eo pid,ppid,args` would have dismissed
200
+ immediately. Inside a script file it is safe; typed at a shell it is not. List
201
+ the matches before you believe the count.