negotium 0.1.29 → 0.1.30

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 (36) hide show
  1. package/dist/agent-helpers.js +366 -50
  2. package/dist/agent-helpers.js.map +10 -9
  3. package/dist/background-bash.js.map +1 -1
  4. package/dist/{chunk-yk55n9dk.js → chunk-en2s6xnh.js} +3 -3
  5. package/dist/{chunk-yk55n9dk.js.map → chunk-en2s6xnh.js.map} +4 -4
  6. package/dist/cron.js +4903 -4479
  7. package/dist/cron.js.map +11 -11
  8. package/dist/hosted-agent.js +328 -18
  9. package/dist/hosted-agent.js.map +9 -8
  10. package/dist/main.js +5711 -5039
  11. package/dist/main.js.map +16 -16
  12. package/dist/mcp-factories.js.map +1 -1
  13. package/dist/prompts.js.map +1 -1
  14. package/dist/registry.js +1 -1
  15. package/dist/rollout.js +1 -1
  16. package/dist/runtime/src/agents/claude-provider.ts +2 -0
  17. package/dist/runtime/src/agents/codex-provider.ts +192 -5
  18. package/dist/runtime/src/agents/rollout/codex.ts +198 -2
  19. package/dist/runtime/src/agents/rollout/index.ts +6 -0
  20. package/dist/runtime/src/agents/tool-format.ts +228 -15
  21. package/dist/runtime/src/bus.ts +21 -3
  22. package/dist/runtime/src/runtime/turn-runner.ts +11 -5
  23. package/dist/runtime/src/types/api.ts +2 -0
  24. package/dist/runtime/src/types.ts +2 -0
  25. package/dist/runtime/src/version.ts +1 -1
  26. package/dist/runtime-helpers.js.map +1 -1
  27. package/dist/storage.js.map +1 -1
  28. package/dist/types/packages/core/src/agents/rollout/codex.d.ts +24 -0
  29. package/dist/types/packages/core/src/agents/rollout/index.d.ts +2 -2
  30. package/dist/types/packages/core/src/agents/tool-format.d.ts +16 -9
  31. package/dist/types/packages/core/src/bus.d.ts +2 -2
  32. package/dist/types/packages/core/src/types/api.d.ts +2 -0
  33. package/dist/types/packages/core/src/types.d.ts +2 -0
  34. package/dist/types/packages/core/src/version.d.ts +1 -1
  35. package/dist/vault.js.map +1 -1
  36. 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
  `);
@@ -1671,7 +1804,7 @@ import {
1671
1804
  unlinkSync as unlinkSync5,
1672
1805
  writeFileSync as writeFileSync4
1673
1806
  } from "fs";
1674
- import { dirname as dirname5, join as join9 } from "path";
1807
+ import { dirname as dirname6, join as join9 } from "path";
1675
1808
 
1676
1809
  // ../../packages/core/src/security/sanitize.ts
1677
1810
  function sanitizeTopicName(name, lowercase = false) {
@@ -1688,7 +1821,7 @@ function sanitizeFileName(name) {
1688
1821
  // ../../packages/core/src/storage/storage-host.ts
1689
1822
  import { mkdirSync as mkdirSync6 } from "fs";
1690
1823
  import { homedir as homedir6 } from "os";
1691
- import { dirname as dirname4, join as join8, resolve as resolve3 } from "path";
1824
+ import { dirname as dirname5, join as join8, resolve as resolve4 } from "path";
1692
1825
  var configuredHost = {};
1693
1826
  var fallbackDatabase = null;
1694
1827
  var fallbackDatabasePath = null;
@@ -1697,7 +1830,7 @@ var initializedSchemas = new WeakMap;
1697
1830
  var initializingDatabases = new WeakSet;
1698
1831
  function envPath(name, fallback) {
1699
1832
  const value = process.env[name]?.trim();
1700
- return resolve3(value || fallback);
1833
+ return resolve4(value || fallback);
1701
1834
  }
1702
1835
  function defaultStateDir() {
1703
1836
  return envPath("NEGOTIUM_STATE_DIR", join8(homedir6(), ".negotium"));
@@ -1726,7 +1859,7 @@ function defaultDatabase() {
1726
1859
  return fallbackDatabase;
1727
1860
  if (fallbackDatabase)
1728
1861
  fallbackDatabase.close();
1729
- mkdirSync6(dirname4(path), { recursive: true });
1862
+ mkdirSync6(dirname5(path), { recursive: true });
1730
1863
  fallbackDatabase = new Database(path, { create: true });
1731
1864
  fallbackDatabasePath = path;
1732
1865
  initializeDatabase(fallbackDatabase);
@@ -1819,7 +1952,7 @@ function appendConversationEventStrict(userId, topicName, agent, event) {
1819
1952
  agent,
1820
1953
  event
1821
1954
  };
1822
- mkdirSync7(dirname5(path), { recursive: true });
1955
+ mkdirSync7(dirname6(path), { recursive: true });
1823
1956
  appendJsonlLine(path, JSON.stringify(entry));
1824
1957
  }
1825
1958
  function readConversation(userId, topicName) {
@@ -1981,7 +2114,7 @@ function cleanupAgentFork(handle) {
1981
2114
  }
1982
2115
  // ../../packages/core/src/agents/archiver.ts
1983
2116
  import { randomUUID as randomUUID5 } from "crypto";
1984
- import { existsSync as existsSync13, readdirSync as readdirSync2, readFileSync as readFileSync10, statSync as statSync4 } from "fs";
2117
+ import { existsSync as existsSync13, readdirSync as readdirSync2, readFileSync as readFileSync11, statSync as statSync5 } from "fs";
1985
2118
  import { join as join15 } from "path";
1986
2119
 
1987
2120
  // ../../packages/core/src/agents/index.ts
@@ -2014,8 +2147,8 @@ function deepMapStrings(value, fn) {
2014
2147
  import { AsyncLocalStorage } from "async_hooks";
2015
2148
 
2016
2149
  // ../../packages/core/src/security/sensitive-path.ts
2017
- import { realpathSync } from "fs";
2018
- import { resolve as resolve4 } from "path";
2150
+ import { realpathSync as realpathSync2 } from "fs";
2151
+ import { resolve as resolve5 } from "path";
2019
2152
  var SENSITIVE_PATH_PATTERNS = [
2020
2153
  /\/\.env(\.|$)/i,
2021
2154
  /\/\.ssh\//i,
@@ -2032,11 +2165,11 @@ var SENSITIVE_PATH_PATTERNS = [
2032
2165
  /\/sessions\.db(-wal|-shm|-journal)?$/i
2033
2166
  ];
2034
2167
  function isSensitivePath(filePath) {
2035
- const normalized = resolve4(filePath);
2168
+ const normalized = resolve5(filePath);
2036
2169
  if (SENSITIVE_PATH_PATTERNS.some((p) => p.test(normalized)))
2037
2170
  return true;
2038
2171
  try {
2039
- const real = realpathSync(normalized);
2172
+ const real = realpathSync2(normalized);
2040
2173
  if (real !== normalized)
2041
2174
  return SENSITIVE_PATH_PATTERNS.some((p) => p.test(real));
2042
2175
  } catch {}
@@ -2340,12 +2473,12 @@ function createBackgroundBashManager(options = {}) {
2340
2473
  usedPorts.clear();
2341
2474
  knownContexts.clear();
2342
2475
  const deadline = now() + 3000;
2343
- await Promise.all(entries.map((instance) => new Promise((resolve5) => {
2476
+ await Promise.all(entries.map((instance) => new Promise((resolve6) => {
2344
2477
  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()));
2478
+ return resolve6();
2479
+ instance.process.once("exit", resolve6);
2480
+ instance.process.once("error", resolve6);
2481
+ const timer = setTimeout(resolve6, Math.max(0, deadline - now()));
2349
2482
  timer.unref?.();
2350
2483
  })));
2351
2484
  }
@@ -3365,7 +3498,8 @@ async function* claudeProvider(opts) {
3365
3498
  yield {
3366
3499
  type: "tool_result",
3367
3500
  toolUseId: trBlock.tool_use_id || "",
3368
- content: trContent
3501
+ content: trContent,
3502
+ ...trBlock.is_error ? { isError: true } : {}
3369
3503
  };
3370
3504
  }
3371
3505
  }
@@ -3379,7 +3513,9 @@ async function* claudeProvider(opts) {
3379
3513
  }
3380
3514
 
3381
3515
  // ../../packages/core/src/agents/codex-provider.ts
3382
- import { existsSync as existsSync10 } from "fs";
3516
+ import { execFileSync as execFileSync5 } from "child_process";
3517
+ import { existsSync as existsSync10, readFileSync as readFileSync8, realpathSync as realpathSync3, statSync as statSync3 } from "fs";
3518
+ import { isAbsolute, relative, resolve as resolve6 } from "path";
3383
3519
  import { Codex } from "@openai/codex-sdk";
3384
3520
 
3385
3521
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
@@ -3398,10 +3534,10 @@ import {
3398
3534
  } from "fs";
3399
3535
  import { createRequire } from "module";
3400
3536
  import { tmpdir } from "os";
3401
- import { dirname as dirname6, join as join11 } from "path";
3537
+ import { dirname as dirname7, join as join11 } from "path";
3402
3538
 
3403
3539
  // ../../packages/core/src/version.ts
3404
- var NEGOTIUM_VERSION = "0.1.29";
3540
+ var NEGOTIUM_VERSION = "0.1.30";
3405
3541
 
3406
3542
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
3407
3543
  var moduleRequire = createRequire(import.meta.url);
@@ -3420,7 +3556,7 @@ var SAFE_BUNDLED_CODEX_VERSION = BUNDLED_CODEX_VERSION.replace(/[^a-zA-Z0-9._-]/
3420
3556
  var NEGOTIUM_MODEL_CACHE = `negotium-models-cache-${SAFE_BUNDLED_CODEX_VERSION}.json`;
3421
3557
  var NEGOTIUM_MODEL_CATALOG = `negotium-model-catalog-${SAFE_BUNDLED_CODEX_VERSION}.json`;
3422
3558
  function codexCliScriptPath() {
3423
- return join11(dirname6(bundledCodexPackagePath), "bin", "codex.js");
3559
+ return join11(dirname7(bundledCodexPackagePath), "bin", "codex.js");
3424
3560
  }
3425
3561
  function parseCodexModelCache(contents, sourcePath) {
3426
3562
  let parsed;
@@ -3461,14 +3597,14 @@ function writePrivateFileAtomic(path, contents) {
3461
3597
  }
3462
3598
  }
3463
3599
  function bundledCodexModelCachePath(authFilePath) {
3464
- return join11(dirname6(authFilePath), NEGOTIUM_MODEL_CACHE);
3600
+ return join11(dirname7(authFilePath), NEGOTIUM_MODEL_CACHE);
3465
3601
  }
3466
3602
  async function bootstrapCodexModelCache(codexHome, cachePath) {
3467
3603
  const child = spawn3(process.execPath, [codexCliScriptPath(), "app-server", "--stdio"], {
3468
3604
  env: { ...process.env, CODEX_HOME: codexHome },
3469
3605
  stdio: ["pipe", "pipe", "pipe"]
3470
3606
  });
3471
- await new Promise((resolve5, reject) => {
3607
+ await new Promise((resolve6, reject) => {
3472
3608
  let settled = false;
3473
3609
  let stdoutBuffer = "";
3474
3610
  let stderr = "";
@@ -3487,7 +3623,7 @@ async function bootstrapCodexModelCache(codexHome, cachePath) {
3487
3623
  else if (!existsSync9(cachePath))
3488
3624
  reject(new Error("Codex did not create its model cache"));
3489
3625
  else
3490
- resolve5();
3626
+ resolve6();
3491
3627
  };
3492
3628
  const send = (message) => {
3493
3629
  child.stdin.write(`${JSON.stringify(message)}
@@ -3546,7 +3682,7 @@ async function bootstrapCodexModelCache(codexHome, cachePath) {
3546
3682
  });
3547
3683
  }
3548
3684
  async function bootstrapIsolatedCodexModelCache(authFilePath, bootstrap) {
3549
- const sourceHome = dirname6(authFilePath);
3685
+ const sourceHome = dirname7(authFilePath);
3550
3686
  const isolatedHome = mkdtempSync(join11(tmpdir(), "negotium-codex-models-"));
3551
3687
  const isolatedCachePath = join11(isolatedHome, "models_cache.json");
3552
3688
  try {
@@ -3566,7 +3702,7 @@ async function bootstrapIsolatedCodexModelCache(authFilePath, bootstrap) {
3566
3702
  }
3567
3703
  }
3568
3704
  async function ensureCodexModelCache(authFilePath, bootstrap = bootstrapCodexModelCache) {
3569
- const codexHome = dirname6(authFilePath);
3705
+ const codexHome = dirname7(authFilePath);
3570
3706
  const configuredCachePath = process.env.NEGOTIUM_CODEX_MODELS_CACHE_FILE;
3571
3707
  if (configuredCachePath) {
3572
3708
  if (!existsSync9(configuredCachePath)) {
@@ -3595,7 +3731,7 @@ async function ensureCodexModelCache(authFilePath, bootstrap = bootstrapCodexMod
3595
3731
  return bundledCachePath;
3596
3732
  }
3597
3733
  function writeCodexCatalogWithNativeMultiAgentDisabled(authFilePath, sourcePath) {
3598
- const codexHome = dirname6(authFilePath);
3734
+ const codexHome = dirname7(authFilePath);
3599
3735
  const outputPath = join11(codexHome, NEGOTIUM_MODEL_CATALOG);
3600
3736
  const parsed = readCodexModelCache(sourcePath).parsed;
3601
3737
  const models = parsed.models.map((model, index) => {
@@ -3610,6 +3746,52 @@ function writeCodexCatalogWithNativeMultiAgentDisabled(authFilePath, sourcePath)
3610
3746
  return outputPath;
3611
3747
  }
3612
3748
 
3749
+ // ../../packages/core/src/agents/tool-format.ts
3750
+ import { diffLines as computeLineDiff } from "diff";
3751
+ function changedDiffLines(value) {
3752
+ const lines = value.split(`
3753
+ `);
3754
+ if (lines.at(-1) === "")
3755
+ lines.pop();
3756
+ return lines;
3757
+ }
3758
+ function buildNumberedDiffSummary(before, after, startLine = 1) {
3759
+ const removed = [];
3760
+ const added = [];
3761
+ const preview = [];
3762
+ let oldLine = startLine;
3763
+ let newLine = startLine;
3764
+ for (const part of computeLineDiff(before, after)) {
3765
+ const lines = changedDiffLines(part.value);
3766
+ if (part.removed) {
3767
+ for (const line of lines) {
3768
+ removed.push(line);
3769
+ preview.push(`${oldLine} -${line}`);
3770
+ oldLine += 1;
3771
+ }
3772
+ continue;
3773
+ }
3774
+ if (part.added) {
3775
+ for (const line of lines) {
3776
+ added.push(line);
3777
+ preview.push(`${newLine} +${line}`);
3778
+ newLine += 1;
3779
+ }
3780
+ continue;
3781
+ }
3782
+ oldLine += lines.length;
3783
+ newLine += lines.length;
3784
+ }
3785
+ return {
3786
+ ...removed.length > 0 ? { before: removed.join(`
3787
+ `) } : {},
3788
+ ...added.length > 0 ? { after: added.join(`
3789
+ `) } : {},
3790
+ ...preview.length > 0 ? { diffPreview: preview.join(`
3791
+ `) } : {}
3792
+ };
3793
+ }
3794
+
3613
3795
  // ../../packages/core/src/agents/codex-provider.ts
3614
3796
  function mapEffort(effort) {
3615
3797
  if (!effort)
@@ -3692,22 +3874,142 @@ function summarizeMcpToolCallResult(item) {
3692
3874
  return text.slice(0, 200);
3693
3875
  return JSON.stringify(item.result.structured_content ?? "").slice(0, 200);
3694
3876
  }
3695
- function fileChangeEvents(item) {
3877
+ var CODEX_DIFF_FILE_LIMIT = 512 * 1024;
3878
+ var CODEX_DIFF_BASELINE_FILE_LIMIT = 200;
3879
+ var CODEX_DIFF_BASELINE_BYTE_LIMIT = 16 * 1024 * 1024;
3880
+ function canonicalPath(path) {
3881
+ try {
3882
+ return realpathSync3(resolve6(path));
3883
+ } catch {
3884
+ return resolve6(path);
3885
+ }
3886
+ }
3887
+ function textFile(path, maxBytes = CODEX_DIFF_FILE_LIMIT) {
3888
+ if (!existsSync10(path))
3889
+ return null;
3890
+ try {
3891
+ const byteLimit = Math.min(CODEX_DIFF_FILE_LIMIT, Math.max(0, maxBytes));
3892
+ if (statSync3(path).size > byteLimit)
3893
+ return;
3894
+ const content = readFileSync8(path);
3895
+ if (content.byteLength > byteLimit || content.includes(0))
3896
+ return;
3897
+ return content.toString("utf8");
3898
+ } catch {
3899
+ return;
3900
+ }
3901
+ }
3902
+
3903
+ class CodexFilePreviewTracker {
3904
+ #cwd;
3905
+ #root;
3906
+ #baseline = new Map;
3907
+ #baselineAvailable = false;
3908
+ constructor(cwd) {
3909
+ this.#cwd = canonicalPath(cwd);
3910
+ const root = this.#git(["rev-parse", "--show-toplevel"], this.#cwd)?.trim();
3911
+ this.#root = root ? canonicalPath(root) : null;
3912
+ if (!this.#root)
3913
+ return;
3914
+ const status = this.#git(["status", "--porcelain=v1", "-z", "--untracked-files=all"]);
3915
+ if (status === undefined)
3916
+ return;
3917
+ this.#baselineAvailable = true;
3918
+ let loadedFiles = 0;
3919
+ let loadedBytes = 0;
3920
+ const records = status.split("\x00");
3921
+ for (let index = 0;index < records.length; index += 1) {
3922
+ const record = records[index];
3923
+ if (!record || record.length < 4)
3924
+ continue;
3925
+ const code = record.slice(0, 2);
3926
+ const path = record.slice(3);
3927
+ const remainingBytes = CODEX_DIFF_BASELINE_BYTE_LIMIT - loadedBytes;
3928
+ const content = loadedFiles < CODEX_DIFF_BASELINE_FILE_LIMIT && remainingBytes > 0 ? textFile(resolve6(this.#root, path), remainingBytes) : undefined;
3929
+ this.#baseline.set(path, content);
3930
+ if (typeof content === "string") {
3931
+ loadedFiles += 1;
3932
+ loadedBytes += Buffer.byteLength(content);
3933
+ }
3934
+ if (/[RC]/.test(code))
3935
+ index += 1;
3936
+ }
3937
+ }
3938
+ preview(path, succeeded) {
3939
+ if (!this.#root || !this.#baselineAvailable || !succeeded)
3940
+ return {};
3941
+ const absolute = canonicalPath(isAbsolute(path) ? path : resolve6(this.#cwd, path));
3942
+ const relativePath = relative(this.#root, absolute);
3943
+ if (!relativePath || relativePath.startsWith("..") || isAbsolute(relativePath))
3944
+ return {};
3945
+ const before = this.#baseline.has(relativePath) ? this.#baseline.get(relativePath) : this.#headFile(relativePath);
3946
+ const after = textFile(absolute);
3947
+ this.#baseline.set(relativePath, after);
3948
+ if (before === undefined || after === undefined)
3949
+ return {};
3950
+ const diff = buildNumberedDiffSummary(before ?? "", after ?? "");
3951
+ return {
3952
+ ...diff.before !== undefined ? { before: diff.before } : {},
3953
+ ...diff.after !== undefined ? { after: diff.after } : {},
3954
+ ...diff.diffPreview !== undefined ? { diff_preview: diff.diffPreview } : {}
3955
+ };
3956
+ }
3957
+ #headFile(path) {
3958
+ const content = this.#git(["show", `HEAD:${path}`]);
3959
+ if (content === undefined)
3960
+ return null;
3961
+ return Buffer.byteLength(content) > CODEX_DIFF_FILE_LIMIT || content.includes("\x00") ? undefined : content;
3962
+ }
3963
+ #git(args, cwd = this.#root ?? undefined) {
3964
+ try {
3965
+ return execFileSync5("git", ["-C", cwd ?? ".", ...args], {
3966
+ encoding: "utf8",
3967
+ maxBuffer: CODEX_DIFF_FILE_LIMIT * 4,
3968
+ stdio: ["ignore", "pipe", "ignore"]
3969
+ });
3970
+ } catch {
3971
+ return;
3972
+ }
3973
+ }
3974
+ }
3975
+ async function fileChangeEvents(item, previews, cwd, threadId, consumedPatchCallIds) {
3976
+ const expectedPaths = item.changes.map((change) => isAbsolute(change.path) ? change.path : resolve6(cwd, change.path));
3977
+ let nativePreview;
3978
+ if (threadId && item.status === "completed") {
3979
+ await new Promise((resolveDelay) => setTimeout(resolveDelay, 20));
3980
+ nativePreview = readLatestCodexPatchPreview(threadId, expectedPaths, consumedPatchCallIds);
3981
+ }
3982
+ for (const delayMs of [20, 40, 80, 120]) {
3983
+ if (nativePreview || !threadId || item.status !== "completed")
3984
+ break;
3985
+ await new Promise((resolveDelay) => setTimeout(resolveDelay, delayMs));
3986
+ nativePreview = readLatestCodexPatchPreview(threadId, expectedPaths, consumedPatchCallIds);
3987
+ }
3988
+ if (nativePreview)
3989
+ consumedPatchCallIds.add(nativePreview.callId);
3696
3990
  return item.changes.flatMap((change, index) => {
3697
3991
  const name = change.kind === "add" ? "Write" : change.kind === "delete" ? "Delete" : "Edit";
3698
3992
  const toolUseId = `${item.id}:${index}`;
3699
3993
  const success = item.status === "completed";
3994
+ const nativeChange = nativePreview?.changes[index];
3995
+ const fallbackPreview = previews.preview(change.path, success);
3996
+ const preview = success && nativeChange ? {
3997
+ ...nativeChange.before !== undefined ? { before: nativeChange.before } : {},
3998
+ ...nativeChange.after !== undefined ? { after: nativeChange.after } : {},
3999
+ ...nativeChange.diffPreview !== undefined ? { diff_preview: nativeChange.diffPreview } : {}
4000
+ } : fallbackPreview;
3700
4001
  return [
3701
4002
  {
3702
4003
  type: "tool_use",
3703
4004
  name,
3704
- input: { file_path: change.path, change_kind: change.kind },
4005
+ input: { file_path: change.path, change_kind: change.kind, ...preview },
3705
4006
  toolUseId
3706
4007
  },
3707
4008
  {
3708
4009
  type: "tool_result",
3709
4010
  toolUseId,
3710
- content: success ? `${change.kind} applied: ${change.path}` : `${change.kind} failed: ${change.path}`
4011
+ content: success ? `${change.kind} applied: ${change.path}` : `${change.kind} failed: ${change.path}`,
4012
+ ...success ? {} : { isError: true }
3711
4013
  }
3712
4014
  ];
3713
4015
  });
@@ -3793,6 +4095,8 @@ function prependFirstEvent(iter, firstEvent, alreadyDone) {
3793
4095
  };
3794
4096
  }
3795
4097
  async function* codexProvider(opts) {
4098
+ const filePreviews = new CodexFilePreviewTracker(opts.cwd);
4099
+ const consumedPatchCallIds = new Set(opts.sessionId ? readCodexPatchCallIds(opts.sessionId) : []);
3796
4100
  const codexAuthPath = hostedCodexAuthFilePath();
3797
4101
  if (!existsSync10(codexAuthPath)) {
3798
4102
  yield {
@@ -3927,10 +4231,11 @@ async function* codexProvider(opts) {
3927
4231
  if (!item)
3928
4232
  break;
3929
4233
  if (item.type === "command_execution") {
4234
+ const command = String(item.command ?? "");
3930
4235
  yield {
3931
4236
  type: "tool_use",
3932
4237
  name: "Bash",
3933
- input: { command: String(item.command ?? "") },
4238
+ input: { command },
3934
4239
  toolUseId: String(item.id ?? "")
3935
4240
  };
3936
4241
  } else if (item.type === "mcp_tool_call") {
@@ -3976,19 +4281,24 @@ async function* codexProvider(opts) {
3976
4281
  if (reasoning)
3977
4282
  yield { type: "reasoning", content: reasoning };
3978
4283
  } else if (item.type === "mcp_tool_call") {
4284
+ const isError = item.status === "failed" || Boolean(item.error);
3979
4285
  yield {
3980
4286
  type: "tool_result",
3981
4287
  toolUseId: String(item.id ?? ""),
3982
- content: summarizeMcpToolCallResult(item)
4288
+ content: summarizeMcpToolCallResult(item),
4289
+ ...isError ? { isError: true } : {}
3983
4290
  };
3984
4291
  } else if (item.type === "command_execution") {
4292
+ const isError = item.status === "failed" || typeof item.exit_code === "number" && item.exit_code !== 0;
3985
4293
  yield {
3986
4294
  type: "tool_result",
3987
4295
  toolUseId: String(item.id ?? ""),
3988
- content: String(item.aggregated_output ?? "").slice(0, 200)
4296
+ content: String(item.aggregated_output ?? "").slice(0, 200),
4297
+ ...isError ? { isError: true } : {}
3989
4298
  };
3990
4299
  } else if (item.type === "file_change") {
3991
- for (const fileEvent of fileChangeEvents(item)) {
4300
+ const fileEvents = await fileChangeEvents(item, filePreviews, opts.cwd, currentSessionId, consumedPatchCallIds);
4301
+ for (const fileEvent of fileEvents) {
3992
4302
  yield fileEvent;
3993
4303
  }
3994
4304
  } else if (item.type === "error") {
@@ -4184,8 +4494,8 @@ function maestroProvider(opts) {
4184
4494
  }
4185
4495
 
4186
4496
  // ../../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";
4497
+ import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync9, renameSync as renameSync5, statSync as statSync4, writeFileSync as writeFileSync6 } from "fs";
4498
+ import { dirname as dirname8, join as join12 } from "path";
4189
4499
  function safeUserIdComponent2(userId) {
4190
4500
  const str = String(userId);
4191
4501
  if (!str || /[/\\]|\.\./.test(str)) {
@@ -4211,7 +4521,7 @@ function readTasks(userId, scopeKey) {
4211
4521
  if (!existsSync11(path))
4212
4522
  return [];
4213
4523
  try {
4214
- const parsed = JSON.parse(readFileSync8(path, "utf-8"));
4524
+ const parsed = JSON.parse(readFileSync9(path, "utf-8"));
4215
4525
  return Array.isArray(parsed?.tasks) ? parsed.tasks : [];
4216
4526
  } catch (e) {
4217
4527
  logger.warn({ err: e, path }, "tasks: failed to read task store");
@@ -4220,7 +4530,7 @@ function readTasks(userId, scopeKey) {
4220
4530
  }
4221
4531
  function taskFileMtimeNs(userId, scopeKey) {
4222
4532
  try {
4223
- return statSync3(getTaskFilePath(userId, scopeKey), { bigint: true }).mtimeNs;
4533
+ return statSync4(getTaskFilePath(userId, scopeKey), { bigint: true }).mtimeNs;
4224
4534
  } catch {
4225
4535
  return null;
4226
4536
  }
@@ -4541,9 +4851,15 @@ class SqliteRuntimeBus {
4541
4851
  toolUseId: id
4542
4852
  });
4543
4853
  }
4544
- broadcastToolOutput(topicId, queryId, toolUseId, content) {
4854
+ broadcastToolOutput(topicId, queryId, toolUseId, content, isError) {
4545
4855
  const id = toolUseId.trim() || `${queryId}:tool`;
4546
- this.broadcastAiStatus(topicId, { kind: "tool_output", queryId, toolUseId: id, content });
4856
+ this.broadcastAiStatus(topicId, {
4857
+ kind: "tool_output",
4858
+ queryId,
4859
+ toolUseId: id,
4860
+ content,
4861
+ ...isError ? { isError: true } : {}
4862
+ });
4547
4863
  }
4548
4864
  broadcastToolStatus(topicId, queryId, kind, content, meta) {
4549
4865
  this.broadcastAiStatus(topicId, {
@@ -4585,8 +4901,8 @@ var WsHub = {
4585
4901
  };
4586
4902
 
4587
4903
  // ../../packages/core/src/prompts/builders.ts
4588
- import { readFileSync as readFileSync9 } from "fs";
4589
- import { resolve as resolve5 } from "path";
4904
+ import { readFileSync as readFileSync10 } from "fs";
4905
+ import { resolve as resolve7 } from "path";
4590
4906
 
4591
4907
  // ../../packages/core/src/agents/model-catalog.ts
4592
4908
  var CODEX_PRO_20X_COST = "ChatGPT Pro 20x subscription: $200/month";
@@ -4687,10 +5003,10 @@ var SELECTABLE_MODELS = [
4687
5003
  ];
4688
5004
 
4689
5005
  // ../../packages/core/src/prompts/builders.ts
4690
- var PROMPTS_DIR = resolve5(PROJECT_ROOT, "src/prompts");
4691
- var SESSIONS_DIR = resolve5(PROMPTS_DIR, "sessions");
5006
+ var PROMPTS_DIR = resolve7(PROJECT_ROOT, "src/prompts");
5007
+ var SESSIONS_DIR = resolve7(PROMPTS_DIR, "sessions");
4692
5008
  function loadAgentPrompt(filename) {
4693
- const raw = readFileSync9(resolve5(AGENTS_PROMPTS_DIR, filename), "utf-8");
5009
+ const raw = readFileSync10(resolve7(AGENTS_PROMPTS_DIR, filename), "utf-8");
4694
5010
  const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
4695
5011
  if (!match)
4696
5012
  throw new Error(`Agent prompt ${filename} is missing frontmatter`);
@@ -4871,7 +5187,7 @@ function setTopicBrief(topicId, fields) {
4871
5187
  }
4872
5188
 
4873
5189
  // ../../packages/core/src/storage/wiki.ts
4874
- import { basename, dirname as dirname8, join as join14 } from "path";
5190
+ import { basename as basename2, dirname as dirname9, join as join14 } from "path";
4875
5191
  function getSharedWikiDir(workspaceDir = resolveStorageWorkspaceDir()) {
4876
5192
  return workspaceDir === resolveStorageWorkspaceDir() ? resolveStorageSharedWikiDir() : join14(workspaceDir, "wiki");
4877
5193
  }
@@ -5595,7 +5911,7 @@ function findSummaryFile(topicTitle, date, sinceMs, topicId) {
5595
5911
  continue;
5596
5912
  const p = join15(dir, f);
5597
5913
  try {
5598
- const m = statSync4(p).mtimeMs;
5914
+ const m = statSync5(p).mtimeMs;
5599
5915
  if (m >= sinceMs && (!best || m > best.mtime))
5600
5916
  best = { path: p, mtime: m };
5601
5917
  } catch {}
@@ -5607,7 +5923,7 @@ function finalizeGeneralMemory(userId, topicTitle, messageCount, startMs, ok, to
5607
5923
  const date = new Date().toISOString().slice(0, 10);
5608
5924
  try {
5609
5925
  const summaryPath = ok ? findSummaryFile(topicTitle, date, startMs, topicId) : null;
5610
- const summaryMd = summaryPath ? readFileSync10(summaryPath, "utf-8") : "";
5926
+ const summaryMd = summaryPath ? readFileSync11(summaryPath, "utf-8") : "";
5611
5927
  const oneLine = summaryMd && distillOneLine(summaryMd) || `${messageCount}\uAC1C \uBA54\uC2DC\uC9C0 \uC544\uCE74\uC774\uBE0C`;
5612
5928
  const prev = getTopicBrief(generalTopicId);
5613
5929
  const prevEntries = (prev?.briefMd ?? "").split(`
@@ -6567,4 +6883,4 @@ export {
6567
6883
  MIN_MEMORY_ARCHIVE_EXCHANGES
6568
6884
  };
6569
6885
 
6570
- //# debugId=A260F0394930433E64756E2164756E21
6886
+ //# debugId=CC442F5FF6F506FC64756E2164756E21