agent-relay-server 0.35.2 → 0.35.3
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/docs/openapi.json +1 -1
- package/package.json +1 -1
- package/runner/src/adapter.ts +3 -1
- package/src/mcp.ts +28 -8
package/docs/openapi.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"openapi": "3.1.0",
|
|
3
3
|
"info": {
|
|
4
4
|
"title": "Agent Relay API",
|
|
5
|
-
"version": "0.35.
|
|
5
|
+
"version": "0.35.3",
|
|
6
6
|
"description": "Real-time message bus for inter-agent communication. Agent-first: this spec is designed for machine consumption — agents can self-discover the full API surface via GET /api/spec.",
|
|
7
7
|
"license": {
|
|
8
8
|
"name": "MIT",
|
package/package.json
CHANGED
package/runner/src/adapter.ts
CHANGED
|
@@ -154,7 +154,9 @@ export interface ProviderAdapter {
|
|
|
154
154
|
probeActivity?(process: ManagedProcess): Promise<"busy" | "idle" | "unknown">;
|
|
155
155
|
terminalAttachSpec?(process: ManagedProcess): Promise<TerminalAttachSpec>;
|
|
156
156
|
respondToPermissionDecision?(process: ManagedProcess, input: ProviderPermissionDecisionInput): Promise<Record<string, unknown> | void>;
|
|
157
|
-
|
|
157
|
+
// `options.readyTimeoutMs` lets the runner widen the provider-ready wait for the
|
|
158
|
+
// first (cold-start) delivery vs. a fast re-attempt after a ready signal (#329).
|
|
159
|
+
deliverInitialPrompt?(process: ManagedProcess, prompt: string, options?: { readyTimeoutMs?: number }): Promise<void>;
|
|
158
160
|
deliver(process: ManagedProcess, messages: Message[]): Promise<void>;
|
|
159
161
|
onStatusChange(cb: (status: ProviderStatusUpdate) => void): void;
|
|
160
162
|
// Subscribe to session-mirror events from providers that emit them directly
|
package/src/mcp.ts
CHANGED
|
@@ -50,7 +50,7 @@ import type { ActivityKind, AgentCard, ArtifactKind, ArtifactSensitivity, Attach
|
|
|
50
50
|
import { LAND_STRATEGIES, applyWorkspaceAction, waitForWorkspaceStatus, type WorkspaceAction } from "./workspace-actions";
|
|
51
51
|
import { describeWorkspacePhase, landReceipt, readyContract, worktreeMcpInstructions } from "./workspace-phase";
|
|
52
52
|
import { type ProviderEffort } from "agent-relay-sdk/provider-catalog";
|
|
53
|
-
import { errMessage, isRecord, SPAWN_PROVIDERS, APPROVAL_MODES, VALID_EFFORTS, VALID_WORKSPACE_MODES } from "agent-relay-sdk";
|
|
53
|
+
import { errMessage, isRecord, stringValue, SPAWN_PROVIDERS, APPROVAL_MODES, VALID_EFFORTS, VALID_WORKSPACE_MODES } from "agent-relay-sdk";
|
|
54
54
|
import { runnerRuntimeTokenEnv } from "./runtime-tokens";
|
|
55
55
|
|
|
56
56
|
type JsonRpcId = string | number | null;
|
|
@@ -257,11 +257,11 @@ const TOOLS: ToolDefinition[] = [
|
|
|
257
257
|
properties: {
|
|
258
258
|
provider: { type: "string", enum: SPAWN_PROVIDERS },
|
|
259
259
|
orchestratorId: { type: "string", description: "Target host. Defaults to the host that owns cwd, else YOUR OWN host — only set it to spawn onto a different machine." },
|
|
260
|
-
cwd: { type: "string", description: "Working directory for the agent. Must resolve within the target orchestrator's base directory (enforced server-side)." },
|
|
260
|
+
cwd: { type: "string", description: "Working directory for the agent. Defaults to YOUR OWN cwd (the repo you're working in) when omitted, else the orchestrator's base dir. Must resolve within the target orchestrator's base directory (enforced server-side). NOTE: workspaceMode `isolated` requires a git repo — the resolved cwd must be inside one or the spawn is rejected." },
|
|
261
261
|
label: { type: "string" },
|
|
262
262
|
model: { type: "string" },
|
|
263
263
|
effort: { type: "string", enum: VALID_EFFORTS },
|
|
264
|
-
approvalMode: { type: "string", enum: APPROVAL_MODES },
|
|
264
|
+
approvalMode: { type: "string", enum: APPROVAL_MODES, description: "Permission posture for the worker. Defaults to YOUR OWN approval mode when omitted (a headless `guarded` worker wedges on the first approval prompt), so a coordinator running `open` spawns workers that can act autonomously. Pass an explicit value to narrow a child (e.g. `read-only` reviewer)." },
|
|
265
265
|
prompt: { type: "string", description: "Initial task/message delivered to the agent on launch — spawn and hand it its first instruction in one call (no separate follow-up message needed)." },
|
|
266
266
|
systemPromptAppend: { type: "string" },
|
|
267
267
|
profile: { type: "string", description: "Agent profile name to apply (env, instructions, permissions, MCP/skills, spawn quota)." },
|
|
@@ -791,16 +791,37 @@ async function relaySpawnAgent(auth: McpAuthContext, args: Record<string, unknow
|
|
|
791
791
|
const provider = enumField(args.provider, "provider", SPAWN_PROVIDERS) as SpawnProvider;
|
|
792
792
|
const cwd = optionalString(args.cwd, "cwd", 500);
|
|
793
793
|
const callerId = callerAgentId(auth);
|
|
794
|
-
|
|
794
|
+
// One caller-record lookup, reused for host preference (#221), the cwd default (#328) and the
|
|
795
|
+
// approvalMode default (#331) — an agent spawning a helper inherits its own context instead of
|
|
796
|
+
// falling back to hardcoded values.
|
|
797
|
+
const caller = callerId ? getAgent(callerId) : undefined;
|
|
798
|
+
const preferHost = caller?.machine;
|
|
795
799
|
const orchestrator = selectSpawnOrchestrator(provider, optionalString(args.orchestratorId, "orchestratorId", 200), cwd, preferHost);
|
|
796
|
-
|
|
800
|
+
// #328 — default cwd to the caller's OWN cwd (the repo it's working in), not the orchestrator
|
|
801
|
+
// base dir, so "agent spawns a helper for its current task" Just Works — especially isolated mode,
|
|
802
|
+
// which needs a git repo (the base dir usually isn't one, so it silently downgraded to shared).
|
|
803
|
+
// Only adopt the caller's cwd when it resolves within the TARGET host's base dir (preferHost
|
|
804
|
+
// already biases the target to the caller's host; a cross-host path may not exist there). Non-agent
|
|
805
|
+
// callers (no caller record) keep the base-dir fallback. Precedence: explicit cwd > caller cwd > base dir.
|
|
806
|
+
const callerCwd = stringValue(caller?.meta?.cwd);
|
|
807
|
+
const inheritedCwd = callerCwd && isPathWithinBase(callerCwd, orchestrator.baseDir) ? callerCwd : undefined;
|
|
808
|
+
const resolvedCwd = cwd || inheritedCwd || orchestrator.baseDir;
|
|
797
809
|
// #308 §3 — cwd must resolve within the TARGET host's base dir. A path valid on your own host
|
|
798
810
|
// may not exist on a different orchestrator, so validate against the chosen host and say which.
|
|
799
811
|
if (cwd && !isPathWithinBase(cwd, orchestrator.baseDir)) {
|
|
800
812
|
throw new ValidationError(`cwd '${cwd}' is not within ${orchestrator.id} (host ${orchestrator.hostname})'s base dir '${orchestrator.baseDir}' — a path valid on your host may not exist on the target. Pass a cwd under that base dir, or omit cwd to default to it.`);
|
|
801
813
|
}
|
|
802
814
|
const selection = providerSelection(provider, args);
|
|
803
|
-
|
|
815
|
+
// #331 — default the child's approval mode to the CALLER's, not a hardcoded `guarded`. A headless
|
|
816
|
+
// `guarded` child wedges on the first tool-call approval prompt (no human at the TUI — it can't even
|
|
817
|
+
// read its own spawn message). A trusted coordinator running `open` spawns workers that can actually
|
|
818
|
+
// work in their isolated worktrees; an explicit arg always wins and can NARROW a child (e.g. a
|
|
819
|
+
// read-only reviewer); non-agent/admin callers (no caller record) keep the safe `guarded` default.
|
|
820
|
+
// Precedence: explicit approvalMode > caller mode > guarded.
|
|
821
|
+
const callerApprovalMode = optionalEnum(stringValue(caller?.meta?.approvalMode), "approvalMode", APPROVAL_MODES) as SpawnApprovalMode | undefined;
|
|
822
|
+
const approvalMode = (optionalEnum(args.approvalMode, "approvalMode", APPROVAL_MODES) as SpawnApprovalMode | undefined)
|
|
823
|
+
?? callerApprovalMode
|
|
824
|
+
?? "guarded";
|
|
804
825
|
const spawnRequestId = optionalString(args.spawnRequestId, "spawnRequestId", 160) ?? generateSpawnRequestId();
|
|
805
826
|
const label = optionalString(args.label, "label", 120);
|
|
806
827
|
const policyName = optionalString(args.policyName, "policyName", 120);
|
|
@@ -815,8 +836,7 @@ async function relaySpawnAgent(auth: McpAuthContext, args: Record<string, unknow
|
|
|
815
836
|
// granted only to agents whose profile sets maxSpawnedAgents>0 and never to children).
|
|
816
837
|
// Server/admin tokens have no caller identity → unrestricted by design.
|
|
817
838
|
if (callerId) {
|
|
818
|
-
|
|
819
|
-
if (me?.spawnedBy) {
|
|
839
|
+
if (caller?.spawnedBy) {
|
|
820
840
|
throw new McpAuthError("spawned agents cannot spawn further agents (no grandchildren)");
|
|
821
841
|
}
|
|
822
842
|
const quota = auth.component?.constraints?.maxSpawnedAgents ?? 0;
|