@nylorun/agents 0.22.0-beta → 0.23.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 +96 -0
- package/README.md +16 -13
- package/dist/environments.d.ts +27 -1
- package/dist/environments.js +1 -1
- package/dist/files.d.ts +1 -1
- package/dist/files.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/session-client.d.ts +2 -1
- package/dist/skills/index.d.ts +0 -1
- package/dist/skills/index.js +0 -1
- package/dist/skills/load.d.ts +3 -2
- package/dist/skills/load.js +44 -14
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,101 @@
|
|
|
1
1
|
# @nylorun/agents
|
|
2
2
|
|
|
3
|
+
## 0.23.0-beta
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 0a2fbc5: **Artifacts reach the harness through the Harness API; `read_artifact` and `save_artifact` run in the harness (protocol 15, Harness API 4).** The first step of the harness boundary: model-facing work belongs to the harness, and the Runtime offers primitives and holds limits and access. A fresh start for agent sessions: the Runtime speaks protocol 15 only. See MIGRATION.md.
|
|
8
|
+
|
|
9
|
+
- **The artifact seam.** A harness reads and writes the artifacts of the run it holds: `artifact.read {artifactId, version?, offset?, length?}` answers the version's metadata (`ArtifactMetaSchema`) and its bytes from `offset`, at most `turn.start.limits.chunkBytes`; `artifact.write` (`begin`, `chunk`, `commit`, `abort`) makes a file artifact of the session or a new version of one of its own, streamed with backpressure into the Object store and committed in Postgres within the Tenant's and the Environment's artifact limits. The scope is always the run's session: its own artifacts, and the Tenant-wide ones its messages named; anything else is the new error code `not_found`, alike. A write's `key` makes it idempotent within the session (`artifact_versions.write_key`, migration 0028), and its `effectId` links the artifact to a tool call of the turn, whose call id `artifact.created` carries. An upload the run leaves open commits nothing. Client helpers: `readArtifact`, `openArtifactWrite`, `writeArtifactBytes`, `toBase64` and `fromBase64` in `@nylorun/core/harness-api`.
|
|
10
|
+
- **Our harness runs the artifact tools.** `read_artifact` (every agent) and `save_artifact` (with a sandbox) are our engine's tools, defined in `@nylorun/harness` (`artifactToolsFor`, `runArtifactTool`) and run over the seam and the run's sandbox files (`HarnessExecutors.files`, `SandboxManager.writeBytes`); their calls are kind `harness`. `save_artifact` keys its write by its effect id. `read_artifact` takes `path` with a sandbox, which copies the whole artifact into it, and hands a file of any other type to the model as a file of the result (the Model Gate writes the note for one the model does not read; `artifact.unsupported` is gone). Its page is harness configuration, `createHarness({ artifactPageBytes })` (32 KiB by default), and now always ends on a whole character. `runDurable` takes `engineTools`; `apiHost` takes the engine's configuration.
|
|
11
|
+
- **The Runtime pins no artifact tools.** `runtimeManifestFor` has no `save` or `read`; `RUNTIME_TOOL_NAMES` no longer holds the two names, so an author tool may take one (our harness then refuses the turn). A session without a sandbox or skills pins its definition unchanged. Core no longer runs either tool (`tenant/artifact-tool.ts` is gone). Migration 0029 closes every agent session that pinned them (`closedReason: "protocol_15"`, `409 session_closed`); workflow sessions carry over.
|
|
12
|
+
- **Media types are core's.** `contentTypeFor`, `essence`, `normalizeContentType`, `isText`, `isModelImage`, `modelInputOf`, `inputLabel`, `extensionFor`, `unreadableReason`, `MODEL_IMAGE_TYPES` and `DEFAULT_CONTENT_TYPE` are exported from `@nylorun/core/contracts`, for the Runtime and harnesses alike.
|
|
13
|
+
- `PROTOCOL_VERSION` 15 with required feature `harness-boundary`; `HARNESS_API_VERSION` 4. The SDKs and the CLI send protocol 15.
|
|
14
|
+
|
|
15
|
+
- ebe7348: **The harness decides how files reach the model; the Model Gate only fetches and translates (protocol 15).** The second step of the harness boundary, still protocol 15 and Harness API 4. See MIGRATION.md.
|
|
16
|
+
|
|
17
|
+
- **Any file may be sent.** A message naming a file the model does not read is accepted: `422 attachment_unsupported` and its error code are gone, and AG-UI and A2A accept such files too. Files are still held to the artifact size limits at upload.
|
|
18
|
+
- **`turn.start.model`** is the model descriptor (`ModelDescriptorSchema`): the provider, id, inputs, context window and output limit of the model the turn's calls use, absent for flows and without a model. One resolution (`model/descriptor.ts`) serves it, the catalog's inputs and the gate.
|
|
19
|
+
- **Our engine shows each file.** An image or document the model reads goes as its artifact reference, a text file is read over the artifact seam and inlined as a `<file>` block, and anything else (past `modelFileBytes`, a kind the model does not read) is a note it writes. `fileView`, `representFiles`, `fileBlock`, `fileNote` and `mediaReferenceLine` (moved from `@nylorun/core/define`) are exported from `@nylorun/harness`. A budget from the descriptor bounds the prompt before the first answer, and compaction counts files as the model gets them.
|
|
20
|
+
- **The Model Gate refuses instead of writing notes.** A text file as a media part, or a kind the model or its API does not read, fails the call with the new model failure code `unsupported_input`; a reference it cannot fetch fails `invalid_request`. Nothing is sent, and neither is retried.
|
|
21
|
+
- **Structured answers are the engine's.** `ModelCall.outputSchema` is removed: our engine writes the JSON instruction and parses the answer (fenced, with control characters repaired; `parseJsonWithRepair` ported from pi-ai). `message.assistant` for a structured answer now carries its JSON text.
|
|
22
|
+
- **`read_artifact`** refuses a file the turn's model does not read (`artifact.unsupported`, with the reason).
|
|
23
|
+
- `unreadableReason` is removed; `isOffice` joins the media type helpers in `@nylorun/core/contracts`.
|
|
24
|
+
- Studio's file picker accepts every file, and the refusal chips are gone.
|
|
25
|
+
|
|
26
|
+
- ff6470e: **The harness writes the model's instructions and defines the sandbox and skill tools; no `runtime` block (protocol 15).** The third step of the harness boundary, still protocol 15 and Harness API 4. See MIGRATION.md.
|
|
27
|
+
|
|
28
|
+
- **No `runtime` block.** The Runtime no longer pins model-facing text or tools on a session's manifest: a session pins its definition as registered (`manifestHash` equals `definitionHash`) and keeps its sandbox spec on the session. The manifest schema refuses `runtime` with what replaces it (`REMOVED_MANIFEST_FIELDS`). `runtimeManifestFor`, `runtimeToolOf`, `RuntimeManifest`, `RUNTIME_TOOL_NAMES`, `isRuntimeToolName`, `createSandboxTools`, `SANDBOX_INSTRUCTIONS`, the skill tool names, `isSkillTool`, `SKILLS_USAGE`, `formatSkillCatalog` and `skillOfToolCall` leave `@nylorun/core/define`; `RUNTIME_PART_ID` is `SESSION_PART_ID` (`"session"`), and `agentFrom` takes `sessionInstructions`. `@nylorun/agents` no longer re-exports the skill catalog helpers. Migration 0030 closes the agent sessions that still pinned a `runtime` block (`closedReason: "protocol_15"`).
|
|
29
|
+
- **`turn.start` states facts.** `environment` (`TurnEnvironmentSchema`: `{id, kind, sandbox?: {workspace}}`) and `routing.sandbox.spec` (the pinned spec) join the agent's `skills`.
|
|
30
|
+
- **Our engine words them.** `@nylorun/harness` writes the skill catalog, the sandbox note and the skills' folder note, and defines the six sandbox tools, `load_skill` and `read_skill_resource`, in the order and bytes the `runtime` block had, so the prompt's prefix is unchanged (`engineToolsFor`, `sandboxTools`, `sandboxInstructions`, `skillCatalogText`, `skillFolderNote`, `skillTools`, all exported for a harness of your own). The skill tools run in the harness over `definition.file` (`runSkillTool`), and core no longer runs them (`tenant/skill-tool.ts` is gone). The sandbox tools' limits are harness configuration (`createHarness({ sandboxTools })`, `harnessExecutors({ sandboxTools })`, `DEFAULT_SANDBOX_TOOL_LIMITS`), as is the most a skill read takes into the context (`createHarness({ skillReadBytes })`, 10 MiB by default), held to the run's `limits.recordEntryBytes`.
|
|
31
|
+
- **Effects name their target.** A tool effect carries `target: {kind: "mcp" | "http" | "sandbox" | "harness" | "platform"}` (`EffectTargetSchema`) and may carry `skill`; the record routes by the kind, never by the tool's name, and a call without a target or one the session lacks is `tool.unavailable`. `hello.features.tools` and `NativeToolSchema` are removed, no tool name is reserved, and the record keeps the skill the intent names when the agent has it. A skill tool's `tool.completed` is `kind: "harness"` (it was `platform`).
|
|
32
|
+
- **Studio** drops the manifest panel's Runtime section: it shows the sandbox tools when the session's Environment has a sandbox.
|
|
33
|
+
|
|
34
|
+
- f443475: **The harness builds the MCP tool list and shapes tool results; tools are identified by server and tool (protocol 15).** The fourth step of the harness boundary, still protocol 15 and Harness API 4. See MIGRATION.md.
|
|
35
|
+
|
|
36
|
+
- **Tools have an identity.** An MCP tool call's target is `{kind: "mcp", server, tool}` (the server's own name for the tool) and an HTTP tool call's `{kind: "http", tool}` (its declared name); `platform` is gone. `toolName` is the model's name, for display. The record checks an MCP call against the session's discovery (`tool.unknown`) and the turn's tool settings (`tool.disabled`), and `tool.completed`, tool-call rows (`tool`), approvals (`Approve server / tool?`, `interaction.tool`) and traces (`nylorun.tool.identity`) carry the identity. The Tool Gate matches a call to its journaled target.
|
|
37
|
+
- **Tool policy keys on identity.** A rule's `tool` is the tool's identity, never the model's name. A rule whose `tool` has `__` in it is refused at `PUT /v1/tenant/tool-policy`; a stored one makes the policy unreadable, so every call is denied (and `GET` answers `409`) until the policy names the tool by `server` and `tool`.
|
|
38
|
+
- **`turn.start.mcp` states facts.** The session pins what its servers listed, raw (`McpSnapshot` v2: servers, scrubbed instructions, tools as named); each turn gets `mcp` (`McpFactsSchema`: every tool with `enabled` and the manifest's `deferred` hint) in place of `sessionTools` and `routing.mcpSnapshot`, and `session.mcp` answers `{mcp}`. `mcp.discovered` has no `renamed` or `deferred`. Migration 0031 closes the sessions that pinned the former snapshot (`closedReason: "protocol_15"`).
|
|
39
|
+
- **Our engine names, defers and searches.** `@nylorun/harness` names MCP tools (`mcpToolName`, `mcpToolNames`, within the 64-character provider ceiling), defers them by the manifest's setting or the model's window and pins that layout in its state (`mcpToolSet`, `McpLayout`), and runs `tool_search` (BM25) itself; core's `tool_search`, `deferredToolsTools`, `TOOL_SEARCH_TOOL` and `TOOL_CALL_TOOL` leave `@nylorun/core/define`, and `runDurable` takes `mcp` instead of `sessionTools`.
|
|
40
|
+
- **Our engine shapes tool results.** Core no longer stores large or binary tool results: the record refuses an outcome past `limits.recordEntryBytes` (`limit_exceeded`). Our engine spills them with `artifact.write`, keyed by the call and its bytes (`spillKey`), with a preview, an image as a file for a model that reads images, and one budget per step (`shapeToolOutcome`, `StepBudget`, exported for a harness of your own). The Tool Gate still scrubs credentials and refuses answers past `limits.toolAnswerBytes`.
|
|
41
|
+
- **Harness configuration.** `createHarness({ mcpTools, toolResults })` sets the deferral share, characters per token, fallback window, the note's instruction length, `tool_search`'s limits, the inline cap, preview head and tail, step budget, stub, small size and entry envelope (`DEFAULT_MCP_TOOL_CONFIG`, `DEFAULT_TOOL_RESULT_CONFIG`).
|
|
42
|
+
- **The MCP preview** names tools as Nylorun's engine would (`modelName`, `renamed`) and shows a server's instructions uncut.
|
|
43
|
+
- **Studio** shows an MCP tool as `server / tool` in tool and approval cards, with the model's name, and links the artifacts a tool's result was stored in.
|
|
44
|
+
- **`examples/harness-minimal`** spills a large `bash` output with `artifact.write` and keeps a preview; its README shows how a harness of your own offers MCP tools.
|
|
45
|
+
|
|
46
|
+
- f2e9b0a: **Skill and artifact limits are set per Tenant and per Environment manifest; a skill holds up to 2 GiB by default, with no limit per file or on the number of files.**
|
|
47
|
+
|
|
48
|
+
- The 10 MiB limit per skill file and the 500-file limit are gone. A skill's files may hold up to the Tenant's skill limit together: 2 GiB by default (`SKILL_TOTAL_BYTES_DEFAULT`). A file at two paths counts twice, since a sandbox holds both copies. `SKILL_FILES_MAX` and `DEFINITION_FILE_MAX_BYTES` are no longer exported.
|
|
49
|
+
- The Tenant's skill limit is the new Management API setting `GET`/`PUT /v1/tenant/skills` (`{ limits: { totalBytes } }`), also available as `admin.settings.skills` and `nylorun tenant settings skills [get | put <file|->]`.
|
|
50
|
+
- `PUT /v1/files/sha256:<hex>` takes a file up to that limit.
|
|
51
|
+
- `PUT /v1/agents/{id}` refuses a skill over it, an agent used as a tool included: `413 limit_exceeded`, with `details.skills` listing each skill and its size.
|
|
52
|
+
- An environment manifest, or the inline fields of `PUT /v1/environments/{id}`, may set `limits`: `artifacts.fileBytes`, `artifacts.totalBytes` and `skills.totalBytes`. Like the manifest's other fields, they are fixed for the Environment's life.
|
|
53
|
+
- Each limit must be at most the Tenant's. A higher one is refused with `400` when the Environment is made or the manifest is saved.
|
|
54
|
+
- A limit left out is the Tenant's at the time it is checked. A Tenant that lowers a limit lowers it for every Environment.
|
|
55
|
+
- The Environment view's new `limits` field gives the limits in force.
|
|
56
|
+
- An Environment's artifacts are its sessions' artifacts and its disk versions. Uploads, `save_artifact`, turn-end exports and disk snapshots hold them to the Environment's per-file limit and to its own total, as well as to the Tenant's total. A turn-end export past the Environment's total is skipped with the new reason `environment_total`, and a disk snapshot past it is `413` with `details.limit: "environment_total"`.
|
|
57
|
+
- A session opens on an Environment only when its agent's skills fit the Environment's skill limit; otherwise it is `409 environment_unsuitable`, with `details.skills`.
|
|
58
|
+
- `PUT /v1/files` streams the body to the Object store instead of holding it in memory. Only bytes that match the named hash reach the file's key. Environment files named by hash are held to the same per-file maximum.
|
|
59
|
+
- `@nylorun/agents`: loading a skills folder no longer checks a size, since the limit is the Tenant's; the Runtime refuses an oversized skill. The `skills.file-too-large` and `skills.too-many-files` errors are gone. Files are hashed and read a chunk at a time.
|
|
60
|
+
- Sandboxes get skill files as a stream. A harness fetches them from core 16 MiB at a time: `definition.file` takes optional `offset` and `length` (at most the Runtime limit `chunkBytes`).
|
|
61
|
+
- `load_skill` and `read_skill_resource` read files of up to 10 MiB into the model's context. A larger file fails with `skill.resource_too_large`, and the error names the file's path in the sandbox when there is one.
|
|
62
|
+
|
|
63
|
+
### Patch Changes
|
|
64
|
+
|
|
65
|
+
- cf425c6: **The harness mounts skills and saves outputs; the in-process harness uses the gates; compaction is a transcript edit (protocol 15).** The fifth step of the harness boundary, still protocol 15 and Harness API 4. See MIGRATION.md.
|
|
66
|
+
|
|
67
|
+
- **Skill folders are our engine's.** Before each sandbox call our engine mounts each skill version of the turn's tree at `/skills/<name>@<hash>/` with `executors.files.mount`, reading the bytes over `definition.file`; the SandboxManager only writes them (`mountFolder`), once per open sandbox (`present` after). `SandboxManager.mountSkills`, its `definitionFile` port and `SandboxSessionRef.manifest` are gone, and the operator sandbox route mounts nothing.
|
|
68
|
+
- **The outputs folder is our engine's (Q5).** At a completed turn's end in a sandbox our engine saves `/workspace/outputs` as the session's `outputs` folder before `turn.completed`, over the new folder mode of `artifact.write` (`kind: "folder"`, `file`, chunks named by hash, `unchanged`, `refused`). Core takes every byte, checks each file's hash before content is stored under it, dedupes, and keeps the content epoch and quotas. Versions say `source: "engine"`; `artifact.export.skipped` and `artifact.export.failed` are harness claims. Core's export (`artifacts/export.ts`, the `WorkspaceReader`, `workspace.read` listing and bytes calls) is removed.
|
|
69
|
+
- **No limit literals.** The new Runtime limit `folderEntries` (10,000; a count, its ceiling from the manifest's one string) replaces `EXPORT_MAX_FILES`, and `createHarness({ outputs: { dir, maxBytes } })` (`/workspace/outputs`, 1 GiB) replaces `EXPORT_MAX_BYTES`. `RUNTIME_LIMIT_CEILINGS` entries are `{value, unit, why}`.
|
|
70
|
+
- **In-process parity.** Every model call goes through the Model Gate: a scripted model is served by `providerModelGate`, metered like any. The MCP pool opens every server through the Tool Gate (`openRemote` is required; the in-process gate's `openMcp` finds the server in the session's pinned manifest and authorizes it with the vault). `ModelRoute` is `{tenantId, modelGate}`; `TenantContext.modelProvider`, `useVaultModel` and `workspaces` are gone.
|
|
71
|
+
- **Compaction is a transcript edit (Q4).** Summary calls say `purpose: "summary"` (no `message.assistant`), and the segment's edit that replaced the history says `kind: "compaction"` with its facts and summary calls (`CompactionFactsSchema`, `transcriptUpdates(..., compaction)`). Core checks the calls and writes one `context.compacted`, which loses `partial`.
|
|
72
|
+
|
|
73
|
+
- 23774c4: **The last hard-coded limits are configuration (harness boundary B15c).** Still protocol 15 and Harness API 4. Every default is the value before. See MIGRATION.md, and DEPLOYMENT.md's Runtime limits and Host tuning.
|
|
74
|
+
|
|
75
|
+
- **21 more Runtime limits** (`GET`/`PUT /v1/tenant/limits`): a message's parts (`messageParts`), page sizes (`pageItems`, `exportPageItems`, `environmentEventItems`; a page asked larger holds that many), labels (`artifactLabels`, `labelKeyChars`, `labelValueChars`), a manifest's setup command, descriptions and secret hosts (`setupCommandChars`, `manifestDescriptionChars`, `secretDescriptionChars`, `secretHosts`), a failed pod's reason (`setupReasonChars`, `setupCommandExcerptChars`), secret values (`secretValueChars`), the tool policy and budgets (`toolRules`, `toolRuleFieldChars`, `toolRuleTextChars`, `modelBudgets`) and flows (`flowConcurrency`, `flowMapItems`, `flowLoopIterations`, replacing the `NYLORUN_FLOW_*` variables no Host read). Request schemas lose these maxima; the Runtime checks the Tenant's.
|
|
76
|
+
- **More Host tuning** (`NYLORUN_TUNING`): token lifetimes and clock tolerance (`keys.*`), the advance deadline and grace (`execution.*`), `nylorun-operate`'s waits, the credential store's lock, sweep arming, Postgres batches and the replication pool, a basin repair's log cadence, egress sockets, the engine poll and sandbox memory floor, MCP listing pages and diagnostics, sandbox helper commands, the scrub threshold, exec event and local stream sizes; and in the engine, `flowValueChars` and `toolResults.minPreviewBytes`.
|
|
77
|
+
- **The CLI's own waits are `NYLORUN_CLI_TUNING`**: `nylorun start`'s waits and polls, the Compose healthchecks and WAL cap, and `nylorun local sandbox`'s waits.
|
|
78
|
+
- **Studio follows the Runtime's limits**: no upload cap of its own, the Tenant's `requestBodyBytes` read for a write it parses past the default (`@nylorun/agents` exports `RUNTIME_LIMIT_DEFAULTS`), no CPU cap in its form.
|
|
79
|
+
- **Kept, each justified where it is defined**: Linux's `MAX_ARG_STRLEN` (a setup command's ceiling) and `PATH_MAX` (a link target), a model-facing tool name's 64 characters, RFC 6838's media type length, Postgres's 65,535 bind parameters, a tmpfs page, and identifier grammars (the protocol's).
|
|
80
|
+
|
|
81
|
+
- 2459430: **An `environment_secret` reads `nylorun_managed` in the sandbox instead of `nylorun-managed`, so CLIs that check a token's format send their requests.**
|
|
82
|
+
|
|
83
|
+
- The Vercel CLI refuses a `VERCEL_TOKEN` with any character outside letters, digits and `_` (`Must not contain: "-"`) and exits before egress-gate can set the header. `ENVIRONMENT_SECRET_SENTINEL` is now `nylorun_managed`.
|
|
84
|
+
- egress-gate never compares the value: it replaces the credential's header whatever the CLI sent, so nothing else changes. A script that checks for the literal `nylorun-managed` should check for `nylorun_managed`.
|
|
85
|
+
|
|
86
|
+
- Pin core to the tested release.
|
|
87
|
+
- Updated dependencies [0a2fbc5]
|
|
88
|
+
- Updated dependencies [ebe7348]
|
|
89
|
+
- Updated dependencies [ff6470e]
|
|
90
|
+
- Updated dependencies [f443475]
|
|
91
|
+
- Updated dependencies [cf425c6]
|
|
92
|
+
- Updated dependencies [23774c4]
|
|
93
|
+
- Updated dependencies [b1e815d]
|
|
94
|
+
- Updated dependencies [2459430]
|
|
95
|
+
- Updated dependencies [f2e9b0a]
|
|
96
|
+
- Updated dependencies [6772612]
|
|
97
|
+
- @nylorun/core@0.23.0-beta
|
|
98
|
+
|
|
3
99
|
## 0.22.0-beta
|
|
4
100
|
|
|
5
101
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -242,9 +242,9 @@ const assistant = Agent({ id: "assistant", name: "Order assistant" })
|
|
|
242
242
|
.skills("./assistant-skills");
|
|
243
243
|
```
|
|
244
244
|
|
|
245
|
-
Each subdirectory under the catalog must contain a `SKILL.md` with YAML frontmatter (`name`, `description`) per [Agent Skills](https://agentskills.io/home). Every file of a skill's folder, binary included (not `.git/`, `node_modules/`, OS files like `.DS_Store`, or `.env` and `.env.*` files, which hold secrets), is part of the definition: the manifest names each one by path and SHA-256 (`skills.<name>.files`)
|
|
245
|
+
Each subdirectory under the catalog must contain a `SKILL.md` with YAML frontmatter (`name`, `description`) per [Agent Skills](https://agentskills.io/home). Every file of a skill's folder, binary included (not `.git/`, `node_modules/`, OS files like `.DS_Store`, or `.env` and `.env.*` files, which hold secrets), is part of the definition: the manifest names each one by path and SHA-256 (`skills.<name>.files`). A skill's files may hold together at most the Tenant's skill limit (`PUT /v1/tenant/skills`, default 2 GiB); no file and no count of files has a limit of its own, and an Environment's manifest may set a lower limit for its sessions (`limits.skills.totalBytes`; a session whose agent's skills do not fit is `409 environment_unsuitable`). `saveAgent` uploads the files the Runtime does not hold (`client.files`, `PUT /v1/files/sha256:<hex>`) before it puts the definition, which the Runtime refuses while it names a file it lacks (`400 definition_files_missing`) or a skill past the Tenant's limit (`413 limit_exceeded`). `read_skill_resource` reads files of up to 10 MiB into the model's context; a larger one is used in the sandbox.
|
|
246
246
|
|
|
247
|
-
The Runtime serves the skills itself, with no call to your process. When
|
|
247
|
+
The Runtime serves the skills itself, with no call to your process. When our harness runs a turn, it adds the skill catalog to the instructions and two tools: `load_skill` returns a skill's `SKILL.md` body and the paths of its other files, and `read_skill_resource` returns a text file. A session with a sandbox also has each skill's files read-only under `/skills/<name>@<hash>/`, so the model can run a skill's scripts with `bash`.
|
|
248
248
|
|
|
249
249
|
Declare MCP servers with `.mcp(...)` (same map shape as agent-plugins `mcpServers`):
|
|
250
250
|
|
|
@@ -310,9 +310,9 @@ await release();
|
|
|
310
310
|
|
|
311
311
|
With a sandbox (kind `virtual` or `pod`), the model gets `bash`, `read`, `write`, `edit`, `grep` and `glob` on a persistent `/workspace`. These tools run in the Runtime, not in your process. A `virtual` Environment is an emulated shell in the Runtime's harness, which is not a VM boundary and takes no `image`; a `pod` runs `image` with its own volume (`storage`) once the Tenant has sandbox pods (`nylorun local sandbox enable`). Kind `none` has a vault and no sandbox, for agents that use only remote MCP servers and HTTP tools. A session that names no Environment has no sandbox and no credentials.
|
|
312
312
|
|
|
313
|
-
An Environment never changes what it runs (protocol 14, its view's `manifest`): a `put` on an existing one may change its `labels` and owner guard, fields that match are accepted (so get-or-create stays one call), and anything else is `409 environment_immutable`. A save of a new manifest version changes no Environment: make a new one, which can start from the old one's disk (`from`). The Runtime checks what an Environment runs against the Tenant's limits (`GET`/`PUT /v1/tenant/sandbox`: a network ceiling, a resource maximum, the idle timeout) and answers `400` with every problem it finds. `lifecycle.idle` stops a pod after that long without a turn (at most the Tenant's); `lifecycle.deleteAfter` deletes the Environment, its vault and its files once nothing has used it that long. Private networks, loopback, the host and cloud metadata endpoints are always blocked.
|
|
313
|
+
An Environment never changes what it runs (protocol 14, its view's `manifest`): a `put` on an existing one may change its `labels` and owner guard, fields that match are accepted (so get-or-create stays one call), and anything else is `409 environment_immutable`. A save of a new manifest version changes no Environment: make a new one, which can start from the old one's disk (`from`). The Runtime checks what an Environment runs against the Tenant's limits (`GET`/`PUT /v1/tenant/sandbox`: a network ceiling, a resource maximum, the idle timeout; `/v1/tenant/artifacts` and `/v1/tenant/skills` for its `limits`) and answers `400` with every problem it finds. `lifecycle.idle` stops a pod after that long without a turn (at most the Tenant's); `lifecycle.deleteAfter` deletes the Environment, its vault and its files once nothing has used it that long. Private networks, loopback, the host and cloud metadata endpoints are always blocked.
|
|
314
314
|
|
|
315
|
-
Secrets are write-only: `client.environments.secrets.put(id, name, secret)` creates or replaces one, and `list`, `get` and `delete` never return a value. An `environment_secret` (`{ value, allowedHosts, inject? }`) shows its name to commands as the sentinel `
|
|
315
|
+
Secrets are write-only: `client.environments.secrets.put(id, name, secret)` creates or replaces one, and `list`, `get` and `delete` never return a value. An `environment_secret` (`{ value, allowedHosts, inject? }`) shows its name to commands as the sentinel `nylorun_managed`, and egress-gate sets its header on HTTPS requests to `allowedHosts` (pods only); an `environment_variable` (`{ value }`) is a plain variable; `bearer` (`{ url, value, via?, identity? }`) and `headers` (`{ url, headers, via?, identity? }`) are credentials for the MCP servers and HTTP tools at `url`. A second secret for one host, or a second credential for one URL, is `409 credential_conflict` at the write ([DEPLOYMENT.md](../../guides/DEPLOYMENT.md#credentials)). When what an Environment runs declares a secret's slot (a manifest's `secrets`), send only `{ value }` (or `{ headers }`): the type and binding come from the slot, and a name it does not declare is `409 secret_not_in_manifest`. An agent built with `.requires({ sandbox: "pod", secrets: ["GH_TOKEN"] })` opens a session only on an Environment that has them (`409 environment_unsuitable`). `saveAgent(agent, { dryRun: true })` checks a definition and saves nothing.
|
|
316
316
|
|
|
317
317
|
Sessions on one Environment share its `/workspace` and run their turns at the same time (one turn per session); which sessions work on one disk, and when, is your app's call. Stop, reset, snapshot and restore wait until no turn runs (`409 environment_busy`). Deleting a session only detaches it. With `ownerUserId` set, a session of another owner is `403 environment_owner_mismatch` (a caller acting for a person gets the `404` of a missing Environment), checked when it opens and at every turn start. `client.environments.list({ labels })`, `page`, `get(id)`, `events(id, { types, since, until })` (its lifecycle, and `environment.egress` for every connection its sandbox opens, naming the turns that were running), `stop(id)`, `reset(id)` and `delete(id)` manage them, and `npx nylorun environments list | delete` does the same from the command line. Acting for a person (`app.as(...)`), changing an Environment or its secrets needs `environments:write`. A trusted issuer's token reaches only the Environments its issuer grants (the identity file's `environments` templates, exact ids or prefixes ending in `/*`), checked at every turn start. The Tenant holds at most `limits.environments` of them (`PUT /v1/tenant/sandbox`, default 100).
|
|
318
318
|
|
|
@@ -638,8 +638,8 @@ What the Runtime answers (Host feature `a2a-endpoint`):
|
|
|
638
638
|
- Text parts, a single data part, or text and `raw` file parts in (at most
|
|
639
639
|
20 MiB each, stored as artifacts of the context's session); the turn's
|
|
640
640
|
output as the artifact `output` (text, or JSON as a data part). A file by
|
|
641
|
-
`url
|
|
642
|
-
|
|
641
|
+
`url` is `ContentTypeNotSupportedError`; a file of any type is accepted, and
|
|
642
|
+
the agent's harness decides how its model sees it.
|
|
643
643
|
- Clients must send `A2A-Version: 1.0` (header or `?A2A-Version=`).
|
|
644
644
|
- Not yet: `ListTasks`, streaming and `SubscribeToTask`
|
|
645
645
|
(`UnsupportedOperationError`; the card says `streaming: false`), push
|
|
@@ -674,8 +674,10 @@ const { url } = await client.artifacts.link(files[0].artifactId); // no credenti
|
|
|
674
674
|
A session with a sandbox gives the agent `save_artifact`, which saves a file it
|
|
675
675
|
made as an artifact of the session; each new artifact or version appears in the
|
|
676
676
|
session's events as `artifact.created` or `artifact.version.created`. The Tenant's
|
|
677
|
-
limits (`PUT /v1/tenant/artifacts`: 100 MiB per file and 10 GiB in all by default)
|
|
678
|
-
|
|
677
|
+
limits (`PUT /v1/tenant/artifacts`: 100 MiB per file and 10 GiB in all by default),
|
|
678
|
+
and those an Environment's manifest sets for its sessions' artifacts (`limits.artifacts`,
|
|
679
|
+
at most the Tenant's), refuse a larger upload with `413 limit_exceeded`, and nothing
|
|
680
|
+
is stored. Acting for
|
|
679
681
|
a person (`as()`, or a trusted issuer's token), a client reaches only the artifacts of that
|
|
680
682
|
person's sessions, and an upload names one of them.
|
|
681
683
|
|
|
@@ -704,11 +706,11 @@ oldest-first behavior and does not require `artifact-reads`.
|
|
|
704
706
|
|
|
705
707
|
### Outputs: folder artifacts
|
|
706
708
|
|
|
707
|
-
At the end of each turn,
|
|
709
|
+
At the end of each turn, our harness saves the files the agent wrote into
|
|
708
710
|
`/workspace/outputs` of its sandbox as a version of the session's **folder**
|
|
709
|
-
artifact `outputs` (kind `folder`): the first
|
|
711
|
+
artifact `outputs` (kind `folder`): the first save creates it, and each later
|
|
710
712
|
turn whose outputs changed adds a version, announced by `artifact.created` or
|
|
711
|
-
`artifact.version.created` with `source: "export"
|
|
713
|
+
`artifact.version.created` with `source: "engine"` (`"export"` before protocol 15). A file that did not change is
|
|
712
714
|
stored once. Read a folder as a tree, one file by path, a diff or a zip:
|
|
713
715
|
|
|
714
716
|
```ts
|
|
@@ -720,8 +722,9 @@ const zip = await client.artifacts.zip(outputs.artifactId); // a streamed Respon
|
|
|
720
722
|
const { url } = await client.artifacts.link(outputs.artifactId, { file: "app/index.html" }); // or the zip without `file`
|
|
721
723
|
```
|
|
722
724
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
+
A save past a limit (the Runtime limit `folderEntries`, 10,000 files by default;
|
|
726
|
+
1 GiB, our harness's own bound; the per-file limit or the Tenant total) stores
|
|
727
|
+
nothing, and our harness records `artifact.export.skipped`; the turn still
|
|
725
728
|
completes.
|
|
726
729
|
|
|
727
730
|
## Acting for a person (app servers)
|
package/dist/environments.d.ts
CHANGED
|
@@ -116,7 +116,7 @@ export type EnvironmentFileBytes = Readonly<Record<string, Uint8Array>>;
|
|
|
116
116
|
/**
|
|
117
117
|
* A manifest `files` entry by content hash (Q6): `contents` (each file's path under `path` and
|
|
118
118
|
* its bytes or text) hashed, as `entry` for the manifest and `bytes` for `manifests.save`'s
|
|
119
|
-
* `files`, which uploads them. Each file holds at most
|
|
119
|
+
* `files`, which uploads them. Each file holds at most 2 GiB.
|
|
120
120
|
*/
|
|
121
121
|
export declare function environmentFiles(path: string, contents: Readonly<Record<string, Uint8Array | string>>): Promise<{
|
|
122
122
|
entry: {
|
|
@@ -233,6 +233,19 @@ export declare class EnvironmentsClient {
|
|
|
233
233
|
path: string;
|
|
234
234
|
files: Record<string, string>;
|
|
235
235
|
})[] | undefined;
|
|
236
|
+
limits?: {
|
|
237
|
+
artifacts?: {
|
|
238
|
+
fileBytes?: number | undefined;
|
|
239
|
+
totalBytes?: number | undefined;
|
|
240
|
+
} | undefined;
|
|
241
|
+
skills?: {
|
|
242
|
+
totalBytes?: number | undefined;
|
|
243
|
+
} | undefined;
|
|
244
|
+
disk?: {
|
|
245
|
+
versionBytes?: number | undefined;
|
|
246
|
+
versionEntries?: number | undefined;
|
|
247
|
+
} | undefined;
|
|
248
|
+
} | undefined;
|
|
236
249
|
secrets?: Record<string, {
|
|
237
250
|
required?: boolean | undefined;
|
|
238
251
|
description?: string | undefined;
|
|
@@ -269,6 +282,19 @@ export declare class EnvironmentsClient {
|
|
|
269
282
|
manifestName?: string | undefined;
|
|
270
283
|
manifestVersion?: number | undefined;
|
|
271
284
|
manifestHash?: string | undefined;
|
|
285
|
+
limits: {
|
|
286
|
+
artifacts: {
|
|
287
|
+
fileBytes: number;
|
|
288
|
+
totalBytes: number;
|
|
289
|
+
};
|
|
290
|
+
skills: {
|
|
291
|
+
totalBytes: number;
|
|
292
|
+
};
|
|
293
|
+
disk: {
|
|
294
|
+
versionBytes: number;
|
|
295
|
+
versionEntries: number;
|
|
296
|
+
};
|
|
297
|
+
};
|
|
272
298
|
forkedFrom?: {
|
|
273
299
|
environment: string;
|
|
274
300
|
disk: number | null;
|
package/dist/environments.js
CHANGED
|
@@ -71,7 +71,7 @@ export class EnvironmentSecretsClient {
|
|
|
71
71
|
/**
|
|
72
72
|
* A manifest `files` entry by content hash (Q6): `contents` (each file's path under `path` and
|
|
73
73
|
* its bytes or text) hashed, as `entry` for the manifest and `bytes` for `manifests.save`'s
|
|
74
|
-
* `files`, which uploads them. Each file holds at most
|
|
74
|
+
* `files`, which uploads them. Each file holds at most 2 GiB.
|
|
75
75
|
*/
|
|
76
76
|
export async function environmentFiles(path, contents) {
|
|
77
77
|
const files = {};
|
package/dist/files.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export declare class FilesClient {
|
|
|
17
17
|
signal?: AbortSignal;
|
|
18
18
|
}): Promise<boolean>;
|
|
19
19
|
/**
|
|
20
|
-
* Uploads one file, at most
|
|
20
|
+
* Uploads one file, at most 2 GiB. The Runtime checks its bytes hash to `sha256` (default:
|
|
21
21
|
* their hash) and keeps a file it already holds as it is.
|
|
22
22
|
*/
|
|
23
23
|
upload(bytes: Uint8Array, options?: {
|
package/dist/files.js
CHANGED
|
@@ -37,7 +37,7 @@ export class FilesClient {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
|
-
* Uploads one file, at most
|
|
40
|
+
* Uploads one file, at most 2 GiB. The Runtime checks its bytes hash to `sha256` (default:
|
|
41
41
|
* their hash) and keeps a file it already holds as it is.
|
|
42
42
|
*/
|
|
43
43
|
async upload(bytes, options = {}) {
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export { AgentBuildError, AgentLifecycleError, VerdictSchema, isVerdict, Workflo
|
|
|
7
7
|
export type { FlowAgentBuilder, Flow, Named, LoopOptions, StageOptions, HttpTarget, } from "@nylorun/core/define";
|
|
8
8
|
export type { AgentOptions, AgentManifest, BuiltAgent, BuiltWorkflow, Verdict, WorkflowBinding, WorkflowManifest, ToolDefinition, ToolExecutionContext, ToolOutcome, JsonValue, JsonObject, } from "@nylorun/core/define";
|
|
9
9
|
export type { SessionPage, SessionListItem, SessionManifestView, HistoryPage, SessionUsageTotals, ModelCall, ModelCallsPage, EnvironmentPage, ToolCall, ToolCallsPage, LiveEvent, SessionEvent, SessionEventOf, EventType, SessionCommand, SubjectScope, EnvironmentView, EnvironmentEvent, EnvironmentEventType, EgressRunningTurn, EnvironmentKind, EnvironmentRecipe, EnvironmentSecretView, EnvironmentSnapshot, EnvironmentRestore, EnvironmentVersions, RestoreEnvironmentRequest, EnvironmentForkPlan, ForkEnvironmentRequest, ForkEnvironmentResponse, EnvironmentManifest, EnvironmentManifestPlan, EnvironmentManifestView, EnvironmentManifestVersions, SaveEnvironmentManifestResponse, MessagePart, ArtifactView, ArtifactKind, ArtifactListItem, ArtifactPage, ArtifactVersionView, ArtifactLink, UploadArtifactResponse, DefinitionFileView, ArtifactTree, ArtifactDiff, FolderEntry, } from "@nylorun/core/contracts";
|
|
10
|
-
export { ENVIRONMENT_EVENT_TYPES, SUBJECT_SCOPES } from "@nylorun/core/contracts";
|
|
10
|
+
export { ENVIRONMENT_EVENT_TYPES, RUNTIME_LIMIT_DEFAULTS, SUBJECT_SCOPES } from "@nylorun/core/contracts";
|
|
11
11
|
export { AgentsClient, SessionClient, createClient } from "./client.js";
|
|
12
12
|
export { ArtifactsClient } from "./artifacts.js";
|
|
13
13
|
export { FilesClient, definitionFileHash } from "./files.js";
|
|
@@ -21,7 +21,7 @@ export { RuntimeError, IncompatibleRuntimeError } from "./http.js";
|
|
|
21
21
|
export type { Destination, IncompatibleReason, TokenSource } from "./http.js";
|
|
22
22
|
export { loadPlugin, PluginError } from "./plugins/load.js";
|
|
23
23
|
export type { LoadedPlugin, PluginDiagnostic } from "./plugins/load.js";
|
|
24
|
-
export { loadSkillsFromDirectory, resolveSkillsRoot, parseSkill, SkillsError,
|
|
24
|
+
export { loadSkillsFromDirectory, resolveSkillsRoot, parseSkill, SkillsError, } from "./skills/index.js";
|
|
25
25
|
export type { SkillDiagnostic } from "./skills/index.js";
|
|
26
26
|
export { McpError } from "./mcp/index.js";
|
|
27
27
|
export type { McpServerSpec } from "./mcp/index.js";
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ export { hashManifest, PROTOCOL_VERSION, PROTOCOL_FEATURES, HOST_PROTOCOL, DEFIN
|
|
|
4
4
|
export { checkHealth, describeIncompatibility, parseProtocolRange, } from "@nylorun/core/transport";
|
|
5
5
|
export { Agent, AgentBuilder } from "./builder.js";
|
|
6
6
|
export { AgentBuildError, AgentLifecycleError, VerdictSchema, isVerdict, WorkflowBuildError, isBuiltWorkflow, flow, tool, http, defineSchema, ToolError, } from "@nylorun/core/define";
|
|
7
|
-
export { ENVIRONMENT_EVENT_TYPES, SUBJECT_SCOPES } from "@nylorun/core/contracts";
|
|
7
|
+
export { ENVIRONMENT_EVENT_TYPES, RUNTIME_LIMIT_DEFAULTS, SUBJECT_SCOPES } from "@nylorun/core/contracts";
|
|
8
8
|
export { AgentsClient, SessionClient, createClient } from "./client.js";
|
|
9
9
|
export { ArtifactsClient } from "./artifacts.js";
|
|
10
10
|
export { FilesClient, definitionFileHash } from "./files.js";
|
|
@@ -13,5 +13,5 @@ export { AccessClient } from "./access.js";
|
|
|
13
13
|
export { resolveConnection, ConnectionError } from "./connection.js";
|
|
14
14
|
export { RuntimeError, IncompatibleRuntimeError } from "./http.js";
|
|
15
15
|
export { loadPlugin, PluginError } from "./plugins/load.js";
|
|
16
|
-
export { loadSkillsFromDirectory, resolveSkillsRoot, parseSkill, SkillsError,
|
|
16
|
+
export { loadSkillsFromDirectory, resolveSkillsRoot, parseSkill, SkillsError, } from "./skills/index.js";
|
|
17
17
|
export { McpError } from "./mcp/index.js";
|
package/dist/session-client.d.ts
CHANGED
|
@@ -242,8 +242,9 @@ export declare class SessionClient {
|
|
|
242
242
|
turnId: string;
|
|
243
243
|
callId: string | null;
|
|
244
244
|
toolName: string | null;
|
|
245
|
-
kind: "harness" | "http" | "mcp" | "
|
|
245
|
+
kind: "harness" | "http" | "mcp" | "sandbox" | null;
|
|
246
246
|
server: string | null;
|
|
247
|
+
tool: string | null;
|
|
247
248
|
skill: string | null;
|
|
248
249
|
plugin: {
|
|
249
250
|
name: string;
|
package/dist/skills/index.d.ts
CHANGED
package/dist/skills/index.js
CHANGED
package/dist/skills/load.d.ts
CHANGED
|
@@ -24,8 +24,9 @@ export declare function resolveSkillsRoot(directory: string): string;
|
|
|
24
24
|
/**
|
|
25
25
|
* Load Agent Skills (agentskills.io) from a catalog directory.
|
|
26
26
|
* Each immediate subdirectory with a valid SKILL.md becomes one skill, with every file of its
|
|
27
|
-
* folder (text or binary) hashed. Throws `SkillsError` for a
|
|
28
|
-
*
|
|
27
|
+
* folder (text or binary) hashed. Throws `SkillsError` for a path a manifest cannot name. How
|
|
28
|
+
* much a skill may hold is the Tenant's limit (`/v1/tenant/skills`, default 2 GiB), which the
|
|
29
|
+
* Runtime checks when the files are uploaded and the agent is saved.
|
|
29
30
|
*/
|
|
30
31
|
export declare function loadSkillsFromDirectory(directory: string, diagnostics: SkillDiagnostic[], options?: {
|
|
31
32
|
readonly boundary?: string;
|
package/dist/skills/load.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { existsSync, readdirSync, readFileSync, realpathSync, statSync, } from "node:fs";
|
|
3
|
-
import {
|
|
2
|
+
import { closeSync, existsSync, openSync, readdirSync, readFileSync, readSync, realpathSync, statSync, } from "node:fs";
|
|
3
|
+
import { open } from "node:fs/promises";
|
|
4
4
|
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
|
-
import {
|
|
5
|
+
import { SKILL_ENTRY, skillFilePathIssue, } from "@nylorun/core/define";
|
|
6
6
|
import matter from "gray-matter";
|
|
7
7
|
const SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
8
8
|
/** Version control and operating system files, never part of a skill. */
|
|
@@ -33,8 +33,9 @@ export function resolveSkillsRoot(directory) {
|
|
|
33
33
|
/**
|
|
34
34
|
* Load Agent Skills (agentskills.io) from a catalog directory.
|
|
35
35
|
* Each immediate subdirectory with a valid SKILL.md becomes one skill, with every file of its
|
|
36
|
-
* folder (text or binary) hashed. Throws `SkillsError` for a
|
|
37
|
-
*
|
|
36
|
+
* folder (text or binary) hashed. Throws `SkillsError` for a path a manifest cannot name. How
|
|
37
|
+
* much a skill may hold is the Tenant's limit (`/v1/tenant/skills`, default 2 GiB), which the
|
|
38
|
+
* Runtime checks when the files are uploaded and the agent is saved.
|
|
38
39
|
*/
|
|
39
40
|
export function loadSkillsFromDirectory(directory, diagnostics, options = {}) {
|
|
40
41
|
const root = resolveSkillsRoot(directory);
|
|
@@ -154,21 +155,50 @@ function readFiles(name, boundary, directory, diagnostics, prefix) {
|
|
|
154
155
|
const issue = skillFilePathIssue(path);
|
|
155
156
|
if (issue)
|
|
156
157
|
throw new SkillsError(`${prefix}.invalid-path`, `Skill '${name}' has a file whose path ${issue}: ${path}`);
|
|
157
|
-
|
|
158
|
-
throw new SkillsError(`${prefix}.file-too-large`, `Skill '${name}' file ${path} is ${stats.size} bytes; a skill file may be at most ${DEFINITION_FILE_MAX_BYTES} (10 MiB)`);
|
|
159
|
-
const hash = `sha256:${createHash("sha256").update(readFileSync(real)).digest("hex")}`;
|
|
158
|
+
const hash = hashFile(real);
|
|
160
159
|
files[path] = hash;
|
|
161
|
-
sources[hash] = {
|
|
162
|
-
size: stats.size,
|
|
163
|
-
read: async () => new Uint8Array(await readFile(real)),
|
|
164
|
-
};
|
|
165
|
-
if (Object.keys(files).length > SKILL_FILES_MAX)
|
|
166
|
-
throw new SkillsError(`${prefix}.too-many-files`, `Skill '${name}' has more than ${SKILL_FILES_MAX} files; a skill may have at most ${SKILL_FILES_MAX}`);
|
|
160
|
+
sources[hash] = { size: stats.size, read: () => readWhole(real, stats.size) };
|
|
167
161
|
}
|
|
168
162
|
};
|
|
169
163
|
walk(directory, "");
|
|
170
164
|
return { files: sorted(files), sources };
|
|
171
165
|
}
|
|
166
|
+
/** Read in chunks: neither the hash nor the read is bound by `readFile`'s 2 GiB limit. */
|
|
167
|
+
const CHUNK_BYTES = 1024 * 1024;
|
|
168
|
+
/** `sha256:<hex>` of the file at `path`, read a chunk at a time. */
|
|
169
|
+
function hashFile(path) {
|
|
170
|
+
const hash = createHash("sha256");
|
|
171
|
+
const chunk = Buffer.allocUnsafe(CHUNK_BYTES);
|
|
172
|
+
const fd = openSync(path, "r");
|
|
173
|
+
try {
|
|
174
|
+
for (let read; (read = readSync(fd, chunk, 0, CHUNK_BYTES, null)) > 0;)
|
|
175
|
+
hash.update(chunk.subarray(0, read));
|
|
176
|
+
}
|
|
177
|
+
finally {
|
|
178
|
+
closeSync(fd);
|
|
179
|
+
}
|
|
180
|
+
return `sha256:${hash.digest("hex")}`;
|
|
181
|
+
}
|
|
182
|
+
/** The `size` bytes of the file at `path`. */
|
|
183
|
+
async function readWhole(path, size) {
|
|
184
|
+
const bytes = new Uint8Array(size);
|
|
185
|
+
const file = await open(path, "r");
|
|
186
|
+
try {
|
|
187
|
+
let at = 0;
|
|
188
|
+
while (at < size) {
|
|
189
|
+
const { bytesRead } = await file.read(bytes, at, Math.min(CHUNK_BYTES, size - at), at);
|
|
190
|
+
if (bytesRead === 0)
|
|
191
|
+
break;
|
|
192
|
+
at += bytesRead;
|
|
193
|
+
}
|
|
194
|
+
if (at !== size)
|
|
195
|
+
throw new Error(`${path} changed while it was read: ${at} of ${size} bytes`);
|
|
196
|
+
}
|
|
197
|
+
finally {
|
|
198
|
+
await file.close();
|
|
199
|
+
}
|
|
200
|
+
return bytes;
|
|
201
|
+
}
|
|
172
202
|
function sorted(files) {
|
|
173
203
|
return Object.fromEntries(Object.entries(files).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)));
|
|
174
204
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nylorun/agents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0-beta",
|
|
4
4
|
"description": "Nylorun agent definitions and session client",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"test": "vitest run --passWithNoTests"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@nylorun/core": "0.
|
|
46
|
+
"@nylorun/core": "0.23.0-beta",
|
|
47
47
|
"gray-matter": "^4.0.3"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|