@kybernesis/create 0.6.0 → 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/doctor.js CHANGED
@@ -242,14 +242,16 @@ export async function doctor() {
242
242
  const hasLocal = existsSync(join(cwd, "agent/tools/local_shell.ts"));
243
243
  const hasManage = existsSync(join(cwd, "agent/channels/kyb.ts"));
244
244
  if (hasLocal) {
245
- // Without the relay secret the tools compile, appear in the tool list, and
246
- // fail at the moment the user asks for something — the worst time to learn
247
- // a deployment is incomplete.
248
- if (process.env.LOCAL_EXEC_AGENT_SECRET) {
249
- add("pass", "local execution is configured (LOCAL_EXEC_AGENT_SECRET set)");
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");
250
252
  }
251
253
  else {
252
- add("fail", "local execution has no LOCAL_EXEC_AGENT_SECRET", "the local_* tools will be offered to the model and fail on first use; set the shared secret the control-plane relay expects");
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");
253
255
  }
254
256
  }
255
257
  if (hasManage) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kybernesis/create",
3
- "version": "0.6.0",
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
@@ -1873,6 +1873,45 @@ client with an existing ChatGPT or Claude subscription pays no incremental
1873
1873
  inference cost for the pilot. Say the number out loud in the discovery
1874
1874
  conversation — it changes the shape of the deal.
1875
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
+
1876
1915
  ### 11.5 Third-party APIs: broker the credential, pin the version
1877
1916
 
1878
1917
  Do not put a client's API token on the agent host. Put it in an exe.dev
@@ -1974,6 +2013,40 @@ Also: a long-lived channel session caches the compiled agent. After changing
1974
2013
  capabilities, start a **fresh conversation** before deciding the change did not
1975
2014
  work.
1976
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
+
1977
2050
  ### 11.9 Credential checklist — collect ALL of these from the client
1978
2051
 
1979
2052
  Nothing here can be borrowed from another agent or another account.
@@ -2047,8 +2120,14 @@ manage, since it cannot check a grant for a name it does not know.
2047
2120
  rebuilds, so it works on a VM and refuses on a read-only serverless bundle,
2048
2121
  with that reason. Set `restartCommand` in `agent/channels/kyb.ts` or an
2049
2122
  install completes without taking effect.
2050
- 4. **For `local`: the relay secret** (`LOCAL_EXEC_AGENT_SECRET`) matching the
2051
- control plane.
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.
2052
2131
 
2053
2132
  ### 12.4 What consent looks like for the user
2054
2133
 
@@ -2065,11 +2144,23 @@ colleague knows "build me a demo" from "look at my repo".
2065
2144
 
2066
2145
  ### 12.5 State this plainly to the client
2067
2146
 
2068
- - **Local execution is not yet a governed capability.** The agent authenticates
2069
- to the relay with a shared secret, so anyone holding it can reach a connected
2070
- desktop in that org. It must become its own revocable grant, separate from
2071
- "may talk to this agent". Do not install `local` at a client who would treat
2072
- that as a surprise.
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.
2073
2164
  - **Reading a file sends it to the model.** Execution is local; the reasoning is
2074
2165
  not. Fine for most work, and a conversation to have before a Studio points at
2075
2166
  a regulated repository.
@@ -2077,6 +2168,89 @@ colleague knows "build me a demo" from "look at my repo".
2077
2168
  means the repository is no longer only yours. Agree who reviews what Studio
2078
2169
  writes — routines land as source files, so a normal review works.
2079
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
+
2080
2254
  ## 13. Known gaps — state these plainly, do not sell around them
2081
2255
 
2082
2256
  Being straight about these is a feature. Clients have met vendors who were not.
@@ -2094,9 +2268,12 @@ Being straight about these is a feature. Clients have met vendors who were not.
2094
2268
  requester or a `manage`-grant holder may approve — are the planned governance half in
2095
2269
  `@kybernesis/enterprise`.
2096
2270
 
2097
- 3. **Eve Studio sign-in is specced, not built.** Employees who do not live in Slack have
2098
- no polished desktop door yet; HTTP access is token-by-hand via the device flow. The
2099
- 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.
2100
2277
 
2101
2278
  4. **Off-boarding SLA equals the token TTL** (1h default) for already-minted sessions.
2102
2279
  Suspension is immediate; revocation is not. Tune `IDENTITY_TOKEN_TTL_SECONDS` to the
@@ -2107,9 +2284,20 @@ Being straight about these is a feature. Clients have met vendors who were not.
2107
2284
  a time per session — simultaneous speakers resolve in arrival order, with mid-turn
2108
2285
  messages folded into the next turn best-effort.
2109
2286
 
2110
- 6. **Per-user OAuth into personal SaaS and local-file work (the device bridge) are future
2111
- builds.** Org service accounts with static tokens cover most pilot asks. Subagents in
2112
- 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.
2113
2301
 
2114
2302
  7. **DM memory is per-workspace, not per-employee, unless you build it.** Splitting DMs
2115
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.