negotium 0.2.18 → 0.2.20

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 (55) hide show
  1. package/dist/agent-helpers.js +367 -313
  2. package/dist/agent-helpers.js.map +18 -18
  3. package/dist/background-bash.js +3 -2
  4. package/dist/background-bash.js.map +3 -3
  5. package/dist/browser-runtime.js +60 -36
  6. package/dist/browser-runtime.js.map +6 -6
  7. package/dist/{chunk-fc58cqry.js → chunk-g2kqfdwm.js} +154 -24
  8. package/dist/{chunk-fc58cqry.js.map → chunk-g2kqfdwm.js.map} +7 -6
  9. package/dist/hosted-agent.js +183 -53
  10. package/dist/hosted-agent.js.map +8 -7
  11. package/dist/main.js +1395 -626
  12. package/dist/main.js.map +27 -26
  13. package/dist/mcp-factories.js +1204 -624
  14. package/dist/mcp-factories.js.map +21 -20
  15. package/dist/prompts.js +3 -2
  16. package/dist/prompts.js.map +3 -3
  17. package/dist/query-runtime.js +47 -26
  18. package/dist/query-runtime.js.map +4 -4
  19. package/dist/registry.js +3 -3
  20. package/dist/registry.js.map +2 -2
  21. package/dist/rollout.js +1 -1
  22. package/dist/runtime/src/agents/archiver.ts +4 -3
  23. package/dist/runtime/src/application/switch-topic-access-mode.ts +81 -8
  24. package/dist/runtime/src/bus.ts +19 -2
  25. package/dist/runtime/src/mcp/canonical-proxy-server.ts +13 -14
  26. package/dist/runtime/src/mcp/session-comm/runtime.ts +16 -0
  27. package/dist/runtime/src/mcp/wiki-search-index.ts +359 -0
  28. package/dist/runtime/src/mcp/wiki-server.ts +559 -98
  29. package/dist/runtime/src/platform/config.ts +13 -1
  30. package/dist/runtime/src/platform/mcp-config.ts +1 -1
  31. package/dist/runtime/src/prompts/agents/wiki-archiver.md +62 -47
  32. package/dist/runtime/src/runtime/ask-callbacks.ts +19 -11
  33. package/dist/runtime/src/runtime/background-sessions.ts +9 -7
  34. package/dist/runtime/src/runtime/visual-store.ts +31 -23
  35. package/dist/runtime/src/storage/api-topics.ts +26 -0
  36. package/dist/runtime/src/storage/browser-profiles.ts +16 -8
  37. package/dist/runtime/src/storage/runtime-events.ts +30 -20
  38. package/dist/runtime/src/storage/runtime-gateway-submissions.ts +24 -16
  39. package/dist/runtime/src/storage/runtime-turn-requests.ts +9 -1
  40. package/dist/runtime/src/storage/self-schedules.ts +32 -24
  41. package/dist/runtime/src/storage/storage-host.ts +105 -43
  42. package/dist/runtime/src/storage/vault.ts +12 -3
  43. package/dist/runtime/src/version.ts +1 -1
  44. package/dist/runtime-helpers.js +47 -26
  45. package/dist/runtime-helpers.js.map +4 -4
  46. package/dist/storage.js +76 -41
  47. package/dist/storage.js.map +4 -4
  48. package/dist/types/packages/core/src/agents/archiver.d.ts +1 -1
  49. package/dist/types/packages/core/src/mcp/wiki-search-index.d.ts +59 -0
  50. package/dist/types/packages/core/src/storage/api-topics.d.ts +15 -0
  51. package/dist/types/packages/core/src/storage/storage-host.d.ts +10 -0
  52. package/dist/types/packages/core/src/version.d.ts +1 -1
  53. package/dist/vault.js +140 -10
  54. package/dist/vault.js.map +6 -5
  55. package/package.json +1 -1
@@ -73,7 +73,7 @@ export interface ArchiverRuntime {
73
73
  export declare function createArchiverRuntime(host: ArchiverHost): ArchiverRuntime;
74
74
  /**
75
75
  * Locate the summary the archiver turn just wrote. The wiki MCP's
76
- * `save_wiki_entry` writes `summaries/<date>-<title>[~N].md`.
76
+ * `wiki_write(kind="summary")` writes `summaries/<date>-<title>[~N].md`.
77
77
  */
78
78
  export declare function findSummaryFile(storage: ArchiverStorageHost, topicTitle: string, date: string, sinceMs: number, topicId?: string): string | null;
79
79
  export declare function runArchiverTurn(params: RunArchiverTurnParams): boolean;
@@ -0,0 +1,59 @@
1
+ export type IndexedWikiKind = "article" | "summary";
2
+ export interface WikiSearchIndexSource {
3
+ kind: IndexedWikiKind;
4
+ key: string;
5
+ path: string;
6
+ mtimeMs: number;
7
+ size: number;
8
+ }
9
+ export interface PreparedWikiSearchDocument {
10
+ title: string;
11
+ text: string;
12
+ tokens: string[];
13
+ date?: string;
14
+ family?: string;
15
+ }
16
+ export interface WikiSearchIndexDocument extends PreparedWikiSearchDocument {
17
+ id: string;
18
+ kind: IndexedWikiKind;
19
+ key: string;
20
+ path: string;
21
+ }
22
+ export interface WikiSearchIndexMetadata {
23
+ id: string;
24
+ kind: IndexedWikiKind;
25
+ key: string;
26
+ title: string;
27
+ date?: string;
28
+ }
29
+ export interface WikiSummaryTemporalQuery {
30
+ year?: string;
31
+ month?: string;
32
+ direction: "latest" | "oldest";
33
+ limit: number;
34
+ }
35
+ export interface WikiSearchIndexSyncResult {
36
+ added: number;
37
+ updated: number;
38
+ removed: number;
39
+ }
40
+ export declare class WikiSearchIndex {
41
+ #private;
42
+ readonly path: string;
43
+ constructor(path: string);
44
+ close(): void;
45
+ /**
46
+ * Record one document that was just written.
47
+ *
48
+ * The write path already holds the document text, so the index is maintained
49
+ * incrementally and never needs to scan the content directories. `sync` stays
50
+ * for the explicit backfill of documents that predate the write tool.
51
+ */
52
+ upsert(source: WikiSearchIndexSource, document: PreparedWikiSearchDocument): void;
53
+ sync(sources: WikiSearchIndexSource[], prepare: (source: WikiSearchIndexSource, text: string) => PreparedWikiSearchDocument): WikiSearchIndexSyncResult;
54
+ metadata(kinds: IndexedWikiKind[]): WikiSearchIndexMetadata[];
55
+ summaryMetadataByDate(query: WikiSummaryTemporalQuery): WikiSearchIndexMetadata[];
56
+ matchingDocumentIds(kinds: IndexedWikiKind[], terms: string[]): Set<string>;
57
+ documents(ids: Set<string>): WikiSearchIndexDocument[];
58
+ }
59
+ export declare function resetCorruptWikiSearchIndex(path: string): void;
@@ -63,6 +63,21 @@ export declare function normalizeTopicState(input: {
63
63
  agent?: AgentKind;
64
64
  };
65
65
  export declare function upsertTopic(t: TopicDto): void;
66
+ /**
67
+ * Apply one access mode to a set of topics as a single all-or-nothing write.
68
+ *
69
+ * A partial write is not a cosmetic glitch here: a public parent left with
70
+ * private subagent children (or the reverse) is exactly the half-exposed state
71
+ * the access-mode cascade exists to prevent, so every row commits or none do.
72
+ *
73
+ * Deliberately a narrow UPDATE instead of a loop over {@link upsertTopic}.
74
+ * That helper opens its own transaction, and the node:sqlite shim in
75
+ * `sqlite.ts` emulates transactions with bare BEGIN/COMMIT rather than
76
+ * savepoints — nesting one inside an outer transaction would fail outright.
77
+ * It also rewrites participants and browser-profile ownership, none of which
78
+ * an access-mode change should touch.
79
+ */
80
+ export declare function setTopicAccessModes(topicIds: readonly string[], accessMode: TopicAccessMode): void;
66
81
  export declare function listTopics(): TopicDto[];
67
82
  export declare function getTopic(id: string): TopicDto | null;
68
83
  /** Return the private manager room owned by a user, excluding the retired shared General row. */
@@ -5,6 +5,16 @@ type InternalStorageDatabase = InstanceType<typeof Database>;
5
5
  type StorageSchemaInitializer = (database: InternalStorageDatabase) => void;
6
6
  export declare function initializeDatabase(database: InternalStorageDatabase): void;
7
7
  export declare function resolveStorageDatabase(): InternalStorageDatabase;
8
+ /**
9
+ * The host-injected connection, or null when Negotium owns its own store.
10
+ *
11
+ * Unlike {@link resolveStorageDatabase} this never opens anything. Callers that
12
+ * manage their own short-lived connections need to know whether a host store
13
+ * exists *before* deciding to open one by path: opening it anyway is how an
14
+ * embedded Negotium ends up writing half its state into the host's database and
15
+ * the other half into `~/.negotium`, with nothing reporting an error.
16
+ */
17
+ export declare function configuredStorageDatabase(): InternalStorageDatabase | null;
8
18
  export declare function resolveStorageDataDir(): string;
9
19
  export declare function resolveStorageLogDir(): string;
10
20
  export declare function resolveStorageSessionAsksDir(): string;
@@ -1 +1 @@
1
- export declare const NEGOTIUM_VERSION = "0.2.18";
1
+ export declare const NEGOTIUM_VERSION = "0.2.20";
package/dist/vault.js CHANGED
@@ -2,8 +2,8 @@
2
2
  var __require = import.meta.require;
3
3
 
4
4
  // ../../packages/core/src/storage/vault.ts
5
- import { chmodSync as chmodSync2, mkdirSync as mkdirSync2 } from "fs";
6
- import { join as join2 } from "path";
5
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync3 } from "fs";
6
+ import { join as join3 } from "path";
7
7
 
8
8
  // ../../packages/core/src/platform/config.ts
9
9
  import { execFileSync } from "child_process";
@@ -125,11 +125,12 @@ function versionAtLeast(actualVersion, minimumVersion) {
125
125
  }
126
126
  return true;
127
127
  }
128
+ var BROWSER_RS_VERSION_PROBE_TIMEOUT_MS = 15000;
128
129
  function browserRsMeetsMinimumVersion(candidate) {
129
130
  try {
130
131
  const output = execFileSync(candidate, ["--version"], {
131
132
  encoding: "utf8",
132
- timeout: 2000,
133
+ timeout: BROWSER_RS_VERSION_PROBE_TIMEOUT_MS,
133
134
  stdio: ["ignore", "pipe", "ignore"]
134
135
  }).trim();
135
136
  const match = output.match(/^browser-rs (\d+)\.(\d+)\.(\d+)$/);
@@ -340,6 +341,134 @@ if (isBun) {
340
341
  Database = NodeDatabase;
341
342
  }
342
343
 
344
+ // ../../packages/core/src/storage/storage-host.ts
345
+ import { mkdirSync as mkdirSync2 } from "fs";
346
+ import { homedir as homedir2 } from "os";
347
+ import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
348
+ var STORAGE_HOST_STATE = Symbol.for("negotium.storage-host.state.v1");
349
+ function storageState() {
350
+ const holder = globalThis;
351
+ const existing = holder[STORAGE_HOST_STATE];
352
+ if (existing)
353
+ return existing;
354
+ const created = {
355
+ configuredHost: {},
356
+ fallbackDatabase: null,
357
+ fallbackDatabasePath: null,
358
+ frames: [],
359
+ schemaInitializers: [],
360
+ initializedSchemas: new WeakMap,
361
+ initializingDatabases: new WeakSet
362
+ };
363
+ Object.defineProperty(holder, STORAGE_HOST_STATE, {
364
+ value: created,
365
+ enumerable: false,
366
+ writable: false,
367
+ configurable: false
368
+ });
369
+ return created;
370
+ }
371
+ function envPath(name, fallback) {
372
+ const value = process.env[name]?.trim();
373
+ return resolve2(value || fallback);
374
+ }
375
+ function defaultStateDir() {
376
+ return envPath("NEGOTIUM_STATE_DIR", join2(homedir2(), ".negotium"));
377
+ }
378
+ function defaultDataDir() {
379
+ return envPath("NEGOTIUM_DATA_DIR", join2(defaultStateDir(), "data"));
380
+ }
381
+ function defaultSessionsDatabasePath() {
382
+ return envPath("SESSIONS_DB_PATH", join2(resolveStorageDataDir(), "sessions.db"));
383
+ }
384
+ var SQLITE_INIT_RETRY_MS = 25;
385
+ var SQLITE_INIT_TIMEOUT_MS = 5000;
386
+ var SQLITE_INIT_SLEEP = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
387
+ function isSqliteBusy(error) {
388
+ const message = error instanceof Error ? error.message : String(error);
389
+ return /database is (?:locked|busy)|SQLITE_(?:BUSY|LOCKED)/i.test(message);
390
+ }
391
+ function execWithBusyRetry(database, sql, timeoutMs = SQLITE_INIT_TIMEOUT_MS) {
392
+ const deadline = Date.now() + timeoutMs;
393
+ while (true) {
394
+ try {
395
+ database.exec(sql);
396
+ return;
397
+ } catch (error) {
398
+ if (!isSqliteBusy(error) || Date.now() >= deadline)
399
+ throw error;
400
+ Atomics.wait(SQLITE_INIT_SLEEP, 0, 0, Math.min(SQLITE_INIT_RETRY_MS, deadline - Date.now()));
401
+ }
402
+ }
403
+ }
404
+ function initializeDatabase(database) {
405
+ database.exec("PRAGMA busy_timeout = 5000");
406
+ execWithBusyRetry(database, "PRAGMA journal_mode = WAL");
407
+ database.exec("PRAGMA foreign_keys = ON");
408
+ database.exec("PRAGMA wal_autocheckpoint = 1000");
409
+ try {
410
+ database.exec("PRAGMA wal_checkpoint(TRUNCATE)");
411
+ } catch {}
412
+ }
413
+ function defaultDatabase() {
414
+ const path = defaultSessionsDatabasePath();
415
+ const state = storageState();
416
+ if (state.fallbackDatabase && state.fallbackDatabasePath === path)
417
+ return state.fallbackDatabase;
418
+ if (state.fallbackDatabase)
419
+ state.fallbackDatabase.close();
420
+ mkdirSync2(dirname2(path), { recursive: true });
421
+ state.fallbackDatabase = new Database(path, { create: true });
422
+ state.fallbackDatabasePath = path;
423
+ initializeDatabase(state.fallbackDatabase);
424
+ return state.fallbackDatabase;
425
+ }
426
+ function resolveStorageDatabase() {
427
+ return storageState().configuredHost.database ?? defaultDatabase();
428
+ }
429
+ function resolveStorageDataDir() {
430
+ return storageState().configuredHost.dataDir ?? defaultDataDir();
431
+ }
432
+ function ensureStorageSchemas(database = resolveStorageDatabase()) {
433
+ const state = storageState();
434
+ if (state.initializingDatabases.has(database))
435
+ return;
436
+ let initialized = state.initializedSchemas.get(database);
437
+ if (!initialized) {
438
+ initialized = new Set;
439
+ state.initializedSchemas.set(database, initialized);
440
+ }
441
+ state.initializingDatabases.add(database);
442
+ try {
443
+ for (const entry of state.schemaInitializers) {
444
+ if (initialized.has(entry.initialize))
445
+ continue;
446
+ initialized.add(entry.initialize);
447
+ try {
448
+ entry.initialize(database);
449
+ } catch (error) {
450
+ initialized.delete(entry.initialize);
451
+ throw error;
452
+ }
453
+ }
454
+ } finally {
455
+ state.initializingDatabases.delete(database);
456
+ }
457
+ }
458
+ var internalStorageDatabase = new Proxy({}, {
459
+ get(_target, property) {
460
+ const database = resolveStorageDatabase();
461
+ ensureStorageSchemas(database);
462
+ const value = Reflect.get(database, property, database);
463
+ return typeof value === "function" ? value.bind(database) : value;
464
+ },
465
+ set(_target, property, value) {
466
+ const database = resolveStorageDatabase();
467
+ ensureStorageSchemas(database);
468
+ return Reflect.set(database, property, value, database);
469
+ }
470
+ });
471
+
343
472
  // ../../packages/core/src/storage/vault-crypto-core.ts
344
473
  import { createCipheriv, createDecipheriv, createHash, randomBytes as randomBytes2 } from "crypto";
345
474
  var ENVELOPE_PREFIX = "otium-vault:v1:";
@@ -429,17 +558,18 @@ function initializeVaultDatabase(database) {
429
558
  }
430
559
  }
431
560
  function openVaultDatabase(dataDir) {
432
- const vaultDir = join2(dataDir, "vault");
433
- const path = join2(vaultDir, "vault.db");
434
- mkdirSync2(vaultDir, { recursive: true, mode: 448 });
561
+ const vaultDir = join3(dataDir, "vault");
562
+ const path = join3(vaultDir, "vault.db");
563
+ mkdirSync3(vaultDir, { recursive: true, mode: 448 });
435
564
  const database = new Database(path, { create: true });
436
565
  chmodSync2(path, 384);
437
566
  initializeVaultDatabase(database);
438
567
  return database;
439
568
  }
440
569
  function activeVaultDatabase() {
441
- if (!vaultDb)
442
- vaultDb = openVaultDatabase(DATA_DIR);
570
+ if (!vaultDb) {
571
+ vaultDb = openVaultDatabase(resolveStorageDataDir());
572
+ }
443
573
  return vaultDb;
444
574
  }
445
575
  function configureVaultStorage(options) {
@@ -448,7 +578,7 @@ function configureVaultStorage(options) {
448
578
  }
449
579
  const previousDb = vaultDb;
450
580
  const previousMasterKey = vaultMasterKey;
451
- const configuredDb = options.database ?? openVaultDatabase(options.dataDir ?? DATA_DIR);
581
+ const configuredDb = options.database ?? openVaultDatabase(options.dataDir ?? resolveStorageDataDir());
452
582
  if (options.database)
453
583
  initializeVaultDatabase(configuredDb);
454
584
  vaultDb = configuredDb;
@@ -626,4 +756,4 @@ export {
626
756
  VAULT_DESCRIPTION_MAX_LENGTH
627
757
  };
628
758
 
629
- //# debugId=BC1F6A83A7692EAF64756E2164756E21
759
+ //# debugId=E210ECA559CC5E1164756E2164756E21