A reply can stop and ask you something
Three features turn out to be one mechanism: a command waiting to be approved, a question the model wants answered, and "this reply is waiting for you" are all — stop the generation, put an interactive block in the bubble, wait for a POST, carry on. So there is one primitive, and the only thing using it so far is `ask_user`: a model can offer you a few answers and a box to write your own. The shell executor is not here yet. This lands first on purpose, because it is the riskiest machinery in the feature and it is worth having working before any subprocess exists to complicate it. Two things about where the pause sits. It pauses a round, not a call: a round's calls run together under a semaphore, and parking four coroutines on four separate answers inside that gather would queue them behind each other invisibly. And Stop had to be taught about it — `cancel` is read between streamed chunks and there are no chunks while paused, so the button did nothing at all until `request_stop` learned to resolve the pause itself. Also here: a risk class on every tool (read, write, execute), which is what the four permission modes will be a table over, and the systemd unit loses ProtectKernelTunables. That last one is not tidying — it bind-mounts /proc/sys read-only, which stops bubblewrap mounting /proc at all, and the obvious workaround would expose this process's environment and with it the encryption key. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
{% from "_macros.html" import icon %}
|
||||
{#
|
||||
The reply has stopped and is waiting for you.
|
||||
|
||||
Two shapes, one mechanism: a question the model asked, and (later) a command
|
||||
waiting to be allowed. Everything shown here is model output and is escaped
|
||||
accordingly -- the question text, the options on the buttons and the command
|
||||
itself all came from a model that may have been reading somebody else's file
|
||||
a moment ago.
|
||||
|
||||
Deliberately attributed to the model rather than styled as if LLeMbas were
|
||||
asking. A question that looks like it came from the application is a question
|
||||
people answer with things they would not tell a chatbot.
|
||||
|
||||
hx-swap="none" because the SSE stream clears this card the moment the answer
|
||||
lands; swapping a response in here would fight it.
|
||||
#}
|
||||
<div class="interaction interaction--{{ ask.kind }}">
|
||||
<p class="interaction__from">
|
||||
{{ icon("sparkle", "icon--sm") }}
|
||||
<span>The model is asking you</span>
|
||||
</p>
|
||||
|
||||
{% for item in ask.items %}
|
||||
<div class="interaction__item">
|
||||
<p class="interaction__title">{{ item.title }}</p>
|
||||
{% if item.detail %}
|
||||
<pre class="interaction__detail">{{ item.detail }}</pre>
|
||||
{% endif %}
|
||||
{% if item.reason %}
|
||||
<p class="interaction__reason">{{ item.reason }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<form class="interaction__actions"
|
||||
hx-post="/api/chats/{{ chat_id }}/interaction/{{ ask.id }}"
|
||||
hx-swap="none">
|
||||
{% if ask.kind == "question" %}
|
||||
{% for option in ask.options %}
|
||||
<button class="btn" type="submit" name="choice" value="{{ option }}">{{ option }}</button>
|
||||
{% endfor %}
|
||||
{% if ask.allow_free_text %}
|
||||
{# Never type="password". A model talked into asking for a credential
|
||||
must not be handed a field that looks built for one, and a transcript
|
||||
is not a place to put secrets. #}
|
||||
<div class="interaction__write">
|
||||
<input class="input" type="text" name="text" autocomplete="off"
|
||||
placeholder="Or write your own answer…">
|
||||
<button class="btn btn--primary" type="submit">
|
||||
{{ icon("send", "icon--sm") }} Answer
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<button class="btn btn--primary" type="submit" name="choice" value="allow">
|
||||
{{ icon("check", "icon--sm") }} Allow
|
||||
</button>
|
||||
<button class="btn" type="submit" name="choice" value="allow_always">
|
||||
Always allow this
|
||||
</button>
|
||||
<button class="btn btn--danger" type="submit" name="choice" value="deny">
|
||||
{{ icon("x", "icon--sm") }} Don't
|
||||
</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
Reference in New Issue
Block a user