@kybernesis/create 0.24.2 → 0.24.4

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.
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Give every `exeModel({ …, createOpenAI })` call in an agent the Anthropic
3
+ * factory too.
4
+ *
5
+ * @remarks
6
+ * Claude on the exe gateway is served on its Messages API, not the OpenAI one.
7
+ * Older scaffolds wrote subagents such as the builder with only `createOpenAI`
8
+ * and a Claude default (`anthropic/claude-sonnet-5`). That never had a working
9
+ * route, and since exe 0.17 `exeModel` refuses it at load, so the agent's whole
10
+ * eval suite stops before the first case and an update is rolled back for a
11
+ * file nobody touched. Passing `createAnthropic` as well is harmless for a
12
+ * non-Claude id (exeModel only uses it for `anthropic/…`), so the repair is
13
+ * unconditional for any call that lacks it.
14
+ *
15
+ * Returns the agent-relative paths it changed.
16
+ */
17
+ export declare function repairExeClaudeRoute(cwd: string): string[];
18
+ /** The source with `createAnthropic` passed to each exeModel call that has only `createOpenAI`, and imported. */
19
+ export declare function withAnthropicFactory(source: string): string;
@@ -0,0 +1,69 @@
1
+ import { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
2
+ import { join, relative } from "node:path";
3
+ /**
4
+ * Give every `exeModel({ …, createOpenAI })` call in an agent the Anthropic
5
+ * factory too.
6
+ *
7
+ * @remarks
8
+ * Claude on the exe gateway is served on its Messages API, not the OpenAI one.
9
+ * Older scaffolds wrote subagents such as the builder with only `createOpenAI`
10
+ * and a Claude default (`anthropic/claude-sonnet-5`). That never had a working
11
+ * route, and since exe 0.17 `exeModel` refuses it at load, so the agent's whole
12
+ * eval suite stops before the first case and an update is rolled back for a
13
+ * file nobody touched. Passing `createAnthropic` as well is harmless for a
14
+ * non-Claude id (exeModel only uses it for `anthropic/…`), so the repair is
15
+ * unconditional for any call that lacks it.
16
+ *
17
+ * Returns the agent-relative paths it changed.
18
+ */
19
+ export function repairExeClaudeRoute(cwd) {
20
+ const root = join(cwd, "agent");
21
+ if (!existsSync(root))
22
+ return [];
23
+ const changed = [];
24
+ for (const file of sourceFiles(root)) {
25
+ const source = readFileSync(file, "utf8");
26
+ const next = withAnthropicFactory(source);
27
+ if (next === source)
28
+ continue;
29
+ writeFileSync(file, next);
30
+ changed.push(relative(cwd, file));
31
+ }
32
+ return changed;
33
+ }
34
+ /** The source with `createAnthropic` passed to each exeModel call that has only `createOpenAI`, and imported. */
35
+ export function withAnthropicFactory(source) {
36
+ if (!/\bexeModel\s*\(/.test(source))
37
+ return source;
38
+ let touched = false;
39
+ const next = source.replace(/\bexeModel\s*\(\s*\{([^{}]*)\}\s*\)/g, (call, body) => {
40
+ if (!/\bcreateOpenAI\b/.test(body) || /\bcreateAnthropic\b/.test(body))
41
+ return call;
42
+ touched = true;
43
+ return call.replace(/\bcreateOpenAI\b/, "createOpenAI, createAnthropic");
44
+ });
45
+ if (!touched)
46
+ return source;
47
+ if (/\bimport\s*\{[^}]*\bcreateAnthropic\b[^}]*\}\s*from\s*["']@ai-sdk\/anthropic["']/.test(next))
48
+ return next;
49
+ const openaiImport = /^import\s*\{[^}]*\bcreateOpenAI\b[^}]*\}\s*from\s*["']@ai-sdk\/openai["'];?[^\n]*\n/m.exec(next);
50
+ const line = 'import { createAnthropic } from "@ai-sdk/anthropic";\n';
51
+ if (openaiImport) {
52
+ const at = openaiImport.index + openaiImport[0].length;
53
+ return next.slice(0, at) + line + next.slice(at);
54
+ }
55
+ return line + next;
56
+ }
57
+ function sourceFiles(dir) {
58
+ const out = [];
59
+ for (const entry of readdirSync(dir)) {
60
+ if (entry === "node_modules" || entry.startsWith("."))
61
+ continue;
62
+ const full = join(dir, entry);
63
+ if (statSync(full).isDirectory())
64
+ out.push(...sourceFiles(full));
65
+ else if (/\.(ts|mts|js|mjs)$/.test(entry))
66
+ out.push(full);
67
+ }
68
+ return out;
69
+ }
package/dist/upgrade.js CHANGED
@@ -11,6 +11,7 @@ import { dockerPruneCronArtifact } from "./docker-prune-cron.js";
11
11
  import { newestOnLine } from "./on-line-version.js";
12
12
  import { findMatchingAgentServiceUnit, repairManageRestart } from "./systemd.js";
13
13
  import { repairTerminalSandboxCleanupHooks } from "./sandbox-cleanup.js";
14
+ import { repairExeClaudeRoute } from "./exe-claude-route.js";
14
15
  import { repairOptInTools, repairRemovedDefaultTools as repairRemovedDefaultTools_ } from "./removed-default-tools.js";
15
16
  import { EVE_VERSION, bold, capture, dim, green, parseEnv, red, run, yellow } from "./util.js";
16
17
  import { inspectEveAgent } from "./agent-limits.js";
@@ -75,6 +76,25 @@ function repairLocalQueueTimeouts(cwd, deps) {
75
76
  ` ${dim("slower than 30s was redelivered and its steps re-run — the agent answered twice.")}\n` +
76
77
  ` ${dim("Takes effect on the next server restart.")}\n`);
77
78
  }
79
+ /** Route Claude ids through the gateway's Messages API wherever an agent builds an exe model (see exe-claude-route.ts). */
80
+ function repairExeClaudeRoutes(cwd, deps) {
81
+ if (!deps["@kybernesis/exe"])
82
+ return;
83
+ const changed = repairExeClaudeRoute(cwd);
84
+ if (changed.length === 0)
85
+ return;
86
+ if (!deps["@ai-sdk/anthropic"]) {
87
+ const added = commandResult("npm", ["install", "@ai-sdk/anthropic"], cwd);
88
+ if (added.status !== 0) {
89
+ console.log(red(` ✗ could not install @ai-sdk/anthropic for the Claude route:\n${added.stderr.trim()}`));
90
+ process.exitCode = 1;
91
+ return;
92
+ }
93
+ }
94
+ console.log(` ${green("+")} passed createAnthropic to exeModel in ${changed.join(", ")}\n` +
95
+ ` ${dim("Claude on the exe gateway is served on its Messages API; with only createOpenAI")}\n` +
96
+ ` ${dim("a Claude id has no route, and exe 0.17+ refuses it when the agent loads.")}\n`);
97
+ }
78
98
  /**
79
99
  * Finish a Buzz install that a version bump alone leaves half-done.
80
100
  *
@@ -582,7 +602,10 @@ function exeJudgeUrl(cwd, env) {
582
602
  if (!judgeProvider)
583
603
  return null;
584
604
  const escaped = judgeProvider.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
585
- const providerBody = new RegExp(`(?:const|let)\\s+${escaped}\\s*=\\s*create[A-Za-z]+\\s*\\(\\s*\\{([\\s\\S]*?)\\}\\s*\\)`).exec(source)?.[1];
605
+ // The provider may be made inline (`model: createAnthropic({ … }).evaluationModel(…)`)
606
+ // as well as bound to a name first; both carry the base URL in the same object.
607
+ const inline = /judge\s*:\s*\{[\s\S]*?model\s*:\s*create[A-Za-z]+\s*\(\s*\{([\s\S]*?)\}\s*\)/.exec(source)?.[1];
608
+ const providerBody = inline ?? new RegExp(`(?:const|let)\\s+${escaped}\\s*=\\s*create[A-Za-z]+\\s*\\(\\s*\\{([\\s\\S]*?)\\}\\s*\\)`).exec(source)?.[1];
586
609
  if (!providerBody)
587
610
  return /EXE_LLM_URL|llm\.int\.exe\.xyz/.test(source) ? undefined : null;
588
611
  if (/EXE_LLM_URL/.test(providerBody))
@@ -915,6 +938,21 @@ export async function upgrade(options = {}) {
915
938
  console.log(` ${dim(selfVersion.fix)} ${dim("(continuing: --allow-stale)")}\n`);
916
939
  }
917
940
  reportAgentInputLimit(cwd);
941
+ // An agent already on a newer eve line than this kyb was certified on must
942
+ // be upgraded by that line's kyb: from here every @kybernesis package would
943
+ // be "upgraded" to a build for the older eve.
944
+ const eveNow = installedPackageValue(cwd, "eve", ".version");
945
+ const nowLine = eveNow ? majorMinor(eveNow) : null;
946
+ const ownLine = majorMinor(EVE_VERSION);
947
+ if (nowLine && ownLine && (nowLine[0] > ownLine[0] || (nowLine[0] === ownLine[0] && nowLine[1] > ownLine[1]))) {
948
+ if (options.check)
949
+ printPlan({ ok: false, unresolved: ["eve"], changes: [], resetsConversations: false });
950
+ fail(`This agent runs eve ${eveNow}, a newer line than this kyb is certified for (${EVE_VERSION}). ` +
951
+ `Run the kyb for its line:\n npx --yes --package=@kybernesis/create@eve-${nowLine[0]}.${nowLine[1]} kyb upgrade\n` +
952
+ `Nothing was changed.`);
953
+ return;
954
+ }
955
+ const selfInstalled = "installed" in selfVersion ? selfVersion.installed : undefined;
918
956
  const entries = dependencyEntries(pkg);
919
957
  const installedVersions = new Map();
920
958
  const targetVersions = new Map();
@@ -933,11 +971,16 @@ export async function upgrade(options = {}) {
933
971
  // that supports the eve this host will be running — NOT npm's `latest`,
934
972
  // which for a per-eve-line package is routinely a build for a different
935
973
  // line and installs without complaint.
974
+ // kyb itself declares no eve peer, so the newest published kyb can belong
975
+ // to another eve line. The project carries the kyb doing this upgrade (a
976
+ // stale one renewed itself above), never a newer one.
936
977
  const target = name === "eve"
937
978
  ? EVE_VERSION
938
- : name.startsWith("@kybernesis/")
939
- ? newestBuildForEve(name, EVE_VERSION)
940
- : capture("npm", ["view", name, "version"])?.trim();
979
+ : name === "@kybernesis/create"
980
+ ? selfInstalled && versionLt(installed, selfInstalled) ? selfInstalled : installed
981
+ : name.startsWith("@kybernesis/")
982
+ ? newestBuildForEve(name, EVE_VERSION)
983
+ : capture("npm", ["view", name, "version"])?.trim();
941
984
  if (!target) {
942
985
  console.log(` ${yellow("!")} ${name}: ` +
943
986
  (name.startsWith("@kybernesis/")
@@ -1038,6 +1081,7 @@ export async function upgrade(options = {}) {
1038
1081
  repairNotifySetup(cwd, deps);
1039
1082
  repairReasoningSandboxes(cwd, deps);
1040
1083
  repairManageRestart(cwd, deps);
1084
+ repairExeClaudeRoutes(cwd, deps);
1041
1085
  return;
1042
1086
  }
1043
1087
  console.log(bold(`\nInstalling: ${changed.join(", ")}\n`));
@@ -1129,6 +1173,7 @@ export async function upgrade(options = {}) {
1129
1173
  repairNotifySetup(cwd, deps);
1130
1174
  repairReasoningSandboxes(cwd, deps);
1131
1175
  repairManageRestart(cwd, deps);
1176
+ repairExeClaudeRoutes(cwd, deps);
1132
1177
  run("npm", ["run", "typecheck"], { cwd });
1133
1178
  if (eveChanged) {
1134
1179
  const infoOk = run("npx", ["eve", "info"], { cwd, allowFail: true, quiet: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kybernesis/create",
3
- "version": "0.24.2",
3
+ "version": "0.24.4",
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",