Skip to content

Commit be8e31a

Browse files
committed
feat: add browser handoff attachment
1 parent 95aff87 commit be8e31a

6 files changed

Lines changed: 81 additions & 0 deletions

File tree

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,14 @@ const handoff = await agent.createBrowserHandoff({
391391
console.log(handoff.url);
392392
```
393393

394+
#### `attachBrowserHandoff(params): Promise<BrowserHandoffAttachResult>`
395+
396+
Consume a one-time handoff token and attach the referenced CLI session.
397+
398+
```typescript
399+
const attached = await agent.attachBrowserHandoff({ token: handoff.token });
400+
```
401+
394402
#### `getState(): Promise<GetStateResult>`
395403

396404
Get the current agent state.

src/__tests__/session-control.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,32 @@ describe('session control RPCs', () => {
6262
const agent = Agent.fromSDK(sdk);
6363
await expect(agent.createBrowserHandoff(params)).resolves.toEqual(handoff);
6464
});
65+
66+
it('attaches a browser handoff token through every public layer', async () => {
67+
const client = new RPCClient();
68+
const calls: Array<{ method: string; params?: unknown }> = [];
69+
const attached = {
70+
success: true,
71+
sessionId: 'session-attached',
72+
workspaceRoot: '/workspace',
73+
messageCount: 12,
74+
};
75+
getTransport(client).request = async (method, params) => {
76+
calls.push({ method, params });
77+
return attached;
78+
};
79+
80+
const params = { token: 'handoff-token' };
81+
await expect(client.attachBrowserHandoff(params)).resolves.toEqual(attached);
82+
expect(calls).toEqual([{
83+
method: 'autohand.browserHandoff.attach',
84+
params,
85+
}]);
86+
87+
const sdk = {
88+
attachBrowserHandoff: async () => attached,
89+
} as unknown as AutohandSDK;
90+
const agent = Agent.fromSDK(sdk);
91+
await expect(agent.attachBrowserHandoff(params)).resolves.toEqual(attached);
92+
});
6593
});

src/rpc/client.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ import type {
3636
GetMessagesResult,
3737
BrowserHandoffCreateParams,
3838
BrowserHandoffCreateResult,
39+
BrowserHandoffAttachParams,
40+
BrowserHandoffAttachResult,
3941
PermissionDecision,
4042
PermissionResponseParams,
4143
SDKEvent,
@@ -364,6 +366,18 @@ export class RPCClient {
364366
) as Promise<BrowserHandoffCreateResult>;
365367
}
366368

369+
/**
370+
* Consume a browser handoff token and attach its session.
371+
*/
372+
async attachBrowserHandoff(
373+
params: BrowserHandoffAttachParams
374+
): Promise<BrowserHandoffAttachResult> {
375+
return this.transport.request(
376+
'autohand.browserHandoff.attach',
377+
params
378+
) as Promise<BrowserHandoffAttachResult>;
379+
}
380+
367381
/**
368382
* Respond to a permission request
369383
*

src/sdk/agent.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ import type {
3434
GetSkillsRegistryResult,
3535
BrowserHandoffCreateParams,
3636
BrowserHandoffCreateResult,
37+
BrowserHandoffAttachParams,
38+
BrowserHandoffAttachResult,
3739
InstallSkillParams,
3840
InstallSkillResult,
3941
McpListServersResult,
@@ -561,6 +563,12 @@ export class Agent {
561563
return this.sdk.createBrowserHandoff(params);
562564
}
563565

566+
async attachBrowserHandoff(
567+
params: BrowserHandoffAttachParams
568+
): Promise<BrowserHandoffAttachResult> {
569+
return this.sdk.attachBrowserHandoff(params);
570+
}
571+
564572
async setPlanMode(enabled: boolean): Promise<void> {
565573
await this.sdk.setPlanMode(enabled);
566574
}

src/sdk/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ import type {
3636
GetMessagesResult,
3737
BrowserHandoffCreateParams,
3838
BrowserHandoffCreateResult,
39+
BrowserHandoffAttachParams,
40+
BrowserHandoffAttachResult,
3941
ResetResult,
4042
PermissionDecisionScope,
4143
PermissionResponseParams,
@@ -1421,6 +1423,16 @@ export class AutohandSDK {
14211423
return this.client.createBrowserHandoff(params);
14221424
}
14231425

1426+
/**
1427+
* Attach the session referenced by a one-time browser handoff token.
1428+
*/
1429+
async attachBrowserHandoff(
1430+
params: BrowserHandoffAttachParams
1431+
): Promise<BrowserHandoffAttachResult> {
1432+
await this.ensureStarted();
1433+
return this.client.attachBrowserHandoff(params);
1434+
}
1435+
14241436
// ============================================================================
14251437
// Permission Handling
14261438
// ============================================================================

src/types/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1853,6 +1853,10 @@ export interface BrowserHandoffCreateParams {
18531853
installUrl?: string;
18541854
}
18551855

1856+
export interface BrowserHandoffAttachParams {
1857+
token: string;
1858+
}
1859+
18561860
export interface PermissionResponseParams {
18571861
requestId: string;
18581862
decision?: PermissionDecision | PermissionDecisionAlias;
@@ -1890,6 +1894,13 @@ export interface BrowserHandoffCreateResult {
18901894
url: string;
18911895
}
18921896

1897+
export interface BrowserHandoffAttachResult {
1898+
success: boolean;
1899+
sessionId?: string;
1900+
workspaceRoot?: string;
1901+
messageCount?: number;
1902+
}
1903+
18931904
export interface GetStateResult {
18941905
status: 'idle' | 'processing' | 'waiting_permission';
18951906
sessionId: string | null;

0 commit comments

Comments
 (0)