guidinghand 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,281 @@
1
+ "use strict";
2
+ // GuidingHand SDK for JavaScript and TypeScript: the /v1 API (https://guidinghand.ai/openapi.json).
3
+ // No dependencies: it uses the platform's fetch and WebCrypto (Node 18+, Deno, Bun, Cloudflare Workers).
4
+ //
5
+ // import GuidingHand from 'guidinghand';
6
+ // const gh = new GuidingHand(); // reads GUIDINGHAND_API_KEY
7
+ // const session = await gh.sessions.create({ agent_id: 'billing' });
8
+ // // send session.invite_url to the person at the computer, then:
9
+ // await gh.sessions.waitForConnection(session.session_id);
10
+ // const task = await gh.tasks.run(session.session_id, { prompt: 'Turn on Dark Mode', onQuestion: async (q) => 'Work', onApproval: async () => true });
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.GuidingHand = exports.WebhookVerificationError = exports.NeedsInputError = exports.SessionExpiredError = exports.TimeoutError = exports.APIConnectionError = exports.APIError = exports.RateLimitError = exports.ConflictError = exports.NotFoundError = exports.PermissionDeniedError = exports.PaymentRequiredError = exports.AuthenticationError = exports.InvalidRequestError = exports.GuidingHandError = exports.VERSION = void 0;
13
+ exports.verifyWebhook = verifyWebhook;
14
+ exports.VERSION = '0.1.0';
15
+ // ---------- errors ----------
16
+ class GuidingHandError extends Error {
17
+ status;
18
+ type;
19
+ body;
20
+ extra;
21
+ constructor(message, status = 0, type = 'error', body = null) {
22
+ super(message);
23
+ this.name = new.target.name;
24
+ this.status = status;
25
+ this.type = type;
26
+ this.body = body;
27
+ const e = body?.error;
28
+ this.extra = e ? Object.fromEntries(Object.entries(e).filter(([k]) => k !== 'type' && k !== 'message')) : {};
29
+ }
30
+ }
31
+ exports.GuidingHandError = GuidingHandError;
32
+ class InvalidRequestError extends GuidingHandError {
33
+ }
34
+ exports.InvalidRequestError = InvalidRequestError;
35
+ class AuthenticationError extends GuidingHandError {
36
+ }
37
+ exports.AuthenticationError = AuthenticationError;
38
+ class PaymentRequiredError extends GuidingHandError {
39
+ }
40
+ exports.PaymentRequiredError = PaymentRequiredError;
41
+ class PermissionDeniedError extends GuidingHandError {
42
+ }
43
+ exports.PermissionDeniedError = PermissionDeniedError;
44
+ class NotFoundError extends GuidingHandError {
45
+ }
46
+ exports.NotFoundError = NotFoundError;
47
+ class ConflictError extends GuidingHandError {
48
+ }
49
+ exports.ConflictError = ConflictError;
50
+ class RateLimitError extends GuidingHandError {
51
+ }
52
+ exports.RateLimitError = RateLimitError;
53
+ class APIError extends GuidingHandError {
54
+ }
55
+ exports.APIError = APIError;
56
+ class APIConnectionError extends GuidingHandError {
57
+ }
58
+ exports.APIConnectionError = APIConnectionError;
59
+ class TimeoutError extends GuidingHandError {
60
+ }
61
+ exports.TimeoutError = TimeoutError;
62
+ class SessionExpiredError extends GuidingHandError {
63
+ }
64
+ exports.SessionExpiredError = SessionExpiredError;
65
+ /** A task waits on a question or approval and `run()` got no handler for it. */
66
+ class NeedsInputError extends GuidingHandError {
67
+ task;
68
+ constructor(task) { super(`The task is waiting for ${task.pending?.type === 'approval' ? 'an approval' : 'an answer'}; pass ${task.pending?.type === 'approval' ? 'onApproval' : 'onQuestion'} to run().`, 0, 'needs_input'); this.task = task; }
69
+ }
70
+ exports.NeedsInputError = NeedsInputError;
71
+ class WebhookVerificationError extends GuidingHandError {
72
+ }
73
+ exports.WebhookVerificationError = WebhookVerificationError;
74
+ const BY_STATUS = { 400: InvalidRequestError, 401: AuthenticationError, 402: PaymentRequiredError, 403: PermissionDeniedError, 404: NotFoundError, 409: ConflictError, 413: InvalidRequestError, 429: RateLimitError };
75
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
76
+ const env = (k) => (typeof process !== 'undefined' ? process.env?.[k] : undefined);
77
+ class GuidingHand {
78
+ baseUrl;
79
+ agents;
80
+ sessions;
81
+ tasks;
82
+ webhook;
83
+ apiKey;
84
+ timeout;
85
+ maxRetries;
86
+ fetchImpl;
87
+ constructor(opts = {}) {
88
+ const key = opts.apiKey ?? env('GUIDINGHAND_API_KEY');
89
+ if (!key)
90
+ throw new AuthenticationError('No API key: pass { apiKey } or set GUIDINGHAND_API_KEY. Make one in the console under Settings → API keys.', 401, 'authentication');
91
+ this.apiKey = key;
92
+ this.baseUrl = (opts.baseUrl ?? env('GUIDINGHAND_BASE_URL') ?? 'https://guidinghand.ai').replace(/\/+$/, '');
93
+ this.timeout = opts.timeout ?? 60_000;
94
+ this.maxRetries = opts.maxRetries ?? 2;
95
+ this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);
96
+ this.agents = new Agents(this);
97
+ this.sessions = new Sessions(this);
98
+ this.tasks = new Tasks(this);
99
+ this.webhook = new WebhookEndpoint(this);
100
+ }
101
+ /** @internal */
102
+ async request(method, path, { query, body, retry, wait = 0, raw } = {}) {
103
+ const qs = query ? Object.entries(query).filter(([, v]) => v !== undefined && v !== null && v !== '').map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`).join('&') : '';
104
+ const url = `${this.baseUrl}/v1${path}${qs ? `?${qs}` : ''}`;
105
+ const canRetry = retry ?? (method === 'GET' || method === 'DELETE');
106
+ for (let attempt = 0;; attempt++) {
107
+ let res;
108
+ try {
109
+ res = await this.fetchImpl(url, {
110
+ method,
111
+ headers: { Authorization: `Bearer ${this.apiKey}`, 'User-Agent': `guidinghand-js/${exports.VERSION}`, ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}) },
112
+ body: body !== undefined ? JSON.stringify(body) : undefined,
113
+ signal: AbortSignal.timeout(this.timeout + wait),
114
+ });
115
+ }
116
+ catch (e) {
117
+ if (canRetry && attempt < this.maxRetries) {
118
+ await sleep(500 * 2 ** attempt);
119
+ continue;
120
+ }
121
+ const timedOut = e?.name === 'TimeoutError';
122
+ throw timedOut ? new TimeoutError(`No answer from ${this.baseUrl} in ${Math.round((this.timeout + wait) / 1000)} s.`, 0, 'timeout') : new APIConnectionError(`Couldn’t reach ${this.baseUrl}: ${e?.message ?? e}`, 0, 'connection');
123
+ }
124
+ if (res.ok)
125
+ return (raw ? new Uint8Array(await res.arrayBuffer()) : await res.json());
126
+ if (canRetry && attempt < this.maxRetries && (res.status === 429 || res.status >= 500)) {
127
+ const after = Number(res.headers.get('retry-after'));
128
+ await sleep(Number.isFinite(after) && after > 0 ? after * 1000 : 500 * 2 ** attempt);
129
+ continue;
130
+ }
131
+ const j = await res.json().catch(() => null);
132
+ const Err = BY_STATUS[res.status] ?? (res.status >= 500 ? APIError : GuidingHandError);
133
+ throw new Err(j?.error?.message ?? `The server answered ${res.status}.`, res.status, j?.error?.type ?? 'error', j);
134
+ }
135
+ }
136
+ /** @internal Every item of a paged list. */
137
+ async *pages(path, query) {
138
+ let cursor;
139
+ do {
140
+ const p = await this.request('GET', path, { query: { ...query, limit: 100, cursor } });
141
+ yield* p.data;
142
+ cursor = p.has_more ? p.next_cursor : null;
143
+ } while (cursor);
144
+ }
145
+ }
146
+ exports.GuidingHand = GuidingHand;
147
+ exports.default = GuidingHand;
148
+ class Resource {
149
+ client;
150
+ constructor(client) {
151
+ this.client = client;
152
+ }
153
+ }
154
+ const enc = encodeURIComponent;
155
+ class Agents extends Resource {
156
+ list() { return this.client.request('GET', '/agents'); }
157
+ create(params) { return this.client.request('POST', '/agents', { body: params }); }
158
+ retrieve(agentId) { return this.client.request('GET', `/agents/${enc(agentId)}`); }
159
+ update(agentId, params) { return this.client.request('PATCH', `/agents/${enc(agentId)}`, { body: params }); }
160
+ delete(agentId) { return this.client.request('DELETE', `/agents/${enc(agentId)}`); }
161
+ }
162
+ class Sessions extends Resource {
163
+ /** Makes a code and its invite link. Send `invite_url` to the person at the computer. */
164
+ create(params = {}) { return this.client.request('POST', '/sessions', { body: params }); }
165
+ list(params = {}) { return this.client.request('GET', '/sessions', { query: params }); }
166
+ /** Every session, newest first, across pages. */
167
+ listAll(params = {}) { return this.client.pages('/sessions', params); }
168
+ retrieve(sessionId) { return this.client.request('GET', `/sessions/${enc(sessionId)}`); }
169
+ /** Disconnects the computer and deletes the session's tasks and recordings. */
170
+ delete(sessionId) { return this.client.request('DELETE', `/sessions/${enc(sessionId)}`); }
171
+ /** Resolves once the person's computer is connected. */
172
+ async waitForConnection(sessionId, { timeout = 600_000, pollInterval = 3_000 } = {}) {
173
+ const until = Date.now() + timeout;
174
+ for (;;) {
175
+ const s = await this.retrieve(sessionId);
176
+ if (s.status === 'connected')
177
+ return s;
178
+ if (s.status === 'expired')
179
+ throw new SessionExpiredError(`Session ${sessionId} expired before a computer connected. Make a new one.`, 0, 'session_expired');
180
+ if (Date.now() + pollInterval > until)
181
+ throw new TimeoutError(`No computer connected to ${sessionId} within ${Math.round(timeout / 1000)} s.`, 0, 'timeout');
182
+ await sleep(pollInterval);
183
+ }
184
+ }
185
+ }
186
+ class Tasks extends Resource {
187
+ /** Starts a task on the session's computer (it must be connected). */
188
+ create(sessionId, params) { return this.client.request('POST', `/sessions/${enc(sessionId)}/tasks`, { body: params, retry: !!params.request_id }); }
189
+ list(params = {}) { return this.client.request('GET', '/tasks', { query: params }); }
190
+ listAll(params = {}) { return this.client.pages('/tasks', params); }
191
+ retrieve(taskId, { include } = {}) { return this.client.request('GET', `/tasks/${enc(taskId)}`, { query: { include: include?.join(',') } }); }
192
+ /** Events after `after`; with `wait_ms`, waits (long poll) for the next one. */
193
+ events(taskId, { after = 0, wait_ms = 0 } = {}) {
194
+ return this.client.request('GET', `/tasks/${enc(taskId)}/events`, { query: { after, wait_ms }, wait: wait_ms });
195
+ }
196
+ respond(taskId, params) { return this.client.request('POST', `/tasks/${enc(taskId)}/respond`, { body: params }); }
197
+ stop(taskId) { return this.client.request('POST', `/tasks/${enc(taskId)}/stop`, { body: {} }); }
198
+ recording(taskId) { return this.client.request('GET', `/tasks/${enc(taskId)}/recording`); }
199
+ /** One recorded screen, as PNG bytes. */
200
+ recordingFrame(taskId, seq) { return this.client.request('GET', `/tasks/${enc(taskId)}/recording/${seq}`, { raw: true }); }
201
+ /** Every event of a task as it happens, until it's done. `for await (const e of gh.tasks.stream(id))` */
202
+ async *stream(taskId, { after = 0 } = {}) {
203
+ for (;;) {
204
+ const r = await this.events(taskId, { after, wait_ms: 25_000 });
205
+ for (const e of r.data)
206
+ yield e;
207
+ after = r.cursor;
208
+ if (r.task.done)
209
+ return r.task;
210
+ }
211
+ }
212
+ /** Starts a task and sees it through: events to onEvent, questions to onQuestion, approvals to onApproval. Resolves with the finished task. */
213
+ async run(sessionId, { onEvent, onQuestion, onApproval, timeout, ...params }) {
214
+ let task = await this.create(sessionId, params);
215
+ const until = timeout ? Date.now() + timeout : Infinity;
216
+ let after = 0, answered = '';
217
+ while (!task.done) {
218
+ if (Date.now() > until) {
219
+ await this.stop(task.task_id).catch(() => { });
220
+ throw new TimeoutError(`Task ${task.task_id} took longer than ${Math.round(timeout / 1000)} s; stopped it.`, 0, 'timeout');
221
+ }
222
+ const r = await this.events(task.task_id, { after, wait_ms: Math.min(25_000, Math.max(0, until - Date.now())) });
223
+ task = r.task;
224
+ after = r.cursor;
225
+ for (const e of r.data)
226
+ await onEvent?.(e, task);
227
+ const p = task.pending;
228
+ if (!p || task.done)
229
+ continue;
230
+ const id = p.type === 'question' ? p.question_id : p.approval_id;
231
+ if (id === answered)
232
+ continue; // answered already; the task is picking it up
233
+ if (p.type === 'question') {
234
+ if (!onQuestion)
235
+ throw new NeedsInputError(task);
236
+ await this.respond(task.task_id, { question_id: p.question_id, answer: String(await onQuestion(p, task)) });
237
+ }
238
+ else {
239
+ if (!onApproval)
240
+ throw new NeedsInputError(task);
241
+ const d = await onApproval(p, task);
242
+ const decision = typeof d === 'object' ? d : { decision: d === true || d === 'approve' ? 'approve' : 'deny' };
243
+ await this.respond(task.task_id, { approval_id: p.approval_id, ...decision });
244
+ }
245
+ answered = id;
246
+ }
247
+ return task;
248
+ }
249
+ }
250
+ // ---------- webhooks ----------
251
+ class WebhookEndpoint extends Resource {
252
+ retrieve() { return this.client.request('GET', '/webhook'); }
253
+ /** Sets the endpoint. The signing secret is in the response when it's new (or with rotate_secret), never again. */
254
+ update(params) { return this.client.request('PUT', '/webhook', { body: params }); }
255
+ delete() { return this.client.request('PUT', '/webhook', { body: { url: null } }); }
256
+ }
257
+ /**
258
+ * Checks a webhook's GuidingHand-Signature header against the raw request body and your signing secret, and
259
+ * returns the parsed event. Throws WebhookVerificationError if it doesn't match or is older than `tolerance` s.
260
+ */
261
+ async function verifyWebhook(payload, signatureHeader, secret, { tolerance = 300 } = {}) {
262
+ const fail = (m) => new WebhookVerificationError(m, 400, 'webhook_verification');
263
+ const parts = Object.fromEntries(String(signatureHeader ?? '').split(',').map((kv) => kv.trim().split('=')));
264
+ const t = Number(parts.t), v1 = parts.v1;
265
+ if (!t || !v1)
266
+ throw fail('Missing or malformed GuidingHand-Signature header.');
267
+ if (Math.abs(Date.now() / 1000 - t) > tolerance)
268
+ throw fail('The webhook’s timestamp is too old.');
269
+ const body = typeof payload === 'string' ? payload : new TextDecoder().decode(payload);
270
+ const key = await crypto.subtle.importKey('raw', new TextEncoder().encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
271
+ const mac = new Uint8Array(await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(`${t}.${body}`)));
272
+ const want = [...mac].map((b) => b.toString(16).padStart(2, '0')).join('');
273
+ if (want.length !== v1.length)
274
+ throw fail('The webhook’s signature doesn’t match.');
275
+ let diff = 0;
276
+ for (let i = 0; i < want.length; i++)
277
+ diff |= want.charCodeAt(i) ^ v1.charCodeAt(i);
278
+ if (diff)
279
+ throw fail('The webhook’s signature doesn’t match.');
280
+ return JSON.parse(body);
281
+ }
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1,327 @@
1
+ export declare const VERSION = "0.1.0";
2
+ export type Metadata = Record<string, string>;
3
+ export type Effort = 'low' | 'medium' | 'high' | null;
4
+ export type Agent = {
5
+ object: 'agent';
6
+ agent_id: string;
7
+ name: string;
8
+ instructions: string;
9
+ effort: Effort;
10
+ greeting: string;
11
+ is_default: boolean;
12
+ invite_url_template: string;
13
+ created_at: string | null;
14
+ updated_at: string | null;
15
+ };
16
+ export type Device = {
17
+ os: 'mac' | 'windows' | 'linux' | 'unknown';
18
+ name: string | null;
19
+ width: number;
20
+ height: number;
21
+ app_version: string | null;
22
+ };
23
+ export type SessionStatus = 'waiting' | 'connected' | 'disconnected' | 'expired';
24
+ export type Session = {
25
+ object: 'session';
26
+ session_id: string;
27
+ code: string;
28
+ agent_id: string;
29
+ invite_url: string;
30
+ status: SessionStatus;
31
+ device: Device | null;
32
+ metadata: Metadata;
33
+ created_at: string;
34
+ paired_at: string | null;
35
+ last_active_at: string;
36
+ expires_at: string | null;
37
+ task_count?: number;
38
+ latest_task_id?: string | null;
39
+ };
40
+ export type TaskStatus = 'queued' | 'running' | 'waiting_for_user' | 'waiting_for_approval' | 'completed' | 'failed' | 'stopped';
41
+ export type Question = {
42
+ type: 'question';
43
+ question_id: string;
44
+ question: string;
45
+ options: string[];
46
+ };
47
+ export type Approval = {
48
+ type: 'approval';
49
+ approval_id: string;
50
+ action: string;
51
+ risk: 'low' | 'medium' | 'high';
52
+ };
53
+ export type EventType = 'started' | 'progress' | 'thinking' | 'action' | 'message' | 'question' | 'answer' | 'approval_required' | 'approved' | 'denied' | 'completed' | 'error' | 'stopped';
54
+ export type TaskEvent = {
55
+ cursor: number;
56
+ type: EventType;
57
+ message: string;
58
+ ts: string;
59
+ data?: Record<string, unknown>;
60
+ };
61
+ export type Task = {
62
+ object: 'task';
63
+ task_id: string;
64
+ session_id: string;
65
+ agent_id: string;
66
+ status: TaskStatus;
67
+ done: boolean;
68
+ prompt: string;
69
+ result: string | null;
70
+ error: string | null;
71
+ pending: Question | Approval | null;
72
+ cursor?: number;
73
+ metadata: Metadata;
74
+ created_at: string;
75
+ updated_at: string;
76
+ active_seconds: number;
77
+ billed_minutes: number | null;
78
+ replay_url: string | null;
79
+ recording?: {
80
+ frames: number;
81
+ };
82
+ events?: TaskEvent[];
83
+ trace?: unknown[];
84
+ interrupted?: boolean;
85
+ };
86
+ export type Frame = {
87
+ seq: number;
88
+ t_ms: number;
89
+ after_event: number;
90
+ width: number;
91
+ height: number;
92
+ url: string;
93
+ };
94
+ export type Recording = {
95
+ object: 'recording';
96
+ task_id: string;
97
+ frames: Frame[];
98
+ };
99
+ export type WebhookEventType = 'session.connected' | 'session.disconnected' | 'task.started' | 'task.waiting_for_user' | 'task.waiting_for_approval' | 'task.completed' | 'task.failed' | 'task.stopped';
100
+ export type Webhook = {
101
+ object: 'webhook';
102
+ url: string | null;
103
+ events: WebhookEventType[];
104
+ has_secret: boolean;
105
+ secret?: string;
106
+ event_types: WebhookEventType[];
107
+ };
108
+ export type WebhookEvent = {
109
+ id: string;
110
+ type: WebhookEventType;
111
+ created_at: string;
112
+ org_id: string;
113
+ data: {
114
+ task?: Task;
115
+ session?: Session;
116
+ };
117
+ };
118
+ export type Page<T> = {
119
+ data: T[];
120
+ has_more: boolean;
121
+ next_cursor: string | null;
122
+ };
123
+ export type Deleted = {
124
+ object: string;
125
+ deleted: true;
126
+ };
127
+ export declare class GuidingHandError extends Error {
128
+ status: number;
129
+ type: string;
130
+ body: unknown;
131
+ extra: Record<string, unknown>;
132
+ constructor(message: string, status?: number, type?: string, body?: unknown);
133
+ }
134
+ export declare class InvalidRequestError extends GuidingHandError {
135
+ }
136
+ export declare class AuthenticationError extends GuidingHandError {
137
+ }
138
+ export declare class PaymentRequiredError extends GuidingHandError {
139
+ }
140
+ export declare class PermissionDeniedError extends GuidingHandError {
141
+ }
142
+ export declare class NotFoundError extends GuidingHandError {
143
+ }
144
+ export declare class ConflictError extends GuidingHandError {
145
+ }
146
+ export declare class RateLimitError extends GuidingHandError {
147
+ }
148
+ export declare class APIError extends GuidingHandError {
149
+ }
150
+ export declare class APIConnectionError extends GuidingHandError {
151
+ }
152
+ export declare class TimeoutError extends GuidingHandError {
153
+ }
154
+ export declare class SessionExpiredError extends GuidingHandError {
155
+ }
156
+ /** A task waits on a question or approval and `run()` got no handler for it. */
157
+ export declare class NeedsInputError extends GuidingHandError {
158
+ task: Task;
159
+ constructor(task: Task);
160
+ }
161
+ export declare class WebhookVerificationError extends GuidingHandError {
162
+ }
163
+ export type ClientOptions = {
164
+ /** An org API key (gh_live_…). Defaults to the GUIDINGHAND_API_KEY environment variable. */
165
+ apiKey?: string;
166
+ /** Defaults to https://guidinghand.ai (use https://dev.guidinghand.ai for Stripe test mode). */
167
+ baseUrl?: string;
168
+ /** Per request, in ms (long polls add their wait). Default 60 000. */
169
+ timeout?: number;
170
+ /** Retries on connection errors, 429 and 5xx, for reads and for starts with a request_id. Default 2. */
171
+ maxRetries?: number;
172
+ fetch?: typeof fetch;
173
+ };
174
+ type Req = {
175
+ query?: Record<string, string | number | undefined | null>;
176
+ body?: unknown;
177
+ retry?: boolean;
178
+ wait?: number;
179
+ raw?: boolean;
180
+ };
181
+ export declare class GuidingHand {
182
+ readonly baseUrl: string;
183
+ readonly agents: Agents;
184
+ readonly sessions: Sessions;
185
+ readonly tasks: Tasks;
186
+ readonly webhook: WebhookEndpoint;
187
+ private apiKey;
188
+ private timeout;
189
+ private maxRetries;
190
+ private fetchImpl;
191
+ constructor(opts?: ClientOptions);
192
+ /** @internal */
193
+ request<T>(method: string, path: string, { query, body, retry, wait, raw }?: Req): Promise<T>;
194
+ /** @internal Every item of a paged list. */
195
+ pages<T>(path: string, query: Record<string, string | number | undefined | null>): AsyncGenerator<T>;
196
+ }
197
+ export default GuidingHand;
198
+ declare class Resource {
199
+ protected client: GuidingHand;
200
+ constructor(client: GuidingHand);
201
+ }
202
+ export type AgentCreate = {
203
+ name: string;
204
+ agent_id?: string;
205
+ instructions?: string;
206
+ effort?: Effort;
207
+ greeting?: string;
208
+ };
209
+ export type AgentUpdate = Partial<Omit<AgentCreate, 'agent_id'>>;
210
+ declare class Agents extends Resource {
211
+ list(): Promise<Page<Agent>>;
212
+ create(params: AgentCreate): Promise<Agent>;
213
+ retrieve(agentId: string): Promise<Agent>;
214
+ update(agentId: string, params: AgentUpdate): Promise<Agent>;
215
+ delete(agentId: string): Promise<Deleted>;
216
+ }
217
+ export type SessionCreate = {
218
+ agent_id?: string;
219
+ metadata?: Metadata;
220
+ };
221
+ export type SessionList = {
222
+ agent_id?: string;
223
+ limit?: number;
224
+ cursor?: string;
225
+ };
226
+ declare class Sessions extends Resource {
227
+ /** Makes a code and its invite link. Send `invite_url` to the person at the computer. */
228
+ create(params?: SessionCreate): Promise<Session & {
229
+ session_token: string;
230
+ }>;
231
+ list(params?: SessionList): Promise<Page<Session>>;
232
+ /** Every session, newest first, across pages. */
233
+ listAll(params?: Omit<SessionList, 'limit' | 'cursor'>): AsyncGenerator<Session>;
234
+ retrieve(sessionId: string): Promise<Session>;
235
+ /** Disconnects the computer and deletes the session's tasks and recordings. */
236
+ delete(sessionId: string): Promise<Deleted>;
237
+ /** Resolves once the person's computer is connected. */
238
+ waitForConnection(sessionId: string, { timeout, pollInterval }?: {
239
+ timeout?: number;
240
+ pollInterval?: number;
241
+ }): Promise<Session>;
242
+ }
243
+ export type TaskCreate = {
244
+ prompt: string;
245
+ agent_id?: string;
246
+ request_id?: string;
247
+ metadata?: Metadata;
248
+ };
249
+ export type TaskList = {
250
+ session_id?: string;
251
+ agent_id?: string;
252
+ status?: TaskStatus;
253
+ limit?: number;
254
+ cursor?: string;
255
+ };
256
+ export type Respond = {
257
+ question_id: string;
258
+ answer: string;
259
+ } | {
260
+ approval_id: string;
261
+ decision: 'approve' | 'deny';
262
+ note?: string;
263
+ };
264
+ export type RunOptions = TaskCreate & {
265
+ /** Every event, as it happens. */
266
+ onEvent?: (event: TaskEvent, task: Task) => void | Promise<void>;
267
+ /** The agent asks something: return the answer. */
268
+ onQuestion?: (question: Question, task: Task) => string | Promise<string>;
269
+ /** The agent wants to do something consequential: return true (or 'approve') to let it, false (or 'deny', or { decision, note }) not to. */
270
+ onApproval?: (approval: Approval, task: Task) => boolean | 'approve' | 'deny' | {
271
+ decision: 'approve' | 'deny';
272
+ note?: string;
273
+ } | Promise<boolean | 'approve' | 'deny' | {
274
+ decision: 'approve' | 'deny';
275
+ note?: string;
276
+ }>;
277
+ /** Give up (and stop the task) after this long, in ms. */
278
+ timeout?: number;
279
+ };
280
+ declare class Tasks extends Resource {
281
+ /** Starts a task on the session's computer (it must be connected). */
282
+ create(sessionId: string, params: TaskCreate): Promise<Task>;
283
+ list(params?: TaskList): Promise<Page<Task>>;
284
+ listAll(params?: Omit<TaskList, 'limit' | 'cursor'>): AsyncGenerator<Task>;
285
+ retrieve(taskId: string, { include }?: {
286
+ include?: ('events' | 'trace')[];
287
+ }): Promise<Task>;
288
+ /** Events after `after`; with `wait_ms`, waits (long poll) for the next one. */
289
+ events(taskId: string, { after, wait_ms }?: {
290
+ after?: number;
291
+ wait_ms?: number;
292
+ }): Promise<{
293
+ data: TaskEvent[];
294
+ cursor: number;
295
+ task: Task;
296
+ }>;
297
+ respond(taskId: string, params: Respond): Promise<Task>;
298
+ stop(taskId: string): Promise<Task & {
299
+ interrupted: boolean;
300
+ }>;
301
+ recording(taskId: string): Promise<Recording>;
302
+ /** One recorded screen, as PNG bytes. */
303
+ recordingFrame(taskId: string, seq: number): Promise<Uint8Array>;
304
+ /** Every event of a task as it happens, until it's done. `for await (const e of gh.tasks.stream(id))` */
305
+ stream(taskId: string, { after }?: {
306
+ after?: number;
307
+ }): AsyncGenerator<TaskEvent, Task>;
308
+ /** Starts a task and sees it through: events to onEvent, questions to onQuestion, approvals to onApproval. Resolves with the finished task. */
309
+ run(sessionId: string, { onEvent, onQuestion, onApproval, timeout, ...params }: RunOptions): Promise<Task>;
310
+ }
311
+ declare class WebhookEndpoint extends Resource {
312
+ retrieve(): Promise<Webhook>;
313
+ /** Sets the endpoint. The signing secret is in the response when it's new (or with rotate_secret), never again. */
314
+ update(params: {
315
+ url: string;
316
+ events?: WebhookEventType[];
317
+ rotate_secret?: boolean;
318
+ }): Promise<Webhook>;
319
+ delete(): Promise<Webhook>;
320
+ }
321
+ /**
322
+ * Checks a webhook's GuidingHand-Signature header against the raw request body and your signing secret, and
323
+ * returns the parsed event. Throws WebhookVerificationError if it doesn't match or is older than `tolerance` s.
324
+ */
325
+ export declare function verifyWebhook(payload: string | Uint8Array, signatureHeader: string | null | undefined, secret: string, { tolerance }?: {
326
+ tolerance?: number;
327
+ }): Promise<WebhookEvent>;