@kybernesis/create 0.22.0 → 0.23.1
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/add.js +37 -0
- package/dist/capabilities.d.ts +30 -0
- package/dist/capabilities.js +88 -0
- package/dist/cli.js +3 -1
- package/dist/init.js +10 -25
- package/dist/templates.d.ts +20 -0
- package/dist/templates.js +152 -0
- package/package.json +1 -1
- package/skills/kybernesis-packages/SKILL.md +10 -3
package/dist/add.js
CHANGED
|
@@ -2,6 +2,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { basename, join, resolve } from "node:path";
|
|
3
3
|
import { bold, dim, green, red, run, yellow } from "./util.js";
|
|
4
4
|
import { addBuzz } from "./buzz.js";
|
|
5
|
+
import { applyDispatch, applyEngineer, modelConfigOf } from "./capabilities.js";
|
|
5
6
|
import { CHANNEL_KINDS, arcanaMemorySlotTs, channelPlan } from "./templates.js";
|
|
6
7
|
/**
|
|
7
8
|
* `kyb add channel <kind>` — put another surface on an agent that already exists.
|
|
@@ -26,11 +27,15 @@ export async function add(what, rest) {
|
|
|
26
27
|
// the provisioner and people naturally type, so both spellings land here.
|
|
27
28
|
if (what === "buzz" || (what === "channel" && rest.find((a) => !a.startsWith("-")) === "buzz"))
|
|
28
29
|
return addBuzz();
|
|
30
|
+
if (what === "dispatch" || what === "engineer")
|
|
31
|
+
return addCapability(what);
|
|
29
32
|
if (what !== "channel") {
|
|
30
33
|
console.error(red(`kyb add: don't know how to add "${what ?? ""}".`));
|
|
31
34
|
console.log(dim(` kyb add channel <${CHANNEL_KINDS.filter((k) => k !== "none").join("|")}>`));
|
|
32
35
|
console.log(dim(` kyb add memory ${dim("— an eve 0.49 memory slot backed by Arcana (recall before every turn)")}`));
|
|
33
36
|
console.log(dim(` kyb add buzz ${dim("— the agent's own Buzz identity, tools and bridge, ready for its owner's community")}`));
|
|
37
|
+
console.log(dim(` kyb add dispatch ${dim("— call and be called by the agents the control plane allows (governed edges)")}`));
|
|
38
|
+
console.log(dim(` kyb add engineer ${dim("— the builder subagent: workshop sandbox, vision dev loop, file delivery")}`));
|
|
34
39
|
process.exitCode = 1;
|
|
35
40
|
return;
|
|
36
41
|
}
|
|
@@ -145,3 +150,35 @@ export function addMemory(cwd = resolve(process.cwd())) {
|
|
|
145
150
|
console.log(dim(` recall before every turn from ARCANA_COMPANY_WORKSPACE; capture off; ${extensionMounted ? "tools from the extension" : "remember/recall/search as memory__* tools"}`));
|
|
146
151
|
console.log(dim(" needs eve >= 0.49 and @kybernesis/arcana >= 0.4 — kyb upgrade carries both. Then: npx eve info"));
|
|
147
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* `kyb add dispatch` / `kyb add engineer` — what `kyb init` now builds in, put
|
|
155
|
+
* on an agent scaffolded before it did. Same code path as init, so an added
|
|
156
|
+
* capability is the same files a new agent gets.
|
|
157
|
+
*/
|
|
158
|
+
function addCapability(what) {
|
|
159
|
+
const dir = resolve(process.cwd());
|
|
160
|
+
if (!existsSync(join(dir, "package.json")) || !existsSync(join(dir, "agent"))) {
|
|
161
|
+
console.error(red(`kyb add ${what}: run this inside an agent directory.`));
|
|
162
|
+
process.exitCode = 1;
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const host = existsSync(join(dir, "scripts/eve-server.sh")) ? "exe" : "vercel";
|
|
166
|
+
console.log(bold(`kyb add ${what}`));
|
|
167
|
+
console.log(dim(` agent: ${agentNameOf(dir)}\n host: ${host}`));
|
|
168
|
+
if (what === "dispatch") {
|
|
169
|
+
for (const file of applyDispatch(dir))
|
|
170
|
+
console.log(green(` + ${file}`));
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
if (existsSync(join(dir, "agent/subagents/builder"))) {
|
|
174
|
+
console.log(yellow(" agent/subagents/builder already exists — leaving it alone."));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const { files, steps } = applyEngineer(dir, host, modelConfigOf(dir));
|
|
178
|
+
for (const file of files)
|
|
179
|
+
console.log(green(` + ${file}`));
|
|
180
|
+
if (steps.length)
|
|
181
|
+
console.log(`\n${bold("Then:")}\n${steps.map((s, i) => ` ${i + 1}. ${s}`).join("\n")}`);
|
|
182
|
+
}
|
|
183
|
+
console.log(dim("\n Type-check, build and restart the agent for it to take effect."));
|
|
184
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type HostKind } from "./templates.js";
|
|
2
|
+
import type { ModelScaffoldConfig } from "./model-reach.js";
|
|
3
|
+
/**
|
|
4
|
+
* The registry items install the newest release of each third-party
|
|
5
|
+
* extension, and those are built on a later eve than the certified one, which
|
|
6
|
+
* eve then refuses at discovery ("has no compile or runtime usage") and the
|
|
7
|
+
* agent never boots. These are the releases proven on the certified eve.
|
|
8
|
+
* Move them with the eve certification, never on their own.
|
|
9
|
+
*/
|
|
10
|
+
export declare const ENGINEER_EXTENSION_PINS: readonly ["@agent-browser/eve@0.34.0", "@github-tools/eve-extension@0.8.0"];
|
|
11
|
+
/**
|
|
12
|
+
* Agent-to-agent: the eve channel becomes the governed dispatch door (people
|
|
13
|
+
* still come in through kybernesisAuth in the same walk) and granted peers
|
|
14
|
+
* become tools. arpAuth is put back into the walk when the agent has the
|
|
15
|
+
* identity item, since the new channel replaces the file that carried it.
|
|
16
|
+
*/
|
|
17
|
+
export declare function applyDispatch(dir: string): string[];
|
|
18
|
+
/**
|
|
19
|
+
* The engineer: a declared `builder` subagent with its own workshop sandbox,
|
|
20
|
+
* memory and skills, plus the browser and GitHub tools at the root.
|
|
21
|
+
*/
|
|
22
|
+
export declare function applyEngineer(dir: string, host: HostKind, model: ModelScaffoldConfig): {
|
|
23
|
+
files: string[];
|
|
24
|
+
steps: string[];
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* The model config an existing agent was authored with, read back from its
|
|
28
|
+
* agent/agent.ts, so a builder added later runs the same route as its root.
|
|
29
|
+
*/
|
|
30
|
+
export declare function modelConfigOf(dir: string): ModelScaffoldConfig;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { finalizeGithubToolsRegistryMount, wireArpAuth } from "./init.js";
|
|
4
|
+
import { DISPATCH_DEP, dispatchEveChannelTs, dispatchPeersTs, engineerPlan } from "./templates.js";
|
|
5
|
+
import { run, yellow } from "./util.js";
|
|
6
|
+
/**
|
|
7
|
+
* The capabilities every company agent carries, applied the same way by
|
|
8
|
+
* `kyb init` and by `kyb add` on an agent built before they were standard.
|
|
9
|
+
* Each returns what it wrote, so `add` can report it.
|
|
10
|
+
*/
|
|
11
|
+
// Official eve-registry limbs installed with the engineer subagent.
|
|
12
|
+
// connection/vercel is Vercel-Connect-backed, so it is VERCEL-HOST ONLY: on a
|
|
13
|
+
// self-hosted agent it cannot get an OIDC token and the agent fails to boot.
|
|
14
|
+
const ENGINEER_ITEMS_ALL = ["extension/agent-browser", "extension/github-tools"];
|
|
15
|
+
const ENGINEER_ITEMS_VERCEL = ["connection/vercel"];
|
|
16
|
+
/**
|
|
17
|
+
* The registry items install the newest release of each third-party
|
|
18
|
+
* extension, and those are built on a later eve than the certified one, which
|
|
19
|
+
* eve then refuses at discovery ("has no compile or runtime usage") and the
|
|
20
|
+
* agent never boots. These are the releases proven on the certified eve.
|
|
21
|
+
* Move them with the eve certification, never on their own.
|
|
22
|
+
*/
|
|
23
|
+
export const ENGINEER_EXTENSION_PINS = ["@agent-browser/eve@0.34.0", "@github-tools/eve-extension@0.8.0"];
|
|
24
|
+
function write(dir, path, content) {
|
|
25
|
+
const full = join(dir, path);
|
|
26
|
+
mkdirSync(join(full, ".."), { recursive: true });
|
|
27
|
+
writeFileSync(full, content);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Agent-to-agent: the eve channel becomes the governed dispatch door (people
|
|
31
|
+
* still come in through kybernesisAuth in the same walk) and granted peers
|
|
32
|
+
* become tools. arpAuth is put back into the walk when the agent has the
|
|
33
|
+
* identity item, since the new channel replaces the file that carried it.
|
|
34
|
+
*/
|
|
35
|
+
export function applyDispatch(dir) {
|
|
36
|
+
run("npm", ["install", DISPATCH_DEP, "--no-audit", "--no-fund"], { cwd: dir, allowFail: true });
|
|
37
|
+
let channel = dispatchEveChannelTs();
|
|
38
|
+
if (existsSync(join(dir, "agent/channels/arp.ts")))
|
|
39
|
+
channel = wireArpAuth(channel) ?? channel;
|
|
40
|
+
write(dir, "agent/channels/eve.ts", channel);
|
|
41
|
+
write(dir, "agent/tools/peers.ts", dispatchPeersTs());
|
|
42
|
+
return ["agent/channels/eve.ts", "agent/tools/peers.ts"];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The engineer: a declared `builder` subagent with its own workshop sandbox,
|
|
46
|
+
* memory and skills, plus the browser and GitHub tools at the root.
|
|
47
|
+
*/
|
|
48
|
+
export function applyEngineer(dir, host, model) {
|
|
49
|
+
const plan = engineerPlan(host, model);
|
|
50
|
+
run("npm", ["install", ...plan.deps, "--no-audit", "--no-fund"], { cwd: dir, allowFail: true });
|
|
51
|
+
const items = [...ENGINEER_ITEMS_ALL, ...(host === "vercel" ? ENGINEER_ITEMS_VERCEL : [])];
|
|
52
|
+
for (const item of items) {
|
|
53
|
+
const ok = run("npx", ["eve", "add", item, "--overwrite"], { cwd: dir, allowFail: true });
|
|
54
|
+
if (item === "extension/github-tools")
|
|
55
|
+
finalizeGithubToolsRegistryMount(dir, ok);
|
|
56
|
+
if (!ok)
|
|
57
|
+
console.log(yellow(` ! ${item} did not install cleanly — re-run: npx eve add ${item}`));
|
|
58
|
+
}
|
|
59
|
+
// --legacy-peer-deps: both declare peer ranges that trail the certified eve.
|
|
60
|
+
const pinned = run("npm", ["install", "--save-exact", "--legacy-peer-deps", ...ENGINEER_EXTENSION_PINS, "--no-audit", "--no-fund"], {
|
|
61
|
+
cwd: dir,
|
|
62
|
+
allowFail: true,
|
|
63
|
+
});
|
|
64
|
+
if (!pinned)
|
|
65
|
+
console.log(yellow(` ! could not pin ${ENGINEER_EXTENSION_PINS.join(" ")} — install them by hand or discovery refuses the newer ones`));
|
|
66
|
+
for (const file of plan.files)
|
|
67
|
+
write(dir, file.path, file.content);
|
|
68
|
+
// A declared subagent sees none of the root's skills, so the memory skills
|
|
69
|
+
// its instructions name are copied in beside it.
|
|
70
|
+
const skills = join(dir, "node_modules/@kybernesis/arcana/dist/extension/skills");
|
|
71
|
+
if (existsSync(skills))
|
|
72
|
+
cpSync(skills, join(dir, "agent/subagents/builder/skills"), { recursive: true });
|
|
73
|
+
else
|
|
74
|
+
console.log(yellow(` ! arcana skills not found at ${skills} — copy them into agent/subagents/builder/skills`));
|
|
75
|
+
return { files: plan.files.map((f) => f.path), steps: plan.steps };
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The model config an existing agent was authored with, read back from its
|
|
79
|
+
* agent/agent.ts, so a builder added later runs the same route as its root.
|
|
80
|
+
*/
|
|
81
|
+
export function modelConfigOf(dir) {
|
|
82
|
+
const source = existsSync(join(dir, "agent/agent.ts")) ? readFileSync(join(dir, "agent/agent.ts"), "utf8") : "";
|
|
83
|
+
const reach = /\breach\s*:\s*["']([a-z-]+)["']/.exec(source)?.[1];
|
|
84
|
+
const model = /\bmodel\s*:\s*["']([^"']+)["']/.exec(source)?.[1];
|
|
85
|
+
if (reach === "claude-sub")
|
|
86
|
+
return { reach: "claude-sub", model: (model ?? "claude-opus-5").replace(/^anthropic\//, "") };
|
|
87
|
+
return { reach: "default", model: model ?? "anthropic/claude-sonnet-5" };
|
|
88
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -222,7 +222,7 @@ ${bold("kyb")} ${dim(VERSION)} — Kybernesis agent scaffolder & FDE toolkit
|
|
|
222
222
|
${dim(" Every command below exists in this build. If one is missing, the install is older than the docs:")}
|
|
223
223
|
${dim(" npm i -g @kybernesis/create@latest")}
|
|
224
224
|
|
|
225
|
-
${bold("kyb init [name]")} scaffold a Kybernesis eve agent (core: enterprise + arcana + evals)
|
|
225
|
+
${bold("kyb init [name]")} scaffold a Kybernesis eve agent (core: enterprise + arcana + dispatch + evals)
|
|
226
226
|
--channel=<kind> ${dim("none|slack|imessage|telegram|discord|web (default: none)")}
|
|
227
227
|
--host=<kind> ${dim("vercel|exe (default: vercel)")}
|
|
228
228
|
--subagents=a,b ${dim("department subagents (default: none)")}
|
|
@@ -235,6 +235,8 @@ ${dim(" npm i -g @kybernesis/create@latest")}
|
|
|
235
235
|
${dim("slack|imessage|telegram|discord|web — writes the channel, deps and env")}
|
|
236
236
|
${bold("kyb add buzz")} ${dim("the agent's own Buzz identity, tools and bridge (connected from the Channels page)")}
|
|
237
237
|
${bold("kyb add memory")} ${dim("eve 0.49 memory slot backed by Arcana: recall before every turn")}
|
|
238
|
+
${bold("kyb add dispatch")} ${dim("agent-to-agent: governed door + control-plane peers as tools")}
|
|
239
|
+
${bold("kyb add engineer")} ${dim("the builder subagent: workshop sandbox, vision dev loop, delivery")}
|
|
238
240
|
${bold("kyb doctor")} preflight checks (keys, issuer, envs, discovery)
|
|
239
241
|
${bold("kyb skills")} install/refresh the FDE skill suite for Claude Code
|
|
240
242
|
--global ${dim("install to ~/.claude/skills instead of this repo")}
|
package/dist/init.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { appendFileSync, chmodSync, copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
import { DEFAULT_ISSUER, EVE_VERSION, REGISTRY_URL, ask, bold, closePrompts, dim, green, run, slug, red, yellow, } from "./util.js";
|
|
4
|
-
import { CHANNEL_KINDS, channelPlan, computerPlan,
|
|
4
|
+
import { CHANNEL_KINDS, channelPlan, computerPlan, envExample, evalFileTs, exeEvalConfigTs, evalScript, hostAgentTs, hostSteps, githubToolsMountTs, identityMd, rootArcanaTs, subagentAgentTs, subagentArcanaTs, subagentInstructionsMd, } from "./templates.js";
|
|
5
5
|
import { suiteDir } from "./skills.js";
|
|
6
|
+
import { applyDispatch, applyEngineer } from "./capabilities.js";
|
|
6
7
|
import { configureArcana } from "./arcana.js";
|
|
7
8
|
import { upsertEnv } from "./envfile.js";
|
|
8
9
|
import { systemdRestartCommand } from "./systemd.js";
|
|
@@ -21,11 +22,6 @@ const CORE_ITEMS = ["enterprise", "arcana", "evals", "identity"];
|
|
|
21
22
|
// Vercel function cannot keep a desktop alive, so there — and only there —
|
|
22
23
|
// both are skipped, and init says so out loud.
|
|
23
24
|
const CORE_EXE_ITEMS = ["vault"];
|
|
24
|
-
// Official eve-registry limbs installed with the engineer subagent.
|
|
25
|
-
// connection/vercel is Vercel-Connect-backed, so it is VERCEL-HOST ONLY: on a
|
|
26
|
-
// self-hosted agent it cannot get an OIDC token and the agent fails to boot.
|
|
27
|
-
const ENGINEER_ITEMS_ALL = ["extension/agent-browser", "extension/github-tools"];
|
|
28
|
-
const ENGINEER_ITEMS_VERCEL = ["connection/vercel"];
|
|
29
25
|
/**
|
|
30
26
|
* Replace Eve's freshly installed registry mount with the Kybernesis-managed
|
|
31
27
|
* token-aware form. A failed registry add must not create a capability file.
|
|
@@ -132,6 +128,9 @@ export async function init(rawName, options = {}) {
|
|
|
132
128
|
for (const item of CORE_ITEMS) {
|
|
133
129
|
run("npx", ["eve", "add", `@kybernesis/${item}`, "--overwrite"], { cwd: dir });
|
|
134
130
|
}
|
|
131
|
+
// Core too: other agents the company allows can call this one, and it can
|
|
132
|
+
// call them, governed by edges granted in the control plane.
|
|
133
|
+
applyDispatch(dir);
|
|
135
134
|
// Anthropic is always the exe eval judge. Both providers are installed on
|
|
136
135
|
// every exe agent: exeModelReach can be switched to any route at boot, and
|
|
137
136
|
// between them the routes call both.
|
|
@@ -177,18 +176,10 @@ export async function init(rawName, options = {}) {
|
|
|
177
176
|
` This agent cannot be connected to a desktop until they install.`));
|
|
178
177
|
}
|
|
179
178
|
}
|
|
180
|
-
|
|
181
|
-
if (
|
|
179
|
+
let engSteps = [];
|
|
180
|
+
if (engineer) {
|
|
182
181
|
console.log(bold("\n2c Engineer subagent: workshop sandbox + vision dev loop …"));
|
|
183
|
-
|
|
184
|
-
const engItems = [...ENGINEER_ITEMS_ALL, ...(host === "vercel" ? ENGINEER_ITEMS_VERCEL : [])];
|
|
185
|
-
for (const item of engItems) {
|
|
186
|
-
const ok = run("npx", ["eve", "add", item, "--overwrite"], { cwd: dir, allowFail: true });
|
|
187
|
-
if (item === "extension/github-tools")
|
|
188
|
-
finalizeGithubToolsRegistryMount(dir, ok);
|
|
189
|
-
if (!ok)
|
|
190
|
-
console.log(yellow(` ! ${item} did not install cleanly — re-run: npx eve add ${item}`));
|
|
191
|
-
}
|
|
182
|
+
engSteps = applyEngineer(dir, host, model).steps;
|
|
192
183
|
}
|
|
193
184
|
const compPlan = host === "exe" ? computerPlan(name) : null;
|
|
194
185
|
if (host !== "exe") {
|
|
@@ -382,13 +373,6 @@ export async function init(rawName, options = {}) {
|
|
|
382
373
|
mkdirSync(join(full, ".."), { recursive: true });
|
|
383
374
|
writeFileSync(full, file.content);
|
|
384
375
|
}
|
|
385
|
-
if (engPlan) {
|
|
386
|
-
for (const file of engPlan.files) {
|
|
387
|
-
const full = join(dir, file.path);
|
|
388
|
-
mkdirSync(join(full, ".."), { recursive: true });
|
|
389
|
-
writeFileSync(full, file.content);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
376
|
if (compPlan) {
|
|
393
377
|
for (const file of compPlan.files) {
|
|
394
378
|
const full = join(dir, file.path);
|
|
@@ -412,6 +396,7 @@ export async function init(rawName, options = {}) {
|
|
|
412
396
|
: yellow(" ! verify manually: npm run typecheck && npx eve info"));
|
|
413
397
|
const parts = [
|
|
414
398
|
"governed (enterprise)",
|
|
399
|
+
"agent-to-agent (dispatch)",
|
|
415
400
|
"remembering (arcana)",
|
|
416
401
|
"self-testing (evals)",
|
|
417
402
|
channel === "none" ? null : `${channel} channel`,
|
|
@@ -427,7 +412,7 @@ export async function init(rawName, options = {}) {
|
|
|
427
412
|
`Arcana: create workspaces (${name}-company, ${name}-eval${depts.map((d) => `, ${name}-${d}`).join("")}) + scoped kb_ keys; fill .env.local from .env.example`,
|
|
428
413
|
...hostSteps(host, name, model),
|
|
429
414
|
...plan.steps,
|
|
430
|
-
...
|
|
415
|
+
...engSteps,
|
|
431
416
|
...(compPlan?.steps ?? []),
|
|
432
417
|
`Control plane: register agent "${name}" at ${issuer}/agents + grant the pilot cohort`,
|
|
433
418
|
`npm run eval → green → deploy → live smoke + the revoke demo`,
|
package/dist/templates.d.ts
CHANGED
|
@@ -41,6 +41,12 @@ export declare function channelPlan(kind: ChannelKind, name: string, host: HostK
|
|
|
41
41
|
export type HostKind = "vercel" | "exe";
|
|
42
42
|
export declare function hostAgentTs(host: HostKind, model: ModelScaffoldConfig): string;
|
|
43
43
|
export declare function hostSteps(host: HostKind, name: string, model: ModelScaffoldConfig): string[];
|
|
44
|
+
/** agent/channels/eve.ts: people, granted agents, and the local eval runner, through one door. */
|
|
45
|
+
export declare function dispatchEveChannelTs(): string;
|
|
46
|
+
/** agent/tools/peers.ts: every agent this one may call, as tools, resolved per turn. */
|
|
47
|
+
export declare function dispatchPeersTs(): string;
|
|
48
|
+
/** Pinned to the eve line the scaffold certifies, like every other @kybernesis dep it installs. */
|
|
49
|
+
export declare const DISPATCH_DEP = "@kybernesis/dispatch@^0.7.0";
|
|
44
50
|
export interface EngineerPlan {
|
|
45
51
|
files: Array<{
|
|
46
52
|
path: string;
|
|
@@ -49,6 +55,20 @@ export interface EngineerPlan {
|
|
|
49
55
|
deps: string[];
|
|
50
56
|
steps: string[];
|
|
51
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* What the builder is told. A declared subagent inherits nothing from the
|
|
60
|
+
* root, instructions included, so without this it builds with no idea it is
|
|
61
|
+
* briefed second-hand, that it remembers, or where its authority stops.
|
|
62
|
+
* Agent-facing prose: no agent or company names, it ships to every customer.
|
|
63
|
+
*/
|
|
64
|
+
export declare function builderInstructionsMd(): string;
|
|
65
|
+
/**
|
|
66
|
+
* The builder's memory: the company brain the root uses, not a workspace of
|
|
67
|
+
* its own. A company agent has one brain, and the agent that builds was the one
|
|
68
|
+
* remembering nothing about what it built. ARCANA_BUILDER_WORKSPACE splits it
|
|
69
|
+
* off for an agent that wants a separate one.
|
|
70
|
+
*/
|
|
71
|
+
export declare function builderArcanaTs(): string;
|
|
52
72
|
export declare function engineerPlan(host: HostKind, model: ModelScaffoldConfig): EngineerPlan;
|
|
53
73
|
/**
|
|
54
74
|
* The eval judge, for an exe.dev host.
|
package/dist/templates.js
CHANGED
|
@@ -488,6 +488,62 @@ export function hostSteps(host, name, model) {
|
|
|
488
488
|
`npx eve deploy`,
|
|
489
489
|
];
|
|
490
490
|
}
|
|
491
|
+
// ── Agent-to-agent (dispatch) ───────────────────────────────────────────
|
|
492
|
+
// Every agent is reachable by the agents its company allows, and can call
|
|
493
|
+
// them, with nothing named in code: the edge is granted in the control plane.
|
|
494
|
+
// The receiving side is the eve channel itself, so it REPLACES the plain
|
|
495
|
+
// governed channel the enterprise item writes; people still come in through
|
|
496
|
+
// kybernesisAuth in the same walk.
|
|
497
|
+
/** agent/channels/eve.ts: people, granted agents, and the local eval runner, through one door. */
|
|
498
|
+
export function dispatchEveChannelTs() {
|
|
499
|
+
return `import { localDev } from "eve/channels/auth";
|
|
500
|
+
import { dispatchChannel } from "@kybernesis/dispatch";
|
|
501
|
+
import { kybernesisAuth } from "@kybernesis/enterprise";
|
|
502
|
+
|
|
503
|
+
// One door, three kinds of caller, each its own decision:
|
|
504
|
+
// - people, through KYBER Studio, the phone and the web app: kybernesisAuth
|
|
505
|
+
// admits only an identity session holding a grant on this agent;
|
|
506
|
+
// - other agents: dispatchChannel's governed walk admits a short-lived A2A
|
|
507
|
+
// token minted for an edge granted in the control plane naming this agent
|
|
508
|
+
// as callee. Nothing is listed here; the grant lives in the control plane;
|
|
509
|
+
// - the eval runner: localDev() accepts loopback only and is inert in
|
|
510
|
+
// production. Local evals send no credentials at all.
|
|
511
|
+
//
|
|
512
|
+
// KYBERNESIS_AGENT must match the control-plane registration exactly,
|
|
513
|
+
// including case: the mint checks the callee by name.
|
|
514
|
+
export default dispatchChannel({
|
|
515
|
+
governed: {
|
|
516
|
+
issuer: process.env.KYBERNESIS_ISSUER ?? "https://agent.kybernesis.ai",
|
|
517
|
+
agent: process.env.KYBERNESIS_AGENT!,
|
|
518
|
+
},
|
|
519
|
+
extraAuth: [
|
|
520
|
+
kybernesisAuth({
|
|
521
|
+
issuer: process.env.KYBERNESIS_ISSUER ?? "https://agent.kybernesis.ai",
|
|
522
|
+
agent: process.env.KYBERNESIS_AGENT!,
|
|
523
|
+
}),
|
|
524
|
+
localDev(),
|
|
525
|
+
],
|
|
526
|
+
});
|
|
527
|
+
`;
|
|
528
|
+
}
|
|
529
|
+
/** agent/tools/peers.ts: every agent this one may call, as tools, resolved per turn. */
|
|
530
|
+
export function dispatchPeersTs() {
|
|
531
|
+
return `import { governedPeers } from "@kybernesis/dispatch";
|
|
532
|
+
|
|
533
|
+
// Every agent this one is ALLOWED to call, as tools, resolved from the control
|
|
534
|
+
// plane at the start of each turn. Granting an edge there is the whole job:
|
|
535
|
+
// the tool appears on the next turn and goes when the edge is revoked, with no
|
|
536
|
+
// file to write and no redeploy.
|
|
537
|
+
//
|
|
538
|
+
// These calls carry THIS agent's identity. Where a peer holds personal data and
|
|
539
|
+
// should answer as the person instead, declare it with remotePeer() under
|
|
540
|
+
// agent/subagents/ (it forwards the principal) and list its name in
|
|
541
|
+
// \`declared\` here so it is not offered twice.
|
|
542
|
+
export default governedPeers();
|
|
543
|
+
`;
|
|
544
|
+
}
|
|
545
|
+
/** Pinned to the eve line the scaffold certifies, like every other @kybernesis dep it installs. */
|
|
546
|
+
export const DISPATCH_DEP = "@kybernesis/dispatch@^0.7.0";
|
|
491
547
|
/** The workshop sandbox, per host. Same recipe; different backend. */
|
|
492
548
|
function workshopSandbox(host) {
|
|
493
549
|
if (host === "exe") {
|
|
@@ -599,6 +655,100 @@ export default defineSandbox(() =>
|
|
|
599
655
|
);
|
|
600
656
|
`;
|
|
601
657
|
}
|
|
658
|
+
/**
|
|
659
|
+
* What the builder is told. A declared subagent inherits nothing from the
|
|
660
|
+
* root, instructions included, so without this it builds with no idea it is
|
|
661
|
+
* briefed second-hand, that it remembers, or where its authority stops.
|
|
662
|
+
* Agent-facing prose: no agent or company names, it ships to every customer.
|
|
663
|
+
*/
|
|
664
|
+
export function builderInstructionsMd() {
|
|
665
|
+
return `# Identity
|
|
666
|
+
|
|
667
|
+
You are the builder. You write and ship software: prototypes, deliverables,
|
|
668
|
+
internal tools and fixes. You have the workshop: a sandbox with a shell, a
|
|
669
|
+
filesystem, a browser you can see through, and file delivery.
|
|
670
|
+
|
|
671
|
+
You receive fully briefed tasks from the agent you work for and never see the
|
|
672
|
+
original conversation. Treat the message as the whole brief.
|
|
673
|
+
|
|
674
|
+
# How you work
|
|
675
|
+
|
|
676
|
+
Verify by looking. You can screenshot what you built. A page you have not
|
|
677
|
+
looked at is a page you have not finished.
|
|
678
|
+
|
|
679
|
+
Start dev servers detached. A foreground server blocks the turn and hangs the
|
|
680
|
+
whole task.
|
|
681
|
+
|
|
682
|
+
Say what you actually ran: commands, versions, what passed. "Should work" is not
|
|
683
|
+
a report.
|
|
684
|
+
|
|
685
|
+
Deliver, don't describe. When the output is a file, deliver it and give the
|
|
686
|
+
plain URL. Markdown around a link breaks it in some chat apps.
|
|
687
|
+
|
|
688
|
+
Stop at production. Preview deploys are yours; promoting to production is
|
|
689
|
+
always a person's decision.
|
|
690
|
+
|
|
691
|
+
# Memory
|
|
692
|
+
|
|
693
|
+
You share the company's long-term memory (Arcana) through the \`arcana\`
|
|
694
|
+
connection: recall, search, timeline, remember and brain notes.
|
|
695
|
+
|
|
696
|
+
- Look it up before you start. Never claim something is unknown without
|
|
697
|
+
searching; an empty entity recall is not enough, so escalate to search.
|
|
698
|
+
- Record what you built, what you tried that failed, and the decisions behind a
|
|
699
|
+
stack choice. A project shipped and forgotten teaches nobody.
|
|
700
|
+
- Long-form material goes in a brain note: \`arcana_brain_write\` and
|
|
701
|
+
\`arcana_brain_add\`, always both, or nothing can find it.
|
|
702
|
+
- Load the \`recall\`, \`remember\` or \`brain-note\` skill when the task
|
|
703
|
+
is more than one obvious call.
|
|
704
|
+
|
|
705
|
+
# When the brief is thin
|
|
706
|
+
|
|
707
|
+
Build anyway. You cannot ask anyone; the person who asked is one step removed.
|
|
708
|
+
Take the most reasonable reading, say plainly what you assumed at the top, and
|
|
709
|
+
build what that reading deserves.
|
|
710
|
+
`;
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* The builder's memory: the company brain the root uses, not a workspace of
|
|
714
|
+
* its own. A company agent has one brain, and the agent that builds was the one
|
|
715
|
+
* remembering nothing about what it built. ARCANA_BUILDER_WORKSPACE splits it
|
|
716
|
+
* off for an agent that wants a separate one.
|
|
717
|
+
*/
|
|
718
|
+
export function builderArcanaTs() {
|
|
719
|
+
return `import { defineMcpClientConnection } from "eve/connections";
|
|
720
|
+
|
|
721
|
+
// The company brain, shared with the root agent by design: knowing and doing
|
|
722
|
+
// are different jobs, the memory is not. No placeholder: a builder quietly
|
|
723
|
+
// addressing a workspace nobody owns is amnesia that looks like a model
|
|
724
|
+
// problem. Set it with \`kyb arcana\`.
|
|
725
|
+
const workspace = process.env.ARCANA_BUILDER_WORKSPACE ?? process.env.ARCANA_COMPANY_WORKSPACE;
|
|
726
|
+
if (!workspace) {
|
|
727
|
+
throw new Error("ARCANA_COMPANY_WORKSPACE is not set — run \`kyb arcana\` to set the workspace and its key.");
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
export default defineMcpClientConnection({
|
|
731
|
+
url: "https://mcp.arcana.kybernesis.ai/mcp",
|
|
732
|
+
description:
|
|
733
|
+
"The company's long-term memory (Arcana), shared with the agent you work for: what was built, what was tried, what failed, and the decisions behind it.",
|
|
734
|
+
auth: {
|
|
735
|
+
getToken: async () => {
|
|
736
|
+
// The eval brain is a different workspace with its own key (Arcana keys
|
|
737
|
+
// answer 403 outside their own workspace), recognised by name.
|
|
738
|
+
const evalWorkspace = process.env.ARCANA_EVAL_WORKSPACE;
|
|
739
|
+
const isEval = evalWorkspace ? workspace === evalWorkspace : workspace.endsWith("-eval");
|
|
740
|
+
const token =
|
|
741
|
+
(isEval ? process.env.ARCANA_EVAL_API_KEY : undefined) ??
|
|
742
|
+
process.env.ARCANA_BUILDER_API_KEY ??
|
|
743
|
+
process.env.ARCANA_API_KEY;
|
|
744
|
+
if (!token) throw new Error("ARCANA_API_KEY is not set.");
|
|
745
|
+
return { token };
|
|
746
|
+
},
|
|
747
|
+
},
|
|
748
|
+
headers: { "X-Kyberagent-Agent": async () => workspace },
|
|
749
|
+
});
|
|
750
|
+
`;
|
|
751
|
+
}
|
|
602
752
|
export function engineerPlan(host, model) {
|
|
603
753
|
const onExe = host === "exe";
|
|
604
754
|
const renderedModel = agentModelTemplate(host, model);
|
|
@@ -652,6 +802,8 @@ export { default } from "@kybernesis/engineer";
|
|
|
652
802
|
path: "agent/subagents/builder/sandbox/sandbox.ts",
|
|
653
803
|
content: workshopSandbox(host),
|
|
654
804
|
},
|
|
805
|
+
{ path: "agent/subagents/builder/instructions.md", content: builderInstructionsMd() },
|
|
806
|
+
{ path: "agent/subagents/builder/connections/arcana.ts", content: builderArcanaTs() },
|
|
655
807
|
];
|
|
656
808
|
if (onExe) {
|
|
657
809
|
files.push({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kybernesis/create",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.1",
|
|
4
4
|
"description": "The Kybernesis agent scaffolder and FDE toolkit: one command to a governed, remembering, multiplayer, self-testing eve agent \u2014 plus doctor and upgrade.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -89,9 +89,11 @@ then `eve add @kybernesis/<item>`). Each covers one axis:
|
|
|
89
89
|
it also carries the owner's setup routes (`GET /eve/v1/kyb/setup`,
|
|
90
90
|
`POST .../setup/env`, `POST .../setup/action`): a `manage` grant only;
|
|
91
91
|
allowlisted env (`EXE_MODEL`, `EXE_LLM_URL`, `ANTHROPIC_API_KEY`,
|
|
92
|
-
`OPENAI_API_KEY`, `SLACK_BOT_TOKEN`, `SLACK_SIGNING_SECRET
|
|
92
|
+
`OPENAI_API_KEY`, `SLACK_BOT_TOKEN`, `SLACK_SIGNING_SECRET`, and since 0.20
|
|
93
|
+
`KYBERNESIS_VOICE_OPENAI_KEY`, the owner's key for voice calls, with a `voice`
|
|
94
|
+
setup item shown when @kybernesis/voice is installed) and the actions
|
|
93
95
|
`claude.login` (link, then the code on stdin), `linq.signup`/`linq.verify`, `linq.contacts`/`linq.contact.add`/`linq.contact.remove` (who can text a shared line, 0.15),
|
|
94
|
-
`link.login`/`link.logout`, `wallet.add` (0.16: adds the payments item to an agent with a computer, rebuilds, rolls back on a failed build, restarts only after a good one) and `vnc.password`. Every change applies at one restart that
|
|
96
|
+
`link.login`/`link.logout`, `wallet.add` (0.16: adds the payments item to an agent with a computer, rebuilds, rolls back on a failed build, restarts only after a good one), `linq.add` (0.18: the same for the iMessage channel, offered while `agent/channels/linq.ts` is missing, so an agent built without iMessage can answer the line it signs up), `slack.add` (0.19: the control-plane Slack channel via `kyb add channel slack`, offered to a registered agent on its own host with no Slack channel; the client then asks the control plane to make the agent's Slack app), `buzz.add` (0.19: `kyb add buzz` plus a build, answered 202 at once because the Buzz CLI build takes minutes; the `buzz` item reports `busy`, then the Buzz card or why it failed) and `vnc.password`. Every change applies at one restart that
|
|
95
97
|
waits until no conversation is in flight (deploy.sh's count). A BYOK key only
|
|
96
98
|
takes effect when `agent/agent.ts` uses that provider; status says so.
|
|
97
99
|
Since 0.12 it also takes `KYB_MODEL_REACH` (`gateway`, `byok`,
|
|
@@ -129,7 +131,12 @@ then `eve add @kybernesis/<item>`). Each covers one axis:
|
|
|
129
131
|
token, grants the person who ran it, idempotent by name),
|
|
130
132
|
`deploy` (copy + install + restart + PROVE it; `eve deploy` on Vercel),
|
|
131
133
|
`doctor`, `upgrade` (carries eve to the Kybernesis-CERTIFIED pin, never
|
|
132
|
-
blind latest), `skills`. Ships THIS skill suite.
|
|
134
|
+
blind latest), `skills`. Ships THIS skill suite. Since 0.23 every `init`
|
|
135
|
+
carries dispatch (the governed `agent/channels/eve.ts` plus
|
|
136
|
+
`agent/tools/peers.ts` = `governedPeers()`), and `--engineer` writes a
|
|
137
|
+
declared `builder` subagent with its own instructions, the company Arcana
|
|
138
|
+
connection and the memory skills. `kyb add dispatch` and `kyb add engineer`
|
|
139
|
+
retrofit an agent built before that.
|
|
133
140
|
|
|
134
141
|
## Gotchas that each cost a real debugging session
|
|
135
142
|
|