let agent decide

This commit is contained in:
Ian Gulliver
2026-10-16 22:18:00 -07:00
parent 6cc71b859e
commit 7e0e6d9585
5 changed files with 22 additions and 23 deletions
+1 -1
View File
@@ -245,7 +245,7 @@ An open byre page shows a browser notification for each new inbox item, raised b
The left pane's **Settings** tab asks the browser for notification permission and edits the preferences.
The Inbox tab shows one item at a time, the task's oldest, filling the tab without a card frame; the next appears when it is answered. Each item is answered on its own; there is no batch submission. A pick-one question's choices are clickable blocks, and clicking one sends that answer at once. A choice's block (or a pick-any choice's checkbox label) holds the whole list item: its first line, which is the choice's text, and everything under it, such as an example's paragraph or code block. A pick-any question's choices are checkboxes, sent with the Submit button at the bottom of the card. Below the choices, a "Let the agent decide" block sends at once an `other` answer reading "Use your judgment for this decision and continue within the existing task instructions." (`delegateText` in `web/src/inbox.ts`), which shows in the card's answer list like any other; the `ask_human` description tells agents that this answer settles only that decision and never grants an authorization the owner must give explicitly. Under it, an "Other…" block in the same style opens the only text field. The choice blocks, the checkboxes and these two blocks take focus with Tab, and ArrowDown/ArrowUp move focus between them; the focused one gets a 2px accent ring. A card has at most one Submit button: on a pick-any question it sends the checked choices with the Other text alongside (one `option` response), or the Other text alone as `other` when nothing is checked. An error has a "Reply…" block (`other`) and a Dismiss button. The Other and Reply text boxes send with the Submit button under them or on Enter (Shift+Enter for a newline), like the message box, and fold back into their block when left empty. The list updates in place: a card stays attached while its item is unchanged, so task and inbox updates from other agents don't pull focus out of a text box being typed in.
The Inbox tab shows one item at a time, the task's oldest, filling the tab without a card frame; the next appears when it is answered. Each item is answered on its own; there is no batch submission. A pick-one question's choices are clickable blocks, and clicking one sends that answer at once. A choice's block (or a pick-any choice's checkbox label) holds the whole list item: its first line, which is the choice's text, and everything under it, such as an example's paragraph or code block. A pick-any question's choices are checkboxes, sent with the Submit button at the bottom of the card. Below the choices, an "Other…" block in the same style opens the only text field. Under it, every question has one submit row: a "Let the agent decide" button at its left, away from the choices so it isn't hit by accident, and the Submit button at its right. Let the agent decide sends at once an `other` answer reading "Use your judgment for this decision and continue within the existing task instructions." (`delegateText` in `web/src/inbox.ts`), which shows in the card's answer list like any other; the `ask_human` description tells agents that this answer settles only that decision and never grants an authorization the owner must give explicitly. The choice blocks, the checkboxes and the Other block take focus with Tab, and ArrowDown/ArrowUp move focus between them; the focused one gets a 2px accent ring. A question has exactly one Submit button, enabled once there is something to send: on a pick-one question it sends the Other text as `other`; on a pick-any question it sends the checked choices with the Other text alongside (one `option` response), or the Other text alone as `other` when nothing is checked. An error has a "Reply…" block (`other`) and a Dismiss button. The Other and Reply text boxes send with the Submit button under them or on Enter (Shift+Enter for a newline), like the message box, and fold back into their block when left empty. The list updates in place: a card stays attached while its item is unchanged, so task and inbox updates from other agents don't pull focus out of a text box being typed in.
There is no snoozing. The `snoozed_until` and `snooze_until` columns are still in the schema, always empty, so builds from older branches keep working against the same database; nothing reads them. For the same reason `inbox_items` keeps its `priority` and `blocking` columns, which nothing reads or writes; new rows take their defaults (0 and 1, which older builds read as a blocking question).
+8 -10
View File
@@ -181,24 +181,22 @@ function card(item) {
};
return h("div", { class: "text-entry" }, block, editor);
};
const delegate = () => clickable(h("div", { class: "pick other", role: "button", tabIndex: 0 }, "Let the agent decide"), () => {
void submit({ item_id: item.id, type: "other", text: delegateText });
});
const delegate = () => {
const button = h("button", { type: "button", class: "delegate" }, "Let the agent decide");
button.onclick = () => void submit({ item_id: item.id, type: "other", text: delegateText });
return button;
};
const renderActions = () => {
switch (item.kind) {
case "question": {
const placeholder = "Your answer, a question back, or new instructions";
if (!multiple) {
replace(actions, delegate(), textEntry("Other…", placeholder));
return;
}
const send = h("button", { type: "button", class: "primary" }, "Submit");
const typed = () => (textAnswer(item.id) ?? "").trim();
const refresh = () => {
send.disabled = !typed() && checkedAnswer(item.id).length === 0;
send.disabled = !typed() && (!multiple || checkedAnswer(item.id).length === 0);
};
const go = () => {
const options = checkedAnswer(item.id);
const options = multiple ? checkedAnswer(item.id) : [];
if (options.length > 0) {
void submit({ item_id: item.id, type: "option", options, text: typed() ? textAnswer(item.id) : undefined });
return;
@@ -210,7 +208,7 @@ function card(item) {
const other = textEntry("Other…", placeholder, { onInput: refresh, onSubmit: go });
send.onclick = go;
refresh();
replace(actions, delegate(), other, h("div", { class: "submit-row" }, send));
replace(actions, other, h("div", { class: "submit-row" }, delegate(), send));
return;
}
default: {
+1 -1
View File
File diff suppressed because one or more lines are too long
+8 -11
View File
@@ -208,26 +208,23 @@ function card(item: InboxItem): HTMLElement {
return h("div", { class: "text-entry" }, block, editor);
};
const delegate = () =>
clickable(h("div", { class: "pick other", role: "button", tabIndex: 0 }, "Let the agent decide"), () => {
void submit({ item_id: item.id, type: "other", text: delegateText });
});
const delegate = () => {
const button = h("button", { type: "button", class: "delegate" }, "Let the agent decide");
button.onclick = () => void submit({ item_id: item.id, type: "other", text: delegateText });
return button;
};
const renderActions = () => {
switch (item.kind) {
case "question": {
const placeholder = "Your answer, a question back, or new instructions";
if (!multiple) {
replace(actions, delegate(), textEntry("Other…", placeholder));
return;
}
const send = h("button", { type: "button", class: "primary" }, "Submit");
const typed = () => (textAnswer(item.id) ?? "").trim();
const refresh = () => {
send.disabled = !typed() && checkedAnswer(item.id).length === 0;
send.disabled = !typed() && (!multiple || checkedAnswer(item.id).length === 0);
};
const go = () => {
const options = checkedAnswer(item.id);
const options = multiple ? checkedAnswer(item.id) : [];
if (options.length > 0) {
void submit({ item_id: item.id, type: "option", options, text: typed() ? textAnswer(item.id) : undefined });
return;
@@ -239,7 +236,7 @@ function card(item: InboxItem): HTMLElement {
const other = textEntry("Other…", placeholder, { onInput: refresh, onSubmit: go });
send.onclick = go;
refresh();
replace(actions, delegate(), other, h("div", { class: "submit-row" }, send));
replace(actions, other, h("div", { class: "submit-row" }, delegate(), send));
return;
}
default: {
+4
View File
@@ -1820,6 +1820,10 @@ pre.file-text .line::before {
justify-content: flex-end;
}
.submit-row .delegate {
margin-right: auto;
}
.reply-row {
display: flex;
align-items: flex-start;