Use request-scoped project agent config in hosted chat - #2724
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3c09a0b615
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| context: input.context, | ||
| ...(input.credentials ? { credentials: input.credentials } : {}), | ||
| ...(input.agentSource ? { agentSource: input.agentSource } : {}), | ||
| ...(input.agentConfig ? { agentConfig: input.agentConfig } : {}), |
There was a problem hiding this comment.
Add agentConfig to the internal stream schema
When an invocation includes this new field, buildRuntimeAgentControlPlaneStreamRequestFromInvocation now emits agentConfig, but AgentStreamHandler parses that transformed object with getInternalAgentStreamRequestSchema(), whose control-plane schema in src/internal-agents/schema.ts is .strict() and has no agentConfig field. In that environment, any runtime invocation carrying a request-scoped project agent config is accepted by RuntimeAgentRunInvocationSchema and then rejected as an invalid internal agent stream request before streaming, so the new project-agent-config path cannot work through /api/control-plane/runs/.../stream until the strict schema accepts the field.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
This pull request extends the runtime agent invocation contract and hosted-chat parsing so a project-selected agent definition (agentConfig) can be carried through hosted chat execution, and takes precedence over resolving a hosted agent config by agentId.
Changes:
- Add optional
agentConfigtoRuntimeAgentRunInvocationand propagate it into the control-plane stream request builder. - Preserve
agentConfigwhen converting runtime invocations into hosted chat requests, and prefer it over hosted config resolution. - Add hosted-chat validation to ensure
agentConfig.idmatches the requested agent id, plus tests covering preservation.
Verification
- Not run in this review environment.
- Recommended (per PR description):
deno fmt --check src/agent/runtime/agent-invocation-contract.ts src/agent/runtime/agent-invocation-contract.test.ts src/agent/hosted/chat-request-parser.ts src/agent/hosted/chat-request.test.ts src/agent/hosted/veryfront-cloud-agent-service.tsdeno test --allow-env src/agent/runtime/agent-invocation-contract.test.ts src/agent/hosted/chat-request.test.ts
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/agent/runtime/agent-invocation-contract.ts | Adds agentConfig to the runtime invocation schema and forwards it into the control-plane stream request. |
| src/agent/runtime/agent-invocation-contract.test.ts | Adds coverage to ensure agentConfig is preserved when building control-plane stream requests. |
| src/agent/hosted/chat-request-parser.ts | Threads agentConfig through hosted-chat parsing and validates agentConfig.id matches agentId. |
| src/agent/hosted/chat-request.test.ts | Adds coverage to ensure agentConfig is preserved when parsing runtime invocations into hosted chat requests. |
| src/agent/hosted/veryfront-cloud-agent-service.ts | Prefers request-scoped agentConfig over resolving hosted agent config by id during chat execution preparation. |
Comments suppressed due to low confidence (1)
src/agent/runtime/agent-invocation-contract.ts:324
agentConfigis accepted without any size bound and without validating that it matchesrun.agentId. This allows oversizedagentConfigpayloads (unboundedinstructions, arrays, etc.) and permits inconsistent invocations where the selected agent definition does not match the requested agent id. Adding a byte-size limit (similar tocontext) and a schema-level invariant check keeps the contract safe and consistent for all consumers, not just hosted-chat parsing.
export const getRuntimeAgentRunInvocationSchema = defineSchema((v) =>
v.object({
run: getRuntimeAgentRunContextSchema(),
messages: v.array(v.unknown()).default([]),
tools: v.array(getRuntimeAgentToolSchema()).max(50).default([]),
context: v.array(getRuntimeAgentContextItemSchema()).max(10).default([]).refine(
(value) => isWithinJsonSizeLimit(value, MAX_CONTEXT_TOTAL_BYTES),
{ message: "context must be less than 64 KB total" },
),
agentSource: getRuntimeAgentSourceContextSchema().optional(),
agentConfig: getRuntimeAgentMarkdownDefinitionSchema().optional(),
credentials: getRuntimeAgentCredentialsSchema().optional(),
forwardedProps: v.record(v.string(), v.unknown()).optional().refine(
(value) => value === undefined || isWithinJsonSizeLimit(value, MAX_FORWARDED_PROPS_BYTES),
{ message: "forwardedProps must be less than 192 KB" },
),
})
);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| }); | ||
|
|
Critical review — Score: 86 / 100Solid, well-scoped change that threads request-scoped What's good
Concerns
VerdictReady to merge after (a) a one-line comment documenting the API-is-trusted-caller assumption at the Automated critical review. 👍/👎 welcome. |
Re-review (deeper pass) — Score revised: 68 / 100 (was 86)No new commits since the first review ( 🔴 Main finding —
|
| Consumer | Endpoint | Honors agentConfig? |
|---|---|---|
veryfront-cloud-agent-service.ts → prepareChatExecution (req.agentConfig ?? …) |
Node per-agent /api/runs |
✅ yes (wired + tested) |
agent-stream.handler.ts (AgentStreamHandler) |
/api/control-plane/runs/:runId/stream |
❌ no — dropped |
On the control-plane stream path:
parseAgentStreamPayloadnow acceptsagentConfig(this PR's schema fix), andinternal-agents/schema.test.tsasserts it survives parsing.- But
toRuntimeRunAgentInput(payload)(src/internal-agents/schema.ts:322) maps onlythreadId, runId, parentRunId, state, messages, tools, context, forwardedProps— it omitsagentConfig, andAgUiRuntimeRequest(ag-ui-contract.ts:166) has no such field. - The agent that actually executes is resolved server-side:
const agent = this.deps.getAgent(payload.agentId)(agent-stream.handler.ts:651) — the deployed/registered agent, ignoring the request-scoped config. grep -c agentConfig src/server/handlers/request/agent-stream.handler.ts→ 0. The handler never reads it.
Why this matters: the paired API PR (veryfront-api#3768, "Preserve project agent config for runtime skills") builds agentConfig from the structured project-agent source and attaches it to the control-plane runtime request (enqueue-project-agent-run / buildRuntimeRequestForProjectAgent). That request is served by AgentStreamHandler — the path that discards it. So for project-agent durable runs, the selected skills/tools/persona do not survive into execution, which is the stated goal of both PRs.
This is exactly what the codex P2 comment was reaching for: it said the field couldn't work through /api/control-plane/runs/.../stream "until the strict schema accepts the field." Accepting it was necessary but not sufficient — the handler still has to use it. Right now the change converts a loud 400 rejection into a silent no-op, which is arguably worse (no signal that config was ignored).
Ask: confirm which handler serves the API's project-agent runs. If any flow through AgentStreamHandler (they appear to), wire payload.agentConfig into the runtime agent there — e.g. build/override the Agent from agentConfig instead of, or layered onto, getAgent(payload.agentId), and carry it through toRuntimeRunAgentInput. Only prepareChatExecution honors it today.
🟠 The internal-schema test gives false confidence
internal-agents/schema.test.ts asserts internalRequest.agentConfig equals the input — but that checks the parsed request, not the RuntimeRunAgentInput produced by toRuntimeRunAgentInput, which is what feeds the runtime and where the field is lost. A test that round-trips through toRuntimeRunAgentInput (and asserts the runtime actually applies the config) would have caught the drop. As written, the test locks in the preserved-then-discarded behavior.
Still valid from the first review
- Trust boundary:
agentConfig(instructions/skills/tools/model) is taken verbatim, bypassingresolveAgentConfig(); only gated by id-match + 64 KB. Fine if the API is the sole authenticated caller, but document it. tools:true/skills:truepassthrough remains possible via request-scoped config.- Dead/untested mismatch branch in
chat-request-parser.ts:131(unreachable; invocationsuperRefinefires first with a different message). - Duplicated
MAX_AGENT_CONFIG_BYTESconstant.
What's genuinely good
The contract/schema plumbing, id-match refinements at every boundary, size caps, and the prepareChatExecution seam are clean and correct. The hosted-cloud-agent-service path works and is well tested. Local run of the three touched test files still passes (39 steps, 0 failed).
Verdict
Not mergeable as "project agent config survives into execution" until the control-plane stream handler consumes agentConfig (or you confirm project-agent runs never touch that handler). The lower score reflects a headline capability that is silently inert on its primary path, plus a test that masks it — not a code-quality regression in what is wired.
Automated critical re-review. 👍/👎 welcome.
|
Addressed the latest runtime-agent-config review feedback in What changed:
Local verification:
Normal pre-push hook got through format, lint, typecheck, and 2,219 unit tests, then failed on unrelated host observability env leakage:
Pushed with |
|
Final PR-feedback follow-up status:
|
Re-review #3 (post-merge) — Score revised: 90 / 100 (was 68)Two follow-up commits ( ✅ The silent-drop is fixed —
|
Summary
Verification
Notes