negotium 0.2.19 → 0.2.21
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/agent-helpers.js +573 -593
- package/dist/agent-helpers.js.map +17 -17
- package/dist/background-bash.js +3 -2
- package/dist/background-bash.js.map +3 -3
- package/dist/browser-runtime.js +76 -36
- package/dist/browser-runtime.js.map +6 -6
- package/dist/canonical-mcp-bridge.js +1 -1
- package/dist/{chunk-fc58cqry.js → chunk-rhp26p2c.js} +171 -26
- package/dist/{chunk-fc58cqry.js.map → chunk-rhp26p2c.js.map} +6 -5
- package/dist/{chunk-r0xs3t81.js → chunk-s2gez3wg.js} +18 -3
- package/dist/{chunk-r0xs3t81.js.map → chunk-s2gez3wg.js.map} +2 -2
- package/dist/hosted-agent.js +185 -54
- package/dist/hosted-agent.js.map +7 -6
- package/dist/main.js +1209 -1114
- package/dist/main.js.map +25 -25
- package/dist/mcp-catalog.js.map +1 -1
- package/dist/mcp-factories.js +526 -540
- package/dist/mcp-factories.js.map +17 -17
- package/dist/outbox.js.map +1 -1
- package/dist/platform-runtime.js.map +1 -1
- package/dist/prompts.js +3 -2
- package/dist/prompts.js.map +3 -3
- package/dist/query-runtime.js +63 -26
- package/dist/query-runtime.js.map +4 -4
- package/dist/registry.js +4 -3
- package/dist/registry.js.map +3 -3
- package/dist/rollout.js +1 -1
- package/dist/runtime/src/agents/archiver.ts +3 -2
- package/dist/runtime/src/application/switch-topic-access-mode.ts +81 -8
- package/dist/runtime/src/bus.ts +19 -2
- package/dist/runtime/src/mcp/session-comm/runtime.ts +16 -0
- package/dist/runtime/src/platform/config.ts +13 -1
- package/dist/runtime/src/runtime/ask-callbacks.ts +19 -11
- package/dist/runtime/src/runtime/background-sessions.ts +9 -7
- package/dist/runtime/src/runtime/visual-store.ts +31 -23
- package/dist/runtime/src/storage/api-topics.ts +26 -0
- package/dist/runtime/src/storage/browser-profiles.ts +16 -8
- package/dist/runtime/src/storage/runtime-events.ts +30 -20
- package/dist/runtime/src/storage/runtime-gateway-submissions.ts +24 -16
- package/dist/runtime/src/storage/runtime-turn-requests.ts +9 -1
- package/dist/runtime/src/storage/self-schedules.ts +32 -24
- package/dist/runtime/src/storage/storage-host.ts +105 -43
- package/dist/runtime/src/storage/vault.ts +12 -3
- package/dist/runtime/src/version.ts +1 -1
- package/dist/runtime-helpers.js +63 -26
- package/dist/runtime-helpers.js.map +4 -4
- package/dist/sqlite.js +17 -1
- package/dist/sqlite.js.map +2 -2
- package/dist/storage.js +92 -46
- package/dist/storage.js.map +4 -4
- package/dist/types/packages/core/src/storage/api-topics.d.ts +15 -0
- package/dist/types/packages/core/src/storage/storage-host.d.ts +10 -0
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/dist/vault.js +156 -10
- package/dist/vault.js.map +6 -5
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { db } from "#storage/forum-db";
|
|
2
|
+
import { registerStorageSchemaInitializer } from "#storage/storage-host";
|
|
2
3
|
|
|
3
4
|
export const RUNTIME_EVENT_TYPES = [
|
|
4
5
|
"message",
|
|
@@ -29,26 +30,35 @@ interface RuntimeEventRow {
|
|
|
29
30
|
created_at: string;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
db.exec
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
33
|
+
// Registered rather than executed at import time. A bare top-level `db.exec`
|
|
34
|
+
// resolves the storage connection the moment this module is imported, which for
|
|
35
|
+
// an embedding host is before `configureStorageHost()` has run — Negotium then
|
|
36
|
+
// opens its own database in the default state directory, caches it, and every
|
|
37
|
+
// later caller silently keeps using it instead of the host's.
|
|
38
|
+
registerStorageSchemaInitializer((database) => {
|
|
39
|
+
database.exec(`
|
|
40
|
+
CREATE TABLE IF NOT EXISTS runtime_events (
|
|
41
|
+
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
42
|
+
source_id TEXT NOT NULL,
|
|
43
|
+
event_type TEXT NOT NULL CHECK (
|
|
44
|
+
event_type IN (
|
|
45
|
+
'message',
|
|
46
|
+
'message-updated',
|
|
47
|
+
'ai-status',
|
|
48
|
+
'topic-created',
|
|
49
|
+
'topic-updated',
|
|
50
|
+
'topic-deleted'
|
|
51
|
+
)
|
|
52
|
+
),
|
|
53
|
+
topic_id TEXT NOT NULL,
|
|
54
|
+
payload_json TEXT NOT NULL,
|
|
55
|
+
created_at TEXT NOT NULL
|
|
56
|
+
)
|
|
57
|
+
`);
|
|
58
|
+
database.exec(
|
|
59
|
+
"CREATE INDEX IF NOT EXISTS idx_runtime_events_topic_seq ON runtime_events(topic_id, seq)",
|
|
60
|
+
);
|
|
61
|
+
});
|
|
52
62
|
|
|
53
63
|
const types = new Set<string>(RUNTIME_EVENT_TYPES);
|
|
54
64
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { db } from "#storage/forum-db";
|
|
2
|
+
import { registerStorageSchemaInitializer } from "#storage/storage-host";
|
|
2
3
|
|
|
3
4
|
export interface RuntimeGatewaySubmission {
|
|
4
5
|
clientMessageId: string;
|
|
@@ -25,19 +26,29 @@ interface RuntimeGatewaySubmissionRow {
|
|
|
25
26
|
payload_hash: string | null;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
db.exec
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
29
|
+
// Registered rather than executed at import time. A bare top-level `db.exec`
|
|
30
|
+
// resolves the storage connection the moment this module is imported, which for
|
|
31
|
+
// an embedding host is before `configureStorageHost()` has run — Negotium then
|
|
32
|
+
// opens its own database in the default state directory, caches it, and every
|
|
33
|
+
// later caller silently keeps using it instead of the host's.
|
|
34
|
+
registerStorageSchemaInitializer((database) => {
|
|
35
|
+
database.exec(`
|
|
36
|
+
CREATE TABLE IF NOT EXISTS runtime_gateway_submissions (
|
|
37
|
+
client_message_id TEXT PRIMARY KEY,
|
|
38
|
+
request_id TEXT NOT NULL UNIQUE,
|
|
39
|
+
topic_id TEXT NOT NULL,
|
|
40
|
+
message_id TEXT NOT NULL,
|
|
41
|
+
user_id TEXT NOT NULL,
|
|
42
|
+
created_at TEXT NOT NULL,
|
|
43
|
+
ack_cursor INTEGER NOT NULL DEFAULT 0,
|
|
44
|
+
message_cursor INTEGER NOT NULL DEFAULT 0,
|
|
45
|
+
payload_hash TEXT
|
|
46
|
+
)
|
|
47
|
+
`);
|
|
48
|
+
database.exec(
|
|
49
|
+
"CREATE INDEX IF NOT EXISTS idx_runtime_gateway_submissions_topic ON runtime_gateway_submissions(topic_id)",
|
|
50
|
+
);
|
|
51
|
+
});
|
|
41
52
|
try {
|
|
42
53
|
db.exec(
|
|
43
54
|
"ALTER TABLE runtime_gateway_submissions ADD COLUMN ack_cursor INTEGER NOT NULL DEFAULT 0",
|
|
@@ -51,9 +62,6 @@ try {
|
|
|
51
62
|
"ALTER TABLE runtime_gateway_submissions ADD COLUMN message_cursor INTEGER NOT NULL DEFAULT 0",
|
|
52
63
|
);
|
|
53
64
|
} catch {}
|
|
54
|
-
db.exec(
|
|
55
|
-
"CREATE INDEX IF NOT EXISTS idx_runtime_gateway_submissions_topic ON runtime_gateway_submissions(topic_id)",
|
|
56
|
-
);
|
|
57
65
|
|
|
58
66
|
function rowToSubmission(row: RuntimeGatewaySubmissionRow): RuntimeGatewaySubmission {
|
|
59
67
|
return {
|
|
@@ -9,6 +9,7 @@ import { db } from "#storage/forum-db";
|
|
|
9
9
|
import { TURN_LEASE_STALE_MS } from "#storage/runtime-leases";
|
|
10
10
|
import { getRuntimeTopicEpoch, TOPIC_MAINTENANCE_STALE_MS } from "#storage/runtime-topic-state";
|
|
11
11
|
import type { StorageDatabase } from "#storage/storage-contract";
|
|
12
|
+
import { registerStorageSchemaInitializer } from "#storage/storage-host";
|
|
12
13
|
import type { AgentKind, EffortLevel, PeerRuntimeBridgeContext } from "#types";
|
|
13
14
|
|
|
14
15
|
const REQUEST_CLAIM_STALE_MS = TURN_LEASE_STALE_MS;
|
|
@@ -149,7 +150,14 @@ export function ensureRuntimeUserTurnRequestsSchema(database: StorageDatabase):
|
|
|
149
150
|
);
|
|
150
151
|
}
|
|
151
152
|
|
|
152
|
-
|
|
153
|
+
// Registered rather than run at import time. Calling this with `db` on module
|
|
154
|
+
// evaluation resolves the storage connection immediately, which for an
|
|
155
|
+
// embedding host happens before `configureStorageHost()` — Negotium then opens
|
|
156
|
+
// and caches its own database in the default state directory, and every later
|
|
157
|
+
// caller silently keeps using it instead of the host's.
|
|
158
|
+
registerStorageSchemaInitializer((database) =>
|
|
159
|
+
ensureRuntimeUserTurnRequestsSchema(database as unknown as StorageDatabase),
|
|
160
|
+
);
|
|
153
161
|
|
|
154
162
|
function rowToRequest(row: RuntimeUserTurnRequestRow): RuntimeUserTurnRequest {
|
|
155
163
|
let attachments: string[] | undefined;
|
|
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { db } from "#storage/forum-db";
|
|
3
3
|
import { TURN_LEASE_STALE_MS } from "#storage/runtime-leases";
|
|
4
4
|
import { TOPIC_MAINTENANCE_STALE_MS } from "#storage/runtime-topic-state";
|
|
5
|
+
import { registerStorageSchemaInitializer } from "#storage/storage-host";
|
|
5
6
|
|
|
6
7
|
export type SelfScheduleStatus = "pending" | "running";
|
|
7
8
|
|
|
@@ -33,30 +34,37 @@ interface SelfScheduleRow {
|
|
|
33
34
|
running_query_id: string | null;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
db.exec
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
37
|
+
// Registered rather than executed at import time. A bare top-level `db.exec`
|
|
38
|
+
// resolves the storage connection the moment this module is imported, which for
|
|
39
|
+
// an embedding host is before `configureStorageHost()` has run — Negotium then
|
|
40
|
+
// opens its own database in the default state directory, caches it, and every
|
|
41
|
+
// later caller silently keeps using it instead of the host's.
|
|
42
|
+
registerStorageSchemaInitializer((database) => {
|
|
43
|
+
database.exec(`
|
|
44
|
+
CREATE TABLE IF NOT EXISTS runtime_self_schedules (
|
|
45
|
+
id TEXT PRIMARY KEY,
|
|
46
|
+
topic_id TEXT NOT NULL,
|
|
47
|
+
user_id TEXT NOT NULL,
|
|
48
|
+
message TEXT NOT NULL,
|
|
49
|
+
deliver_at INTEGER NOT NULL,
|
|
50
|
+
created_at INTEGER NOT NULL,
|
|
51
|
+
updated_at INTEGER NOT NULL,
|
|
52
|
+
status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'running')),
|
|
53
|
+
claimed_by TEXT,
|
|
54
|
+
claimed_at INTEGER,
|
|
55
|
+
running_query_id TEXT
|
|
56
|
+
)
|
|
57
|
+
`);
|
|
58
|
+
database.exec(`
|
|
59
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_runtime_self_schedules_one_pending
|
|
60
|
+
ON runtime_self_schedules(topic_id)
|
|
61
|
+
WHERE status = 'pending'
|
|
62
|
+
`);
|
|
63
|
+
database.exec(`
|
|
64
|
+
CREATE INDEX IF NOT EXISTS idx_runtime_self_schedules_due
|
|
65
|
+
ON runtime_self_schedules(status, deliver_at)
|
|
66
|
+
`);
|
|
67
|
+
});
|
|
60
68
|
|
|
61
69
|
function rowToSchedule(row: SelfScheduleRow): SelfSchedule {
|
|
62
70
|
return {
|
|
@@ -14,28 +14,73 @@ export type {
|
|
|
14
14
|
StorageTransaction,
|
|
15
15
|
} from "#storage/storage-contract";
|
|
16
16
|
|
|
17
|
-
let configuredHost: Readonly<StorageHostConfig> = {};
|
|
18
17
|
type InternalStorageDatabase = InstanceType<typeof Database>;
|
|
19
18
|
type OwnedStorageDatabase = InternalStorageDatabase & { close(): void };
|
|
20
|
-
let fallbackDatabase: OwnedStorageDatabase | null = null;
|
|
21
|
-
let fallbackDatabasePath: string | null = null;
|
|
22
19
|
|
|
23
20
|
interface StorageHostFrame {
|
|
24
21
|
active: boolean;
|
|
25
22
|
patch: Readonly<StorageHostConfig>;
|
|
26
23
|
}
|
|
27
24
|
|
|
28
|
-
const storageHostFrames: StorageHostFrame[] = [];
|
|
29
|
-
|
|
30
25
|
type StorageSchemaInitializer = (database: InternalStorageDatabase) => void;
|
|
31
26
|
interface RegisteredSchemaInitializer {
|
|
32
27
|
initialize: StorageSchemaInitializer;
|
|
33
28
|
priority: number;
|
|
34
29
|
}
|
|
35
30
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
interface StorageHostState {
|
|
32
|
+
configuredHost: Readonly<StorageHostConfig>;
|
|
33
|
+
fallbackDatabase: OwnedStorageDatabase | null;
|
|
34
|
+
fallbackDatabasePath: string | null;
|
|
35
|
+
frames: StorageHostFrame[];
|
|
36
|
+
schemaInitializers: RegisteredSchemaInitializer[];
|
|
37
|
+
initializedSchemas: WeakMap<InternalStorageDatabase, Set<StorageSchemaInitializer>>;
|
|
38
|
+
initializingDatabases: WeakSet<InternalStorageDatabase>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Which database an embedded Negotium uses is a property of the *process*, so
|
|
43
|
+
* the state deciding it is keyed off a registered symbol rather than held in
|
|
44
|
+
* module scope.
|
|
45
|
+
*
|
|
46
|
+
* Module scope would be equivalent only if every caller resolved to the same
|
|
47
|
+
* module instance, and in the published package they do not:
|
|
48
|
+
* `build-negotium-package.ts` emits most public entrypoints as independent
|
|
49
|
+
* graphs, so each gets a private copy of this file. A host that configured
|
|
50
|
+
* `negotium/storage` therefore left `negotium/agent-helpers` unconfigured, and
|
|
51
|
+
* that copy opened a second database under the default state directory — with
|
|
52
|
+
* no error, since both files are valid and writable.
|
|
53
|
+
*
|
|
54
|
+
* Merging those entrypoints into one graph looks tidier and does cut ~3MB from
|
|
55
|
+
* `dist`, but Bun 1.2.15 (the pinned version) emits a shared chunk's export
|
|
56
|
+
* list twice and the result is invalid ESM — "Cannot export a duplicate name".
|
|
57
|
+
* That is bundler output with no counterpart in this source, so there is
|
|
58
|
+
* nothing to de-duplicate here; it is fixed in Bun 1.3.14. Until the floor
|
|
59
|
+
* moves, correctness cannot depend on how the package is chunked.
|
|
60
|
+
*/
|
|
61
|
+
const STORAGE_HOST_STATE = Symbol.for("negotium.storage-host.state.v1");
|
|
62
|
+
|
|
63
|
+
function storageState(): StorageHostState {
|
|
64
|
+
const holder = globalThis as typeof globalThis & { [STORAGE_HOST_STATE]?: StorageHostState };
|
|
65
|
+
const existing = holder[STORAGE_HOST_STATE];
|
|
66
|
+
if (existing) return existing;
|
|
67
|
+
const created: StorageHostState = {
|
|
68
|
+
configuredHost: {},
|
|
69
|
+
fallbackDatabase: null,
|
|
70
|
+
fallbackDatabasePath: null,
|
|
71
|
+
frames: [],
|
|
72
|
+
schemaInitializers: [],
|
|
73
|
+
initializedSchemas: new WeakMap(),
|
|
74
|
+
initializingDatabases: new WeakSet(),
|
|
75
|
+
};
|
|
76
|
+
Object.defineProperty(holder, STORAGE_HOST_STATE, {
|
|
77
|
+
value: created,
|
|
78
|
+
enumerable: false,
|
|
79
|
+
writable: false,
|
|
80
|
+
configurable: false,
|
|
81
|
+
});
|
|
82
|
+
return created;
|
|
83
|
+
}
|
|
39
84
|
|
|
40
85
|
function envPath(name: string, fallback: string): string {
|
|
41
86
|
const value = process.env[name]?.trim();
|
|
@@ -114,41 +159,55 @@ export function initializeDatabase(database: InternalStorageDatabase): void {
|
|
|
114
159
|
|
|
115
160
|
function defaultDatabase(): InternalStorageDatabase {
|
|
116
161
|
const path = defaultSessionsDatabasePath();
|
|
117
|
-
|
|
118
|
-
if (fallbackDatabase) fallbackDatabase
|
|
162
|
+
const state = storageState();
|
|
163
|
+
if (state.fallbackDatabase && state.fallbackDatabasePath === path) return state.fallbackDatabase;
|
|
164
|
+
if (state.fallbackDatabase) state.fallbackDatabase.close();
|
|
119
165
|
mkdirSync(dirname(path), { recursive: true });
|
|
120
|
-
fallbackDatabase = new Database(path, { create: true }) as unknown as OwnedStorageDatabase;
|
|
121
|
-
fallbackDatabasePath = path;
|
|
122
|
-
initializeDatabase(fallbackDatabase);
|
|
123
|
-
return fallbackDatabase;
|
|
166
|
+
state.fallbackDatabase = new Database(path, { create: true }) as unknown as OwnedStorageDatabase;
|
|
167
|
+
state.fallbackDatabasePath = path;
|
|
168
|
+
initializeDatabase(state.fallbackDatabase);
|
|
169
|
+
return state.fallbackDatabase;
|
|
124
170
|
}
|
|
125
171
|
|
|
126
172
|
export function resolveStorageDatabase(): InternalStorageDatabase {
|
|
127
|
-
return (configuredHost.database ?? defaultDatabase()) as InternalStorageDatabase;
|
|
173
|
+
return (storageState().configuredHost.database ?? defaultDatabase()) as InternalStorageDatabase;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* The host-injected connection, or null when Negotium owns its own store.
|
|
178
|
+
*
|
|
179
|
+
* Unlike {@link resolveStorageDatabase} this never opens anything. Callers that
|
|
180
|
+
* manage their own short-lived connections need to know whether a host store
|
|
181
|
+
* exists *before* deciding to open one by path: opening it anyway is how an
|
|
182
|
+
* embedded Negotium ends up writing half its state into the host's database and
|
|
183
|
+
* the other half into `~/.negotium`, with nothing reporting an error.
|
|
184
|
+
*/
|
|
185
|
+
export function configuredStorageDatabase(): InternalStorageDatabase | null {
|
|
186
|
+
return (storageState().configuredHost.database as InternalStorageDatabase | undefined) ?? null;
|
|
128
187
|
}
|
|
129
188
|
|
|
130
189
|
export function resolveStorageDataDir(): string {
|
|
131
|
-
return configuredHost.dataDir ?? defaultDataDir();
|
|
190
|
+
return storageState().configuredHost.dataDir ?? defaultDataDir();
|
|
132
191
|
}
|
|
133
192
|
|
|
134
193
|
export function resolveStorageLogDir(): string {
|
|
135
|
-
return configuredHost.logDir ?? defaultLogDir();
|
|
194
|
+
return storageState().configuredHost.logDir ?? defaultLogDir();
|
|
136
195
|
}
|
|
137
196
|
|
|
138
197
|
export function resolveStorageSessionAsksDir(): string {
|
|
139
|
-
return configuredHost.sessionAsksDir ?? defaultSessionAsksDir();
|
|
198
|
+
return storageState().configuredHost.sessionAsksDir ?? defaultSessionAsksDir();
|
|
140
199
|
}
|
|
141
200
|
|
|
142
201
|
export function resolveStorageWorkspaceDir(): string {
|
|
143
|
-
return configuredHost.workspaceDir ?? defaultWorkspaceDir();
|
|
202
|
+
return storageState().configuredHost.workspaceDir ?? defaultWorkspaceDir();
|
|
144
203
|
}
|
|
145
204
|
|
|
146
205
|
export function resolveStorageSharedWikiDir(): string {
|
|
147
|
-
return configuredHost.sharedWikiDir ?? join(resolveStorageWorkspaceDir(), "wiki");
|
|
206
|
+
return storageState().configuredHost.sharedWikiDir ?? join(resolveStorageWorkspaceDir(), "wiki");
|
|
148
207
|
}
|
|
149
208
|
|
|
150
209
|
export function resolveStorageUsersLogDir(): string {
|
|
151
|
-
return configuredHost.usersLogDir ?? join(resolveStorageDataDir(), "users");
|
|
210
|
+
return storageState().configuredHost.usersLogDir ?? join(resolveStorageDataDir(), "users");
|
|
152
211
|
}
|
|
153
212
|
|
|
154
213
|
const STORAGE_PATH_KEYS = [
|
|
@@ -176,11 +235,9 @@ function normalizeStorageHostPatch(options: StorageHostConfig): Readonly<Storage
|
|
|
176
235
|
}
|
|
177
236
|
|
|
178
237
|
function refreshConfiguredHost(): void {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
...storageHostFrames.filter((frame) => frame.active).map((frame) => frame.patch),
|
|
183
|
-
),
|
|
238
|
+
const state = storageState();
|
|
239
|
+
state.configuredHost = Object.freeze(
|
|
240
|
+
Object.assign({}, ...state.frames.filter((frame) => frame.active).map((frame) => frame.patch)),
|
|
184
241
|
);
|
|
185
242
|
}
|
|
186
243
|
|
|
@@ -193,52 +250,57 @@ function refreshConfiguredHost(): void {
|
|
|
193
250
|
*/
|
|
194
251
|
export function configureStorageHost(options: StorageHostConfig): () => void {
|
|
195
252
|
const frame: StorageHostFrame = { active: true, patch: normalizeStorageHostPatch(options) };
|
|
196
|
-
|
|
253
|
+
storageState().frames.push(frame);
|
|
197
254
|
refreshConfiguredHost();
|
|
198
255
|
return () => {
|
|
199
256
|
if (!frame.active) return;
|
|
200
257
|
frame.active = false;
|
|
201
|
-
const
|
|
202
|
-
|
|
258
|
+
const frames = storageState().frames;
|
|
259
|
+
const index = frames.indexOf(frame);
|
|
260
|
+
if (index >= 0) frames.splice(index, 1);
|
|
203
261
|
refreshConfiguredHost();
|
|
204
262
|
};
|
|
205
263
|
}
|
|
206
264
|
|
|
207
265
|
/** Remove every configured host layer and restore standalone fallbacks. */
|
|
208
266
|
export function resetStorageHost(): void {
|
|
209
|
-
|
|
210
|
-
|
|
267
|
+
const frames = storageState().frames;
|
|
268
|
+
for (const frame of frames) frame.active = false;
|
|
269
|
+
frames.length = 0;
|
|
211
270
|
refreshConfiguredHost();
|
|
212
271
|
}
|
|
213
272
|
|
|
214
273
|
/** Close only Negotium's fallback connection. Injected connections are borrowed. */
|
|
215
274
|
export function closeStorageDatabase(): void {
|
|
216
|
-
|
|
217
|
-
fallbackDatabase
|
|
218
|
-
fallbackDatabase
|
|
219
|
-
|
|
275
|
+
const state = storageState();
|
|
276
|
+
if (!state.fallbackDatabase) return;
|
|
277
|
+
state.fallbackDatabase.close();
|
|
278
|
+
state.fallbackDatabase = null;
|
|
279
|
+
state.fallbackDatabasePath = null;
|
|
220
280
|
}
|
|
221
281
|
|
|
222
282
|
export function registerStorageSchemaInitializer(
|
|
223
283
|
initialize: StorageSchemaInitializer,
|
|
224
284
|
priority = 100,
|
|
225
285
|
): void {
|
|
226
|
-
|
|
227
|
-
|
|
286
|
+
const initializers = storageState().schemaInitializers;
|
|
287
|
+
initializers.push({ initialize, priority });
|
|
288
|
+
initializers.sort((a, b) => a.priority - b.priority);
|
|
228
289
|
}
|
|
229
290
|
|
|
230
291
|
export function ensureStorageSchemas(
|
|
231
292
|
database: InternalStorageDatabase = resolveStorageDatabase(),
|
|
232
293
|
): void {
|
|
233
|
-
|
|
234
|
-
|
|
294
|
+
const state = storageState();
|
|
295
|
+
if (state.initializingDatabases.has(database)) return;
|
|
296
|
+
let initialized = state.initializedSchemas.get(database);
|
|
235
297
|
if (!initialized) {
|
|
236
298
|
initialized = new Set();
|
|
237
|
-
initializedSchemas.set(database, initialized);
|
|
299
|
+
state.initializedSchemas.set(database, initialized);
|
|
238
300
|
}
|
|
239
|
-
initializingDatabases.add(database);
|
|
301
|
+
state.initializingDatabases.add(database);
|
|
240
302
|
try {
|
|
241
|
-
for (const entry of schemaInitializers) {
|
|
303
|
+
for (const entry of state.schemaInitializers) {
|
|
242
304
|
if (initialized.has(entry.initialize)) continue;
|
|
243
305
|
// Mark first so a migration that calls through the public db proxy does
|
|
244
306
|
// not recursively invoke itself. Remove on failure so the next call can retry.
|
|
@@ -251,7 +313,7 @@ export function ensureStorageSchemas(
|
|
|
251
313
|
}
|
|
252
314
|
}
|
|
253
315
|
} finally {
|
|
254
|
-
initializingDatabases.delete(database);
|
|
316
|
+
state.initializingDatabases.delete(database);
|
|
255
317
|
}
|
|
256
318
|
}
|
|
257
319
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { chmodSync, mkdirSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { VAULT_MASTER_KEY } from "#platform/config";
|
|
4
4
|
import { Database } from "#storage/sqlite";
|
|
5
|
+
import { resolveStorageDataDir } from "#storage/storage-host";
|
|
5
6
|
import { decryptVaultValue, encryptVaultValue } from "#storage/vault-crypto";
|
|
6
7
|
|
|
7
8
|
export type VaultDatabase = Pick<InstanceType<typeof Database>, "exec" | "prepare">;
|
|
@@ -72,7 +73,14 @@ function openVaultDatabase(dataDir: string): VaultDatabase {
|
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
function activeVaultDatabase(): VaultDatabase {
|
|
75
|
-
if (!vaultDb)
|
|
76
|
+
if (!vaultDb) {
|
|
77
|
+
// `resolveStorageDataDir()`, not the import-time `DATA_DIR` constant: an
|
|
78
|
+
// embedding host configures its data directory during bootstrap, and any
|
|
79
|
+
// vault access that happens before `configureVaultStorage()` runs would
|
|
80
|
+
// otherwise pin the vault to Negotium's own state directory for the life of
|
|
81
|
+
// the process — silently, since both paths are valid and writable.
|
|
82
|
+
vaultDb = openVaultDatabase(resolveStorageDataDir());
|
|
83
|
+
}
|
|
76
84
|
return vaultDb;
|
|
77
85
|
}
|
|
78
86
|
|
|
@@ -83,7 +91,8 @@ export function configureVaultStorage(options: VaultStorageOptions): () => void
|
|
|
83
91
|
}
|
|
84
92
|
const previousDb = vaultDb;
|
|
85
93
|
const previousMasterKey = vaultMasterKey;
|
|
86
|
-
const configuredDb =
|
|
94
|
+
const configuredDb =
|
|
95
|
+
options.database ?? openVaultDatabase(options.dataDir ?? resolveStorageDataDir());
|
|
87
96
|
if (options.database) initializeVaultDatabase(configuredDb);
|
|
88
97
|
vaultDb = configuredDb;
|
|
89
98
|
vaultMasterKey = options.masterKey ?? VAULT_MASTER_KEY;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const NEGOTIUM_VERSION = "0.2.
|
|
1
|
+
export const NEGOTIUM_VERSION = "0.2.21";
|
package/dist/runtime-helpers.js
CHANGED
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
2
18
|
var __require = import.meta.require;
|
|
3
19
|
|
|
4
20
|
// ../../packages/core/src/agents/deep-map.ts
|
|
@@ -534,11 +550,12 @@ function versionAtLeast(actualVersion, minimumVersion) {
|
|
|
534
550
|
}
|
|
535
551
|
return true;
|
|
536
552
|
}
|
|
553
|
+
var BROWSER_RS_VERSION_PROBE_TIMEOUT_MS = 15000;
|
|
537
554
|
function browserRsMeetsMinimumVersion(candidate) {
|
|
538
555
|
try {
|
|
539
556
|
const output = execFileSync(candidate, ["--version"], {
|
|
540
557
|
encoding: "utf8",
|
|
541
|
-
timeout:
|
|
558
|
+
timeout: BROWSER_RS_VERSION_PROBE_TIMEOUT_MS,
|
|
542
559
|
stdio: ["ignore", "pipe", "ignore"]
|
|
543
560
|
}).trim();
|
|
544
561
|
const match = output.match(/^browser-rs (\d+)\.(\d+)\.(\d+)$/);
|
|
@@ -764,12 +781,29 @@ if (isBun) {
|
|
|
764
781
|
}
|
|
765
782
|
|
|
766
783
|
// ../../packages/core/src/storage/storage-host.ts
|
|
767
|
-
var
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
784
|
+
var STORAGE_HOST_STATE = Symbol.for("negotium.storage-host.state.v1");
|
|
785
|
+
function storageState() {
|
|
786
|
+
const holder = globalThis;
|
|
787
|
+
const existing = holder[STORAGE_HOST_STATE];
|
|
788
|
+
if (existing)
|
|
789
|
+
return existing;
|
|
790
|
+
const created = {
|
|
791
|
+
configuredHost: {},
|
|
792
|
+
fallbackDatabase: null,
|
|
793
|
+
fallbackDatabasePath: null,
|
|
794
|
+
frames: [],
|
|
795
|
+
schemaInitializers: [],
|
|
796
|
+
initializedSchemas: new WeakMap,
|
|
797
|
+
initializingDatabases: new WeakSet
|
|
798
|
+
};
|
|
799
|
+
Object.defineProperty(holder, STORAGE_HOST_STATE, {
|
|
800
|
+
value: created,
|
|
801
|
+
enumerable: false,
|
|
802
|
+
writable: false,
|
|
803
|
+
configurable: false
|
|
804
|
+
});
|
|
805
|
+
return created;
|
|
806
|
+
}
|
|
773
807
|
function envPath(name, fallback) {
|
|
774
808
|
const value = process.env[name]?.trim();
|
|
775
809
|
return resolve2(value || fallback);
|
|
@@ -814,37 +848,40 @@ function initializeDatabase(database) {
|
|
|
814
848
|
}
|
|
815
849
|
function defaultDatabase() {
|
|
816
850
|
const path = defaultSessionsDatabasePath();
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
851
|
+
const state = storageState();
|
|
852
|
+
if (state.fallbackDatabase && state.fallbackDatabasePath === path)
|
|
853
|
+
return state.fallbackDatabase;
|
|
854
|
+
if (state.fallbackDatabase)
|
|
855
|
+
state.fallbackDatabase.close();
|
|
821
856
|
mkdirSync4(dirname3(path), { recursive: true });
|
|
822
|
-
fallbackDatabase = new Database(path, { create: true });
|
|
823
|
-
fallbackDatabasePath = path;
|
|
824
|
-
initializeDatabase(fallbackDatabase);
|
|
825
|
-
return fallbackDatabase;
|
|
857
|
+
state.fallbackDatabase = new Database(path, { create: true });
|
|
858
|
+
state.fallbackDatabasePath = path;
|
|
859
|
+
initializeDatabase(state.fallbackDatabase);
|
|
860
|
+
return state.fallbackDatabase;
|
|
826
861
|
}
|
|
827
862
|
function resolveStorageDatabase() {
|
|
828
|
-
return configuredHost.database ?? defaultDatabase();
|
|
863
|
+
return storageState().configuredHost.database ?? defaultDatabase();
|
|
829
864
|
}
|
|
830
865
|
function resolveStorageDataDir() {
|
|
831
|
-
return configuredHost.dataDir ?? defaultDataDir();
|
|
866
|
+
return storageState().configuredHost.dataDir ?? defaultDataDir();
|
|
832
867
|
}
|
|
833
868
|
function registerStorageSchemaInitializer(initialize, priority = 100) {
|
|
834
|
-
|
|
835
|
-
|
|
869
|
+
const initializers = storageState().schemaInitializers;
|
|
870
|
+
initializers.push({ initialize, priority });
|
|
871
|
+
initializers.sort((a, b) => a.priority - b.priority);
|
|
836
872
|
}
|
|
837
873
|
function ensureStorageSchemas(database = resolveStorageDatabase()) {
|
|
838
|
-
|
|
874
|
+
const state = storageState();
|
|
875
|
+
if (state.initializingDatabases.has(database))
|
|
839
876
|
return;
|
|
840
|
-
let initialized = initializedSchemas.get(database);
|
|
877
|
+
let initialized = state.initializedSchemas.get(database);
|
|
841
878
|
if (!initialized) {
|
|
842
879
|
initialized = new Set;
|
|
843
|
-
initializedSchemas.set(database, initialized);
|
|
880
|
+
state.initializedSchemas.set(database, initialized);
|
|
844
881
|
}
|
|
845
|
-
initializingDatabases.add(database);
|
|
882
|
+
state.initializingDatabases.add(database);
|
|
846
883
|
try {
|
|
847
|
-
for (const entry of schemaInitializers) {
|
|
884
|
+
for (const entry of state.schemaInitializers) {
|
|
848
885
|
if (initialized.has(entry.initialize))
|
|
849
886
|
continue;
|
|
850
887
|
initialized.add(entry.initialize);
|
|
@@ -856,7 +893,7 @@ function ensureStorageSchemas(database = resolveStorageDatabase()) {
|
|
|
856
893
|
}
|
|
857
894
|
}
|
|
858
895
|
} finally {
|
|
859
|
-
initializingDatabases.delete(database);
|
|
896
|
+
state.initializingDatabases.delete(database);
|
|
860
897
|
}
|
|
861
898
|
}
|
|
862
899
|
var internalStorageDatabase = new Proxy({}, {
|
|
@@ -1558,4 +1595,4 @@ export {
|
|
|
1558
1595
|
CLAUDE_EFFORT_VALUES
|
|
1559
1596
|
};
|
|
1560
1597
|
|
|
1561
|
-
//# debugId=
|
|
1598
|
+
//# debugId=6B371802D724F43F64756E2164756E21
|