@nylorun/runtime 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.
Files changed (157) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +10 -2
  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.js +13 -2
  20. package/dist/api/http/body.d.ts +3 -3
  21. package/dist/api/http/body.js +7 -7
  22. package/dist/api/http/routes/environment-manifests.d.ts +9 -0
  23. package/dist/api/http/routes/environment-manifests.js +118 -0
  24. package/dist/api/http/routes/environments.js +61 -12
  25. package/dist/api/http/routes/reads.d.ts +52 -3
  26. package/dist/api/http/routes/sessions.js +13 -3
  27. package/dist/api/http/routes/tenant.js +35 -7
  28. package/dist/api/inbound-files.d.ts +65 -0
  29. package/dist/api/inbound-files.js +115 -0
  30. package/dist/api/openapi.js +18 -3
  31. package/dist/artifacts/disk.js +20 -14
  32. package/dist/artifacts/media-types.d.ts +15 -0
  33. package/dist/artifacts/media-types.js +56 -4
  34. package/dist/artifacts/parts.d.ts +12 -3
  35. package/dist/artifacts/parts.js +15 -4
  36. package/dist/artifacts/workspace.d.ts +4 -4
  37. package/dist/artifacts/workspace.js +6 -18
  38. package/dist/core/flow-host.d.ts +1 -1
  39. package/dist/core/flow-host.js +2 -2
  40. package/dist/core/sandbox-routes.js +2 -2
  41. package/dist/core/turn-manifest.d.ts +3 -2
  42. package/dist/core/turn-manifest.js +29 -5
  43. package/dist/gates/egress-credentials.js +1 -1
  44. package/dist/gates/egress.d.ts +13 -19
  45. package/dist/gates/egress.js +30 -29
  46. package/dist/gates/http-tool.d.ts +3 -4
  47. package/dist/gates/http-tool.js +8 -12
  48. package/dist/gates/journal.js +0 -1
  49. package/dist/gates/mcp-handler.js +2 -3
  50. package/dist/gates/policy.js +1 -3
  51. package/dist/gates/tool-contract.d.ts +1 -1
  52. package/dist/harness/calls.d.ts +2 -2
  53. package/dist/harness/calls.js +9 -12
  54. package/dist/harness/main.js +7 -1
  55. package/dist/harness/pod.d.ts +6 -1
  56. package/dist/harness/pod.js +5 -0
  57. package/dist/harness/service.js +5 -5
  58. package/dist/harness/setup.d.ts +2 -0
  59. package/dist/harness/setup.js +25 -1
  60. package/dist/harness-api/record.d.ts +1 -1
  61. package/dist/harness-api/record.js +13 -13
  62. package/dist/harness-api/server.d.ts +27 -4
  63. package/dist/harness-api/server.js +41 -8
  64. package/dist/harness-api/tool-journal.d.ts +17 -8
  65. package/dist/harness-api/tool-journal.js +41 -11
  66. package/dist/harness-api/workspace.d.ts +7 -5
  67. package/dist/harness-api/workspace.js +25 -19
  68. package/dist/host/gates.js +2 -2
  69. package/dist/host/stack-config.js +1 -1
  70. package/dist/keys/keys.d.ts +9 -2
  71. package/dist/keys/keys.js +4 -1
  72. package/dist/management-openapi.json +366 -61
  73. package/dist/mcp/connect.d.ts +1 -2
  74. package/dist/mcp/connect.js +1 -4
  75. package/dist/mcp/pool.d.ts +1 -4
  76. package/dist/mcp/pool.js +13 -27
  77. package/dist/mcp/preview.js +0 -1
  78. package/dist/mcp/snapshot.d.ts +11 -13
  79. package/dist/mcp/snapshot.js +27 -38
  80. package/dist/model/capabilities.d.ts +26 -0
  81. package/dist/model/capabilities.js +44 -0
  82. package/dist/model/catalog.d.ts +34 -3
  83. package/dist/model/catalog.js +38 -2
  84. package/dist/model/models.d.ts +2 -0
  85. package/dist/model/models.js +1 -1
  86. package/dist/model/native-blocks.d.ts +35 -0
  87. package/dist/model/native-blocks.js +170 -0
  88. package/dist/model/pi-model.js +106 -94
  89. package/dist/openapi.json +9554 -5104
  90. package/dist/record/environment.js +1 -1
  91. package/dist/sandbox/join.js +4 -0
  92. package/dist/sandbox/manager.d.ts +32 -14
  93. package/dist/sandbox/manager.js +132 -93
  94. package/dist/sandbox/placement.d.ts +1 -1
  95. package/dist/sandbox/placement.js +2 -2
  96. package/dist/sandbox/pods/client.d.ts +3 -1
  97. package/dist/sandbox/pods/lifecycle.d.ts +8 -0
  98. package/dist/sandbox/pods/lifecycle.js +15 -0
  99. package/dist/sandbox/pods/spec.js +2 -1
  100. package/dist/sandbox/resolve.d.ts +5 -3
  101. package/dist/sandbox/resolve.js +9 -2
  102. package/dist/sandbox/runtime-tools.d.ts +9 -0
  103. package/dist/sandbox/runtime-tools.js +16 -0
  104. package/dist/sandbox/session-sandbox.d.ts +4 -6
  105. package/dist/sandbox/session-sandbox.js +47 -94
  106. package/dist/sandbox/setup.d.ts +4 -1
  107. package/dist/sandbox/setup.js +46 -9
  108. package/dist/sandbox/share.d.ts +12 -7
  109. package/dist/sandbox/share.js +27 -25
  110. package/dist/sandbox/skills.d.ts +8 -6
  111. package/dist/sandbox/skills.js +24 -23
  112. package/dist/store/postgres/drizzle/0024_flat_manifest.sql +7 -0
  113. package/dist/store/postgres/drizzle/0025_environment_manifests.sql +14 -0
  114. package/dist/store/postgres/drizzle/0026_environment_forks.sql +1 -0
  115. package/dist/store/postgres/drizzle/0027_environment_image_digest.sql +1 -0
  116. package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
  117. package/dist/store/postgres/reads.js +8 -2
  118. package/dist/store/postgres/schema.d.ts +217 -0
  119. package/dist/store/postgres/schema.js +36 -2
  120. package/dist/store/postgres/store.js +79 -4
  121. package/dist/store/types.d.ts +68 -14
  122. package/dist/tenant/advance.js +3 -0
  123. package/dist/tenant/artifact-tool.d.ts +1 -1
  124. package/dist/tenant/artifact-tool.js +43 -22
  125. package/dist/tenant/commands.js +24 -4
  126. package/dist/tenant/context.d.ts +10 -4
  127. package/dist/tenant/effects.js +7 -9
  128. package/dist/tenant/environment-manifests.d.ts +57 -0
  129. package/dist/tenant/environment-manifests.js +200 -0
  130. package/dist/tenant/environments.d.ts +54 -47
  131. package/dist/tenant/environments.js +304 -83
  132. package/dist/tenant/model-setting.d.ts +14 -0
  133. package/dist/tenant/model-setting.js +51 -0
  134. package/dist/tenant/sessions.d.ts +5 -1
  135. package/dist/tenant/sessions.js +45 -12
  136. package/dist/tenant/skill-tool.d.ts +3 -3
  137. package/dist/tenant/skill-tool.js +6 -11
  138. package/dist/tenant/sweep.js +1 -1
  139. package/dist/tenant/tool-results.js +1 -1
  140. package/dist/tenant/tool-search.d.ts +7 -11
  141. package/dist/tenant/tool-search.js +8 -5
  142. package/dist/tenant/transcript.d.ts +51 -32
  143. package/dist/tenant/transcript.js +2 -1
  144. package/dist/traces/ids.d.ts +0 -1
  145. package/dist/traces/ids.js +0 -4
  146. package/dist/traces/map.d.ts +2 -2
  147. package/dist/traces/map.js +1 -29
  148. package/dist/vault/coverage.js +11 -13
  149. package/dist/vault/host-model.d.ts +4 -1
  150. package/dist/vault/host-model.js +10 -2
  151. package/dist/vault/service.d.ts +49 -4
  152. package/dist/vault/service.js +153 -6
  153. package/dist/version.d.ts +1 -1
  154. package/dist/version.js +1 -1
  155. package/package.json +3 -3
  156. package/dist/sandbox/capability.d.ts +0 -3
  157. package/dist/sandbox/capability.js +0 -10
@@ -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) {
@@ -4,6 +4,7 @@
4
4
  * Payload schemas: `@nylorun/core/contracts` (`parseTranscriptEvent`).
5
5
  */
6
6
  import type { HostEffect } from "@nylorun/harness/run";
7
+ import type { PluginLabel } from "@nylorun/core/define";
7
8
  import type { TOOL_CALL_KINDS, TOOL_CALL_STATUSES } from "@nylorun/core/contracts";
8
9
  export type ToolCallStatus = (typeof TOOL_CALL_STATUSES)[number];
9
10
  export type ToolCallKind = (typeof TOOL_CALL_KINDS)[number];
@@ -136,6 +137,8 @@ export interface ToolCallFacts {
136
137
  readonly status: ToolCallStatus;
137
138
  readonly kind?: ToolCallKind;
138
139
  readonly server?: string;
140
+ readonly skill?: string;
141
+ readonly plugin?: PluginLabel;
139
142
  readonly attempts?: number;
140
143
  readonly durationMs?: number;
141
144
  readonly effectId?: string;
@@ -156,11 +159,12 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
156
159
  };
157
160
  output?: undefined;
158
161
  redacted: number;
159
- capabilityId: string;
160
162
  toolName: string;
161
163
  status?: "cancelled" | "completed" | "failed" | undefined;
162
- kind?: "http" | "mcp" | "platform" | "sandbox" | undefined;
164
+ kind?: "harness" | "http" | "mcp" | "platform" | "sandbox" | undefined;
163
165
  server?: string | undefined;
166
+ skill?: string | undefined;
167
+ plugin?: PluginLabel | undefined;
164
168
  attempts?: number | undefined;
165
169
  durationMs?: number | undefined;
166
170
  effectId?: string | undefined;
@@ -176,11 +180,12 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
176
180
  };
177
181
  output?: undefined;
178
182
  redacted: number;
179
- capabilityId: string;
180
183
  toolName: string;
181
184
  status?: "cancelled" | "completed" | "failed" | undefined;
182
- kind?: "http" | "mcp" | "platform" | "sandbox" | undefined;
185
+ kind?: "harness" | "http" | "mcp" | "platform" | "sandbox" | undefined;
183
186
  server?: string | undefined;
187
+ skill?: string | undefined;
188
+ plugin?: PluginLabel | undefined;
184
189
  attempts?: number | undefined;
185
190
  durationMs?: number | undefined;
186
191
  effectId?: string | undefined;
@@ -196,11 +201,12 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
196
201
  };
197
202
  output?: undefined;
198
203
  redacted?: undefined;
199
- capabilityId: string;
200
204
  toolName: string;
201
205
  status?: "cancelled" | "completed" | "failed" | undefined;
202
- kind?: "http" | "mcp" | "platform" | "sandbox" | undefined;
206
+ kind?: "harness" | "http" | "mcp" | "platform" | "sandbox" | undefined;
203
207
  server?: string | undefined;
208
+ skill?: string | undefined;
209
+ plugin?: PluginLabel | undefined;
204
210
  attempts?: number | undefined;
205
211
  durationMs?: number | undefined;
206
212
  effectId?: string | undefined;
@@ -216,11 +222,12 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
216
222
  };
217
223
  output?: undefined;
218
224
  redacted?: undefined;
219
- capabilityId: string;
220
225
  toolName: string;
221
226
  status?: "cancelled" | "completed" | "failed" | undefined;
222
- kind?: "http" | "mcp" | "platform" | "sandbox" | undefined;
227
+ kind?: "harness" | "http" | "mcp" | "platform" | "sandbox" | undefined;
223
228
  server?: string | undefined;
229
+ skill?: string | undefined;
230
+ plugin?: PluginLabel | undefined;
224
231
  attempts?: number | undefined;
225
232
  durationMs?: number | undefined;
226
233
  effectId?: string | undefined;
@@ -231,11 +238,12 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
231
238
  error?: undefined;
232
239
  output: unknown;
233
240
  redacted: number;
234
- capabilityId: string;
235
241
  toolName: string;
236
242
  status?: "cancelled" | "completed" | "failed" | undefined;
237
- kind?: "http" | "mcp" | "platform" | "sandbox" | undefined;
243
+ kind?: "harness" | "http" | "mcp" | "platform" | "sandbox" | undefined;
238
244
  server?: string | undefined;
245
+ skill?: string | undefined;
246
+ plugin?: PluginLabel | undefined;
239
247
  attempts?: number | undefined;
240
248
  durationMs?: number | undefined;
241
249
  effectId?: string | undefined;
@@ -246,11 +254,12 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
246
254
  error?: undefined;
247
255
  output: unknown;
248
256
  redacted: number;
249
- capabilityId: string;
250
257
  toolName: string;
251
258
  status?: "cancelled" | "completed" | "failed" | undefined;
252
- kind?: "http" | "mcp" | "platform" | "sandbox" | undefined;
259
+ kind?: "harness" | "http" | "mcp" | "platform" | "sandbox" | undefined;
253
260
  server?: string | undefined;
261
+ skill?: string | undefined;
262
+ plugin?: PluginLabel | undefined;
254
263
  attempts?: number | undefined;
255
264
  durationMs?: number | undefined;
256
265
  effectId?: string | undefined;
@@ -261,11 +270,12 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
261
270
  error?: undefined;
262
271
  output: unknown;
263
272
  redacted?: undefined;
264
- capabilityId: string;
265
273
  toolName: string;
266
274
  status?: "cancelled" | "completed" | "failed" | undefined;
267
- kind?: "http" | "mcp" | "platform" | "sandbox" | undefined;
275
+ kind?: "harness" | "http" | "mcp" | "platform" | "sandbox" | undefined;
268
276
  server?: string | undefined;
277
+ skill?: string | undefined;
278
+ plugin?: PluginLabel | undefined;
269
279
  attempts?: number | undefined;
270
280
  durationMs?: number | undefined;
271
281
  effectId?: string | undefined;
@@ -276,11 +286,12 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
276
286
  error?: undefined;
277
287
  output: unknown;
278
288
  redacted?: undefined;
279
- capabilityId: string;
280
289
  toolName: string;
281
290
  status?: "cancelled" | "completed" | "failed" | undefined;
282
- kind?: "http" | "mcp" | "platform" | "sandbox" | undefined;
291
+ kind?: "harness" | "http" | "mcp" | "platform" | "sandbox" | undefined;
283
292
  server?: string | undefined;
293
+ skill?: string | undefined;
294
+ plugin?: PluginLabel | undefined;
284
295
  attempts?: number | undefined;
285
296
  durationMs?: number | undefined;
286
297
  effectId?: string | undefined;
@@ -298,11 +309,12 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
298
309
  };
299
310
  output?: undefined;
300
311
  redacted: number;
301
- capabilityId: string;
302
312
  toolName: string;
303
313
  status?: "cancelled" | "completed" | "failed" | undefined;
304
- kind?: "http" | "mcp" | "platform" | "sandbox" | undefined;
314
+ kind?: "harness" | "http" | "mcp" | "platform" | "sandbox" | undefined;
305
315
  server?: string | undefined;
316
+ skill?: string | undefined;
317
+ plugin?: PluginLabel | undefined;
306
318
  attempts?: number | undefined;
307
319
  durationMs?: number | undefined;
308
320
  effectId?: string | undefined;
@@ -320,11 +332,12 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
320
332
  };
321
333
  output?: undefined;
322
334
  redacted: number;
323
- capabilityId: string;
324
335
  toolName: string;
325
336
  status?: "cancelled" | "completed" | "failed" | undefined;
326
- kind?: "http" | "mcp" | "platform" | "sandbox" | undefined;
337
+ kind?: "harness" | "http" | "mcp" | "platform" | "sandbox" | undefined;
327
338
  server?: string | undefined;
339
+ skill?: string | undefined;
340
+ plugin?: PluginLabel | undefined;
328
341
  attempts?: number | undefined;
329
342
  durationMs?: number | undefined;
330
343
  effectId?: string | undefined;
@@ -342,11 +355,12 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
342
355
  };
343
356
  output?: undefined;
344
357
  redacted?: undefined;
345
- capabilityId: string;
346
358
  toolName: string;
347
359
  status?: "cancelled" | "completed" | "failed" | undefined;
348
- kind?: "http" | "mcp" | "platform" | "sandbox" | undefined;
360
+ kind?: "harness" | "http" | "mcp" | "platform" | "sandbox" | undefined;
349
361
  server?: string | undefined;
362
+ skill?: string | undefined;
363
+ plugin?: PluginLabel | undefined;
350
364
  attempts?: number | undefined;
351
365
  durationMs?: number | undefined;
352
366
  effectId?: string | undefined;
@@ -364,11 +378,12 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
364
378
  };
365
379
  output?: undefined;
366
380
  redacted?: undefined;
367
- capabilityId: string;
368
381
  toolName: string;
369
382
  status?: "cancelled" | "completed" | "failed" | undefined;
370
- kind?: "http" | "mcp" | "platform" | "sandbox" | undefined;
383
+ kind?: "harness" | "http" | "mcp" | "platform" | "sandbox" | undefined;
371
384
  server?: string | undefined;
385
+ skill?: string | undefined;
386
+ plugin?: PluginLabel | undefined;
372
387
  attempts?: number | undefined;
373
388
  durationMs?: number | undefined;
374
389
  effectId?: string | undefined;
@@ -381,11 +396,12 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
381
396
  error?: undefined;
382
397
  output: unknown;
383
398
  redacted: number;
384
- capabilityId: string;
385
399
  toolName: string;
386
400
  status?: "cancelled" | "completed" | "failed" | undefined;
387
- kind?: "http" | "mcp" | "platform" | "sandbox" | undefined;
401
+ kind?: "harness" | "http" | "mcp" | "platform" | "sandbox" | undefined;
388
402
  server?: string | undefined;
403
+ skill?: string | undefined;
404
+ plugin?: PluginLabel | undefined;
389
405
  attempts?: number | undefined;
390
406
  durationMs?: number | undefined;
391
407
  effectId?: string | undefined;
@@ -398,11 +414,12 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
398
414
  error?: undefined;
399
415
  output: unknown;
400
416
  redacted: number;
401
- capabilityId: string;
402
417
  toolName: string;
403
418
  status?: "cancelled" | "completed" | "failed" | undefined;
404
- kind?: "http" | "mcp" | "platform" | "sandbox" | undefined;
419
+ kind?: "harness" | "http" | "mcp" | "platform" | "sandbox" | undefined;
405
420
  server?: string | undefined;
421
+ skill?: string | undefined;
422
+ plugin?: PluginLabel | undefined;
406
423
  attempts?: number | undefined;
407
424
  durationMs?: number | undefined;
408
425
  effectId?: string | undefined;
@@ -415,11 +432,12 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
415
432
  error?: undefined;
416
433
  output: unknown;
417
434
  redacted?: undefined;
418
- capabilityId: string;
419
435
  toolName: string;
420
436
  status?: "cancelled" | "completed" | "failed" | undefined;
421
- kind?: "http" | "mcp" | "platform" | "sandbox" | undefined;
437
+ kind?: "harness" | "http" | "mcp" | "platform" | "sandbox" | undefined;
422
438
  server?: string | undefined;
439
+ skill?: string | undefined;
440
+ plugin?: PluginLabel | undefined;
423
441
  attempts?: number | undefined;
424
442
  durationMs?: number | undefined;
425
443
  effectId?: string | undefined;
@@ -432,11 +450,12 @@ export declare function toolCompleted(request: HostEffect, value: unknown, facts
432
450
  error?: undefined;
433
451
  output: unknown;
434
452
  redacted?: undefined;
435
- capabilityId: string;
436
453
  toolName: string;
437
454
  status?: "cancelled" | "completed" | "failed" | undefined;
438
- kind?: "http" | "mcp" | "platform" | "sandbox" | undefined;
455
+ kind?: "harness" | "http" | "mcp" | "platform" | "sandbox" | undefined;
439
456
  server?: string | undefined;
457
+ skill?: string | undefined;
458
+ plugin?: PluginLabel | undefined;
440
459
  attempts?: number | undefined;
441
460
  durationMs?: number | undefined;
442
461
  effectId?: string | undefined;
@@ -158,13 +158,14 @@ export function toolCompleted(request, value, facts) {
158
158
  : {};
159
159
  return {
160
160
  ...ids,
161
- capabilityId: request.capabilityId,
162
161
  toolName: request.toolName,
163
162
  ...(facts
164
163
  ? {
165
164
  status: facts.status,
166
165
  ...(facts.kind === undefined ? {} : { kind: facts.kind }),
167
166
  ...(facts.server === undefined ? {} : { server: facts.server }),
167
+ ...(facts.skill === undefined ? {} : { skill: facts.skill }),
168
+ ...(facts.plugin === undefined ? {} : { plugin: facts.plugin }),
168
169
  ...(facts.attempts === undefined ? {} : { attempts: facts.attempts }),
169
170
  ...(facts.durationMs === undefined ? {} : { durationMs: facts.durationMs }),
170
171
  ...(facts.effectId === undefined ? {} : { effectId: facts.effectId }),
@@ -3,7 +3,6 @@ export type TraceKey = Buffer;
3
3
  export declare function traceIdOf(key: TraceKey, tenantId: string, sessionId: string, turnId: string): string;
4
4
  export declare function turnSpanOf(key: TraceKey, turnId: string): string;
5
5
  export declare function effectSpanOf(key: TraceKey, effectId: string): string;
6
- export declare function egressSpanOf(key: TraceKey, sessionId: string, seq: number): string;
7
6
  export declare function eventSpanOf(key: TraceKey, sessionId: string, seq: number): string;
8
7
  /** W3C trace context (R7.4): `traceparent`, and `tracestate` when the caller sent one. */
9
8
  export interface TraceContext {
@@ -8,7 +8,6 @@
8
8
  * (R7.4): then that trace id.
9
9
  * - **turn span**: `span|turn|<turn>`; every crossing's span is its child.
10
10
  * - **effect span**: `span|effect|<effect id>`, for a model or tool call or a flow stage.
11
- * - **egress span**: `span|egress|<session>|<seq>`, for one `egress.connection` row.
12
11
  * - **event span**: `span|event|<session>|<seq>`, for a marker or a row without an effect id.
13
12
  */
14
13
  import { createHmac } from "node:crypto";
@@ -26,9 +25,6 @@ export function turnSpanOf(key, turnId) {
26
25
  export function effectSpanOf(key, effectId) {
27
26
  return hmacHex(key, `span|effect|${effectId}`, 8);
28
27
  }
29
- export function egressSpanOf(key, sessionId, seq) {
30
- return hmacHex(key, `span|egress|${sessionId}|${seq}`, 8);
31
- }
32
28
  export function eventSpanOf(key, sessionId, seq) {
33
29
  return hmacHex(key, `span|event|${sessionId}|${seq}`, 8);
34
30
  }
@@ -10,7 +10,6 @@
10
10
  * | `tool.completed` | `execute_tool {toolName}` (INTERNAL) |
11
11
  * | `node.completed` | `execute_tool {key}`, a flow's HTTP stage (INTERNAL) |
12
12
  * | `effect.uncertain` | the call's span, with an error status |
13
- * | `egress.connection` | `CONNECT {host}` (CLIENT) |
14
13
  * | `turn.paused`, `command.approve`, `command.respond` | a marker with no length |
15
14
  *
16
15
  * Every other row, and every row without a turn, makes none. Every child's parent is the turn's
@@ -18,7 +17,8 @@
18
17
  *
19
18
  * Timing: a span with a gate's own measurement (`gate`, R7.1) is that measurement; any other ends
20
19
  * at the row's time and starts `durationMs` before it (the journal's window, exported as
21
- * `nylorun.effect.window_ms`). An egress span is the gate's: `startedAt` plus `durationMs`.
20
+ * `nylorun.effect.window_ms`). Egress has no span: a connection is the Environment's, not a
21
+ * turn's (D67), and is read from the Environment's events.
22
22
  *
23
23
  * Attribute names follow the OTel GenAI conventions at `GENAI_SEMCONV` (still in Development
24
24
  * status), recorded on the resource as `nylorun.semconv.genai`; the rest are `nylorun.*`. A span
@@ -1,4 +1,4 @@
1
- import { effectSpanOf, egressSpanOf, eventSpanOf, parseTraceparent, traceIdOf, turnSpanOf } from "./ids.js";
1
+ import { effectSpanOf, eventSpanOf, parseTraceparent, traceIdOf, turnSpanOf } from "./ids.js";
2
2
  /** The commit of open-telemetry/semantic-conventions-genai whose attribute names spans use. */
3
3
  export const GENAI_SEMCONV = "open-telemetry/semantic-conventions-genai@6fd0d763a092db245a54104fc1124031eb3c51d2";
4
4
  /**
@@ -188,34 +188,6 @@ export function spanOf(row, turn, settings) {
188
188
  status: errorStatus(content("errors", p.message)),
189
189
  };
190
190
  }
191
- case "egress.connection": {
192
- const refused = p.outcome === "refused";
193
- const started = Date.parse(String(p.startedAt));
194
- const attributes = base
195
- .str("server.address", p.host)
196
- .int("server.port", p.port)
197
- .str("nylorun.environment.id", p.environmentId)
198
- .str("nylorun.egress.outcome", p.outcome)
199
- .str("nylorun.egress.mode", p.mode)
200
- .bool("nylorun.egress.credential_bound", p.credentialBound)
201
- .strings("nylorun.egress.credential_ids", p.credentialIds)
202
- .int("nylorun.egress.requests", p.requests)
203
- .int("nylorun.egress.bytes_up", p.bytesUp)
204
- .int("nylorun.egress.bytes_down", p.bytesDown)
205
- .int("nylorun.egress.status", p.status)
206
- .str("error.type", refused ? (p.reason ?? "refused") : undefined);
207
- const times = Number.isFinite(started)
208
- ? { start: started, end: started + Math.max(0, Math.round(Number(p.durationMs) || 0)) }
209
- : undefined;
210
- return {
211
- ...child(egressSpanOf(settings.key, row.sessionId, row.seq), `CONNECT ${String(p.host)}`, SPAN_KIND.client),
212
- ...(times
213
- ? { startTimeUnixNano: nanos(times.start), endTimeUnixNano: nanos(times.end) }
214
- : window(row.time, undefined)),
215
- attributes: attributes.list,
216
- status: refused ? errorStatus(undefined) : { code: STATUS.unset },
217
- };
218
- }
219
191
  case "turn.paused":
220
192
  case "command.approve":
221
193
  case "command.respond": {
@@ -18,19 +18,17 @@ export function credentialNeeds(manifest) {
18
18
  };
19
19
  const owner = (agentId) => (agentId === undefined ? {} : { agentId });
20
20
  const agent = (manifest, agentId) => {
21
- for (const capability of manifest.capabilities) {
22
- for (const server of Object.values(capability.mcpServers ?? {}))
23
- add({ kind: "mcp", ...owner(agentId), name: server.name, serverName: server.name, url: server.url });
24
- for (const tool of capability.tools ?? [])
25
- if (tool.http?.credential !== undefined)
26
- add({
27
- kind: "http",
28
- ...owner(agentId),
29
- name: tool.name,
30
- serverName: tool.http.credential,
31
- url: tool.http.url,
32
- });
33
- }
21
+ for (const tool of manifest.tools ?? [])
22
+ if (tool.http?.credential !== undefined)
23
+ add({
24
+ kind: "http",
25
+ ...owner(agentId),
26
+ name: tool.name,
27
+ serverName: tool.http.credential,
28
+ url: tool.http.url,
29
+ });
30
+ for (const server of Object.values(manifest.mcpServers ?? {}))
31
+ add({ kind: "mcp", ...owner(agentId), name: server.name, serverName: server.name, url: server.url });
34
32
  for (const child of delegatesOf(manifest))
35
33
  agent(child.manifest, child.manifest.id);
36
34
  for (const child of flowDelegatesOf(manifest))