agi 0.3.0 → 0.4.1

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.
package/dist/index.mjs CHANGED
@@ -1,4 +1,12 @@
1
1
  import { createParser } from 'eventsource-parser';
2
+ import { spawn } from 'child_process';
3
+ import { EventEmitter } from 'events';
4
+ import { createInterface } from 'readline';
5
+ import { existsSync } from 'fs';
6
+ import { dirname, join, delimiter } from 'path';
7
+ import { fileURLToPath } from 'url';
8
+ import { platform, arch } from 'os';
9
+ import { createRequire } from 'module';
2
10
 
3
11
  // src/http.ts
4
12
 
@@ -112,6 +120,43 @@ var HTTPClient = class {
112
120
  }
113
121
  throw lastError || new AGIError("Request failed after retries");
114
122
  }
123
+ /**
124
+ * Make an HTTP request to an absolute URL with retries and error handling
125
+ */
126
+ async requestUrl(method, url, options) {
127
+ const headers = this.buildHeaders(options?.headers);
128
+ let lastError;
129
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
130
+ try {
131
+ const controller = new AbortController();
132
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
133
+ const response = await fetch(url, {
134
+ method,
135
+ headers,
136
+ body: options?.json ? JSON.stringify(options.json) : void 0,
137
+ signal: controller.signal
138
+ });
139
+ clearTimeout(timeoutId);
140
+ if (!response.ok) {
141
+ await this.handleErrorResponse(response);
142
+ }
143
+ const data = await response.json();
144
+ return data;
145
+ } catch (error) {
146
+ lastError = error;
147
+ if (error instanceof AGIError && error.statusCode && error.statusCode < 500) {
148
+ if (error.statusCode !== 429) {
149
+ throw error;
150
+ }
151
+ }
152
+ if (attempt === this.maxRetries) {
153
+ break;
154
+ }
155
+ await this.sleep(Math.pow(2, attempt) * 1e3);
156
+ }
157
+ }
158
+ throw lastError || new AGIError("Request failed after retries");
159
+ }
115
160
  /**
116
161
  * Stream Server-Sent Events from an endpoint
117
162
  */
@@ -192,10 +237,11 @@ var HTTPClient = class {
192
237
  }
193
238
  async handleErrorResponse(response) {
194
239
  let errorData;
240
+ const text = await response.text();
195
241
  try {
196
- errorData = await response.json();
242
+ errorData = JSON.parse(text);
197
243
  } catch {
198
- errorData = await response.text();
244
+ errorData = text;
199
245
  }
200
246
  const errorMessage = typeof errorData === "object" && errorData.message ? errorData.message : typeof errorData === "string" ? errorData : `HTTP ${response.status}: ${response.statusText}`;
201
247
  switch (response.status) {
@@ -577,7 +623,8 @@ function normalizeSessionResponse(data) {
577
623
  status: data.status,
578
624
  createdAt: data.created_at ?? data.createdAt,
579
625
  environmentId: data.environment_id ?? data.environmentId,
580
- goal: data.goal
626
+ goal: data.goal,
627
+ agentSessionType: data.agent_session_type ?? data.agentSessionType
581
628
  };
582
629
  }
583
630
 
@@ -590,16 +637,24 @@ var SessionsResource = class {
590
637
  /**
591
638
  * Create a new agent session
592
639
  *
593
- * @param agentName - Agent model to use (e.g., "agi-0", "agi-0-fast", "agi-1")
640
+ * @param agentName - Agent model to use (e.g., "agi-0", "agi-2-claude")
594
641
  * @param options - Session creation options
595
- * @returns SessionResponse with session_id, vnc_url, status, etc.
642
+ * @returns SessionResponse with sessionId, vncUrl, agentUrl, status, etc.
596
643
  *
597
644
  * @example
598
645
  * ```typescript
646
+ * // Standard browser session
599
647
  * const session = await client.sessions.create('agi-0', {
600
648
  * webhookUrl: 'https://yourapp.com/webhook',
601
649
  * maxSteps: 200
602
650
  * });
651
+ *
652
+ * // Desktop session (client-managed)
653
+ * const session = await client.sessions.create('agi-2-claude', {
654
+ * agentSessionType: 'desktop',
655
+ * goal: 'Open calculator and compute 2+2'
656
+ * });
657
+ * console.log(session.agentUrl); // Use with client.desktop.step()
603
658
  * ```
604
659
  */
605
660
  async create(agentName = "agi-0", options) {
@@ -612,6 +667,11 @@ var SessionsResource = class {
612
667
  if (options?.restoreFromEnvironmentId) {
613
668
  payload.restore_from_environment_id = options.restoreFromEnvironmentId;
614
669
  }
670
+ if (options?.agentSessionType) {
671
+ payload.agent_session_type = options.agentSessionType;
672
+ }
673
+ if (options?.cdpUrl) payload.cdp_url = options.cdpUrl;
674
+ if (options?.environmentType) payload.environment_type = options.environmentType;
615
675
  const response = await this.http.request("POST", "/v1/sessions", {
616
676
  json: payload
617
677
  });
@@ -876,6 +936,90 @@ var SessionsResource = class {
876
936
  async screenshot(sessionId) {
877
937
  return this.http.request("GET", `/v1/sessions/${sessionId}/screenshot`);
878
938
  }
939
+ // ===== CLIENT-DRIVEN SESSION CONTROL =====
940
+ /**
941
+ * Execute a single step for client-driven sessions (desktop mode).
942
+ *
943
+ * In desktop mode (agentSessionType="desktop"), the client manages the
944
+ * execution loop. This method sends a screenshot to the agent and receives
945
+ * actions to execute locally.
946
+ *
947
+ * @param agentUrl - Agent service URL from session.agentUrl
948
+ * @param sessionId - Session ID (required for routing in shared sandbox)
949
+ * @param screenshot - Base64-encoded screenshot (full resolution, JPEG or PNG)
950
+ * @param message - Optional user message (goal on first call, or follow-up instruction)
951
+ * @returns StepDesktopResponse with actions, thinking, finished, askUser, and step
952
+ *
953
+ * @example
954
+ * ```typescript
955
+ * // Create a desktop session
956
+ * const session = await client.sessions.create('agi-2-claude', {
957
+ * agentSessionType: 'desktop',
958
+ * goal: 'Open calculator and compute 2+2'
959
+ * });
960
+ *
961
+ * // Client-managed loop
962
+ * let finished = false;
963
+ * while (!finished) {
964
+ * const screenshot = captureScreenshot(); // Client captures
965
+ * const result = await client.sessions.step(
966
+ * session.agentUrl!,
967
+ * session.sessionId,
968
+ * screenshot
969
+ * );
970
+ * executeActions(result.actions); // Client executes
971
+ * finished = result.finished;
972
+ * if (result.askUser) {
973
+ * const answer = await promptUser(result.askUser);
974
+ * // Send answer in next step
975
+ * }
976
+ * }
977
+ * ```
978
+ */
979
+ async step(agentUrl, sessionId, screenshot, message) {
980
+ const url = `${agentUrl.replace(/\/$/, "")}/step_desktop`;
981
+ const payload = { screenshot, session_id: sessionId };
982
+ if (message !== void 0) {
983
+ payload.message = message;
984
+ }
985
+ const response = await this.http.requestUrl("POST", url, {
986
+ json: payload
987
+ });
988
+ return {
989
+ actions: response.actions || [],
990
+ thinking: response.thinking,
991
+ finished: response.finished ?? false,
992
+ askUser: response.ask_user ?? response.askUser,
993
+ step: response.step ?? 0
994
+ };
995
+ }
996
+ // ===== MODELS =====
997
+ /**
998
+ * List available agent models.
999
+ *
1000
+ * @param filter - Optional filter: "cdp" for browser agents, "desktop" for
1001
+ * desktop agents
1002
+ * @returns ModelsResponse with list of available model names
1003
+ *
1004
+ * @example
1005
+ * ```typescript
1006
+ * // List all models
1007
+ * const models = await client.sessions.listModels();
1008
+ * console.log(models.models);
1009
+ *
1010
+ * // List only desktop-compatible models
1011
+ * const desktopModels = await client.sessions.listModels('desktop');
1012
+ * console.log(desktopModels.models);
1013
+ * // ['agi-2-claude', 'agi-2-qwen']
1014
+ * ```
1015
+ */
1016
+ async listModels(filter) {
1017
+ const query = {};
1018
+ if (filter) {
1019
+ query.filter = filter;
1020
+ }
1021
+ return this.http.request("GET", "/v1/models", { query });
1022
+ }
879
1023
  };
880
1024
 
881
1025
  // src/client.ts
@@ -941,6 +1085,615 @@ var AGIClient = class {
941
1085
  }
942
1086
  };
943
1087
 
944
- export { AGIClient, AGIError, APIError, AgentExecutionError, AuthenticationError, NotFoundError, PermissionError, RateLimitError, Screenshot, SessionContext, SessionsResource, ValidationError };
1088
+ // src/loop.ts
1089
+ var AgentLoop = class {
1090
+ client;
1091
+ agentUrl;
1092
+ sessionId;
1093
+ captureScreenshot;
1094
+ executeActions;
1095
+ onThinking;
1096
+ onAskUser;
1097
+ onStep;
1098
+ stepDelay;
1099
+ _state = "idle";
1100
+ _lastResult = null;
1101
+ _currentStep = 0;
1102
+ // Pause control using Promise-based approach
1103
+ pauseResolve = null;
1104
+ pausePromise = null;
1105
+ constructor(options) {
1106
+ this.client = options.client;
1107
+ this.agentUrl = options.agentUrl;
1108
+ this.sessionId = options.sessionId;
1109
+ this.captureScreenshot = options.captureScreenshot;
1110
+ this.executeActions = options.executeActions;
1111
+ this.onThinking = options.onThinking;
1112
+ this.onAskUser = options.onAskUser;
1113
+ this.onStep = options.onStep;
1114
+ this.stepDelay = options.stepDelay ?? 0;
1115
+ }
1116
+ /** Current state of the loop. */
1117
+ get state() {
1118
+ return this._state;
1119
+ }
1120
+ /** Current step number. */
1121
+ get currentStep() {
1122
+ return this._currentStep;
1123
+ }
1124
+ /** Last step result, if any. */
1125
+ get lastResult() {
1126
+ return this._lastResult;
1127
+ }
1128
+ /**
1129
+ * Start the execution loop.
1130
+ *
1131
+ * Runs the loop until the task is finished, stopped, or an unhandled
1132
+ * ask_user question is encountered.
1133
+ *
1134
+ * @param message - Optional initial message (goal or instruction).
1135
+ * Usually not needed if goal was set during session creation.
1136
+ * @returns The final StepDesktopResponse
1137
+ * @throws Error if loop is already running
1138
+ */
1139
+ async start(message) {
1140
+ if (this._state === "running") {
1141
+ throw new Error("Loop is already running");
1142
+ }
1143
+ this._state = "running";
1144
+ let currentMessage = message;
1145
+ let result = null;
1146
+ try {
1147
+ while (true) {
1148
+ if (this.pausePromise) {
1149
+ await this.pausePromise;
1150
+ }
1151
+ const currentState = this._state;
1152
+ if (currentState === "paused") {
1153
+ continue;
1154
+ }
1155
+ if (currentState !== "running") {
1156
+ break;
1157
+ }
1158
+ const screenshot = await this.captureScreenshot();
1159
+ result = await this.client.sessions.step(
1160
+ this.agentUrl,
1161
+ this.sessionId,
1162
+ screenshot,
1163
+ currentMessage
1164
+ );
1165
+ currentMessage = void 0;
1166
+ this._lastResult = result;
1167
+ this._currentStep = result.step;
1168
+ if (result.thinking && this.onThinking) {
1169
+ this.onThinking(result.thinking);
1170
+ }
1171
+ if (this.onStep) {
1172
+ this.onStep(result.step, result);
1173
+ }
1174
+ if (result.askUser) {
1175
+ if (this.onAskUser) {
1176
+ const answer = await this.onAskUser(result.askUser);
1177
+ currentMessage = answer;
1178
+ } else {
1179
+ this._state = "stopped";
1180
+ return result;
1181
+ }
1182
+ }
1183
+ if (result.actions.length > 0) {
1184
+ await this.executeActions(result.actions);
1185
+ }
1186
+ if (result.finished) {
1187
+ this._state = "finished";
1188
+ return result;
1189
+ }
1190
+ if (this.stepDelay > 0) {
1191
+ await new Promise((resolve) => setTimeout(resolve, this.stepDelay));
1192
+ }
1193
+ }
1194
+ } catch (error) {
1195
+ this._state = "stopped";
1196
+ throw error;
1197
+ }
1198
+ if (result === null) {
1199
+ result = {
1200
+ actions: [],
1201
+ thinking: void 0,
1202
+ finished: true,
1203
+ askUser: void 0,
1204
+ step: this._currentStep
1205
+ };
1206
+ }
1207
+ return result;
1208
+ }
1209
+ /**
1210
+ * Pause the execution loop.
1211
+ *
1212
+ * The loop will complete the current step before pausing.
1213
+ * Call resume() to continue.
1214
+ */
1215
+ pause() {
1216
+ if (this._state !== "running") {
1217
+ return;
1218
+ }
1219
+ this._state = "paused";
1220
+ this.pausePromise = new Promise((resolve) => {
1221
+ this.pauseResolve = resolve;
1222
+ });
1223
+ }
1224
+ /**
1225
+ * Resume a paused loop.
1226
+ */
1227
+ resume() {
1228
+ if (this._state !== "paused") {
1229
+ return;
1230
+ }
1231
+ this._state = "running";
1232
+ if (this.pauseResolve) {
1233
+ this.pauseResolve();
1234
+ this.pauseResolve = null;
1235
+ this.pausePromise = null;
1236
+ }
1237
+ }
1238
+ /**
1239
+ * Stop the execution loop.
1240
+ *
1241
+ * The loop will complete the current step before stopping.
1242
+ */
1243
+ stop() {
1244
+ this._state = "stopped";
1245
+ if (this.pauseResolve) {
1246
+ this.pauseResolve();
1247
+ this.pauseResolve = null;
1248
+ this.pausePromise = null;
1249
+ }
1250
+ }
1251
+ /** Check if the loop is currently running. */
1252
+ isRunning() {
1253
+ return this._state === "running";
1254
+ }
1255
+ /** Check if the loop is currently paused. */
1256
+ isPaused() {
1257
+ return this._state === "paused";
1258
+ }
1259
+ /** Check if the loop has finished successfully. */
1260
+ isFinished() {
1261
+ return this._state === "finished";
1262
+ }
1263
+ };
1264
+ var __filename$1 = fileURLToPath(import.meta.url);
1265
+ var __dirname$1 = dirname(__filename$1);
1266
+ var require2 = createRequire(import.meta.url);
1267
+ function getPlatformId() {
1268
+ const os = platform();
1269
+ const cpu = arch();
1270
+ if (os === "darwin") {
1271
+ return cpu === "arm64" ? "darwin-arm64" : "darwin-x64";
1272
+ } else if (os === "linux") {
1273
+ return "linux-x64";
1274
+ } else if (os === "win32") {
1275
+ return "win32-x64";
1276
+ }
1277
+ throw new Error(`Unsupported platform: ${os}-${cpu}`);
1278
+ }
1279
+ function getBinaryFilename(platformId) {
1280
+ const id = platformId ?? getPlatformId();
1281
+ if (id === "win32-x64") {
1282
+ return "agi-driver.exe";
1283
+ }
1284
+ return "agi-driver";
1285
+ }
1286
+ function getSearchPaths(platformId) {
1287
+ const filename = getBinaryFilename(platformId);
1288
+ const paths = [];
1289
+ const packageName = `@agi/agi-${platformId}`;
1290
+ try {
1291
+ const packagePath = require2.resolve(`${packageName}/package.json`);
1292
+ const packageDir = dirname(packagePath);
1293
+ paths.push(join(packageDir, filename));
1294
+ } catch {
1295
+ }
1296
+ paths.push(join(__dirname$1, "..", "..", "bin", filename));
1297
+ paths.push(join(__dirname$1, "..", "..", "..", "bin", filename));
1298
+ const envPath = process.env.PATH || "";
1299
+ for (const dir of envPath.split(delimiter)) {
1300
+ if (dir) {
1301
+ paths.push(join(dir, filename));
1302
+ }
1303
+ }
1304
+ return paths;
1305
+ }
1306
+ function findBinaryPath() {
1307
+ const platformId = getPlatformId();
1308
+ const searchPaths = getSearchPaths(platformId);
1309
+ for (const path of searchPaths) {
1310
+ if (existsSync(path)) {
1311
+ return path;
1312
+ }
1313
+ }
1314
+ throw new Error(
1315
+ `Could not find agi-driver binary for ${platformId}. Searched: ${searchPaths.join(", ")}. Install the optional dependency @agi/agi-${platformId} or ensure agi-driver is in PATH.`
1316
+ );
1317
+ }
1318
+ function isBinaryAvailable() {
1319
+ try {
1320
+ findBinaryPath();
1321
+ return true;
1322
+ } catch {
1323
+ return false;
1324
+ }
1325
+ }
1326
+
1327
+ // src/driver/protocol.ts
1328
+ function parseEvent(line) {
1329
+ const data = JSON.parse(line);
1330
+ return data;
1331
+ }
1332
+ function serializeCommand(command) {
1333
+ return JSON.stringify(command);
1334
+ }
1335
+
1336
+ // src/driver/driver.ts
1337
+ var AgentDriver = class extends EventEmitter {
1338
+ binaryPath;
1339
+ model;
1340
+ platform;
1341
+ mode;
1342
+ agentName;
1343
+ apiUrl;
1344
+ environmentType;
1345
+ env;
1346
+ process = null;
1347
+ readline = null;
1348
+ state = "idle";
1349
+ step = 0;
1350
+ sessionId = "";
1351
+ screenWidth = 0;
1352
+ screenHeight = 0;
1353
+ resolveStart = null;
1354
+ rejectStart = null;
1355
+ // Pending callbacks for user interaction
1356
+ pendingConfirm = null;
1357
+ pendingAnswer = null;
1358
+ constructor(options = {}) {
1359
+ super();
1360
+ this.binaryPath = options.binaryPath ?? findBinaryPath();
1361
+ this.model = options.model ?? "claude-sonnet";
1362
+ this.platform = options.platform ?? "desktop";
1363
+ this.mode = options.mode ?? "";
1364
+ this.agentName = options.agentName ?? "";
1365
+ this.apiUrl = options.apiUrl ?? "";
1366
+ this.environmentType = options.environmentType ?? "";
1367
+ this.env = options.env ?? {};
1368
+ }
1369
+ /**
1370
+ * Get the current state of the driver.
1371
+ */
1372
+ get currentState() {
1373
+ return this.state;
1374
+ }
1375
+ /**
1376
+ * Get the current step number.
1377
+ */
1378
+ get currentStep() {
1379
+ return this.step;
1380
+ }
1381
+ /**
1382
+ * Check if the driver is running.
1383
+ */
1384
+ get isRunning() {
1385
+ return this.state === "running";
1386
+ }
1387
+ /**
1388
+ * Check if the driver is waiting for user input.
1389
+ */
1390
+ get isWaiting() {
1391
+ return this.state === "waiting_confirmation" || this.state === "waiting_answer";
1392
+ }
1393
+ /**
1394
+ * Start the agent with a goal.
1395
+ *
1396
+ * @param goal - The task for the agent to accomplish
1397
+ * @param screenshot - Initial screenshot (base64-encoded). Not needed in local mode.
1398
+ * @param screenWidth - Screen width in pixels. Not needed in local mode.
1399
+ * @param screenHeight - Screen height in pixels. Not needed in local mode.
1400
+ * @param mode - Override the mode set in DriverOptions.
1401
+ * @returns Promise that resolves when the agent finishes
1402
+ */
1403
+ async start(goal, screenshot = "", screenWidth = 0, screenHeight = 0, mode) {
1404
+ if (this.process) {
1405
+ throw new Error("Driver is already running");
1406
+ }
1407
+ this.sessionId = `session_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
1408
+ this.screenWidth = screenWidth;
1409
+ this.screenHeight = screenHeight;
1410
+ return new Promise((resolve, reject) => {
1411
+ this.resolveStart = resolve;
1412
+ this.rejectStart = reject;
1413
+ this.process = spawn(this.binaryPath, [], {
1414
+ stdio: ["pipe", "pipe", "pipe"],
1415
+ env: {
1416
+ ...process.env,
1417
+ ...this.env
1418
+ }
1419
+ });
1420
+ this.process.on("error", (err) => {
1421
+ this.cleanup();
1422
+ if (this.rejectStart) {
1423
+ this.rejectStart(err);
1424
+ this.rejectStart = null;
1425
+ this.resolveStart = null;
1426
+ }
1427
+ });
1428
+ this.process.on("exit", (code) => {
1429
+ this.cleanup();
1430
+ if (this.rejectStart) {
1431
+ this.rejectStart(new Error(`Driver exited with code ${code}`));
1432
+ this.rejectStart = null;
1433
+ this.resolveStart = null;
1434
+ }
1435
+ });
1436
+ this.readline = createInterface({
1437
+ input: this.process.stdout,
1438
+ crlfDelay: Infinity
1439
+ });
1440
+ this.readline.on("line", (line) => {
1441
+ this.handleLine(line).catch((err) => {
1442
+ this.emit("error", {
1443
+ event: "error",
1444
+ message: `Error handling line: ${err.message}`,
1445
+ code: "parse_error",
1446
+ recoverable: false,
1447
+ step: this.step
1448
+ });
1449
+ });
1450
+ });
1451
+ this.process.stderr?.on("data", (data) => {
1452
+ this.emit("stderr", data.toString());
1453
+ });
1454
+ this.once("ready", () => {
1455
+ const startCmd = {
1456
+ command: "start",
1457
+ session_id: this.sessionId,
1458
+ goal,
1459
+ screenshot,
1460
+ screen_width: screenWidth,
1461
+ screen_height: screenHeight,
1462
+ platform: this.platform,
1463
+ model: this.model,
1464
+ mode: mode ?? this.mode,
1465
+ agent_name: this.agentName || void 0,
1466
+ api_url: this.apiUrl || void 0,
1467
+ environment_type: this.environmentType || void 0
1468
+ };
1469
+ this.sendCommand(startCmd);
1470
+ });
1471
+ });
1472
+ }
1473
+ /**
1474
+ * Send a new screenshot to the driver.
1475
+ *
1476
+ * @param screenshot - Base64-encoded screenshot
1477
+ * @param screenWidth - Screen width in pixels
1478
+ * @param screenHeight - Screen height in pixels
1479
+ */
1480
+ sendScreenshot(screenshot, screenWidth, screenHeight) {
1481
+ if (!this.process) {
1482
+ throw new Error("Driver is not running");
1483
+ }
1484
+ const cmd = {
1485
+ command: "screenshot",
1486
+ data: screenshot,
1487
+ screen_width: screenWidth ?? this.screenWidth,
1488
+ screen_height: screenHeight ?? this.screenHeight
1489
+ };
1490
+ this.sendCommand(cmd);
1491
+ }
1492
+ /**
1493
+ * Pause the driver.
1494
+ */
1495
+ pause() {
1496
+ if (!this.process) return;
1497
+ this.sendCommand({ command: "pause" });
1498
+ }
1499
+ /**
1500
+ * Resume the driver.
1501
+ */
1502
+ resume() {
1503
+ if (!this.process) return;
1504
+ this.sendCommand({ command: "resume" });
1505
+ }
1506
+ /**
1507
+ * Stop the driver.
1508
+ *
1509
+ * @param reason - Reason for stopping
1510
+ */
1511
+ async stop(reason) {
1512
+ if (!this.process) return;
1513
+ const cmd = {
1514
+ command: "stop",
1515
+ reason
1516
+ };
1517
+ this.sendCommand(cmd);
1518
+ await new Promise((resolve) => {
1519
+ if (!this.process) {
1520
+ resolve();
1521
+ return;
1522
+ }
1523
+ this.process.once("exit", () => resolve());
1524
+ setTimeout(() => {
1525
+ if (this.process) {
1526
+ this.process.kill();
1527
+ }
1528
+ resolve();
1529
+ }, 1e3);
1530
+ });
1531
+ this.cleanup();
1532
+ }
1533
+ /**
1534
+ * Respond to a confirmation request.
1535
+ *
1536
+ * @param approved - Whether the action is approved
1537
+ * @param message - Optional message to send with the response
1538
+ */
1539
+ respondConfirm(approved, message) {
1540
+ if (!this.process || this.state !== "waiting_confirmation") {
1541
+ throw new Error("Not waiting for confirmation");
1542
+ }
1543
+ const cmd = {
1544
+ command: "confirm",
1545
+ approved,
1546
+ message
1547
+ };
1548
+ this.sendCommand(cmd);
1549
+ if (this.pendingConfirm) {
1550
+ this.pendingConfirm(approved, message);
1551
+ this.pendingConfirm = null;
1552
+ }
1553
+ }
1554
+ /**
1555
+ * Respond to a question.
1556
+ *
1557
+ * @param text - The answer text
1558
+ * @param questionId - Optional question ID
1559
+ */
1560
+ respondAnswer(text, questionId) {
1561
+ if (!this.process || this.state !== "waiting_answer") {
1562
+ throw new Error("Not waiting for answer");
1563
+ }
1564
+ const cmd = {
1565
+ command: "answer",
1566
+ text,
1567
+ question_id: questionId
1568
+ };
1569
+ this.sendCommand(cmd);
1570
+ if (this.pendingAnswer) {
1571
+ this.pendingAnswer(text);
1572
+ this.pendingAnswer = null;
1573
+ }
1574
+ }
1575
+ sendCommand(cmd) {
1576
+ if (!this.process?.stdin) return;
1577
+ const line = serializeCommand(cmd) + "\n";
1578
+ this.process.stdin.write(line);
1579
+ }
1580
+ async handleLine(line) {
1581
+ if (!line.trim()) return;
1582
+ let event;
1583
+ try {
1584
+ event = parseEvent(line);
1585
+ } catch (e) {
1586
+ console.error("Failed to parse event:", line);
1587
+ return;
1588
+ }
1589
+ this.step = event.step;
1590
+ this.emit("event", event);
1591
+ switch (event.event) {
1592
+ case "ready":
1593
+ this.emit("ready", event);
1594
+ break;
1595
+ case "state_change":
1596
+ this.state = event.state;
1597
+ this.emit("state_change", event.state, event);
1598
+ break;
1599
+ case "thinking":
1600
+ this.emit("thinking", event.text, event);
1601
+ break;
1602
+ case "action":
1603
+ this.emit("action", event.action, event);
1604
+ break;
1605
+ case "confirm": {
1606
+ this.state = "waiting_confirmation";
1607
+ const confirmListeners = this.rawListeners("confirm");
1608
+ let confirmHandled = false;
1609
+ for (const listener of confirmListeners) {
1610
+ try {
1611
+ const approved = await listener(event.reason, event);
1612
+ if (typeof approved === "boolean" && !confirmHandled) {
1613
+ this.respondConfirm(approved);
1614
+ confirmHandled = true;
1615
+ }
1616
+ } catch {
1617
+ }
1618
+ }
1619
+ break;
1620
+ }
1621
+ case "ask_question": {
1622
+ this.state = "waiting_answer";
1623
+ const questionListeners = this.rawListeners("ask_question");
1624
+ let questionHandled = false;
1625
+ for (const listener of questionListeners) {
1626
+ try {
1627
+ const answer = await listener(event.question, event);
1628
+ if (typeof answer === "string" && !questionHandled) {
1629
+ this.respondAnswer(answer, event.question_id);
1630
+ questionHandled = true;
1631
+ }
1632
+ } catch {
1633
+ }
1634
+ }
1635
+ break;
1636
+ }
1637
+ case "screenshot_captured":
1638
+ this.emit("screenshot_captured", event);
1639
+ break;
1640
+ case "session_created":
1641
+ this.emit("session_created", event);
1642
+ break;
1643
+ case "finished":
1644
+ this.handleFinished(event);
1645
+ break;
1646
+ case "error":
1647
+ this.handleError(event);
1648
+ break;
1649
+ }
1650
+ }
1651
+ handleFinished(event) {
1652
+ this.state = "finished";
1653
+ this.emit("finished", event);
1654
+ if (this.resolveStart) {
1655
+ this.resolveStart({
1656
+ success: event.success,
1657
+ reason: event.reason,
1658
+ summary: event.summary,
1659
+ step: event.step
1660
+ });
1661
+ this.resolveStart = null;
1662
+ this.rejectStart = null;
1663
+ }
1664
+ this.cleanup();
1665
+ }
1666
+ handleError(event) {
1667
+ this.emit("error", event);
1668
+ if (!event.recoverable) {
1669
+ this.state = "error";
1670
+ if (this.rejectStart) {
1671
+ this.rejectStart(new Error(`${event.code}: ${event.message}`));
1672
+ this.rejectStart = null;
1673
+ this.resolveStart = null;
1674
+ }
1675
+ this.cleanup();
1676
+ }
1677
+ }
1678
+ cleanup() {
1679
+ if (this.readline) {
1680
+ this.readline.close();
1681
+ this.readline = null;
1682
+ }
1683
+ if (this.rejectStart) {
1684
+ this.rejectStart(new Error("Driver stopped"));
1685
+ this.rejectStart = null;
1686
+ this.resolveStart = null;
1687
+ }
1688
+ if (this.process) {
1689
+ this.process.kill();
1690
+ this.process = null;
1691
+ }
1692
+ this.pendingConfirm = null;
1693
+ this.pendingAnswer = null;
1694
+ }
1695
+ };
1696
+
1697
+ export { AGIClient, AGIError, APIError, AgentDriver, AgentExecutionError, AgentLoop, AuthenticationError, NotFoundError, PermissionError, RateLimitError, Screenshot, SessionContext, SessionsResource, ValidationError, findBinaryPath, getPlatformId, isBinaryAvailable };
945
1698
  //# sourceMappingURL=index.mjs.map
946
1699
  //# sourceMappingURL=index.mjs.map