negotium 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -411,9 +411,9 @@ var init_sqlite = __esm(async () => {
411
411
  }
412
412
  });
413
413
 
414
- // ../../packages/core/src/storage/vault-crypto.ts
414
+ // ../../packages/core/src/storage/vault-crypto-core.ts
415
415
  import { createCipheriv, createDecipheriv, createHash, randomBytes as randomBytes2 } from "crypto";
416
- function encryptionKey(masterKey = VAULT_MASTER_KEY) {
416
+ function encryptionKey(masterKey) {
417
417
  return createHash("sha256").update("otium-vault-value-v1\x00", "utf8").update(masterKey, "utf8").digest().subarray(0, KEY_BYTES);
418
418
  }
419
419
  function aad(userId, key) {
@@ -422,7 +422,7 @@ function aad(userId, key) {
422
422
  function isEncryptedVaultValue(value) {
423
423
  return value.startsWith(ENVELOPE_PREFIX);
424
424
  }
425
- function encryptVaultValue(userId, key, value, masterKey = VAULT_MASTER_KEY) {
425
+ function encryptVaultValueWithKey(userId, key, value, masterKey) {
426
426
  const iv = randomBytes2(IV_BYTES);
427
427
  const cipher = createCipheriv("aes-256-gcm", encryptionKey(masterKey), iv);
428
428
  cipher.setAAD(aad(userId, key));
@@ -430,7 +430,7 @@ function encryptVaultValue(userId, key, value, masterKey = VAULT_MASTER_KEY) {
430
430
  const tag = cipher.getAuthTag();
431
431
  return `${ENVELOPE_PREFIX}${iv.toString("base64url")}.${ciphertext.toString("base64url")}.${tag.toString("base64url")}`;
432
432
  }
433
- function decryptVaultValue(userId, key, storedValue, masterKey = VAULT_MASTER_KEY) {
433
+ function decryptVaultValueWithKey(userId, key, storedValue, masterKey) {
434
434
  if (!isEncryptedVaultValue(storedValue)) {
435
435
  return { value: storedValue, legacyPlaintext: true };
436
436
  }
@@ -452,8 +452,18 @@ function decryptVaultValue(userId, key, storedValue, masterKey = VAULT_MASTER_KE
452
452
  return { value: plaintext.toString("utf8"), legacyPlaintext: false };
453
453
  }
454
454
  var ENVELOPE_PREFIX = "otium-vault:v1:", IV_BYTES = 12, KEY_BYTES = 32;
455
+ var init_vault_crypto_core = () => {};
456
+
457
+ // ../../packages/core/src/storage/vault-crypto.ts
458
+ function encryptVaultValue(userId, key, value, masterKey = VAULT_MASTER_KEY) {
459
+ return encryptVaultValueWithKey(userId, key, value, masterKey);
460
+ }
461
+ function decryptVaultValue(userId, key, storedValue, masterKey = VAULT_MASTER_KEY) {
462
+ return decryptVaultValueWithKey(userId, key, storedValue, masterKey);
463
+ }
455
464
  var init_vault_crypto = __esm(() => {
456
465
  init_config();
466
+ init_vault_crypto_core();
457
467
  });
458
468
 
459
469
  // ../../packages/core/src/storage/vault.ts
@@ -4584,7 +4594,7 @@ var exports_version = {};
4584
4594
  __export(exports_version, {
4585
4595
  NEGOTIUM_VERSION: () => NEGOTIUM_VERSION
4586
4596
  });
4587
- var NEGOTIUM_VERSION = "0.2.0";
4597
+ var NEGOTIUM_VERSION = "0.2.2";
4588
4598
 
4589
4599
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
4590
4600
  import { spawn as spawn3 } from "child_process";
@@ -43716,18 +43726,49 @@ function addDirectoryContents(operations, sourceDir, destinationDir, stagingDir)
43716
43726
  });
43717
43727
  }
43718
43728
  }
43719
- function assertNoCollisions(operations) {
43729
+ function pathWithin(path, root) {
43730
+ return path === root || path.startsWith(`${root}/`);
43731
+ }
43732
+ function collisionPolicy(operation, stateDir2) {
43733
+ const destination = operation.destination;
43734
+ if (!destination || !existsSync36(destination))
43735
+ return;
43736
+ if (pathWithin(destination, join42(stateDir2, "runtime")))
43737
+ return "keep-destination";
43738
+ if (pathWithin(destination, join42(stateDir2, "binaries")))
43739
+ return "keep-destination";
43740
+ if (pathWithin(destination, join42(stateDir2, "secrets")))
43741
+ return "replace-destination";
43742
+ if (destination === join42(stateDir2, "data", "vault", "vault.db")) {
43743
+ return "replace-destination";
43744
+ }
43745
+ if (pathWithin(destination, join42(stateDir2, "browser", "profiles"))) {
43746
+ return "replace-destination";
43747
+ }
43748
+ if (pathWithin(destination, join42(stateDir2, "data", "conversations")) && destination.endsWith(".jsonl")) {
43749
+ return "merge-jsonl";
43750
+ }
43751
+ if (pathWithin(destination, join42(stateDir2, "data", "tasks")) && destination.endsWith(".json")) {
43752
+ return "merge-tasks";
43753
+ }
43754
+ if (statSync19(operation.source).isFile() && statSync19(destination).isFile() && readFileSync27(operation.source).equals(readFileSync27(destination))) {
43755
+ return "keep-destination";
43756
+ }
43757
+ throw new Error(`Migration collision at ${destination}.`);
43758
+ }
43759
+ function configureCollisions(operations, stateDir2, stagingRoot) {
43720
43760
  const destinations = new Set;
43721
43761
  for (const operation of operations) {
43722
43762
  if (!operation.destination)
43723
43763
  continue;
43724
- if (existsSync36(operation.destination)) {
43725
- throw new Error(`Migration collision at ${operation.destination}.`);
43726
- }
43727
43764
  if (destinations.has(operation.destination)) {
43728
43765
  throw new Error(`Multiple legacy paths target ${operation.destination}.`);
43729
43766
  }
43730
43767
  destinations.add(operation.destination);
43768
+ operation.collision = collisionPolicy(operation, stateDir2);
43769
+ if (operation.collision && operation.collision !== "keep-destination") {
43770
+ operation.destinationStaged = join42(stagingRoot, randomUUID34());
43771
+ }
43731
43772
  }
43732
43773
  }
43733
43774
  function writeJournal(path, journal) {
@@ -43736,16 +43777,96 @@ function writeJournal(path, journal) {
43736
43777
  }
43737
43778
  function rollbackFilesystem(journal, journalPath) {
43738
43779
  for (const operation of [...journal.operations].reverse()) {
43739
- const current3 = operation.state === "placed" ? operation.destination : operation.staged;
43740
- if (!current3 || !existsSync36(current3) || existsSync36(operation.source))
43741
- continue;
43742
- mkdirSync35(dirname23(operation.source), { recursive: true });
43743
- renameSync16(current3, operation.source);
43780
+ if (operation.state === "placed") {
43781
+ if (operation.collision === "keep-destination" || !operation.destination) {
43782
+ if (existsSync36(operation.staged) && !existsSync36(operation.source)) {
43783
+ mkdirSync35(dirname23(operation.source), { recursive: true });
43784
+ renameSync16(operation.staged, operation.source);
43785
+ }
43786
+ } else if (operation.destination && existsSync36(operation.destination)) {
43787
+ if (operation.collision === "merge-jsonl" || operation.collision === "merge-tasks") {
43788
+ rmSync11(operation.destination, { recursive: true, force: true });
43789
+ if (existsSync36(operation.staged) && !existsSync36(operation.source)) {
43790
+ mkdirSync35(dirname23(operation.source), { recursive: true });
43791
+ renameSync16(operation.staged, operation.source);
43792
+ }
43793
+ } else if (!existsSync36(operation.source)) {
43794
+ mkdirSync35(dirname23(operation.source), { recursive: true });
43795
+ renameSync16(operation.destination, operation.source);
43796
+ }
43797
+ }
43798
+ } else if (existsSync36(operation.staged) && !existsSync36(operation.source)) {
43799
+ mkdirSync35(dirname23(operation.source), { recursive: true });
43800
+ renameSync16(operation.staged, operation.source);
43801
+ }
43802
+ if (operation.destination && operation.destinationStaged && existsSync36(operation.destinationStaged) && !existsSync36(operation.destination)) {
43803
+ mkdirSync35(dirname23(operation.destination), { recursive: true });
43804
+ renameSync16(operation.destinationStaged, operation.destination);
43805
+ }
43744
43806
  operation.state = "pending";
43745
43807
  }
43746
43808
  journal.phase = "filesystem";
43747
43809
  writeJournal(journalPath, journal);
43748
43810
  }
43811
+ function mergedJsonl(source, destination) {
43812
+ const lines = new Set;
43813
+ for (const path of [source, destination]) {
43814
+ for (const line2 of readFileSync27(path, "utf8").split(/\r?\n/)) {
43815
+ if (line2)
43816
+ lines.add(line2);
43817
+ }
43818
+ }
43819
+ return lines.size > 0 ? `${[...lines].join(`
43820
+ `)}
43821
+ ` : "";
43822
+ }
43823
+ function readTaskFile(path) {
43824
+ const parsed = JSON.parse(readFileSync27(path, "utf8"));
43825
+ if (parsed.version !== 1 || !Array.isArray(parsed.tasks)) {
43826
+ throw new Error(`Invalid task file during migration: ${path}`);
43827
+ }
43828
+ return parsed;
43829
+ }
43830
+ function mergedTasks(source, destination) {
43831
+ const legacy = readTaskFile(source).tasks;
43832
+ const current3 = readTaskFile(destination).tasks;
43833
+ const merged = legacy.map((task) => ({ ...task }));
43834
+ const usedIds = new Set(merged.map((task) => String(task.id)));
43835
+ let nextId = Math.max(0, ...[...usedIds].map(Number).filter(Number.isInteger)) + 1;
43836
+ const remapped = new Map;
43837
+ for (const task of current3) {
43838
+ const originalId = String(task.id);
43839
+ const exact = merged.find((candidate) => candidate.id === originalId && JSON.stringify(candidate) === JSON.stringify(task));
43840
+ if (exact) {
43841
+ remapped.set(originalId, originalId);
43842
+ continue;
43843
+ }
43844
+ const id = usedIds.has(originalId) ? String(nextId++) : originalId;
43845
+ usedIds.add(id);
43846
+ remapped.set(originalId, id);
43847
+ merged.push({ ...task, id });
43848
+ }
43849
+ for (const task of merged.slice(legacy.length)) {
43850
+ if (task.blockedBy)
43851
+ task.blockedBy = task.blockedBy.map((id) => remapped.get(id) ?? id);
43852
+ }
43853
+ return `${JSON.stringify({ version: 1, tasks: merged }, null, 2)}
43854
+ `;
43855
+ }
43856
+ function placeOperation(operation) {
43857
+ if (!operation.destination || operation.collision === "keep-destination")
43858
+ return;
43859
+ mkdirSync35(dirname23(operation.destination), { recursive: true });
43860
+ if (operation.collision === "merge-jsonl") {
43861
+ writeFileSync25(operation.destination, mergedJsonl(operation.staged, operation.destinationStaged));
43862
+ return;
43863
+ }
43864
+ if (operation.collision === "merge-tasks") {
43865
+ writeFileSync25(operation.destination, mergedTasks(operation.staged, operation.destinationStaged));
43866
+ return;
43867
+ }
43868
+ renameSync16(operation.staged, operation.destination);
43869
+ }
43749
43870
  function tablesWithColumn(database, columnName) {
43750
43871
  const tables = database.query("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'").all();
43751
43872
  return tables.map((row) => row.name).filter((table) => database.query(`PRAGMA table_info("${table.replaceAll('"', '""')}")`).all().some((column) => column.name === columnName));
@@ -43800,8 +43921,8 @@ function migrateDatabase(dbPath, source, masterKey, legacyTopicProfiles = []) {
43800
43921
  throw new Error("Cannot migrate encrypted vault rows without vault-master-key.");
43801
43922
  const rows = database.query("SELECT key, value FROM vault WHERE user_id = ?").all(source);
43802
43923
  for (const row of rows) {
43803
- const plaintext = decryptVaultValue(source, row.key, row.value, masterKey).value;
43804
- database.query("UPDATE vault SET value = ? WHERE user_id = ? AND key = ?").run(encryptVaultValue(CANONICAL_LOCAL_USER_ID, row.key, plaintext, masterKey), source, row.key);
43924
+ const plaintext = decryptVaultValueWithKey(source, row.key, row.value, masterKey).value;
43925
+ database.query("UPDATE vault SET value = ? WHERE user_id = ? AND key = ?").run(encryptVaultValueWithKey(CANONICAL_LOCAL_USER_ID, row.key, plaintext, masterKey), source, row.key);
43805
43926
  }
43806
43927
  }
43807
43928
  database.query(`UPDATE ${quote(table)} SET user_id = ? WHERE user_id = ?`).run(CANONICAL_LOCAL_USER_ID, source);
@@ -43983,7 +44104,7 @@ function migrateSingleUserState(options) {
43983
44104
  state: "pending"
43984
44105
  });
43985
44106
  }
43986
- assertNoCollisions(operations);
44107
+ configureCollisions(operations, stateDir2, stagingRoot);
43987
44108
  journal = {
43988
44109
  id: SINGLE_USER_MIGRATION_ID,
43989
44110
  sourcePrincipal: source,
@@ -43999,21 +44120,43 @@ function migrateSingleUserState(options) {
43999
44120
  const vaultDbBackup = join42(stagingRoot, "vault.db.rollback");
44000
44121
  try {
44001
44122
  for (const operation of journal.operations) {
44002
- if (operation.state === "pending" && !existsSync36(operation.source) && existsSync36(operation.staged)) {
44003
- operation.state = "staged";
44004
- }
44005
- if (operation.state === "staged" && operation.destination && !existsSync36(operation.staged) && existsSync36(operation.destination)) {
44006
- operation.state = "placed";
44123
+ if (operation.state === "staged")
44124
+ operation.state = "source-staged";
44125
+ if (operation.state === "pending" && !existsSync36(operation.source)) {
44126
+ if (existsSync36(operation.staged))
44127
+ operation.state = "source-staged";
44128
+ else if (operation.destination && existsSync36(operation.destination)) {
44129
+ operation.state = "placed";
44130
+ }
44007
44131
  }
44008
44132
  if (operation.state === "pending") {
44009
44133
  mkdirSync35(dirname23(operation.staged), { recursive: true });
44010
44134
  renameSync16(operation.source, operation.staged);
44011
- operation.state = "staged";
44135
+ operation.state = "source-staged";
44012
44136
  writeJournal(journalPath, journal);
44013
44137
  }
44014
- if (operation.state === "staged" && operation.destination) {
44015
- mkdirSync35(dirname23(operation.destination), { recursive: true });
44016
- renameSync16(operation.staged, operation.destination);
44138
+ if (operation.state === "source-staged" && operation.collision === "replace-destination" && operation.destination && operation.destinationStaged && !existsSync36(operation.staged) && existsSync36(operation.destination) && existsSync36(operation.destinationStaged)) {
44139
+ operation.state = "placed";
44140
+ writeJournal(journalPath, journal);
44141
+ }
44142
+ if (operation.state === "destination-staged" && operation.collision !== "merge-jsonl" && operation.collision !== "merge-tasks" && operation.collision !== "keep-destination" && operation.destination && !existsSync36(operation.staged) && existsSync36(operation.destination)) {
44143
+ operation.state = "placed";
44144
+ writeJournal(journalPath, journal);
44145
+ }
44146
+ if (operation.state === "source-staged") {
44147
+ if (operation.destination && operation.destinationStaged) {
44148
+ if (existsSync36(operation.destination) && !existsSync36(operation.destinationStaged)) {
44149
+ renameSync16(operation.destination, operation.destinationStaged);
44150
+ }
44151
+ if (!existsSync36(operation.destinationStaged)) {
44152
+ throw new Error(`Missing collision backup for ${operation.destination}.`);
44153
+ }
44154
+ }
44155
+ operation.state = "destination-staged";
44156
+ writeJournal(journalPath, journal);
44157
+ }
44158
+ if (operation.state === "destination-staged") {
44159
+ placeOperation(operation);
44017
44160
  operation.state = "placed";
44018
44161
  writeJournal(journalPath, journal);
44019
44162
  }
@@ -44056,11 +44199,22 @@ function migrateSingleUserState(options) {
44056
44199
  migratedTables
44057
44200
  };
44058
44201
  for (const store of ["conversations", "tasks", "users"]) {
44059
- rmSync11(join42(stateDir2, "data", store, source), { recursive: true, force: true });
44202
+ rmSync11(join42(stateDir2, "data", store, source), {
44203
+ recursive: true,
44204
+ force: true
44205
+ });
44060
44206
  }
44061
- rmSync11(join42(stateDir2, "workspace", "browser-profiles"), { recursive: true, force: true });
44207
+ rmSync11(join42(stateDir2, "run"), { recursive: true, force: true });
44208
+ rmSync11(join42(stateDir2, "bin"), { recursive: true, force: true });
44209
+ rmSync11(join42(stateDir2, "workspace", "browser-profiles"), {
44210
+ recursive: true,
44211
+ force: true
44212
+ });
44062
44213
  writeFileSync25(markerPath, `${JSON.stringify(marker, null, 2)}
44063
- `, { mode: 384, flag: "wx" });
44214
+ `, {
44215
+ mode: 384,
44216
+ flag: "wx"
44217
+ });
44064
44218
  journal.phase = "complete";
44065
44219
  rmSync11(stagingRoot, { recursive: true, force: true });
44066
44220
  return {
@@ -44082,7 +44236,7 @@ function migrateSingleUserState(options) {
44082
44236
  }
44083
44237
  var CANONICAL_LOCAL_USER_ID = "local", SINGLE_USER_MIGRATION_ID = "negotium-0.2.0-single-user";
44084
44238
  var init_single_user = __esm(async () => {
44085
- init_vault_crypto();
44239
+ init_vault_crypto_core();
44086
44240
  await init_sqlite();
44087
44241
  });
44088
44242
 
@@ -44788,4 +44942,4 @@ switch (command) {
44788
44942
  }
44789
44943
  }
44790
44944
 
44791
- //# debugId=0E990AB8BEC23DD564756E2164756E21
44945
+ //# debugId=29CE1495263F2C6864756E2164756E21