doer-agent 0.5.6 → 0.5.8

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.
@@ -279,33 +279,57 @@ function extractLastSessionMessage(candidateLines) {
279
279
  }
280
280
  return null;
281
281
  }
282
- async function readLastSessionMessage(fileHandle, fileSize) {
283
- const chunkBytes = 16_384;
284
- const maxScanBytes = 131_072;
285
- if (fileSize <= 0) {
286
- return null;
287
- }
288
- let position = fileSize;
289
- let scanned = 0;
290
- let carry = "";
291
- while (position > 0 && scanned < maxScanBytes) {
292
- const readSize = Math.min(chunkBytes, position, maxScanBytes - scanned);
293
- position -= readSize;
294
- scanned += readSize;
295
- const buffer = Buffer.alloc(readSize);
296
- const { bytesRead } = await fileHandle.read(buffer, 0, readSize, position);
282
+ function linePrefixContainsSessionMessageCandidate(prefix) {
283
+ return (/"type"\s*:\s*"event_msg"/.test(prefix) &&
284
+ /"type"\s*:\s*"(agent_message|user_message)"/.test(prefix));
285
+ }
286
+ async function readLineSpan(fileHandle, start, end) {
287
+ const readSize = Math.max(0, end - start);
288
+ if (readSize <= 0) {
289
+ return "";
290
+ }
291
+ const buffer = Buffer.alloc(readSize);
292
+ let totalBytesRead = 0;
293
+ while (totalBytesRead < readSize) {
294
+ const { bytesRead } = await fileHandle.read(buffer, totalBytesRead, readSize - totalBytesRead, start + totalBytesRead);
297
295
  if (bytesRead <= 0) {
298
296
  break;
299
297
  }
300
- const merged = buffer.toString("utf8", 0, bytesRead) + carry;
301
- const lines = merged.split(/\r?\n/);
302
- carry = lines.shift() || "";
303
- const found = extractLastSessionMessage(lines.reverse());
304
- if (found) {
305
- return found;
298
+ totalBytesRead += bytesRead;
299
+ }
300
+ return buffer.toString("utf8", 0, totalBytesRead).trim();
301
+ }
302
+ async function readLastSessionMessage(workspaceRoot, filePath) {
303
+ const index = await readSessionLineIndex(workspaceRoot, filePath);
304
+ const totalLines = index.lineStartOffsets.length;
305
+ if (totalLines <= 0 || index.size <= 0) {
306
+ return null;
307
+ }
308
+ const resolvedFile = resolveSessionFilePath(workspaceRoot, filePath);
309
+ const fileHandle = await open(resolvedFile, "r");
310
+ try {
311
+ for (let lineIndex = totalLines - 1; lineIndex >= 0; lineIndex -= 1) {
312
+ const start = index.lineStartOffsets[lineIndex] ?? index.size;
313
+ const end = lineIndex + 1 < totalLines ? (index.lineStartOffsets[lineIndex + 1] ?? index.size) : index.size;
314
+ const spanBytes = Math.max(0, end - start);
315
+ if (spanBytes <= 0) {
316
+ continue;
317
+ }
318
+ const prefixBytes = Math.min(spanBytes, 1024);
319
+ const prefix = await readLineSpan(fileHandle, start, start + prefixBytes);
320
+ if (!linePrefixContainsSessionMessageCandidate(prefix)) {
321
+ continue;
322
+ }
323
+ const found = extractLastSessionMessage([await readLineSpan(fileHandle, start, end)]);
324
+ if (found) {
325
+ return found;
326
+ }
306
327
  }
328
+ return null;
329
+ }
330
+ finally {
331
+ await fileHandle.close().catch(() => undefined);
307
332
  }
308
- return extractLastSessionMessage([carry]);
309
333
  }
310
334
  function normalizeSessionMeta(rawMeta, filePath, mtimeMs) {
311
335
  const baseName = path.basename(filePath, path.extname(filePath));
@@ -321,13 +345,13 @@ function normalizeSessionMeta(rawMeta, filePath, mtimeMs) {
321
345
  filePath,
322
346
  };
323
347
  }
324
- async function readSessionSummary(filePath, mtimeMs) {
348
+ async function readSessionSummary(workspaceRoot, filePath, mtimeMs) {
325
349
  let fileHandle = null;
326
350
  try {
327
351
  fileHandle = await open(filePath, "r");
328
352
  const entryStat = await fileHandle.stat();
329
353
  const firstLine = await readFirstLine(fileHandle, entryStat.size);
330
- const tailSummary = await readLastSessionMessage(fileHandle, entryStat.size);
354
+ const tailSummary = await readLastSessionMessage(workspaceRoot, filePath);
331
355
  let normalized = normalizeSessionMeta({}, filePath, mtimeMs);
332
356
  if (firstLine) {
333
357
  try {
@@ -363,6 +387,7 @@ async function readSessionSummary(filePath, mtimeMs) {
363
387
  }
364
388
  }
365
389
  async function listAgentSessions(workspaceRoot) {
390
+ const maxSessionSummaries = 10;
366
391
  const sessionsRoot = getSessionsRootPath(workspaceRoot);
367
392
  let sessionsRootStat;
368
393
  try {
@@ -376,8 +401,23 @@ async function listAgentSessions(workspaceRoot) {
376
401
  }
377
402
  const files = await collectSessionJsonlFiles(workspaceRoot);
378
403
  files.sort((a, b) => b.mtimeMs - a.mtimeMs || a.filePath.localeCompare(b.filePath));
379
- const sessions = await Promise.all(files.slice(0, 10).map((file) => readSessionSummary(file.filePath, file.mtimeMs)));
380
- return sessions.sort((a, b) => toSortableTimestampMs(b.updatedAt) - toSortableTimestampMs(a.updatedAt) || b.filePath.localeCompare(a.filePath));
404
+ const sessions = [];
405
+ for (let index = 0; index < files.length; index += 1) {
406
+ const file = files[index];
407
+ sessions.push(await readSessionSummary(workspaceRoot, file.filePath, file.mtimeMs));
408
+ sessions.sort((a, b) => toSortableTimestampMs(b.updatedAt) - toSortableTimestampMs(a.updatedAt) || b.filePath.localeCompare(a.filePath));
409
+ if (sessions.length > maxSessionSummaries) {
410
+ sessions.length = maxSessionSummaries;
411
+ }
412
+ const nextFile = files[index + 1] ?? null;
413
+ const oldestSelectedSession = sessions[maxSessionSummaries - 1] ?? null;
414
+ if (nextFile &&
415
+ oldestSelectedSession &&
416
+ toSortableTimestampMs(oldestSelectedSession.updatedAt) >= nextFile.mtimeMs) {
417
+ break;
418
+ }
419
+ }
420
+ return sessions;
381
421
  }
382
422
  async function readSessionLineIndex(workspaceRoot, filePath) {
383
423
  const resolvedFile = resolveSessionFilePath(workspaceRoot, filePath);
@@ -17,6 +17,8 @@ export function createDefaultAgentSettingsConfig() {
17
17
  codex: {
18
18
  model: "gpt-5.5",
19
19
  authMode: "api_key",
20
+ computerUseEnabled: false,
21
+ browserUseEnabled: false,
20
22
  },
21
23
  realtime: {
22
24
  model: process.env.OPENAI_REALTIME_MODEL?.trim() || "gpt-realtime",
@@ -203,6 +205,8 @@ export function normalizeAgentSettingsConfig(value, fallback) {
203
205
  codex: {
204
206
  model: typeof codex.model === "string" && codex.model.trim() ? codex.model.trim() : base.codex.model,
205
207
  authMode: codex.authMode === "oauth" ? "oauth" : codex.authMode === "api_key" ? "api_key" : base.codex.authMode,
208
+ computerUseEnabled: typeof codex.computerUseEnabled === "boolean" ? codex.computerUseEnabled : base.codex.computerUseEnabled,
209
+ browserUseEnabled: typeof codex.browserUseEnabled === "boolean" ? codex.browserUseEnabled : base.codex.browserUseEnabled,
206
210
  },
207
211
  realtime: {
208
212
  model: typeof realtime.model === "string" && realtime.model.trim() ? realtime.model.trim() : base.realtime.model,
@@ -281,6 +285,8 @@ export async function toAgentSettingsPublic(args) {
281
285
  codex: {
282
286
  model: args.config.codex.model,
283
287
  authMode: args.config.codex.authMode,
288
+ computerUseEnabled: args.config.codex.computerUseEnabled,
289
+ browserUseEnabled: args.config.codex.browserUseEnabled,
284
290
  hasApiKey: false,
285
291
  apiKeyMasked: null,
286
292
  apiKeyLength: null,
@@ -355,6 +361,8 @@ export function normalizeAgentSettingsPatch(value) {
355
361
  move("personality", "general", "personality");
356
362
  move("codexModel", "codex", "model");
357
363
  move("codexAuthMode", "codex", "authMode");
364
+ move("computerUseEnabled", "codex", "computerUseEnabled");
365
+ move("browserUseEnabled", "codex", "browserUseEnabled");
358
366
  move("realtimeModel", "realtime", "model");
359
367
  move("realtimeVoice", "realtime", "voice");
360
368
  move("realtimeWakeName", "realtime", "wakeName");
package/dist/agent.js CHANGED
@@ -283,6 +283,10 @@ async function handleRunRpcMessage(args) {
283
283
  agentProjectDir: AGENT_PROJECT_DIR,
284
284
  workspaceRoot,
285
285
  }),
286
+ "--config",
287
+ `features.computer_use=${localAgentSettings.codex.computerUseEnabled ? "true" : "false"}`,
288
+ "--config",
289
+ `features.browser_use=${localAgentSettings.codex.browserUseEnabled ? "true" : "false"}`,
286
290
  ],
287
291
  }),
288
292
  cwd: request.cwd,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doer-agent",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "description": "Reverse-polling agent runtime for doer",
5
5
  "type": "module",
6
6
  "main": "dist/agent.js",