Contract: pass approval option warnings, omit threadId on host pushes
Gateway / check (push) Successful in 8s
Gateway / image (push) Successful in 21s

This commit is contained in:
gib committed 2026-11-01 02:36:44 -05:00
1 parent 7ad1045be4
commit 21215210e3
6 files changed
+12 -10

No files matched your search

+4 -3
View File
@@ -87,7 +87,8 @@ PendingRequest {
} }
``` ```
For questions (`kind: "user_input"`), `options` is empty and `questions` lists An option may also carry `warning`, a short note about a risky choice such as "Applies to every
thread". For questions (`kind: "user_input"`), `options` is empty and `questions` lists
`{ id, header, question, options: [{ label, value }], allowCustomAnswer }`. `{ id, header, question, options: [{ label, value }], allowCustomAnswer }`.
## Thread ## Thread
@@ -145,8 +146,8 @@ The new thread uses the project's default model, or the host's, and the host's d
## Notifications ## Notifications
The gateway pushes when a thread starts waiting for you, finishes, or fails. Each push has an The gateway pushes when a thread starts waiting for you, finishes, or fails. Each push has an
`aps.category` and the custom keys `hostId`, `threadId`, and, for approvals and questions, `aps.category` and the custom keys `hostId`, `threadId` (absent on `host` pushes), and, for
`requestId`. approvals and questions, `requestId`.
| Category | When | Actions the watch registers | | Category | When | Actions the watch registers |
| --- | --- | --- | | --- | --- | --- |
+4 -3
View File
@@ -21,8 +21,8 @@ export interface PendingRequest {
/** What the agent wants to do, such as the command line. Null when the server gave none. */ /** What the agent wants to do, such as the command line. Null when the server gave none. */
prompt: string | null; prompt: string | null;
reason: string | null; reason: string | null;
/** Approval choices, in the server's order. Empty for questions. */ /** Approval choices, in the server's order. Empty for questions. `warning` flags a risky choice. */
options: Array<{ decision: ApprovalDecision; label: string }>; options: Array<{ decision: ApprovalDecision; label: string; warning?: string }>;
/** Questions for the user. Empty for approvals. */ /** Questions for the user. Empty for approvals. */
questions: Array<{ questions: Array<{
id: string; id: string;
@@ -126,7 +126,8 @@ export interface ErrorResponse {
/** Custom keys on every push notification, beside `aps`. */ /** Custom keys on every push notification, beside `aps`. */
export interface PushPayload { export interface PushPayload {
hostId: string; hostId: string;
threadId: string; /** Absent on `host` pushes, which are about a host rather than a thread. */
threadId?: string;
requestId?: string; requestId?: string;
} }
+1 -1
View File
@@ -35,7 +35,7 @@ const sendPush: SendPush = async (push) => {
alert: { title: push.title, ...(push.subtitle ? { subtitle: push.subtitle } : {}), body: push.body }, alert: { title: push.title, ...(push.subtitle ? { subtitle: push.subtitle } : {}), body: push.body },
sound: "default", sound: "default",
category: push.category, category: push.category,
"thread-id": push.data.threadId || push.data.hostId, "thread-id": push.data.threadId ?? push.data.hostId,
}, },
...push.data, ...push.data,
}; };
+1 -1
View File
@@ -79,7 +79,7 @@ export class Notifier {
title: label, title: label,
body, body,
category: "host", category: "host",
data: { hostId, threadId: "" }, data: { hostId },
collapseId: `host:${hostId}`, collapseId: `host:${hostId}`,
}).catch(() => {}); }).catch(() => {});
} }
+1 -1
View File
@@ -64,7 +64,7 @@ function pendingRequest(thread: ThreadShell, turnItems: BoundedThread["projectio
); );
if (item && isApprovalRequest(item)) { if (item && isApprovalRequest(item)) {
const options = item.options?.length const options = item.options?.length
? item.options.map(({ decision, label }) => ({ decision, label })) ? item.options.map(({ decision, label, warning }) => ({ decision, label, ...(warning ? { warning } : {}) }))
: [ : [
{ decision: "accept" as const, label: "Allow" }, { decision: "accept" as const, label: "Allow" },
{ decision: "decline" as const, label: "Deny" }, { decision: "decline" as const, label: "Deny" },
+1 -1
View File
@@ -69,7 +69,7 @@ describe("summarize", () => {
reason: null, reason: null,
options: [ options: [
{ decision: "accept", label: "Allow once" }, { decision: "accept", label: "Allow once" },
{ decision: "acceptAlways", label: "Always allow" }, { decision: "acceptAlways", label: "Always allow", warning: "Applies to every thread" },
{ decision: "decline", label: "Deny" }, { decision: "decline", label: "Deny" },
], ],
questions: [], questions: [],