@kybernesis/create 0.24.4 → 0.26.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/README.md +4 -0
- package/dist/capabilities.d.ts +5 -7
- package/dist/capabilities.js +8 -9
- package/dist/doctor.d.ts +5 -0
- package/dist/doctor.js +41 -1
- package/dist/init.js +20 -1
- package/dist/line-hop.d.ts +74 -0
- package/dist/line-hop.js +207 -0
- package/dist/studio-core.d.ts +68 -0
- package/dist/studio-core.js +124 -0
- package/dist/templates.js +7 -5
- package/dist/upgrade.js +111 -37
- package/dist/util.d.ts +3 -2
- package/dist/util.js +3 -2
- package/package.json +2 -2
- package/skills/eve-building/SKILL.md +2 -2
- package/skills/fde-engagement/references/playbook.md +7 -5
package/README.md
CHANGED
|
@@ -14,6 +14,10 @@ npx @kybernesis/create init acme-atlas
|
|
|
14
14
|
|
|
15
15
|
`kyb init --host=exe` always installs the agent's computer (`@kybernesis/computer`: a persistent desktop with Chrome a person can watch and take over) and its vault (`@kybernesis/vault`: per-agent, per-person logins and cards it fills on that computer without seeing them). They are not options. A Vercel host gets neither, because a function cannot keep a desktop alive, and init says so.
|
|
16
16
|
|
|
17
|
+
## Core on every Studio agent
|
|
18
|
+
|
|
19
|
+
`kyb init --studio` (every agent the provisioner builds) also installs what a person sees beyond words in KYBER Studio and on the phone: cards under replies (`@kybernesis/cards` and the `show` tool from the `cards` registry item), artifacts (`@kybernesis/artifacts`: pages the agent makes and keeps, served by manage) and magic (`@kybernesis/magic`: proactive heads-ups, off until each person turns them on). On a Linq agent, magic delivers into the iMessage thread the person last used. `kyb upgrade` adds whichever of the three an agent with `@kybernesis/manage` is missing, lists them in `--check` so the owner's Update button shows, and holds the result to the eval gate even when no other package moved.
|
|
20
|
+
|
|
17
21
|
## `kyb init [name]`
|
|
18
22
|
|
|
19
23
|
Scaffolds the full Kybernesis stack:
|
package/dist/capabilities.d.ts
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { type HostKind } from "./templates.js";
|
|
2
2
|
import type { ModelScaffoldConfig } from "./model-reach.js";
|
|
3
3
|
/**
|
|
4
|
-
* The
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* agent never boots. These are the releases proven on the certified eve.
|
|
8
|
-
* Move them with the eve certification, never on their own.
|
|
4
|
+
* The third-party extensions at the releases proven on the certified eve (see
|
|
5
|
+
* THIRD_PARTY_PINS): the registry items install the newest release, which eve
|
|
6
|
+
* refuses at discovery when it is built on a later eve.
|
|
9
7
|
*/
|
|
10
|
-
export declare const ENGINEER_EXTENSION_PINS:
|
|
8
|
+
export declare const ENGINEER_EXTENSION_PINS: string[];
|
|
11
9
|
/**
|
|
12
10
|
* Agent-to-agent: the eve channel becomes the governed dispatch door (people
|
|
13
11
|
* still come in through kybernesisAuth in the same walk) and granted peers
|
|
@@ -17,7 +15,7 @@ export declare const ENGINEER_EXTENSION_PINS: readonly ["@agent-browser/eve@0.34
|
|
|
17
15
|
export declare function applyDispatch(dir: string): string[];
|
|
18
16
|
/**
|
|
19
17
|
* The engineer: a declared `builder` subagent with its own workshop sandbox,
|
|
20
|
-
* memory and skills, plus the
|
|
18
|
+
* memory and skills, plus the GitHub tools at the root.
|
|
21
19
|
*/
|
|
22
20
|
export declare function applyEngineer(dir: string, host: HostKind, model: ModelScaffoldConfig): {
|
|
23
21
|
files: string[];
|
package/dist/capabilities.js
CHANGED
|
@@ -2,6 +2,7 @@ import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { finalizeGithubToolsRegistryMount, wireArpAuth } from "./init.js";
|
|
4
4
|
import { DISPATCH_DEP, dispatchEveChannelTs, dispatchPeersTs, engineerPlan } from "./templates.js";
|
|
5
|
+
import { THIRD_PARTY_PINS } from "./line-hop.js";
|
|
5
6
|
import { run, yellow } from "./util.js";
|
|
6
7
|
/**
|
|
7
8
|
* The capabilities every company agent carries, applied the same way by
|
|
@@ -11,16 +12,14 @@ import { run, yellow } from "./util.js";
|
|
|
11
12
|
// Official eve-registry limbs installed with the engineer subagent.
|
|
12
13
|
// connection/vercel is Vercel-Connect-backed, so it is VERCEL-HOST ONLY: on a
|
|
13
14
|
// self-hosted agent it cannot get an OIDC token and the agent fails to boot.
|
|
14
|
-
const ENGINEER_ITEMS_ALL = ["extension/
|
|
15
|
+
const ENGINEER_ITEMS_ALL = ["extension/github-tools"];
|
|
15
16
|
const ENGINEER_ITEMS_VERCEL = ["connection/vercel"];
|
|
16
17
|
/**
|
|
17
|
-
* The
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* agent never boots. These are the releases proven on the certified eve.
|
|
21
|
-
* Move them with the eve certification, never on their own.
|
|
18
|
+
* The third-party extensions at the releases proven on the certified eve (see
|
|
19
|
+
* THIRD_PARTY_PINS): the registry items install the newest release, which eve
|
|
20
|
+
* refuses at discovery when it is built on a later eve.
|
|
22
21
|
*/
|
|
23
|
-
export const ENGINEER_EXTENSION_PINS =
|
|
22
|
+
export const ENGINEER_EXTENSION_PINS = Object.entries(THIRD_PARTY_PINS).map(([name, version]) => `${name}@${version}`);
|
|
24
23
|
function write(dir, path, content) {
|
|
25
24
|
const full = join(dir, path);
|
|
26
25
|
mkdirSync(join(full, ".."), { recursive: true });
|
|
@@ -43,7 +42,7 @@ export function applyDispatch(dir) {
|
|
|
43
42
|
}
|
|
44
43
|
/**
|
|
45
44
|
* The engineer: a declared `builder` subagent with its own workshop sandbox,
|
|
46
|
-
* memory and skills, plus the
|
|
45
|
+
* memory and skills, plus the GitHub tools at the root.
|
|
47
46
|
*/
|
|
48
47
|
export function applyEngineer(dir, host, model) {
|
|
49
48
|
const plan = engineerPlan(host, model);
|
|
@@ -56,7 +55,7 @@ export function applyEngineer(dir, host, model) {
|
|
|
56
55
|
if (!ok)
|
|
57
56
|
console.log(yellow(` ! ${item} did not install cleanly — re-run: npx eve add ${item}`));
|
|
58
57
|
}
|
|
59
|
-
// --legacy-peer-deps:
|
|
58
|
+
// --legacy-peer-deps: it declares a peer range that trails the certified eve.
|
|
60
59
|
const pinned = run("npm", ["install", "--save-exact", "--legacy-peer-deps", ...ENGINEER_EXTENSION_PINS, "--no-audit", "--no-fund"], {
|
|
61
60
|
cwd: dir,
|
|
62
61
|
allowFail: true,
|
package/dist/doctor.d.ts
CHANGED
|
@@ -8,6 +8,11 @@ export interface Check {
|
|
|
8
8
|
}
|
|
9
9
|
/** Report the opt-in GitHub capability only when its mount is present. */
|
|
10
10
|
export declare function githubToolsDoctorCheck(mountExists: boolean, env: Record<string, string | undefined>): Check | null;
|
|
11
|
+
/**
|
|
12
|
+
* What the agent still carries from an older eve line: everything `kyb
|
|
13
|
+
* upgrade` would move (reported, not done), and what it leaves to a person.
|
|
14
|
+
*/
|
|
15
|
+
export declare function lineHopDoctorChecks(cwd: string): Check[];
|
|
11
16
|
/** The shape @kybernesis/exe's inspector returns; declared here so create keeps zero runtime deps. */
|
|
12
17
|
export interface DockerTemplateInspection {
|
|
13
18
|
status: "skipped" | "present" | "failed";
|
package/dist/doctor.js
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
|
2
2
|
import { TEMPLATE_CONTAINER_PROBE, parseTemplateContainers, templateContainerDoctorChecks } from "./sandbox-orphans.js";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
|
-
import { bold, capture, dim, green, parseEnv, red, yellow } from "./util.js";
|
|
5
|
+
import { EVE_VERSION, REGISTRY_URL, bold, capture, dim, green, parseEnv, red, yellow } from "./util.js";
|
|
6
6
|
import { diagnoseManageRestart, findMatchingAgentServiceUnit } from "./systemd.js";
|
|
7
7
|
import { inspectEveAgent } from "./agent-limits.js";
|
|
8
8
|
import { classifyModelReach } from "./model-reach.js";
|
|
@@ -10,6 +10,8 @@ import { countAbandonedRuns } from "./run-store.js";
|
|
|
10
10
|
import { describeStaleSidecar, newestPackageChange, staleSidecars } from "./stale-sidecar.js";
|
|
11
11
|
import { checkSelfVersion } from "./self-version.js";
|
|
12
12
|
import { envUrlProblems } from "./env-urls.js";
|
|
13
|
+
import { migrateManifest, migrateSources } from "./line-hop.js";
|
|
14
|
+
import { missingStudioCore } from "./studio-core.js";
|
|
13
15
|
const MARK = {
|
|
14
16
|
pass: green("✓"),
|
|
15
17
|
warn: yellow("!"),
|
|
@@ -25,6 +27,38 @@ export function githubToolsDoctorCheck(mountExists, env) {
|
|
|
25
27
|
detail: "set GITHUB_TOKEN in .env.local or the deployment environment, then rebuild and restart",
|
|
26
28
|
};
|
|
27
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* What the agent still carries from an older eve line: everything `kyb
|
|
32
|
+
* upgrade` would move (reported, not done), and what it leaves to a person.
|
|
33
|
+
*/
|
|
34
|
+
export function lineHopDoctorChecks(cwd) {
|
|
35
|
+
const checks = [];
|
|
36
|
+
try {
|
|
37
|
+
const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf8"));
|
|
38
|
+
const manifest = migrateManifest(pkg, REGISTRY_URL);
|
|
39
|
+
for (const move of manifest.moves) {
|
|
40
|
+
checks.push({
|
|
41
|
+
verdict: "warn",
|
|
42
|
+
label: move.to === null ? `${move.name} is installed` : `${move.name} is ${move.from}, not ${move.to}`,
|
|
43
|
+
detail: `${move.why}; kyb upgrade moves it`,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
if (manifest.registry) {
|
|
47
|
+
checks.push({ verdict: "warn", label: "the @kybernesis registry is not the one for this eve line", detail: `kyb upgrade points it at ${manifest.registry.to}` });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
/* no readable package.json: the dependency checks say so */
|
|
52
|
+
}
|
|
53
|
+
const sources = migrateSources(cwd, { write: false });
|
|
54
|
+
for (const file of sources.updated)
|
|
55
|
+
checks.push({ verdict: "warn", label: `${file} is written for an older eve`, detail: `kyb upgrade rewrites it for eve ${EVE_VERSION}` });
|
|
56
|
+
for (const file of sources.removed)
|
|
57
|
+
checks.push({ verdict: "warn", label: `${file} mounts an extension that does not run on eve ${EVE_VERSION}`, detail: "kyb upgrade removes it" });
|
|
58
|
+
for (const { file, action } of sources.manual)
|
|
59
|
+
checks.push({ verdict: "warn", label: `${file} needs a hand`, detail: action });
|
|
60
|
+
return checks;
|
|
61
|
+
}
|
|
28
62
|
export function dockerTemplateDoctorChecks(result) {
|
|
29
63
|
if (result.status === "skipped")
|
|
30
64
|
return [];
|
|
@@ -197,6 +231,10 @@ export async function doctor() {
|
|
|
197
231
|
else
|
|
198
232
|
add("warn", `${p} not installed`, `eve add @kybernesis/${p.split("/")[1]}`);
|
|
199
233
|
}
|
|
234
|
+
// Core on every agent Studio manages; kyb upgrade adds whichever is missing.
|
|
235
|
+
for (const core of missingStudioCore(deps)) {
|
|
236
|
+
add("warn", `${core.pkg} not installed (${core.what})`, `npx kyb upgrade adds it, or npx eve add @kybernesis/${core.item}`);
|
|
237
|
+
}
|
|
200
238
|
/**
|
|
201
239
|
* multiplayer is conversation mechanics for a SHARED channel — threads with
|
|
202
240
|
* per-speaker identity, the public/DM split. An agent reached only through
|
|
@@ -242,6 +280,8 @@ export async function doctor() {
|
|
|
242
280
|
const githubCheck = githubToolsDoctorCheck(existsSync(join(cwd, "agent/extensions/github.ts")), env);
|
|
243
281
|
if (githubCheck)
|
|
244
282
|
add(githubCheck.verdict, githubCheck.label, githubCheck.detail);
|
|
283
|
+
for (const check of lineHopDoctorChecks(cwd))
|
|
284
|
+
add(check.verdict, check.label, check.detail);
|
|
245
285
|
// ── arcana keys: validate every key↔workspace pair read-only ───────────
|
|
246
286
|
const arcanaPairs = [];
|
|
247
287
|
if (env.ARCANA_API_KEY && env.ARCANA_COMPANY_WORKSPACE)
|
package/dist/init.js
CHANGED
|
@@ -9,6 +9,7 @@ import { upsertEnv } from "./envfile.js";
|
|
|
9
9
|
import { systemdRestartCommand } from "./systemd.js";
|
|
10
10
|
import { sandboxCleanupScaffoldFiles } from "./sandbox-cleanup.js";
|
|
11
11
|
import { MODEL_REACH_ENV, resolveModelScaffold } from "./model-reach.js";
|
|
12
|
+
import { STUDIO_CORE, ignoreMagicState, wireMagicLinq } from "./studio-core.js";
|
|
12
13
|
/**
|
|
13
14
|
* The always-installed core. Everything else — channels, subagents, engineer,
|
|
14
15
|
* host bindings — is opt-in, because assuming them means the FDE deletes files
|
|
@@ -151,7 +152,7 @@ export async function init(rawName, options = {}) {
|
|
|
151
152
|
// client change THIS AGENT — its dependencies and its source; `notify`
|
|
152
153
|
// lets it reach the person's phone. Different blast radius, so an agent
|
|
153
154
|
// can have one without the others.
|
|
154
|
-
console.log(bold("\n2b2 KYBER Studio: local execution + management routes + phone notifications …"));
|
|
155
|
+
console.log(bold("\n2b2 KYBER Studio: local execution + management routes + phone notifications + cards, artifacts, heads-ups …"));
|
|
155
156
|
const studioFailures = [];
|
|
156
157
|
for (const item of ["local", "manage", "notify"]) {
|
|
157
158
|
// The @kybernesis/ prefix is load-bearing: a bare name resolves against
|
|
@@ -168,6 +169,20 @@ export async function init(rawName, options = {}) {
|
|
|
168
169
|
console.log(yellow(` ! ${item} did not install — re-run: npx eve add @kybernesis/${item}`));
|
|
169
170
|
}
|
|
170
171
|
}
|
|
172
|
+
// What a person sees beyond words, on every agent Studio reaches: cards
|
|
173
|
+
// under replies, artifacts, and magic (off until each person opts in).
|
|
174
|
+
// After manage and notify on purpose: artifacts are served by manage's
|
|
175
|
+
// routes, and magic delivers into its conversation and rings the phone.
|
|
176
|
+
for (const core of STUDIO_CORE) {
|
|
177
|
+
const ok = run("npx", ["eve", "add", `@kybernesis/${core.item}`, "--overwrite"], { cwd: dir, allowFail: true });
|
|
178
|
+
if (!ok) {
|
|
179
|
+
studioFailures.push(core.item);
|
|
180
|
+
console.log(yellow(` ! ${core.item} did not install — re-run: npx eve add @kybernesis/${core.item}`));
|
|
181
|
+
}
|
|
182
|
+
else if (core.item === "magic") {
|
|
183
|
+
ignoreMagicState(dir);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
171
186
|
// Said again, loudly, at the end. A warning printed sixty lines before a
|
|
172
187
|
// green summary is a warning nobody reads — and the agent that results
|
|
173
188
|
// looks correctly scaffolded right up until KYBER Studio refuses it.
|
|
@@ -351,6 +366,9 @@ export async function init(rawName, options = {}) {
|
|
|
351
366
|
else {
|
|
352
367
|
console.log(bold(`\n4/6 Channel: ${channel} (no channel file) …`));
|
|
353
368
|
}
|
|
369
|
+
// A person who last texted the agent gets their heads-ups in that thread.
|
|
370
|
+
if (studio && wireMagicLinq(dir))
|
|
371
|
+
console.log(dim(" agent/lib/magic-surfaces.ts — heads-ups also land in iMessage threads"));
|
|
354
372
|
if (depts.length) {
|
|
355
373
|
console.log(bold(`\n4b Generating ${depts.length} department subagent(s) …`));
|
|
356
374
|
const skillsSource = join(dir, "node_modules/@kybernesis/arcana/dist/extension/skills");
|
|
@@ -403,6 +421,7 @@ export async function init(rawName, options = {}) {
|
|
|
403
421
|
host === "exe" ? "exe.dev host" : null,
|
|
404
422
|
model.reach === "claude-sub" ? "Claude subscription reach" : null,
|
|
405
423
|
studio ? "KYBER Studio (local execution + management routes)" : null,
|
|
424
|
+
studio ? "cards, artifacts and heads-ups (each person opts in)" : null,
|
|
406
425
|
engineer ? "engineer subagent (workshop + vision loop)" : null,
|
|
407
426
|
compPlan ? "its own computer (desktop + Chrome, watch and take over)" : null,
|
|
408
427
|
compPlan ? "its own vault (logins and cards it fills without seeing)" : null,
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an agent built on eve 0.68 needs to run on eve 0.81, beyond new package
|
|
3
|
+
* versions. Each step touches only what a Kybernesis scaffold or registry item
|
|
4
|
+
* wrote, in the exact form it wrote it; anything authored by hand is reported
|
|
5
|
+
* and left alone.
|
|
6
|
+
*/
|
|
7
|
+
/** Where `kyb init` pointed the @kybernesis registry before the registry served one copy per eve line. */
|
|
8
|
+
export declare const UNVERSIONED_REGISTRY_URL = "https://registry.kybernesis.ai/r/{name}.json";
|
|
9
|
+
/**
|
|
10
|
+
* Third-party extensions, at the releases proven on the certified eve. The
|
|
11
|
+
* registry items install their newest release, which is built on a later eve
|
|
12
|
+
* and refused at discovery. Move them with the eve certification, never on
|
|
13
|
+
* their own.
|
|
14
|
+
*/
|
|
15
|
+
export declare const THIRD_PARTY_PINS: Readonly<Record<string, string>>;
|
|
16
|
+
/** Third-party extensions with no release that mounts on the certified eve, and why. */
|
|
17
|
+
export declare const DROPPED_EXTENSIONS: Readonly<Record<string, string>>;
|
|
18
|
+
/** eve 0.81.1 peers on ai ^7.0.137; an agent's own range for ai must allow it. */
|
|
19
|
+
export declare const AI_FLOOR = "7.0.137";
|
|
20
|
+
/** Exactly what `eve add extension/agent-browser` wrote. */
|
|
21
|
+
export declare const AGENT_BROWSER_MOUNT = "import browser from \"@agent-browser/eve\";\n\nexport default browser({});\n";
|
|
22
|
+
export interface DependencyMove {
|
|
23
|
+
name: string;
|
|
24
|
+
from: string;
|
|
25
|
+
/** null when the dependency is removed. */
|
|
26
|
+
to: string | null;
|
|
27
|
+
why: string;
|
|
28
|
+
}
|
|
29
|
+
export interface ManifestMigration {
|
|
30
|
+
moves: DependencyMove[];
|
|
31
|
+
registry: {
|
|
32
|
+
from: string;
|
|
33
|
+
to: string;
|
|
34
|
+
} | null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The manifest half: third-party pins, dropped extensions, the ai floor and
|
|
38
|
+
* the registry namespace. Mutates `pkg` and says what moved; writing it is the
|
|
39
|
+
* caller's, so a dry run can read the answer without touching disk.
|
|
40
|
+
*/
|
|
41
|
+
export declare function migrateManifest(pkg: Record<string, unknown>, registryUrl: string): ManifestMigration;
|
|
42
|
+
export interface SourceMigration {
|
|
43
|
+
/** Files changed, relative to the agent. */
|
|
44
|
+
updated: string[];
|
|
45
|
+
/** Files removed, relative to the agent. */
|
|
46
|
+
removed: string[];
|
|
47
|
+
/** Files that need a hand, with what to do. */
|
|
48
|
+
manual: Array<{
|
|
49
|
+
file: string;
|
|
50
|
+
action: string;
|
|
51
|
+
}>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The source half: the judge, the browser mount, the ask tools, and old tool
|
|
55
|
+
* names in prose. With `write: false` it only says what it would do.
|
|
56
|
+
*/
|
|
57
|
+
export declare function migrateSources(cwd: string, { write }?: {
|
|
58
|
+
write?: boolean;
|
|
59
|
+
}): SourceMigration;
|
|
60
|
+
/** The conversation store. The first boot on eve 0.81 deletes one written by an older line. */
|
|
61
|
+
export declare const WORKFLOW_STORE = ".eve/.workflow-data";
|
|
62
|
+
/** Where the store from line `fromLine` (e.g. "0.68") is kept across a hop. */
|
|
63
|
+
export declare function workflowStoreBackup(fromLine: string): string;
|
|
64
|
+
/** Build caches from the old line that the new one must not reuse. */
|
|
65
|
+
export declare const STALE_EVE_CACHES: string[];
|
|
66
|
+
/**
|
|
67
|
+
* Copy the conversation store aside before anything on the new line boots
|
|
68
|
+
* (the eval gate's dev server shares it with production). An existing backup
|
|
69
|
+
* is never overwritten: it is the copy taken before the first attempt.
|
|
70
|
+
*/
|
|
71
|
+
export declare function backUpWorkflowStore(cwd: string, fromLine: string): "copied" | "kept" | "none";
|
|
72
|
+
/** Put the backed-up store back, for an agent that is going back to its old line. */
|
|
73
|
+
export declare function restoreWorkflowStore(cwd: string, fromLine: string): boolean;
|
|
74
|
+
export declare function clearStaleEveCaches(cwd: string): string[];
|
package/dist/line-hop.js
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { cpSync, existsSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join, relative } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* What an agent built on eve 0.68 needs to run on eve 0.81, beyond new package
|
|
5
|
+
* versions. Each step touches only what a Kybernesis scaffold or registry item
|
|
6
|
+
* wrote, in the exact form it wrote it; anything authored by hand is reported
|
|
7
|
+
* and left alone.
|
|
8
|
+
*/
|
|
9
|
+
/** Where `kyb init` pointed the @kybernesis registry before the registry served one copy per eve line. */
|
|
10
|
+
export const UNVERSIONED_REGISTRY_URL = "https://registry.kybernesis.ai/r/{name}.json";
|
|
11
|
+
/**
|
|
12
|
+
* Third-party extensions, at the releases proven on the certified eve. The
|
|
13
|
+
* registry items install their newest release, which is built on a later eve
|
|
14
|
+
* and refused at discovery. Move them with the eve certification, never on
|
|
15
|
+
* their own.
|
|
16
|
+
*/
|
|
17
|
+
export const THIRD_PARTY_PINS = { "@github-tools/eve-extension": "0.8.1" };
|
|
18
|
+
/** Third-party extensions with no release that mounts on the certified eve, and why. */
|
|
19
|
+
export const DROPPED_EXTENSIONS = {
|
|
20
|
+
"@agent-browser/eve": "no release mounts on eve 0.81; the agent reads pages on its own computer (open_browser)",
|
|
21
|
+
};
|
|
22
|
+
/** eve 0.81.1 peers on ai ^7.0.137; an agent's own range for ai must allow it. */
|
|
23
|
+
export const AI_FLOOR = "7.0.137";
|
|
24
|
+
/** Exactly what `eve add extension/agent-browser` wrote. */
|
|
25
|
+
export const AGENT_BROWSER_MOUNT = `import browser from "@agent-browser/eve";\n\nexport default browser({});\n`;
|
|
26
|
+
function floorBelow(range, floor) {
|
|
27
|
+
const match = /^[\^~]?(\d+)\.(\d+)\.(\d+)$/.exec(range.trim());
|
|
28
|
+
if (!match)
|
|
29
|
+
return false;
|
|
30
|
+
const want = floor.split(".").map(Number);
|
|
31
|
+
const have = match.slice(1, 4).map(Number);
|
|
32
|
+
if (have[0] !== want[0])
|
|
33
|
+
return false;
|
|
34
|
+
for (let i = 0; i < 3; i += 1)
|
|
35
|
+
if (have[i] !== want[i])
|
|
36
|
+
return have[i] < want[i];
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The manifest half: third-party pins, dropped extensions, the ai floor and
|
|
41
|
+
* the registry namespace. Mutates `pkg` and says what moved; writing it is the
|
|
42
|
+
* caller's, so a dry run can read the answer without touching disk.
|
|
43
|
+
*/
|
|
44
|
+
export function migrateManifest(pkg, registryUrl) {
|
|
45
|
+
const moves = [];
|
|
46
|
+
for (const section of ["dependencies", "devDependencies"]) {
|
|
47
|
+
const deps = pkg[section];
|
|
48
|
+
if (!deps)
|
|
49
|
+
continue;
|
|
50
|
+
for (const [name, range] of Object.entries(deps)) {
|
|
51
|
+
if (name in DROPPED_EXTENSIONS) {
|
|
52
|
+
delete deps[name];
|
|
53
|
+
moves.push({ name, from: range, to: null, why: DROPPED_EXTENSIONS[name] });
|
|
54
|
+
}
|
|
55
|
+
else if (THIRD_PARTY_PINS[name] && range !== THIRD_PARTY_PINS[name]) {
|
|
56
|
+
deps[name] = THIRD_PARTY_PINS[name];
|
|
57
|
+
moves.push({ name, from: range, to: THIRD_PARTY_PINS[name], why: "the release proven on the certified eve" });
|
|
58
|
+
}
|
|
59
|
+
else if (name === "ai" && floorBelow(range, AI_FLOOR)) {
|
|
60
|
+
deps[name] = `^${AI_FLOOR}`;
|
|
61
|
+
moves.push({ name, from: range, to: `^${AI_FLOOR}`, why: "the certified eve needs it" });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const overrides = pkg.overrides;
|
|
66
|
+
if (overrides && typeof overrides.ai === "string" && floorBelow(overrides.ai, AI_FLOOR))
|
|
67
|
+
overrides.ai = `^${AI_FLOOR}`;
|
|
68
|
+
let registry = null;
|
|
69
|
+
const registries = pkg.registries;
|
|
70
|
+
if (registries?.["@kybernesis"] === UNVERSIONED_REGISTRY_URL && registryUrl !== UNVERSIONED_REGISTRY_URL) {
|
|
71
|
+
registries["@kybernesis"] = registryUrl;
|
|
72
|
+
registry = { from: UNVERSIONED_REGISTRY_URL, to: registryUrl };
|
|
73
|
+
}
|
|
74
|
+
return { moves, registry };
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Registry-written workflow tools whose question must survive the person
|
|
78
|
+
* sending another message. On 0.69+ an `execute` tool's open question is
|
|
79
|
+
* withdrawn by one; a `task` keeps it open and hands the answer back later.
|
|
80
|
+
*/
|
|
81
|
+
const ASK_TOOLS = [
|
|
82
|
+
{
|
|
83
|
+
path: "agent/tools/request_vault_item.ts",
|
|
84
|
+
helper: "@kybernesis/vault/ask",
|
|
85
|
+
explain: (source) => source.replace("// Parks durably; ", "// Runs as a task: the question stays open through any other message the person\n" +
|
|
86
|
+
"// sends (an `execute` call's question is withdrawn by one), and the answer\n" +
|
|
87
|
+
"// reaches the model later in the conversation. Parks durably; "),
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
path: "agent/tools/approve_spend_request.ts",
|
|
91
|
+
helper: "@kybernesis/payments",
|
|
92
|
+
explain: (source) => source.replace("\nexport default defineWorkflowTool(", "\n// Runs as a task: the question stays open through any other message the person\n" +
|
|
93
|
+
"// sends (an `execute` call's question is withdrawn by one, which would cancel\n" +
|
|
94
|
+
"// the purchase), and the result reaches the model later in the conversation." +
|
|
95
|
+
"\nexport default defineWorkflowTool("),
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
const EXECUTE_WORKFLOW = /( {2})async execute\(input, ctx\) \{(\n\s*"use workflow";)/;
|
|
99
|
+
function walk(dir, keep) {
|
|
100
|
+
if (!existsSync(dir))
|
|
101
|
+
return [];
|
|
102
|
+
return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
|
|
103
|
+
const path = join(dir, entry.name);
|
|
104
|
+
if (entry.isDirectory())
|
|
105
|
+
return entry.name === "node_modules" ? [] : walk(path, keep);
|
|
106
|
+
return entry.isFile() && keep(entry.name) ? [path] : [];
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The source half: the judge, the browser mount, the ask tools, and old tool
|
|
111
|
+
* names in prose. With `write: false` it only says what it would do.
|
|
112
|
+
*/
|
|
113
|
+
export function migrateSources(cwd, { write = true } = {}) {
|
|
114
|
+
const result = { updated: [], removed: [], manual: [] };
|
|
115
|
+
const rel = (path) => relative(cwd, path);
|
|
116
|
+
// eve 0.72 grades with a decision model; `.evaluationModel(` no longer compiles.
|
|
117
|
+
for (const path of [join(cwd, "evals/evals.config.ts"), join(cwd, "evals.config.ts")]) {
|
|
118
|
+
if (!existsSync(path))
|
|
119
|
+
continue;
|
|
120
|
+
const source = readFileSync(path, "utf8");
|
|
121
|
+
if (!source.includes(".evaluationModel("))
|
|
122
|
+
continue;
|
|
123
|
+
if (write)
|
|
124
|
+
writeFileSync(path, source.replaceAll(".evaluationModel(", ".decisionModel("));
|
|
125
|
+
result.updated.push(rel(path));
|
|
126
|
+
}
|
|
127
|
+
// The browser extension: remove the scaffold's mount, flag any other.
|
|
128
|
+
for (const path of walk(join(cwd, "agent"), (name) => /\.[cm]?[jt]s$/.test(name))) {
|
|
129
|
+
const source = readFileSync(path, "utf8");
|
|
130
|
+
if (!source.includes("@agent-browser/eve"))
|
|
131
|
+
continue;
|
|
132
|
+
if (source === AGENT_BROWSER_MOUNT) {
|
|
133
|
+
if (write)
|
|
134
|
+
rmSync(path);
|
|
135
|
+
result.removed.push(rel(path));
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
result.manual.push({ file: rel(path), action: `remove it: @agent-browser/eve ${DROPPED_EXTENSIONS["@agent-browser/eve"]}` });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
for (const tool of ASK_TOOLS) {
|
|
142
|
+
const path = join(cwd, tool.path);
|
|
143
|
+
if (!existsSync(path))
|
|
144
|
+
continue;
|
|
145
|
+
const source = readFileSync(path, "utf8");
|
|
146
|
+
if (!source.includes(tool.helper) || !EXECUTE_WORKFLOW.test(source))
|
|
147
|
+
continue;
|
|
148
|
+
const swapped = source.replace(EXECUTE_WORKFLOW, "$1async task(input, ctx) {$2");
|
|
149
|
+
if (write)
|
|
150
|
+
writeFileSync(path, swapped.includes("Runs as a task:") ? swapped : tool.explain(swapped));
|
|
151
|
+
result.updated.push(tool.path);
|
|
152
|
+
}
|
|
153
|
+
// Prose that names tools eve 0.81 no longer has: the model would look for them.
|
|
154
|
+
for (const path of walk(join(cwd, "agent"), (name) => /\.(md|ts)$/.test(name))) {
|
|
155
|
+
const source = readFileSync(path, "utf8");
|
|
156
|
+
const names = ["connection_search", "load_skill"].filter((name) => new RegExp(`\\b${name}\\b`).test(source));
|
|
157
|
+
if (names.length === 0)
|
|
158
|
+
continue;
|
|
159
|
+
result.manual.push({
|
|
160
|
+
file: rel(path),
|
|
161
|
+
action: `rewrite ${names.map((n) => `\`${n}\``).join(" and ")}: eve 0.81 finds tools with \`eve__search\`, calls them with \`eve__tool\`, and loads skills with \`eve__skill\``,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
/** The conversation store. The first boot on eve 0.81 deletes one written by an older line. */
|
|
167
|
+
export const WORKFLOW_STORE = ".eve/.workflow-data";
|
|
168
|
+
/** Where the store from line `fromLine` (e.g. "0.68") is kept across a hop. */
|
|
169
|
+
export function workflowStoreBackup(fromLine) {
|
|
170
|
+
return `${WORKFLOW_STORE}.eve-${fromLine}`;
|
|
171
|
+
}
|
|
172
|
+
/** Build caches from the old line that the new one must not reuse. */
|
|
173
|
+
export const STALE_EVE_CACHES = [".eve/sandbox-cache", ".eve/dev-runtime"];
|
|
174
|
+
/**
|
|
175
|
+
* Copy the conversation store aside before anything on the new line boots
|
|
176
|
+
* (the eval gate's dev server shares it with production). An existing backup
|
|
177
|
+
* is never overwritten: it is the copy taken before the first attempt.
|
|
178
|
+
*/
|
|
179
|
+
export function backUpWorkflowStore(cwd, fromLine) {
|
|
180
|
+
const store = join(cwd, WORKFLOW_STORE);
|
|
181
|
+
const backup = join(cwd, workflowStoreBackup(fromLine));
|
|
182
|
+
if (existsSync(backup))
|
|
183
|
+
return "kept";
|
|
184
|
+
if (!existsSync(store))
|
|
185
|
+
return "none";
|
|
186
|
+
cpSync(store, backup, { recursive: true, preserveTimestamps: true });
|
|
187
|
+
return "copied";
|
|
188
|
+
}
|
|
189
|
+
/** Put the backed-up store back, for an agent that is going back to its old line. */
|
|
190
|
+
export function restoreWorkflowStore(cwd, fromLine) {
|
|
191
|
+
const backup = join(cwd, workflowStoreBackup(fromLine));
|
|
192
|
+
if (!existsSync(backup))
|
|
193
|
+
return false;
|
|
194
|
+
const store = join(cwd, WORKFLOW_STORE);
|
|
195
|
+
rmSync(store, { recursive: true, force: true });
|
|
196
|
+
cpSync(backup, store, { recursive: true, preserveTimestamps: true });
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
export function clearStaleEveCaches(cwd) {
|
|
200
|
+
return STALE_EVE_CACHES.filter((dir) => {
|
|
201
|
+
const path = join(cwd, dir);
|
|
202
|
+
if (!existsSync(path))
|
|
203
|
+
return false;
|
|
204
|
+
rmSync(path, { recursive: true, force: true });
|
|
205
|
+
return true;
|
|
206
|
+
});
|
|
207
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a person gets from an agent in KYBER Studio and on the phone beyond
|
|
3
|
+
* words: cards under a reply (the `show` tool), pages the agent makes and
|
|
4
|
+
* keeps (artifacts), and heads-ups it sends on its own (magic, off for each
|
|
5
|
+
* person until they turn it on). Core on every agent since 2026-10-23 (Ian:
|
|
6
|
+
* "they ship with every single agent").
|
|
7
|
+
*
|
|
8
|
+
* Keyed on @kybernesis/manage, like notify: manage serves the artifact pages
|
|
9
|
+
* and owns the conversation magic delivers into, so without it neither has
|
|
10
|
+
* anywhere to land. Every agent the provisioner builds has it (--studio).
|
|
11
|
+
*
|
|
12
|
+
* Each one's marker is its own package. An agent that already has it, added
|
|
13
|
+
* by hand or customised (Sid's own delivery map), is never touched.
|
|
14
|
+
*/
|
|
15
|
+
export declare const STUDIO_CORE: readonly [{
|
|
16
|
+
readonly item: "cards";
|
|
17
|
+
readonly pkg: "@kybernesis/cards";
|
|
18
|
+
readonly what: "cards under its replies";
|
|
19
|
+
}, {
|
|
20
|
+
readonly item: "artifacts";
|
|
21
|
+
readonly pkg: "@kybernesis/artifacts";
|
|
22
|
+
readonly what: "pages it makes and keeps";
|
|
23
|
+
}, {
|
|
24
|
+
readonly item: "magic";
|
|
25
|
+
readonly pkg: "@kybernesis/magic";
|
|
26
|
+
readonly what: "heads-ups, off until each person turns them on";
|
|
27
|
+
}];
|
|
28
|
+
export type StudioCoreItem = (typeof STUDIO_CORE)[number];
|
|
29
|
+
/** The core items this agent is missing, in install order. None without manage. */
|
|
30
|
+
export declare function missingStudioCore(deps: Record<string, string>): StudioCoreItem[];
|
|
31
|
+
/**
|
|
32
|
+
* The additions an upgrade would make, for `kyb upgrade --check`.
|
|
33
|
+
*
|
|
34
|
+
* Without them the Update button stays hidden on an agent whose packages are
|
|
35
|
+
* all current, which is exactly the agent these were built after. `resolve`
|
|
36
|
+
* names the build that would install; one that resolves to nothing is left
|
|
37
|
+
* out, because nothing published runs on this eve.
|
|
38
|
+
*/
|
|
39
|
+
export declare function planStudioCore(deps: Record<string, string>, resolve: (pkg: string) => string | undefined): Array<{
|
|
40
|
+
name: string;
|
|
41
|
+
from: string;
|
|
42
|
+
to: string;
|
|
43
|
+
}>;
|
|
44
|
+
/**
|
|
45
|
+
* Where magic delivers on an agent that also answers on iMessage through Linq.
|
|
46
|
+
*
|
|
47
|
+
* The registry's map sends every heads-up into the conversation Studio and the
|
|
48
|
+
* phone share. A person who last texted the agent should get it in that same
|
|
49
|
+
* thread instead (Linq thread ids start with "linq:").
|
|
50
|
+
*/
|
|
51
|
+
export declare function magicSurfacesLinqTs(): string;
|
|
52
|
+
/**
|
|
53
|
+
* Point magic at Linq when this agent has a Linq channel. Only ever called
|
|
54
|
+
* right after the registry wrote its default map, so nothing hand-made is lost.
|
|
55
|
+
*/
|
|
56
|
+
export declare function wireMagicLinq(cwd: string): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Keep magic's per-person state out of git. `.kyb/magic/people/<id>/` holds
|
|
59
|
+
* what each person turned on and what they were told; it belongs to the host.
|
|
60
|
+
*/
|
|
61
|
+
export declare function ignoreMagicState(cwd: string): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Install each missing core item from the agent's own @kybernesis registry,
|
|
64
|
+
* the same way `eve add` would by hand: its package and its files. Returns
|
|
65
|
+
* what was added; a failure is said and skipped, and the eval gate after it
|
|
66
|
+
* judges the agent as it stands.
|
|
67
|
+
*/
|
|
68
|
+
export declare function repairStudioCore(cwd: string, deps: Record<string, string>): StudioCoreItem[];
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { appendFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { dim, green, run, yellow } from "./util.js";
|
|
4
|
+
/**
|
|
5
|
+
* What a person gets from an agent in KYBER Studio and on the phone beyond
|
|
6
|
+
* words: cards under a reply (the `show` tool), pages the agent makes and
|
|
7
|
+
* keeps (artifacts), and heads-ups it sends on its own (magic, off for each
|
|
8
|
+
* person until they turn it on). Core on every agent since 2026-10-23 (Ian:
|
|
9
|
+
* "they ship with every single agent").
|
|
10
|
+
*
|
|
11
|
+
* Keyed on @kybernesis/manage, like notify: manage serves the artifact pages
|
|
12
|
+
* and owns the conversation magic delivers into, so without it neither has
|
|
13
|
+
* anywhere to land. Every agent the provisioner builds has it (--studio).
|
|
14
|
+
*
|
|
15
|
+
* Each one's marker is its own package. An agent that already has it, added
|
|
16
|
+
* by hand or customised (Sid's own delivery map), is never touched.
|
|
17
|
+
*/
|
|
18
|
+
export const STUDIO_CORE = [
|
|
19
|
+
{ item: "cards", pkg: "@kybernesis/cards", what: "cards under its replies" },
|
|
20
|
+
{ item: "artifacts", pkg: "@kybernesis/artifacts", what: "pages it makes and keeps" },
|
|
21
|
+
{ item: "magic", pkg: "@kybernesis/magic", what: "heads-ups, off until each person turns them on" },
|
|
22
|
+
];
|
|
23
|
+
/** The core items this agent is missing, in install order. None without manage. */
|
|
24
|
+
export function missingStudioCore(deps) {
|
|
25
|
+
if (!deps["@kybernesis/manage"])
|
|
26
|
+
return [];
|
|
27
|
+
return STUDIO_CORE.filter((core) => !deps[core.pkg]);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The additions an upgrade would make, for `kyb upgrade --check`.
|
|
31
|
+
*
|
|
32
|
+
* Without them the Update button stays hidden on an agent whose packages are
|
|
33
|
+
* all current, which is exactly the agent these were built after. `resolve`
|
|
34
|
+
* names the build that would install; one that resolves to nothing is left
|
|
35
|
+
* out, because nothing published runs on this eve.
|
|
36
|
+
*/
|
|
37
|
+
export function planStudioCore(deps, resolve) {
|
|
38
|
+
return missingStudioCore(deps).flatMap((core) => {
|
|
39
|
+
const version = resolve(core.pkg);
|
|
40
|
+
return version ? [{ name: core.pkg, from: "none", to: version }] : [];
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Where magic delivers on an agent that also answers on iMessage through Linq.
|
|
45
|
+
*
|
|
46
|
+
* The registry's map sends every heads-up into the conversation Studio and the
|
|
47
|
+
* phone share. A person who last texted the agent should get it in that same
|
|
48
|
+
* thread instead (Linq thread ids start with "linq:").
|
|
49
|
+
*/
|
|
50
|
+
export function magicSurfacesLinqTs() {
|
|
51
|
+
return `import type { MagicDispatch } from "@kybernesis/magic";
|
|
52
|
+
import type { ScheduleToFn } from "eve/schedules";
|
|
53
|
+
import kyb from "../channels/kyb.js";
|
|
54
|
+
import linq from "../channels/linq.js";
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Where a heads-up is delivered: the conversation the person last used.
|
|
58
|
+
*
|
|
59
|
+
* A person who last texted this agent gets it in that same iMessage thread
|
|
60
|
+
* (Linq thread ids start with "linq:"). Everyone else gets it in the
|
|
61
|
+
* conversation Studio and the phone app share, which rings their phone.
|
|
62
|
+
*/
|
|
63
|
+
export function deliver(to: ScheduleToFn, dispatch: MagicDispatch) {
|
|
64
|
+
const { surface, packet } = dispatch;
|
|
65
|
+
if (surface.token?.startsWith("linq:")) {
|
|
66
|
+
return to(linq, { threadId: surface.token }).send(packet, { auth: surface.auth });
|
|
67
|
+
}
|
|
68
|
+
return to(kyb, { routine: "magic" }).send(packet, { auth: surface.auth });
|
|
69
|
+
}
|
|
70
|
+
`;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Point magic at Linq when this agent has a Linq channel. Only ever called
|
|
74
|
+
* right after the registry wrote its default map, so nothing hand-made is lost.
|
|
75
|
+
*/
|
|
76
|
+
export function wireMagicLinq(cwd) {
|
|
77
|
+
if (!existsSync(join(cwd, "agent/channels/linq.ts")))
|
|
78
|
+
return false;
|
|
79
|
+
const surfaces = join(cwd, "agent/lib/magic-surfaces.ts");
|
|
80
|
+
if (!existsSync(surfaces))
|
|
81
|
+
return false;
|
|
82
|
+
writeFileSync(surfaces, magicSurfacesLinqTs());
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Keep magic's per-person state out of git. `.kyb/magic/people/<id>/` holds
|
|
87
|
+
* what each person turned on and what they were told; it belongs to the host.
|
|
88
|
+
*/
|
|
89
|
+
export function ignoreMagicState(cwd) {
|
|
90
|
+
const path = join(cwd, ".gitignore");
|
|
91
|
+
const current = existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
92
|
+
if (current.split("\n").some((line) => /^\/?\.kyb\/?$/.test(line.trim())))
|
|
93
|
+
return false;
|
|
94
|
+
appendFileSync(path, `${current && !current.endsWith("\n") ? "\n" : ""}.kyb\n`);
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Install each missing core item from the agent's own @kybernesis registry,
|
|
99
|
+
* the same way `eve add` would by hand: its package and its files. Returns
|
|
100
|
+
* what was added; a failure is said and skipped, and the eval gate after it
|
|
101
|
+
* judges the agent as it stands.
|
|
102
|
+
*/
|
|
103
|
+
export function repairStudioCore(cwd, deps) {
|
|
104
|
+
const added = [];
|
|
105
|
+
for (const core of missingStudioCore(deps)) {
|
|
106
|
+
const ok = run("npx", ["eve", "add", `@kybernesis/${core.item}`, "--yes"], { cwd, allowFail: true, quiet: true });
|
|
107
|
+
if (!ok) {
|
|
108
|
+
console.log(` ${yellow("!")} could not add ${core.pkg} (${core.what}). Run: npx eve add @kybernesis/${core.item}`);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
deps[core.pkg] = "latest";
|
|
112
|
+
added.push(core);
|
|
113
|
+
if (core.item === "magic") {
|
|
114
|
+
ignoreMagicState(cwd);
|
|
115
|
+
if (wireMagicLinq(cwd))
|
|
116
|
+
console.log(` ${dim("magic delivers into iMessage threads too (agent/lib/magic-surfaces.ts)")}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (added.length > 0) {
|
|
120
|
+
console.log(` ${green("+")} Added ${added.map((core) => `${core.pkg} ${dim(`(${core.what})`)}`).join(", ")}`);
|
|
121
|
+
console.log(` ${dim("Takes effect after the next build and restart.")}\n`);
|
|
122
|
+
}
|
|
123
|
+
return added;
|
|
124
|
+
}
|
package/dist/templates.js
CHANGED
|
@@ -792,9 +792,10 @@ export { deliver as default } from "@kybernesis/engineer/tools";
|
|
|
792
792
|
// that matters is host access, and the old wording blurred exactly the line
|
|
793
793
|
// people read this comment to understand.
|
|
794
794
|
//
|
|
795
|
-
// The root DOES get
|
|
796
|
-
// extension/
|
|
797
|
-
//
|
|
795
|
+
// The root DOES get GitHub tools — the engineer layer installs
|
|
796
|
+
// extension/github-tools at the root, deliberately, because reading a repo is
|
|
797
|
+
// not the same blast radius as running a command. Pages are read on the
|
|
798
|
+
// agent's own computer (open_browser).
|
|
798
799
|
export { default } from "@kybernesis/engineer";
|
|
799
800
|
`,
|
|
800
801
|
},
|
|
@@ -860,8 +861,9 @@ export default defineEvalConfig({
|
|
|
860
861
|
// Verified against the exe integration rather than assumed: it validates model
|
|
861
862
|
// names and refuses one it does not carry, so a stale default here fails the
|
|
862
863
|
// whole suite at the judge rather than at anything the agent did.
|
|
863
|
-
// eve ≥0.
|
|
864
|
-
|
|
864
|
+
// eve ≥0.72 grades with a DECISION model (an evaluation model from 0.62),
|
|
865
|
+
// not a language model instance.
|
|
866
|
+
judge: { model: exe.decisionModel(process.env.EXE_JUDGE_MODEL ?? "claude-sonnet-5") },
|
|
865
867
|
// Real model and real memory on every turn: generous timeout, gentle concurrency.
|
|
866
868
|
timeoutMs: 300_000,
|
|
867
869
|
maxConcurrency: 1,
|
package/dist/upgrade.js
CHANGED
|
@@ -12,8 +12,10 @@ import { newestOnLine } from "./on-line-version.js";
|
|
|
12
12
|
import { findMatchingAgentServiceUnit, repairManageRestart } from "./systemd.js";
|
|
13
13
|
import { repairTerminalSandboxCleanupHooks } from "./sandbox-cleanup.js";
|
|
14
14
|
import { repairExeClaudeRoute } from "./exe-claude-route.js";
|
|
15
|
+
import { planStudioCore, repairStudioCore } from "./studio-core.js";
|
|
15
16
|
import { repairOptInTools, repairRemovedDefaultTools as repairRemovedDefaultTools_ } from "./removed-default-tools.js";
|
|
16
|
-
import { EVE_VERSION, bold, capture, dim, green, parseEnv, red, run, yellow } from "./util.js";
|
|
17
|
+
import { EVE_VERSION, REGISTRY_URL, bold, capture, dim, green, parseEnv, red, run, yellow } from "./util.js";
|
|
18
|
+
import { backUpWorkflowStore, clearStaleEveCaches, migrateManifest, migrateSources, restoreWorkflowStore, workflowStoreBackup, } from "./line-hop.js";
|
|
17
19
|
import { inspectEveAgent } from "./agent-limits.js";
|
|
18
20
|
import { remoteProjectPath, sshTarget } from "./deploy.js";
|
|
19
21
|
import { classifyModelReach } from "./model-reach.js";
|
|
@@ -598,11 +600,11 @@ function exeJudgeUrl(cwd, env) {
|
|
|
598
600
|
if (!path)
|
|
599
601
|
return null;
|
|
600
602
|
const source = readFileSync(path, "utf8");
|
|
601
|
-
const judgeProvider = /judge\s*:\s*\{[\s\S]*?model\s*:\s*([A-Za-z_$][\w$]*)\s*(?:\.evaluationModel\s*)?\(/.exec(source)?.[1];
|
|
603
|
+
const judgeProvider = /judge\s*:\s*\{[\s\S]*?model\s*:\s*([A-Za-z_$][\w$]*)\s*(?:\.(?:decisionModel|evaluationModel)\s*)?\(/.exec(source)?.[1];
|
|
602
604
|
if (!judgeProvider)
|
|
603
605
|
return null;
|
|
604
606
|
const escaped = judgeProvider.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
605
|
-
// The provider may be made inline (`model: createAnthropic({ … }).
|
|
607
|
+
// The provider may be made inline (`model: createAnthropic({ … }).decisionModel(…)`)
|
|
606
608
|
// as well as bound to a name first; both carry the base URL in the same object.
|
|
607
609
|
const inline = /judge\s*:\s*\{[\s\S]*?model\s*:\s*create[A-Za-z]+\s*\(\s*\{([\s\S]*?)\}\s*\)/.exec(source)?.[1];
|
|
608
610
|
const providerBody = inline ?? new RegExp(`(?:const|let)\\s+${escaped}\\s*=\\s*create[A-Za-z]+\\s*\\(\\s*\\{([\\s\\S]*?)\\}\\s*\\)`).exec(source)?.[1];
|
|
@@ -665,7 +667,7 @@ async function runUpgradeEvalGate(cwd, host) {
|
|
|
665
667
|
const target = sshTarget(cwd, typeof host === "string" && host.length > 0 ? host : undefined);
|
|
666
668
|
if (!target) {
|
|
667
669
|
reportEvalGateUnavailable(cwd, null, ["no ssh target is configured"]);
|
|
668
|
-
return;
|
|
670
|
+
return "unavailable";
|
|
669
671
|
}
|
|
670
672
|
console.log(bold(`\nRunning the eval gate on ${target} (${remoteProjectPath(cwd)}) — deploy only on green …\n`));
|
|
671
673
|
const result = spawnSync("ssh", [target, `cd ${remoteProjectPath(cwd)} && npm run eval`], {
|
|
@@ -675,27 +677,26 @@ async function runUpgradeEvalGate(cwd, host) {
|
|
|
675
677
|
});
|
|
676
678
|
if (result.status === 0) {
|
|
677
679
|
console.log(`\n${green("✓ Upgrade green.")} Deploy with: ${bold("npx eve deploy")}\n`);
|
|
680
|
+
return "green";
|
|
678
681
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
}
|
|
683
|
-
return;
|
|
682
|
+
console.log(`\n${red("✗ Evals failed after upgrade.")} Do NOT deploy — inspect .eve/evals/ artifacts on the host.\n`);
|
|
683
|
+
process.exitCode = 1;
|
|
684
|
+
return "failed";
|
|
684
685
|
}
|
|
685
686
|
const preflight = await preflightEvalGate(cwd);
|
|
686
687
|
if (preflight.kind === "unavailable") {
|
|
687
688
|
reportEvalGateUnavailable(cwd, sshTarget(cwd), preflight.reasons);
|
|
688
|
-
return;
|
|
689
|
+
return "unavailable";
|
|
689
690
|
}
|
|
690
691
|
console.log(bold("\nRunning the eval gate (npm run eval) — deploy only on green …\n"));
|
|
691
692
|
const ok = run("npm", ["run", "eval"], { cwd, allowFail: true });
|
|
692
693
|
if (ok) {
|
|
693
694
|
console.log(`\n${green("✓ Upgrade green.")} Deploy with: ${bold("npx eve deploy")}\n`);
|
|
695
|
+
return "green";
|
|
694
696
|
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
}
|
|
697
|
+
console.log(`\n${red("✗ Evals failed after upgrade.")} Do NOT deploy — inspect .eve/evals/ artifacts.\n`);
|
|
698
|
+
process.exitCode = 1;
|
|
699
|
+
return "failed";
|
|
699
700
|
}
|
|
700
701
|
function upgradeEnvironment(cwd) {
|
|
701
702
|
const envPath = join(cwd, ".env.local");
|
|
@@ -890,6 +891,47 @@ function startStoppedBridge(cwd, unit) {
|
|
|
890
891
|
` systemctl status ${unit}`);
|
|
891
892
|
return false;
|
|
892
893
|
}
|
|
894
|
+
/**
|
|
895
|
+
* A third-party extension whose declared peer range trails the certified
|
|
896
|
+
* eve (the Vercel-owned @github-tools/eve-extension does) makes npm's
|
|
897
|
+
* default resolver ERESOLVE before it will write a tree at all. That is not
|
|
898
|
+
* a reason to abort the upgrade: retry the same install with
|
|
899
|
+
* --legacy-peer-deps so the tree is written, then let assessPeerTree judge
|
|
900
|
+
* WHO actually objects. A @kybernesis or root conflict still stops
|
|
901
|
+
* everything; only a stale third-party edge is tolerated (same principle as
|
|
902
|
+
* the KYB-548 peer-tree gate). KYB-561.
|
|
903
|
+
*/
|
|
904
|
+
function npmInstall(cwd, args) {
|
|
905
|
+
let result = commandResult("npm", args, cwd);
|
|
906
|
+
if (result.status !== 0 && /ERESOLVE/i.test(`${result.stdout}\n${result.stderr}`)) {
|
|
907
|
+
console.log(yellow("\n ! npm reported a peer conflict. Retrying with --legacy-peer-deps so the installed peer tree can be assessed."));
|
|
908
|
+
result = commandResult("npm", [...args, "--legacy-peer-deps"], cwd);
|
|
909
|
+
}
|
|
910
|
+
return result;
|
|
911
|
+
}
|
|
912
|
+
function manifestMoved(migration) {
|
|
913
|
+
return migration.moves.length > 0 || migration.registry !== null;
|
|
914
|
+
}
|
|
915
|
+
function reportManifestMigration(migration) {
|
|
916
|
+
for (const move of migration.moves) {
|
|
917
|
+
console.log(move.to === null
|
|
918
|
+
? ` ${green("-")} ${move.name} removed ${dim(`(${move.why})`)}`
|
|
919
|
+
: ` ${yellow("↑")} ${move.name}: ${move.from} → ${move.to} ${dim(`(${move.why})`)}`);
|
|
920
|
+
}
|
|
921
|
+
if (migration.registry)
|
|
922
|
+
console.log(` ${green("+")} @kybernesis registry → ${migration.registry.to} ${dim("(the items built for this eve line)")}`);
|
|
923
|
+
}
|
|
924
|
+
function reportSourceMigration(cwd) {
|
|
925
|
+
const result = migrateSources(cwd);
|
|
926
|
+
for (const file of result.updated)
|
|
927
|
+
console.log(` ${green("+")} updated ${file} ${dim(`(for eve ${EVE_VERSION})`)}`);
|
|
928
|
+
for (const file of result.removed)
|
|
929
|
+
console.log(` ${green("-")} removed ${file} ${dim("(mounted an extension that does not run on this eve)")}`);
|
|
930
|
+
for (const { file, action } of result.manual)
|
|
931
|
+
console.log(` ${yellow("!")} ${file}: ${action}`);
|
|
932
|
+
if (result.updated.length || result.removed.length || result.manual.length)
|
|
933
|
+
console.log();
|
|
934
|
+
}
|
|
893
935
|
/** The one line `kyb upgrade --check` prints for a program to read; everything else it prints is for people. */
|
|
894
936
|
export const UPGRADE_PLAN_PREFIX = "KYB_UPGRADE_PLAN ";
|
|
895
937
|
function printPlan(plan) {
|
|
@@ -1045,13 +1087,22 @@ export async function upgrade(options = {}) {
|
|
|
1045
1087
|
const changed = [...new Set(targets
|
|
1046
1088
|
.filter((target) => target.installedVersion !== target.targetVersion && !(target.name === "eve" && versionLt(target.targetVersion, target.installedVersion)))
|
|
1047
1089
|
.map((target) => `${target.name}@${target.targetVersion}`))];
|
|
1090
|
+
// Third-party pins, dropped extensions and the registry namespace for this
|
|
1091
|
+
// eve line. pkg now carries them; it is written below with the rest.
|
|
1092
|
+
const manifest = migrateManifest(pkg, REGISTRY_URL);
|
|
1048
1093
|
if (options.check) {
|
|
1049
1094
|
printPlan({
|
|
1050
1095
|
ok: true,
|
|
1051
1096
|
eve: { installed: eveInstalled ?? null, certified: EVE_VERSION },
|
|
1052
|
-
changes: [
|
|
1097
|
+
changes: [
|
|
1098
|
+
...new Map(targets
|
|
1053
1099
|
.filter((target) => changed.includes(`${target.name}@${target.targetVersion}`))
|
|
1054
|
-
.map((target) => [target.name, { name: target.name, from: target.installedVersion, to: target.targetVersion }])).values()
|
|
1100
|
+
.map((target) => [target.name, { name: target.name, from: target.installedVersion, to: target.targetVersion }])).values(),
|
|
1101
|
+
...manifest.moves.map((move) => ({ name: move.name, from: move.from, to: move.to ?? "removed" })),
|
|
1102
|
+
// Packages every agent now ships with that this one predates: an
|
|
1103
|
+
// upgrade adds them, so the Update button has to show them.
|
|
1104
|
+
...planStudioCore(deps, (name) => newestBuildForEve(name, EVE_VERSION)),
|
|
1105
|
+
],
|
|
1055
1106
|
resetsConversations: eveChanged,
|
|
1056
1107
|
});
|
|
1057
1108
|
return;
|
|
@@ -1062,6 +1113,15 @@ export async function upgrade(options = {}) {
|
|
|
1062
1113
|
}
|
|
1063
1114
|
repairLocalQueueTimeouts(cwd, deps);
|
|
1064
1115
|
if (changed.length === 0) {
|
|
1116
|
+
if (manifestMoved(manifest)) {
|
|
1117
|
+
reportManifestMigration(manifest);
|
|
1118
|
+
writeFileSync(packagePath, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
1119
|
+
const reinstalled = npmInstall(cwd, ["install"]);
|
|
1120
|
+
if (reinstalled.status !== 0) {
|
|
1121
|
+
fail("\nnpm install failed after the dependency moves above; package.json carries them, the tree may not.");
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1065
1125
|
if (loosePin) {
|
|
1066
1126
|
const pinned = commandResult("npm", ["install", "--save-exact", `eve@${eveTarget}`], cwd);
|
|
1067
1127
|
if (pinned.status !== 0) {
|
|
@@ -1079,12 +1139,24 @@ export async function upgrade(options = {}) {
|
|
|
1079
1139
|
repairHostArtifacts(cwd, deps);
|
|
1080
1140
|
repairEvalCommand(cwd);
|
|
1081
1141
|
repairNotifySetup(cwd, deps);
|
|
1142
|
+
const added = repairStudioCore(cwd, deps);
|
|
1082
1143
|
repairReasoningSandboxes(cwd, deps);
|
|
1083
1144
|
repairManageRestart(cwd, deps);
|
|
1084
1145
|
repairExeClaudeRoutes(cwd, deps);
|
|
1146
|
+
reportSourceMigration(cwd);
|
|
1147
|
+
if (added.length === 0)
|
|
1148
|
+
return;
|
|
1149
|
+
// New tools and a schedule on a live agent: the same gate as any upgrade.
|
|
1150
|
+
run("npm", ["run", "typecheck"], { cwd });
|
|
1151
|
+
if (options.skipEval) {
|
|
1152
|
+
console.log(yellow("\nEval gate SKIPPED (--skip-eval). Run `npm run eval` before deploying.\n"));
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1155
|
+
await runUpgradeEvalGate(cwd, options.host);
|
|
1085
1156
|
return;
|
|
1086
1157
|
}
|
|
1087
1158
|
console.log(bold(`\nInstalling: ${changed.join(", ")}\n`));
|
|
1159
|
+
reportManifestMigration(manifest);
|
|
1088
1160
|
let stoppedBridge = null;
|
|
1089
1161
|
if (cleanInstall && deps["@kybernesis/buzz"]) {
|
|
1090
1162
|
const stopped = stopActiveBridge(cwd, buzzBridgeUnit(cwd));
|
|
@@ -1092,6 +1164,18 @@ export async function upgrade(options = {}) {
|
|
|
1092
1164
|
return;
|
|
1093
1165
|
stoppedBridge = stopped;
|
|
1094
1166
|
}
|
|
1167
|
+
// A new eve line: keep the conversations aside before anything on it boots
|
|
1168
|
+
// (its first boot deletes an older line's store, and the eval gate's dev
|
|
1169
|
+
// server shares the store with production), and drop the old line's caches.
|
|
1170
|
+
const storeLine = cleanInstall && installedMajorMinor ? installedMajorMinor.join(".") : null;
|
|
1171
|
+
if (storeLine) {
|
|
1172
|
+
const kept = backUpWorkflowStore(cwd, storeLine);
|
|
1173
|
+
if (kept !== "none") {
|
|
1174
|
+
console.log(` ${green("+")} conversations from eve ${storeLine} kept at ${workflowStoreBackup(storeLine)}${kept === "kept" ? ` ${dim("(from an earlier attempt)")}` : ""}`);
|
|
1175
|
+
}
|
|
1176
|
+
for (const dir of clearStaleEveCaches(cwd))
|
|
1177
|
+
console.log(` ${green("-")} cleared ${dir} ${dim(`(built for eve ${storeLine})`)}`);
|
|
1178
|
+
}
|
|
1095
1179
|
if (cleanInstall) {
|
|
1096
1180
|
rewriteManifest(cwd, pkg, targets);
|
|
1097
1181
|
try {
|
|
@@ -1108,28 +1192,12 @@ export async function upgrade(options = {}) {
|
|
|
1108
1192
|
// reason rewriteManifest pins it: npm would save a caret by default.
|
|
1109
1193
|
const eveChange = changed.find((spec) => spec.startsWith("eve@")) ?? (loosePin ? `eve@${eveTarget}` : undefined);
|
|
1110
1194
|
const others = changed.filter((spec) => spec !== eveChange);
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
* eve (the Vercel-owned @github-tools/eve-extension and @agent-browser/eve
|
|
1114
|
-
* both do) makes npm's default resolver ERESOLVE before it will write a
|
|
1115
|
-
* tree at all. That is not a reason to abort the upgrade: retry the same
|
|
1116
|
-
* install with --legacy-peer-deps so the tree is written, then let
|
|
1117
|
-
* assessPeerTree below judge WHO actually objects. A @kybernesis or root
|
|
1118
|
-
* conflict still stops everything; only a stale third-party edge is
|
|
1119
|
-
* tolerated (same principle as the KYB-548 peer-tree gate). KYB-561.
|
|
1120
|
-
*/
|
|
1121
|
-
const npmInstall = (args) => {
|
|
1122
|
-
let result = commandResult("npm", args, cwd);
|
|
1123
|
-
if (result.status !== 0 && /ERESOLVE/i.test(`${result.stdout}\n${result.stderr}`)) {
|
|
1124
|
-
console.log(yellow("\n ! npm reported a peer conflict. Retrying with --legacy-peer-deps so the installed peer tree can be assessed."));
|
|
1125
|
-
result = commandResult("npm", [...args, "--legacy-peer-deps"], cwd);
|
|
1126
|
-
}
|
|
1127
|
-
return result;
|
|
1128
|
-
};
|
|
1195
|
+
if (!cleanInstall && manifestMoved(manifest))
|
|
1196
|
+
writeFileSync(packagePath, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
1129
1197
|
const installArgs = cleanInstall ? ["install"] : ["install", ...others];
|
|
1130
|
-
let installed = cleanInstall || others.length > 0 ? npmInstall(installArgs) : { status: 0, stdout: "", stderr: "" };
|
|
1198
|
+
let installed = cleanInstall || others.length > 0 || manifestMoved(manifest) ? npmInstall(cwd, installArgs) : { status: 0, stdout: "", stderr: "" };
|
|
1131
1199
|
if (installed.status === 0 && !cleanInstall && eveChange) {
|
|
1132
|
-
installed = npmInstall(["install", "--save-exact", eveChange]);
|
|
1200
|
+
installed = npmInstall(cwd, ["install", "--save-exact", eveChange]);
|
|
1133
1201
|
}
|
|
1134
1202
|
if (installed.status !== 0) {
|
|
1135
1203
|
const output = `${installed.stdout}\n${installed.stderr}`;
|
|
@@ -1171,9 +1239,11 @@ export async function upgrade(options = {}) {
|
|
|
1171
1239
|
repairHostArtifacts(cwd, deps);
|
|
1172
1240
|
repairEvalCommand(cwd);
|
|
1173
1241
|
repairNotifySetup(cwd, deps);
|
|
1242
|
+
repairStudioCore(cwd, deps);
|
|
1174
1243
|
repairReasoningSandboxes(cwd, deps);
|
|
1175
1244
|
repairManageRestart(cwd, deps);
|
|
1176
1245
|
repairExeClaudeRoutes(cwd, deps);
|
|
1246
|
+
reportSourceMigration(cwd);
|
|
1177
1247
|
run("npm", ["run", "typecheck"], { cwd });
|
|
1178
1248
|
if (eveChanged) {
|
|
1179
1249
|
const infoOk = run("npx", ["eve", "info"], { cwd, allowFail: true, quiet: true });
|
|
@@ -1183,5 +1253,9 @@ export async function upgrade(options = {}) {
|
|
|
1183
1253
|
console.log(yellow("\nEval gate SKIPPED (--skip-eval). Run `npm run eval` before deploying.\n"));
|
|
1184
1254
|
return;
|
|
1185
1255
|
}
|
|
1186
|
-
await runUpgradeEvalGate(cwd, options.host);
|
|
1256
|
+
const outcome = await runUpgradeEvalGate(cwd, options.host);
|
|
1257
|
+
// Going back means the old line, and the old line needs its conversations.
|
|
1258
|
+
if (outcome === "failed" && storeLine && restoreWorkflowStore(cwd, storeLine)) {
|
|
1259
|
+
console.log(` ${green("+")} conversations from eve ${storeLine} put back from ${workflowStoreBackup(storeLine)} ${dim("(for when the agent goes back to it)")}\n`);
|
|
1260
|
+
}
|
|
1187
1261
|
}
|
package/dist/util.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export declare const EVE_VERSION = "0.
|
|
2
|
-
|
|
1
|
+
export declare const EVE_VERSION = "0.81.1";
|
|
2
|
+
/** The registry items built for EVE_VERSION's line; every @kybernesis dep in them is tagged for it. */
|
|
3
|
+
export declare const REGISTRY_URL = "https://registry.kybernesis.ai/r/eve-0.81/{name}.json";
|
|
3
4
|
export declare const DEFAULT_ISSUER = "https://agent.kybernesis.ai";
|
|
4
5
|
export declare const green: (s: string) => string;
|
|
5
6
|
export declare const yellow: (s: string) => string;
|
package/dist/util.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { createInterface } from "node:readline/promises";
|
|
3
|
-
export const EVE_VERSION = "0.
|
|
4
|
-
|
|
3
|
+
export const EVE_VERSION = "0.81.1";
|
|
4
|
+
/** The registry items built for EVE_VERSION's line; every @kybernesis dep in them is tagged for it. */
|
|
5
|
+
export const REGISTRY_URL = "https://registry.kybernesis.ai/r/eve-0.81/{name}.json";
|
|
5
6
|
export const DEFAULT_ISSUER = "https://agent.kybernesis.ai";
|
|
6
7
|
const TTY = process.stdout.isTTY === true;
|
|
7
8
|
export const green = (s) => (TTY ? `\x1b[32m${s}\x1b[0m` : s);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kybernesis/create",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
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",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "24.x",
|
|
42
|
-
"eve": "0.
|
|
42
|
+
"eve": "0.81.1",
|
|
43
43
|
"typescript": "7.0.2"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|
|
@@ -72,7 +72,7 @@ Docs: `docs/connections/*`.
|
|
|
72
72
|
|
|
73
73
|
## Skills (agent/skills/)
|
|
74
74
|
|
|
75
|
-
On-demand procedures (model calls `
|
|
75
|
+
On-demand procedures (model calls `eve__skill` when the description matches).
|
|
76
76
|
Forms: flat `.md` (first line = routing description) → packaged dir with
|
|
77
77
|
`SKILL.md` (+`references/`, description frontmatter required) → `defineSkill`
|
|
78
78
|
(only for typed/generated content). The description is a ROUTING HINT — write
|
|
@@ -112,7 +112,7 @@ Instruct serial delegation ("one draft at a time, wait for each result").
|
|
|
112
112
|
## Test in eve dev
|
|
113
113
|
|
|
114
114
|
`npx eve dev` boots the local runtime + chat TUI. Walk: identity → skill
|
|
115
|
-
routing (watch
|
|
115
|
+
routing (watch eve__skill) → delegation → memory round-trip → (engineer)
|
|
116
116
|
screenshot turn. Local principal counts as a DM surface. Kill the dev server
|
|
117
117
|
before `npm run eval`. The TUI is NOT the deployed agent — redeploy after
|
|
118
118
|
every change.
|
|
@@ -77,7 +77,7 @@ exactly §§2, 5, 6, and 7 of this playbook:
|
|
|
77
77
|
| --- | --- | --- |
|
|
78
78
|
| `npm create @kybernesis <name>` | Day 1, once | Same as `kyb init <name>` without installing anything first |
|
|
79
79
|
| `kyb init <name>` | Day 1, once | eve scaffold (pinned version) + registry + all four packages + generated departments + eval wiring + env template + checklist |
|
|
80
|
-
| `kyb init <name> --engineer` | When the pilot includes building software | Everything above PLUS the engineer layer: workshop sandbox (Playwright baked into the template), the vision screenshot tool, build/ship skills, and the official limbs (
|
|
80
|
+
| `kyb init <name> --engineer` | When the pilot includes building software | Everything above PLUS the engineer layer: workshop sandbox (Playwright baked into the template), the vision screenshot tool, build/ship skills, and the official limbs (github-tools, vercel connection) |
|
|
81
81
|
| `kyb doctor` | **Constantly** — after every human step, before every deploy, whenever anything is weird | Live preflight: every Arcana key↔workspace pair validated against the API (with the specific fix per failure: wrong-key 403 vs missing-workspace 404), issuer JWKS reachability, `KYBERNESIS_AGENT`, Slack connector env, `eve info` discovery, port-2000 conflict. Exit 1 on failure → usable in CI |
|
|
82
82
|
| `kyb upgrade` | Maintenance visits / after Kybernesis ships a package update | Compares installed `@kybernesis/*` against npm **and eve against the Kybernesis-certified version** (never blindly npm-latest — we certify eve releases in the platform repo first), installs what's behind, typechecks (+ `eve info` after a framework bump), then **runs the eval suite as the gate** — tells you to deploy only on green |
|
|
83
83
|
| `kyb upgrade --skip-eval` | Never for production changes | Same, without the gate |
|
|
@@ -613,8 +613,10 @@ six build/ship skills + engineering-conduct instructions) and
|
|
|
613
613
|
`agent/sandbox/sandbox.ts` (the **workshop**: Playwright + Chromium baked into
|
|
614
614
|
the sandbox template, domain allowlist on deployed sessions). Pair it with the
|
|
615
615
|
official limbs if not using `kyb init --engineer`:
|
|
616
|
-
`npx eve add extension/
|
|
617
|
-
|
|
616
|
+
`npx eve add extension/github-tools connection/vercel` (run individually), then
|
|
617
|
+
pin `@github-tools/eve-extension` to the release in `kyb`'s THIRD_PARTY_PINS.
|
|
618
|
+
(extension/agent-browser does not mount on eve 0.81; the agent reads pages on
|
|
619
|
+
its own computer with `open_browser`.)
|
|
618
620
|
|
|
619
621
|
**Wire the Vercel connection** (preview deploys + a clickable link for every
|
|
620
622
|
build — proven live 2026-08-06). After `vercel link` in the agent dir:
|
|
@@ -866,7 +868,7 @@ reach for the connection at all. Write it like a capability, name the systems.
|
|
|
866
868
|
### 4.3e Skills — teach the client's procedures (and mine skills.sh)
|
|
867
869
|
|
|
868
870
|
A skill is a markdown procedure the model loads **on demand** (eve advertises
|
|
869
|
-
each skill's description; the model calls `
|
|
871
|
+
each skill's description; the model calls `eve__skill` when a turn matches).
|
|
870
872
|
Anything the client's team does "the same way every time" — their release
|
|
871
873
|
checklist, escalation path, report format — is a skill, not an always-on
|
|
872
874
|
instruction. Keep always-on for identity and rules; skills for procedures.
|
|
@@ -1018,7 +1020,7 @@ this order — each line catches a different class of wiring mistake:
|
|
|
1018
1020
|
1. **Identity**: "who are you, what can you do?" — does the persona match
|
|
1019
1021
|
identity.md, and does it write chat-length answers?
|
|
1020
1022
|
2. **Skill routing**: ask something a skill covers *without naming the skill*
|
|
1021
|
-
— watch for the `
|
|
1023
|
+
— watch for the `eve__skill` call. If it doesn't fire, the skill's
|
|
1022
1024
|
`description` isn't written as a triggering task.
|
|
1023
1025
|
3. **Delegation**: ask a department question — watch the subagent call and
|
|
1024
1026
|
check the answer came back briefed (§4.4's "pack everything" rule).
|