agent-relay-server 0.33.1 → 0.34.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-server",
3
- "version": "0.33.1",
3
+ "version": "0.34.1",
4
4
  "description": "Lightweight HTTP message relay for inter-agent communication across machines",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
@@ -174,7 +174,23 @@ export function profileAllowsRelayFeature(config: RunnerSpawnConfig, feature: ke
174
174
 
175
175
  export const RELAY_CONTEXT = `[agent-relay] You are connected to Agent Relay, a real-time message bus between agents and users. When you receive a relay message: read it, do what it asks, and reply through the relay when a text response is needed. Use agent-relay /react <messageId> <emoji> for lightweight acknowledgement or approval. If Relay MCP tools are available, prefer relay_reply, relay_get_message, relay_get_thread, relay_send_message, relay_upload_artifact, relay_attach_artifact, relay_agent_status, relay_find_agents, relay_spawn_agent, and relay_shutdown_agent. You never need to know or pass your own agent id — relay fills it from your token; use relay_whoami only if you need to reason about yourself. relay_spawn_targets / relay_spawn_agent / relay_shutdown_agent only appear if your profile grants spawning (a live-children quota); when present, call relay_spawn_targets FIRST for the live host/provider/model matrix + your quota, then stand up long-living child agents and shut down your own — find them later with relay_find_agents spawnedBy:me. CLI fallback: agent-relay /reply <messageId> --stdin < response.md; if a delivered message says it was truncated, fetch the full body with: agent-relay get-message <messageId>. For command details, run: agent-relay /guide`;
176
176
 
177
- export const PROVIDER_MESSAGE_BODY_PREVIEW_CHARS = 4000;
177
+ // #306 deliver the FULL message body by default. Only a pathological body beyond this
178
+ // high cap truncates (with a get-message hint) so it can't nuke an agent's context; the 99%
179
+ // case (incl. multi-thousand-char handoffs/reports) arrives whole in one shot, no extra
180
+ // round-trip. The mirror flow (user typing directly) already injects full text — this removes
181
+ // the asymmetry where agent↔agent + attachment-bearing messages were second-class.
182
+ export const DEFAULT_PROVIDER_MESSAGE_BODY_MAX_CHARS = 24_000;
183
+
184
+ // Resolve the delivered-body cap. Deployment-dependent (the right ceiling tracks the host's
185
+ // context budget), so it's overridable via env — set on the orchestrator/host that spawns runners.
186
+ export function providerMessageBodyMaxChars(): number {
187
+ const raw = process.env.AGENT_RELAY_MESSAGE_BODY_MAX_CHARS;
188
+ if (raw !== undefined) {
189
+ const parsed = Number.parseInt(raw.trim(), 10);
190
+ if (Number.isFinite(parsed) && parsed > 0) return parsed;
191
+ }
192
+ return DEFAULT_PROVIDER_MESSAGE_BODY_MAX_CHARS;
193
+ }
178
194
 
179
195
  function attachmentRefs(message: Message): Record<string, unknown>[] {
180
196
  const payloadRefs = message.payload?.attachments;
@@ -280,15 +296,16 @@ export function providerAttachmentText(message: Message): string | undefined {
280
296
 
281
297
  export function providerMessageText(messages: Message[]): string {
282
298
  const replyable = latestReplyableMessage(messages);
299
+ const maxChars = providerMessageBodyMaxChars();
283
300
  const sections = messages
284
301
  .map((message) => {
285
302
  const subject = message.subject ? `Subject: ${message.subject}\n` : "";
286
303
  const isMemoryContext = isMemoryInjection(message);
287
304
  const canReferenceMessage = isPersistedRelayMessage(message) && !isMemoryContext && !isReactionNotification(message);
288
- const shouldPreview = canReferenceMessage && message.body.length > PROVIDER_MESSAGE_BODY_PREVIEW_CHARS;
305
+ const shouldPreview = canReferenceMessage && message.body.length > maxChars;
289
306
  const preview = shouldPreview
290
307
  ? {
291
- body: message.body.slice(0, PROVIDER_MESSAGE_BODY_PREVIEW_CHARS),
308
+ body: message.body.slice(0, maxChars),
292
309
  truncated: true,
293
310
  }
294
311
  : {
@@ -297,7 +314,7 @@ export function providerMessageText(messages: Message[]): string {
297
314
  };
298
315
  const truncationGuidance = preview.truncated
299
316
  ? [
300
- `[truncated: showing first ${PROVIDER_MESSAGE_BODY_PREVIEW_CHARS} of ${message.body.length} chars]`,
317
+ `[truncated: showing first ${maxChars} of ${message.body.length} chars]`,
301
318
  `Read full: agent-relay get-message ${message.id}`,
302
319
  `Body only: agent-relay get-message ${message.id} --body`,
303
320
  `Long reply: agent-relay /reply ${message.id} --stdin < response.md`,
package/src/mcp.ts CHANGED
@@ -820,10 +820,10 @@ async function relaySpawnAgent(auth: McpAuthContext, args: Record<string, unknow
820
820
  }
821
821
  }
822
822
 
823
- assertComponentResourceAllowed(auth, {
824
- scope: "agent:write",
825
- resource: { orchestratorId: orchestrator.id, cwd: resolvedCwd, policyName, spawnRequestId },
826
- });
823
+ // #323 — gate child spawn only on `orchestrators` (the parent's legit bound), NOT its self-scoping
824
+ // spawnRequestIds/cwdPrefixes/policies: those describe the child, not parent-owned resources, so
825
+ // gating on them makes maxSpawnedAgents unreachable for every component token (cwd checked above).
826
+ assertComponentResourceAllowed(auth, { scope: "agent:write", resource: { orchestratorId: orchestrator.id } });
827
827
 
828
828
  // Child runner token: a normal long-living agent that is NOT itself spawn-capable
829
829
  // (canSpawn:false → no grandchildren), stamped with authoritative lineage so it registers