Skip to content

Voice: source.policy is read at call time with no deadline, and can disagree with the merge #350

Description

@kshivang

Follow-up from the review of #349. Not a blocker there; recording it so it isn't lost.

CompositeVoiceToolExecutor.kt:248:

val approve = withContext(Dispatchers.IO) { runCatching { source.policy.approve }.getOrNull() }

No deadline. enumerate() deliberately puts every embedder getter — namePrefix, policy, excludeExtra, irreversibleExtra — behind the 2s enumeration deadline, pinned by VoiceToolSourceIsolationTest."a hanging classifier does not hang the call". This read sits outside it, and runCatching answers a getter that throws while doing nothing for one that hangs — which is the enumeration KDoc's own argument.

Reachability is narrow: a getter that hangs consistently already fails enumeration, so the tool is never advertised and this line is never reached. It needs one that blocks only sometimes — e.g. taking a plugin-registry lock held by a concurrent reload. Blast radius is an IO thread pinned until the 120s watchdog.

It can also disagree with what was advertised. hostApproves is captured at merge time (VoiceToolMerge.kt:228) and decides whether voice_confirm appears in the advertised schema; approve is re-read live.

  • Approver installed mid-call: the model was told about a token protocol that now never runs — harmless.
  • Approver removed mid-call: the in-band gate takes over and hands back a voice_confirm token for a parameter the advertised schema never declared, so the model has no sanctioned way to redeem it and the gated tool becomes a confirm loop.

Both fall out of one change: have AdvertisedExternalTool carry the resolved approve (or the whole resolved VoiceToolPolicy) from the merge, which already ran inside the deadline, and drop the live read. gated is already snapshotted that way — this makes approve consistent with it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions