@kybernesis/create 0.12.1 → 0.13.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 +31 -1
- package/dist/templates.js +23 -2
- package/dist/upgrade.js +94 -1
- package/package.json +1 -1
- package/skills/fde-engagement/references/playbook.md +44 -33
package/dist/doctor.js
CHANGED
|
@@ -275,6 +275,32 @@ export async function doctor() {
|
|
|
275
275
|
else {
|
|
276
276
|
add("fail", `self-hosted: ${shortQueueTimeouts.join(" and ")} left at the 30s default`, "one queue delivery holds a connection open for the entire turn, so any turn slower than the timeout is redelivered and its steps re-run — the agent answers the same question twice, with two different answers, and nothing reports an error. Set both to 900000 in .env.local and restart the server");
|
|
277
277
|
}
|
|
278
|
+
/**
|
|
279
|
+
* Instructions that name a tool the agent does not have.
|
|
280
|
+
*
|
|
281
|
+
* `deliver` mounts with the engineer layer, which is mounted on the builder
|
|
282
|
+
* subagent — so an agent whose ROOT is told to hand over files (by its own
|
|
283
|
+
* instructions, or by our playbook, which says to) promises a file it
|
|
284
|
+
* cannot send. It cannot see the mismatch either: it writes the contents to
|
|
285
|
+
* a memory note, or apologises without a cause, and the delivery
|
|
286
|
+
* infrastructure passes every check while nobody can receive anything.
|
|
287
|
+
*/
|
|
288
|
+
const rootDeliver = existsSync(join(cwd, "agent/tools/deliver.ts"));
|
|
289
|
+
const instructionsMentionDeliver = (() => {
|
|
290
|
+
try {
|
|
291
|
+
const dir = join(cwd, "agent/instructions");
|
|
292
|
+
return readdirSync(dir).some((file) => readFileSync(join(dir, file), "utf8").includes("deliver"));
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
})();
|
|
298
|
+
if (instructionsMentionDeliver && !rootDeliver) {
|
|
299
|
+
add("fail", "instructions tell this agent to deliver files, but the root has no deliver tool", 'add agent/tools/deliver.ts: export { deliver as default } from "@kybernesis/engineer/tools" — the engineer layer mounts it on the builder subagent only');
|
|
300
|
+
}
|
|
301
|
+
else if (rootDeliver) {
|
|
302
|
+
add("pass", "the agent that talks to people can also hand them a file");
|
|
303
|
+
}
|
|
278
304
|
// The exe VM sandbox backend needs a credential that cannot be scoped.
|
|
279
305
|
// Surface the blast radius here, where it is still cheap to change course.
|
|
280
306
|
const sandboxFile = join(cwd, "agent/sandbox/sandbox.ts");
|
|
@@ -350,7 +376,11 @@ export async function doctor() {
|
|
|
350
376
|
add("fail", "engineer subagent has NO sandbox of its own", "subagents do not inherit the root sandbox — add agent/subagents/builder/sandbox/sandbox.ts or the vision loop cannot run");
|
|
351
377
|
}
|
|
352
378
|
if (existsSync(join(builderDir, "extensions/engineer.ts"))) {
|
|
353
|
-
|
|
379
|
+
// Not "the root keeps no shell": every eve agent has a sandbox with bash,
|
|
380
|
+
// read_file, write_file, glob and grep. What the local mount withholds is
|
|
381
|
+
// the build loop and any shell ON THE HOST. This sentence is one people
|
|
382
|
+
// reason about security boundaries from, so it says the true thing.
|
|
383
|
+
add("pass", "engineer mounted locally on the subagent (root gets no host shell, no build loop)");
|
|
354
384
|
}
|
|
355
385
|
else {
|
|
356
386
|
add("warn", "engineer extension not mounted on the subagent", "agent/subagents/builder/extensions/engineer.ts");
|
package/dist/templates.js
CHANGED
|
@@ -513,18 +513,39 @@ export default defineAgent({
|
|
|
513
513
|
"Builds and runs software: scaffolds projects, writes code, installs dependencies, runs builds and dev servers, and visually verifies rendered pages. Use when the user asks for something to be BUILT, prototyped, deployed, or fixed in code — not for questions, planning, or scheduling.",
|
|
514
514
|
model: ${JSON.stringify(model)},
|
|
515
515
|
});
|
|
516
|
+
`,
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
path: "agent/tools/deliver.ts",
|
|
520
|
+
content: `// Handing a file to the person you are talking to.
|
|
521
|
+
//
|
|
522
|
+
// The engineer layer mounts on \`builder\`, so building stays there — but
|
|
523
|
+
// DELIVERING is not building. It reads one file from this agent's own sandbox
|
|
524
|
+
// and copies it to storage, runs no commands and writes nothing back, and the
|
|
525
|
+
// agent that needs it is the one talking to the user.
|
|
526
|
+
//
|
|
527
|
+
// Without this the root is told (by its instructions, and by our own playbook)
|
|
528
|
+
// to deliver files and has no such tool. It cannot detect the mismatch, so it
|
|
529
|
+
// promises a file and then quietly puts the contents somewhere nobody can
|
|
530
|
+
// reach — a memory note, or an apology with no cause given.
|
|
531
|
+
export { deliver as default } from "@kybernesis/engineer/tools";
|
|
516
532
|
`,
|
|
517
533
|
},
|
|
518
534
|
{
|
|
519
535
|
path: "agent/subagents/builder/extensions/engineer.ts",
|
|
520
536
|
content: `// Engineer layer mounted LOCALLY on this subagent (eve >=0.30): screenshot,
|
|
521
537
|
// deliver, and the trade-school skills belong to \`builder\` alone, so the root
|
|
522
|
-
//
|
|
538
|
+
// gets no shell ON THE HOST and none of the build loop.
|
|
539
|
+
//
|
|
540
|
+
// Not "the root has no shell" — every eve agent has a sandbox with bash,
|
|
541
|
+
// read_file, write_file, glob and grep, and this app builds two templates
|
|
542
|
+
// (root and builder). The root's shell is its own container. The distinction
|
|
543
|
+
// that matters is host access, and the old wording blurred exactly the line
|
|
544
|
+
// people read this comment to understand.
|
|
523
545
|
//
|
|
524
546
|
// The root DOES get a browser and GitHub tools — the engineer layer installs
|
|
525
547
|
// extension/agent-browser and extension/github-tools at the root, deliberately,
|
|
526
548
|
// because reading a page is not the same blast radius as running a command.
|
|
527
|
-
// This comment used to claim otherwise, which is worse than saying nothing.
|
|
528
549
|
export { default } from "@kybernesis/engineer";
|
|
529
550
|
`,
|
|
530
551
|
},
|
package/dist/upgrade.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { upsertEnv } from "./envfile.js";
|
|
4
4
|
import { EVE_VERSION, bold, capture, dim, green, red, run, yellow } from "./util.js";
|
|
@@ -87,6 +87,98 @@ function repairLocalQueueTimeouts(cwd, deps) {
|
|
|
87
87
|
` ${dim("slower than 30s was redelivered and its steps re-run — the agent answered twice.")}\n` +
|
|
88
88
|
` ${dim("Takes effect on the next server restart.")}\n`);
|
|
89
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Finish a Buzz install that a version bump alone leaves half-done.
|
|
92
|
+
*
|
|
93
|
+
* @remarks
|
|
94
|
+
* A capability that arrives in a package but needs four manual steps to switch
|
|
95
|
+
* on has not really shipped. That is what happened here: `kyb upgrade` moved an
|
|
96
|
+
* agent to a version whose whole point was acting in a workspace, and the agent
|
|
97
|
+
* went on being unable to, because the extension was not mounted, the process
|
|
98
|
+
* had none of the environment the bridge had, and the CLI was not on the host.
|
|
99
|
+
* Everything looked upgraded and nothing was different.
|
|
100
|
+
*
|
|
101
|
+
* The person who hit it was an engineer with a terminal, and it still took a
|
|
102
|
+
* hand-written prompt to sort out. A client would not have got there at all —
|
|
103
|
+
* they would have concluded the feature did not work.
|
|
104
|
+
*
|
|
105
|
+
* So each of those is repaired here, from what the host can already tell us:
|
|
106
|
+
* the bridge's own service file holds the relay and the identity, and the rest
|
|
107
|
+
* is a file and a binary.
|
|
108
|
+
*/
|
|
109
|
+
function repairBuzzSetup(cwd, deps) {
|
|
110
|
+
if (!deps["@kybernesis/buzz"])
|
|
111
|
+
return;
|
|
112
|
+
const done = [];
|
|
113
|
+
// 1. The extension mount. Without it the tools do not exist, and nothing says so.
|
|
114
|
+
const mount = join(cwd, "agent/extensions/buzz.ts");
|
|
115
|
+
if (!existsSync(mount)) {
|
|
116
|
+
mkdirSync(join(cwd, "agent/extensions"), { recursive: true });
|
|
117
|
+
writeFileSync(mount, `// The agent's hands in Buzz: projects, issues, pull requests, patches, repos,
|
|
118
|
+
` +
|
|
119
|
+
`// long-form notes, channel canvases, workflows, the feed, media — everything the
|
|
120
|
+
` +
|
|
121
|
+
`// workspace has beyond talking, which the bridge alone does not provide.
|
|
122
|
+
` +
|
|
123
|
+
`//
|
|
124
|
+
` +
|
|
125
|
+
`// Actions are signed with THIS AGENT's key and appear under its name.
|
|
126
|
+
` +
|
|
127
|
+
`export { default } from "@kybernesis/buzz/extension";
|
|
128
|
+
`);
|
|
129
|
+
done.push("mounted the Buzz extension (agent/extensions/buzz.ts)");
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* 2. The environment. The bridge runs with the relay and the key; the AGENT
|
|
133
|
+
* process runs with neither, because nothing ever needed it to until the
|
|
134
|
+
* tools existed. Read from the service file rather than asked for, because
|
|
135
|
+
* the answer is already on the host and a person retyping it will eventually
|
|
136
|
+
* mistype it.
|
|
137
|
+
*/
|
|
138
|
+
const envPath = join(cwd, ".env.local");
|
|
139
|
+
const env = existsSync(envPath) ? readFileSync(envPath, "utf8") : "";
|
|
140
|
+
if (!/^BUZZ_RELAY=/m.test(env)) {
|
|
141
|
+
const unit = capture("sh", [
|
|
142
|
+
"-c",
|
|
143
|
+
"grep -ohE 'BUZZ_(RELAY|KEYFILE)=[^ \"]+' /etc/systemd/system/*buzz-bridge.service 2>/dev/null | head -2",
|
|
144
|
+
]);
|
|
145
|
+
const values = {};
|
|
146
|
+
for (const line of (unit ?? "").split("\n")) {
|
|
147
|
+
const [name, ...rest] = line.trim().split("=");
|
|
148
|
+
if (name && rest.length)
|
|
149
|
+
values[name] = rest.join("=").replace(/^"|"$/g, "");
|
|
150
|
+
}
|
|
151
|
+
if (values.BUZZ_RELAY) {
|
|
152
|
+
upsertEnv(cwd, {
|
|
153
|
+
BUZZ_RELAY: values.BUZZ_RELAY,
|
|
154
|
+
...(values.BUZZ_KEYFILE ? { BUZZ_KEYFILE: values.BUZZ_KEYFILE } : {}),
|
|
155
|
+
});
|
|
156
|
+
done.push("gave the agent process the relay and identity its bridge already had");
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
console.log(` ${yellow("!")} Buzz tools need BUZZ_RELAY and BUZZ_KEYFILE in .env.local — the same ` +
|
|
160
|
+
`values the bridge service uses. Could not read them from this host.`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// 3. The CLI itself. Built once, in a container, because no binary is published.
|
|
164
|
+
if (!existsSync(join(cwd, ".buzz/bin/buzz"))) {
|
|
165
|
+
const hasDocker = capture("sh", ["-c", "command -v docker >/dev/null && echo yes"])?.trim() === "yes";
|
|
166
|
+
if (hasDocker) {
|
|
167
|
+
console.log(dim(" building the Buzz CLI for this host (first time only, a few minutes) …"));
|
|
168
|
+
const ok = run("npx", ["kybernesis-buzz", "install-cli"], { cwd, allowFail: true, quiet: true });
|
|
169
|
+
done.push(ok ? "installed the Buzz CLI (.buzz/bin/buzz)" : "could NOT install the Buzz CLI — run: npx kybernesis-buzz install-cli");
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
console.log(` ${yellow("!")} No docker here, so the Buzz CLI cannot be built. Without it the agent ` +
|
|
173
|
+
`can read the workspace but not act in it. Install docker, or set BUZZ_CLI_URL to a ` +
|
|
174
|
+
`binary built for this platform, then: npx kybernesis-buzz install-cli`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (done.length > 0) {
|
|
178
|
+
console.log(` ${green("+")} Buzz: ${done.join("; ")}`);
|
|
179
|
+
console.log(` ${dim("Takes effect after the next build and restart.")}\n`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
90
182
|
export async function upgrade(skipEval) {
|
|
91
183
|
const cwd = process.cwd();
|
|
92
184
|
const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf8"));
|
|
@@ -94,6 +186,7 @@ export async function upgrade(skipEval) {
|
|
|
94
186
|
console.log(bold("\nkyb upgrade — checking @kybernesis/* and eve against npm\n"));
|
|
95
187
|
warnIfStale();
|
|
96
188
|
repairLocalQueueTimeouts(cwd, deps);
|
|
189
|
+
repairBuzzSetup(cwd, deps);
|
|
97
190
|
const toUpgrade = [];
|
|
98
191
|
const unresolved = [];
|
|
99
192
|
for (const name of kybernesisPackages(deps)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kybernesis/create",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.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",
|
|
@@ -178,9 +178,12 @@ something. Chase them a week out — an unprovisioned Vercel team on Day 1 costs
|
|
|
178
178
|
- [ ] **Confirm who at ACME is the Slack workspace admin.** Creating the Slack connector
|
|
179
179
|
requires someone who can approve a Slack app install. If that person is on holiday
|
|
180
180
|
your Day 2 is Slack-less.
|
|
181
|
-
- [ ] **
|
|
182
|
-
|
|
183
|
-
|
|
181
|
+
- [ ] **Know the shape you are aiming for** before writing any of it: one
|
|
182
|
+
`agent/agent.ts` that picks a model and mounts extensions, instructions
|
|
183
|
+
split into identity and per-surface, one subagent per department that owns
|
|
184
|
+
its own tools and skills, and an `evals/` suite that gates the deploy.
|
|
185
|
+
Every later phase adds to that skeleton; building it in a different shape
|
|
186
|
+
is what makes the eval suite and the routing evals fight you.
|
|
184
187
|
- [ ] **Confirm package versions you will pin.** As of 2026-08-06:
|
|
185
188
|
`@kybernesis/arcana@0.1.1`, `@kybernesis/enterprise@0.1.2`,
|
|
186
189
|
`@kybernesis/multiplayer@0.1.0`, `@kybernesis/evals@0.2.1`,
|
|
@@ -436,9 +439,9 @@ What to know when choosing:
|
|
|
436
439
|
principal). Prefer `session.started` over per-turn switching: prompt caches
|
|
437
440
|
are per model, and every switch re-ingests the conversation at uncached
|
|
438
441
|
prices. Resolver failures degrade to the fallback, never fail the turn.
|
|
439
|
-
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
+
- Whatever the agent runs, eval judges are configured separately in
|
|
443
|
+
`evals/evals.config.ts` and must **never** be the model under test — a model
|
|
444
|
+
grading its own output measures agreement, not correctness.
|
|
442
445
|
|
|
443
446
|
### 4.1 Install `@kybernesis/enterprise` (governance) — do this FIRST
|
|
444
447
|
|
|
@@ -532,16 +535,11 @@ npx eve add @kybernesis/multiplayer
|
|
|
532
535
|
This writes `agent/channels/slack.ts` and `agent/instructions/multiplayer.md`, and
|
|
533
536
|
declares the `SLACK_CONNECTOR_UID` env var.
|
|
534
537
|
|
|
535
|
-
>
|
|
536
|
-
>
|
|
537
|
-
> `@kybernesis/multiplayer@0.1.0` is **not yet published to npm**, so the dependency
|
|
538
|
-
> install step will fail until it is. Check first:
|
|
538
|
+
> Published and installable — `eve add` resolves the registry item and writes both
|
|
539
|
+
> files. Confirm what you are pinning before you promise a version to a client:
|
|
539
540
|
> ```bash
|
|
540
|
-
> npm view @kybernesis/multiplayer version
|
|
541
|
+
> npm view @kybernesis/multiplayer version
|
|
541
542
|
> ```
|
|
542
|
-
> If it 404s, install from the workspace checkout at `~/kyber/packages/multiplayer` (or
|
|
543
|
-
> vendor the two files by hand — they are shown below and in
|
|
544
|
-
> `agent/instructions/multiplayer.md`) and revisit once the publish lands.
|
|
545
543
|
|
|
546
544
|
The whole Slack integration is one file:
|
|
547
545
|
|
|
@@ -968,7 +966,7 @@ token minted per edge; the callee URL comes from the registry (discovery), env
|
|
|
968
966
|
var still wins. THE DEMO: revoke the edge in the admin → the caller is refused
|
|
969
967
|
(edge_not_granted) within 5 minutes, no redeploy; re-grant → restored. Run it
|
|
970
968
|
for the client — it's the whole governance story in one minute. Full lifecycle
|
|
971
|
-
proven live 2026-08-07
|
|
969
|
+
proven live 2026-08-07 between two governed agents. Budget note: the deployed agent and
|
|
972
970
|
local eval runs share the project's AI Gateway budget — size it for both.
|
|
973
971
|
|
|
974
972
|
### 4.4 Author the agent's identity and instructions
|
|
@@ -982,8 +980,7 @@ surface.ts example below). Keep always-on instructions to identity, tone, and
|
|
|
982
980
|
standing rules; procedures belong in skills (§4.3e) — the model loads those on
|
|
983
981
|
demand instead of paying for them every turn.
|
|
984
982
|
|
|
985
|
-
|
|
986
|
-
three sections worth copying:
|
|
983
|
+
Give `agent/instructions/identity.md` three sections:
|
|
987
984
|
|
|
988
985
|
1. **Identity** — who the agent is, and *how to write for Slack*: short paragraphs,
|
|
989
986
|
bullets, no headings unless the answer is genuinely long. Slack is a chat surface, not
|
|
@@ -996,9 +993,9 @@ three sections worth copying:
|
|
|
996
993
|
asks for something personal in a channel.
|
|
997
994
|
|
|
998
995
|
For per-session context, `defineDynamic` on `session.started` lets you inject
|
|
999
|
-
surface-specific instructions
|
|
1000
|
-
|
|
1001
|
-
|
|
996
|
+
surface-specific instructions — greeting a DM session by the caller's verified
|
|
997
|
+
name, and reminding a channel session that everything it posts is public. The
|
|
998
|
+
same agent should not talk to a room the way it talks to one person.
|
|
1002
999
|
|
|
1003
1000
|
**Author these with Claude Code (§4.0), and judge drafts by test, not by
|
|
1004
1001
|
reading** — paste the discovery notes, have it draft identity.md and the
|
|
@@ -1077,7 +1074,7 @@ export default defineAgent({
|
|
|
1077
1074
|
workspace })` mount with that department's scoped key) and only that
|
|
1078
1075
|
subagent gets the connection + skills + instructions. This is the default
|
|
1079
1076
|
pattern now. The plain-connection alternative below still works (it's what
|
|
1080
|
-
pre-0.30 required, and what you'll find in
|
|
1077
|
+
pre-0.30 required, and what you'll find in subagents written before it) when you
|
|
1081
1078
|
want the connection without the shipped skills:
|
|
1082
1079
|
|
|
1083
1080
|
```ts
|
|
@@ -1107,9 +1104,9 @@ export default defineAgent({
|
|
|
1107
1104
|
|
|
1108
1105
|
`agent/schedules/*.ts` for anything recurring — a Monday pipeline summary, a Friday
|
|
1109
1106
|
financial report. **Schedules live on the root agent only**; a scheduled root turn
|
|
1110
|
-
delegates to the subagent that owns the work.
|
|
1111
|
-
|
|
1112
|
-
|
|
1107
|
+
delegates to the subagent that owns the work. A worked example: fire at 02:00 UTC
|
|
1108
|
+
on Friday, delegate to a `finance` subagent, and DM the result to a configured
|
|
1109
|
+
Slack user. Note that DMing a user from a schedule needs the
|
|
1113
1110
|
`im:write` scope on the Slack connector — add it during Phase 5 or the first run fails
|
|
1114
1111
|
silently at the last step.
|
|
1115
1112
|
|
|
@@ -1147,8 +1144,8 @@ rate, delegation mix, p50/p95 duration.
|
|
|
1147
1144
|
userId by default, so PostHog sees one anonymous actor named after the service. To
|
|
1148
1145
|
attribute turns to the verified speaker, add a sibling hook that stamps the
|
|
1149
1146
|
per-message-authenticated principal via evlog's `useLogger` — on `step.started`, not
|
|
1150
|
-
`turn.started`, so evlog's turn state exists regardless of hook ordering
|
|
1151
|
-
|
|
1147
|
+
`turn.started`, so evlog's turn state exists regardless of hook ordering. Then a
|
|
1148
|
+
one-time $identify per person maps ids
|
|
1152
1149
|
to names. Per-employee telemetry must be a deliberate, disclosed choice at a client.
|
|
1153
1150
|
|
|
1154
1151
|
### 4.7 Environment variables
|
|
@@ -1931,8 +1928,8 @@ conversation — it changes the shape of the deal.
|
|
|
1931
1928
|
|
|
1932
1929
|
**Grok, on a SuperGrok or X Premium+ subscription.** Same arrangement, without
|
|
1933
1930
|
the broker: xAI's Grok Build CLI does a device login and writes a credential
|
|
1934
|
-
that is a valid bearer for `https://api.x.ai/v1`. Proven in production on
|
|
1935
|
-
twelve evals, twenty-nine gates, green on the subscription.
|
|
1931
|
+
that is a valid bearer for `https://api.x.ai/v1`. Proven in production on a
|
|
1932
|
+
self-hosted agent — twelve evals, twenty-nine gates, green on the subscription.
|
|
1936
1933
|
|
|
1937
1934
|
```bash
|
|
1938
1935
|
# on the host, as the unix user the agent runs as
|
|
@@ -1962,8 +1959,8 @@ Three things to know before you promise it to a client:
|
|
|
1962
1959
|
host for a week, it is an open question, and it would present to the client as
|
|
1963
1960
|
the agent breaking for no reason.
|
|
1964
1961
|
|
|
1965
|
-
**The model will lie about which model it is.**
|
|
1966
|
-
"Claude Opus 4.6, Anthropic" and attributed it to an instruction that exists
|
|
1962
|
+
**The model will lie about which model it is.** A self-hosted agent running Grok
|
|
1963
|
+
stated it was "Claude Opus 4.6, Anthropic" and attributed it to an instruction that exists
|
|
1967
1964
|
nowhere in its context. Verify from the host — the configured model id and the
|
|
1968
1965
|
credential in use — never by asking the agent. Expect a client to ask it in a
|
|
1969
1966
|
demo, and have the real answer ready.
|
|
@@ -2055,9 +2052,23 @@ tool. Reads went from unreliable to deterministic with about sixty lines.
|
|
|
2055
2052
|
### 11.6 The engineer layer, self-hosted
|
|
2056
2053
|
|
|
2057
2054
|
`--engineer` scaffolds a **builder subagent** that owns the build capability, so
|
|
2058
|
-
the root agent
|
|
2059
|
-
workshop sandbox, Playwright, screenshots, visual
|
|
2060
|
-
reduced version of the Vercel one.
|
|
2055
|
+
the root agent gets **no shell on the host and no build loop**. It comes with the
|
|
2056
|
+
full production loop — workshop sandbox, Playwright, screenshots, visual
|
|
2057
|
+
verification, delivery — not a reduced version of the Vercel one.
|
|
2058
|
+
|
|
2059
|
+
> Say it that way rather than "the root never gets a shell". Every eve agent has
|
|
2060
|
+
> a sandbox with `bash`, `read_file`, `write_file`, `glob` and `grep`, and an
|
|
2061
|
+
> `--engineer` app builds two templates. The root's shell is its own container.
|
|
2062
|
+
> The line that matters is host access, and the shorter phrasing blurs exactly
|
|
2063
|
+
> the boundary people quote this passage to explain.
|
|
2064
|
+
|
|
2065
|
+
**Delivery belongs to the root as well.** The engineer layer mounts on `builder`,
|
|
2066
|
+
but the agent that talks to people is the one asked for files — and delivering
|
|
2067
|
+
reads one file and copies it, which is not the blast radius that justified
|
|
2068
|
+
keeping the build loop away from the root. `kyb init --engineer` now scaffolds
|
|
2069
|
+
`agent/tools/deliver.ts` for exactly this. An agent instructed to hand over files
|
|
2070
|
+
without that mount cannot tell it is missing: it writes the contents to a memory
|
|
2071
|
+
note and reports success.
|
|
2061
2072
|
|
|
2062
2073
|
- **Subagents own their sandbox; they do NOT inherit the root's.** A builder
|
|
2063
2074
|
without its own `sandbox/sandbox.ts` gets a bare template and every screenshot
|