feat(run,notifications,profiles): stop sending prompts nobody typed, and fix notification + profile delivery
Run console:
- Picking a pipeline no longer pre-writes anything into the prompt box
(`promptPrefill` in `LaneConsolePane.tsx` is gone — it wrote the driving
slash command on a claude lane and a stage checklist on a codex one).
Whatever sits in that box is sent as a real prompt the moment the run
starts, so the box is the user's alone now.
- Resuming a session sends NO prompt at all (`handleStart` in `RunSetup.tsx`):
the resumed session carries its own transcript, and leftover text in the box
fired an extra turn into it.
Notifications:
- `client/src/lib/notify.ts` is now the ONE sender, used by both
`useNotifications` and Settings' test button, and it reports where the
notification landed: `POST /api/push/send` answering `{native:false,pushed:0}`
means it went NOWHERE, so the client falls back to a local notification
instead of counting the 200 as delivered.
- A notification carries the lane that raised it (`/workspace?lane=<id>`), and
Workspace selects that lane on arrival.
Profiles:
- `server/lib/cc-profiles.js` reads and writes the macOS login Keychain item
`Claude Code-credentials`, where the `claude` CLI actually keeps
`claudeAiOauth` on darwin — a switch used to rewrite a file nothing reads.
The file stays the fallback, is kept in step only when it already exists, and
`AGENTHUB_CC_KEYCHAIN=0` pins it (tests set it).
Tests: npm run test:server (1820 pass), client vitest (553 pass).
This commit is contained in:
@@ -121,7 +121,7 @@ const schemas = {
|
||||
type: "object",
|
||||
required: ["title", "body"],
|
||||
description:
|
||||
"Notification content to broadcast. Both fields are mandatory; a missing title or body yields a 400. The same title/body is delivered to every reachable surface (native Electron notification + all stored Web Push subscriptions).",
|
||||
"Notification content to broadcast. `title`/`body` are mandatory; a missing one yields a 400. The same content is delivered to every reachable surface (native Electron notification + all stored Web Push subscriptions).",
|
||||
properties: {
|
||||
title: {
|
||||
type: "string",
|
||||
@@ -133,6 +133,12 @@ const schemas = {
|
||||
description: "Notification body text.",
|
||||
example: "Your Claude Code session finished with 3 subagents.",
|
||||
},
|
||||
url: {
|
||||
type: "string",
|
||||
description:
|
||||
"Optional same-origin path the Web Push delivery navigates to on click, via the service worker's `notificationclick` handler (`client/public/sw.js`). Ignored by the native Electron leg, which has no click handling. Omit for notifications with no specific target.",
|
||||
example: "/workspace?lane=3",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -168,6 +174,27 @@ const schemas = {
|
||||
"Count of Web Push sends that were rejected. Subscriptions rejected with HTTP 410 (Gone) are deleted from `push_subscriptions` as part of this request.",
|
||||
example: 1,
|
||||
},
|
||||
errors: {
|
||||
type: "array",
|
||||
description:
|
||||
"Why the rejected sends were rejected, straight from the push service (capped at 3). Diagnostic only - it never includes the subscription endpoint, which is a capability URL. Drives the Settings test-notification banner, so a delivery that reached nobody names its reason instead of looking like success.",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
statusCode: {
|
||||
type: "integer",
|
||||
nullable: true,
|
||||
description: "HTTP status the push service answered with, when there was one.",
|
||||
example: 403,
|
||||
},
|
||||
message: {
|
||||
type: "string",
|
||||
description: "Response body or error message, truncated to 200 characters.",
|
||||
example: "UnauthorizedRegistration",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -287,7 +314,7 @@ const paths = {
|
||||
tags: ["Push"],
|
||||
summary: "Broadcast a notification to all surfaces",
|
||||
description:
|
||||
"Dispatches a notification to every reachable surface at once: it fires a native OS notification via Electron's main-process Notification API when the server is hosted inside the desktop app, AND sends an encrypted Web Push delivery to every stored subscription. Both legs run unconditionally so whichever surface the user is on receives the alert — under `npm start` the native leg is a no-op, and under the desktop app the Web Push leg is typically a no-op (Electron has no FCM credentials, so `push_subscriptions` is empty). Subscriptions rejected with HTTP 410 (Gone) are pruned from `push_subscriptions` during the request. The response reports `{ native, pushed, failed }` so the caller can tell a real delivery from a silent no-op. No authentication (local-first). A missing `title` or `body` returns 400; an unexpected dispatch error returns 500.",
|
||||
"Dispatches a notification to every reachable surface at once: it fires a native OS notification via Electron's main-process Notification API when the server is hosted inside the desktop app, AND sends an encrypted Web Push delivery to every stored subscription. Both legs run unconditionally so whichever surface the user is on receives the alert — under `npm start` the native leg is a no-op, and under the desktop app the Web Push leg is typically a no-op (Electron has no FCM credentials, so `push_subscriptions` is empty). Subscriptions rejected with HTTP 410 (Gone) are pruned from `push_subscriptions` during the request. The response reports `{ native, pushed, failed, errors }` so the caller can tell a real delivery from a silent no-op, and can show WHY a rejected delivery failed. No authentication (local-first). A missing `title` or `body` returns 400; an unexpected dispatch error returns 500.",
|
||||
operationId: "pushSend",
|
||||
requestBody: {
|
||||
required: true,
|
||||
|
||||
Reference in New Issue
Block a user