negotium 0.2.3 → 0.2.5
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 +3366 -3048
- package/dist/agent-helpers.js.map +49 -48
- package/dist/background-bash.js +1 -3
- package/dist/background-bash.js.map +4 -4
- package/dist/browser-runtime.js +137 -23
- package/dist/browser-runtime.js.map +11 -10
- package/dist/chunk-qmbcbhyy.js +2407 -0
- package/dist/chunk-qmbcbhyy.js.map +35 -0
- package/dist/hosted-agent.js +56 -39
- package/dist/hosted-agent.js.map +13 -13
- package/dist/main.js +3276 -2808
- package/dist/main.js.map +54 -51
- package/dist/mcp-factories.js +6471 -6148
- package/dist/mcp-factories.js.map +49 -48
- package/dist/prompts.js +1 -3
- package/dist/prompts.js.map +4 -4
- package/dist/query-runtime.js +22 -4
- package/dist/query-runtime.js.map +6 -6
- package/dist/registry.js +169 -213
- package/dist/registry.js.map +8 -9
- package/dist/rollout.js +1 -1
- package/dist/runtime/src/agents/api-topic-agent-switch.ts +6 -12
- package/dist/runtime/src/agents/claude-provider.ts +2 -2
- package/dist/runtime/src/agents/codex-app-server.ts +136 -0
- package/dist/runtime/src/agents/codex-native-multi-agent.ts +1 -1
- package/dist/runtime/src/agents/codex-provider.ts +22 -11
- package/dist/runtime/src/agents/codex-registry.ts +9 -17
- package/dist/runtime/src/agents/contracts.ts +2 -0
- package/dist/runtime/src/agents/execution-host.ts +6 -0
- package/dist/runtime/src/agents/fork.ts +24 -18
- package/dist/runtime/src/agents/idle-archiver.ts +7 -2
- package/dist/runtime/src/agents/index.ts +21 -15
- package/dist/runtime/src/agents/maestro-provider.ts +1 -1
- package/dist/runtime/src/agents/maestro-registry.ts +26 -7
- package/dist/runtime/src/agents/memory-archive-policy.ts +20 -0
- package/dist/runtime/src/agents/rollout/codex.ts +31 -23
- package/dist/runtime/src/agents/self-config-core.ts +2 -4
- package/dist/runtime/src/agents/topic-agent-switch.ts +4 -10
- package/dist/runtime/src/application/submit-runtime-gateway-turn.ts +77 -19
- package/dist/runtime/src/application/switch-topic-model.ts +7 -10
- package/dist/runtime/src/index.ts +4 -0
- package/dist/runtime/src/mcp/factories/session-comm.ts +12 -0
- package/dist/runtime/src/mcp/session-comm/default-host.ts +16 -0
- package/dist/runtime/src/mcp/session-comm/server.ts +25 -0
- package/dist/runtime/src/mcp/wiki-server.ts +19 -12
- package/dist/runtime/src/platform/config.ts +4 -3
- package/dist/runtime/src/platform/log-rotation.ts +45 -0
- package/dist/runtime/src/platform/mcp-config.ts +9 -3
- package/dist/runtime/src/platform/playwright/manager.ts +57 -1
- package/dist/runtime/src/platform/playwright/profile-management.ts +33 -0
- package/dist/runtime/src/platform/playwright/public-runtime.ts +5 -0
- package/dist/runtime/src/platform/playwright/vault-broker.ts +9 -10
- package/dist/runtime/src/query/active-rooms.ts +7 -1
- package/dist/runtime/src/runtime/errors.ts +1 -0
- package/dist/runtime/src/runtime/turn-event-stream.ts +46 -1
- package/dist/runtime/src/runtime/turn-runner.ts +176 -100
- package/dist/runtime/src/runtime/user-turn-envelope.ts +19 -0
- package/dist/runtime/src/storage/browser-profiles.ts +35 -10
- package/dist/runtime/src/storage/runtime-gateway-submissions.ts +12 -3
- package/dist/runtime/src/storage/runtime-turn-requests.ts +68 -7
- package/dist/runtime/src/storage/storage-host.ts +31 -5
- package/dist/runtime/src/storage/vault.ts +30 -2
- package/dist/runtime/src/topics/derive.ts +38 -2
- package/dist/runtime/src/topics/lifecycle.ts +12 -2
- package/dist/runtime/src/types.ts +2 -0
- package/dist/runtime/src/version.ts +1 -1
- package/dist/runtime-helpers.js +22 -4
- package/dist/runtime-helpers.js.map +5 -5
- package/dist/storage.js +22 -2
- package/dist/storage.js.map +5 -5
- package/dist/types/packages/core/src/agents/api-topic-agent-switch.d.ts +1 -1
- package/dist/types/packages/core/src/agents/codex-app-server.d.ts +13 -0
- package/dist/types/packages/core/src/agents/codex-native-multi-agent.d.ts +1 -0
- package/dist/types/packages/core/src/agents/contracts.d.ts +2 -0
- package/dist/types/packages/core/src/agents/execution-host.d.ts +2 -0
- package/dist/types/packages/core/src/agents/fork.d.ts +8 -7
- package/dist/types/packages/core/src/agents/memory-archive-policy.d.ts +10 -0
- package/dist/types/packages/core/src/agents/rollout/codex.d.ts +4 -2
- package/dist/types/packages/core/src/agents/self-config-core.d.ts +1 -1
- package/dist/types/packages/core/src/mcp/factories/session-comm.d.ts +1 -0
- package/dist/types/packages/core/src/platform/config.d.ts +0 -1
- package/dist/types/packages/core/src/platform/mcp-config.d.ts +3 -0
- package/dist/types/packages/core/src/platform/playwright/manager.d.ts +15 -0
- package/dist/types/packages/core/src/platform/playwright/profile-management.d.ts +8 -0
- package/dist/types/packages/core/src/platform/playwright/public-runtime.d.ts +2 -1
- package/dist/types/packages/core/src/query/active-rooms.d.ts +7 -1
- package/dist/types/packages/core/src/runtime/turn-runner.d.ts +42 -1
- package/dist/types/packages/core/src/runtime/user-turn-envelope.d.ts +7 -0
- package/dist/types/packages/core/src/storage/browser-profiles.d.ts +3 -1
- package/dist/types/packages/core/src/storage/runtime-turn-requests.d.ts +17 -0
- package/dist/types/packages/core/src/storage/storage-host.d.ts +1 -0
- package/dist/types/packages/core/src/storage/vault.d.ts +8 -0
- package/dist/types/packages/core/src/types.d.ts +2 -0
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/dist/vault.js +19 -5
- package/dist/vault.js.map +5 -5
- package/package.json +2 -2
- package/dist/chunk-hsqgb2hw.js +0 -1223
- package/dist/chunk-hsqgb2hw.js.map +0 -19
|
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import {
|
|
3
3
|
flattenUserTurnAttachments,
|
|
4
4
|
legacyUserTurnEnvelope,
|
|
5
|
-
|
|
5
|
+
renderUserTurnBatch,
|
|
6
6
|
type UserTurnEnvelope,
|
|
7
7
|
} from "#runtime/user-turn-envelope";
|
|
8
8
|
import { db } from "#storage/forum-db";
|
|
@@ -31,10 +31,14 @@ export interface RuntimeUserTurnExecution {
|
|
|
31
31
|
bridgeSessionFromHistory?: boolean;
|
|
32
32
|
peerBridge?: PeerRuntimeBridgeContext;
|
|
33
33
|
from?: string;
|
|
34
|
+
/** Vault namespace for this turn, independent from the local execution principal. */
|
|
35
|
+
vaultUserId?: string;
|
|
34
36
|
/** Newly accepted user texts not yet recorded in the unified conversation log. */
|
|
35
37
|
conversationPrompts?: string[];
|
|
36
38
|
/** Number of leading userMessages already present in the unified conversation log. */
|
|
37
39
|
loggedUserMessageCount?: number;
|
|
40
|
+
/** Native session whose provider output proves it accepted this request's user turn. */
|
|
41
|
+
providerSessionId?: string;
|
|
38
42
|
/** Request ids whose ordered messages were folded into this replacement. */
|
|
39
43
|
supersededRequestIds?: string[];
|
|
40
44
|
}
|
|
@@ -175,7 +179,11 @@ function rowToRequest(row: RuntimeUserTurnRequestRow): RuntimeUserTurnRequest {
|
|
|
175
179
|
(Array.isArray((item as UserTurnEnvelope).attachments) &&
|
|
176
180
|
(item as UserTurnEnvelope).attachments?.every(
|
|
177
181
|
(attachment) => typeof attachment === "string",
|
|
178
|
-
)))
|
|
182
|
+
))) &&
|
|
183
|
+
((item as UserTurnEnvelope).actorUserId === undefined ||
|
|
184
|
+
typeof (item as UserTurnEnvelope).actorUserId === "string") &&
|
|
185
|
+
((item as UserTurnEnvelope).actorLabel === undefined ||
|
|
186
|
+
typeof (item as UserTurnEnvelope).actorLabel === "string"),
|
|
179
187
|
)
|
|
180
188
|
) {
|
|
181
189
|
userMessages = parsed as UserTurnEnvelope[];
|
|
@@ -285,6 +293,8 @@ export function mergeRuntimeUserTurnRequest(input: {
|
|
|
285
293
|
execution: RuntimeUserTurnExecution;
|
|
286
294
|
topicEpoch: number;
|
|
287
295
|
alreadyIncludedRequestIds?: string[];
|
|
296
|
+
/** Requests already committed to the provider's native session; keep their lineage, not prompts. */
|
|
297
|
+
omitRequestIds?: string[];
|
|
288
298
|
}): { requestId: string; supersededRequestIds: string[] } {
|
|
289
299
|
const now = Date.now();
|
|
290
300
|
return db
|
|
@@ -295,14 +305,23 @@ export function mergeRuntimeUserTurnRequest(input: {
|
|
|
295
305
|
)
|
|
296
306
|
.all(input.topicId);
|
|
297
307
|
const previous = rows.map(rowToRequest);
|
|
308
|
+
const omittedRequestIds = new Set([
|
|
309
|
+
...(input.omitRequestIds ?? []),
|
|
310
|
+
...previous
|
|
311
|
+
.filter((request) => Boolean(request.execution?.providerSessionId))
|
|
312
|
+
.map((request) => request.requestId),
|
|
313
|
+
]);
|
|
314
|
+
const carried = previous.filter((request) => !omittedRequestIds.has(request.requestId));
|
|
298
315
|
const alreadyIncludedRequestIds = new Set(input.alreadyIncludedRequestIds ?? []);
|
|
299
|
-
const alreadyIncludedMessages =
|
|
316
|
+
const alreadyIncludedMessages = carried
|
|
300
317
|
.filter((request) => alreadyIncludedRequestIds.has(request.requestId))
|
|
301
318
|
.flatMap((request) => request.userMessages);
|
|
302
319
|
const includedPrefixMatches = alreadyIncludedMessages.every((message, index) => {
|
|
303
320
|
const candidate = input.userMessages[index];
|
|
304
321
|
return (
|
|
305
322
|
candidate?.prompt === message.prompt &&
|
|
323
|
+
candidate.actorUserId === message.actorUserId &&
|
|
324
|
+
candidate.actorLabel === message.actorLabel &&
|
|
306
325
|
JSON.stringify(candidate.attachments ?? []) === JSON.stringify(message.attachments ?? [])
|
|
307
326
|
);
|
|
308
327
|
});
|
|
@@ -310,7 +329,7 @@ export function mergeRuntimeUserTurnRequest(input: {
|
|
|
310
329
|
? alreadyIncludedMessages.length
|
|
311
330
|
: 0;
|
|
312
331
|
const userMessages = [
|
|
313
|
-
...
|
|
332
|
+
...carried.flatMap((request) => request.userMessages),
|
|
314
333
|
...input.userMessages.slice(alreadyIncludedMessageCount),
|
|
315
334
|
];
|
|
316
335
|
const incomingLoggedCount = Math.min(
|
|
@@ -318,7 +337,7 @@ export function mergeRuntimeUserTurnRequest(input: {
|
|
|
318
337
|
input.userMessages.length,
|
|
319
338
|
);
|
|
320
339
|
const loggedUserMessageCount =
|
|
321
|
-
|
|
340
|
+
carried.reduce((count, request) => count + loggedMessageCount(request), 0) +
|
|
322
341
|
Math.max(0, incomingLoggedCount - alreadyIncludedMessageCount);
|
|
323
342
|
const execution: RuntimeUserTurnExecution = {
|
|
324
343
|
...input.execution,
|
|
@@ -335,7 +354,7 @@ export function mergeRuntimeUserTurnRequest(input: {
|
|
|
335
354
|
.slice(loggedUserMessageCount)
|
|
336
355
|
.map((message) => message.prompt),
|
|
337
356
|
};
|
|
338
|
-
const sessionBase =
|
|
357
|
+
const sessionBase = carried.find(
|
|
339
358
|
(request) => request.execution?.sessionIdSpecified,
|
|
340
359
|
)?.execution;
|
|
341
360
|
if (sessionBase?.sessionIdSpecified) {
|
|
@@ -355,7 +374,7 @@ export function mergeRuntimeUserTurnRequest(input: {
|
|
|
355
374
|
input.requestId,
|
|
356
375
|
input.topicId,
|
|
357
376
|
input.userId,
|
|
358
|
-
|
|
377
|
+
renderUserTurnBatch(userMessages),
|
|
359
378
|
JSON.stringify(userMessages),
|
|
360
379
|
attachments?.length ? JSON.stringify(attachments) : null,
|
|
361
380
|
input.allowAutoContinue ? 1 : 0,
|
|
@@ -371,6 +390,46 @@ export function mergeRuntimeUserTurnRequest(input: {
|
|
|
371
390
|
.immediate();
|
|
372
391
|
}
|
|
373
392
|
|
|
393
|
+
/**
|
|
394
|
+
* Record that provider output proves the native session contains this durable
|
|
395
|
+
* turn. A session-id event alone is insufficient: some providers publish it
|
|
396
|
+
* before appending the user prompt. This marker lets a different runtime
|
|
397
|
+
* process omit only a demonstrably committed prompt when it merges a steering
|
|
398
|
+
* message after preemption.
|
|
399
|
+
*
|
|
400
|
+
* Match only the exact request id. A late session event from a superseded turn
|
|
401
|
+
* must not mark its replacement (whose prompts have not reached the provider).
|
|
402
|
+
*/
|
|
403
|
+
export function markRuntimeUserTurnProviderSessionObserved(
|
|
404
|
+
topicId: string,
|
|
405
|
+
requestId: string,
|
|
406
|
+
sessionId: string,
|
|
407
|
+
): boolean {
|
|
408
|
+
return db
|
|
409
|
+
.transaction(() => {
|
|
410
|
+
const row = db
|
|
411
|
+
.query<RuntimeUserTurnRequestRow, [string, string]>(
|
|
412
|
+
"SELECT * FROM runtime_user_turn_requests WHERE topic_id = ? AND request_id = ?",
|
|
413
|
+
)
|
|
414
|
+
.get(topicId, requestId);
|
|
415
|
+
if (!row) return false;
|
|
416
|
+
const request = rowToRequest(row);
|
|
417
|
+
const execution: RuntimeUserTurnExecution = {
|
|
418
|
+
...request.execution,
|
|
419
|
+
providerSessionId: sessionId,
|
|
420
|
+
};
|
|
421
|
+
const result = db
|
|
422
|
+
.query(
|
|
423
|
+
`UPDATE runtime_user_turn_requests
|
|
424
|
+
SET execution_json = ?
|
|
425
|
+
WHERE topic_id = ? AND request_id = ?`,
|
|
426
|
+
)
|
|
427
|
+
.run(JSON.stringify(execution), topicId, requestId);
|
|
428
|
+
return result.changes === 1;
|
|
429
|
+
})
|
|
430
|
+
.immediate();
|
|
431
|
+
}
|
|
432
|
+
|
|
374
433
|
export function markRuntimeUserTurnMessagesLogged(
|
|
375
434
|
topicId: string,
|
|
376
435
|
requestId: string,
|
|
@@ -392,6 +451,8 @@ export function markRuntimeUserTurnMessagesLogged(
|
|
|
392
451
|
const candidate = request.userMessages[index];
|
|
393
452
|
return (
|
|
394
453
|
candidate?.prompt === message.prompt &&
|
|
454
|
+
candidate.actorUserId === message.actorUserId &&
|
|
455
|
+
candidate.actorLabel === message.actorLabel &&
|
|
395
456
|
JSON.stringify(candidate.attachments ?? []) ===
|
|
396
457
|
JSON.stringify(message.attachments ?? [])
|
|
397
458
|
);
|
|
@@ -67,16 +67,42 @@ function defaultSessionsDatabasePath(): string {
|
|
|
67
67
|
return envPath("SESSIONS_DB_PATH", join(resolveStorageDataDir(), "sessions.db"));
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
const SQLITE_INIT_RETRY_MS = 25;
|
|
71
|
+
const SQLITE_INIT_TIMEOUT_MS = 5_000;
|
|
72
|
+
const SQLITE_INIT_SLEEP = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
|
|
73
|
+
|
|
74
|
+
function isSqliteBusy(error: unknown): boolean {
|
|
75
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
76
|
+
return /database is (?:locked|busy)|SQLITE_(?:BUSY|LOCKED)/i.test(message);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function execWithBusyRetry(
|
|
80
|
+
database: InternalStorageDatabase,
|
|
81
|
+
sql: string,
|
|
82
|
+
timeoutMs = SQLITE_INIT_TIMEOUT_MS,
|
|
83
|
+
): void {
|
|
84
|
+
const deadline = Date.now() + timeoutMs;
|
|
85
|
+
while (true) {
|
|
86
|
+
try {
|
|
87
|
+
database.exec(sql);
|
|
88
|
+
return;
|
|
89
|
+
} catch (error) {
|
|
90
|
+
if (!isSqliteBusy(error) || Date.now() >= deadline) throw error;
|
|
91
|
+
Atomics.wait(SQLITE_INIT_SLEEP, 0, 0, Math.min(SQLITE_INIT_RETRY_MS, deadline - Date.now()));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function initializeDatabase(database: InternalStorageDatabase): void {
|
|
71
97
|
// busy_timeout FIRST. Switching to WAL needs a brief exclusive lock, so when
|
|
72
98
|
// two processes open the same database at the same moment — the node daemon
|
|
73
99
|
// and an adapter, or two MCP servers — one of them hits SQLITE_BUSY. With the
|
|
74
100
|
// timeout configured afterwards there was no retry budget in effect for the
|
|
75
|
-
// statement that needed it most
|
|
76
|
-
//
|
|
77
|
-
//
|
|
101
|
+
// statement that needed it most. Older Bun releases can still surface
|
|
102
|
+
// SQLITE_BUSY immediately for journal_mode, so keep a bounded host-level
|
|
103
|
+
// retry around that one exclusive transition as well.
|
|
78
104
|
database.exec("PRAGMA busy_timeout = 5000");
|
|
79
|
-
database
|
|
105
|
+
execWithBusyRetry(database, "PRAGMA journal_mode = WAL");
|
|
80
106
|
database.exec("PRAGMA foreign_keys = ON");
|
|
81
107
|
database.exec("PRAGMA wal_autocheckpoint = 1000");
|
|
82
108
|
try {
|
|
@@ -143,6 +143,33 @@ export function vaultListWithValues(userId: string): VaultEntryWithValue[] {
|
|
|
143
143
|
}));
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Return only rows that can still be authenticated with the active master key.
|
|
148
|
+
*
|
|
149
|
+
* This is intentionally limited to redaction indexes: one damaged credential
|
|
150
|
+
* must not disable unrelated tools, while direct access to that credential
|
|
151
|
+
* continues to fail closed through vaultGetValue().
|
|
152
|
+
*/
|
|
153
|
+
export function vaultListDecryptableValues(userId: string): VaultEntryWithValue[] {
|
|
154
|
+
const rows = activeVaultDatabase()
|
|
155
|
+
.prepare("SELECT key, description, value FROM vault WHERE user_id = ? ORDER BY key")
|
|
156
|
+
.all(userId) as VaultEntryWithValue[];
|
|
157
|
+
const entries: VaultEntryWithValue[] = [];
|
|
158
|
+
for (const row of rows) {
|
|
159
|
+
try {
|
|
160
|
+
entries.push({
|
|
161
|
+
key: row.key,
|
|
162
|
+
description: row.description,
|
|
163
|
+
value: decryptRow(userId, row.key, row.value),
|
|
164
|
+
});
|
|
165
|
+
} catch {
|
|
166
|
+
// A value that cannot be authenticated cannot be exposed by substitution,
|
|
167
|
+
// so there is no plaintext form available to add to the redaction index.
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return entries;
|
|
171
|
+
}
|
|
172
|
+
|
|
146
173
|
export function vaultSet(userId: string, key: string, value: string, description = ""): void {
|
|
147
174
|
const normalizedKey = normalizeVaultKey(key);
|
|
148
175
|
activeVaultDatabase()
|
|
@@ -203,10 +230,11 @@ export interface VaultSubstitutionResult {
|
|
|
203
230
|
|
|
204
231
|
/** Replace {{KEY}} placeholders and report which credentials were consumed. */
|
|
205
232
|
export function vaultSubstituteDetailed(userId: string, text: string): VaultSubstitutionResult {
|
|
206
|
-
const entries = new Map
|
|
233
|
+
const entries = new Map<string, string | undefined>();
|
|
207
234
|
const usedKeys = new Set<string>();
|
|
208
235
|
const substituted = text.replace(/\{\{([^}]+)\}\}/g, (match, rawKey: string) => {
|
|
209
236
|
const key = normalizeVaultKey(rawKey);
|
|
237
|
+
if (!entries.has(key)) entries.set(key, vaultGetValue(userId, key));
|
|
210
238
|
const value = entries.get(key);
|
|
211
239
|
if (value === undefined) return match;
|
|
212
240
|
usedKeys.add(key);
|
|
@@ -248,7 +276,7 @@ function encodedSecretForms(value: string): string[] {
|
|
|
248
276
|
|
|
249
277
|
/** Scrub raw and common encoded forms before tool output reaches a model. */
|
|
250
278
|
export function redactVaultSecrets(userId: string, text: string): string {
|
|
251
|
-
const candidates =
|
|
279
|
+
const candidates = vaultListDecryptableValues(userId)
|
|
252
280
|
.flatMap((entry) => encodedSecretForms(entry.value).map((form) => ({ form, key: entry.key })))
|
|
253
281
|
.sort((a, b) => b.form.length - a.form.length || a.key.localeCompare(b.key));
|
|
254
282
|
if (candidates.length === 0) return text;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { createHash, randomUUID } from "node:crypto";
|
|
11
11
|
import { mkdirSync, rmSync, unlinkSync } from "node:fs";
|
|
12
|
-
import { cleanupAgentFork, type ForkHandle } from "#agents/fork";
|
|
12
|
+
import { cleanupAgentFork, type ForkHandle, forkAgentSession } from "#agents/fork";
|
|
13
13
|
import { resolveCompactionExecution, resolveModelForAgent } from "#agents/model-catalog";
|
|
14
14
|
import { getRegistry, getRegistryOperations } from "#agents/registry";
|
|
15
15
|
import { WsHub } from "#bus";
|
|
@@ -26,6 +26,7 @@ import { getApiTopicConfig, setApiTopicConfig } from "#storage/api-topic-config"
|
|
|
26
26
|
import {
|
|
27
27
|
findTopicTitleConflict,
|
|
28
28
|
getTopic,
|
|
29
|
+
getTopicSessionId,
|
|
29
30
|
inferTopicKind,
|
|
30
31
|
isTopicVisible,
|
|
31
32
|
listTopics,
|
|
@@ -312,6 +313,11 @@ async function createDerivedTopicImpl(
|
|
|
312
313
|
(subagent?.agent ? undefined : sourceConfig?.model) ??
|
|
313
314
|
derived.defaultModel;
|
|
314
315
|
const rolloutModel = resolveModelForAgent(agent, requestedRolloutModel, registry);
|
|
316
|
+
const sourceRolloutModel = resolveModelForAgent(
|
|
317
|
+
agent,
|
|
318
|
+
sourceConfig?.model ?? topic.defaultModel,
|
|
319
|
+
registry,
|
|
320
|
+
);
|
|
315
321
|
const requestedRolloutEffort = subagent?.agent
|
|
316
322
|
? registry.defaultEffort
|
|
317
323
|
: (sourceConfig?.effort ?? derived.defaultEffort);
|
|
@@ -323,7 +329,37 @@ async function createDerivedTopicImpl(
|
|
|
323
329
|
|
|
324
330
|
if (copyHistory) {
|
|
325
331
|
if (!forkSnapshot) throw new Error("fork snapshot was not captured");
|
|
326
|
-
const
|
|
332
|
+
const parentSessionId = getTopicSessionId(sourceTopicId);
|
|
333
|
+
const cachePreservingNativeFork =
|
|
334
|
+
(agent === "codex" || agent === "maestro") && sourceRolloutModel === rolloutModel;
|
|
335
|
+
if (cachePreservingNativeFork && parentSessionId) {
|
|
336
|
+
try {
|
|
337
|
+
rollbackHandle = await forkAgentSession({
|
|
338
|
+
agent,
|
|
339
|
+
parentSessionId,
|
|
340
|
+
cwd,
|
|
341
|
+
userId,
|
|
342
|
+
topicName: topic.title,
|
|
343
|
+
title,
|
|
344
|
+
model: rolloutModel,
|
|
345
|
+
...(rolloutEffort ? { effort: rolloutEffort } : {}),
|
|
346
|
+
});
|
|
347
|
+
sessionId = rollbackHandle.forkId;
|
|
348
|
+
logger.info(
|
|
349
|
+
{ sourceTopicId, derivedTopicId: derived.id, parentSessionId, sessionId },
|
|
350
|
+
"createDerivedTopic: native provider fork materialized",
|
|
351
|
+
);
|
|
352
|
+
} catch (error) {
|
|
353
|
+
logger.warn(
|
|
354
|
+
{ err: error, sourceTopicId, derivedTopicId: derived.id, parentSessionId },
|
|
355
|
+
"createDerivedTopic: native provider fork failed; using snapshot fallback",
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
// Successful native forks retain the exact provider prefix. Once that
|
|
360
|
+
// path is unavailable, cache continuity is already lost; keep a small
|
|
361
|
+
// snapshot verbatim and compact only an oversized fallback.
|
|
362
|
+
const compactionRequired = !sessionId && shouldCompactForkEntries(forkSnapshot.entries);
|
|
327
363
|
if (compactionRequired) {
|
|
328
364
|
try {
|
|
329
365
|
compactedForkEntries = await createCompactedRolloutEntries(
|
|
@@ -9,7 +9,10 @@ import { rmSync } from "node:fs";
|
|
|
9
9
|
import { runArchiverTurn } from "#agents/archiver";
|
|
10
10
|
import { cancelIdleArchiveForTopic } from "#agents/idle-archiver";
|
|
11
11
|
import { cancelSubagentWatchForDeletedTopic } from "#agents/mcp-tools/spawn-subagent";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
MIN_MEMORY_ARCHIVE_EXCHANGES,
|
|
14
|
+
meetsMemoryArchiveExchangeThreshold,
|
|
15
|
+
} from "#agents/memory-archive-policy";
|
|
13
16
|
import { type PurgeSessionRef, purgeTopicLogs } from "#agents/topic-cleanup";
|
|
14
17
|
import { WsHub } from "#bus";
|
|
15
18
|
import { killBgBash } from "#platform/background-bash/manager";
|
|
@@ -238,7 +241,14 @@ async function deleteTopicCascadeImpl(
|
|
|
238
241
|
}
|
|
239
242
|
}
|
|
240
243
|
|
|
241
|
-
if (
|
|
244
|
+
if (
|
|
245
|
+
archived &&
|
|
246
|
+
meetsMemoryArchiveExchangeThreshold(
|
|
247
|
+
archived.exchangeCount,
|
|
248
|
+
MIN_MEMORY_ARCHIVE_EXCHANGES,
|
|
249
|
+
topic,
|
|
250
|
+
)
|
|
251
|
+
) {
|
|
242
252
|
try {
|
|
243
253
|
const memoryTopic = getTopicMemoryOrigin(topicId) ?? topic;
|
|
244
254
|
runArchiverTurn({
|
|
@@ -176,6 +176,8 @@ export interface AgentQueryOptions {
|
|
|
176
176
|
cwd: string;
|
|
177
177
|
systemPrompt: string;
|
|
178
178
|
userId?: string;
|
|
179
|
+
/** Credential namespace when it differs from the execution principal. */
|
|
180
|
+
vaultUserId?: string;
|
|
179
181
|
session?: string;
|
|
180
182
|
playwrightPort?: number;
|
|
181
183
|
playwrightCapability?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const NEGOTIUM_VERSION = "0.2.
|
|
1
|
+
export const NEGOTIUM_VERSION = "0.2.5";
|
package/dist/runtime-helpers.js
CHANGED
|
@@ -447,7 +447,6 @@ var BROWSER_DIR = resolveLocalStateDir("NEGOTIUM_BROWSER_DIR", "browser");
|
|
|
447
447
|
var BROWSER_PROFILES_DIR = resolve(BROWSER_DIR, "profiles");
|
|
448
448
|
var BINARIES_DIR = resolve(STATE_DIR, "binaries");
|
|
449
449
|
var SECRETS_DIR = resolve(STATE_DIR, "secrets");
|
|
450
|
-
var CONTEXTS_DIR = resolve(STATE_DIR, "data", "contexts");
|
|
451
450
|
var DM_WORKSPACE_DIR = resolve(STATE_DIR, "data", "dm");
|
|
452
451
|
var SESSION_WORKSPACE_DIR = resolve(STATE_DIR, "data", "sessions");
|
|
453
452
|
var CLAUDE_EXECUTABLE_ENV = envText("NEGOTIUM_CLAUDE_EXECUTABLE");
|
|
@@ -590,7 +589,6 @@ mkdirSync2(WORKSPACE_DIR, { recursive: true });
|
|
|
590
589
|
mkdirSync2(TOPIC_WORKSPACE_DIR, { recursive: true });
|
|
591
590
|
mkdirSync2(SHARED_WIKI_DIR, { recursive: true });
|
|
592
591
|
mkdirSync2(CRON_WORKSPACE_DIR, { recursive: true });
|
|
593
|
-
mkdirSync2(CONTEXTS_DIR, { recursive: true });
|
|
594
592
|
mkdirSync2(DM_WORKSPACE_DIR, { recursive: true });
|
|
595
593
|
mkdirSync2(SESSION_WORKSPACE_DIR, { recursive: true });
|
|
596
594
|
mkdirSync2(BROWSER_DIR, { recursive: true });
|
|
@@ -695,9 +693,29 @@ function defaultDataDir() {
|
|
|
695
693
|
function defaultSessionsDatabasePath() {
|
|
696
694
|
return envPath("SESSIONS_DB_PATH", join2(resolveStorageDataDir(), "sessions.db"));
|
|
697
695
|
}
|
|
696
|
+
var SQLITE_INIT_RETRY_MS = 25;
|
|
697
|
+
var SQLITE_INIT_TIMEOUT_MS = 5000;
|
|
698
|
+
var SQLITE_INIT_SLEEP = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
|
|
699
|
+
function isSqliteBusy(error) {
|
|
700
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
701
|
+
return /database is (?:locked|busy)|SQLITE_(?:BUSY|LOCKED)/i.test(message);
|
|
702
|
+
}
|
|
703
|
+
function execWithBusyRetry(database, sql, timeoutMs = SQLITE_INIT_TIMEOUT_MS) {
|
|
704
|
+
const deadline = Date.now() + timeoutMs;
|
|
705
|
+
while (true) {
|
|
706
|
+
try {
|
|
707
|
+
database.exec(sql);
|
|
708
|
+
return;
|
|
709
|
+
} catch (error) {
|
|
710
|
+
if (!isSqliteBusy(error) || Date.now() >= deadline)
|
|
711
|
+
throw error;
|
|
712
|
+
Atomics.wait(SQLITE_INIT_SLEEP, 0, 0, Math.min(SQLITE_INIT_RETRY_MS, deadline - Date.now()));
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
698
716
|
function initializeDatabase(database) {
|
|
699
717
|
database.exec("PRAGMA busy_timeout = 5000");
|
|
700
|
-
database
|
|
718
|
+
execWithBusyRetry(database, "PRAGMA journal_mode = WAL");
|
|
701
719
|
database.exec("PRAGMA foreign_keys = ON");
|
|
702
720
|
database.exec("PRAGMA wal_autocheckpoint = 1000");
|
|
703
721
|
try {
|
|
@@ -1114,4 +1132,4 @@ export {
|
|
|
1114
1132
|
CLAUDE_EFFORT_VALUES
|
|
1115
1133
|
};
|
|
1116
1134
|
|
|
1117
|
-
//# debugId=
|
|
1135
|
+
//# debugId=38CF47A0C3C063A564756E2164756E21
|