diff --git a/beehive/BUZZ_OWNER_SIGNIN.md b/beehive/BUZZ_OWNER_SIGNIN.md index 97c54bcfe2..37488f3132 100644 --- a/beehive/BUZZ_OWNER_SIGNIN.md +++ b/beehive/BUZZ_OWNER_SIGNIN.md @@ -1,7 +1,10 @@ # Explicit Buzz owner sign-in Agents offers **Sign in with Buzz key** alongside saved-key and matching-key import. -The confirmation says Keychain read only, session only, no local copy. Merely +Selecting the action signs in immediately: selection itself is the explicit +gesture, and no extra Beehive confirmation is shown. While the key is read the +status line states Keychain read only, session only, no local copy, and that an +OS keychain permission dialog is separate and not bypassed. Merely opening Beehive, selecting Agents, configuring routing, or choosing another sign-in method never invokes this reader. It is not provider sharing/discovery. @@ -79,6 +82,13 @@ Final semantic candidate, pinned Node **24.15.0**, package-local TypeScript: A separate fresh traversal of the **unchanged installed launcher** exercised saved-key sign-in (cancel/confirm), with the same explicit isolation. This is compatibility evidence, not installation of this feature or real relay proof. +- 2025-09-16 prompt removal: the extra Beehive confirmation before the Buzz-key + read was deleted; one selection dispatches the request. The source-UI PTY + driver (`ui-single-gesture.py`) replays screen state and verified Agents → + select Buzz key once → signed-in controls with **no** Beehive confirm or other + input, Esc still cancelling the in-flight read; same fresh-HOME, + fixture-credential and service fences, production Keychain untouched. The + 12/12 backend and 8/8 renderer suites and package typecheck re-passed. - **One** default concurrent full-package run was attempted on the final semantic candidate. It exceeded the outer tool's **400 second** bound, so no final suite summary exists. Retained output reports failures in assignment, broker, and diff --git a/beehive/src/manager-view.ts b/beehive/src/manager-view.ts index 3fa442ccda..7b053d4e60 100644 --- a/beehive/src/manager-view.ts +++ b/beehive/src/manager-view.ts @@ -22,7 +22,7 @@ function request(action: string, values?: Record, target?: string const id = ++sequence; const completion = new Promise(resolve => { pending = { id, resolve }; }); const message: ManagerRequest = { id, action, values, target, revision }; - screen.setPending(true); const retire = screen.temporaryNotice(action === 'add-databricks' ? 'Signing in through your browser… Esc cancels the native helper. An OS credential write or browser window may remain; cancelled sign-in will not save a provider.' : 'Working… Esc stops waiting. Remote work and saved changes are not cancelled. Inspect before you try again.'); + screen.setPending(true); const retire = screen.temporaryNotice(action === 'add-databricks' ? 'Signing in through your browser… Esc cancels the native helper. An OS credential write or browser window may remain; cancelled sign-in will not save a provider.' : action === 'signin-buzz' ? 'Reading the saved Buzz owner key… Keychain read only · session only · no local copy. Esc stops waiting. An OS keychain permission dialog may appear; it is separate and is not bypassed.' : 'Working… Esc stops waiting. Remote work and saved changes are not cancelled. Inspect before you try again.'); output.write(JSON.stringify(message) + '\n'); return completion.finally(retire); } @@ -46,10 +46,14 @@ async function routing(action: string, secret = false) { if (key === undefined) return; values.secret = key; } - const confirmation = action === 'signin-buzz' - ? `Sign in with Buzz key\nKeychain read only · session only · no local copy\nOwner: ${shortIdentity(owner)}\nRelay: ${relay}` - : `${action === 'configure' ? 'Configure this computer' : 'Sign in'}\nOwner: ${shortIdentity(owner)}\nRelay: ${relay}\n${action === 'configure' ? 'Create host identity in the OS credential store?' : 'Access owner key in the OS credential store?'}`; - if (!await screen.confirm(confirmation)) { delete values.secret; return; } + // Buzz-key sign-in carries no extra Beehive prompt: selecting the action is + // itself the explicit gesture (an OS keychain permission dialog is separate + // and still applies), so the request dispatches immediately. The saved-key + // and import paths keep their confirmation. + if (action !== 'signin-buzz') { + const confirmation = `${action === 'configure' ? 'Configure this computer' : 'Sign in'}\nOwner: ${shortIdentity(owner)}\nRelay: ${relay}\n${action === 'configure' ? 'Create host identity in the OS credential store?' : 'Access owner key in the OS credential store?'}`; + if (!await screen.confirm(confirmation)) { delete values.secret; return; } + } await request(action, values); delete values.secret; } async function registerForm(continuation?: string, agent?: string) { diff --git a/beehive/test/manager-installed-fixture.ts b/beehive/test/manager-installed-fixture.ts index d8142bdfe7..7ad78e740a 100644 --- a/beehive/test/manager-installed-fixture.ts +++ b/beehive/test/manager-installed-fixture.ts @@ -21,6 +21,10 @@ export async function fixtureCredential(input: any, signal: AbortSignal) { if (input.action === 'add-provider') { addProvider(input.directory,input.name,input.secret,{read:r=>backend.read(r as unknown as CredentialReference),create:(r,s)=>backend.create(r as unknown as CredentialReference,s)},input.type,input.endpoint,input.wire); return {ok:true}; } if (input.action === 'models') return { ok: true, models: ['fixture-model'] }; if (input.action === 'signin-buzz') { + // Test-only pacing so a PTY driver can observe the pending state and exercise + // Esc cancellation deterministically; inert unless the driver sets it. + const paced = Number(process.env.BEEHIVE_TEST_SIGNIN_DELAY_MS ?? '0'); + if (paced > 0) await new Promise((resolve, reject) => { const timer = setTimeout(resolve, paced); signal.addEventListener('abort', () => { clearTimeout(timer); reject(signal.reason); }, { once: true }); }); const key = backend.read(credentialReference('owner', input.owner)); const result = readBuzzOwnerKey(input.owner, () => key === null ? null : JSON.stringify({identity:nip19.nsecEncode(Buffer.from(key,'hex'))})); if (!result.ok) throw Error(buzzOwnerKeyErrors[result.reason]);