negotium 0.1.29 → 0.1.31

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.
Files changed (46) hide show
  1. package/dist/agent-helpers.js +378 -76
  2. package/dist/agent-helpers.js.map +13 -12
  3. package/dist/background-bash.js.map +1 -1
  4. package/dist/{chunk-yk55n9dk.js → chunk-1p2n47qd.js} +7 -24
  5. package/dist/{chunk-yk55n9dk.js.map → chunk-1p2n47qd.js.map} +4 -4
  6. package/dist/cron.js +4992 -4537
  7. package/dist/cron.js.map +14 -14
  8. package/dist/hosted-agent.js +335 -43
  9. package/dist/hosted-agent.js.map +10 -9
  10. package/dist/main.js +7351 -6678
  11. package/dist/main.js.map +25 -24
  12. package/dist/mcp-factories.js +13 -8
  13. package/dist/mcp-factories.js.map +5 -5
  14. package/dist/prompts.js +2 -3
  15. package/dist/prompts.js.map +4 -4
  16. package/dist/registry.js +1 -1
  17. package/dist/rollout.js +1 -1
  18. package/dist/runtime/src/agents/claude-provider.ts +2 -0
  19. package/dist/runtime/src/agents/codex-provider.ts +192 -5
  20. package/dist/runtime/src/agents/rollout/codex.ts +201 -22
  21. package/dist/runtime/src/agents/rollout/index.ts +6 -0
  22. package/dist/runtime/src/agents/tool-format.ts +228 -15
  23. package/dist/runtime/src/bus.ts +21 -3
  24. package/dist/runtime/src/mcp/background-bash-server.ts +7 -4
  25. package/dist/runtime/src/mcp/wiki-server.ts +8 -5
  26. package/dist/runtime/src/platform/mcp-config.ts +9 -4
  27. package/dist/runtime/src/prompts/builders.ts +2 -3
  28. package/dist/runtime/src/runtime/turn-runner.ts +71 -17
  29. package/dist/runtime/src/storage/wiki-summary-names.ts +25 -5
  30. package/dist/runtime/src/types/api.ts +2 -0
  31. package/dist/runtime/src/types.ts +2 -0
  32. package/dist/runtime/src/version.ts +1 -1
  33. package/dist/runtime-helpers.js.map +1 -1
  34. package/dist/storage.js +17 -4
  35. package/dist/storage.js.map +4 -4
  36. package/dist/types/packages/core/src/agents/rollout/codex.d.ts +24 -0
  37. package/dist/types/packages/core/src/agents/rollout/index.d.ts +2 -2
  38. package/dist/types/packages/core/src/agents/tool-format.d.ts +16 -9
  39. package/dist/types/packages/core/src/bus.d.ts +2 -2
  40. package/dist/types/packages/core/src/prompts/builders.d.ts +1 -1
  41. package/dist/types/packages/core/src/storage/wiki-summary-names.d.ts +9 -0
  42. package/dist/types/packages/core/src/types/api.d.ts +2 -0
  43. package/dist/types/packages/core/src/types.d.ts +2 -0
  44. package/dist/types/packages/core/src/version.d.ts +1 -1
  45. package/dist/vault.js.map +1 -1
  46. package/package.json +2 -1
@@ -1296,9 +1296,9 @@ import { join as join10 } from "path";
1296
1296
 
1297
1297
  // ../../packages/core/src/agents/rollout/codex.ts
1298
1298
  import { randomBytes as randomBytes3 } from "crypto";
1299
- import { existsSync as existsSync5, readFileSync as readFileSync4, statSync as statSync2, unlinkSync as unlinkSync4 } from "fs";
1299
+ import { existsSync as existsSync5, readFileSync as readFileSync4, realpathSync, statSync as statSync2, unlinkSync as unlinkSync4 } from "fs";
1300
1300
  import { homedir as homedir5 } from "os";
1301
- import { join as join7 } from "path";
1301
+ import { basename, dirname as dirname4, join as join7, resolve as resolve3 } from "path";
1302
1302
  function codexSessionsDir() {
1303
1303
  return join7(process.env.CODEX_HOME || join7(homedir5(), ".codex"), "sessions");
1304
1304
  }
@@ -1364,6 +1364,139 @@ function patchEnvContext(envContext, cwd, currentDate, timezone) {
1364
1364
  }
1365
1365
  }
1366
1366
  }
1367
+ function canonicalFilePath(path) {
1368
+ const absolute = resolve3(path);
1369
+ try {
1370
+ return realpathSync(absolute);
1371
+ } catch {
1372
+ try {
1373
+ return join7(realpathSync(dirname4(absolute)), basename(absolute));
1374
+ } catch {
1375
+ return absolute;
1376
+ }
1377
+ }
1378
+ }
1379
+ function changedPatchLines(value) {
1380
+ const removed = [];
1381
+ const added = [];
1382
+ const diffPreview = [];
1383
+ let oldLine = 0;
1384
+ let newLine = 0;
1385
+ let sawHunk = false;
1386
+ for (const line of value.split(`
1387
+ `)) {
1388
+ const hunk = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/.exec(line);
1389
+ if (hunk) {
1390
+ if (sawHunk)
1391
+ diffPreview.push("\u2026");
1392
+ sawHunk = true;
1393
+ oldLine = Number.parseInt(hunk[1] ?? "0", 10);
1394
+ newLine = Number.parseInt(hunk[2] ?? "0", 10);
1395
+ continue;
1396
+ }
1397
+ if (!sawHunk && (line.startsWith("--- ") || line.startsWith("+++ ")))
1398
+ continue;
1399
+ if (line.startsWith("\\ No newline"))
1400
+ continue;
1401
+ if (line.startsWith("-")) {
1402
+ removed.push(line.slice(1));
1403
+ diffPreview.push(`${oldLine} -${line.slice(1)}`);
1404
+ oldLine += 1;
1405
+ continue;
1406
+ }
1407
+ if (line.startsWith("+")) {
1408
+ added.push(line.slice(1));
1409
+ diffPreview.push(`${newLine} +${line.slice(1)}`);
1410
+ newLine += 1;
1411
+ continue;
1412
+ }
1413
+ if (line.startsWith(" ")) {
1414
+ diffPreview.push(`${newLine} ${line.slice(1)}`);
1415
+ oldLine += 1;
1416
+ newLine += 1;
1417
+ }
1418
+ }
1419
+ return {
1420
+ ...removed.length > 0 ? { before: removed.join(`
1421
+ `) } : {},
1422
+ ...added.length > 0 ? { after: added.join(`
1423
+ `) } : {},
1424
+ ...diffPreview.length > 0 ? { diffPreview: diffPreview.join(`
1425
+ `) } : {}
1426
+ };
1427
+ }
1428
+ function extractLatestCodexPatchPreview(jsonl, expectedPaths, consumedCallIds = new Set, expectedCallId) {
1429
+ const expected = expectedPaths.map(canonicalFilePath);
1430
+ const lines = jsonl.trimEnd().split(`
1431
+ `);
1432
+ for (let index = lines.length - 1;index >= 0; index -= 1) {
1433
+ try {
1434
+ const entry = JSON.parse(lines[index] ?? "");
1435
+ if (entry.type !== "event_msg" || entry.payload?.type !== "patch_apply_end")
1436
+ continue;
1437
+ const callId = entry.payload.call_id;
1438
+ const rawChanges = entry.payload.changes;
1439
+ if (!callId || consumedCallIds.has(callId) || expectedCallId !== undefined && callId !== expectedCallId || !rawChanges) {
1440
+ continue;
1441
+ }
1442
+ const entries = Object.entries(rawChanges);
1443
+ const matched = expected.map((path) => entries.find(([candidate]) => canonicalFilePath(candidate) === path));
1444
+ const complete = matched.filter((change) => change !== undefined);
1445
+ if (complete.length !== expected.length)
1446
+ continue;
1447
+ return {
1448
+ callId,
1449
+ changes: complete.map(([path, change]) => {
1450
+ if (typeof change.unified_diff === "string") {
1451
+ return { path, ...changedPatchLines(change.unified_diff) };
1452
+ }
1453
+ const content = typeof change.content === "string" ? change.content.replace(/\n$/, "") : undefined;
1454
+ return {
1455
+ path,
1456
+ ...change.type === "delete" && content !== undefined ? { before: content } : {},
1457
+ ...change.type === "add" && content !== undefined ? { after: content } : {}
1458
+ };
1459
+ })
1460
+ };
1461
+ } catch {}
1462
+ }
1463
+ return;
1464
+ }
1465
+ function extractCodexPatchCallIds(jsonl) {
1466
+ const callIds = [];
1467
+ for (const line of jsonl.trimEnd().split(`
1468
+ `)) {
1469
+ try {
1470
+ const entry = JSON.parse(line);
1471
+ if (entry.type === "event_msg" && entry.payload?.type === "patch_apply_end" && typeof entry.payload.call_id === "string") {
1472
+ callIds.push(entry.payload.call_id);
1473
+ }
1474
+ } catch {}
1475
+ }
1476
+ return callIds;
1477
+ }
1478
+ function readCodexPatchCallIds(threadId) {
1479
+ const path = latestCodexRolloutPath(threadId);
1480
+ if (!path)
1481
+ return [];
1482
+ try {
1483
+ return extractCodexPatchCallIds(readFileSync4(path, "utf8"));
1484
+ } catch (error) {
1485
+ logger.debug({ error, threadId }, "codex patch ids: rollout read failed");
1486
+ return [];
1487
+ }
1488
+ }
1489
+ function readLatestCodexPatchPreview(threadId, expectedPaths, consumedCallIds = new Set, expectedCallId) {
1490
+ const path = latestCodexRolloutPath(threadId);
1491
+ if (!path)
1492
+ return;
1493
+ try {
1494
+ return extractLatestCodexPatchPreview(readFileSync4(path, "utf8"), expectedPaths, consumedCallIds, expectedCallId);
1495
+ } catch (error) {
1496
+ logger.debug({ error, threadId }, "codex patch preview: rollout read failed");
1497
+ return;
1498
+ }
1499
+ }
1367
1500
  function extractLatestCodexContextUsage(jsonl) {
1368
1501
  const lines = jsonl.trimEnd().split(`
1369
1502
  `);
@@ -1382,28 +1515,11 @@ function extractLatestCodexContextUsage(jsonl) {
1382
1515
  return;
1383
1516
  }
1384
1517
  function readLatestCodexContextUsage(threadId) {
1385
- const sessionsDir = codexSessionsDir();
1386
- const candidates = [];
1387
- const buckets = candidateDateBuckets(threadId);
1518
+ const path = latestCodexRolloutPath(threadId);
1519
+ if (!path)
1520
+ return;
1388
1521
  try {
1389
- if (buckets) {
1390
- for (const bucket of buckets) {
1391
- const dir = join7(sessionsDir, bucket);
1392
- if (!existsSync5(dir))
1393
- continue;
1394
- const glob = new Bun.Glob(`rollout-*-${threadId}.jsonl`);
1395
- for (const rel of glob.scanSync({ cwd: dir, onlyFiles: true })) {
1396
- candidates.push(join7(dir, rel));
1397
- }
1398
- }
1399
- } else {
1400
- const glob = new Bun.Glob(`**/rollout-*-${threadId}.jsonl`);
1401
- for (const rel of glob.scanSync({ cwd: sessionsDir, onlyFiles: true })) {
1402
- candidates.push(join7(sessionsDir, rel));
1403
- }
1404
- }
1405
- const path = candidates.sort((a, b) => statSync2(b).mtimeMs - statSync2(a).mtimeMs)[0];
1406
- return path ? extractLatestCodexContextUsage(readFileSync4(path, "utf8")) : undefined;
1522
+ return extractLatestCodexContextUsage(readFileSync4(path, "utf8"));
1407
1523
  } catch (error) {
1408
1524
  logger.debug({ error, threadId }, "codex context usage: rollout read failed");
1409
1525
  return;
@@ -1671,7 +1787,7 @@ import {
1671
1787
  unlinkSync as unlinkSync5,
1672
1788
  writeFileSync as writeFileSync4
1673
1789
  } from "fs";
1674
- import { dirname as dirname5, join as join9 } from "path";
1790
+ import { dirname as dirname6, join as join9 } from "path";
1675
1791
 
1676
1792
  // ../../packages/core/src/security/sanitize.ts
1677
1793
  function sanitizeTopicName(name, lowercase = false) {
@@ -1688,7 +1804,7 @@ function sanitizeFileName(name) {
1688
1804
  // ../../packages/core/src/storage/storage-host.ts
1689
1805
  import { mkdirSync as mkdirSync6 } from "fs";
1690
1806
  import { homedir as homedir6 } from "os";
1691
- import { dirname as dirname4, join as join8, resolve as resolve3 } from "path";
1807
+ import { dirname as dirname5, join as join8, resolve as resolve4 } from "path";
1692
1808
  var configuredHost = {};
1693
1809
  var fallbackDatabase = null;
1694
1810
  var fallbackDatabasePath = null;
@@ -1697,7 +1813,7 @@ var initializedSchemas = new WeakMap;
1697
1813
  var initializingDatabases = new WeakSet;
1698
1814
  function envPath(name, fallback) {
1699
1815
  const value = process.env[name]?.trim();
1700
- return resolve3(value || fallback);
1816
+ return resolve4(value || fallback);
1701
1817
  }
1702
1818
  function defaultStateDir() {
1703
1819
  return envPath("NEGOTIUM_STATE_DIR", join8(homedir6(), ".negotium"));
@@ -1726,7 +1842,7 @@ function defaultDatabase() {
1726
1842
  return fallbackDatabase;
1727
1843
  if (fallbackDatabase)
1728
1844
  fallbackDatabase.close();
1729
- mkdirSync6(dirname4(path), { recursive: true });
1845
+ mkdirSync6(dirname5(path), { recursive: true });
1730
1846
  fallbackDatabase = new Database(path, { create: true });
1731
1847
  fallbackDatabasePath = path;
1732
1848
  initializeDatabase(fallbackDatabase);
@@ -1819,7 +1935,7 @@ function appendConversationEventStrict(userId, topicName, agent, event) {
1819
1935
  agent,
1820
1936
  event
1821
1937
  };
1822
- mkdirSync7(dirname5(path), { recursive: true });
1938
+ mkdirSync7(dirname6(path), { recursive: true });
1823
1939
  appendJsonlLine(path, JSON.stringify(entry));
1824
1940
  }
1825
1941
  function readConversation(userId, topicName) {
@@ -1981,7 +2097,7 @@ function cleanupAgentFork(handle) {
1981
2097
  }
1982
2098
  // ../../packages/core/src/agents/archiver.ts
1983
2099
  import { randomUUID as randomUUID5 } from "crypto";
1984
- import { existsSync as existsSync13, readdirSync as readdirSync2, readFileSync as readFileSync10, statSync as statSync4 } from "fs";
2100
+ import { existsSync as existsSync13, readdirSync as readdirSync2, readFileSync as readFileSync11, statSync as statSync5 } from "fs";
1985
2101
  import { join as join15 } from "path";
1986
2102
 
1987
2103
  // ../../packages/core/src/agents/index.ts
@@ -2014,8 +2130,8 @@ function deepMapStrings(value, fn) {
2014
2130
  import { AsyncLocalStorage } from "async_hooks";
2015
2131
 
2016
2132
  // ../../packages/core/src/security/sensitive-path.ts
2017
- import { realpathSync } from "fs";
2018
- import { resolve as resolve4 } from "path";
2133
+ import { realpathSync as realpathSync2 } from "fs";
2134
+ import { resolve as resolve5 } from "path";
2019
2135
  var SENSITIVE_PATH_PATTERNS = [
2020
2136
  /\/\.env(\.|$)/i,
2021
2137
  /\/\.ssh\//i,
@@ -2032,11 +2148,11 @@ var SENSITIVE_PATH_PATTERNS = [
2032
2148
  /\/sessions\.db(-wal|-shm|-journal)?$/i
2033
2149
  ];
2034
2150
  function isSensitivePath(filePath) {
2035
- const normalized = resolve4(filePath);
2151
+ const normalized = resolve5(filePath);
2036
2152
  if (SENSITIVE_PATH_PATTERNS.some((p) => p.test(normalized)))
2037
2153
  return true;
2038
2154
  try {
2039
- const real = realpathSync(normalized);
2155
+ const real = realpathSync2(normalized);
2040
2156
  if (real !== normalized)
2041
2157
  return SENSITIVE_PATH_PATTERNS.some((p) => p.test(real));
2042
2158
  } catch {}
@@ -2340,12 +2456,12 @@ function createBackgroundBashManager(options = {}) {
2340
2456
  usedPorts.clear();
2341
2457
  knownContexts.clear();
2342
2458
  const deadline = now() + 3000;
2343
- await Promise.all(entries.map((instance) => new Promise((resolve5) => {
2459
+ await Promise.all(entries.map((instance) => new Promise((resolve6) => {
2344
2460
  if (instance.process.exitCode !== null || instance.process.killed)
2345
- return resolve5();
2346
- instance.process.once("exit", resolve5);
2347
- instance.process.once("error", resolve5);
2348
- const timer = setTimeout(resolve5, Math.max(0, deadline - now()));
2461
+ return resolve6();
2462
+ instance.process.once("exit", resolve6);
2463
+ instance.process.once("error", resolve6);
2464
+ const timer = setTimeout(resolve6, Math.max(0, deadline - now()));
2349
2465
  timer.unref?.();
2350
2466
  })));
2351
2467
  }
@@ -2606,11 +2722,10 @@ var MCP_CATALOG = {
2606
2722
  },
2607
2723
  "background-bash": {
2608
2724
  ...commonRuntimeMcpPolicy("background-bash"),
2609
- build({ agent, bgBashPort, userId, topicId, session }) {
2610
- const topic = topicId ?? session;
2611
- if (bgBashPort === undefined || !topic)
2725
+ build({ agent, bgBashPort, userId, topicId }) {
2726
+ if (bgBashPort === undefined || !topicId)
2612
2727
  return null;
2613
- return backgroundBashTransport(agent, bgBashPort, userId, topic);
2728
+ return backgroundBashTransport(agent, bgBashPort, userId, topicId);
2614
2729
  }
2615
2730
  },
2616
2731
  "agent-health": {
@@ -3365,7 +3480,8 @@ async function* claudeProvider(opts) {
3365
3480
  yield {
3366
3481
  type: "tool_result",
3367
3482
  toolUseId: trBlock.tool_use_id || "",
3368
- content: trContent
3483
+ content: trContent,
3484
+ ...trBlock.is_error ? { isError: true } : {}
3369
3485
  };
3370
3486
  }
3371
3487
  }
@@ -3379,7 +3495,9 @@ async function* claudeProvider(opts) {
3379
3495
  }
3380
3496
 
3381
3497
  // ../../packages/core/src/agents/codex-provider.ts
3382
- import { existsSync as existsSync10 } from "fs";
3498
+ import { execFileSync as execFileSync5 } from "child_process";
3499
+ import { existsSync as existsSync10, readFileSync as readFileSync8, realpathSync as realpathSync3, statSync as statSync3 } from "fs";
3500
+ import { isAbsolute, relative, resolve as resolve6 } from "path";
3383
3501
  import { Codex } from "@openai/codex-sdk";
3384
3502
 
3385
3503
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
@@ -3398,10 +3516,10 @@ import {
3398
3516
  } from "fs";
3399
3517
  import { createRequire } from "module";
3400
3518
  import { tmpdir } from "os";
3401
- import { dirname as dirname6, join as join11 } from "path";
3519
+ import { dirname as dirname7, join as join11 } from "path";
3402
3520
 
3403
3521
  // ../../packages/core/src/version.ts
3404
- var NEGOTIUM_VERSION = "0.1.29";
3522
+ var NEGOTIUM_VERSION = "0.1.31";
3405
3523
 
3406
3524
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
3407
3525
  var moduleRequire = createRequire(import.meta.url);
@@ -3420,7 +3538,7 @@ var SAFE_BUNDLED_CODEX_VERSION = BUNDLED_CODEX_VERSION.replace(/[^a-zA-Z0-9._-]/
3420
3538
  var NEGOTIUM_MODEL_CACHE = `negotium-models-cache-${SAFE_BUNDLED_CODEX_VERSION}.json`;
3421
3539
  var NEGOTIUM_MODEL_CATALOG = `negotium-model-catalog-${SAFE_BUNDLED_CODEX_VERSION}.json`;
3422
3540
  function codexCliScriptPath() {
3423
- return join11(dirname6(bundledCodexPackagePath), "bin", "codex.js");
3541
+ return join11(dirname7(bundledCodexPackagePath), "bin", "codex.js");
3424
3542
  }
3425
3543
  function parseCodexModelCache(contents, sourcePath) {
3426
3544
  let parsed;
@@ -3461,14 +3579,14 @@ function writePrivateFileAtomic(path, contents) {
3461
3579
  }
3462
3580
  }
3463
3581
  function bundledCodexModelCachePath(authFilePath) {
3464
- return join11(dirname6(authFilePath), NEGOTIUM_MODEL_CACHE);
3582
+ return join11(dirname7(authFilePath), NEGOTIUM_MODEL_CACHE);
3465
3583
  }
3466
3584
  async function bootstrapCodexModelCache(codexHome, cachePath) {
3467
3585
  const child = spawn3(process.execPath, [codexCliScriptPath(), "app-server", "--stdio"], {
3468
3586
  env: { ...process.env, CODEX_HOME: codexHome },
3469
3587
  stdio: ["pipe", "pipe", "pipe"]
3470
3588
  });
3471
- await new Promise((resolve5, reject) => {
3589
+ await new Promise((resolve6, reject) => {
3472
3590
  let settled = false;
3473
3591
  let stdoutBuffer = "";
3474
3592
  let stderr = "";
@@ -3487,7 +3605,7 @@ async function bootstrapCodexModelCache(codexHome, cachePath) {
3487
3605
  else if (!existsSync9(cachePath))
3488
3606
  reject(new Error("Codex did not create its model cache"));
3489
3607
  else
3490
- resolve5();
3608
+ resolve6();
3491
3609
  };
3492
3610
  const send = (message) => {
3493
3611
  child.stdin.write(`${JSON.stringify(message)}
@@ -3546,7 +3664,7 @@ async function bootstrapCodexModelCache(codexHome, cachePath) {
3546
3664
  });
3547
3665
  }
3548
3666
  async function bootstrapIsolatedCodexModelCache(authFilePath, bootstrap) {
3549
- const sourceHome = dirname6(authFilePath);
3667
+ const sourceHome = dirname7(authFilePath);
3550
3668
  const isolatedHome = mkdtempSync(join11(tmpdir(), "negotium-codex-models-"));
3551
3669
  const isolatedCachePath = join11(isolatedHome, "models_cache.json");
3552
3670
  try {
@@ -3566,7 +3684,7 @@ async function bootstrapIsolatedCodexModelCache(authFilePath, bootstrap) {
3566
3684
  }
3567
3685
  }
3568
3686
  async function ensureCodexModelCache(authFilePath, bootstrap = bootstrapCodexModelCache) {
3569
- const codexHome = dirname6(authFilePath);
3687
+ const codexHome = dirname7(authFilePath);
3570
3688
  const configuredCachePath = process.env.NEGOTIUM_CODEX_MODELS_CACHE_FILE;
3571
3689
  if (configuredCachePath) {
3572
3690
  if (!existsSync9(configuredCachePath)) {
@@ -3595,7 +3713,7 @@ async function ensureCodexModelCache(authFilePath, bootstrap = bootstrapCodexMod
3595
3713
  return bundledCachePath;
3596
3714
  }
3597
3715
  function writeCodexCatalogWithNativeMultiAgentDisabled(authFilePath, sourcePath) {
3598
- const codexHome = dirname6(authFilePath);
3716
+ const codexHome = dirname7(authFilePath);
3599
3717
  const outputPath = join11(codexHome, NEGOTIUM_MODEL_CATALOG);
3600
3718
  const parsed = readCodexModelCache(sourcePath).parsed;
3601
3719
  const models = parsed.models.map((model, index) => {
@@ -3610,6 +3728,52 @@ function writeCodexCatalogWithNativeMultiAgentDisabled(authFilePath, sourcePath)
3610
3728
  return outputPath;
3611
3729
  }
3612
3730
 
3731
+ // ../../packages/core/src/agents/tool-format.ts
3732
+ import { diffLines as computeLineDiff } from "diff";
3733
+ function changedDiffLines(value) {
3734
+ const lines = value.split(`
3735
+ `);
3736
+ if (lines.at(-1) === "")
3737
+ lines.pop();
3738
+ return lines;
3739
+ }
3740
+ function buildNumberedDiffSummary(before, after, startLine = 1) {
3741
+ const removed = [];
3742
+ const added = [];
3743
+ const preview = [];
3744
+ let oldLine = startLine;
3745
+ let newLine = startLine;
3746
+ for (const part of computeLineDiff(before, after)) {
3747
+ const lines = changedDiffLines(part.value);
3748
+ if (part.removed) {
3749
+ for (const line of lines) {
3750
+ removed.push(line);
3751
+ preview.push(`${oldLine} -${line}`);
3752
+ oldLine += 1;
3753
+ }
3754
+ continue;
3755
+ }
3756
+ if (part.added) {
3757
+ for (const line of lines) {
3758
+ added.push(line);
3759
+ preview.push(`${newLine} +${line}`);
3760
+ newLine += 1;
3761
+ }
3762
+ continue;
3763
+ }
3764
+ oldLine += lines.length;
3765
+ newLine += lines.length;
3766
+ }
3767
+ return {
3768
+ ...removed.length > 0 ? { before: removed.join(`
3769
+ `) } : {},
3770
+ ...added.length > 0 ? { after: added.join(`
3771
+ `) } : {},
3772
+ ...preview.length > 0 ? { diffPreview: preview.join(`
3773
+ `) } : {}
3774
+ };
3775
+ }
3776
+
3613
3777
  // ../../packages/core/src/agents/codex-provider.ts
3614
3778
  function mapEffort(effort) {
3615
3779
  if (!effort)
@@ -3692,22 +3856,142 @@ function summarizeMcpToolCallResult(item) {
3692
3856
  return text.slice(0, 200);
3693
3857
  return JSON.stringify(item.result.structured_content ?? "").slice(0, 200);
3694
3858
  }
3695
- function fileChangeEvents(item) {
3859
+ var CODEX_DIFF_FILE_LIMIT = 512 * 1024;
3860
+ var CODEX_DIFF_BASELINE_FILE_LIMIT = 200;
3861
+ var CODEX_DIFF_BASELINE_BYTE_LIMIT = 16 * 1024 * 1024;
3862
+ function canonicalPath(path) {
3863
+ try {
3864
+ return realpathSync3(resolve6(path));
3865
+ } catch {
3866
+ return resolve6(path);
3867
+ }
3868
+ }
3869
+ function textFile(path, maxBytes = CODEX_DIFF_FILE_LIMIT) {
3870
+ if (!existsSync10(path))
3871
+ return null;
3872
+ try {
3873
+ const byteLimit = Math.min(CODEX_DIFF_FILE_LIMIT, Math.max(0, maxBytes));
3874
+ if (statSync3(path).size > byteLimit)
3875
+ return;
3876
+ const content = readFileSync8(path);
3877
+ if (content.byteLength > byteLimit || content.includes(0))
3878
+ return;
3879
+ return content.toString("utf8");
3880
+ } catch {
3881
+ return;
3882
+ }
3883
+ }
3884
+
3885
+ class CodexFilePreviewTracker {
3886
+ #cwd;
3887
+ #root;
3888
+ #baseline = new Map;
3889
+ #baselineAvailable = false;
3890
+ constructor(cwd) {
3891
+ this.#cwd = canonicalPath(cwd);
3892
+ const root = this.#git(["rev-parse", "--show-toplevel"], this.#cwd)?.trim();
3893
+ this.#root = root ? canonicalPath(root) : null;
3894
+ if (!this.#root)
3895
+ return;
3896
+ const status = this.#git(["status", "--porcelain=v1", "-z", "--untracked-files=all"]);
3897
+ if (status === undefined)
3898
+ return;
3899
+ this.#baselineAvailable = true;
3900
+ let loadedFiles = 0;
3901
+ let loadedBytes = 0;
3902
+ const records = status.split("\x00");
3903
+ for (let index = 0;index < records.length; index += 1) {
3904
+ const record = records[index];
3905
+ if (!record || record.length < 4)
3906
+ continue;
3907
+ const code = record.slice(0, 2);
3908
+ const path = record.slice(3);
3909
+ const remainingBytes = CODEX_DIFF_BASELINE_BYTE_LIMIT - loadedBytes;
3910
+ const content = loadedFiles < CODEX_DIFF_BASELINE_FILE_LIMIT && remainingBytes > 0 ? textFile(resolve6(this.#root, path), remainingBytes) : undefined;
3911
+ this.#baseline.set(path, content);
3912
+ if (typeof content === "string") {
3913
+ loadedFiles += 1;
3914
+ loadedBytes += Buffer.byteLength(content);
3915
+ }
3916
+ if (/[RC]/.test(code))
3917
+ index += 1;
3918
+ }
3919
+ }
3920
+ preview(path, succeeded) {
3921
+ if (!this.#root || !this.#baselineAvailable || !succeeded)
3922
+ return {};
3923
+ const absolute = canonicalPath(isAbsolute(path) ? path : resolve6(this.#cwd, path));
3924
+ const relativePath = relative(this.#root, absolute);
3925
+ if (!relativePath || relativePath.startsWith("..") || isAbsolute(relativePath))
3926
+ return {};
3927
+ const before = this.#baseline.has(relativePath) ? this.#baseline.get(relativePath) : this.#headFile(relativePath);
3928
+ const after = textFile(absolute);
3929
+ this.#baseline.set(relativePath, after);
3930
+ if (before === undefined || after === undefined)
3931
+ return {};
3932
+ const diff = buildNumberedDiffSummary(before ?? "", after ?? "");
3933
+ return {
3934
+ ...diff.before !== undefined ? { before: diff.before } : {},
3935
+ ...diff.after !== undefined ? { after: diff.after } : {},
3936
+ ...diff.diffPreview !== undefined ? { diff_preview: diff.diffPreview } : {}
3937
+ };
3938
+ }
3939
+ #headFile(path) {
3940
+ const content = this.#git(["show", `HEAD:${path}`]);
3941
+ if (content === undefined)
3942
+ return null;
3943
+ return Buffer.byteLength(content) > CODEX_DIFF_FILE_LIMIT || content.includes("\x00") ? undefined : content;
3944
+ }
3945
+ #git(args, cwd = this.#root ?? undefined) {
3946
+ try {
3947
+ return execFileSync5("git", ["-C", cwd ?? ".", ...args], {
3948
+ encoding: "utf8",
3949
+ maxBuffer: CODEX_DIFF_FILE_LIMIT * 4,
3950
+ stdio: ["ignore", "pipe", "ignore"]
3951
+ });
3952
+ } catch {
3953
+ return;
3954
+ }
3955
+ }
3956
+ }
3957
+ async function fileChangeEvents(item, previews, cwd, threadId, consumedPatchCallIds) {
3958
+ const expectedPaths = item.changes.map((change) => isAbsolute(change.path) ? change.path : resolve6(cwd, change.path));
3959
+ let nativePreview;
3960
+ if (threadId && item.status === "completed") {
3961
+ await new Promise((resolveDelay) => setTimeout(resolveDelay, 20));
3962
+ nativePreview = readLatestCodexPatchPreview(threadId, expectedPaths, consumedPatchCallIds);
3963
+ }
3964
+ for (const delayMs of [20, 40, 80, 120]) {
3965
+ if (nativePreview || !threadId || item.status !== "completed")
3966
+ break;
3967
+ await new Promise((resolveDelay) => setTimeout(resolveDelay, delayMs));
3968
+ nativePreview = readLatestCodexPatchPreview(threadId, expectedPaths, consumedPatchCallIds);
3969
+ }
3970
+ if (nativePreview)
3971
+ consumedPatchCallIds.add(nativePreview.callId);
3696
3972
  return item.changes.flatMap((change, index) => {
3697
3973
  const name = change.kind === "add" ? "Write" : change.kind === "delete" ? "Delete" : "Edit";
3698
3974
  const toolUseId = `${item.id}:${index}`;
3699
3975
  const success = item.status === "completed";
3976
+ const nativeChange = nativePreview?.changes[index];
3977
+ const fallbackPreview = previews.preview(change.path, success);
3978
+ const preview = success && nativeChange ? {
3979
+ ...nativeChange.before !== undefined ? { before: nativeChange.before } : {},
3980
+ ...nativeChange.after !== undefined ? { after: nativeChange.after } : {},
3981
+ ...nativeChange.diffPreview !== undefined ? { diff_preview: nativeChange.diffPreview } : {}
3982
+ } : fallbackPreview;
3700
3983
  return [
3701
3984
  {
3702
3985
  type: "tool_use",
3703
3986
  name,
3704
- input: { file_path: change.path, change_kind: change.kind },
3987
+ input: { file_path: change.path, change_kind: change.kind, ...preview },
3705
3988
  toolUseId
3706
3989
  },
3707
3990
  {
3708
3991
  type: "tool_result",
3709
3992
  toolUseId,
3710
- content: success ? `${change.kind} applied: ${change.path}` : `${change.kind} failed: ${change.path}`
3993
+ content: success ? `${change.kind} applied: ${change.path}` : `${change.kind} failed: ${change.path}`,
3994
+ ...success ? {} : { isError: true }
3711
3995
  }
3712
3996
  ];
3713
3997
  });
@@ -3793,6 +4077,8 @@ function prependFirstEvent(iter, firstEvent, alreadyDone) {
3793
4077
  };
3794
4078
  }
3795
4079
  async function* codexProvider(opts) {
4080
+ const filePreviews = new CodexFilePreviewTracker(opts.cwd);
4081
+ const consumedPatchCallIds = new Set(opts.sessionId ? readCodexPatchCallIds(opts.sessionId) : []);
3796
4082
  const codexAuthPath = hostedCodexAuthFilePath();
3797
4083
  if (!existsSync10(codexAuthPath)) {
3798
4084
  yield {
@@ -3927,10 +4213,11 @@ async function* codexProvider(opts) {
3927
4213
  if (!item)
3928
4214
  break;
3929
4215
  if (item.type === "command_execution") {
4216
+ const command = String(item.command ?? "");
3930
4217
  yield {
3931
4218
  type: "tool_use",
3932
4219
  name: "Bash",
3933
- input: { command: String(item.command ?? "") },
4220
+ input: { command },
3934
4221
  toolUseId: String(item.id ?? "")
3935
4222
  };
3936
4223
  } else if (item.type === "mcp_tool_call") {
@@ -3976,19 +4263,24 @@ async function* codexProvider(opts) {
3976
4263
  if (reasoning)
3977
4264
  yield { type: "reasoning", content: reasoning };
3978
4265
  } else if (item.type === "mcp_tool_call") {
4266
+ const isError = item.status === "failed" || Boolean(item.error);
3979
4267
  yield {
3980
4268
  type: "tool_result",
3981
4269
  toolUseId: String(item.id ?? ""),
3982
- content: summarizeMcpToolCallResult(item)
4270
+ content: summarizeMcpToolCallResult(item),
4271
+ ...isError ? { isError: true } : {}
3983
4272
  };
3984
4273
  } else if (item.type === "command_execution") {
4274
+ const isError = item.status === "failed" || typeof item.exit_code === "number" && item.exit_code !== 0;
3985
4275
  yield {
3986
4276
  type: "tool_result",
3987
4277
  toolUseId: String(item.id ?? ""),
3988
- content: String(item.aggregated_output ?? "").slice(0, 200)
4278
+ content: String(item.aggregated_output ?? "").slice(0, 200),
4279
+ ...isError ? { isError: true } : {}
3989
4280
  };
3990
4281
  } else if (item.type === "file_change") {
3991
- for (const fileEvent of fileChangeEvents(item)) {
4282
+ const fileEvents = await fileChangeEvents(item, filePreviews, opts.cwd, currentSessionId, consumedPatchCallIds);
4283
+ for (const fileEvent of fileEvents) {
3992
4284
  yield fileEvent;
3993
4285
  }
3994
4286
  } else if (item.type === "error") {
@@ -4184,8 +4476,8 @@ function maestroProvider(opts) {
4184
4476
  }
4185
4477
 
4186
4478
  // ../../packages/core/src/storage/tasks.ts
4187
- import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync8, renameSync as renameSync5, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
4188
- import { dirname as dirname7, join as join12 } from "path";
4479
+ import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync9, renameSync as renameSync5, statSync as statSync4, writeFileSync as writeFileSync6 } from "fs";
4480
+ import { dirname as dirname8, join as join12 } from "path";
4189
4481
  function safeUserIdComponent2(userId) {
4190
4482
  const str = String(userId);
4191
4483
  if (!str || /[/\\]|\.\./.test(str)) {
@@ -4211,7 +4503,7 @@ function readTasks(userId, scopeKey) {
4211
4503
  if (!existsSync11(path))
4212
4504
  return [];
4213
4505
  try {
4214
- const parsed = JSON.parse(readFileSync8(path, "utf-8"));
4506
+ const parsed = JSON.parse(readFileSync9(path, "utf-8"));
4215
4507
  return Array.isArray(parsed?.tasks) ? parsed.tasks : [];
4216
4508
  } catch (e) {
4217
4509
  logger.warn({ err: e, path }, "tasks: failed to read task store");
@@ -4220,7 +4512,7 @@ function readTasks(userId, scopeKey) {
4220
4512
  }
4221
4513
  function taskFileMtimeNs(userId, scopeKey) {
4222
4514
  try {
4223
- return statSync3(getTaskFilePath(userId, scopeKey), { bigint: true }).mtimeNs;
4515
+ return statSync4(getTaskFilePath(userId, scopeKey), { bigint: true }).mtimeNs;
4224
4516
  } catch {
4225
4517
  return null;
4226
4518
  }
@@ -4541,9 +4833,15 @@ class SqliteRuntimeBus {
4541
4833
  toolUseId: id
4542
4834
  });
4543
4835
  }
4544
- broadcastToolOutput(topicId, queryId, toolUseId, content) {
4836
+ broadcastToolOutput(topicId, queryId, toolUseId, content, isError) {
4545
4837
  const id = toolUseId.trim() || `${queryId}:tool`;
4546
- this.broadcastAiStatus(topicId, { kind: "tool_output", queryId, toolUseId: id, content });
4838
+ this.broadcastAiStatus(topicId, {
4839
+ kind: "tool_output",
4840
+ queryId,
4841
+ toolUseId: id,
4842
+ content,
4843
+ ...isError ? { isError: true } : {}
4844
+ });
4547
4845
  }
4548
4846
  broadcastToolStatus(topicId, queryId, kind, content, meta) {
4549
4847
  this.broadcastAiStatus(topicId, {
@@ -4585,8 +4883,8 @@ var WsHub = {
4585
4883
  };
4586
4884
 
4587
4885
  // ../../packages/core/src/prompts/builders.ts
4588
- import { readFileSync as readFileSync9 } from "fs";
4589
- import { resolve as resolve5 } from "path";
4886
+ import { readFileSync as readFileSync10 } from "fs";
4887
+ import { resolve as resolve7 } from "path";
4590
4888
 
4591
4889
  // ../../packages/core/src/agents/model-catalog.ts
4592
4890
  var CODEX_PRO_20X_COST = "ChatGPT Pro 20x subscription: $200/month";
@@ -4687,10 +4985,10 @@ var SELECTABLE_MODELS = [
4687
4985
  ];
4688
4986
 
4689
4987
  // ../../packages/core/src/prompts/builders.ts
4690
- var PROMPTS_DIR = resolve5(PROJECT_ROOT, "src/prompts");
4691
- var SESSIONS_DIR = resolve5(PROMPTS_DIR, "sessions");
4988
+ var PROMPTS_DIR = resolve7(PROJECT_ROOT, "src/prompts");
4989
+ var SESSIONS_DIR = resolve7(PROMPTS_DIR, "sessions");
4692
4990
  function loadAgentPrompt(filename) {
4693
- const raw = readFileSync9(resolve5(AGENTS_PROMPTS_DIR, filename), "utf-8");
4991
+ const raw = readFileSync10(resolve7(AGENTS_PROMPTS_DIR, filename), "utf-8");
4694
4992
  const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
4695
4993
  if (!match)
4696
4994
  throw new Error(`Agent prompt ${filename} is missing frontmatter`);
@@ -4871,7 +5169,7 @@ function setTopicBrief(topicId, fields) {
4871
5169
  }
4872
5170
 
4873
5171
  // ../../packages/core/src/storage/wiki.ts
4874
- import { basename, dirname as dirname8, join as join14 } from "path";
5172
+ import { basename as basename2, dirname as dirname9, join as join14 } from "path";
4875
5173
  function getSharedWikiDir(workspaceDir = resolveStorageWorkspaceDir()) {
4876
5174
  return workspaceDir === resolveStorageWorkspaceDir() ? resolveStorageSharedWikiDir() : join14(workspaceDir, "wiki");
4877
5175
  }
@@ -4884,7 +5182,11 @@ function isEphemeralWikiTopicId(topicId) {
4884
5182
  return topicId?.startsWith("__") ?? false;
4885
5183
  }
4886
5184
  function wikiSummaryStorageSlug(rawTopic, topicId) {
4887
- return topicId && !isEphemeralWikiTopicId(topicId) ? wikiSummarySlug(topicId) : wikiSummarySlug(rawTopic);
5185
+ const titleSlug = wikiSummarySlug(rawTopic);
5186
+ if (!topicId || isEphemeralWikiTopicId(topicId))
5187
+ return titleSlug;
5188
+ const idSlug = wikiSummarySlug(topicId);
5189
+ return titleSlug === idSlug ? idSlug : `${titleSlug}--${idSlug}`;
4888
5190
  }
4889
5191
  function wikiSummaryFilename(date, rawTopic, topicId) {
4890
5192
  return `${date}-${wikiSummaryStorageSlug(rawTopic, topicId)}.md`;
@@ -5595,7 +5897,7 @@ function findSummaryFile(topicTitle, date, sinceMs, topicId) {
5595
5897
  continue;
5596
5898
  const p = join15(dir, f);
5597
5899
  try {
5598
- const m = statSync4(p).mtimeMs;
5900
+ const m = statSync5(p).mtimeMs;
5599
5901
  if (m >= sinceMs && (!best || m > best.mtime))
5600
5902
  best = { path: p, mtime: m };
5601
5903
  } catch {}
@@ -5607,7 +5909,7 @@ function finalizeGeneralMemory(userId, topicTitle, messageCount, startMs, ok, to
5607
5909
  const date = new Date().toISOString().slice(0, 10);
5608
5910
  try {
5609
5911
  const summaryPath = ok ? findSummaryFile(topicTitle, date, startMs, topicId) : null;
5610
- const summaryMd = summaryPath ? readFileSync10(summaryPath, "utf-8") : "";
5912
+ const summaryMd = summaryPath ? readFileSync11(summaryPath, "utf-8") : "";
5611
5913
  const oneLine = summaryMd && distillOneLine(summaryMd) || `${messageCount}\uAC1C \uBA54\uC2DC\uC9C0 \uC544\uCE74\uC774\uBE0C`;
5612
5914
  const prev = getTopicBrief(generalTopicId);
5613
5915
  const prevEntries = (prev?.briefMd ?? "").split(`
@@ -6567,4 +6869,4 @@ export {
6567
6869
  MIN_MEMORY_ARCHIVE_EXCHANGES
6568
6870
  };
6569
6871
 
6570
- //# debugId=A260F0394930433E64756E2164756E21
6872
+ //# debugId=F639F58BC810D1BD64756E2164756E21