Files
kandev/apps/backend/config
João Salavisa 744aabee96 fix(mcp): keep spawner attribution on a spawned session's first turn (#2047)
* fix(mcp): keep spawner attribution on a spawned session's first turn

A session started through spawn_session_kandev never learned who spawned
it, so it had no way to report back. The MCP handler did prepend a
<kandev-system> attribution block, but the first-turn canonicalizer
(sysprompt.canonicalizeKandevContext) drops every system block it cannot
attribute to server state — and the spawn block was not on the trusted
list, so it was deleted before the agent ever saw it. Spawns are always a
first turn, so this happened every time.

Generate the block at the launch site instead, from server-resolved
identity, and whitelist that exact content for the injector — the same
pattern entity references already use:

- config/prompts/spawned-session.md holds the attribution template.
- sysprompt.SpawnedSessionContext builds it; sysprompt.StripTags (lifted
  out of the handlers' stripSystemTag) keeps embedded IDs from closing
  the block early.
- LaunchSessionRequest.SpawnOrigin carries the spawner identity and is
  kept off the wire (json:"-") so a WS client cannot forge a trusted
  system block.
- startTask prepends the block and passes the identical content as
  trusted content to both the task and Office injectors. Its trailing
  env/route parameters move into a startTaskOptions struct rather than
  growing the positional list again.
- The MCP handler now only fills SpawnOrigin (including the spawner's
  session name) and passes the prompt through untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(mcp): derive spawn attribution from the verified spawner session

Addresses both PR review findings.

CodeRabbit: spawnOrigin copied sender_task_id / sender_session_id straight
out of the request, so an unknown or cross-task sender_session_id became
trusted launch attribution — the spawned agent would be told to report to
a session that never asked for it. Resolve the spawner session once, up
front, require it to belong to the claimed sender task, and derive the
origin (and the inherited agent profile) from that row. An unverifiable
claim now yields no attribution instead of a forged one. This also drops
a duplicate session lookup.

Codex: passthrough profiles skip the Kandev MCP wrap, so the new
attribution never reached a spawned passthrough session even though it
gets MCP configured and can reply. Give that path plain-text attribution
(no <kandev-system> markup, which would show up raw in the TTY the user
is watching). The wrap branch moves into applyLaunchPromptContext, which
keeps startTask under the nesting limit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 22:51:40 +01:00
..