@nylorun/runtime 0.24.0-beta → 0.26.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.
Files changed (171) hide show
  1. package/CHANGELOG.md +143 -0
  2. package/README.md +18 -4
  3. package/dist/api/a2a/card.d.ts +11 -1
  4. package/dist/api/a2a/card.js +26 -2
  5. package/dist/api/a2a/endpoint.js +4 -2
  6. package/dist/api/a2a/protocol.d.ts +25 -2
  7. package/dist/api/a2a/protocol.js +20 -3
  8. package/dist/api/a2a/routes.d.ts +4 -1
  9. package/dist/api/a2a/routes.js +65 -7
  10. package/dist/api/ag-ui/endpoint.js +5 -2
  11. package/dist/api/ag-ui/history.d.ts +6 -1
  12. package/dist/api/ag-ui/history.js +35 -2
  13. package/dist/api/ag-ui/routes.js +53 -11
  14. package/dist/api/components.d.ts +1165 -678
  15. package/dist/api/components.js +16 -1
  16. package/dist/api/gate/keys.d.ts +2 -1
  17. package/dist/api/gate/keys.js +7 -0
  18. package/dist/api/gate/routes.js +13 -0
  19. package/dist/api/http/app.d.ts +2 -2
  20. package/dist/api/http/app.js +15 -4
  21. package/dist/api/http/body.d.ts +3 -3
  22. package/dist/api/http/body.js +7 -7
  23. package/dist/api/http/define.d.ts +1 -1
  24. package/dist/api/http/respond.js +1 -1
  25. package/dist/api/http/routes/environment-manifests.d.ts +9 -0
  26. package/dist/api/http/routes/environment-manifests.js +118 -0
  27. package/dist/api/http/routes/environments.js +61 -12
  28. package/dist/api/http/routes/reads.d.ts +52 -3
  29. package/dist/api/http/routes/sessions.js +14 -4
  30. package/dist/api/http/routes/tenant.js +35 -7
  31. package/dist/api/inbound-files.d.ts +65 -0
  32. package/dist/api/inbound-files.js +115 -0
  33. package/dist/api/openapi.js +18 -3
  34. package/dist/artifacts/disk.js +20 -14
  35. package/dist/artifacts/media-types.d.ts +15 -0
  36. package/dist/artifacts/media-types.js +56 -4
  37. package/dist/artifacts/parts.d.ts +12 -3
  38. package/dist/artifacts/parts.js +15 -4
  39. package/dist/artifacts/workspace.d.ts +4 -4
  40. package/dist/artifacts/workspace.js +6 -18
  41. package/dist/core/flow-host.d.ts +1 -1
  42. package/dist/core/flow-host.js +2 -2
  43. package/dist/core/sandbox-routes.js +2 -2
  44. package/dist/core/turn-manifest.d.ts +3 -2
  45. package/dist/core/turn-manifest.js +29 -5
  46. package/dist/gates/egress-credentials.js +1 -1
  47. package/dist/gates/egress.d.ts +13 -19
  48. package/dist/gates/egress.js +32 -29
  49. package/dist/gates/http-tool.d.ts +3 -4
  50. package/dist/gates/http-tool.js +23 -19
  51. package/dist/gates/journal.js +0 -1
  52. package/dist/gates/mcp-handler.js +2 -3
  53. package/dist/gates/policy.js +1 -3
  54. package/dist/gates/tool-contract.d.ts +1 -1
  55. package/dist/harness/calls.d.ts +2 -2
  56. package/dist/harness/calls.js +9 -12
  57. package/dist/harness/main.js +7 -1
  58. package/dist/harness/pod.d.ts +6 -1
  59. package/dist/harness/pod.js +5 -0
  60. package/dist/harness/service.js +5 -5
  61. package/dist/harness/setup.d.ts +2 -0
  62. package/dist/harness/setup.js +25 -1
  63. package/dist/harness-api/record.d.ts +1 -1
  64. package/dist/harness-api/record.js +13 -13
  65. package/dist/harness-api/server.d.ts +27 -4
  66. package/dist/harness-api/server.js +41 -8
  67. package/dist/harness-api/tool-journal.d.ts +17 -8
  68. package/dist/harness-api/tool-journal.js +41 -11
  69. package/dist/harness-api/workspace.d.ts +7 -5
  70. package/dist/harness-api/workspace.js +25 -19
  71. package/dist/host/gates.js +2 -2
  72. package/dist/host/stack-config.js +1 -1
  73. package/dist/keys/contract.d.ts +1 -1
  74. package/dist/keys/contract.js +1 -1
  75. package/dist/keys/keys.d.ts +9 -2
  76. package/dist/keys/keys.js +4 -1
  77. package/dist/management-openapi.json +366 -61
  78. package/dist/mcp/connect.d.ts +1 -2
  79. package/dist/mcp/connect.js +1 -4
  80. package/dist/mcp/pool.d.ts +1 -4
  81. package/dist/mcp/pool.js +13 -27
  82. package/dist/mcp/preview.d.ts +1 -1
  83. package/dist/mcp/preview.js +1 -2
  84. package/dist/mcp/snapshot.d.ts +11 -13
  85. package/dist/mcp/snapshot.js +27 -38
  86. package/dist/model/capabilities.d.ts +26 -0
  87. package/dist/model/capabilities.js +44 -0
  88. package/dist/model/catalog.d.ts +34 -3
  89. package/dist/model/catalog.js +38 -2
  90. package/dist/model/models.d.ts +2 -0
  91. package/dist/model/models.js +1 -1
  92. package/dist/model/native-blocks.d.ts +35 -0
  93. package/dist/model/native-blocks.js +170 -0
  94. package/dist/model/pi-model.js +106 -94
  95. package/dist/openapi.json +9554 -5076
  96. package/dist/record/environment.js +1 -1
  97. package/dist/sandbox/join.js +4 -0
  98. package/dist/sandbox/manager.d.ts +32 -14
  99. package/dist/sandbox/manager.js +132 -93
  100. package/dist/sandbox/placement.d.ts +1 -1
  101. package/dist/sandbox/placement.js +2 -2
  102. package/dist/sandbox/pods/client.d.ts +3 -1
  103. package/dist/sandbox/pods/lifecycle.d.ts +8 -0
  104. package/dist/sandbox/pods/lifecycle.js +15 -0
  105. package/dist/sandbox/pods/spec.js +2 -1
  106. package/dist/sandbox/resolve.d.ts +5 -3
  107. package/dist/sandbox/resolve.js +9 -2
  108. package/dist/sandbox/runtime-tools.d.ts +9 -0
  109. package/dist/sandbox/runtime-tools.js +16 -0
  110. package/dist/sandbox/session-sandbox.d.ts +4 -6
  111. package/dist/sandbox/session-sandbox.js +47 -94
  112. package/dist/sandbox/setup.d.ts +4 -1
  113. package/dist/sandbox/setup.js +46 -9
  114. package/dist/sandbox/share.d.ts +12 -7
  115. package/dist/sandbox/share.js +27 -25
  116. package/dist/sandbox/skills.d.ts +8 -6
  117. package/dist/sandbox/skills.js +24 -23
  118. package/dist/store/postgres/drizzle/0024_flat_manifest.sql +7 -0
  119. package/dist/store/postgres/drizzle/0025_environment_manifests.sql +14 -0
  120. package/dist/store/postgres/drizzle/0026_environment_forks.sql +1 -0
  121. package/dist/store/postgres/drizzle/0027_environment_image_digest.sql +1 -0
  122. package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
  123. package/dist/store/postgres/migrate.js +1 -1
  124. package/dist/store/postgres/reads.js +8 -2
  125. package/dist/store/postgres/schema.d.ts +217 -0
  126. package/dist/store/postgres/schema.js +36 -2
  127. package/dist/store/postgres/store.js +79 -4
  128. package/dist/store/types.d.ts +68 -14
  129. package/dist/tenant/advance.js +3 -0
  130. package/dist/tenant/artifact-tool.d.ts +1 -1
  131. package/dist/tenant/artifact-tool.js +43 -22
  132. package/dist/tenant/commands.js +24 -4
  133. package/dist/tenant/context.d.ts +10 -4
  134. package/dist/tenant/effects.js +7 -9
  135. package/dist/tenant/environment-manifests.d.ts +57 -0
  136. package/dist/tenant/environment-manifests.js +200 -0
  137. package/dist/tenant/environments.d.ts +54 -47
  138. package/dist/tenant/environments.js +309 -85
  139. package/dist/tenant/ephemeral.d.ts +1 -1
  140. package/dist/tenant/host-token.js +1 -1
  141. package/dist/tenant/identity-file.d.ts +1 -1
  142. package/dist/tenant/identity-file.js +1 -1
  143. package/dist/tenant/model-setting.d.ts +14 -0
  144. package/dist/tenant/model-setting.js +51 -0
  145. package/dist/tenant/outbound.d.ts +6 -5
  146. package/dist/tenant/outbound.js +8 -6
  147. package/dist/tenant/sessions.d.ts +5 -1
  148. package/dist/tenant/sessions.js +45 -12
  149. package/dist/tenant/skill-tool.d.ts +3 -3
  150. package/dist/tenant/skill-tool.js +6 -11
  151. package/dist/tenant/sweep.js +1 -1
  152. package/dist/tenant/tool-results.js +1 -1
  153. package/dist/tenant/tool-search.d.ts +7 -11
  154. package/dist/tenant/tool-search.js +8 -5
  155. package/dist/tenant/transcript.d.ts +51 -32
  156. package/dist/tenant/transcript.js +2 -1
  157. package/dist/tenant/types.d.ts +2 -2
  158. package/dist/traces/ids.d.ts +0 -1
  159. package/dist/traces/ids.js +0 -4
  160. package/dist/traces/map.d.ts +2 -2
  161. package/dist/traces/map.js +1 -29
  162. package/dist/vault/coverage.js +11 -13
  163. package/dist/vault/host-model.d.ts +4 -1
  164. package/dist/vault/host-model.js +10 -2
  165. package/dist/vault/service.d.ts +49 -4
  166. package/dist/vault/service.js +153 -6
  167. package/dist/version.d.ts +1 -1
  168. package/dist/version.js +1 -1
  169. package/package.json +3 -3
  170. package/dist/sandbox/capability.d.ts +0 -3
  171. package/dist/sandbox/capability.js +0 -10
@@ -1,3 +1,17 @@
1
+ /**
2
+ * The Tenant's model settings: the fixture model, and the inputs the operator added to catalog
3
+ * models (`model.inputs`, below).
4
+ *
5
+ * The Tenant-level fixture model: Tenant setting `model.fixture`, seeded with
6
+ * `fixtureModel: true` (`PUT /v1/tenant/config/seed`, Host feature `tenant-fixture-model`).
7
+ * A Tenant with it answers every model call with the Runtime's deterministic fixture model
8
+ * (`core/provider.ts` `toolFixtureModel`) instead of its configured model. Smoke checks seed
9
+ * it (scripts/lib/stack-tenant.mjs).
10
+ *
11
+ * The setting is read once per advance, in a transaction the advance makes anyway, so a Tenant
12
+ * seeded on another node takes it at its next advance.
13
+ */
14
+ import { ModelInputSchema } from "@nylorun/core/contracts";
1
15
  export const FIXTURE_MODEL_SETTING = "model.fixture";
2
16
  /** Whether the Tenant uses the fixture model. */
3
17
  export async function usesFixtureModel(t) {
@@ -10,3 +24,40 @@ export async function seedFixtureModel(t) {
10
24
  await t.putSetting(FIXTURE_MODEL_SETTING, "true");
11
25
  return true;
12
26
  }
27
+ /**
28
+ * The inputs the operator added to catalog models (protocol 12, `PUT /v1/tenant/models/inputs`):
29
+ * Tenant setting `model.inputs`, JSON `{ "<provider>/<model>": ModelInput[] }`. A custom
30
+ * endpoint keeps its own in its `settings.inputs` instead.
31
+ */
32
+ export const MODEL_INPUTS_SETTING = "model.inputs";
33
+ /** The added inputs by `<provider>/<model>`. Bad JSON, keys and inputs are left out: never throws. */
34
+ export async function readAddedInputs(t) {
35
+ const raw = await t.getSetting(MODEL_INPUTS_SETTING);
36
+ if (raw === undefined)
37
+ return {};
38
+ let parsed;
39
+ try {
40
+ parsed = JSON.parse(raw);
41
+ }
42
+ catch {
43
+ return {};
44
+ }
45
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
46
+ return {};
47
+ const added = {};
48
+ for (const [key, value] of Object.entries(parsed)) {
49
+ if (!key.includes("/") || !Array.isArray(value))
50
+ continue;
51
+ added[key] = value.filter((input) => ModelInputSchema.safeParse(input).success);
52
+ }
53
+ return added;
54
+ }
55
+ /** Sets the inputs added to `<provider>/<model>`; an empty list removes its entry. */
56
+ export async function writeAddedInputs(t, key, inputs) {
57
+ const added = await readAddedInputs(t);
58
+ if (inputs.length === 0)
59
+ delete added[key];
60
+ else
61
+ added[key] = [...inputs];
62
+ await t.putSetting(MODEL_INPUTS_SETTING, JSON.stringify(added));
63
+ }
@@ -37,14 +37,15 @@ export declare function isPrivateAddress(address: string): boolean;
37
37
  /** Why the Host refuses `url`, or `undefined` when it may be called. */
38
38
  export declare function refusal(url: URL, policy: OutboundPolicy): string | undefined;
39
39
  /**
40
- * POSTs (or, with `method`, PUTs or PATCHes) the JSON `body` to `url` under `policy`. Never
40
+ * Sends one request to `url` under `policy`: with `method` (default `POST`) and the JSON `body`,
41
+ * or with no body (and no `content-type`) when `body` is undefined, as for a `GET`. Never
41
42
  * throws; `signal` aborts the request.
42
43
  */
43
- export declare function post(target: string, body: string, headers: Record<string, string>, options: {
44
+ export declare function send(target: string, body: string | undefined, headers: Record<string, string>, options: {
44
45
  policy: OutboundPolicy;
45
46
  signal: AbortSignal;
46
47
  maxResponseBytes?: number;
47
- method?: "POST" | "PUT" | "PATCH";
48
+ method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
48
49
  }): Promise<OutboundResult>;
49
50
  /**
50
51
  * A refusal of `guardedFetch`: the Host's policy forbids the URL or every address it resolves
@@ -59,13 +60,13 @@ export declare class OutboundRefused extends Error {
59
60
  export declare class OutboundFailed extends Error {
60
61
  readonly code: string;
61
62
  /**
62
- * The request was sent in full before it failed, as `post()`'s `lost` (R2b C7): the server
63
+ * The request was sent in full before it failed, as `send()`'s `lost` (R2b C7): the server
63
64
  * may have acted on it. False for a failure to resolve or connect.
64
65
  */
65
66
  readonly sent: boolean;
66
67
  constructor(message: string, code: string,
67
68
  /**
68
- * The request was sent in full before it failed, as `post()`'s `lost` (R2b C7): the server
69
+ * The request was sent in full before it failed, as `send()`'s `lost` (R2b C7): the server
69
70
  * may have acted on it. False for a failure to resolve or connect.
70
71
  */
71
72
  sent?: boolean);
@@ -76,10 +76,11 @@ function guardedLookup(policy) {
76
76
  });
77
77
  }
78
78
  /**
79
- * POSTs (or, with `method`, PUTs or PATCHes) the JSON `body` to `url` under `policy`. Never
79
+ * Sends one request to `url` under `policy`: with `method` (default `POST`) and the JSON `body`,
80
+ * or with no body (and no `content-type`) when `body` is undefined, as for a `GET`. Never
80
81
  * throws; `signal` aborts the request.
81
82
  */
82
- export async function post(target, body, headers, options) {
83
+ export async function send(target, body, headers, options) {
83
84
  const { policy, signal } = options;
84
85
  const limit = options.maxResponseBytes ?? MAX_RESPONSE_BYTES;
85
86
  const url = new URL(target);
@@ -108,8 +109,9 @@ export async function post(target, body, headers, options) {
108
109
  headers: {
109
110
  ...headers,
110
111
  host: originalHost,
111
- "content-type": "application/json",
112
- "content-length": String(Buffer.byteLength(body)),
112
+ ...(body === undefined
113
+ ? {}
114
+ : { "content-type": "application/json", "content-length": String(Buffer.byteLength(body)) }),
113
115
  },
114
116
  lookup: lookup,
115
117
  signal,
@@ -156,7 +158,7 @@ export class OutboundFailed extends Error {
156
158
  sent;
157
159
  constructor(message, code,
158
160
  /**
159
- * The request was sent in full before it failed, as `post()`'s `lost` (R2b C7): the server
161
+ * The request was sent in full before it failed, as `send()`'s `lost` (R2b C7): the server
160
162
  * may have acted on it. False for a failure to resolve or connect.
161
163
  */
162
164
  sent = false) {
@@ -223,7 +225,7 @@ export function guardedFetch(policy, options = {}) {
223
225
  outgoing["content-length"] = String(body.byteLength);
224
226
  return await new Promise((resolve, reject) => {
225
227
  let settled = false;
226
- // Set when the request is fully sent, as `post()` does: a failure after it may follow
228
+ // Set when the request is fully sent, as `send()` does: a failure after it may follow
227
229
  // the server acting on the request.
228
230
  let delivered = false;
229
231
  const settle = (fn) => {
@@ -25,10 +25,14 @@ export declare function listAgentsPublic(ctx: TenantContext, agents: ReadonlySet
25
25
  description?: any;
26
26
  }[];
27
27
  }>;
28
- export declare function putDefinition(ctx: TenantContext, agentId: string, body: PutAgentRequest): Promise<{
28
+ export declare function putDefinition(ctx: TenantContext, agentId: string, body: PutAgentRequest, options?: {
29
+ /** Run every check and save nothing (`?dryRun=true`, protocol 14). */
30
+ dryRun?: boolean;
31
+ }): Promise<{
29
32
  agentId: string;
30
33
  manifestHash: string;
31
34
  implementationVersion: string;
35
+ dryRun?: boolean | undefined;
32
36
  }>;
33
37
  /**
34
38
  * `access`, when the request acts for a person, limits the list to that person's sessions of
@@ -9,8 +9,8 @@ import { approvalRefusal, codeToolRefusal } from "@nylorun/core/define";
9
9
  import { aggregateWaits, isWorkflowManifest } from "../core/flow-host.js";
10
10
  import { canonical } from "../store/canonical.js";
11
11
  import { sandboxSpecOf } from "../sandbox/resolve.js";
12
- import { withPlatformTools, withSandboxCapability } from "../sandbox/session-sandbox.js";
13
- import { declaredSandboxes } from "../sandbox/share.js";
12
+ import { withRuntimeTools, withSandbox } from "../sandbox/session-sandbox.js";
13
+ import { authoredRuntimeBlocks, declaredSandboxes } from "../sandbox/share.js";
14
14
  import { effectiveSandboxConfig, readSandboxConfig } from "../sandbox/tenant-config.js";
15
15
  import { HARNESS_ID, checkPlacement, requirePods } from "../sandbox/placement.js";
16
16
  import { openEnvironment, recordAttachment } from "./environments.js";
@@ -51,7 +51,7 @@ export async function listAgentsPublic(ctx, agents) {
51
51
  })),
52
52
  };
53
53
  }
54
- export async function putDefinition(ctx, agentId, body) {
54
+ export async function putDefinition(ctx, agentId, body, options = {}) {
55
55
  if (body.manifest.id !== agentId)
56
56
  fail(400, "Agent id mismatch");
57
57
  DefinitionDocumentSchema.parse(body.manifest);
@@ -66,6 +66,9 @@ export async function putDefinition(ctx, agentId, body) {
66
66
  const declared = declaredSandboxes(body.manifest);
67
67
  if (declared.length > 0)
68
68
  fail(400, `${declared.join(", ")} declares a sandbox. Agents no longer declare one: remove .sandbox(), create an Environment with the sandbox (PUT /v1/environments/{id}) and open the session on it, createSession({ environment }). See MIGRATION.md.`);
69
+ const runtime = authoredRuntimeBlocks(body.manifest);
70
+ if (runtime.length > 0)
71
+ fail(400, `${runtime.join(", ")} sets runtime, which only the Runtime writes when it pins a session's agent. Remove it and register the manifest your builder makes.`);
69
72
  const definition = {
70
73
  ...body,
71
74
  manifestHash: hashManifest(body.manifest),
@@ -75,6 +78,8 @@ export async function putDefinition(ctx, agentId, body) {
75
78
  const missing = await missingDefinitionFiles(t, body.manifest);
76
79
  if (missing.length > 0)
77
80
  fail(400, `The definition names ${missing.length} file(s) the Runtime does not hold; upload each with PUT /v1/files/<sha256> first: ${missing.join(", ")}`, { code: "definition_files_missing", details: { missing } });
81
+ if (options.dryRun)
82
+ return;
78
83
  await t.put("definitions", agentId, definition);
79
84
  await t.insertDefinitionFileUses(agentId, definition.manifestHash, files);
80
85
  });
@@ -82,6 +87,7 @@ export async function putDefinition(ctx, agentId, body) {
82
87
  agentId,
83
88
  manifestHash: definition.manifestHash,
84
89
  implementationVersion: body.implementationVersion,
90
+ ...(options.dryRun ? { dryRun: true } : {}),
85
91
  };
86
92
  }
87
93
  /**
@@ -150,6 +156,8 @@ export async function putSession(ctx, id, body, access, options = {}) {
150
156
  ownerUserId: body.ownerUserId,
151
157
  opaque,
152
158
  });
159
+ if (!isWorkflowManifest(definition.manifest))
160
+ await checkRequirements(t, definition.manifest, environment);
153
161
  const config = await readSandboxConfig(t);
154
162
  // The harness the session runs for its life (R4.1): its Environment's, else ours. A flow
155
163
  // runs in core and our engine, whatever its Environment names.
@@ -163,7 +171,7 @@ export async function putSession(ctx, id, body, access, options = {}) {
163
171
  const spec = environment === undefined || environment.kind === "none"
164
172
  ? undefined
165
173
  : sandboxSpecOf(environment.applied, effectiveSandboxConfig(config).limits.idle);
166
- const pinned = spec === undefined ? platformTools(definition) : pin(definition, spec);
174
+ const pinned = spec === undefined ? runtimeTools(definition) : pin(definition, spec);
167
175
  const created = {
168
176
  id,
169
177
  agentId: body.agentId,
@@ -189,25 +197,50 @@ export async function putSession(ctx, id, body, access, options = {}) {
189
197
  });
190
198
  }
191
199
  /**
192
- * The manifest a session without a sandbox pins: its agents' `read_artifact` (R2b C11), when
193
- * one has a remote MCP server or an HTTP tool; nothing otherwise, and for a workflow.
200
+ * Refuses a session whose agent requires what its Environment lacks (protocol 14, D65): a
201
+ * sandbox (`any`: kind `virtual` or `pod`), a pod, or a secret its vault does not hold. Every
202
+ * gap is named in one `409 environment_unsuitable`.
194
203
  */
195
- function platformTools(definition) {
204
+ async function checkRequirements(t, manifest, environment) {
205
+ const requires = manifest.requires;
206
+ if (requires === undefined)
207
+ return;
208
+ const missing = [];
209
+ const kind = environment?.kind;
210
+ if (requires.sandbox === "any" && kind !== "virtual" && kind !== "pod")
211
+ missing.push("sandbox");
212
+ if (requires.sandbox === "pod" && kind !== "pod")
213
+ missing.push("pod");
214
+ if (requires.secrets?.length) {
215
+ const vault = environment === undefined ? undefined : await t.vaultOfEnvironment(environment.id);
216
+ const held = new Set(vault ? (await t.credentialsForVault(vault.id)).map((row) => row.name) : []);
217
+ for (const name of requires.secrets)
218
+ if (!held.has(name))
219
+ missing.push(`secret:${name}`);
220
+ }
221
+ if (missing.length === 0)
222
+ return;
223
+ const where = environment === undefined ? "the session names no Environment" : `Environment ${environment.id} lacks it`;
224
+ fail(409, `Agent ${manifest.id} requires ${missing.map((gap) => (gap.startsWith("secret:") ? `the secret ${gap.slice(7)}` : gap === "pod" ? "a pod Environment" : "a sandbox")).join(", ")}, and ${where}.`, { code: "environment_unsuitable", details: { missing } });
225
+ }
226
+ /**
227
+ * The manifest a session without a sandbox pins: each agent's `runtime` block, with
228
+ * `read_artifact` (R2b C11) and its skill tools; nothing for a workflow.
229
+ */
230
+ function runtimeTools(definition) {
196
231
  if (isWorkflowManifest(definition.manifest))
197
232
  return {};
198
- const pinned = withPlatformTools(definition.manifest);
199
- if (pinned === undefined)
200
- return {};
233
+ const pinned = withRuntimeTools(definition.manifest);
201
234
  if (!pinned.ok)
202
235
  return fail(400, pinned.message);
203
236
  return { manifest: pinned.manifest, manifestHash: pinned.manifestHash };
204
237
  }
205
- /** The manifest a session with a sandbox pins: the definition with the sandbox capability. */
238
+ /** The manifest a session with a sandbox pins: the definition with the sandbox in its `runtime` block. */
206
239
  function pin(definition, spec) {
207
240
  // A workflow's manifest stays as registered: its agents inherit the sandbox when they start.
208
241
  if (isWorkflowManifest(definition.manifest))
209
242
  return {};
210
- const sandboxed = withSandboxCapability(definition.manifest, spec);
243
+ const sandboxed = withSandbox(definition.manifest, spec);
211
244
  if (!sandboxed.ok)
212
245
  return fail(400, sandboxed.message);
213
246
  return { manifest: sandboxed.manifest, manifestHash: sandboxed.manifestHash };
@@ -1,11 +1,11 @@
1
1
  /**
2
- * The skill tools (track R2 M4): `load_skill` and `read_skill_resource`, which the build gives
3
- * the first capability with skills. The Runtime runs them itself, like `save_artifact`, from the
2
+ * The skill tools (track R2 M4): `load_skill` and `read_skill_resource`, which the Runtime pins
3
+ * in the `runtime` block of an agent with skills. The Runtime runs them itself, like `save_artifact`, from the
4
4
  * definition files uploaded with the agent: no developer process is involved.
5
5
  *
6
6
  * - `load_skill`: the skill's instructions (its `SKILL.md` after the frontmatter), the paths of
7
7
  * its other files, and, when the session has a sandbox, where they are in it
8
- * (`/skills/<name>/`, read-only).
8
+ * (`/skills/<name>@<hash>/`, read-only, D66).
9
9
  * - `read_skill_resource`: one text file of the skill. A binary file is refused: the model reads
10
10
  * it in the sandbox, when there is one.
11
11
  *
@@ -1,19 +1,14 @@
1
- import { LOAD_SKILL_TOOL, SANDBOX_CAPABILITY_ID, SKILL_ENTRY, SKILLS_MOUNT, isSkillTool, skillInstructions, } from "@nylorun/core/define";
2
- import { sandboxCapabilityOf } from "../sandbox/capability.js";
1
+ import { LOAD_SKILL_TOOL, SKILL_ENTRY, skillFolderOf, isSkillTool, skillInstructions, } from "@nylorun/core/define";
2
+ import { sandboxToolSpec } from "../sandbox/runtime-tools.js";
3
3
  import { readDefinitionFile } from "./definition-files.js";
4
4
  const failed = (code, message) => ({ kind: "failed", code, message });
5
5
  /** True when `request` calls a skill tool the Runtime serves for `manifest`'s agent. */
6
6
  export function isSkillToolCall(manifest, request) {
7
- return (request.kind === "tool" &&
8
- isSkillTool(manifest?.capabilities.find((capability) => capability.id === request.capabilityId), request.toolName));
7
+ return request.kind === "tool" && isSkillTool(manifest, request.toolName);
9
8
  }
10
9
  /** Every skill of the agent, by name. */
11
10
  function skillsOf(manifest) {
12
- const skills = new Map();
13
- for (const capability of manifest.capabilities)
14
- for (const skill of Object.values(capability.skills ?? {}))
15
- skills.set(skill.name, skill);
16
- return skills;
11
+ return new Map(Object.values(manifest.skills ?? {}).map((skill) => [skill.name, skill]));
17
12
  }
18
13
  /** UTF-8 text, or undefined for bytes that are not (a NUL byte, or invalid UTF-8). */
19
14
  function textOf(bytes) {
@@ -35,8 +30,8 @@ export async function callSkillTool(ctx, manifest, request, signal) {
35
30
  return request.toolName === LOAD_SKILL_TOOL
36
31
  ? { kind: "completed", output: { unknown: name, available: [...skills.keys()].sort() } }
37
32
  : failed("skill.unknown", `There is no skill '${name}'.`);
38
- const sandboxed = sandboxCapabilityOf(manifest, SANDBOX_CAPABILITY_ID, "bash") !== undefined;
39
- const directory = `${SKILLS_MOUNT}/${skill.name}/`;
33
+ const sandboxed = sandboxToolSpec(manifest, "bash") !== undefined;
34
+ const directory = `${skillFolderOf(skill)}/`;
40
35
  const read = async (path) => {
41
36
  const bytes = await readDefinitionFile(ctx.blobs, skill.files[path], signal);
42
37
  if (!bytes)
@@ -29,7 +29,7 @@
29
29
  * wiped (§17.11), or a parked outbox row.
30
30
  * 4. **Sandboxes.** Idle sandboxes are stopped, records of compute no longer held are marked
31
31
  * stopped, and sandboxes whose session or Environment is gone are removed, by the
32
- * workspace capability (`harness-api/workspace.ts`: here, or in the harness that serves
32
+ * workspace feature (`harness-api/workspace.ts`: here, or in the harness that serves
33
33
  * workspaces). Idle MCP connections of the in-process harness are closed. As often as the
34
34
  * orphan scan, pod Environments whose change stalled get a reconcile (`reconcileStalePods`).
35
35
  * Environments unused for their `lifecycle.deleteAfter` are deleted
@@ -170,7 +170,7 @@ async function shapeBinary(shaping, part, index) {
170
170
  kept.size <= MODEL_FILE_MAX_BYTES)
171
171
  shaping.files.push({
172
172
  mediaType: contentType,
173
- reference: { artifactId: kept.artifactId, version: kept.version, name },
173
+ reference: { artifactId: kept.artifactId, version: kept.version, name, size: kept.size },
174
174
  });
175
175
  return {
176
176
  type: part.type,
@@ -1,16 +1,12 @@
1
- /**
2
- * `tool_search` (R2b C10, Q20): core finds an agent's deferred MCP tools in the session's pinned
3
- * snapshot, ranked with BM25 over their names and descriptions, and returns each match's name,
4
- * description and `inputSchema`, for the model to run with `tool_call`. It reads only the pinned
5
- * snapshot, so the same query finds the same tools for the session's life; a tool the turn's
6
- * manifest disables is never found.
7
- */
8
- import { type AgentManifest } from "@nylorun/core/define";
9
1
  import type { HostEffect } from "@nylorun/harness/run";
10
- import { type McpToolRecord } from "../mcp/snapshot.js";
2
+ import { type McpSnapshot, type McpToolRecord } from "../mcp/snapshot.js";
11
3
  import { type TenantContext } from "./context.js";
12
- /** True when `request` calls `tool_search` of the `nylorun.tools` capability `manifest` has. */
13
- export declare function isToolSearchCall(manifest: AgentManifest | undefined, request: HostEffect): boolean;
4
+ /**
5
+ * True when `request` calls `tool_search`, the session tool an agent gets when the session's
6
+ * pinned snapshot holds deferred tools of its own. The name is reserved: no tool of the agent's
7
+ * own takes it.
8
+ */
9
+ export declare function isToolSearchCall(snapshot: McpSnapshot | undefined, request: HostEffect): boolean;
14
10
  /** Runs `tool_search` for the run's agent. */
15
11
  export declare function callToolSearch(ctx: TenantContext, request: HostEffect): Promise<{
16
12
  output?: undefined;
@@ -5,19 +5,22 @@
5
5
  * snapshot, so the same query finds the same tools for the session's life; a tool the turn's
6
6
  * manifest disables is never found.
7
7
  */
8
- import { TOOL_SEARCH_DEFAULT_LIMIT, TOOL_SEARCH_MAX_LIMIT, TOOL_SEARCH_TOOL, TOOLS_CAPABILITY_ID, } from "@nylorun/core/define";
8
+ import { TOOL_SEARCH_DEFAULT_LIMIT, TOOL_SEARCH_MAX_LIMIT, TOOL_SEARCH_TOOL, } from "@nylorun/core/define";
9
9
  import { isEnabledIn } from "../mcp/snapshot.js";
10
10
  import { sessionOf } from "./context.js";
11
11
  import { turnManifestOf } from "./session.js";
12
12
  /** BM25's term saturation and length normalization, the usual values. */
13
13
  const K1 = 1.2;
14
14
  const B = 0.75;
15
- /** True when `request` calls `tool_search` of the `nylorun.tools` capability `manifest` has. */
16
- export function isToolSearchCall(manifest, request) {
15
+ /**
16
+ * True when `request` calls `tool_search`, the session tool an agent gets when the session's
17
+ * pinned snapshot holds deferred tools of its own. The name is reserved: no tool of the agent's
18
+ * own takes it.
19
+ */
20
+ export function isToolSearchCall(snapshot, request) {
17
21
  return (request.kind === "tool" &&
18
- request.capabilityId === TOOLS_CAPABILITY_ID &&
19
22
  request.toolName === TOOL_SEARCH_TOOL &&
20
- manifest?.capabilities.some((capability) => capability.id === TOOLS_CAPABILITY_ID) === true);
23
+ snapshot?.mcpTools.some((tool) => tool.deferred === true && tool.agentId === request.agent?.id) === true);
21
24
  }
22
25
  /** Runs `tool_search` for the run's agent. */
23
26
  export async function callToolSearch(ctx, request) {