feat(agents): evolvable agent dependencies for flow agents#39
feat(agents): evolvable agent dependencies for flow agents#39zrosenbauer merged 4 commits intomainfrom
Conversation
Flow agents can now declare named agent dependencies via the `agents` config field. These are passed to the handler in params and are shallow-merged by `evolve()`, solving the closure capture problem where evolve() couldn't rewire agents referenced inside a flow handler. Co-Authored-By: Claude <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: 3813f4a The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
Caution Review failedPull request was closed or merged during review No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds support for named sub-agent dependencies for flow agents: new Changes
Sequence Diagram(s)sequenceDiagram
participant Dev as Dev / Evolve call
participant Evolve as evolve()
participant FlowAgent as FlowAgent (prepared)
participant Handler as Flow Handler
participant SubAgent as Sub-agent
Dev->>Evolve: evolve(flow, { agents: { core: evolvedCore } })
Evolve->>FlowAgent: mergeFlowAgentConfigs(base, overrides with agents)
Note right of Evolve: shallow-merge `agents` (base + override)
FlowAgent->>FlowAgent: prepare state (freeze agents)
FlowAgent->>Handler: invoke handler(..., agents: frozenAgents)
Handler->>SubAgent: call via $.agent(agents.core)
SubAgent-->>Handler: respond
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 📝 Coding Plan
Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/agents/src/core/agents/flow/flow-agent.ts (1)
357-362:⚠️ Potential issue | 🟠 MajorPrevent shared
agentsconfig from leaking across flow invocations.At lines 361 and 458,
config.agentsis passed by reference into the handler. If a handler mutates this record, it mutates the shared flow config and affects subsequent invocations. Per guidelines: "Never mutate shared state or function arguments."Apply a defensive shallow copy with
Object.freeze():Fix
const output = await (handler as FlowAgentHandler<TInput, TOutput>)({ input: parsedInput, $, log, + agents: Object.freeze({ ...(config.agents ?? {}) }), - agents: config.agents ?? {}, });Repeat at both lines 361 and 458.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/agents/src/core/agents/flow/flow-agent.ts` around lines 357 - 362, Prevent mutation of shared flow config by creating a defensive, shallow frozen copy of config.agents before passing it into handler calls: replace direct uses of "config.agents ?? {}" in the FlowAgentHandler invocation (the call where "handler as FlowAgentHandler<TInput, TOutput>" is invoked) and the second handler invocation later in the file (the other place currently passing "config.agents ?? {}") with a local variable like "const agents = Object.freeze({ ...(config.agents ?? {}) })" and pass that "agents" variable instead so handlers receive an immutable shallow copy rather than a reference to the shared config.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@packages/agents/src/core/agents/flow/flow-agent.ts`:
- Around line 357-362: Prevent mutation of shared flow config by creating a
defensive, shallow frozen copy of config.agents before passing it into handler
calls: replace direct uses of "config.agents ?? {}" in the FlowAgentHandler
invocation (the call where "handler as FlowAgentHandler<TInput, TOutput>" is
invoked) and the second handler invocation later in the file (the other place
currently passing "config.agents ?? {}") with a local variable like "const
agents = Object.freeze({ ...(config.agents ?? {}) })" and pass that "agents"
variable instead so handlers receive an immutable shallow copy rather than a
reference to the shared config.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 0b3da990-87ee-4adc-b90e-f04005328c06
📒 Files selected for processing (7)
.changeset/flow-agent-agents-dep.mdpackages/agents/src/core/agents/evolve.test.tspackages/agents/src/core/agents/evolve.tspackages/agents/src/core/agents/flow/flow-agent.test.tspackages/agents/src/core/agents/flow/flow-agent.tspackages/agents/src/core/agents/flow/types.tspackages/agents/src/index.ts
Create a shallow frozen copy of config.agents in prepareFlowAgent() so handlers receive an immutable snapshot rather than a direct reference to the shared config object. Co-Authored-By: Claude <noreply@anthropic.com>
The `?? {}` fallback is unnecessary inside a spread — spreading
`undefined` is already a no-op in object literals.
Co-Authored-By: Claude <noreply@anthropic.com>
Summary
agentsfield toFlowAgentConfigso flow agents can declare named agent dependenciesagentsto the handler viaFlowAgentParams, defaulting to{}when not configuredevolve()now shallow-mergesagentson flow agents (same as it does for regular agents'tools/agents)FlowSubAgentstype from@funkai/agentsThis solves the closure capture problem: when a flow handler references a module-level agent import,
evolve()can't rewire it. With this change, handlers useagents.coreinstead of a closed-overcoreAgent, making the reference evolvable.Before
After
Test plan
evolve.test.ts: shallow merge, preserve on no override, handler receives evolved agents at runtime, mapper overloadflow-agent.test.ts: handler receives agents from config, empty record default, streaming pathtsc --noEmit)