nylorun 0.18.2-beta → 0.19.0-beta

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/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # nylorun
2
2
 
3
+ ## 0.19.0-beta
4
+
5
+ ### Minor Changes
6
+
7
+ - aaa079f: Agent folders in the SDK and the CLI (track T6). `@nylorun/agents` adds `packFolder(dir)`, which lists a folder's files with their sizes and hashes (skipping `.git` and `node_modules`, following no symbolic link), `checkFolder`, which compiles it offline with the Runtime's compiler, and `client.agentManifests.saveFolder` and `planFolder`, which upload the files the Runtime lacks and send the folder by hash; `source` and `sourceFile` read a version's folder back. `nylorun init <name>` writes a new folder's `AGENT.md`, `nylorun check [dir…]` prints every problem by file, line and column with no Runtime, and `nylorun apply` saves a folder with `AGENT.md` (`--dry-run` prints the compiled manifest). A folder that does not compile sends nothing.
8
+
9
+ ### Patch Changes
10
+
11
+ - Pin core to the tested release.
12
+ - Pin agents to the tested release.
13
+ - Pin admin to the tested release.
14
+ - Pin runtime to the tested release.
15
+ - Pin studio to the tested release.
16
+ - Updated dependencies [9ba6634]
17
+ - Updated dependencies [aaa079f]
18
+ - Updated dependencies [aaa079f]
19
+ - Updated dependencies
20
+ - Updated dependencies
21
+ - @nylorun/core@0.27.0-beta
22
+ - @nylorun/agents@0.27.0-beta
23
+ - @nylorun/admin@0.19.3-beta
24
+
25
+ ## 0.18.3-beta
26
+
27
+ ### Patch Changes
28
+
29
+ - Pin core to the tested release.
30
+ - Pin agents to the tested release.
31
+ - Pin admin to the tested release.
32
+ - Pin runtime to the tested release.
33
+ - Pin studio to the tested release.
34
+ - Updated dependencies [fce0c9b]
35
+ - Updated dependencies [ab7c08a]
36
+ - Updated dependencies [4a563e7]
37
+ - Updated dependencies
38
+ - Updated dependencies
39
+ - @nylorun/core@0.26.0-beta
40
+ - @nylorun/agents@0.26.0-beta
41
+ - @nylorun/admin@0.19.2-beta
42
+
3
43
  ## 0.18.2-beta
4
44
 
5
45
  ### Patch Changes
package/README.md CHANGED
@@ -53,7 +53,9 @@ nylorun doctor sandbox [--tenant <name>] [--json] # the sandbox backend the Te
53
53
 
54
54
  # apply, agent-manifests, agents, sessions, artifacts: the Runtime API (an application key, or NYLORUN_TOKEN)
55
55
  # every one takes --tenant <name>, and --as <subject> [--scope <scope>]... to act for a person
56
- nylorun apply [<dir>...] [--dry-run] [--if-match <n>] # plan, then save each folder's agent.manifest.json, its files uploaded
56
+ nylorun init <name> [--description <text>] # write <name>/AGENT.md, a new agent folder
57
+ nylorun check [<dir>...] # compile each agent folder offline: every problem by file, line and column
58
+ nylorun apply [<dir>...] [--dry-run] [--if-match <n>] # plan, then save each folder (AGENT.md or agent.manifest.json), its files uploaded
57
59
  nylorun migrate v8 <agent.json> [--runtime <environment-manifest.json>] [--name <name>] [--out <dir>] # the three files from a v7 manifest; reads no Runtime
58
60
  nylorun agent-manifests list [--json] # name, latest version, runtime kind, harness, Agents
59
61
  nylorun agent-manifests get <name>[@<n>] | versions <name> | rm <name>
@@ -476,7 +478,25 @@ from a terminal.
476
478
  An agent runs from a saved **agent manifest** (protocol 16): one named, versioned
477
479
  document with a harness section (instructions, tools, skills, MCP servers,
478
480
  plugins) and a runtime section (kind, image, network, setup, files, secret
479
- slots). Author it as files, one folder per agent:
481
+ slots). Author it as an agent folder (track T6), one folder per agent:
482
+
483
+ ```
484
+ agents/support/AGENT.md # frontmatter (name, description), then the instructions
485
+ agents/support/mcp.json # MCP servers; "Authorization": "Bearer ${GITHUB_TOKEN}" names a slot
486
+ agents/support/runtime.json # kind, network, secrets (optional)
487
+ agents/support/skills/<skill>/SKILL.md, tools/<tool>.json, triggers/<id>.md, files/**
488
+ ```
489
+
490
+ `nylorun init support` writes the `AGENT.md`; `nylorun check agents/support`
491
+ compiles the folder offline with the Runtime's compiler and prints every problem
492
+ as `file:line:column severity: message`, exiting 1 when it does not compile.
493
+ `nylorun apply agents/support` checks it the same way (sending nothing when it
494
+ does not compile), plans it, prints the compiler's notes (such as a slot derived
495
+ from `mcp.json`) and saves it, uploading its files by hash; the Runtime keeps the
496
+ folder as the version's source. `--dry-run` also prints the compiled manifest. A
497
+ change only to a file outside the agent, such as a README, is `unchanged`.
498
+
499
+ Or as three files:
480
500
 
481
501
  ```
482
502
  agents/triage/agent.manifest.json # { "manifestSchemaVersion": 8, "name": "triage",
package/dist/cli.js CHANGED
@@ -13,9 +13,12 @@ ${stackUsage}
13
13
  the sandbox backend the Tenant's Host offers
14
14
 
15
15
  Agent manifests:
16
+ init <name> [--description <text>] write <name>/AGENT.md, a new agent folder
17
+ check [<dir>...] compile each agent folder offline and print every problem
18
+ by file, line and column (reads no Runtime)
16
19
  apply [<dir>...] [--dry-run] [--if-match <n>]
17
- save the agent manifest each folder's agent.manifest.json
18
- describes, with the files it names (Runtime API)
20
+ save the agent manifest each folder describes (its AGENT.md,
21
+ or its agent.manifest.json), with its files (Runtime API)
19
22
  migrate v8 <agent.json> [--runtime <environment-manifest.json>] [--name <name>] [--out <dir>]
20
23
  write the three manifest files from an agent manifest v7
21
24
  and an environment manifest v1 (reads no Runtime)
@@ -164,6 +167,8 @@ async function main() {
164
167
  return;
165
168
  }
166
169
  if (command === "apply" ||
170
+ command === "check" ||
171
+ command === "init" ||
167
172
  command === "migrate" ||
168
173
  command === "agent-manifests" ||
169
174
  command === "agents" ||
@@ -171,6 +176,8 @@ async function main() {
171
176
  command === "artifacts") {
172
177
  const group = {
173
178
  apply: async () => (await import("./client/apply.js")).applyCommand,
179
+ check: async () => (await import("./client/folder.js")).checkCommand,
180
+ init: async () => (await import("./client/folder.js")).initCommand,
174
181
  migrate: async () => (await import("./client/migrate.js")).migrateCommand,
175
182
  "agent-manifests": async () => (await import("./client/agent-manifests.js")).agentManifestsCommand,
176
183
  agents: async () => (await import("./client/agents.js")).agentsCommand,
@@ -7,24 +7,33 @@
7
7
  * version the plan was made against, so a save that raced another fails (`412 manifest_changed`)
8
8
  * instead of overwriting it. A folder without an `agent.manifest.json` applies each subfolder
9
9
  * that has one (`nylorun apply agents/`).
10
+ *
11
+ * An agent folder (a folder with `AGENT.md`, track T6) is compiled offline first, and one that does
12
+ * not compile sends nothing; otherwise its files go up by hash and the Runtime compiles it again
13
+ * and keeps it as the version's source. Its plan adds the compiler's notes, and `--dry-run` prints
14
+ * the compiled manifest.
10
15
  */
11
16
  import { resolve } from "node:path";
12
- import { ComposeError, PluginError, SkillsError, composeManifest, manifestFolders, } from "@nylorun/agents";
17
+ import { ComposeError, PluginError, SkillsError, checkFolder, composeManifest, isAgentFolder, manifestFolders, } from "@nylorun/agents";
13
18
  import { CliError } from "../errors.js";
19
+ import { agentFolders, diagnosticLines, packed } from "./folder.js";
14
20
  import { integerFlag, parseFlags, runtimeCall, runtimeClient, runtimeFlagsUsage, usageError } from "./runtime.js";
15
21
  export const applyUsage = `nylorun apply [<dir>...] [--dry-run] [--if-match <n>]
16
22
 
17
23
  Saves the agent manifest each folder describes (default .), through the Runtime API with an
18
- application key (or NYLORUN_TOKEN). A folder holds agent.manifest.json, which names its
19
- harness.manifest.json and runtime.manifest.json (paths relative to the file that names them);
20
- a folder without one applies each subfolder that has one.
24
+ application key (or NYLORUN_TOKEN). An agent folder holds AGENT.md (nylorun init): it is checked
25
+ offline as nylorun check does, sent by hash, and kept as the version's source. A folder may hold
26
+ agent.manifest.json instead, which names its harness.manifest.json and runtime.manifest.json
27
+ (paths relative to the file that names them). A folder with neither applies each subfolder that
28
+ has one.
21
29
 
22
30
  Each manifest is planned first: the version it moves from and to, each change (section, path,
23
31
  op), warnings and errors. With no errors it is saved, its skills', plugins' and runtime files
24
32
  uploaded first, under If-Match set to the version planned against (0 for a new manifest).
25
33
  Errors, the gaps between its sections included, save nothing and exit 1.
26
34
 
27
- --dry-run print the plans and save nothing
35
+ --dry-run print the plans, and an agent folder's compiled manifest, and
36
+ save nothing
28
37
  --if-match <n> save only over version n (one folder)
29
38
 
30
39
  ${runtimeFlagsUsage}`;
@@ -48,6 +57,13 @@ export function planLines(plan) {
48
57
  ...plan.errors.map((error) => ` error: ${error}`),
49
58
  ];
50
59
  }
60
+ function reportSaved(deps, saved, label) {
61
+ deps.err(saved.unchanged
62
+ ? `Agent manifest ${saved.manifest.name} is unchanged at version ${saved.manifest.version} on ${label}.`
63
+ : `Saved agent manifest ${saved.manifest.name} version ${saved.manifest.version} on ${label}.`);
64
+ for (const warning of saved.warnings)
65
+ deps.err(` warning: ${warning.message}`);
66
+ }
51
67
  /** `nylorun apply [dir…] [--dry-run] [--if-match <n>]` */
52
68
  export async function applyCommand(deps, args) {
53
69
  if (args[0] === "--help" || args[0] === "-h") {
@@ -60,38 +76,79 @@ export async function applyCommand(deps, args) {
60
76
  const cwd = deps.cwd ?? process.cwd();
61
77
  const folders = [];
62
78
  for (const dir of flags.rest.length > 0 ? flags.rest : ["."]) {
63
- const found = await manifestFolders(resolve(cwd, dir)).catch((error) => composeFailure(dir, error));
79
+ const at = resolve(cwd, dir);
80
+ const found = (await isAgentFolder(at))
81
+ ? [at]
82
+ : [
83
+ ...new Set([
84
+ ...(await manifestFolders(at).catch((error) => composeFailure(dir, error))),
85
+ ...(await agentFolders(at)),
86
+ ]),
87
+ ].sort();
64
88
  if (found.length === 0)
65
- throw usageError(`${dir} holds no agent.manifest.json, and no subfolder does.`);
89
+ throw usageError(`${dir} holds no AGENT.md or agent.manifest.json, and no subfolder does.`);
66
90
  folders.push(...found);
67
91
  }
68
92
  if (ifMatch !== undefined && folders.length > 1)
69
93
  throw usageError("--if-match takes one manifest: name one folder.");
70
- // Every folder composes before anything is sent: a broken file stops the whole apply.
71
- const composed = [];
72
- for (const folder of folders)
73
- composed.push({ folder, manifest: await composeManifest(folder).catch((error) => composeFailure(folder, error)) });
74
- const { client, connection } = await runtimeClient(deps, flags);
94
+ // Every folder composes, or compiles, before anything is sent: a broken file stops the whole apply.
95
+ const work = [];
75
96
  let blocked = false;
76
- for (const { manifest } of composed) {
97
+ for (const folder of folders) {
98
+ if (await isAgentFolder(folder)) {
99
+ const pack = await packed(folder);
100
+ const compiled = await checkFolder(pack);
101
+ if (!compiled.manifest) {
102
+ for (const line of diagnosticLines(folder, cwd, compiled.diagnostics))
103
+ deps.err(line);
104
+ blocked = true;
105
+ }
106
+ work.push({ folder, pack });
107
+ }
108
+ else
109
+ work.push({ folder, manifest: await composeManifest(folder).catch((error) => composeFailure(folder, error)) });
110
+ }
111
+ if (blocked) {
112
+ deps.err("Nothing was sent: an agent folder does not compile (nylorun check).");
113
+ return 1;
114
+ }
115
+ const { client, connection } = await runtimeClient(deps, flags);
116
+ const dryRun = flags.booleans.has("--dry-run");
117
+ for (const item of work) {
118
+ const options = ifMatch === undefined ? {} : { ifMatch };
119
+ if ("pack" in item) {
120
+ const plan = await runtimeCall(() => client.agentManifests.planFolder(item.pack, options));
121
+ for (const line of planLines(plan))
122
+ deps.out(line);
123
+ for (const line of diagnosticLines(item.folder, cwd, (plan.diagnostics ?? []).filter((d) => d.severity !== "error")))
124
+ deps.out(` ${line}`);
125
+ if (dryRun && plan.manifest)
126
+ deps.out(JSON.stringify(plan.manifest, null, 2));
127
+ if (plan.errors.length > 0) {
128
+ blocked = true;
129
+ continue;
130
+ }
131
+ if (dryRun || plan.unchanged)
132
+ continue;
133
+ const saved = await runtimeCall(() => client.agentManifests.saveFolder(item.pack, { ifMatch: ifMatch ?? plan.from ?? 0 }));
134
+ reportSaved(deps, saved, connection.label);
135
+ continue;
136
+ }
137
+ const { manifest } = item;
77
138
  for (const warning of manifest.warnings)
78
139
  deps.err(`${manifest.manifest.name}: warning: ${warning}`);
79
140
  // The plan's errors include the gaps the composer found (its sections, the harness's requires).
80
- const plan = await runtimeCall(() => client.agentManifests.plan(manifest, ifMatch === undefined ? {} : { ifMatch }));
141
+ const plan = await runtimeCall(() => client.agentManifests.plan(manifest, options));
81
142
  for (const line of planLines(plan))
82
143
  deps.out(line);
83
144
  if (plan.errors.length > 0) {
84
145
  blocked = true;
85
146
  continue;
86
147
  }
87
- if (flags.booleans.has("--dry-run") || plan.unchanged)
148
+ if (dryRun || plan.unchanged)
88
149
  continue;
89
150
  const saved = await runtimeCall(() => client.agentManifests.save(manifest, { ifMatch: ifMatch ?? plan.from ?? 0 }));
90
- deps.err(saved.unchanged
91
- ? `Agent manifest ${saved.manifest.name} is unchanged at version ${saved.manifest.version} on ${connection.label}.`
92
- : `Saved agent manifest ${saved.manifest.name} version ${saved.manifest.version} on ${connection.label}.`);
93
- for (const warning of saved.warnings)
94
- deps.err(` warning: ${warning.message}`);
151
+ reportSaved(deps, saved, connection.label);
95
152
  }
96
153
  if (blocked)
97
154
  deps.err("Nothing was saved for a manifest with errors.");
@@ -0,0 +1,15 @@
1
+ import { type PackedFolder } from "@nylorun/agents";
2
+ import type { AgentFolderDiagnostic } from "@nylorun/core/folder";
3
+ import { type StackDeps } from "../stack/commands.js";
4
+ export declare const checkUsage = "nylorun check [<dir>...]\n\nCompiles each agent folder (default .) offline, with the compiler the Runtime runs, and prints\nevery problem by file, line and column. A folder holds AGENT.md; a folder without one checks each\nsubfolder that has one. Exits 1 when a folder does not compile. Reads no Runtime.";
5
+ export declare const initUsage = "nylorun init <name> [--description <text>]\n\nWrites <name>/AGENT.md, the smallest agent folder: frontmatter with its name and description, and\nits instructions below. Save it with nylorun apply <name>.";
6
+ /** The agent folders under `dir`: `dir` when it holds an `AGENT.md`, else each subfolder that does. */
7
+ export declare function agentFolders(dir: string): Promise<string[]>;
8
+ /** Packs the folder, or a `CliError` naming why it cannot be. */
9
+ export declare function packed(folder: string): Promise<PackedFolder>;
10
+ /** A folder's diagnostics as lines, each path relative to `cwd`. */
11
+ export declare function diagnosticLines(folder: string, cwd: string, diagnostics: readonly AgentFolderDiagnostic[]): string[];
12
+ /** `nylorun check [dir…]` */
13
+ export declare function checkCommand(deps: StackDeps, args: readonly string[]): Promise<number>;
14
+ /** `nylorun init <name> [--description <text>]` */
15
+ export declare function initCommand(deps: StackDeps, args: readonly string[]): Promise<number>;
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Agent folders (track T6, D79) on the command line, offline: `nylorun check [dir…]` compiles each
3
+ * agent folder with the compiler the Runtime runs (`@nylorun/core/folder`) and prints every
4
+ * diagnostic by file, line and column; `nylorun init <name>` writes a new folder's `AGENT.md`.
5
+ * Neither reads a Runtime. `nylorun apply` saves a folder (`apply.ts`).
6
+ */
7
+ import { existsSync } from "node:fs";
8
+ import { mkdir, readdir, writeFile } from "node:fs/promises";
9
+ import { basename, join, relative, resolve } from "node:path";
10
+ import { isAgentManifestName } from "@nylorun/core/contracts";
11
+ import { AGENT_FOLDER_FILE, FolderError, checkFolder, diagnosticLine, isAgentFolder, packFolder, } from "@nylorun/agents";
12
+ import { CliError } from "../errors.js";
13
+ import { parseStackFlags } from "../stack/commands.js";
14
+ import { usageError } from "./runtime.js";
15
+ export const checkUsage = `nylorun check [<dir>...]
16
+
17
+ Compiles each agent folder (default .) offline, with the compiler the Runtime runs, and prints
18
+ every problem by file, line and column. A folder holds AGENT.md; a folder without one checks each
19
+ subfolder that has one. Exits 1 when a folder does not compile. Reads no Runtime.`;
20
+ export const initUsage = `nylorun init <name> [--description <text>]
21
+
22
+ Writes <name>/AGENT.md, the smallest agent folder: frontmatter with its name and description, and
23
+ its instructions below. Save it with nylorun apply <name>.`;
24
+ /** The agent folders under `dir`: `dir` when it holds an `AGENT.md`, else each subfolder that does. */
25
+ export async function agentFolders(dir) {
26
+ const root = resolve(dir);
27
+ if (await isAgentFolder(root))
28
+ return [root];
29
+ const entries = await readdir(root, { withFileTypes: true }).catch(() => []);
30
+ const found = [];
31
+ for (const entry of entries.filter((item) => item.isDirectory()).sort((a, b) => (a.name < b.name ? -1 : 1)))
32
+ if (await isAgentFolder(join(root, entry.name)))
33
+ found.push(join(root, entry.name));
34
+ return found;
35
+ }
36
+ /** Packs the folder, or a `CliError` naming why it cannot be. */
37
+ export async function packed(folder) {
38
+ return packFolder(folder).catch((error) => {
39
+ if (error instanceof FolderError)
40
+ throw new CliError(error.message, 1);
41
+ throw error;
42
+ });
43
+ }
44
+ /** A folder's diagnostics as lines, each path relative to `cwd`. */
45
+ export function diagnosticLines(folder, cwd, diagnostics) {
46
+ const prefix = relative(cwd, folder);
47
+ return diagnostics.map((item) => diagnosticLine(item.file && prefix ? { ...item, file: `${prefix}/${item.file}` } : item));
48
+ }
49
+ /** `nylorun check [dir…]` */
50
+ export async function checkCommand(deps, args) {
51
+ const flags = parseStackFlags(args, {}, checkUsage);
52
+ const cwd = deps.cwd ?? process.cwd();
53
+ const folders = [];
54
+ for (const dir of flags.rest.length > 0 ? flags.rest : ["."]) {
55
+ const found = await agentFolders(resolve(cwd, dir));
56
+ if (found.length === 0)
57
+ throw usageError(`${dir} holds no ${AGENT_FOLDER_FILE}, and no subfolder does.`);
58
+ folders.push(...found);
59
+ }
60
+ let failed = false;
61
+ for (const folder of folders) {
62
+ const pack = await packed(folder);
63
+ const compiled = await checkFolder(pack);
64
+ for (const skipped of pack.skipped)
65
+ deps.err(`${relative(cwd, folder) || "."}: skipped ${skipped}`);
66
+ for (const line of diagnosticLines(folder, cwd, compiled.diagnostics))
67
+ if (compiled.manifest)
68
+ deps.out(line);
69
+ else
70
+ deps.err(line);
71
+ const label = relative(cwd, folder) || basename(folder);
72
+ if (compiled.manifest)
73
+ deps.out(`${label}: ok, agent manifest ${compiled.manifest.name} (${pack.entries.length} files, ${compiled.compiler})`);
74
+ else {
75
+ failed = true;
76
+ const errors = compiled.diagnostics.filter((item) => item.severity === "error").length;
77
+ deps.err(`${label}: ${errors} ${errors === 1 ? "error" : "errors"}`);
78
+ }
79
+ }
80
+ return failed ? 1 : 0;
81
+ }
82
+ /** `nylorun init <name> [--description <text>]` */
83
+ export async function initCommand(deps, args) {
84
+ const flags = parseStackFlags(args, { values: ["--description"] }, initUsage);
85
+ const [name, ...rest] = flags.rest;
86
+ if (name === undefined || rest.length > 0)
87
+ throw usageError(`Usage: ${initUsage.split("\n")[0]}`);
88
+ if (!isAgentManifestName(name))
89
+ throw usageError(`${name} is not an agent's name: 1-64 letters, digits, '.', '_' and '-', starting with a letter or digit.`);
90
+ const dir = resolve(deps.cwd ?? process.cwd(), name);
91
+ const file = join(dir, AGENT_FOLDER_FILE);
92
+ if (existsSync(file))
93
+ throw new CliError(`${relative(deps.cwd ?? process.cwd(), file)} exists already.`, 1);
94
+ const description = flags.values.get("--description") ?? `Describe what ${name} does in one sentence.`;
95
+ await mkdir(dir, { recursive: true });
96
+ await writeFile(file, `---\nname: ${name}\ndescription: ${JSON.stringify(description)}\n---\nYou are ${name}. Write the agent's instructions here, in Markdown.\n`, { flag: "wx" });
97
+ deps.out(`Wrote ${relative(deps.cwd ?? process.cwd(), file)}. Check it with nylorun check ${name}, and save it with nylorun apply ${name}.`);
98
+ return 0;
99
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nylorun",
3
- "version": "0.18.2-beta",
3
+ "version": "0.19.0-beta",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "description": "Set up and run local Nylorun Tenants (Runtime and Studio) with Docker Compose, and act on a Tenant through its Runtime and Management APIs",
@@ -30,9 +30,9 @@
30
30
  "prepack": "npm run build"
31
31
  },
32
32
  "dependencies": {
33
- "@nylorun/admin": "0.19.1-beta",
34
- "@nylorun/agents": "0.25.0-beta",
35
- "@nylorun/core": "0.25.0-beta"
33
+ "@nylorun/admin": "0.19.3-beta",
34
+ "@nylorun/agents": "0.27.0-beta",
35
+ "@nylorun/core": "0.27.0-beta"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@earendil-works/pi-ai": "1.2.0",
@@ -46,7 +46,7 @@
46
46
  "nylo": "dist/nylo.js"
47
47
  },
48
48
  "nylorun": {
49
- "runtime": "0.29.0-beta",
50
- "studio": "0.30.0-beta"
49
+ "runtime": "0.31.0-beta",
50
+ "studio": "0.31.1-beta"
51
51
  }
52
52
  }