Pass attachments to subagents directly and set subagent mode - #649
Draft
hanna-paasivirta wants to merge 5 commits into
Draft
Pass attachments to subagents directly and set subagent mode#649hanna-paasivirta wants to merge 5 commits into
hanna-paasivirta wants to merge 5 commits into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Short Description
Attachments (run logs, dataclips) now reach subagents as the exact text the user sent. Before, only the planner's own message got through, so a subagent saw a summary of a log, or nothing. Attachments also no longer stick to the conversation history, and oversized ones are refused rather than quietly cut down.
Fixes #643
Implementation Details
The router used to splice attachments into the user's message as text. Nothing carried them any further if the planner was called. So if a user attached a run log and asked "why did the last two steps fail?", the
job_chatcall got the planner's summary of the log instead of the log. The workflow YAML already travels as a payload field, and now attachments do as well.Each agent receives them the way it already receives that kind of context.
job_chatis unchanged — byte-identical tomain. It already hadcontext.log/context.input/context.output, which render as<run_logs>/<input>/<output>. Lightning already uses them. Nothing to regress in a production service.workflow_chatand the planner have no such fields. They take anattachmentspayload field and render it where their other context goes. Forworkflow_chatthat is the end of the system message, next toexisting_yaml, and only when attachments are present. With none, its prompt is byte-identical to before.input_dataclipandrun_inputboth mean input; the two output types likewise. When two attachments want the same field they are joined, not overwritten, so neither is lost. This clash comes from the mapping. It does not exist onmain, where each field has one source.job_chatchecks a fixed list of context keys rather than reading the whole dict, so an unknown key is ignored today too. A new type needs one line in the mapping.Who decides what a subagent gets depends on the route.
state.patientstostate.cases" is not billed for a log the planner has already read. Both tools take a requiredattachmentslist of types.Attachments no longer stick to the history. The enriched string used to be what got saved, so an attached log became a permanent turn. It was re-sent on every later turn with nothing to show it belonged to an earlier run. Attachments now belong to the turn they arrived on. That makes re-sending them the client's job, which is a contract change for Lightning and is documented in
PAYLOAD_SPEC.md.Planner-invoked subagents were running in production mode.
call_job_agentnever setsubagent: True. It also put the YAML incontext["workflow_yaml"], whichPayload.from_dictdoes not read. Sojob_chatran under the production scope prompt ("You ONLY help with job code... tell them to navigate to the workflow overview"), with no<workflow_structure>block and noinspect_job_codetool. The router's direct route set both correctly.call_workflow_agenthad the same defect. It kept its "save your workflow and go to the Inspector" instruction. Both now run in subagent mode.job_chattheedit_workflowescalation tool, and its target is the planner — which is now the caller. A handover returns an empty response, soformat_subagent_result_for_llmturns it into the reason that agent could not finish, for the planner to act on with a different tool.Attachments are never trimmed. Oversized ones are refused. Shortening an attachment would mean answering from evidence the user thinks we read in full. Context Apollo injects itself, like adaptor docs, is a different case and is still truncated.
400 ATTACHMENT_TOO_LARGEbefore any model is called. The error names the largest attachment so the client can offer a shorter selection.max_tokensreserve, the static prompt and adaptor docs are accounted for. The arithmetic sits next to the constant so it can be re-derived if the model or window changes.Adds unit tests for the mapping, the selection, the size guard and the subagent payloads. Also adds the repo's first
service-tier tests: the whole router → planner →job_chatchain runs with every LLM call scripted. The attachment carries an improbable canary, so "did this arrive" is a check on bytes rather than a judgement on wording. No tokens, no human review. One acceptance spec covers the planner reasoning from a log across steps. The attachment types in the existing specs now matchPAYLOAD_SPEC.md. The four test tiers are described inservices/testing/README.md; they previously existed only inpyproject.tomlmarkers and the rootconftest.py.Left alone on purpose: a genuinely too-long prompt still surfaces differently per route.
job_chatgives a cleanPROMPT_TOO_LONG,workflow_chata rawBAD_REQUEST, the planner a500 "Tool execution error". Attachments can no longer cause it, but a long conversation still can, so it wants its own change.AI Usage
Please disclose whether you've used AI in this work (it's cool, we just want to
know!):
You can read more details in our
Responsible AI Policy