@nylorun/agents 0.4.0-beta → 0.5.0-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @nylorun/agents
2
2
 
3
+ ## 0.5.0-beta
4
+
5
+ ### Minor Changes
6
+
7
+ - 1cd7dc7: Add subagents: put an agent in another agent's `tools` (`Agent({ tools: [lookupOrder, researcher] })` or `.use({ tools: [researcher] })`) and the model can delegate to it. The tool is named after the agent's id, takes `{ task: string }`, and its description is the agent's `description`, which is now required for an agent used as a tool. The engine runs the child inside the parent's turn as a durable branch: fresh context, its own tools and hooks served by the root agent's executor, its own MCP servers, the session's sandbox, and only its final output (or `outputSchema` result) returned. Empty output, failures (with partial output marked as evidence), and requests for input or approval inside a child reach the parent as failed tool results. Parallel delegation calls run concurrently, completed child work is never re-run on replay, and cancelling the session cancels every child.
8
+
9
+ v1 is one level deep and non-interactive. Nested delegation, child tools that declare `approval`, and differing sandboxes across the tree fail the build with a named diagnostic. The manifest adds an optional `agent` body on a tool (schema version unchanged), actions and effects carry `agent: { id, path, delegationId }`, tool context gains `ctx.agent`, the durable host resolves a new `delegation` effect kind, and the Runtime emits `delegation.started` / `delegation.completed` events and filters history with `?agent=` (`session.history({ agent })`). Studio shows delegations, labels child actions with their agent, and filters events by agent.
10
+
11
+ ### Patch Changes
12
+
13
+ - Pin core to the tested release.
14
+ - Updated dependencies [1cd7dc7]
15
+ - @nylorun/core@0.4.0-beta
16
+
3
17
  ## 0.4.0-beta
4
18
 
5
19
  ### Minor Changes
package/README.md CHANGED
@@ -110,6 +110,31 @@ The model gets `bash`, `read`, `write`, `edit`, `grep` and `glob` on a Linux mac
110
110
 
111
111
  The `dev` preset allows package registries and code hosts. Private networks, loopback, the host and cloud metadata endpoints are always blocked. Options from the full design that are not in this version (`setup`, `files`, `secrets`, `mount`, `onStart`, `scope`, ...) throw a `SandboxError` that says so. See [the sandbox design](../docs/design/sandboxes.md).
112
112
 
113
+ Put an agent in another agent's `tools` to let the model delegate to it:
114
+
115
+ ```ts
116
+ import { Agent } from "@nylorun/agents";
117
+ import { z } from "zod";
118
+
119
+ const researcher = Agent({
120
+ id: "researcher",
121
+ description: "Investigates an order's history. Returns a short summary with the ids it relied on.",
122
+ instructions: "Investigate one question about one order. Be exhaustive, then be brief.",
123
+ tools: [searchOrders, readTicket],
124
+ outputSchema: z.object({ summary: z.string(), evidence: z.array(z.string()) }),
125
+ });
126
+
127
+ const support = Agent({
128
+ id: "support",
129
+ instructions: "For anything needing more than two lookups, delegate to researcher with a complete, self-contained task.",
130
+ tools: [lookupOrder, refundOrder, researcher],
131
+ });
132
+ ```
133
+
134
+ The tool is named after the agent's `id` and takes `{ task: string }`; the agent's `description` (required) is what the parent's model reads to decide when to delegate. The child starts with a fresh context: it sees its own instructions and the task, nothing of the parent's conversation, and only its final text (or `outputSchema` result) comes back. It keeps its own tools, hooks, skills and MCP servers, served by the executor you already run for the parent (`connectAgents({ agents: [support] })` serves both), shares the session's sandbox, and starts with empty `ctx.state`. Tools can read `ctx.agent` (`{ id, path, delegationId }`). Several delegation calls in one model response run in parallel. An empty answer, a failure (with the child's last text marked as evidence) or a cancelled child reaches the parent's model as a failed tool result, never as success. The Runtime emits `delegation.started` and `delegation.completed`; `session.history({ agent })` filters by `delegationId` (one child invocation) or by path such as `support/researcher` (every concurrent child that shares that path).
135
+
136
+ Delegate when the parent should keep the answer. When a specialist should own the rest of the conversation, switch capabilities with a `before("step")` patch instead. This version is one level deep and non-interactive: a delegated agent cannot use agents as tools, its tools cannot declare `approval` (keep those on the parent), and `ctx.ask`, `ctx.approve`, `ctx.sleep` or `ctx.waitFor` inside it fail with `delegation.interaction-unsupported`. Delegation is not an approval boundary; approvals live on tools.
137
+
113
138
  Use `session.observe({ cursor, signal })` for resumable canonical events, `session.inspect()` for waiting/uncertain state, and `approve`, `respond`, or `cancel` with an explicit stable idempotency key. Retry the same semantic command with the same key. `ownerUserId` must come from trusted server authentication. Application credentials are not browser credentials; browser applications need an authorized backend. Definition authoring is browser-bundleable.
114
139
 
115
140
  The Runtime destination is explicit, or defaults from `NYLORUN_RUNTIME_URL`. Application and executor keys default from `NYLORUN_SERVER_KEY` and `NYLORUN_EXECUTOR_KEY`. Implementation version defaults from `NYLORUN_IMPLEMENTATION_VERSION`, then `dev`. A host must provision executor scope for the agent id. Agents do not hash the manifest, and an in-flight action stays claimable after the registered digest changes. Model selection and model credentials belong to the Runtime.
package/dist/client.d.ts CHANGED
@@ -153,8 +153,10 @@ export declare class SessionClient {
153
153
  cursor: string | null;
154
154
  requestId: string;
155
155
  }>;
156
+ /** `agent` narrows to one agent used as a tool: its delegationId or its path. */
156
157
  history(options?: {
157
158
  cursor?: string;
159
+ agent?: string;
158
160
  signal?: AbortSignal;
159
161
  }): Promise<{
160
162
  items: {
package/dist/client.js CHANGED
@@ -1,21 +1,30 @@
1
+ import { delegateOf } from "@nylorun/core/define";
1
2
  import { LiveEventSchema, SessionItemsResponseSchema, } from "@nylorun/core/contracts";
2
3
  import { Transport, id, segment } from "./http.js";
3
4
  import { observeSSE } from "./sse.js";
4
5
  const middlewareClosure = "Hosted definitions support declarative capabilities and before/after hooks, not middleware closures";
5
- function pluginRootsOf(agent) {
6
+ /** The agent's declarations, then those of each agent it uses as a tool, keyed `<child>/<capability>`. */
7
+ function declarationsOf(agent) {
6
8
  const built = agent.build?.() ?? agent;
7
- const declarations = built.getBinding?.().declarations ?? [];
9
+ const binding = built.getBinding?.();
10
+ const found = (binding?.declarations ?? []).map((item) => ({ key: item.id, item }));
11
+ for (const tool of binding?.tools ?? []) {
12
+ const child = delegateOf(tool)?.agent;
13
+ for (const item of child?.getBinding().declarations ?? [])
14
+ found.push({ key: `${child.id}/${item.id}`, item });
15
+ }
16
+ return found;
17
+ }
18
+ function pluginRootsOf(agent) {
8
19
  const roots = {};
9
- for (const item of declarations) {
20
+ for (const { key, item } of declarationsOf(agent))
10
21
  if (item.pluginRoot)
11
- roots[item.id] = item.pluginRoot;
12
- }
22
+ roots[key] = item.pluginRoot;
13
23
  return Object.keys(roots).length === 0 ? undefined : roots;
14
24
  }
15
25
  /** Closures stay on the live binding. The published manifest cannot name them. */
16
26
  export function assertNoMiddlewareClosures(agent) {
17
- const built = agent.build?.() ?? agent;
18
- if (built.getBinding?.().declarations.some((item) => item.hasMiddleware))
27
+ if (declarationsOf(agent).some(({ item }) => item.hasMiddleware))
19
28
  throw new Error(middlewareClosure);
20
29
  }
21
30
  export class AgentsClient {
@@ -152,8 +161,15 @@ export class SessionClient {
152
161
  ...(options.reason ? { reason: options.reason } : {}),
153
162
  }, options.signal);
154
163
  }
164
+ /** `agent` narrows to one agent used as a tool: its delegationId or its path. */
155
165
  async history(options = {}) {
156
- return SessionItemsResponseSchema.parse(await this.transport.json(`${this.path}/items${options.cursor ? `?cursor=${encodeURIComponent(options.cursor)}` : ""}`, "GET", undefined, options.signal));
166
+ const query = new URLSearchParams();
167
+ if (options.cursor)
168
+ query.set("cursor", options.cursor);
169
+ if (options.agent)
170
+ query.set("agent", options.agent);
171
+ const search = query.toString();
172
+ return SessionItemsResponseSchema.parse(await this.transport.json(`${this.path}/items${search ? `?${search}` : ""}`, "GET", undefined, options.signal));
157
173
  }
158
174
  async *observe(options = {}) {
159
175
  for await (const frame of observeSSE(this.transport, `${this.path}/events`, options)) {
@@ -1,4 +1,4 @@
1
1
  import { type BuiltAgent } from "@nylorun/core/define";
2
2
  import type { Action, ActionOutcome } from "@nylorun/core/contracts";
3
3
  /** Executes code only after a host-issued claim. No engine dependency. */
4
- export declare function executeAction(action: Action, agent: BuiltAgent, signal: AbortSignal): Promise<ActionOutcome>;
4
+ export declare function executeAction(action: Action, root: BuiltAgent, signal: AbortSignal): Promise<ActionOutcome>;
@@ -1,4 +1,4 @@
1
- import { implementationsFor, isToolError, normalizeToolDefinition, normalizedSchemasFor, runHookPoint, } from "@nylorun/core/define";
1
+ import { delegateOf, implementationsFor, isToolError, normalizeToolDefinition, normalizedSchemasFor, runHookPoint, } from "@nylorun/core/define";
2
2
  class Suspend {
3
3
  outcome;
4
4
  constructor(outcome) {
@@ -9,7 +9,10 @@ const object = (value) => value !== null && typeof value === "object" && !Array.
9
9
  ? value
10
10
  : {};
11
11
  /** Executes code only after a host-issued claim. No engine dependency. */
12
- export async function executeAction(action, agent, signal) {
12
+ export async function executeAction(action, root, signal) {
13
+ // Work for an agent used as a tool runs that agent's code, served from the root's binding.
14
+ const agent = action.agent ? delegatedAgent(root, action.agent.id) : root;
15
+ const ref = action.agent ?? { id: root.id, path: root.id };
13
16
  if (action.kind === "hook")
14
17
  // All capabilities at this hook point run concurrently in one action. Never throws.
15
18
  return {
@@ -77,6 +80,7 @@ export async function executeAction(action, agent, signal) {
77
80
  signal,
78
81
  info: ctx.info,
79
82
  session: { id: action.sessionId },
83
+ agent: ref,
80
84
  ...(ctx.resume ? { resume: ctx.resume } : {}),
81
85
  state: {
82
86
  get: (key) => state[key],
@@ -170,6 +174,14 @@ export async function executeAction(action, agent, signal) {
170
174
  };
171
175
  }
172
176
  }
177
+ function delegatedAgent(root, id) {
178
+ for (const capability of Object.values(implementationsFor(root))) {
179
+ const child = delegateOf(capability.tools?.[id])?.agent;
180
+ if (child)
181
+ return child;
182
+ }
183
+ throw new Error(`Agent '${root.id}' does not use '${id}' as a tool`);
184
+ }
173
185
  function message(error) {
174
186
  return error instanceof Error ? error.message : String(error);
175
187
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nylorun/agents",
3
- "version": "0.4.0-beta",
3
+ "version": "0.5.0-beta",
4
4
  "description": "Nylorun agent definitions, session client and connected executor",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -35,7 +35,7 @@
35
35
  "test": "vitest run --passWithNoTests"
36
36
  },
37
37
  "dependencies": {
38
- "@nylorun/core": "0.3.0-beta",
38
+ "@nylorun/core": "0.4.0-beta",
39
39
  "gray-matter": "^4.0.3"
40
40
  },
41
41
  "peerDependencies": {