Conversation
Every `Outcome::NeedsInput` question a tool asks now produces a paired
`InquiryRequest`/`InquiryResponse` in the conversation stream, no
matter how it gets answered: interactive prompt, a "remember for this
turn" cache hit, or a static `QuestionConfig.answer`. Previously only
questions routed through the LLM inquiry backend were recorded, so the
audit trail was incomplete for the common TTY-prompt and static-answer
paths.
`InquiryResponse` is now an enum instead of a flat struct:
`Answered { id, answer }`, `Cancelled { id, reason }`, and
`Redacted { id }`. Cancellation carries a `CancellationReason` (user,
backend error, or one of two routing-guard reasons) so a closed-without-
answer inquiry no longer disappears from the stream. Deserialization
still accepts the old pre-082 flat `{ id, answer }` shape as `Answered`,
so existing conversation histories keep loading.
`jp_tool::Question` gains a `Secret` answer type for values that must
not be persisted, e.g. `jp_tool::Question::secret("token", "API key")`.
A secret's answer reaches the tool in-memory but is recorded as
`Redacted` on disk, is never echoed at the terminal prompt (via a new
`PromptBackend::password` method), and is refused if routing would send
it to the assistant or fall back to a non-interactive backend. Question
IDs are now validated at construction (`QuestionId` rejects a `.`),
since the persisted inquiry ID format changes from
`<tool_call_id>.<question_id>` to `<tool_call_id>.<question_id>.<attempt>`
to stay unique when a question is re-asked within a turn.
`TurnState` splits its old single `persisted_inquiry_responses` map into
`remembered_tool_answers` and `remembered_permission_decisions`, keyed by
new `ToolAnswerCacheKey`/`PermissionCacheKey` types, so a "remember"
answer to a tool question can no longer be conflated with a tool's
permission decision cache.
Orphaned-inquiry cleanup in `ConversationStream::sanitize` is now
turn-scoped, so an `InquiryId` reused across turns can no longer
cross-satisfy a request/response pair from a different turn.
Implements RFD 082, promoted from Discussion to Accepted.
Signed-off-by: Jean Mertz <git@jeanmertz.com>
Signed-off-by: Jean Mertz <git@jeanmertz.com>
Signed-off-by: Jean Mertz <git@jeanmertz.com>
Signed-off-by: Jean Mertz <git@jeanmertz.com>
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.
Every
Outcome::NeedsInputquestion a tool asks now produces a pairedInquiryRequest/InquiryResponsein the conversation stream, no matter how it gets answered: interactive prompt, a "remember for this turn" cache hit, or a staticQuestionConfig.answer. Previously only questions routed through the LLM inquiry backend were recorded, so the audit trail was incomplete for the common TTY-prompt and static-answer paths.InquiryResponseis now an enum instead of a flat struct:Answered { id, answer },Cancelled { id, reason }, andRedacted { id }. Cancellation carries aCancellationReason(user, backend error, or one of two routing-guard reasons) so a closed-without- answer inquiry no longer disappears from the stream. Deserialization still accepts the old pre-082 flat{ id, answer }shape asAnswered, so existing conversation histories keep loading.jp_tool::Questiongains aSecretanswer type for values that must not be persisted, e.g.jp_tool::Question::secret("token", "API key"). A secret's answer reaches the tool in-memory but is recorded asRedactedon disk, is never echoed at the terminal prompt (via a newPromptBackend::passwordmethod), and is refused if routing would send it to the assistant or fall back to a non-interactive backend. Question IDs are now validated at construction (QuestionIdrejects a.), since the persisted inquiry ID format changes from<tool_call_id>.<question_id>to<tool_call_id>.<question_id>.<attempt>to stay unique when a question is re-asked within a turn.TurnStatesplits its old singlepersisted_inquiry_responsesmap intoremembered_tool_answersandremembered_permission_decisions, keyed by newToolAnswerCacheKey/PermissionCacheKeytypes, so a "remember" answer to a tool question can no longer be conflated with a tool's permission decision cache.Orphaned-inquiry cleanup in
ConversationStream::sanitizeis now turn-scoped, so anInquiryIdreused across turns can no longer cross-satisfy a request/response pair from a different turn.Implements RFD 082, promoted from Discussion to Accepted.