@locusai/sdk 0.10.2 → 0.10.4

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.
@@ -23,12 +23,13 @@ export declare class GitWorkflow {
23
23
  createTaskWorktree(task: Task, defaultExecutor: TaskExecutor): {
24
24
  worktreePath: string | null;
25
25
  baseBranch: string | null;
26
+ baseCommitHash: string | null;
26
27
  executor: TaskExecutor;
27
28
  };
28
29
  /**
29
30
  * Commit changes in a task worktree and optionally push to remote.
30
31
  */
31
- commitAndPush(worktreePath: string, task: Task, baseBranch?: string): CommitPushResult;
32
+ commitAndPush(worktreePath: string, task: Task, baseBranch?: string, baseCommitHash?: string): CommitPushResult;
32
33
  /**
33
34
  * Create a pull request for a completed task.
34
35
  */
@@ -1 +1 @@
1
- {"version":3,"file":"git-workflow.d.ts","sourceRoot":"","sources":["../../src/agent/git-workflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAK9C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAExE;;;;;;GAMG;AACH,qBAAa,WAAW;IAKpB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,UAAU;IANpB,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,SAAS,CAAmB;gBAG1B,MAAM,EAAE,YAAY,EACpB,GAAG,EAAE,KAAK,EACV,UAAU,EAAE,MAAM,GAAG,IAAI;IAWnC;;;OAGG;IACH,kBAAkB,CAChB,IAAI,EAAE,IAAI,EACV,eAAe,EAAE,YAAY,GAC5B;QACD,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,QAAQ,EAAE,YAAY,CAAC;KACxB;IA+CD;;OAEG;IACH,aAAa,CACX,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,IAAI,EACV,UAAU,CAAC,EAAE,MAAM,GAClB,gBAAgB;IAqEnB;;OAEG;IACH,iBAAiB,CACf,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,GAClB;QAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IA0BzC;;OAEG;IACH,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,EAAE,OAAO,GAAG,IAAI;CAexE"}
1
+ {"version":3,"file":"git-workflow.d.ts","sourceRoot":"","sources":["../../src/agent/git-workflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAK9C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAExE;;;;;;GAMG;AACH,qBAAa,WAAW;IAKpB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,UAAU;IANpB,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,SAAS,CAAmB;gBAG1B,MAAM,EAAE,YAAY,EACpB,GAAG,EAAE,KAAK,EACV,UAAU,EAAE,MAAM,GAAG,IAAI;IAWnC;;;OAGG;IACH,kBAAkB,CAChB,IAAI,EAAE,IAAI,EACV,eAAe,EAAE,YAAY,GAC5B;QACD,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,QAAQ,EAAE,YAAY,CAAC;KACxB;IAiDD;;OAEG;IACH,aAAa,CACX,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,IAAI,EACV,UAAU,CAAC,EAAE,MAAM,EACnB,cAAc,CAAC,EAAE,MAAM,GACtB,gBAAgB;IA2EnB;;OAEG;IACH,iBAAiB,CACf,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,GAClB;QAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IA0BzC;;OAEG;IACH,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,EAAE,OAAO,GAAG,IAAI;CAexE"}
@@ -17,5 +17,10 @@ export declare class TaskExecutor {
17
17
  success: boolean;
18
18
  summary: string;
19
19
  }>;
20
+ /**
21
+ * Extract a concise summary from the agent's raw output.
22
+ * Takes the last non-empty paragraph, truncated to 500 chars.
23
+ */
24
+ private extractSummary;
20
25
  }
21
26
  //# sourceMappingURL=task-executor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"task-executor.d.ts","sourceRoot":"","sources":["../../src/agent/task-executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAGhD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,QAAQ,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,KAAK,CAAC;CACZ;AAED;;GAEG;AACH,qBAAa,YAAY;IAGX,OAAO,CAAC,IAAI;IAFxB,OAAO,CAAC,aAAa,CAAgB;gBAEjB,IAAI,EAAE,gBAAgB;IAIpC,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAkB1E"}
1
+ {"version":3,"file":"task-executor.d.ts","sourceRoot":"","sources":["../../src/agent/task-executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAGhD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,QAAQ,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,KAAK,CAAC;CACZ;AAED;;GAEG;AACH,qBAAa,YAAY;IAGX,OAAO,CAAC,IAAI;IAFxB,OAAO,CAAC,aAAa,CAAgB;gBAEjB,IAAI,EAAE,gBAAgB;IAIpC,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAiBzE;;;OAGG;IACH,OAAO,CAAC,cAAc;CAoBvB"}
@@ -1 +1 @@
1
- {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/agent/worker.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAc,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGlE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD;;;;;;;;;GASG;AACH,qBAAa,WAAW;IAeV,OAAO,CAAC,MAAM;IAd1B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,WAAW,CAAc;IAGjC,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,iBAAiB,CAA+C;IACxE,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,mBAAmB,CAAuB;IAClD,OAAO,CAAC,kBAAkB,CAAS;gBAEf,MAAM,EAAE,YAAY;IA2ExC,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,OAAgB;YAmB5D,eAAe;YAcf,WAAW;IAgDzB;;OAEG;YACW,WAAW;IA2FzB,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,aAAa;YAgBP,iBAAiB;IAezB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAoH3B"}
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/agent/worker.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAc,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGlE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD;;;;;;;;;GASG;AACH,qBAAa,WAAW;IAeV,OAAO,CAAC,MAAM;IAd1B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,WAAW,CAAc;IAGjC,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,iBAAiB,CAA+C;IACxE,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,mBAAmB,CAAuB;IAClD,OAAO,CAAC,kBAAkB,CAAS;gBAEf,MAAM,EAAE,YAAY;IA2ExC,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,OAAgB;YAmB5D,eAAe;YAcf,WAAW;IAgDzB;;OAEG;YACW,WAAW;IA4FzB,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,aAAa;YAgBP,iBAAiB;IAezB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAoH3B"}
@@ -768,10 +768,12 @@ class ClaudeRunner {
768
768
  currentToolName;
769
769
  activeTools = new Map;
770
770
  activeProcess = null;
771
- constructor(projectPath, model = DEFAULT_MODEL[PROVIDER.CLAUDE], log) {
771
+ timeoutMs;
772
+ constructor(projectPath, model = DEFAULT_MODEL[PROVIDER.CLAUDE], log, timeoutMs) {
772
773
  this.model = model;
773
774
  this.log = log;
774
775
  this.projectPath = import_node_path3.resolve(projectPath);
776
+ this.timeoutMs = timeoutMs ?? DEFAULT_TIMEOUT_MS;
775
777
  }
776
778
  setEventEmitter(emitter) {
777
779
  this.eventEmitter = emitter;
@@ -787,11 +789,14 @@ class ClaudeRunner {
787
789
  let lastError = null;
788
790
  for (let attempt = 1;attempt <= maxRetries; attempt++) {
789
791
  try {
790
- return await this.executeRun(prompt);
792
+ return await this.withTimeout(this.executeRun(prompt));
791
793
  } catch (error) {
792
794
  const err = error;
793
795
  lastError = err;
794
796
  const isLastAttempt = attempt === maxRetries;
797
+ if (err.message.includes("timed out")) {
798
+ throw err;
799
+ }
795
800
  if (!isLastAttempt) {
796
801
  const delay = Math.pow(2, attempt) * 1000;
797
802
  console.warn(`Claude CLI attempt ${attempt} failed: ${err.message}. Retrying in ${delay}ms...`);
@@ -801,6 +806,23 @@ class ClaudeRunner {
801
806
  }
802
807
  throw lastError || new Error("Claude CLI failed after multiple attempts");
803
808
  }
809
+ withTimeout(promise) {
810
+ if (this.timeoutMs <= 0)
811
+ return promise;
812
+ return new Promise((resolve2, reject) => {
813
+ const timer = setTimeout(() => {
814
+ this.abort();
815
+ reject(new Error(`Claude CLI execution timed out after ${Math.round(this.timeoutMs / 60000)} minutes`));
816
+ }, this.timeoutMs);
817
+ promise.then((value) => {
818
+ clearTimeout(timer);
819
+ resolve2(value);
820
+ }, (err) => {
821
+ clearTimeout(timer);
822
+ reject(err);
823
+ });
824
+ });
825
+ }
804
826
  async* runStream(prompt) {
805
827
  const args = [
806
828
  "--dangerously-skip-permissions",
@@ -1165,7 +1187,7 @@ ${c.primary("[Claude]")} ${c.bold(`Running ${content_block.name}...`)}
1165
1187
  return new Error(message);
1166
1188
  }
1167
1189
  }
1168
- var import_node_child_process, import_node_path3, SANDBOX_SETTINGS;
1190
+ var import_node_child_process, import_node_path3, SANDBOX_SETTINGS, DEFAULT_TIMEOUT_MS;
1169
1191
  var init_claude_runner = __esm(() => {
1170
1192
  init_config();
1171
1193
  init_colors();
@@ -1173,12 +1195,16 @@ var init_claude_runner = __esm(() => {
1173
1195
  import_node_child_process = require("node:child_process");
1174
1196
  import_node_path3 = require("node:path");
1175
1197
  SANDBOX_SETTINGS = JSON.stringify({
1198
+ permissions: {
1199
+ deny: ["Read(../**)", "Edit(../**)"]
1200
+ },
1176
1201
  sandbox: {
1177
1202
  enabled: true,
1178
1203
  autoAllow: true,
1179
1204
  allowUnsandboxedCommands: false
1180
1205
  }
1181
1206
  });
1207
+ DEFAULT_TIMEOUT_MS = 60 * 60 * 1000;
1182
1208
  });
1183
1209
 
1184
1210
  // src/ai/codex-runner.ts
@@ -1187,10 +1213,17 @@ class CodexRunner {
1187
1213
  model;
1188
1214
  log;
1189
1215
  activeProcess = null;
1190
- constructor(projectPath, model = DEFAULT_MODEL[PROVIDER.CODEX], log) {
1216
+ eventEmitter;
1217
+ currentToolName;
1218
+ timeoutMs;
1219
+ constructor(projectPath, model = DEFAULT_MODEL[PROVIDER.CODEX], log, timeoutMs) {
1191
1220
  this.projectPath = projectPath;
1192
1221
  this.model = model;
1193
1222
  this.log = log;
1223
+ this.timeoutMs = timeoutMs ?? DEFAULT_TIMEOUT_MS2;
1224
+ }
1225
+ setEventEmitter(emitter) {
1226
+ this.eventEmitter = emitter;
1194
1227
  }
1195
1228
  abort() {
1196
1229
  if (this.activeProcess && !this.activeProcess.killed) {
@@ -1203,9 +1236,12 @@ class CodexRunner {
1203
1236
  let lastError = null;
1204
1237
  for (let attempt = 1;attempt <= maxRetries; attempt++) {
1205
1238
  try {
1206
- return await this.executeRun(prompt);
1239
+ return await this.withTimeout(this.executeRun(prompt));
1207
1240
  } catch (error) {
1208
1241
  lastError = error;
1242
+ if (lastError.message.includes("timed out")) {
1243
+ throw lastError;
1244
+ }
1209
1245
  if (attempt < maxRetries) {
1210
1246
  const delay = Math.pow(2, attempt) * 1000;
1211
1247
  console.warn(`Codex CLI attempt ${attempt} failed: ${lastError.message}. Retrying in ${delay}ms...`);
@@ -1215,9 +1251,31 @@ class CodexRunner {
1215
1251
  }
1216
1252
  throw lastError || new Error("Codex CLI failed after multiple attempts");
1217
1253
  }
1254
+ withTimeout(promise) {
1255
+ if (this.timeoutMs <= 0)
1256
+ return promise;
1257
+ return new Promise((resolve2, reject) => {
1258
+ const timer = setTimeout(() => {
1259
+ this.abort();
1260
+ reject(new Error(`Codex CLI execution timed out after ${Math.round(this.timeoutMs / 60000)} minutes`));
1261
+ }, this.timeoutMs);
1262
+ promise.then((value) => {
1263
+ clearTimeout(timer);
1264
+ resolve2(value);
1265
+ }, (err) => {
1266
+ clearTimeout(timer);
1267
+ reject(err);
1268
+ });
1269
+ });
1270
+ }
1218
1271
  async* runStream(prompt) {
1219
1272
  const outputPath = import_node_path4.join(import_node_os2.tmpdir(), `locus-codex-${import_node_crypto.randomUUID()}.txt`);
1220
1273
  const args = this.buildArgs(outputPath);
1274
+ this.eventEmitter?.emitSessionStarted({
1275
+ model: this.model,
1276
+ provider: "codex"
1277
+ });
1278
+ this.eventEmitter?.emitPromptSubmitted(prompt, prompt.length > 500);
1221
1279
  const codex = import_node_child_process2.spawn("codex", args, {
1222
1280
  cwd: this.projectPath,
1223
1281
  stdio: ["pipe", "pipe", "pipe"],
@@ -1230,7 +1288,21 @@ class CodexRunner {
1230
1288
  let processEnded = false;
1231
1289
  let errorMessage = "";
1232
1290
  let finalOutput = "";
1291
+ let finalContent = "";
1292
+ let isThinking = false;
1233
1293
  const enqueueChunk = (chunk) => {
1294
+ this.emitEventForChunk(chunk, isThinking);
1295
+ if (chunk.type === "thinking") {
1296
+ isThinking = true;
1297
+ } else if (chunk.type === "text_delta" || chunk.type === "tool_use") {
1298
+ if (isThinking) {
1299
+ this.eventEmitter?.emitThinkingStoped();
1300
+ isThinking = false;
1301
+ }
1302
+ }
1303
+ if (chunk.type === "text_delta") {
1304
+ finalContent += chunk.content;
1305
+ }
1234
1306
  if (resolveChunk) {
1235
1307
  const resolve2 = resolveChunk;
1236
1308
  resolveChunk = null;
@@ -1271,16 +1343,21 @@ class CodexRunner {
1271
1343
  codex.stderr.on("data", processOutput);
1272
1344
  codex.on("error", (err) => {
1273
1345
  errorMessage = `Failed to start Codex CLI: ${err.message}. Ensure 'codex' is installed and available in PATH.`;
1346
+ this.eventEmitter?.emitErrorOccurred(errorMessage, "SPAWN_ERROR");
1274
1347
  signalEnd();
1275
1348
  });
1276
1349
  codex.on("close", (code) => {
1277
1350
  this.activeProcess = null;
1278
- this.cleanupTempFile(outputPath);
1279
1351
  if (code === 0) {
1280
1352
  const result = this.readOutput(outputPath, finalOutput);
1353
+ this.cleanupTempFile(outputPath);
1281
1354
  enqueueChunk({ type: "result", content: result });
1282
- } else if (!errorMessage) {
1283
- errorMessage = this.createErrorFromOutput(code, finalOutput).message;
1355
+ } else {
1356
+ this.cleanupTempFile(outputPath);
1357
+ if (!errorMessage) {
1358
+ errorMessage = this.createErrorFromOutput(code, finalOutput).message;
1359
+ this.eventEmitter?.emitErrorOccurred(errorMessage, `EXIT_${code}`);
1360
+ }
1284
1361
  }
1285
1362
  signalEnd();
1286
1363
  });
@@ -1294,6 +1371,12 @@ class CodexRunner {
1294
1371
  } else if (processEnded) {
1295
1372
  if (errorMessage) {
1296
1373
  yield { type: "error", error: errorMessage };
1374
+ this.eventEmitter?.emitSessionEnded(false);
1375
+ } else {
1376
+ if (finalContent) {
1377
+ this.eventEmitter?.emitResponseCompleted(finalContent);
1378
+ }
1379
+ this.eventEmitter?.emitSessionEnded(true);
1297
1380
  }
1298
1381
  break;
1299
1382
  } else {
@@ -1303,6 +1386,12 @@ class CodexRunner {
1303
1386
  if (chunk === null) {
1304
1387
  if (errorMessage) {
1305
1388
  yield { type: "error", error: errorMessage };
1389
+ this.eventEmitter?.emitSessionEnded(false);
1390
+ } else {
1391
+ if (finalContent) {
1392
+ this.eventEmitter?.emitResponseCompleted(finalContent);
1393
+ }
1394
+ this.eventEmitter?.emitSessionEnded(true);
1306
1395
  }
1307
1396
  break;
1308
1397
  }
@@ -1310,6 +1399,36 @@ class CodexRunner {
1310
1399
  }
1311
1400
  }
1312
1401
  }
1402
+ emitEventForChunk(chunk, isThinking) {
1403
+ if (!this.eventEmitter)
1404
+ return;
1405
+ switch (chunk.type) {
1406
+ case "text_delta":
1407
+ this.eventEmitter.emitTextDelta(chunk.content);
1408
+ break;
1409
+ case "tool_use":
1410
+ if (this.currentToolName) {
1411
+ this.eventEmitter.emitToolCompleted(this.currentToolName);
1412
+ }
1413
+ this.currentToolName = chunk.tool;
1414
+ this.eventEmitter.emitToolStarted(chunk.tool);
1415
+ break;
1416
+ case "thinking":
1417
+ if (!isThinking) {
1418
+ this.eventEmitter.emitThinkingStarted(chunk.content);
1419
+ }
1420
+ break;
1421
+ case "result":
1422
+ if (this.currentToolName) {
1423
+ this.eventEmitter.emitToolCompleted(this.currentToolName);
1424
+ this.currentToolName = undefined;
1425
+ }
1426
+ break;
1427
+ case "error":
1428
+ this.eventEmitter.emitErrorOccurred(chunk.error);
1429
+ break;
1430
+ }
1431
+ }
1313
1432
  executeRun(prompt) {
1314
1433
  return new Promise((resolve2, reject) => {
1315
1434
  const outputPath = import_node_path4.join(import_node_os2.tmpdir(), `locus-codex-${import_node_crypto.randomUUID()}.txt`);
@@ -1339,10 +1458,12 @@ class CodexRunner {
1339
1458
  });
1340
1459
  codex.on("close", (code) => {
1341
1460
  this.activeProcess = null;
1342
- this.cleanupTempFile(outputPath);
1343
1461
  if (code === 0) {
1344
- resolve2(this.readOutput(outputPath, output));
1462
+ const result = this.readOutput(outputPath, output);
1463
+ this.cleanupTempFile(outputPath);
1464
+ resolve2(result);
1345
1465
  } else {
1466
+ this.cleanupTempFile(outputPath);
1346
1467
  reject(this.createErrorFromOutput(code, errorOutput));
1347
1468
  }
1348
1469
  });
@@ -1352,12 +1473,18 @@ class CodexRunner {
1352
1473
  }
1353
1474
  buildArgs(outputPath) {
1354
1475
  const args = [
1476
+ "--ask-for-approval",
1477
+ "never",
1355
1478
  "exec",
1356
1479
  "--sandbox",
1357
1480
  "workspace-write",
1358
1481
  "--skip-git-repo-check",
1359
1482
  "--output-last-message",
1360
- outputPath
1483
+ outputPath,
1484
+ "-c",
1485
+ "sandbox_workspace_write.network_access=true",
1486
+ "-c",
1487
+ 'sandbox.excludedCommands=["git", "gh"]'
1361
1488
  ];
1362
1489
  if (this.model) {
1363
1490
  args.push("--model", this.model);
@@ -1408,7 +1535,7 @@ class CodexRunner {
1408
1535
  return new Promise((resolve2) => setTimeout(resolve2, ms));
1409
1536
  }
1410
1537
  }
1411
- var import_node_child_process2, import_node_crypto, import_node_fs2, import_node_os2, import_node_path4;
1538
+ var import_node_child_process2, import_node_crypto, import_node_fs2, import_node_os2, import_node_path4, DEFAULT_TIMEOUT_MS2;
1412
1539
  var init_codex_runner = __esm(() => {
1413
1540
  init_config();
1414
1541
  init_resolve_bin();
@@ -1417,6 +1544,7 @@ var init_codex_runner = __esm(() => {
1417
1544
  import_node_fs2 = require("node:fs");
1418
1545
  import_node_os2 = require("node:os");
1419
1546
  import_node_path4 = require("node:path");
1547
+ DEFAULT_TIMEOUT_MS2 = 60 * 60 * 1000;
1420
1548
  });
1421
1549
 
1422
1550
  // src/ai/factory.ts
@@ -1425,9 +1553,9 @@ function createAiRunner(provider, config) {
1425
1553
  const model = config.model ?? DEFAULT_MODEL[resolvedProvider];
1426
1554
  switch (resolvedProvider) {
1427
1555
  case PROVIDER.CODEX:
1428
- return new CodexRunner(config.projectPath, model, config.log);
1556
+ return new CodexRunner(config.projectPath, model, config.log, config.timeoutMs);
1429
1557
  default:
1430
- return new ClaudeRunner(config.projectPath, model, config.log);
1558
+ return new ClaudeRunner(config.projectPath, model, config.log, config.timeoutMs);
1431
1559
  }
1432
1560
  }
1433
1561
  var init_factory = __esm(() => {
@@ -1952,8 +2080,9 @@ class WorktreeManager {
1952
2080
  this.ensureDirectory(this.rootPath, "Worktree root");
1953
2081
  addWorktree();
1954
2082
  }
1955
- this.log(`Worktree created at ${worktreePath}`, "success");
1956
- return { worktreePath, branch, baseBranch };
2083
+ const baseCommitHash = this.git("rev-parse HEAD", worktreePath).trim();
2084
+ this.log(`Worktree created at ${worktreePath} (base: ${baseCommitHash.slice(0, 8)})`, "success");
2085
+ return { worktreePath, branch, baseBranch, baseCommitHash };
1957
2086
  }
1958
2087
  list() {
1959
2088
  const output = this.git("worktree list --porcelain", this.projectPath);
@@ -2058,27 +2187,54 @@ class WorktreeManager {
2058
2187
  try {
2059
2188
  const count = this.git(`rev-list --count "${baseBranch}..HEAD"`, worktreePath).trim();
2060
2189
  return Number.parseInt(count, 10) > 0;
2190
+ } catch (err) {
2191
+ this.log(`Could not compare HEAD against base branch "${baseBranch}": ${err instanceof Error ? err.message : String(err)}`, "warn");
2192
+ return false;
2193
+ }
2194
+ }
2195
+ hasCommitsAheadOfHash(worktreePath, baseHash) {
2196
+ try {
2197
+ const headHash = this.git("rev-parse HEAD", worktreePath).trim();
2198
+ return headHash !== baseHash;
2061
2199
  } catch {
2062
2200
  return false;
2063
2201
  }
2064
2202
  }
2065
- commitChanges(worktreePath, message, baseBranch) {
2203
+ commitChanges(worktreePath, message, baseBranch, baseCommitHash) {
2066
2204
  const hasUncommittedChanges = this.hasChanges(worktreePath);
2205
+ if (hasUncommittedChanges) {
2206
+ const statusOutput = this.git("status --porcelain", worktreePath).trim();
2207
+ this.log(`Detected uncommitted changes:
2208
+ ${statusOutput.split(`
2209
+ `).slice(0, 10).join(`
2210
+ `)}${statusOutput.split(`
2211
+ `).length > 10 ? `
2212
+ ... and ${statusOutput.split(`
2213
+ `).length - 10} more` : ""}`, "info");
2214
+ }
2067
2215
  if (!hasUncommittedChanges) {
2068
2216
  if (baseBranch && this.hasCommitsAhead(worktreePath, baseBranch)) {
2069
2217
  const hash2 = this.git("rev-parse HEAD", worktreePath).trim();
2070
2218
  this.log(`Agent already committed changes (${hash2.slice(0, 8)}); skipping additional commit`, "info");
2071
2219
  return hash2;
2072
2220
  }
2073
- this.log("No changes to commit", "info");
2221
+ if (baseCommitHash && this.hasCommitsAheadOfHash(worktreePath, baseCommitHash)) {
2222
+ const hash2 = this.git("rev-parse HEAD", worktreePath).trim();
2223
+ this.log(`Agent already committed changes (${hash2.slice(0, 8)}, detected via base commit hash); skipping additional commit`, "info");
2224
+ return hash2;
2225
+ }
2226
+ const branch = this.getBranch(worktreePath);
2227
+ this.log(`No changes detected in worktree (branch: ${branch}, baseBranch: ${baseBranch ?? "none"}, baseCommitHash: ${baseCommitHash?.slice(0, 8) ?? "none"})`, "warn");
2074
2228
  return null;
2075
2229
  }
2076
2230
  this.git("add -A", worktreePath);
2077
2231
  const staged = this.git("diff --cached --name-only", worktreePath).trim();
2078
2232
  if (!staged) {
2079
- this.log("No changes to commit", "info");
2233
+ this.log("All changes were ignored by .gitignore — nothing to commit", "warn");
2080
2234
  return null;
2081
2235
  }
2236
+ this.log(`Staging ${staged.split(`
2237
+ `).length} file(s) for commit`, "info");
2082
2238
  this.gitExec(["commit", "-m", message], worktreePath);
2083
2239
  const hash = this.git("rev-parse HEAD", worktreePath).trim();
2084
2240
  this.log(`Committed: ${hash.slice(0, 8)}`, "success");
@@ -2500,15 +2656,27 @@ class TaskExecutor {
2500
2656
  const basePrompt = await this.promptBuilder.build(task);
2501
2657
  try {
2502
2658
  this.deps.log("Starting Execution...", "info");
2503
- await this.deps.aiRunner.run(basePrompt);
2504
- return {
2505
- success: true,
2506
- summary: "Task completed by the agent"
2507
- };
2659
+ const output = await this.deps.aiRunner.run(basePrompt);
2660
+ const summary = this.extractSummary(output);
2661
+ return { success: true, summary };
2508
2662
  } catch (error) {
2509
2663
  return { success: false, summary: `Error: ${error}` };
2510
2664
  }
2511
2665
  }
2666
+ extractSummary(output) {
2667
+ if (!output || !output.trim()) {
2668
+ return "Task completed by the agent";
2669
+ }
2670
+ const paragraphs = output.split(/\n\n+/).map((p) => p.trim()).filter((p) => p.length > 0);
2671
+ if (paragraphs.length === 0) {
2672
+ return "Task completed by the agent";
2673
+ }
2674
+ const last = paragraphs[paragraphs.length - 1];
2675
+ if (last.length > 500) {
2676
+ return `${last.slice(0, 497)}...`;
2677
+ }
2678
+ return last;
2679
+ }
2512
2680
  }
2513
2681
  var init_task_executor = __esm(() => {
2514
2682
  init_prompt_builder();
@@ -2526,7 +2694,7 @@ class GitWorkflow {
2526
2694
  this.log = log;
2527
2695
  this.ghUsername = ghUsername;
2528
2696
  const projectPath = config.projectPath || process.cwd();
2529
- this.worktreeManager = config.useWorktrees ? new WorktreeManager(projectPath, { cleanupPolicy: "auto" }) : null;
2697
+ this.worktreeManager = config.useWorktrees ? new WorktreeManager(projectPath, { cleanupPolicy: "auto" }, log) : null;
2530
2698
  this.prService = config.autoPush ? new PrService(projectPath, log) : null;
2531
2699
  }
2532
2700
  createTaskWorktree(task, defaultExecutor) {
@@ -2534,6 +2702,7 @@ class GitWorkflow {
2534
2702
  return {
2535
2703
  worktreePath: null,
2536
2704
  baseBranch: null,
2705
+ baseCommitHash: null,
2537
2706
  executor: defaultExecutor
2538
2707
  };
2539
2708
  }
@@ -2559,10 +2728,11 @@ class GitWorkflow {
2559
2728
  return {
2560
2729
  worktreePath: result.worktreePath,
2561
2730
  baseBranch: result.baseBranch,
2731
+ baseCommitHash: result.baseCommitHash,
2562
2732
  executor: taskExecutor
2563
2733
  };
2564
2734
  }
2565
- commitAndPush(worktreePath, task, baseBranch) {
2735
+ commitAndPush(worktreePath, task, baseBranch, baseCommitHash) {
2566
2736
  if (!this.worktreeManager) {
2567
2737
  return { branch: null, pushed: false, pushFailed: false };
2568
2738
  }
@@ -2579,7 +2749,7 @@ class GitWorkflow {
2579
2749
 
2580
2750
  ${trailers.join(`
2581
2751
  `)}`;
2582
- const hash = this.worktreeManager.commitChanges(worktreePath, commitMessage, baseBranch);
2752
+ const hash = this.worktreeManager.commitChanges(worktreePath, commitMessage, baseBranch, baseCommitHash);
2583
2753
  if (!hash) {
2584
2754
  this.log("No changes to commit for this task", "info");
2585
2755
  return {
@@ -2619,7 +2789,12 @@ ${trailers.join(`
2619
2789
  } catch (err) {
2620
2790
  const errorMessage = err instanceof Error ? err.message : String(err);
2621
2791
  this.log(`Git commit failed: ${errorMessage}`, "error");
2622
- return { branch: null, pushed: false, pushFailed: false };
2792
+ return {
2793
+ branch: null,
2794
+ pushed: false,
2795
+ pushFailed: true,
2796
+ pushError: `Git commit/push failed: ${errorMessage}`
2797
+ };
2623
2798
  }
2624
2799
  }
2625
2800
  createPullRequest(task, branch, summary, baseBranch) {
@@ -2851,7 +3026,7 @@ class AgentWorker {
2851
3026
  }
2852
3027
  async executeTask(task) {
2853
3028
  const fullTask = await this.client.tasks.getById(task.id, this.config.workspaceId);
2854
- const { worktreePath, baseBranch, executor } = this.gitWorkflow.createTaskWorktree(fullTask, this.taskExecutor);
3029
+ const { worktreePath, baseBranch, baseCommitHash, executor } = this.gitWorkflow.createTaskWorktree(fullTask, this.taskExecutor);
2855
3030
  this.currentWorktreePath = worktreePath;
2856
3031
  let branchPushed = false;
2857
3032
  let keepBranch = false;
@@ -2863,7 +3038,7 @@ class AgentWorker {
2863
3038
  let prError = null;
2864
3039
  let noChanges = false;
2865
3040
  if (result.success && worktreePath) {
2866
- const commitResult = this.gitWorkflow.commitAndPush(worktreePath, fullTask, baseBranch ?? undefined);
3041
+ const commitResult = this.gitWorkflow.commitAndPush(worktreePath, fullTask, baseBranch ?? undefined, baseCommitHash ?? undefined);
2867
3042
  taskBranch = commitResult.branch;
2868
3043
  branchPushed = commitResult.pushed;
2869
3044
  keepBranch = taskBranch !== null;
@@ -10,7 +10,8 @@ export declare class ClaudeRunner implements AiRunner {
10
10
  private currentToolName?;
11
11
  private activeTools;
12
12
  private activeProcess;
13
- constructor(projectPath: string, model?: string, log?: LogFn | undefined);
13
+ timeoutMs: number;
14
+ constructor(projectPath: string, model?: string, log?: LogFn | undefined, timeoutMs?: number);
14
15
  /**
15
16
  * Set an event emitter to receive execution events.
16
17
  */
@@ -20,6 +21,7 @@ export declare class ClaudeRunner implements AiRunner {
20
21
  */
21
22
  abort(): void;
22
23
  run(prompt: string): Promise<string>;
24
+ private withTimeout;
23
25
  runStream(prompt: string): AsyncGenerator<StreamChunk, void, unknown>;
24
26
  /**
25
27
  * Emit an event corresponding to a stream chunk.
@@ -1 +1 @@
1
- {"version":3,"file":"claude-runner.d.ts","sourceRoot":"","sources":["../../src/ai/claude-runner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,kBAAkB,CAAC;AAGhE,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAwC5C,qBAAa,YAAa,YAAW,QAAQ;IASzC,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,GAAG,CAAC;IATd,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAAC,CAAmB;IACxC,OAAO,CAAC,eAAe,CAAC,CAAS;IACjC,OAAO,CAAC,WAAW,CAA+C;IAClE,OAAO,CAAC,aAAa,CAA6B;gBAGhD,WAAW,EAAE,MAAM,EACX,KAAK,GAAE,MAAuC,EAC9C,GAAG,CAAC,EAAE,KAAK,YAAA;IAKrB;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAIhD;;OAEG;IACH,KAAK,IAAI,IAAI;IAOP,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAyBnC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAAC,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC;IAqL5E;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAiCzB,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,kBAAkB;IAwE1B,OAAO,CAAC,UAAU;IA0FlB,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,oBAAoB;CAO7B"}
1
+ {"version":3,"file":"claude-runner.d.ts","sourceRoot":"","sources":["../../src/ai/claude-runner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,kBAAkB,CAAC;AAGhE,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AA8C5C,qBAAa,YAAa,YAAW,QAAQ;IAUzC,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,GAAG,CAAC;IAVd,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAAC,CAAmB;IACxC,OAAO,CAAC,eAAe,CAAC,CAAS;IACjC,OAAO,CAAC,WAAW,CAA+C;IAClE,OAAO,CAAC,aAAa,CAA6B;IAClD,SAAS,EAAE,MAAM,CAAC;gBAGhB,WAAW,EAAE,MAAM,EACX,KAAK,GAAE,MAAuC,EAC9C,GAAG,CAAC,EAAE,KAAK,YAAA,EACnB,SAAS,CAAC,EAAE,MAAM;IAMpB;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAIhD;;OAEG;IACH,KAAK,IAAI,IAAI;IAOP,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA8B1C,OAAO,CAAC,WAAW;IA0BZ,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAAC,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC;IAqL5E;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAiCzB,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,kBAAkB;IAwE1B,OAAO,CAAC,UAAU;IA0FlB,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,oBAAoB;CAO7B"}
@@ -1,3 +1,4 @@
1
+ import type { ExecEventEmitter } from "../exec/event-emitter.js";
1
2
  import type { StreamChunk } from "../exec/types.js";
2
3
  import type { LogFn } from "./factory.js";
3
4
  import type { AiRunner } from "./runner.js";
@@ -6,13 +7,19 @@ export declare class CodexRunner implements AiRunner {
6
7
  private model;
7
8
  private log?;
8
9
  private activeProcess;
9
- constructor(projectPath: string, model?: string, log?: LogFn | undefined);
10
+ private eventEmitter?;
11
+ private currentToolName?;
12
+ timeoutMs: number;
13
+ constructor(projectPath: string, model?: string, log?: LogFn | undefined, timeoutMs?: number);
14
+ setEventEmitter(emitter: ExecEventEmitter): void;
10
15
  /**
11
16
  * Abort the currently running Codex CLI process, if any.
12
17
  */
13
18
  abort(): void;
14
19
  run(prompt: string): Promise<string>;
20
+ private withTimeout;
15
21
  runStream(prompt: string): AsyncGenerator<StreamChunk, void, unknown>;
22
+ private emitEventForChunk;
16
23
  private executeRun;
17
24
  private buildArgs;
18
25
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"codex-runner.d.ts","sourceRoot":"","sources":["../../src/ai/codex-runner.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,qBAAa,WAAY,YAAW,QAAQ;IAIxC,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,GAAG,CAAC;IALd,OAAO,CAAC,aAAa,CAA6B;gBAGxC,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,MAAsC,EAC7C,GAAG,CAAC,EAAE,KAAK,YAAA;IAGrB;;OAEG;IACH,KAAK,IAAI,IAAI;IAOP,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAuBnC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAAC,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC;IAgH5E,OAAO,CAAC,UAAU;IAuDlB,OAAO,CAAC,SAAS;IAkBjB;;;OAGG;IACH,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,UAAU;IAYlB,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,KAAK;CAGd"}
1
+ {"version":3,"file":"codex-runner.d.ts","sourceRoot":"","sources":["../../src/ai/codex-runner.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAK5C,qBAAa,WAAY,YAAW,QAAQ;IAOxC,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,GAAG,CAAC;IARd,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,YAAY,CAAC,CAAmB;IACxC,OAAO,CAAC,eAAe,CAAC,CAAS;IACjC,SAAS,EAAE,MAAM,CAAC;gBAGR,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,MAAsC,EAC7C,GAAG,CAAC,EAAE,KAAK,YAAA,EACnB,SAAS,CAAC,EAAE,MAAM;IAKpB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAIhD;;OAEG;IACH,KAAK,IAAI,IAAI;IAOP,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA4B1C,OAAO,CAAC,WAAW;IA0BZ,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAAC,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC;IAwJ5E,OAAO,CAAC,iBAAiB;IA+BzB,OAAO,CAAC,UAAU;IAyDlB,OAAO,CAAC,SAAS;IAyBjB;;;OAGG;IACH,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,UAAU;IAYlB,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,KAAK;CAGd"}
@@ -4,6 +4,8 @@ export interface AiRunnerConfig {
4
4
  projectPath: string;
5
5
  model?: string;
6
6
  log?: LogFn;
7
+ /** Maximum execution time in milliseconds (default: 30 minutes) */
8
+ timeoutMs?: number;
7
9
  }
8
10
  export declare function createAiRunner(provider: AiProvider | undefined, config: AiRunnerConfig): AiRunner;
9
11
  //# sourceMappingURL=factory.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/ai/factory.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAExD,MAAM,MAAM,KAAK,GAAG,CAClB,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,KAC1C,IAAI,CAAC;AAEV,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,KAAK,CAAC;CACb;AAED,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,UAAU,GAAG,SAAS,EAChC,MAAM,EAAE,cAAc,GACrB,QAAQ,CAUV"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/ai/factory.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAExD,MAAM,MAAM,KAAK,GAAG,CAClB,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,KAC1C,IAAI,CAAC;AAEV,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,KAAK,CAAC;IACZ,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,UAAU,GAAG,SAAS,EAChC,MAAM,EAAE,cAAc,GACrB,QAAQ,CAoBV"}
@@ -13,6 +13,11 @@ export interface AiRunner {
13
13
  * Used during graceful shutdown to prevent orphaned processes.
14
14
  */
15
15
  abort(): void;
16
+ /**
17
+ * Maximum execution time in milliseconds.
18
+ * When exceeded, the active process is killed and run() rejects.
19
+ */
20
+ timeoutMs?: number;
16
21
  }
17
22
  export type AiProvider = Provider;
18
23
  //# sourceMappingURL=runner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/ai/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,QAAQ;IACvB,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAAC,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACtE;;OAEG;IACH,eAAe,CAAC,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClD;;;OAGG;IACH,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/ai/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,QAAQ;IACvB,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAAC,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACtE;;OAEG;IACH,eAAe,CAAC,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClD;;;OAGG;IACH,KAAK,IAAI,IAAI,CAAC;IACd;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC"}