Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/approval-allow-cancel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"eve": minor
---

Tool approval responses now use `cancel` instead of `deny`, while retaining `approve` for the positive response, aligning the public protocol with the user-facing flow-control semantics.
2 changes: 1 addition & 1 deletion docs/concepts/sessions-runs-and-streaming.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ curl -X POST http://127.0.0.1:2000/eve/v1/session/<sessionId> \

The follow-up reuses the same durable session: same history, same state.

If the session is waiting on a human-in-the-loop approval, a matching text reply such as `approve` or `deny` answers the approval. Other follow-up text is held until the approval is answered, so an unrelated message does not implicitly deny the pending tool call.
If the session is waiting on a human-in-the-loop approval, a matching text reply such as `approve` or `cancel` answers the approval. Other follow-up text is held until the approval is answered, so an unrelated message does not implicitly deny the pending tool call.

If the session is waiting on `ask_question`, a follow-up message clears that pending request before the model continues. An exact option match or permitted freeform response answers the question; any other message marks the question unanswered and starts the follow-up turn.

Expand Down
2 changes: 1 addition & 1 deletion docs/guides/frontend/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ if (request) {
}
```

`request.prompt` and `request.options` give you what you need to render the approve and deny UI. The default reducer marks the part as responded immediately, then updates it again once eve streams the resumed result.
`request.prompt` and `request.options` give you what you need to render the approve and cancel UI. The default reducer marks the part as responded immediately, then updates it again once eve streams the resumed result.

## Authorization prompts

Expand Down
2 changes: 1 addition & 1 deletion docs/tools/human-in-the-loop.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Approvals and questions share one protocol:
1. The model requests input (an approval, or an `ask_question`).
2. eve emits an `input.requested` stream event carrying the pending requests.
3. The turn parks at `session.waiting`, durably, for as long as it takes.
4. The client answers with `inputResponses` (structured, keyed by `requestId`) or a normal follow-up `message`. A follow-up whose text matches an option ID, option label, or numeric option index resolves automatically, including approval options such as `approve` and `deny`.
4. The client answers with `inputResponses` (structured, keyed by `requestId`) or a normal follow-up `message`. A follow-up whose text matches an option ID, option label, or numeric option index resolves automatically, including approval options such as `approve` and `cancel`.

The run picks back up exactly where it parked. Because the pause is durable, nothing is held in memory while it waits — the process can restart and the parked turn survives.

Expand Down
2 changes: 1 addition & 1 deletion docs/tutorial/ship-it.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export default function Page() {
}
```

`agent.data.messages` and `agent.status` cover most chat UIs. The hook also surfaces HITL prompts (the spend approval from [Step 8](./guard-the-spend)), so the dashboard can render approve/deny controls. For the full API, see [Frontend](../guides/frontend/overview).
`agent.data.messages` and `agent.status` cover most chat UIs. The hook also surfaces HITL prompts (the spend approval from [Step 8](./guard-the-spend)), so the dashboard can render approve/cancel controls. For the full API, see [Frontend](../guides/frontend/overview).

## Replace `placeholderAuth`

Expand Down
2 changes: 1 addition & 1 deletion packages/eve/src/channel/resolve-text.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const APPROVAL_REQUEST: InputRequest = {
display: "confirmation",
options: [
{ id: "approve", label: "Approve", style: "primary" },
{ id: "deny", label: "Deny", style: "danger" },
{ id: "cancel", label: "Cancel", style: "danger" },
],
prompt: 'Approve tool "bash"?',
requestId: "req-1",
Expand Down
2 changes: 1 addition & 1 deletion packages/eve/src/cli/dev/tui/runner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -905,7 +905,7 @@ describe("EveTUIRunner native continuation state", () => {
display: "confirmation",
options: [
{ id: "approve", label: "Approve" },
{ id: "deny", label: "Deny" },
{ id: "cancel", label: "Cancel" },
],
prompt: "Approve get_weather?",
requestId: "request-1",
Expand Down
2 changes: 1 addition & 1 deletion packages/eve/src/cli/dev/tui/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -868,7 +868,7 @@ export class EveTUIRunner {
const response = await this.#renderer.readToolApproval(request, { title });
responses.push({
requestId: request.approvalId,
optionId: response.approved ? "approve" : "deny",
optionId: response.approved ? "approve" : "cancel",
});
this.#pendingInputRequests.delete(request.approvalId);
}
Expand Down
14 changes: 7 additions & 7 deletions packages/eve/src/client/message-reducer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ describe("defaultMessageReducer", () => {
display: "confirmation",
options: [
{ id: "approve", label: "Yes", style: "primary" },
{ id: "deny", label: "No", style: "danger" },
{ id: "cancel", label: "No", style: "danger" },
],
prompt: "Approve tool call: bash",
requestId: "approval_1",
Expand Down Expand Up @@ -409,7 +409,7 @@ describe("defaultMessageReducer", () => {
display: "confirmation",
options: [
{ id: "approve", label: "Yes", style: "primary" },
{ id: "deny", label: "No", style: "danger" },
{ id: "cancel", label: "No", style: "danger" },
],
prompt: "Approve tool call: bash",
requestId: "approval_1",
Expand Down Expand Up @@ -443,7 +443,7 @@ describe("defaultMessageReducer", () => {
display: "confirmation",
options: [
{ id: "approve", label: "Yes", style: "primary" },
{ id: "deny", label: "No", style: "danger" },
{ id: "cancel", label: "No", style: "danger" },
],
prompt: "Approve tool call: bash",
requestId: "approval_1",
Expand All @@ -458,7 +458,7 @@ describe("defaultMessageReducer", () => {
data = reducer.reduce(data, {
data: {
createdAt: 1,
responses: [{ optionId: "deny", requestId: "approval_1" }],
responses: [{ optionId: "cancel", requestId: "approval_1" }],
},
type: "client.input.responded",
});
Expand Down Expand Up @@ -487,12 +487,12 @@ describe("defaultMessageReducer", () => {
display: "confirmation",
options: [
{ id: "approve", label: "Yes", style: "primary" },
{ id: "deny", label: "No", style: "danger" },
{ id: "cancel", label: "No", style: "danger" },
],
prompt: "Approve tool call: bash",
requestId: "approval_1",
},
inputResponse: { optionId: "deny", requestId: "approval_1" },
inputResponse: { optionId: "cancel", requestId: "approval_1" },
kind: "tool-call",
name: "bash",
},
Expand Down Expand Up @@ -522,7 +522,7 @@ describe("defaultMessageReducer", () => {
display: "confirmation",
options: [
{ id: "approve", label: "Yes", style: "primary" },
{ id: "deny", label: "No", style: "danger" },
{ id: "cancel", label: "No", style: "danger" },
],
prompt: "Approve tool call: bash",
requestId: "approval_1",
Expand Down
4 changes: 2 additions & 2 deletions packages/eve/src/evals/runner/execute-task.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ describe("executeTask", () => {
const request = t.requireInputRequest({
display: "confirmation",
input: { command: "pwd" },
optionIds: ["approve", "deny"],
optionIds: ["approve", "cancel"],
prompt: /Approve/,
toolName: "bash",
});
Expand Down Expand Up @@ -720,7 +720,7 @@ function inputRequested(
display: "confirmation",
options: [
{ id: "approve", label: "Approve" },
{ id: "deny", label: "Deny" },
{ id: "cancel", label: "Cancel" },
],
prompt: "Approve?",
requestId,
Expand Down
2 changes: 1 addition & 1 deletion packages/eve/src/execution/subagent-adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ function sampleRequest(): InputRequest {
},
options: [
{ id: "approve", label: "Approve" },
{ id: "deny", label: "Deny" },
{ id: "cancel", label: "Cancel" },
],
prompt: "Approve?",
requestId: "req-1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ function buildApprovalRequest(requestId: string): InputRequest {
display: "confirmation",
options: [
{ id: "approve", label: "Approve", style: "primary" },
{ id: "deny", label: "Deny", style: "danger" },
{ id: "cancel", label: "Cancel", style: "danger" },
],
prompt: "Approve?",
requestId,
Expand Down Expand Up @@ -421,7 +421,7 @@ describe("subagent HITL proxy → concurrent-descendant routing", () => {
payload: {
inputResponses: [
{ optionId: "approve", requestId: "req-a" },
{ optionId: "deny", requestId: "req-b" },
{ optionId: "cancel", requestId: "req-b" },
],
},
state: parkedSession.state,
Expand All @@ -440,7 +440,7 @@ describe("subagent HITL proxy → concurrent-descendant routing", () => {
inputResponses: [{ optionId: "approve", requestId: "req-a" }],
});
expect(byChild.get("subagent:parent:call-b")).toEqual({
inputResponses: [{ optionId: "deny", requestId: "req-b" }],
inputResponses: [{ optionId: "cancel", requestId: "req-b" }],
});

// A response whose requestId does not match any proxy entry
Expand Down
4 changes: 2 additions & 2 deletions packages/eve/src/execution/subagent-hitl-proxy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ describe("routeDeliverPayload", () => {
payload: {
inputResponses: [
{ optionId: "approve", requestId: "req-a" },
{ optionId: "deny", requestId: "req-b" },
{ optionId: "cancel", requestId: "req-b" },
{ optionId: "ignore", requestId: "req-parent" },
],
},
Expand All @@ -46,7 +46,7 @@ describe("routeDeliverPayload", () => {
const childA = routed.forChildren.find((c) => c.childContinuationToken === "child-a");
const childB = routed.forChildren.find((c) => c.childContinuationToken === "child-b");
expect(childA?.payload.inputResponses).toEqual([{ optionId: "approve", requestId: "req-a" }]);
expect(childB?.payload.inputResponses).toEqual([{ optionId: "deny", requestId: "req-b" }]);
expect(childB?.payload.inputResponses).toEqual([{ optionId: "cancel", requestId: "req-b" }]);

expect(routed.forSelf?.inputResponses).toEqual([
{ optionId: "ignore", requestId: "req-parent" },
Expand Down
2 changes: 1 addition & 1 deletion packages/eve/src/execution/workflow-steps.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1484,7 +1484,7 @@ describe("runProxySubagentEventStep", () => {
},
options: [
{ id: "approve", label: "Approve" },
{ id: "deny", label: "Deny" },
{ id: "cancel", label: "Cancel" },
],
prompt: "Approve?",
requestId: "req-1",
Expand Down
14 changes: 7 additions & 7 deletions packages/eve/src/harness/input-extraction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe("extractQuestionInputRequests", () => {
toolCalls: [
{
input: {
options: [{ id: "yes", label: "Yes" }],
options: [{ id: "yes", label: "Approve" }],
prompt: "Continue?",
},
toolCallId: "call-1",
Expand All @@ -27,14 +27,14 @@ describe("extractQuestionInputRequests", () => {
action: {
callId: "call-1",
input: {
options: [{ id: "yes", label: "Yes" }],
options: [{ id: "yes", label: "Approve" }],
prompt: "Continue?",
},
kind: "tool-call",
toolName: "ask_question",
},
display: "select",
options: [{ id: "yes", label: "Yes" }],
options: [{ id: "yes", label: "Approve" }],
prompt: "Continue?",
requestId: "call-1",
},
Expand Down Expand Up @@ -118,8 +118,8 @@ describe("extractToolApprovalInputRequests", () => {
allowFreeform: false,
display: "confirmation",
options: [
{ id: "approve", label: "Yes" },
{ id: "deny", label: "No" },
{ id: "approve", label: "Approve" },
{ id: "cancel", label: "Cancel" },
],
prompt: "Approve tool call: bash",
requestId: "approval-1",
Expand Down Expand Up @@ -155,8 +155,8 @@ describe("extractToolApprovalInputRequests", () => {
allowFreeform: false,
display: "confirmation",
options: [
{ id: "approve", label: "Yes" },
{ id: "deny", label: "No" },
{ id: "approve", label: "Approve" },
{ id: "cancel", label: "Cancel" },
],
prompt: "Approve tool call: bash",
requestId: "approval-1",
Expand Down
4 changes: 2 additions & 2 deletions packages/eve/src/harness/input-extraction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,8 @@ function extractApprovalRequests(input: {
allowFreeform: false,
display: "confirmation",
options: [
{ id: "approve", label: "Yes" },
{ id: "deny", label: "No" },
{ id: "approve", label: "Approve" },
{ id: "cancel", label: "Cancel" },
],
prompt: `Approve tool call: ${toolCall.toolName}`,
requestId: approval.approvalId,
Expand Down
28 changes: 14 additions & 14 deletions packages/eve/src/harness/input-requests.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ describe("resolvePendingInput", () => {
display: "confirmation",
options: [
{ id: "approve", label: "Yes" },
{ id: "deny", label: "No" },
{ id: "cancel", label: "No" },
],
prompt: "Approve tool call: bash",
requestId: "approval-1",
Expand Down Expand Up @@ -266,7 +266,7 @@ describe("resolvePendingInput", () => {
display: "confirmation",
options: [
{ id: "approve", label: "Yes" },
{ id: "deny", label: "No" },
{ id: "cancel", label: "No" },
],
prompt: "Approve tool call: bash",
requestId: "approval-1",
Expand Down Expand Up @@ -296,7 +296,7 @@ describe("resolvePendingInput", () => {
// Deliver an approval response AND a message simultaneously.
const result = resolvePendingInput({
stepInput: {
inputResponses: [{ requestId: "approval-1", optionId: "deny" }],
inputResponses: [{ requestId: "approval-1", optionId: "cancel" }],
message: "Ignore that and say hi instead.",
},
session,
Expand Down Expand Up @@ -333,7 +333,7 @@ describe("resolvePendingInput", () => {
display: "confirmation",
options: [
{ id: "approve", label: "Yes" },
{ id: "deny", label: "No" },
{ id: "cancel", label: "No" },
],
prompt: "Approve tool call: bash",
requestId: "approval-1",
Expand Down Expand Up @@ -392,7 +392,7 @@ describe("resolvePendingInput", () => {
display: "confirmation",
options: [
{ id: "approve", label: "Yes" },
{ id: "deny", label: "No" },
{ id: "cancel", label: "No" },
],
prompt: "Approve tool call: bash",
requestId: "approval-1",
Expand Down Expand Up @@ -456,7 +456,7 @@ describe("resolvePendingInput", () => {
display: "confirmation",
options: [
{ id: "approve", label: "Yes" },
{ id: "deny", label: "No" },
{ id: "cancel", label: "No" },
],
prompt: "Approve tool call: vercel__list_projects",
requestId: "approval-1",
Expand Down Expand Up @@ -522,7 +522,7 @@ describe("resolvePendingInput", () => {
display: "confirmation",
options: [
{ id: "approve", label: "Yes" },
{ id: "deny", label: "No" },
{ id: "cancel", label: "No" },
],
prompt: "Approve tool call: bash",
requestId: "approval-1",
Expand Down Expand Up @@ -551,7 +551,7 @@ describe("resolvePendingInput", () => {

const result = resolvePendingInput({
stepInput: {
inputResponses: [{ requestId: "approval-1", optionId: "deny" }],
inputResponses: [{ requestId: "approval-1", optionId: "cancel" }],
},
session,
});
Expand Down Expand Up @@ -591,7 +591,7 @@ describe("resolvePendingInput", () => {
display: "confirmation",
options: [
{ id: "approve", label: "Yes" },
{ id: "deny", label: "No" },
{ id: "cancel", label: "No" },
],
prompt: "Approve tool call: bash",
requestId: "approval-1",
Expand All @@ -603,7 +603,7 @@ describe("resolvePendingInput", () => {

const result = resolvePendingInput({
stepInput: {
inputResponses: [{ requestId: "approval-1", optionId: "deny" }],
inputResponses: [{ requestId: "approval-1", optionId: "cancel" }],
},
session,
});
Expand Down Expand Up @@ -648,7 +648,7 @@ describe("resolvePendingInput", () => {
display: "confirmation",
options: [
{ id: "approve", label: "Yes" },
{ id: "deny", label: "No" },
{ id: "cancel", label: "No" },
],
prompt: "Approve tool call: bash",
requestId: "approval-1",
Expand Down Expand Up @@ -684,7 +684,7 @@ describe("resolvePendingInput", () => {
display: "confirmation",
options: [
{ id: "approve", label: "Yes" },
{ id: "deny", label: "No" },
{ id: "cancel", label: "No" },
],
prompt: "Approve tool call: bash",
requestId: "approval-1",
Expand Down Expand Up @@ -724,7 +724,7 @@ describe("resolvePendingInput", () => {
display: "confirmation",
options: [
{ id: "approve", label: "Yes" },
{ id: "deny", label: "No" },
{ id: "cancel", label: "No" },
],
prompt: "Approve tool call: bash",
requestId: "approval-1",
Expand Down Expand Up @@ -783,7 +783,7 @@ describe("resolvePendingInput", () => {
display: "confirmation",
options: [
{ id: "approve", label: "Yes" },
{ id: "deny", label: "No" },
{ id: "cancel", label: "No" },
],
prompt: "Approve tool call: linear_whoami",
requestId: "approval-1",
Expand Down
Loading
Loading