@kybernesis/create 0.14.1 → 0.14.3

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,30 @@
1
+ /** Eve 0.49's root default when the compiled config omits the policy. */
2
+ export declare const CERTIFIED_INHERITED_MAX_INPUT_TOKENS_PER_SESSION = 40000000;
3
+ export type AgentInputLimit = {
4
+ kind: "explicit-numeric";
5
+ value: number;
6
+ } | {
7
+ kind: "explicit-uncapped";
8
+ } | {
9
+ kind: "inherited";
10
+ value: typeof CERTIFIED_INHERITED_MAX_INPUT_TOKENS_PER_SESSION;
11
+ } | {
12
+ kind: "unresolved";
13
+ reason: string;
14
+ };
15
+ export interface EveInfoInspection {
16
+ diagnostics: {
17
+ errors: number;
18
+ warnings: number;
19
+ } | null;
20
+ status: string | null;
21
+ limit: AgentInputLimit;
22
+ }
23
+ /**
24
+ * Read the effective root policy from Eve's compiled artifact, never from
25
+ * authored TypeScript. The 40M fallback is certified only when that artifact
26
+ * truly omits maxInputTokensPerSession.
27
+ */
28
+ export declare function parseEveInfoInspection(output: string, readText?: (path: string) => string): EveInfoInspection;
29
+ /** Compile once through Eve's structured info contract and inspect its artifact. */
30
+ export declare function inspectEveAgent(cwd: string, env?: Record<string, string>): EveInfoInspection | null;
@@ -0,0 +1,86 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { capture } from "./util.js";
3
+ /** Eve 0.49's root default when the compiled config omits the policy. */
4
+ export const CERTIFIED_INHERITED_MAX_INPUT_TOKENS_PER_SESSION = 40_000_000;
5
+ /**
6
+ * Read the effective root policy from Eve's compiled artifact, never from
7
+ * authored TypeScript. The 40M fallback is certified only when that artifact
8
+ * truly omits maxInputTokensPerSession.
9
+ */
10
+ export function parseEveInfoInspection(output, readText = (path) => readFileSync(path, "utf8")) {
11
+ let info;
12
+ try {
13
+ info = JSON.parse(output);
14
+ }
15
+ catch (error) {
16
+ return unresolved(`eve info --json was not valid JSON: ${error.message}`);
17
+ }
18
+ const diagnostics = parseDiagnostics(info.diagnostics);
19
+ const status = typeof info.status === "string" ? info.status : null;
20
+ const manifestPath = info.artifacts && typeof info.artifacts.compiledManifest === "string"
21
+ ? info.artifacts.compiledManifest
22
+ : null;
23
+ if (!manifestPath) {
24
+ return { diagnostics, status, limit: { kind: "unresolved", reason: "compiled manifest path was unavailable" } };
25
+ }
26
+ let manifest;
27
+ try {
28
+ manifest = JSON.parse(readText(manifestPath));
29
+ }
30
+ catch (error) {
31
+ return {
32
+ diagnostics,
33
+ status,
34
+ limit: { kind: "unresolved", reason: `compiled manifest could not be read: ${error.message}` },
35
+ };
36
+ }
37
+ if (!isRecord(manifest) || !isRecord(manifest.config)) {
38
+ return { diagnostics, status, limit: { kind: "unresolved", reason: "compiled root config was unavailable" } };
39
+ }
40
+ const limits = manifest.config.limits;
41
+ if (limits === undefined) {
42
+ return inherited(diagnostics, status);
43
+ }
44
+ if (!isRecord(limits)) {
45
+ return { diagnostics, status, limit: { kind: "unresolved", reason: "compiled root limits were malformed" } };
46
+ }
47
+ if (!Object.hasOwn(limits, "maxInputTokensPerSession")) {
48
+ return inherited(diagnostics, status);
49
+ }
50
+ const value = limits.maxInputTokensPerSession;
51
+ if (value === false)
52
+ return { diagnostics, status, limit: { kind: "explicit-uncapped" } };
53
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) {
54
+ return { diagnostics, status, limit: { kind: "explicit-numeric", value } };
55
+ }
56
+ return {
57
+ diagnostics,
58
+ status,
59
+ limit: { kind: "unresolved", reason: "compiled maxInputTokensPerSession was neither a positive number nor false" },
60
+ };
61
+ }
62
+ /** Compile once through Eve's structured info contract and inspect its artifact. */
63
+ export function inspectEveAgent(cwd, env) {
64
+ const output = capture("npx", ["eve", "info", "--json"], cwd, env);
65
+ return output === null ? null : parseEveInfoInspection(output);
66
+ }
67
+ function parseDiagnostics(value) {
68
+ if (!isRecord(value))
69
+ return null;
70
+ return typeof value.errors === "number" && typeof value.warnings === "number"
71
+ ? { errors: value.errors, warnings: value.warnings }
72
+ : null;
73
+ }
74
+ function inherited(diagnostics, status) {
75
+ return {
76
+ diagnostics,
77
+ status,
78
+ limit: { kind: "inherited", value: CERTIFIED_INHERITED_MAX_INPUT_TOKENS_PER_SESSION },
79
+ };
80
+ }
81
+ function unresolved(reason) {
82
+ return { diagnostics: null, status: null, limit: { kind: "unresolved", reason } };
83
+ }
84
+ function isRecord(value) {
85
+ return typeof value === "object" && value !== null && !Array.isArray(value);
86
+ }
package/dist/doctor.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type AgentInputLimit } from "./agent-limits.js";
1
2
  type Verdict = "pass" | "warn" | "fail";
2
3
  export interface Check {
3
4
  verdict: Verdict;
@@ -16,6 +17,7 @@ export interface DockerTemplateInspection {
16
17
  }[];
17
18
  }
18
19
  export declare function dockerTemplateDoctorChecks(result: DockerTemplateInspection): Check[];
20
+ export declare function agentInputLimitDoctorCheck(limit: AgentInputLimit): Check;
19
21
  /**
20
22
  * The inspector lives in @kybernesis/exe, next to the runtime that owns the
21
23
  * templates; create already resolves that package's scripts out of
package/dist/doctor.js CHANGED
@@ -3,6 +3,7 @@ import { join } from "node:path";
3
3
  import { pathToFileURL } from "node:url";
4
4
  import { bold, capture, dim, green, parseEnv, red, yellow } from "./util.js";
5
5
  import { diagnoseManageRestart, findMatchingAgentServiceUnit } from "./systemd.js";
6
+ import { inspectEveAgent } from "./agent-limits.js";
6
7
  const MARK = {
7
8
  pass: green("✓"),
8
9
  warn: yellow("!"),
@@ -30,6 +31,26 @@ export function dockerTemplateDoctorChecks(result) {
30
31
  detail: issue.detail,
31
32
  }));
32
33
  }
34
+ export function agentInputLimitDoctorCheck(limit) {
35
+ switch (limit.kind) {
36
+ case "explicit-numeric":
37
+ return { verdict: "pass", label: `Eve max input tokens/session: ${limit.value.toLocaleString("en-US")} (explicit)` };
38
+ case "explicit-uncapped":
39
+ return { verdict: "pass", label: "Eve max input tokens/session: uncapped (explicit)" };
40
+ case "inherited":
41
+ return {
42
+ verdict: "warn",
43
+ label: `Eve max input tokens/session: ${limit.value.toLocaleString("en-US")} (inherited default)`,
44
+ detail: "set limits.maxInputTokensPerSession explicitly in the root agent; use false only when the host policy intentionally permits an uncapped session",
45
+ };
46
+ case "unresolved":
47
+ return {
48
+ verdict: "warn",
49
+ label: "Eve max input tokens/session: unresolved",
50
+ detail: limit.reason,
51
+ };
52
+ }
53
+ }
33
54
  /**
34
55
  * The inspector lives in @kybernesis/exe, next to the runtime that owns the
35
56
  * templates; create already resolves that package's scripts out of
@@ -481,16 +502,19 @@ export async function doctor() {
481
502
  * `env` here is the same merged view every other check reads: .env.local
482
503
  * underneath, the real environment on top.
483
504
  */
484
- const info = capture("npx", ["eve", "info"], cwd, env);
505
+ const info = inspectEveAgent(cwd, env);
485
506
  if (info === null) {
486
- add("fail", "eve info failed", "run: set -a && . ./.env.local && set +a && npx eve info — the agent's own error is in that output");
507
+ add("fail", "eve info failed", "run: set -a && . ./.env.local && set +a && npx eve info --json — the agent's own error is in that output");
508
+ add("warn", "Eve max input tokens/session: unresolved", "eve info failed before the compiled root policy could be inspected");
487
509
  }
488
510
  else {
489
- const diag = /Diagnostics\s+(\d+) errors?, (\d+) warnings?/.exec(info);
490
- if (diag && diag[1] === "0")
491
- add("pass", `eve discovery clean (${diag[2]} warnings)`);
511
+ const diag = info.diagnostics;
512
+ if (diag && diag.errors === 0)
513
+ add("pass", `eve discovery clean (${diag.warnings} warnings)`);
492
514
  else
493
- add("fail", `eve discovery: ${diag ? `${diag[1]} errors` : "unparsed"}`, "npx eve info");
515
+ add("fail", `eve discovery: ${diag ? `${diag.errors} errors` : "unparsed"}`, "npx eve info --json");
516
+ const limitCheck = agentInputLimitDoctorCheck(info.limit);
517
+ add(limitCheck.verdict, limitCheck.label, limitCheck.detail);
494
518
  }
495
519
  const portBusy = capture("lsof", ["-ti", ":2000"]);
496
520
  if (portBusy && portBusy.trim())
package/dist/systemd.js CHANGED
@@ -72,11 +72,24 @@ export function reconcileManageRestart(cwd, name) {
72
72
  return "blocked";
73
73
  }
74
74
  }
75
+ function dropIns(dir) {
76
+ try {
77
+ return readdirSync(dir)
78
+ .filter((name) => name.endsWith(".conf"))
79
+ .sort()
80
+ .map((name) => readFileSync(join(dir, name), "utf8"));
81
+ }
82
+ catch {
83
+ return [];
84
+ }
85
+ }
75
86
  export function findMatchingAgentServiceUnit(cwd, systemdDir = "/etc/systemd/system") {
76
87
  try {
77
88
  for (const file of readdirSync(systemdDir).filter((entry) => entry.endsWith("-agent.service"))) {
78
89
  const path = join(systemdDir, file);
79
- const contents = readFileSync(path, "utf8");
90
+ // The effective unit is the file plus its drop-ins (<unit>.d/*.conf, in
91
+ // name order) — a build gate added through `systemctl edit` counts.
92
+ const contents = [readFileSync(path, "utf8"), ...dropIns(`${path}.d`)].join("\n");
80
93
  const values = parseAgentServiceUnit(path, contents);
81
94
  if (values && resolve(values.app) === resolve(cwd)) {
82
95
  return { path, contents, values, mode: statSync(path).mode & 0o777 };
package/dist/templates.js CHANGED
@@ -366,6 +366,8 @@ import { exeModel } from "@kybernesis/exe";
366
366
  export default defineAgent({
367
367
  model: exeModel({ model: process.env.EXE_MODEL ?? ${JSON.stringify(model)}, createOpenAI }),
368
368
  modelContextWindowTokens: 200_000,
369
+ // This long-lived self-hosted process has no metered platform session ceiling.
370
+ limits: { maxInputTokensPerSession: false },
369
371
  });
370
372
  `;
371
373
  }
package/dist/upgrade.d.ts CHANGED
@@ -1 +1,3 @@
1
+ import { type AgentInputLimit } from "./agent-limits.js";
2
+ export declare function agentInputLimitUpgradeMessage(limit: AgentInputLimit): string;
1
3
  export declare function upgrade(skipEval: boolean): Promise<void>;
package/dist/upgrade.js CHANGED
@@ -5,7 +5,8 @@ import { reconcileHostArtifact } from "./host-artifacts.js";
5
5
  import { repairManageRestart } from "./systemd.js";
6
6
  import { repairTerminalSandboxCleanupHooks } from "./sandbox-cleanup.js";
7
7
  import { repairRemovedDefaultTools as repairRemovedDefaultTools_ } from "./removed-default-tools.js";
8
- import { EVE_VERSION, bold, capture, dim, green, red, run, yellow } from "./util.js";
8
+ import { EVE_VERSION, bold, capture, dim, green, parseEnv, red, run, yellow } from "./util.js";
9
+ import { inspectEveAgent } from "./agent-limits.js";
9
10
  /**
10
11
  * Which packages to upgrade: every `@kybernesis/*` this agent depends on.
11
12
  *
@@ -61,6 +62,32 @@ function warnIfStale() {
61
62
  `compiled into this tool, so an old kyb reports an old pin as current.`);
62
63
  console.log(` ${dim("npm install -g @kybernesis/create@latest")}\n`);
63
64
  }
65
+ export function agentInputLimitUpgradeMessage(limit) {
66
+ switch (limit.kind) {
67
+ case "explicit-numeric":
68
+ return `Eve max input tokens/session: ${limit.value.toLocaleString("en-US")} (explicit; unchanged)`;
69
+ case "explicit-uncapped":
70
+ return "Eve max input tokens/session: uncapped (explicit; unchanged)";
71
+ case "inherited":
72
+ return `Eve max input tokens/session: ${limit.value.toLocaleString("en-US")} inherited — set limits.maxInputTokensPerSession explicitly in agent/agent.ts; upgrade will not rewrite authored source`;
73
+ case "unresolved":
74
+ return `Eve max input tokens/session: unresolved (${limit.reason}); authored source was not changed`;
75
+ }
76
+ }
77
+ function reportAgentInputLimit(cwd) {
78
+ const envPath = join(cwd, ".env.local");
79
+ const env = {
80
+ ...(existsSync(envPath) ? parseEnv(readFileSync(envPath, "utf8")) : {}),
81
+ ...process.env,
82
+ };
83
+ const inspection = inspectEveAgent(cwd, env);
84
+ const limit = inspection?.limit ?? {
85
+ kind: "unresolved",
86
+ reason: "eve info --json failed",
87
+ };
88
+ const marker = limit.kind === "inherited" || limit.kind === "unresolved" ? yellow("!") : green("✓");
89
+ console.log(` ${marker} ${agentInputLimitUpgradeMessage(limit)}`);
90
+ }
64
91
  /**
65
92
  * Raise the local queue's delivery timeouts on an agent that already exists.
66
93
  *
@@ -309,6 +336,8 @@ export async function upgrade(skipEval) {
309
336
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
310
337
  console.log(bold("\nkyb upgrade — checking @kybernesis/* and eve against npm\n"));
311
338
  warnIfStale();
339
+ // Read-only: report the compiled effective policy, never rewrite agent/agent.ts.
340
+ reportAgentInputLimit(cwd);
312
341
  // Env-only, so it is safe before anything is installed.
313
342
  repairLocalQueueTimeouts(cwd, deps);
314
343
  const toUpgrade = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kybernesis/create",
3
- "version": "0.14.1",
3
+ "version": "0.14.3",
4
4
  "description": "The Kybernesis agent scaffolder and FDE toolkit: one command to a governed, remembering, multiplayer, self-testing eve agent — plus doctor and upgrade.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",