nylorun 0.24.0-beta → 0.25.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 +63 -0
- package/README.md +41 -2
- package/dist/cli.js +6 -2
- package/dist/client/agents.js +17 -8
- package/dist/client/folder.d.ts +4 -1
- package/dist/client/folder.js +31 -4
- package/dist/client/init.js +1 -1
- package/dist/client/migrate-format.d.ts +12 -0
- package/dist/client/migrate-format.js +141 -0
- package/dist/client/migrate.js +18 -3
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,68 @@
|
|
|
1
1
|
# nylorun
|
|
2
2
|
|
|
3
|
+
## 0.25.0-beta
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 67ed2ea: `nylorun check --explain` prints each engine feature the agent and its subagents get, on or off,
|
|
8
|
+
with what declares it and the tools and text blocks it adds (`explain` in `--json`). `nylorun
|
|
9
|
+
migrate folder <dir> --to 2` makes an agent folder `agent-folder/2` in place, writing the
|
|
10
|
+
`engine:` declarations that keep what it gets today, after checking that no agent's system text
|
|
11
|
+
or tool list changes (ADR-100).
|
|
12
|
+
- 67ed2ea: Secret routing: the agent folder names, the installation routes (Host feature `secret-routing`, no
|
|
13
|
+
protocol change). A `bearer` or `headers` secret slot (`runtime.json`, `runtime.secrets`) takes the
|
|
14
|
+
gateway's `toolPrefix`, with or without `via`, and a slot's value write
|
|
15
|
+
(`PUT /v1/agents/{id}/secrets/{name}` with `{ value }` or `{ headers }`) takes `via`, with the same
|
|
16
|
+
URL rules as a slot's. At each request the secret's `via` wins, else the slot's in the version the
|
|
17
|
+
Agent runs, else none (the slot's `url`); the slot's `toolPrefix` wins over the secret's and
|
|
18
|
+
applies only with a `via`; the slot's `url` still picks the credential, for MCP servers and HTTP
|
|
19
|
+
tools alike, and an HTTP tool whose URL has `{field}` parameters still fails behind a `via`. A
|
|
20
|
+
write whose `toolPrefix` differs from its slot's is accepted, with a warning in the log. A `via` a
|
|
21
|
+
write copied from its slot follows the slot when the Agent moves, and a fork keeps a secret's own
|
|
22
|
+
`via`. Secret views show the `via` and `toolPrefix` in force, never a value.
|
|
23
|
+
`client.agents.secrets.put` takes `{ value | headers, via?, toolPrefix? }` (`AgentSecretRouting`),
|
|
24
|
+
and `nylorun agents secrets put` takes `--via` and `--tool-prefix`, and a slot's JSON without a
|
|
25
|
+
`type`. The JSON Schemas that gained the slot's `toolPrefix` are published as
|
|
26
|
+
`agent-manifest/v8.2`, `runtime-manifest/v1.1` and `agent-folder/v1/runtime.1`, which `nylorun init`
|
|
27
|
+
maps; the old `$schema` URLs are still read.
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- Pin core to the tested release.
|
|
32
|
+
- Pin agents to the tested release.
|
|
33
|
+
- Pin admin to the tested release.
|
|
34
|
+
- Pin runtime to the tested release.
|
|
35
|
+
- Pin studio to the tested release.
|
|
36
|
+
- Updated dependencies [67ed2ea]
|
|
37
|
+
- Updated dependencies [67ed2ea]
|
|
38
|
+
- Updated dependencies [67ed2ea]
|
|
39
|
+
- Updated dependencies [67ed2ea]
|
|
40
|
+
- Updated dependencies [67ed2ea]
|
|
41
|
+
- Updated dependencies [67ed2ea]
|
|
42
|
+
- Updated dependencies [67ed2ea]
|
|
43
|
+
- Updated dependencies [67ed2ea]
|
|
44
|
+
- Updated dependencies
|
|
45
|
+
- Updated dependencies
|
|
46
|
+
- @nylorun/core@0.34.0-beta
|
|
47
|
+
- @nylorun/agents@0.32.0-beta
|
|
48
|
+
- @nylorun/admin@0.19.10-beta
|
|
49
|
+
|
|
50
|
+
## 0.24.1-beta
|
|
51
|
+
|
|
52
|
+
### Patch Changes
|
|
53
|
+
|
|
54
|
+
- Pin core to the tested release.
|
|
55
|
+
- Pin agents to the tested release.
|
|
56
|
+
- Pin admin to the tested release.
|
|
57
|
+
- Pin runtime to the tested release.
|
|
58
|
+
- Pin studio to the tested release.
|
|
59
|
+
- Updated dependencies [63a6217]
|
|
60
|
+
- Updated dependencies
|
|
61
|
+
- Updated dependencies
|
|
62
|
+
- @nylorun/core@0.33.0-beta
|
|
63
|
+
- @nylorun/agents@0.31.1-beta
|
|
64
|
+
- @nylorun/admin@0.19.9-beta
|
|
65
|
+
|
|
3
66
|
## 0.24.0-beta
|
|
4
67
|
|
|
5
68
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -54,11 +54,12 @@ nylorun doctor sandbox [--tenant <name>] [--json] # the sandbox backend the Te
|
|
|
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
56
|
nylorun init <path> [--description <text>] [--example] # write <path>/AGENT.md, a new agent folder named after the last segment
|
|
57
|
-
nylorun check [<dir>...] [--fix] [--json] # compile each agent folder offline: every problem by file, line and column; --fix makes the fixable ones' edits
|
|
57
|
+
nylorun check [<dir>...] [--fix] [--explain] [--json] # compile each agent folder offline: every problem by file, line and column; --fix makes the fixable ones' edits; --explain prints each engine feature on, and what it adds
|
|
58
58
|
nylorun apply [<dir>...] [--dry-run] [--if-match <n>] [--agent [<id>]] [--watch [--save]] # plan, then save each folder (AGENT.md or agent.manifest.json), its files uploaded
|
|
59
59
|
nylorun chat <agent> [--session <id>] [--message <text>] # send each line (or --message), print each reply
|
|
60
60
|
nylorun migrate v8 <agent.json> [--runtime <environment-manifest.json>] [--name <name>] [--out <dir>] # the three files from a v7 manifest; reads no Runtime
|
|
61
61
|
nylorun migrate folder <dir> [--out <dir>] # the agent folder of a three-file folder; reads no Runtime
|
|
62
|
+
nylorun migrate folder <dir> --to 2 # make an agent folder agent-folder/2, declaring what it gets today
|
|
62
63
|
nylorun export --folder <name>[@<n>] [--out <dir>] # the agent folder of a saved manifest (JSON or TypeScript)
|
|
63
64
|
nylorun pull <name>[@<n>] [--out <dir>] # the folder a version was saved from, byte for byte
|
|
64
65
|
nylorun agent-manifests list [--json] # name, latest version, runtime kind, harness, Agents
|
|
@@ -288,7 +289,11 @@ secret of that Agent's vault, a `bearer` token or a `headers` map, with `via` an
|
|
|
288
289
|
an identity header when an MCP gateway holds each person's sign-in. Write it with
|
|
289
290
|
`nylorun agents secrets put <id> <name>` (a slot's value on standard input) or
|
|
290
291
|
`nylorun agents secrets put <id> <name> <secret.json|->` (the secret's JSON
|
|
291
|
-
comes from a file or standard input, never the command line
|
|
292
|
+
comes from a file or standard input, never the command line; for a slot, only
|
|
293
|
+
`{ "value": … }` or `{ "headers": { … } }`), with `--via <url>` and
|
|
294
|
+
`--tool-prefix <prefix>` for this installation's routing of a `bearer` or
|
|
295
|
+
`headers` secret (Host feature `secret-routing`: the write's `via` wins over the
|
|
296
|
+
slot's, the slot's tool prefix over the write's),
|
|
292
297
|
`client.agents.secrets.put(id, name, secret)` of `@nylorun/agents` (the
|
|
293
298
|
Runtime API, an application key) or in Studio.
|
|
294
299
|
The whole flow, with gateway recipes, is in
|
|
@@ -564,6 +569,40 @@ from `mcp.json`) and saves it, uploading its files by hash; the Runtime keeps th
|
|
|
564
569
|
folder as the version's source. `--dry-run` also prints the compiled manifest. A
|
|
565
570
|
change only to a file outside the agent, such as a README, is `unchanged`.
|
|
566
571
|
|
|
572
|
+
**What the engine adds (`agent-folder/2`, ADR-100).** An `agent-folder/1` folder (no `format`,
|
|
573
|
+
the default) gets every engine feature by default: with a computer in `runtime.json`, the six
|
|
574
|
+
sandbox tools, `save_artifact` and the outputs folder; always `read_artifact`; and its MCP tools
|
|
575
|
+
deferred behind `tool_search` past a share of the context window. A folder with
|
|
576
|
+
`format: agent-folder/2` gets only what it declares. Memory, skills, `loop` and
|
|
577
|
+
`runtime.selfWake` declare their parts as before; `engine:` declares the rest:
|
|
578
|
+
|
|
579
|
+
<!-- doc-test: folder declared AGENT.md -->
|
|
580
|
+
```markdown
|
|
581
|
+
---
|
|
582
|
+
name: declared
|
|
583
|
+
description: Answers from the docs it is given.
|
|
584
|
+
format: agent-folder/2
|
|
585
|
+
memory:
|
|
586
|
+
index: false
|
|
587
|
+
engine:
|
|
588
|
+
artifacts: { read: true }
|
|
589
|
+
toolSearch: false
|
|
590
|
+
---
|
|
591
|
+
Answer from the documents in the conversation.
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
`engine.sandbox` lists any of `bash`, `read`, `write`, `edit`, `grep` and `glob`;
|
|
595
|
+
`artifacts.save`, `outputs` and `sandbox` need a computer; and `engine.instructions.<block>`
|
|
596
|
+
replaces a text block's wording with a Markdown file under `engine/` (the blocks are `skills`,
|
|
597
|
+
`sandbox`, `skill_folder`, `artifacts_save`, `outputs`, `artifacts_read`, `wake`, `memory`,
|
|
598
|
+
`plan`, `connection` and `tool_search`; memory's rule that outside text is data stays). A
|
|
599
|
+
subagent declares its own `engine:`, a subset of its parent's. The compiled manifest lists the
|
|
600
|
+
blocks the agent gets, with their versions and hashes (`harness.engine.blocks`).
|
|
601
|
+
`nylorun check --explain` prints each feature on, and the tools and blocks it adds;
|
|
602
|
+
`previewTurn(manifest)` in `@nylorun/agents` returns the exact system text and tool list.
|
|
603
|
+
`nylorun migrate folder <dir> --to 2` writes the declarations that keep what an `agent-folder/1`
|
|
604
|
+
folder gets, so nothing changes until you remove a line.
|
|
605
|
+
|
|
567
606
|
The loop from an empty folder to an answer:
|
|
568
607
|
|
|
569
608
|
```
|
package/dist/cli.js
CHANGED
|
@@ -17,9 +17,11 @@ Agent manifests:
|
|
|
17
17
|
write <path>/AGENT.md, a new agent folder named after the
|
|
18
18
|
path's last segment; --example adds a tool, mcp.json and a
|
|
19
19
|
trigger
|
|
20
|
-
check [<dir>...] [--fix] [--json]
|
|
20
|
+
check [<dir>...] [--fix] [--explain] [--json]
|
|
21
|
+
compile each agent folder offline and print every problem
|
|
21
22
|
by file, line and column (reads no Runtime); --fix makes
|
|
22
|
-
the edits the fixable ones name
|
|
23
|
+
the edits the fixable ones name; --explain prints each
|
|
24
|
+
engine feature on, and the tools and text it adds
|
|
23
25
|
apply [<dir>...] [--dry-run] [--if-match <n>] [--agent [<id>]] [--watch [--save]]
|
|
24
26
|
save the agent manifest each folder describes (its AGENT.md,
|
|
25
27
|
or its agent.manifest.json), with its files (Runtime API);
|
|
@@ -32,6 +34,8 @@ Agent manifests:
|
|
|
32
34
|
and an environment manifest v1 (reads no Runtime)
|
|
33
35
|
migrate folder <dir> [--out <dir>] write the agent folder of a three-file folder
|
|
34
36
|
(agent.manifest.json; reads no Runtime)
|
|
37
|
+
migrate folder <dir> --to 2 make an agent folder agent-folder/2, declaring every
|
|
38
|
+
engine feature it gets today, so nothing it gets changes
|
|
35
39
|
export --folder <name>[@<version>] [--out <dir>]
|
|
36
40
|
write the agent folder of a saved manifest (Runtime API)
|
|
37
41
|
pull <name>[@<version>] [--out <dir>]
|
package/dist/client/agents.js
CHANGED
|
@@ -45,8 +45,12 @@ NYLORUN_TOKEN).
|
|
|
45
45
|
secrets put <id> <name> the value of a slot its manifest declares, piped in
|
|
46
46
|
on standard input
|
|
47
47
|
secrets put <id> <name> <secret.json|->
|
|
48
|
-
create or replace a secret: {"type": "bearer", ...},
|
|
49
|
-
|
|
48
|
+
create or replace a secret: {"type": "bearer", ...}, or a
|
|
49
|
+
slot's {"headers": {...}} or {"value": ...}, read from a file
|
|
50
|
+
or standard input
|
|
51
|
+
[--via <url>] [--tool-prefix <prefix>]
|
|
52
|
+
a bearer or headers secret's routing: where its requests
|
|
53
|
+
go (over the slot's via) and the gateway's tool prefix
|
|
50
54
|
secrets rm <id> <name>
|
|
51
55
|
triggers list <id> its triggers: status, next and last run, as JSON
|
|
52
56
|
triggers pause|resume <id> <trigger>
|
|
@@ -313,23 +317,28 @@ async function secrets(deps, args) {
|
|
|
313
317
|
return 0;
|
|
314
318
|
}
|
|
315
319
|
if (verb === "put") {
|
|
316
|
-
const usage = "nylorun agents secrets put <id> <name> [<secret.json|->]";
|
|
317
|
-
const flags = parseFlags(rest, {}, usage);
|
|
320
|
+
const usage = "nylorun agents secrets put <id> <name> [<secret.json|->] [--via <url>] [--tool-prefix <prefix>]";
|
|
321
|
+
const flags = parseFlags(rest, { values: ["--via", "--tool-prefix"] }, usage);
|
|
318
322
|
if (flags.rest.length < 2 || flags.rest.length > 3)
|
|
319
323
|
throw usageError(`Usage: ${usage}`);
|
|
320
324
|
const [id, name, path] = flags.rest;
|
|
325
|
+
// This installation's routing of a bearer or headers secret (`secret-routing`).
|
|
326
|
+
const via = flags.values.get("--via");
|
|
327
|
+
const toolPrefix = flags.values.get("--tool-prefix");
|
|
328
|
+
const routing = { ...(via === undefined ? {} : { via }), ...(toolPrefix === undefined ? {} : { toolPrefix }) };
|
|
321
329
|
if (path === undefined) {
|
|
322
330
|
// A slot its manifest declares (D64): the value only, piped in; the binding is the slot's.
|
|
323
331
|
const value = await readSecret(deps, `value of ${name}`, `printf %s "$TOKEN" | nylorun agents secrets put ${id} ${name}`);
|
|
324
332
|
const { client } = await runtimeClient(deps, flags);
|
|
325
|
-
printJson(deps, await runtimeCall(() => client.agents.secrets.put(id, name, { value })));
|
|
333
|
+
printJson(deps, await runtimeCall(() => client.agents.secrets.put(id, name, { value, ...routing })));
|
|
326
334
|
return 0;
|
|
327
335
|
}
|
|
328
336
|
const secret = await readJson(path, "The secret");
|
|
329
|
-
|
|
330
|
-
|
|
337
|
+
// Without a type, a slot's value: { "value": … } or { "headers": { … } }, with via and toolPrefix.
|
|
338
|
+
if (typeof secret.type !== "string" && secret.value === undefined && secret.headers === undefined)
|
|
339
|
+
throw usageError('The secret needs a "type", or is a slot\'s "value" or "headers".');
|
|
331
340
|
const { client } = await runtimeClient(deps, flags);
|
|
332
|
-
printJson(deps, await runtimeCall(() => client.agents.secrets.put(id, name, secret)));
|
|
341
|
+
printJson(deps, await runtimeCall(() => client.agents.secrets.put(id, name, { ...secret, ...routing })));
|
|
333
342
|
return 0;
|
|
334
343
|
}
|
|
335
344
|
if (verb === "rm" || verb === "delete") {
|
package/dist/client/folder.d.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { type PackedFolder } from "@nylorun/agents";
|
|
2
2
|
import { type AgentFolderDiagnostic } from "@nylorun/core/folder";
|
|
3
|
+
import { type ExplainedAgent } from "@nylorun/core/engine";
|
|
3
4
|
import { type StackDeps } from "../stack/commands.js";
|
|
4
5
|
export { initCommand, initUsage } from "./init.js";
|
|
5
|
-
export declare const checkUsage = "nylorun check [<dir>...] [--fix] [--json]\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.\n\nA problem one edit fixes says (fixable with --fix): an unknown key with one did-you-mean, CRLF\nline ends, and secrets in a SKILL.md, which go in AGENT.md's skills block.\n\n --fix make those edits in the folder's files, print each, then\n check again; nothing is written without it, and a file\n that changed since it was checked is left as it is\n --json print { \"folders\": [{ path, ok, manifest?, diagnostics }] }\n for CI: whether each folder compiles, the manifest it\n compiles to, and every diagnostic, each fixable one with\n its fix; with --fix, also what was fixed and what was not";
|
|
6
|
+
export declare const checkUsage = "nylorun check [<dir>...] [--fix] [--explain] [--json]\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.\n\nA problem one edit fixes says (fixable with --fix): an unknown key with one did-you-mean, CRLF\nline ends, and secrets in a SKILL.md, which go in AGENT.md's skills block.\n\n --fix make those edits in the folder's files, print each, then\n check again; nothing is written without it, and a file\n that changed since it was checked is left as it is\n --explain print each engine feature the agent and its subagents get\n (agent-folder/2 declares them; agent-folder/1 gets them all\n by default), with the tools and text blocks each adds\n --json print { \"folders\": [{ path, ok, manifest?, diagnostics }] }\n for CI: whether each folder compiles, the manifest it\n compiles to, and every diagnostic, each fixable one with\n its fix; with --fix, also what was fixed and what was not;\n with --explain, the features as explain";
|
|
6
7
|
/** The agent folders under `dir`: `dir` when it holds an `AGENT.md`, else each subfolder that does. */
|
|
7
8
|
export declare function agentFolders(dir: string): Promise<string[]>;
|
|
8
9
|
/** Packs the folder, or a `CliError` naming why it cannot be. */
|
|
9
10
|
export declare function packed(folder: string): Promise<PackedFolder>;
|
|
10
11
|
/** A folder's diagnostics as lines, each path relative to `cwd`; `fixable` marks those with a fix. */
|
|
11
12
|
export declare function diagnosticLines(folder: string, cwd: string, diagnostics: readonly AgentFolderDiagnostic[], fixable?: boolean): string[];
|
|
13
|
+
/** `check --explain`'s lines for one folder: each agent, then each feature, on or off. */
|
|
14
|
+
export declare function explainLines(agents: readonly ExplainedAgent[]): string[];
|
|
12
15
|
/** `nylorun check [dir…] [--fix] [--json]` */
|
|
13
16
|
export declare function checkCommand(deps: StackDeps, args: readonly string[]): Promise<number>;
|
package/dist/client/folder.js
CHANGED
|
@@ -10,11 +10,12 @@ import { readdir, readFile, writeFile } from "node:fs/promises";
|
|
|
10
10
|
import { basename, join, relative, resolve } from "node:path";
|
|
11
11
|
import { AGENT_FOLDER_FILE, FolderError, checkFolder, diagnosticLine, isAgentFolder, packFolder, } from "@nylorun/agents";
|
|
12
12
|
import { applyFolderFixes } from "@nylorun/core/folder";
|
|
13
|
+
import { explainEngine } from "@nylorun/core/engine";
|
|
13
14
|
import { CliError } from "../errors.js";
|
|
14
15
|
import { parseStackFlags } from "../stack/commands.js";
|
|
15
16
|
import { usageError } from "./runtime.js";
|
|
16
17
|
export { initCommand, initUsage } from "./init.js";
|
|
17
|
-
export const checkUsage = `nylorun check [<dir>...] [--fix] [--json]
|
|
18
|
+
export const checkUsage = `nylorun check [<dir>...] [--fix] [--explain] [--json]
|
|
18
19
|
|
|
19
20
|
Compiles each agent folder (default .) offline, with the compiler the Runtime runs, and prints
|
|
20
21
|
every problem by file, line and column. A folder holds AGENT.md; a folder without one checks each
|
|
@@ -26,10 +27,14 @@ line ends, and secrets in a SKILL.md, which go in AGENT.md's skills block.
|
|
|
26
27
|
--fix make those edits in the folder's files, print each, then
|
|
27
28
|
check again; nothing is written without it, and a file
|
|
28
29
|
that changed since it was checked is left as it is
|
|
30
|
+
--explain print each engine feature the agent and its subagents get
|
|
31
|
+
(agent-folder/2 declares them; agent-folder/1 gets them all
|
|
32
|
+
by default), with the tools and text blocks each adds
|
|
29
33
|
--json print { "folders": [{ path, ok, manifest?, diagnostics }] }
|
|
30
34
|
for CI: whether each folder compiles, the manifest it
|
|
31
35
|
compiles to, and every diagnostic, each fixable one with
|
|
32
|
-
its fix; with --fix, also what was fixed and what was not
|
|
36
|
+
its fix; with --fix, also what was fixed and what was not;
|
|
37
|
+
with --explain, the features as explain`;
|
|
33
38
|
/** The agent folders under `dir`: `dir` when it holds an `AGENT.md`, else each subfolder that does. */
|
|
34
39
|
export async function agentFolders(dir) {
|
|
35
40
|
const root = resolve(dir);
|
|
@@ -107,11 +112,28 @@ async function fixFolder(pack, diagnostics, cwd) {
|
|
|
107
112
|
await writeFile(target, after);
|
|
108
113
|
return { fixed: result.changes, unfixed };
|
|
109
114
|
}
|
|
115
|
+
/** `check --explain`'s lines for one folder: each agent, then each feature, on or off. */
|
|
116
|
+
export function explainLines(agents) {
|
|
117
|
+
const lines = [];
|
|
118
|
+
for (const agent of agents) {
|
|
119
|
+
lines.push(` ${agent.agent}: ${agent.declared ? "declares its engine features (agent-folder/2)" : "every engine feature by default (agent-folder/1)"}`);
|
|
120
|
+
for (const item of agent.features) {
|
|
121
|
+
const adds = [
|
|
122
|
+
...(item.tools.length > 0 ? [`tools ${item.tools.join(", ")}`] : []),
|
|
123
|
+
...(item.blocks.length > 0 ? [`text ${item.blocks.join(", ")}`] : []),
|
|
124
|
+
...(item.note === undefined ? [] : [item.note]),
|
|
125
|
+
];
|
|
126
|
+
lines.push(` ${item.on ? "on " : "off"} ${item.feature} (${item.declaredBy})${item.on && adds.length > 0 ? `: ${adds.join("; ")}` : item.note && item.feature === "toolSearch" ? `: ${item.note}` : ""}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return lines;
|
|
130
|
+
}
|
|
110
131
|
/** `nylorun check [dir…] [--fix] [--json]` */
|
|
111
132
|
export async function checkCommand(deps, args) {
|
|
112
|
-
const flags = parseStackFlags(args, { booleans: ["--json", "--fix"] }, checkUsage);
|
|
133
|
+
const flags = parseStackFlags(args, { booleans: ["--json", "--fix", "--explain"] }, checkUsage);
|
|
113
134
|
const json = flags.booleans.has("--json");
|
|
114
135
|
const fix = flags.booleans.has("--fix");
|
|
136
|
+
const explain = flags.booleans.has("--explain");
|
|
115
137
|
const cwd = deps.cwd ?? process.cwd();
|
|
116
138
|
const folders = [];
|
|
117
139
|
for (const dir of flags.rest.length > 0 ? flags.rest : ["."]) {
|
|
@@ -146,6 +168,7 @@ export async function checkCommand(deps, args) {
|
|
|
146
168
|
...(compiled.manifest ? { manifest: compiled.manifest } : {}),
|
|
147
169
|
diagnostics: compiled.diagnostics,
|
|
148
170
|
...(pack.skipped.length > 0 ? { skipped: pack.skipped } : {}),
|
|
171
|
+
...(explain && compiled.manifest ? { explain: explainEngine(compiled.manifest) } : {}),
|
|
149
172
|
...(fix ? { fixed: fixed.fixed, unfixed: fixed.unfixed } : {}),
|
|
150
173
|
});
|
|
151
174
|
continue;
|
|
@@ -164,8 +187,12 @@ export async function checkCommand(deps, args) {
|
|
|
164
187
|
deps.err(line);
|
|
165
188
|
const label = relative(cwd, folder) || basename(folder);
|
|
166
189
|
const files = `${pack.entries.length} ${pack.entries.length === 1 ? "file" : "files"}`;
|
|
167
|
-
if (compiled.manifest)
|
|
190
|
+
if (compiled.manifest) {
|
|
168
191
|
deps.out(`${label}: ok, agent manifest ${compiled.manifest.name} (${files}, ${compiled.compiler})`);
|
|
192
|
+
if (explain)
|
|
193
|
+
for (const line of explainLines(explainEngine(compiled.manifest)))
|
|
194
|
+
deps.out(line);
|
|
195
|
+
}
|
|
169
196
|
else {
|
|
170
197
|
const errors = compiled.diagnostics.filter((item) => item.severity === "error").length;
|
|
171
198
|
deps.err(`${label}: ${errors} ${errors === 1 ? "error" : "errors"}`);
|
package/dist/client/init.js
CHANGED
|
@@ -49,7 +49,7 @@ export function schemaMappings(root, folder) {
|
|
|
49
49
|
// Folders under a parent (agents/) share one pattern; a folder at the root is named alone.
|
|
50
50
|
const base = parent === "" ? relative(root, folder).split(sep).join("/") : `${parent}/**`;
|
|
51
51
|
return [
|
|
52
|
-
{ fileMatch: [`${base}/runtime.json`], url: `${SCHEMA_BASE}/runtime.json` },
|
|
52
|
+
{ fileMatch: [`${base}/runtime.json`], url: `${SCHEMA_BASE}/runtime.1.json` },
|
|
53
53
|
{ fileMatch: [`${base}/tools/*.json`], url: `${SCHEMA_BASE}/tool.json` },
|
|
54
54
|
{ fileMatch: [`${base}/flow.json`], url: `${SCHEMA_BASE}/flow.json` },
|
|
55
55
|
{ fileMatch: [`${base}/mcp.json`, `!${base}/plugins/**`], url: `${SCHEMA_BASE}/mcp.json` },
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { StackDeps } from "../stack/commands.js";
|
|
2
|
+
export declare const migrateFormatUsage = "nylorun migrate folder <dir> --to 2\n\nMakes the agent folder <dir> agent-folder/2, in place: it adds format: agent-folder/2 to its\nAGENT.md, and to it and each subagent's AGENT.md the engine: block that declares every engine\nfeature the folder gets today by default (the sandbox tools, save_artifact and the outputs folder\nwhen runtime.json has a computer, read_artifact, tool search). Nothing the model reads or may\ncall changes, which it checks before it writes; then remove the lines the agent does not need.\nReads no Runtime.";
|
|
3
|
+
/** The `engine:` block that keeps what an agent-folder/1 agent gets, as YAML lines. */
|
|
4
|
+
export declare function engineLines(computer: boolean): string[];
|
|
5
|
+
/**
|
|
6
|
+
* `text`, an `AGENT.md`, with `lines` added at the end of its frontmatter and, when `format` is
|
|
7
|
+
* set, its `format:` line replaced by `format: <format>` (first in the frontmatter when it had
|
|
8
|
+
* none). Undefined when it has no frontmatter.
|
|
9
|
+
*/
|
|
10
|
+
export declare function withFrontmatter(text: string, lines: readonly string[], format?: string): string | undefined;
|
|
11
|
+
/** `nylorun migrate folder <dir> --to 2` */
|
|
12
|
+
export declare function migrateFormatCommand(deps: StackDeps, dir: string, to: string): Promise<number>;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nylorun migrate folder <dir> --to 2` (ADR-100): makes an `agent-folder/1` folder an
|
|
3
|
+
* `agent-folder/2` one, in place, declaring every engine feature it gets today by default, so no
|
|
4
|
+
* prompt and no tool changes until the author removes a line:
|
|
5
|
+
*
|
|
6
|
+
* - `format: agent-folder/2`;
|
|
7
|
+
* - `engine:` in `AGENT.md` and in each `agents/<id>/AGENT.md`: the six sandbox tools,
|
|
8
|
+
* `save_artifact` and the outputs folder when the runtime has a computer, `read_artifact`
|
|
9
|
+
* always, and tool search.
|
|
10
|
+
*
|
|
11
|
+
* It checks the folder before and after, offline, with the compiler the Runtime runs, and that
|
|
12
|
+
* every agent's system text and tool list are the same (`previewTurn`), and writes nothing when
|
|
13
|
+
* they are not. Reads no Runtime.
|
|
14
|
+
*/
|
|
15
|
+
import { createHash } from "node:crypto";
|
|
16
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
17
|
+
import { join, relative } from "node:path";
|
|
18
|
+
import { AGENT_FOLDER_FILE, checkFolder } from "@nylorun/agents";
|
|
19
|
+
import { SANDBOX_TOOL_NAMES } from "@nylorun/core/define";
|
|
20
|
+
import { previewTurn } from "@nylorun/core/engine";
|
|
21
|
+
import { CliError } from "../errors.js";
|
|
22
|
+
import { diagnosticLines, packed } from "./folder.js";
|
|
23
|
+
import { usageError } from "./runtime.js";
|
|
24
|
+
export const migrateFormatUsage = `nylorun migrate folder <dir> --to 2
|
|
25
|
+
|
|
26
|
+
Makes the agent folder <dir> agent-folder/2, in place: it adds format: agent-folder/2 to its
|
|
27
|
+
AGENT.md, and to it and each subagent's AGENT.md the engine: block that declares every engine
|
|
28
|
+
feature the folder gets today by default (the sandbox tools, save_artifact and the outputs folder
|
|
29
|
+
when runtime.json has a computer, read_artifact, tool search). Nothing the model reads or may
|
|
30
|
+
call changes, which it checks before it writes; then remove the lines the agent does not need.
|
|
31
|
+
Reads no Runtime.`;
|
|
32
|
+
/** The `engine:` block that keeps what an agent-folder/1 agent gets, as YAML lines. */
|
|
33
|
+
export function engineLines(computer) {
|
|
34
|
+
return computer
|
|
35
|
+
? [
|
|
36
|
+
"engine:",
|
|
37
|
+
` sandbox: [${SANDBOX_TOOL_NAMES.join(", ")}]`,
|
|
38
|
+
" artifacts: { read: true, save: true }",
|
|
39
|
+
" outputs: true",
|
|
40
|
+
" toolSearch: true",
|
|
41
|
+
]
|
|
42
|
+
: ["engine:", " artifacts: { read: true, save: false }", " toolSearch: true"];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* `text`, an `AGENT.md`, with `lines` added at the end of its frontmatter and, when `format` is
|
|
46
|
+
* set, its `format:` line replaced by `format: <format>` (first in the frontmatter when it had
|
|
47
|
+
* none). Undefined when it has no frontmatter.
|
|
48
|
+
*/
|
|
49
|
+
export function withFrontmatter(text, lines, format) {
|
|
50
|
+
const eol = text.includes("\r\n") ? "\r\n" : "\n";
|
|
51
|
+
const bom = text.startsWith("") ? "" : "";
|
|
52
|
+
const all = text.slice(bom.length).split(/\r?\n/);
|
|
53
|
+
if (all[0]?.trimEnd() !== "---")
|
|
54
|
+
return undefined;
|
|
55
|
+
const end = all.findIndex((line, index) => index > 0 && line.trimEnd() === "---");
|
|
56
|
+
if (end < 0)
|
|
57
|
+
return undefined;
|
|
58
|
+
let front = all.slice(1, end);
|
|
59
|
+
if (format !== undefined) {
|
|
60
|
+
const at = front.findIndex((line) => /^format\s*:/.test(line));
|
|
61
|
+
if (at >= 0)
|
|
62
|
+
front[at] = `format: ${format}`;
|
|
63
|
+
else
|
|
64
|
+
front = [`format: ${format}`, ...front];
|
|
65
|
+
}
|
|
66
|
+
return bom + ["---", ...front, ...lines, ...all.slice(end)].join(eol);
|
|
67
|
+
}
|
|
68
|
+
/** What each agent of a compiled folder reads and may call, by agent, for a comparison. */
|
|
69
|
+
function seen(manifest) {
|
|
70
|
+
if ("kind" in manifest.harness)
|
|
71
|
+
return "";
|
|
72
|
+
const preview = previewTurn(manifest);
|
|
73
|
+
const one = (item) => ({ system: item.system, tools: item.tools });
|
|
74
|
+
return JSON.stringify({ root: one(preview), pinned: preview.pinned, agents: Object.fromEntries(Object.entries(preview.agents).map(([id, item]) => [id, one(item)])) });
|
|
75
|
+
}
|
|
76
|
+
/** `nylorun migrate folder <dir> --to 2` */
|
|
77
|
+
export async function migrateFormatCommand(deps, dir, to) {
|
|
78
|
+
if (to !== "2" && to !== "agent-folder/2")
|
|
79
|
+
throw usageError(`--to takes 2 (agent-folder/2).\n${migrateFormatUsage}`);
|
|
80
|
+
const cwd = deps.cwd ?? process.cwd();
|
|
81
|
+
const where = relative(cwd, dir) || ".";
|
|
82
|
+
const before = await checkFolder(await packed(dir));
|
|
83
|
+
if (!before.manifest) {
|
|
84
|
+
for (const line of diagnosticLines(dir, cwd, before.diagnostics))
|
|
85
|
+
deps.err(line);
|
|
86
|
+
throw new CliError(`${where} does not compile: fix it first (nylorun check ${where}).`, 1);
|
|
87
|
+
}
|
|
88
|
+
if (before.compiler === "agent-folder/2") {
|
|
89
|
+
deps.out(`${where} is agent-folder/2 already: nothing to do.`);
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
const harness = before.manifest.harness;
|
|
93
|
+
if ("kind" in harness)
|
|
94
|
+
throw new CliError(`${where} is a flow: migrate each agent folder it holds instead.`, 1);
|
|
95
|
+
const kind = before.manifest.runtime?.kind ?? "none";
|
|
96
|
+
const computer = kind === "virtual" || kind === "pod";
|
|
97
|
+
// The root, then each subagent (`agents/<id>/`), each with the declarations it gets today.
|
|
98
|
+
const edits = new Map();
|
|
99
|
+
const read = async (path) => readFile(join(dir, ...path.split("/")), "utf8");
|
|
100
|
+
const root = withFrontmatter(await read(AGENT_FOLDER_FILE), engineLines(computer), "agent-folder/2");
|
|
101
|
+
if (root === undefined)
|
|
102
|
+
throw new CliError(`${join(where, AGENT_FOLDER_FILE)} has no frontmatter.`, 1);
|
|
103
|
+
edits.set(AGENT_FOLDER_FILE, root);
|
|
104
|
+
for (const tool of harness.tools ?? []) {
|
|
105
|
+
if (!tool.agent)
|
|
106
|
+
continue;
|
|
107
|
+
const path = `agents/${tool.name}/${AGENT_FOLDER_FILE}`;
|
|
108
|
+
const edited = withFrontmatter(await read(path), engineLines(computer));
|
|
109
|
+
if (edited === undefined)
|
|
110
|
+
throw new CliError(`${join(where, path)} has no frontmatter.`, 1);
|
|
111
|
+
edits.set(path, edited);
|
|
112
|
+
}
|
|
113
|
+
// The folder as it would be, compiled and compared before anything is written.
|
|
114
|
+
const pack = await packed(dir);
|
|
115
|
+
const encoder = new TextEncoder();
|
|
116
|
+
const files = new Map(pack.files);
|
|
117
|
+
const entries = pack.entries.map((entry) => {
|
|
118
|
+
const text = edits.get(entry.path);
|
|
119
|
+
if (text === undefined)
|
|
120
|
+
return entry;
|
|
121
|
+
const bytes = encoder.encode(text);
|
|
122
|
+
const source = files.get(entry.path);
|
|
123
|
+
files.set(entry.path, { ...source, size: bytes.byteLength, read: async () => bytes });
|
|
124
|
+
return { ...entry, size: bytes.byteLength, sha256: sha256Hex(bytes) };
|
|
125
|
+
});
|
|
126
|
+
const after = await checkFolder({ ...pack, entries, files });
|
|
127
|
+
if (!after.manifest) {
|
|
128
|
+
for (const line of diagnosticLines(dir, cwd, after.diagnostics))
|
|
129
|
+
deps.err(line);
|
|
130
|
+
throw new CliError(`${where} would not compile as agent-folder/2: nothing was written.`, 1);
|
|
131
|
+
}
|
|
132
|
+
if (seen(after.manifest) !== seen(before.manifest))
|
|
133
|
+
throw new CliError(`${where} as agent-folder/2 would change what its model reads or may call: nothing was written.`, 1);
|
|
134
|
+
for (const [path, text] of edits)
|
|
135
|
+
await writeFile(join(dir, ...path.split("/")), text);
|
|
136
|
+
for (const path of edits.keys())
|
|
137
|
+
deps.out(`wrote ${join(where, path)}`);
|
|
138
|
+
deps.out(`${where} is agent-folder/2, and its agents get what they got before. Remove the engine features they do not use, and see what each adds with nylorun check --explain ${where}.`);
|
|
139
|
+
return 0;
|
|
140
|
+
}
|
|
141
|
+
const sha256Hex = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
package/dist/client/migrate.js
CHANGED
|
@@ -26,6 +26,8 @@ import { harnessSectionOf, sectionGaps } from "@nylorun/core/define";
|
|
|
26
26
|
import { CliError } from "../errors.js";
|
|
27
27
|
import { parseStackFlags } from "../stack/commands.js";
|
|
28
28
|
import { migrateFolderCommand, migrateFolderUsage } from "./convert.js";
|
|
29
|
+
import { migrateFormatCommand, migrateFormatUsage } from "./migrate-format.js";
|
|
30
|
+
import { isHelp } from "../errors.js";
|
|
29
31
|
import { readJson, usageError } from "./runtime.js";
|
|
30
32
|
export const migrateUsage = `nylorun migrate v8 <agent.json> [--runtime <environment-manifest.json>] [--name <name>] [--out <dir>]
|
|
31
33
|
|
|
@@ -69,11 +71,24 @@ function hasSkills(loop) {
|
|
|
69
71
|
export async function migrateCommand(deps, args) {
|
|
70
72
|
const [version, ...rest] = args;
|
|
71
73
|
if (version === "--help" || version === "-h") {
|
|
72
|
-
deps.out(`${migrateUsage}\n\n${migrateFolderUsage}`);
|
|
74
|
+
deps.out(`${migrateUsage}\n\n${migrateFolderUsage}\n\n${migrateFormatUsage}`);
|
|
73
75
|
return 0;
|
|
74
76
|
}
|
|
75
|
-
if (version === "folder")
|
|
76
|
-
|
|
77
|
+
if (version === "folder") {
|
|
78
|
+
// `--to 2`: an agent folder made agent-folder/2 in place (ADR-100).
|
|
79
|
+
const to = rest.indexOf("--to");
|
|
80
|
+
if (to < 0)
|
|
81
|
+
return migrateFolderCommand(deps, rest);
|
|
82
|
+
const value = rest[to + 1];
|
|
83
|
+
const others = rest.filter((_, index) => index !== to && index !== to + 1);
|
|
84
|
+
if (others.some(isHelp)) {
|
|
85
|
+
deps.out(migrateFormatUsage);
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
if (value === undefined || value.startsWith("-") || others.length !== 1 || others[0].startsWith("-"))
|
|
89
|
+
throw usageError(`Usage: ${migrateFormatUsage.split("\n")[0]}`);
|
|
90
|
+
return migrateFormatCommand(deps, resolve(deps.cwd ?? process.cwd(), others[0]), value);
|
|
91
|
+
}
|
|
77
92
|
if (version !== "v8")
|
|
78
93
|
throw usageError(version === undefined ? migrateUsage : `nylorun migrate knows v8 and folder only.\n${migrateUsage}\n\n${migrateFolderUsage}`);
|
|
79
94
|
const flags = parseStackFlags(rest, { values: ["--runtime", "--name", "--out"] }, migrateUsage);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nylorun",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.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.
|
|
34
|
-
"@nylorun/agents": "0.
|
|
35
|
-
"@nylorun/core": "0.
|
|
33
|
+
"@nylorun/admin": "0.19.10-beta",
|
|
34
|
+
"@nylorun/agents": "0.32.0-beta",
|
|
35
|
+
"@nylorun/core": "0.34.0-beta"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@earendil-works/pi-ai": "1.3.0",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"nylo": "dist/nylo.js"
|
|
47
47
|
},
|
|
48
48
|
"nylorun": {
|
|
49
|
-
"runtime": "0.
|
|
50
|
-
"studio": "0.35.
|
|
49
|
+
"runtime": "0.39.0-beta",
|
|
50
|
+
"studio": "0.35.2-beta"
|
|
51
51
|
}
|
|
52
52
|
}
|