negotium 0.2.4 → 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 +241 -127
- package/dist/agent-helpers.js.map +19 -19
- package/dist/background-bash.js +1 -3
- package/dist/background-bash.js.map +4 -4
- package/dist/browser-runtime.js +1 -3
- package/dist/browser-runtime.js.map +4 -4
- package/dist/{chunk-1s9ryz8g.js → chunk-qmbcbhyy.js} +20 -12
- package/dist/chunk-qmbcbhyy.js.map +35 -0
- package/dist/hosted-agent.js +13 -10
- package/dist/hosted-agent.js.map +10 -10
- package/dist/main.js +598 -438
- package/dist/main.js.map +30 -29
- package/dist/mcp-factories.js +238 -123
- package/dist/mcp-factories.js.map +18 -18
- package/dist/prompts.js +1 -3
- package/dist/prompts.js.map +4 -4
- package/dist/query-runtime.js +1 -3
- package/dist/query-runtime.js.map +5 -5
- package/dist/registry.js +3 -3
- package/dist/registry.js.map +2 -2
- 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-provider.ts +1 -0
- package/dist/runtime/src/agents/index.ts +19 -14
- package/dist/runtime/src/agents/maestro-provider.ts +1 -1
- package/dist/runtime/src/agents/rollout/codex.ts +25 -18
- 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/wiki-server.ts +19 -12
- package/dist/runtime/src/platform/config.ts +0 -2
- package/dist/runtime/src/platform/log-rotation.ts +45 -0
- package/dist/runtime/src/platform/mcp-config.ts +9 -3
- 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/runtime-gateway-submissions.ts +12 -3
- package/dist/runtime/src/storage/runtime-turn-requests.ts +68 -7
- package/dist/runtime/src/types.ts +2 -0
- package/dist/runtime/src/version.ts +1 -1
- package/dist/runtime-helpers.js +1 -3
- package/dist/runtime-helpers.js.map +4 -4
- package/dist/storage.js.map +1 -1
- package/dist/types/packages/core/src/agents/api-topic-agent-switch.d.ts +1 -1
- package/dist/types/packages/core/src/agents/self-config-core.d.ts +1 -1
- 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/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/runtime-turn-requests.d.ts +17 -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 +1 -3
- package/dist/vault.js.map +4 -4
- package/package.json +1 -1
- package/dist/chunk-1s9ryz8g.js.map +0 -35
package/dist/agent-helpers.js
CHANGED
|
@@ -331,7 +331,7 @@ function resolveDefaultModel(agent, registryDefaultModel) {
|
|
|
331
331
|
function codexAuthFilePath() {
|
|
332
332
|
return process.env.NEGOTIUM_CODEX_AUTH_FILE || join(process.env.CODEX_HOME || join(homedir(), ".codex"), "auth.json");
|
|
333
333
|
}
|
|
334
|
-
var HOME, PROJECT_ROOT, STATE_DIR_ENV, STATE_DIR, WORKSPACE_DIR, TOPIC_WORKSPACE_DIR, SHARED_WIKI_DIR, CRON_WORKSPACE_DIR, BROWSER_DIR, BROWSER_PROFILES_DIR, BINARIES_DIR, SECRETS_DIR,
|
|
334
|
+
var HOME, PROJECT_ROOT, STATE_DIR_ENV, STATE_DIR, WORKSPACE_DIR, TOPIC_WORKSPACE_DIR, SHARED_WIKI_DIR, CRON_WORKSPACE_DIR, BROWSER_DIR, BROWSER_PROFILES_DIR, BINARIES_DIR, SECRETS_DIR, DM_WORKSPACE_DIR, SESSION_WORKSPACE_DIR, CLAUDE_EXECUTABLE_ENV, CLAUDE_EXECUTABLE, DEFAULT_OUTPUT_LANGUAGE = "English", BROWSER_RS_VERSION = "v0.1.16", BROWSER_RS_MIN_SECURE_VERSION = "0.1.15", BROWSER_RS_BIN, PLAYWRIGHT_MCP_BIN, TSX_BIN, TSX_LOADER, TSCONFIG_PATH, SESSION_COMM_SERVER, TASK_SERVER, BROWSER_MCP_SSE_PROXY_SERVER, CANONICAL_MCP_PROXY_SERVER, WIKI_SERVER, TOKEN_STATS_SERVER, COMPACTION_LOG_SERVER, SYSTEM_HEALTH_SERVER, AGENT_HEALTH_SERVER, BACKGROUND_BASH_SERVER, VAULT_SERVER, BG_BASH_BASE_PORT, BG_BASH_MAX_PORT, RUNTIME_MCP_SECRET, NODE_CONTROL_TOKEN, VAULT_MASTER_KEY, NEGOTIUM_PORT, hostname, DATA_DIR, LOG_DIR, UPLOADS_DIR, VAULT_DIR, SESSIONS_DB, DEBUG_FILE, USERS_LOG_DIR, RUN_DIR, PROGRESS_DIR, DM_CMD_DIR, DM_RESP_DIR, SESSION_INBOX_DIR, SESSION_ASKS_DIR, PLAYWRIGHT_BASE_PORT, PLAYWRIGHT_MAX_PORT, PLAYWRIGHT_PORTS_DIR, ACTIVE_QUERY_STALE_MS, AGENTS_PROMPTS_DIR, RESOURCES_DIR, FILE_TAG_REGEX, MODEL_SONNET = "claude-sonnet-5", MODEL_OPUS = "claude-opus-5", MODEL_FABLE = "claude-fable-5", FALLBACK_AGENT, SESSION_AGENT, GATEWAY_AGENT, FALLBACK_MODEL, SESSION_MODEL, GATEWAY_MODEL, FFMPEG_BIN, FFPROBE_BIN, PYTHON_BIN, FASTER_WHISPER_WRAPPER, WHISPER_MODEL, TESSERACT_BIN, PDFTOTEXT_BIN, _envMaxTellDepth, MAX_TELL_DEPTH;
|
|
335
335
|
var init_config = __esm(() => {
|
|
336
336
|
init_config_helpers();
|
|
337
337
|
init_logger();
|
|
@@ -348,7 +348,6 @@ var init_config = __esm(() => {
|
|
|
348
348
|
BROWSER_PROFILES_DIR = resolve2(BROWSER_DIR, "profiles");
|
|
349
349
|
BINARIES_DIR = resolve2(STATE_DIR, "binaries");
|
|
350
350
|
SECRETS_DIR = resolve2(STATE_DIR, "secrets");
|
|
351
|
-
CONTEXTS_DIR = resolve2(STATE_DIR, "data", "contexts");
|
|
352
351
|
DM_WORKSPACE_DIR = resolve2(STATE_DIR, "data", "dm");
|
|
353
352
|
SESSION_WORKSPACE_DIR = resolve2(STATE_DIR, "data", "sessions");
|
|
354
353
|
CLAUDE_EXECUTABLE_ENV = envText("NEGOTIUM_CLAUDE_EXECUTABLE");
|
|
@@ -408,7 +407,6 @@ var init_config = __esm(() => {
|
|
|
408
407
|
mkdirSync(TOPIC_WORKSPACE_DIR, { recursive: true });
|
|
409
408
|
mkdirSync(SHARED_WIKI_DIR, { recursive: true });
|
|
410
409
|
mkdirSync(CRON_WORKSPACE_DIR, { recursive: true });
|
|
411
|
-
mkdirSync(CONTEXTS_DIR, { recursive: true });
|
|
412
410
|
mkdirSync(DM_WORKSPACE_DIR, { recursive: true });
|
|
413
411
|
mkdirSync(SESSION_WORKSPACE_DIR, { recursive: true });
|
|
414
412
|
mkdirSync(BROWSER_DIR, { recursive: true });
|
|
@@ -951,6 +949,7 @@ function getManagerMcpServers(opts) {
|
|
|
951
949
|
function getForumMcpServers(opts) {
|
|
952
950
|
const {
|
|
953
951
|
userId,
|
|
952
|
+
vaultUserId,
|
|
954
953
|
session,
|
|
955
954
|
topicId,
|
|
956
955
|
subagentParentTopicId,
|
|
@@ -981,6 +980,7 @@ function getForumMcpServers(opts) {
|
|
|
981
980
|
};
|
|
982
981
|
const base = buildScope("forum", {
|
|
983
982
|
userId,
|
|
983
|
+
vaultUserId,
|
|
984
984
|
session,
|
|
985
985
|
topicId,
|
|
986
986
|
subagentParentTopicId,
|
|
@@ -1074,6 +1074,7 @@ function getMcpServersForQuery(opts) {
|
|
|
1074
1074
|
}
|
|
1075
1075
|
return getForumMcpServers({
|
|
1076
1076
|
userId: opts.userId || "local",
|
|
1077
|
+
vaultUserId: opts.vaultUserId,
|
|
1077
1078
|
session: opts.session || "default",
|
|
1078
1079
|
topicId: opts.topicId,
|
|
1079
1080
|
subagentParentTopicId: opts.subagentParentTopicId,
|
|
@@ -1275,11 +1276,11 @@ var init_mcp_config = __esm(() => {
|
|
|
1275
1276
|
vault: {
|
|
1276
1277
|
...commonRuntimeMcpPolicy("vault"),
|
|
1277
1278
|
build(ctx) {
|
|
1278
|
-
const { userId, agent } = ctx;
|
|
1279
|
-
const args = [`--user-id=${userId}`];
|
|
1279
|
+
const { userId, vaultUserId, agent } = ctx;
|
|
1280
|
+
const args = [`--user-id=${vaultUserId ?? userId}`];
|
|
1280
1281
|
if (agent !== "codex")
|
|
1281
1282
|
args.push("--list-only=true");
|
|
1282
|
-
return buildBuiltinMcpServer("vault", ctx, () => buildStdioMcpServer(agent, VAULT_SERVER, args));
|
|
1283
|
+
return buildBuiltinMcpServer("vault", { ...ctx, userId: vaultUserId ?? userId }, () => buildStdioMcpServer(agent, VAULT_SERVER, args));
|
|
1283
1284
|
}
|
|
1284
1285
|
}
|
|
1285
1286
|
};
|
|
@@ -1617,6 +1618,14 @@ function renderUserPromptBatch(prompts) {
|
|
|
1617
1618
|
].join(`
|
|
1618
1619
|
`);
|
|
1619
1620
|
}
|
|
1621
|
+
function renderUserTurnPrompt(message) {
|
|
1622
|
+
const rawLabel = message.actorLabel?.trim() || message.actorUserId?.trim();
|
|
1623
|
+
const label = rawLabel?.replace(/[\r\n\t]+/g, " ").replace(/\[/g, "(").replace(/\]/g, ")").replace(/\s{2,}/g, " ");
|
|
1624
|
+
return label ? `[@${label}]: ${message.prompt}` : message.prompt;
|
|
1625
|
+
}
|
|
1626
|
+
function renderUserTurnBatch(messages) {
|
|
1627
|
+
return renderUserPromptBatch(messages.map(renderUserTurnPrompt));
|
|
1628
|
+
}
|
|
1620
1629
|
|
|
1621
1630
|
// ../../packages/core/src/agents/rollout/shared.ts
|
|
1622
1631
|
import { mkdirSync as mkdirSync3 } from "fs";
|
|
@@ -2138,7 +2147,7 @@ var init_claude_registry = __esm(() => {
|
|
|
2138
2147
|
});
|
|
2139
2148
|
|
|
2140
2149
|
// ../../packages/core/src/version.ts
|
|
2141
|
-
var NEGOTIUM_VERSION = "0.2.
|
|
2150
|
+
var NEGOTIUM_VERSION = "0.2.5";
|
|
2142
2151
|
|
|
2143
2152
|
// ../../packages/core/src/agents/codex-native-multi-agent.ts
|
|
2144
2153
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -2434,6 +2443,18 @@ function patchEnvContext(envContext, cwd, currentDate, timezone) {
|
|
|
2434
2443
|
}
|
|
2435
2444
|
}
|
|
2436
2445
|
}
|
|
2446
|
+
function codexRolloutPath(threadId, fallback) {
|
|
2447
|
+
const threadCreatedAt = decodeUuidV7Timestamp(threadId);
|
|
2448
|
+
const createdAt = threadCreatedAt === null ? fallback : new Date(threadCreatedAt);
|
|
2449
|
+
const yyyy = String(createdAt.getFullYear());
|
|
2450
|
+
const mm = String(createdAt.getMonth() + 1).padStart(2, "0");
|
|
2451
|
+
const dd = String(createdAt.getDate()).padStart(2, "0");
|
|
2452
|
+
const hh = String(createdAt.getHours()).padStart(2, "0");
|
|
2453
|
+
const min = String(createdAt.getMinutes()).padStart(2, "0");
|
|
2454
|
+
const ss = String(createdAt.getSeconds()).padStart(2, "0");
|
|
2455
|
+
const dir = join6(codexSessionsDir(), yyyy, mm, dd);
|
|
2456
|
+
return join6(dir, `rollout-${yyyy}-${mm}-${dd}T${hh}-${min}-${ss}-${threadId}.jsonl`);
|
|
2457
|
+
}
|
|
2437
2458
|
function canonicalFilePath(path) {
|
|
2438
2459
|
const absolute = resolve4(path);
|
|
2439
2460
|
try {
|
|
@@ -2746,12 +2767,7 @@ function writeCodexRollout(opts) {
|
|
|
2746
2767
|
type: "event_msg",
|
|
2747
2768
|
payload: { type: "task_complete" }
|
|
2748
2769
|
});
|
|
2749
|
-
const
|
|
2750
|
-
const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
2751
|
-
const dd = String(now.getUTCDate()).padStart(2, "0");
|
|
2752
|
-
const tsStr = tsIso.replace(/[:.]/g, "-").slice(0, 19);
|
|
2753
|
-
const dir = join6(codexSessionsDir(), String(yyyy), mm, dd);
|
|
2754
|
-
const path = join6(dir, `rollout-${tsStr}-${threadId}.jsonl`);
|
|
2770
|
+
const path = codexRolloutPath(threadId, now);
|
|
2755
2771
|
if (opts.threadId) {
|
|
2756
2772
|
sweepPriorRolloutsForThread(opts.threadId);
|
|
2757
2773
|
}
|
|
@@ -3930,7 +3946,7 @@ async function* claudeProvider(opts) {
|
|
|
3930
3946
|
}
|
|
3931
3947
|
};
|
|
3932
3948
|
}
|
|
3933
|
-
const userId = opts.userId ?? "";
|
|
3949
|
+
const userId = opts.vaultUserId ?? opts.userId ?? "";
|
|
3934
3950
|
const withVault = substituteClaudeToolInput(userId, tool_name, tool_input);
|
|
3935
3951
|
if (JSON.stringify(withVault) === JSON.stringify(tool_input)) {
|
|
3936
3952
|
return { continue: true };
|
|
@@ -3950,7 +3966,7 @@ async function* claudeProvider(opts) {
|
|
|
3950
3966
|
hooks: [
|
|
3951
3967
|
async (input) => {
|
|
3952
3968
|
const { tool_response } = input;
|
|
3953
|
-
const userId = opts.userId ?? "";
|
|
3969
|
+
const userId = opts.vaultUserId ?? opts.userId ?? "";
|
|
3954
3970
|
const redacted = deepMapStrings(tool_response, (value) => redactHostedSecrets(userId, value));
|
|
3955
3971
|
if (JSON.stringify(redacted) === JSON.stringify(tool_response)) {
|
|
3956
3972
|
return { continue: true };
|
|
@@ -5364,6 +5380,8 @@ async function* codexProvider(opts) {
|
|
|
5364
5380
|
throw err;
|
|
5365
5381
|
if (!currentSessionId || !isMissingCodexRolloutError(err))
|
|
5366
5382
|
throw err;
|
|
5383
|
+
if (opts.userId && opts.session)
|
|
5384
|
+
throw err;
|
|
5367
5385
|
logger.warn({ staleSessionId: currentSessionId, err: errMsg(err) }, "codexProvider: stale/missing rollout, restarting fresh thread");
|
|
5368
5386
|
thread = codex.startThread(threadOptions);
|
|
5369
5387
|
prompt = promptForThread(opts, true);
|
|
@@ -5663,7 +5681,7 @@ function maestroProvider(opts) {
|
|
|
5663
5681
|
if (opts.agent !== undefined && opts.agent !== "maestro") {
|
|
5664
5682
|
throw new Error(`maestroProvider: unexpected agent "${opts.agent}", expected "maestro"`);
|
|
5665
5683
|
}
|
|
5666
|
-
const userId = opts.userId ?? "";
|
|
5684
|
+
const userId = opts.vaultUserId ?? opts.userId ?? "";
|
|
5667
5685
|
setMcpResolver(hostedMcpServers);
|
|
5668
5686
|
const callerDisallowedTools = opts.disallowedTools ?? [];
|
|
5669
5687
|
const callerToolHooks = opts.toolHooks ?? [];
|
|
@@ -5782,16 +5800,16 @@ async function resolveSessionFileMissing(agent, sessionId, cwd) {
|
|
|
5782
5800
|
}
|
|
5783
5801
|
async function maybeRebuildSession(opts) {
|
|
5784
5802
|
if (!opts.sessionId)
|
|
5785
|
-
return
|
|
5803
|
+
return "ready";
|
|
5786
5804
|
try {
|
|
5787
5805
|
const missing = await resolveSessionFileMissing(opts.agent, opts.sessionId, opts.cwd);
|
|
5788
5806
|
if (!missing)
|
|
5789
|
-
return
|
|
5807
|
+
return "ready";
|
|
5790
5808
|
logger.info({ agent: opts.agent, sessionId: opts.sessionId, topic: opts.session }, "session file missing \u2014 rebuilding from unified log");
|
|
5791
5809
|
const entries = readConversation(opts.userId, opts.session);
|
|
5792
5810
|
if (entries.length === 0) {
|
|
5793
5811
|
logger.info({ agent: opts.agent, sessionId: opts.sessionId }, "no unified log entries to rebuild \u2014 starting fresh session");
|
|
5794
|
-
return
|
|
5812
|
+
return "fresh";
|
|
5795
5813
|
}
|
|
5796
5814
|
const operations = getRegistryOperations(opts.agent);
|
|
5797
5815
|
const result = operations.writeRollout({
|
|
@@ -5802,19 +5820,25 @@ async function maybeRebuildSession(opts) {
|
|
|
5802
5820
|
...opts.effort ? { effort: opts.effort } : {}
|
|
5803
5821
|
});
|
|
5804
5822
|
logger.info({ agent: opts.agent, sessionId: result.sessionId, rolloutPath: result.rolloutPath }, "session rebuilt from unified log");
|
|
5805
|
-
return
|
|
5823
|
+
return "ready";
|
|
5806
5824
|
} catch (err) {
|
|
5807
|
-
logger.warn({ err, agent: opts.agent, sessionId: opts.sessionId }, "session rebuild failed \u2014
|
|
5808
|
-
return
|
|
5825
|
+
logger.warn({ err, agent: opts.agent, sessionId: opts.sessionId }, "session rebuild failed \u2014 preserving resume key");
|
|
5826
|
+
return "failed";
|
|
5809
5827
|
}
|
|
5810
5828
|
}
|
|
5811
5829
|
async function* runAgent(opts) {
|
|
5812
5830
|
const recording = shouldRecordTurn(opts);
|
|
5813
5831
|
const dispatchOpts = { ...opts };
|
|
5814
5832
|
if (hasSessionRepairContext(dispatchOpts)) {
|
|
5815
|
-
const
|
|
5816
|
-
if (
|
|
5833
|
+
const repair = await maybeRebuildSession(dispatchOpts);
|
|
5834
|
+
if (repair === "fresh") {
|
|
5817
5835
|
dispatchOpts.sessionId = undefined;
|
|
5836
|
+
} else if (repair === "failed") {
|
|
5837
|
+
yield {
|
|
5838
|
+
type: "error",
|
|
5839
|
+
content: "Provider session could not be rebuilt from conversation history; the existing session was preserved for retry."
|
|
5840
|
+
};
|
|
5841
|
+
return;
|
|
5818
5842
|
}
|
|
5819
5843
|
}
|
|
5820
5844
|
const taskScope = resolveTaskEventScope(dispatchOpts);
|
|
@@ -11783,7 +11807,7 @@ function rowToRequest(row) {
|
|
|
11783
11807
|
if (row.user_messages_json) {
|
|
11784
11808
|
try {
|
|
11785
11809
|
const parsed = JSON.parse(row.user_messages_json);
|
|
11786
|
-
if (Array.isArray(parsed) && parsed.length > 0 && parsed.every((item) => item && typeof item === "object" && typeof item.prompt === "string" && (item.attachments === undefined || Array.isArray(item.attachments) && item.attachments?.every((attachment) => typeof attachment === "string")))) {
|
|
11810
|
+
if (Array.isArray(parsed) && parsed.length > 0 && parsed.every((item) => item && typeof item === "object" && typeof item.prompt === "string" && (item.attachments === undefined || Array.isArray(item.attachments) && item.attachments?.every((attachment) => typeof attachment === "string")) && (item.actorUserId === undefined || typeof item.actorUserId === "string") && (item.actorLabel === undefined || typeof item.actorLabel === "string"))) {
|
|
11787
11811
|
userMessages = parsed;
|
|
11788
11812
|
}
|
|
11789
11813
|
} catch {
|
|
@@ -11835,19 +11859,24 @@ function mergeRuntimeUserTurnRequest(input) {
|
|
|
11835
11859
|
return db.transaction(() => {
|
|
11836
11860
|
const rows = db.query("SELECT * FROM runtime_user_turn_requests WHERE topic_id = ? ORDER BY created_at ASC, rowid ASC").all(input.topicId);
|
|
11837
11861
|
const previous = rows.map(rowToRequest);
|
|
11862
|
+
const omittedRequestIds = new Set([
|
|
11863
|
+
...input.omitRequestIds ?? [],
|
|
11864
|
+
...previous.filter((request) => Boolean(request.execution?.providerSessionId)).map((request) => request.requestId)
|
|
11865
|
+
]);
|
|
11866
|
+
const carried = previous.filter((request) => !omittedRequestIds.has(request.requestId));
|
|
11838
11867
|
const alreadyIncludedRequestIds = new Set(input.alreadyIncludedRequestIds ?? []);
|
|
11839
|
-
const alreadyIncludedMessages =
|
|
11868
|
+
const alreadyIncludedMessages = carried.filter((request) => alreadyIncludedRequestIds.has(request.requestId)).flatMap((request) => request.userMessages);
|
|
11840
11869
|
const includedPrefixMatches = alreadyIncludedMessages.every((message, index) => {
|
|
11841
11870
|
const candidate = input.userMessages[index];
|
|
11842
|
-
return candidate?.prompt === message.prompt && JSON.stringify(candidate.attachments ?? []) === JSON.stringify(message.attachments ?? []);
|
|
11871
|
+
return candidate?.prompt === message.prompt && candidate.actorUserId === message.actorUserId && candidate.actorLabel === message.actorLabel && JSON.stringify(candidate.attachments ?? []) === JSON.stringify(message.attachments ?? []);
|
|
11843
11872
|
});
|
|
11844
11873
|
const alreadyIncludedMessageCount = includedPrefixMatches ? alreadyIncludedMessages.length : 0;
|
|
11845
11874
|
const userMessages = [
|
|
11846
|
-
...
|
|
11875
|
+
...carried.flatMap((request) => request.userMessages),
|
|
11847
11876
|
...input.userMessages.slice(alreadyIncludedMessageCount)
|
|
11848
11877
|
];
|
|
11849
11878
|
const incomingLoggedCount = Math.min(Math.max(0, input.execution.loggedUserMessageCount ?? 0), input.userMessages.length);
|
|
11850
|
-
const loggedUserMessageCount =
|
|
11879
|
+
const loggedUserMessageCount = carried.reduce((count, request) => count + loggedMessageCount(request), 0) + Math.max(0, incomingLoggedCount - alreadyIncludedMessageCount);
|
|
11851
11880
|
const execution = {
|
|
11852
11881
|
...input.execution,
|
|
11853
11882
|
loggedUserMessageCount,
|
|
@@ -11859,7 +11888,7 @@ function mergeRuntimeUserTurnRequest(input) {
|
|
|
11859
11888
|
],
|
|
11860
11889
|
conversationPrompts: userMessages.slice(loggedUserMessageCount).map((message) => message.prompt)
|
|
11861
11890
|
};
|
|
11862
|
-
const sessionBase =
|
|
11891
|
+
const sessionBase = carried.find((request) => request.execution?.sessionIdSpecified)?.execution;
|
|
11863
11892
|
if (sessionBase?.sessionIdSpecified) {
|
|
11864
11893
|
execution.sessionId = sessionBase.sessionId;
|
|
11865
11894
|
execution.sessionIdSpecified = true;
|
|
@@ -11870,13 +11899,29 @@ function mergeRuntimeUserTurnRequest(input) {
|
|
|
11870
11899
|
(request_id, topic_id, user_id, prompt, user_messages_json, attachments_json,
|
|
11871
11900
|
allow_auto_continue, execution_json, topic_epoch, created_at,
|
|
11872
11901
|
status, claimed_by, claimed_at, running_query_id)
|
|
11873
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', NULL, NULL, NULL)`).run(input.requestId, input.topicId, input.userId,
|
|
11902
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', NULL, NULL, NULL)`).run(input.requestId, input.topicId, input.userId, renderUserTurnBatch(userMessages), JSON.stringify(userMessages), attachments?.length ? JSON.stringify(attachments) : null, input.allowAutoContinue ? 1 : 0, JSON.stringify(execution), input.topicEpoch, now);
|
|
11874
11903
|
return {
|
|
11875
11904
|
requestId: input.requestId,
|
|
11876
11905
|
supersededRequestIds: previous.map((request) => request.requestId)
|
|
11877
11906
|
};
|
|
11878
11907
|
}).immediate();
|
|
11879
11908
|
}
|
|
11909
|
+
function markRuntimeUserTurnProviderSessionObserved(topicId, requestId, sessionId) {
|
|
11910
|
+
return db.transaction(() => {
|
|
11911
|
+
const row = db.query("SELECT * FROM runtime_user_turn_requests WHERE topic_id = ? AND request_id = ?").get(topicId, requestId);
|
|
11912
|
+
if (!row)
|
|
11913
|
+
return false;
|
|
11914
|
+
const request = rowToRequest(row);
|
|
11915
|
+
const execution = {
|
|
11916
|
+
...request.execution,
|
|
11917
|
+
providerSessionId: sessionId
|
|
11918
|
+
};
|
|
11919
|
+
const result = db.query(`UPDATE runtime_user_turn_requests
|
|
11920
|
+
SET execution_json = ?
|
|
11921
|
+
WHERE topic_id = ? AND request_id = ?`).run(JSON.stringify(execution), topicId, requestId);
|
|
11922
|
+
return result.changes === 1;
|
|
11923
|
+
}).immediate();
|
|
11924
|
+
}
|
|
11880
11925
|
function markRuntimeUserTurnMessagesLogged(topicId, requestId, ownerId, loggedUserMessages) {
|
|
11881
11926
|
if (loggedUserMessages.length === 0)
|
|
11882
11927
|
return false;
|
|
@@ -11884,7 +11929,7 @@ function markRuntimeUserTurnMessagesLogged(topicId, requestId, ownerId, loggedUs
|
|
|
11884
11929
|
const requests = db.query("SELECT * FROM runtime_user_turn_requests WHERE topic_id = ? ORDER BY created_at ASC, rowid ASC").all(topicId).map(rowToRequest);
|
|
11885
11930
|
const hasLoggedPrefix = (request2) => loggedUserMessages.length <= request2.userMessages.length && loggedUserMessages.every((message, index) => {
|
|
11886
11931
|
const candidate = request2.userMessages[index];
|
|
11887
|
-
return candidate?.prompt === message.prompt && JSON.stringify(candidate.attachments ?? []) === JSON.stringify(message.attachments ?? []);
|
|
11932
|
+
return candidate?.prompt === message.prompt && candidate.actorUserId === message.actorUserId && candidate.actorLabel === message.actorLabel && JSON.stringify(candidate.attachments ?? []) === JSON.stringify(message.attachments ?? []);
|
|
11888
11933
|
});
|
|
11889
11934
|
const request = requests.find((candidate) => candidate.requestId === requestId && candidate.claimedBy === ownerId && hasLoggedPrefix(candidate)) ?? requests.find((candidate) => candidate.execution?.supersededRequestIds?.includes(requestId) && hasLoggedPrefix(candidate));
|
|
11890
11935
|
if (!request)
|
|
@@ -14141,7 +14186,7 @@ function classifyAgentError(err2, agent) {
|
|
|
14141
14186
|
return `${name} error: ${snippet}`;
|
|
14142
14187
|
}
|
|
14143
14188
|
function isSessionExpiredError(message) {
|
|
14144
|
-
return message.includes(SESSION_EXPIRED_MSG) || /session was recorded with model .+ but is resuming with/i.test(message) || /session.*not found|session.*expired|unknown conversation/i.test(message);
|
|
14189
|
+
return message.includes(SESSION_EXPIRED_MSG) || /session was recorded with model .+ but is resuming with/i.test(message) || /failed to resolve rollout path .+file does not exist/i.test(message) || /session.*not found|session.*expired|unknown conversation/i.test(message);
|
|
14145
14190
|
}
|
|
14146
14191
|
var SESSION_EXPIRED_MSG = "No conversation found with session ID";
|
|
14147
14192
|
var init_errors = __esm(() => {
|
|
@@ -14729,6 +14774,14 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
|
|
|
14729
14774
|
for await (const event of events) {
|
|
14730
14775
|
if (abortController.signal.aborted)
|
|
14731
14776
|
break;
|
|
14777
|
+
if (!control.providerTurnContentObserved && (event.type === "text_delta" || event.type === "text" || event.type === "reasoning" || event.type === "tool_use" || event.type === "tool_result" || event.type === "file" || event.type === "result")) {
|
|
14778
|
+
control.providerTurnContentObserved = true;
|
|
14779
|
+
if (control.sessionId) {
|
|
14780
|
+
for (const requestId of control.durableRequestIds ?? []) {
|
|
14781
|
+
markRuntimeUserTurnProviderSessionObserved(topicId, requestId, control.sessionId);
|
|
14782
|
+
}
|
|
14783
|
+
}
|
|
14784
|
+
}
|
|
14732
14785
|
switch (event.type) {
|
|
14733
14786
|
case "text_delta": {
|
|
14734
14787
|
pendingSawDelta = true;
|
|
@@ -15035,6 +15088,13 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
|
|
|
15035
15088
|
}
|
|
15036
15089
|
break;
|
|
15037
15090
|
case "session":
|
|
15091
|
+
control.sessionId = event.sessionId;
|
|
15092
|
+
control.providerSessionObserved = true;
|
|
15093
|
+
if (control.providerTurnContentObserved) {
|
|
15094
|
+
for (const requestId of control.durableRequestIds ?? []) {
|
|
15095
|
+
markRuntimeUserTurnProviderSessionObserved(topicId, requestId, event.sessionId);
|
|
15096
|
+
}
|
|
15097
|
+
}
|
|
15038
15098
|
if (!silent && onSessionId && getRoomQuery(topicId)?.queryId === queryId) {
|
|
15039
15099
|
try {
|
|
15040
15100
|
if (control.injectParams)
|
|
@@ -15109,7 +15169,9 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
|
|
|
15109
15169
|
if (forkHandle && outcome.kind !== "session-expired" && (!forkRequestId || !interSessionQueue.hasRequest(topicId, forkRequestId))) {
|
|
15110
15170
|
cleanupAgentFork(forkHandle);
|
|
15111
15171
|
}
|
|
15112
|
-
|
|
15172
|
+
const queuedUserTurn = roomId === topicId ? getRuntimeUserTurnRequest(topicId) : null;
|
|
15173
|
+
const hasReplacementUserTurn = queuedUserTurn !== null && queuedUserTurn.requestId !== queryId;
|
|
15174
|
+
if (roomId === topicId && outcome.kind !== "session-expired" && !getRoomQuery(topicId) && !hasReplacementUserTurn) {
|
|
15113
15175
|
const next = takeDeferredInject(topicId);
|
|
15114
15176
|
if (next)
|
|
15115
15177
|
redispatchInject(next);
|
|
@@ -15141,6 +15203,7 @@ var init_turn_event_stream = __esm(async () => {
|
|
|
15141
15203
|
init_visuals(),
|
|
15142
15204
|
init_api_messages(),
|
|
15143
15205
|
init_api_topics(),
|
|
15206
|
+
init_runtime_turn_requests(),
|
|
15144
15207
|
init_token_stats()
|
|
15145
15208
|
]);
|
|
15146
15209
|
});
|
|
@@ -15221,6 +15284,7 @@ __export(exports_turn_runner, {
|
|
|
15221
15284
|
wasLocallyRequeuedAfterUserPreemption: () => wasLocallyRequeuedAfterUserPreemption,
|
|
15222
15285
|
userConversationPromptsToRecord: () => userConversationPromptsToRecord,
|
|
15223
15286
|
upsertTaskPanelMessage: () => upsertTaskPanelMessage,
|
|
15287
|
+
tryReconstructTopicRollout: () => tryReconstructTopicRollout,
|
|
15224
15288
|
triggerTopicAiTurn: () => triggerTopicAiTurn,
|
|
15225
15289
|
topicAllowsVisualFileId: () => topicAllowsVisualFileId,
|
|
15226
15290
|
taskPanelMessageId: () => taskPanelMessageId,
|
|
@@ -15236,9 +15300,11 @@ __export(exports_turn_runner, {
|
|
|
15236
15300
|
resolveVisualMediaInput: () => resolveVisualMediaInput,
|
|
15237
15301
|
resolveTopicTurnSession: () => resolveTopicTurnSession,
|
|
15238
15302
|
resolveTopicTurnExecution: () => resolveTopicTurnExecution,
|
|
15303
|
+
resolveSessionRetry: () => resolveSessionRetry,
|
|
15239
15304
|
resolveModelForAgent: () => resolveModelForAgent,
|
|
15240
15305
|
resolveInitialTurnSessionId: () => resolveInitialTurnSessionId,
|
|
15241
15306
|
resolveCompactionExecution: () => resolveCompactionExecution,
|
|
15307
|
+
renderUserTurnBatch: () => renderUserTurnBatch,
|
|
15242
15308
|
renderUserPromptBatch: () => renderUserPromptBatch,
|
|
15243
15309
|
renderTaskPanel: () => renderTaskPanel,
|
|
15244
15310
|
promptWithAttachments: () => promptWithAttachments,
|
|
@@ -15393,6 +15459,7 @@ function redispatchInject(inject) {
|
|
|
15393
15459
|
aiMention: topic.aiMention
|
|
15394
15460
|
},
|
|
15395
15461
|
userId: inject.userId,
|
|
15462
|
+
vaultUserId: inject.vaultUserId,
|
|
15396
15463
|
prompt: inject.prompt,
|
|
15397
15464
|
allowAutoContinue: false,
|
|
15398
15465
|
origin: inject.origin,
|
|
@@ -15518,12 +15585,10 @@ async function deliverAskError(queryId, sourceLabel, error) {
|
|
|
15518
15585
|
}
|
|
15519
15586
|
function tryReconstructTopicRollout(opts) {
|
|
15520
15587
|
const { topicId, topicTitle, userId, agent, sessionId, cwd, model, effort } = opts;
|
|
15521
|
-
if (!sessionId && !opts.allowFreshSession)
|
|
15522
|
-
return null;
|
|
15523
15588
|
try {
|
|
15524
15589
|
const entries = readConversation(userId, topicTitle);
|
|
15525
15590
|
if (entries.length === 0)
|
|
15526
|
-
return
|
|
15591
|
+
return { kind: "no-history" };
|
|
15527
15592
|
const result = getRegistryOperations(agent).writeRollout({
|
|
15528
15593
|
cwd,
|
|
15529
15594
|
entries,
|
|
@@ -15538,15 +15603,18 @@ function tryReconstructTopicRollout(opts) {
|
|
|
15538
15603
|
rolloutPath: result.rolloutPath,
|
|
15539
15604
|
entries: entries.length
|
|
15540
15605
|
}, sessionId ? "ai: session expired \u2014 rollout reconstructed from unified log" : "ai: provider session bridged from shared conversation log");
|
|
15541
|
-
return result.sessionId;
|
|
15606
|
+
return { kind: "rebuilt", sessionId: result.sessionId };
|
|
15542
15607
|
} catch (err2) {
|
|
15543
15608
|
logger.warn({ err: err2, topicId, agent, sessionId }, "ai: session reconstruct failed");
|
|
15544
|
-
return
|
|
15609
|
+
return { kind: "failed", error: stringifyError(err2) };
|
|
15545
15610
|
}
|
|
15546
15611
|
}
|
|
15547
|
-
function
|
|
15612
|
+
function resolveSessionRetry(opts) {
|
|
15548
15613
|
const reconstructed = tryReconstructTopicRollout(opts);
|
|
15549
|
-
if (reconstructed)
|
|
15614
|
+
if (reconstructed.kind === "rebuilt") {
|
|
15615
|
+
return { kind: "retry", sessionId: reconstructed.sessionId };
|
|
15616
|
+
}
|
|
15617
|
+
if (reconstructed.kind === "failed")
|
|
15550
15618
|
return reconstructed;
|
|
15551
15619
|
if (opts.onSessionReset)
|
|
15552
15620
|
opts.onSessionReset();
|
|
@@ -15554,7 +15622,7 @@ function resolveSessionRetryId(opts) {
|
|
|
15554
15622
|
clearTopicSessionId(opts.topicId, "session-expired");
|
|
15555
15623
|
}
|
|
15556
15624
|
logger.info({ topicId: opts.topicId, agent: opts.agent, hadSessionId: Boolean(opts.sessionId) }, "ai: session expired \u2014 retrying with fresh session");
|
|
15557
|
-
return null;
|
|
15625
|
+
return { kind: "retry", sessionId: null };
|
|
15558
15626
|
}
|
|
15559
15627
|
function mergeSupersedingUserTurn(running, incoming) {
|
|
15560
15628
|
const userMessages = [
|
|
@@ -15562,7 +15630,7 @@ function mergeSupersedingUserTurn(running, incoming) {
|
|
|
15562
15630
|
...incoming.userMessages ?? [legacyUserTurnEnvelope(incoming.prompt, incoming.attachments)]
|
|
15563
15631
|
];
|
|
15564
15632
|
return {
|
|
15565
|
-
prompt:
|
|
15633
|
+
prompt: renderUserTurnBatch(userMessages),
|
|
15566
15634
|
userMessages,
|
|
15567
15635
|
attachments: flattenUserTurnAttachments(userMessages),
|
|
15568
15636
|
sessionId: running.sessionId
|
|
@@ -15589,11 +15657,12 @@ function serializableUserTurnExecution(params) {
|
|
|
15589
15657
|
bridgeSessionFromHistory: params.bridgeSessionFromHistory,
|
|
15590
15658
|
peerBridge: params.peerBridge,
|
|
15591
15659
|
from: params.from,
|
|
15660
|
+
vaultUserId: params.vaultUserId,
|
|
15592
15661
|
conversationPrompts: params._conversationPrompts ?? params._userMessages?.map((message) => message.prompt) ?? [params.prompt],
|
|
15593
15662
|
loggedUserMessageCount: params._loggedUserMessageCount
|
|
15594
15663
|
};
|
|
15595
15664
|
}
|
|
15596
|
-
function waitToStartRemoteUserTurn(params, queryId) {
|
|
15665
|
+
function waitToStartRemoteUserTurn(params, queryId, options) {
|
|
15597
15666
|
const execution = serializableUserTurnExecution(params);
|
|
15598
15667
|
const incomingUserMessages = params._userMessages ?? [
|
|
15599
15668
|
legacyUserTurnEnvelope(params.prompt, params.attachments)
|
|
@@ -15606,7 +15675,8 @@ function waitToStartRemoteUserTurn(params, queryId) {
|
|
|
15606
15675
|
requestId: queryId,
|
|
15607
15676
|
execution,
|
|
15608
15677
|
topicEpoch: execution.runtimeEpoch ?? getRuntimeTopicEpoch(params.topic.id),
|
|
15609
|
-
alreadyIncludedRequestIds: params._durableRequestIds
|
|
15678
|
+
alreadyIncludedRequestIds: params._durableRequestIds,
|
|
15679
|
+
omitRequestIds: options?.omitRequestIds
|
|
15610
15680
|
});
|
|
15611
15681
|
for (const supersededRequestId of merged.supersededRequestIds) {
|
|
15612
15682
|
if (supersededRequestId !== merged.requestId) {
|
|
@@ -15657,6 +15727,7 @@ async function drainOneDurableUserTurn() {
|
|
|
15657
15727
|
const queryId = startAiTurn({
|
|
15658
15728
|
topic,
|
|
15659
15729
|
userId: request.userId,
|
|
15730
|
+
vaultUserId: execution?.vaultUserId,
|
|
15660
15731
|
prompt: request.prompt,
|
|
15661
15732
|
_userMessages: request.userMessages,
|
|
15662
15733
|
_conversationPrompts: execution?.conversationPrompts ?? [request.prompt],
|
|
@@ -15727,12 +15798,13 @@ function startAiTurn(params) {
|
|
|
15727
15798
|
}
|
|
15728
15799
|
const topic = storedTopic;
|
|
15729
15800
|
const { userId, allowAutoContinue, onDispatched } = params;
|
|
15801
|
+
const vaultUserId = params.vaultUserId ?? userId;
|
|
15730
15802
|
const origin = params.origin ?? "user";
|
|
15731
15803
|
const conversationPrompts = params._conversationPrompts ?? [params.prompt];
|
|
15732
15804
|
let loggedUserMessageCount = params._loggedUserMessageCount;
|
|
15733
15805
|
let durableRequestIds = params._durableRequestIds ?? [];
|
|
15734
15806
|
let userMessages = isUserOrigin(origin) ? params._userMessages ?? [legacyUserTurnEnvelope(params.prompt, params.attachments)] : undefined;
|
|
15735
|
-
let prompt = userMessages ?
|
|
15807
|
+
let prompt = userMessages ? renderUserTurnBatch(userMessages) : params.prompt;
|
|
15736
15808
|
let attachments2 = userMessages ? flattenUserTurnAttachments(userMessages) : params.attachments;
|
|
15737
15809
|
const execution = resolveTopicTurnExecution(topic, params);
|
|
15738
15810
|
const sessionResolution = resolveTopicTurnSession(topic, params.sessionId, {
|
|
@@ -15797,6 +15869,7 @@ function startAiTurn(params) {
|
|
|
15797
15869
|
topicId,
|
|
15798
15870
|
runtimeEpoch,
|
|
15799
15871
|
userId,
|
|
15872
|
+
vaultUserId,
|
|
15800
15873
|
prompt,
|
|
15801
15874
|
origin,
|
|
15802
15875
|
sourceNode,
|
|
@@ -15881,16 +15954,22 @@ function startAiTurn(params) {
|
|
|
15881
15954
|
}
|
|
15882
15955
|
if (decision.action === "abort-replace") {
|
|
15883
15956
|
const running = decision.running;
|
|
15957
|
+
let omitRequestIds;
|
|
15884
15958
|
if (isUserOrigin(running.origin)) {
|
|
15885
|
-
|
|
15886
|
-
|
|
15887
|
-
|
|
15888
|
-
|
|
15889
|
-
|
|
15890
|
-
|
|
15891
|
-
|
|
15892
|
-
|
|
15893
|
-
|
|
15959
|
+
if (running.providerTurnContentObserved && running.sessionId) {
|
|
15960
|
+
sessionId = running.sessionId;
|
|
15961
|
+
omitRequestIds = running.durableRequestIds;
|
|
15962
|
+
} else {
|
|
15963
|
+
const merged = mergeSupersedingUserTurn(running, { prompt, userMessages, attachments: attachments2 });
|
|
15964
|
+
userMessages = merged.userMessages;
|
|
15965
|
+
prompt = merged.prompt;
|
|
15966
|
+
attachments2 = merged.attachments;
|
|
15967
|
+
sessionId = merged.sessionId;
|
|
15968
|
+
loggedUserMessageCount = running.userMessages?.length ?? 1;
|
|
15969
|
+
durableRequestIds = [
|
|
15970
|
+
...new Set([...running.durableRequestIds ?? [], ...durableRequestIds])
|
|
15971
|
+
];
|
|
15972
|
+
}
|
|
15894
15973
|
}
|
|
15895
15974
|
if (running.injectParams) {
|
|
15896
15975
|
const runningInject = running.injectParams;
|
|
@@ -15911,6 +15990,20 @@ function startAiTurn(params) {
|
|
|
15911
15990
|
running.abortReason = "internal" /* Internal */;
|
|
15912
15991
|
running.abortController.abort();
|
|
15913
15992
|
logger.info({ topicId, supersededQueryId: running.queryId, supersededOrigin: running.origin }, "ai: new user message superseded running turn");
|
|
15993
|
+
const queuedQueryId = waitToStartRemoteUserTurn({
|
|
15994
|
+
...params,
|
|
15995
|
+
topic,
|
|
15996
|
+
prompt,
|
|
15997
|
+
attachments: attachments2,
|
|
15998
|
+
sessionId,
|
|
15999
|
+
_userMessages: userMessages,
|
|
16000
|
+
_conversationPrompts: conversationPrompts,
|
|
16001
|
+
_loggedUserMessageCount: loggedUserMessageCount,
|
|
16002
|
+
_durableRequestIds: durableRequestIds,
|
|
16003
|
+
_runtimeEpoch: runtimeEpoch
|
|
16004
|
+
}, queryId, { omitRequestIds });
|
|
16005
|
+
announceQueuedUserTurn({ ...params, topic }, queuedQueryId);
|
|
16006
|
+
return queuedQueryId;
|
|
15914
16007
|
}
|
|
15915
16008
|
const abortController = new AbortController;
|
|
15916
16009
|
const control = {
|
|
@@ -15929,6 +16022,7 @@ function startAiTurn(params) {
|
|
|
15929
16022
|
topicId,
|
|
15930
16023
|
runtimeEpoch,
|
|
15931
16024
|
userId,
|
|
16025
|
+
vaultUserId,
|
|
15932
16026
|
prompt,
|
|
15933
16027
|
origin,
|
|
15934
16028
|
requestId,
|
|
@@ -16002,8 +16096,9 @@ function startAiTurn(params) {
|
|
|
16002
16096
|
const resolvedEffort = execution.effort;
|
|
16003
16097
|
const workspaceCwd = cwd ?? workspaceCwdFor(topicId);
|
|
16004
16098
|
mkdirSync19(workspaceCwd, { recursive: true });
|
|
16099
|
+
let historyBridgeError;
|
|
16005
16100
|
if (bridgeSessionFromHistory && !sessionId) {
|
|
16006
|
-
const
|
|
16101
|
+
const bridge = tryReconstructTopicRollout({
|
|
16007
16102
|
topicId,
|
|
16008
16103
|
topicTitle: sessionName,
|
|
16009
16104
|
userId,
|
|
@@ -16011,25 +16106,32 @@ function startAiTurn(params) {
|
|
|
16011
16106
|
sessionId: null,
|
|
16012
16107
|
cwd: workspaceCwd,
|
|
16013
16108
|
model: resolvedModel,
|
|
16014
|
-
...resolvedEffort ? { effort: resolvedEffort } : {}
|
|
16015
|
-
allowFreshSession: true
|
|
16109
|
+
...resolvedEffort ? { effort: resolvedEffort } : {}
|
|
16016
16110
|
});
|
|
16017
|
-
if (
|
|
16018
|
-
sessionId =
|
|
16019
|
-
control.sessionId =
|
|
16111
|
+
if (bridge.kind === "rebuilt") {
|
|
16112
|
+
sessionId = bridge.sessionId;
|
|
16113
|
+
control.sessionId = bridge.sessionId;
|
|
16020
16114
|
if (control.injectParams)
|
|
16021
|
-
control.injectParams.sessionId =
|
|
16115
|
+
control.injectParams.sessionId = bridge.sessionId;
|
|
16022
16116
|
try {
|
|
16023
|
-
onSessionId?.(
|
|
16117
|
+
onSessionId?.(bridge.sessionId);
|
|
16024
16118
|
} catch (err2) {
|
|
16025
16119
|
logger.warn({ err: err2, topicId, queryId, agent: agentKind }, "ai: isolated session owner rejected bridged provider session id");
|
|
16026
16120
|
}
|
|
16121
|
+
} else if (bridge.kind === "failed") {
|
|
16122
|
+
historyBridgeError = "Conversation history could not be encoded into a provider session; no fresh session was started.";
|
|
16027
16123
|
}
|
|
16028
16124
|
}
|
|
16029
16125
|
const messageAttachmentGroups = userMessages?.map((message, index) => materializePromptAttachments(topicId, `${queryId}-${index}`, message.attachments));
|
|
16030
16126
|
const promptAttachments = messageAttachmentGroups?.flat() ?? materializePromptAttachments(topicId, queryId, attachments2);
|
|
16031
|
-
const promptWithFiles = userMessages ?
|
|
16032
|
-
|
|
16127
|
+
const promptWithFiles = userMessages ? renderUserTurnBatch(userMessages.map((message, index) => ({
|
|
16128
|
+
...message,
|
|
16129
|
+
prompt: promptWithAttachments(message.prompt, messageAttachmentGroups?.[index] ?? [])
|
|
16130
|
+
}))) : promptWithAttachments(prompt, promptAttachments);
|
|
16131
|
+
const renderedUserPrompts = userMessages ? userMessages.map((message, index) => renderUserTurnPrompt({
|
|
16132
|
+
...message,
|
|
16133
|
+
prompt: promptWithAttachments(message.prompt, messageAttachmentGroups?.[index] ?? [])
|
|
16134
|
+
})) : undefined;
|
|
16033
16135
|
const agentPrompt = topic.aiMode === "mention" && isUserOrigin(origin) && !silent ? buildMentionOnlyChannelPrompt({
|
|
16034
16136
|
topicId,
|
|
16035
16137
|
userId,
|
|
@@ -16145,6 +16247,10 @@ function startAiTurn(params) {
|
|
|
16145
16247
|
const turnMcpEnabled = browserProfileOwner ? enabledMcp : enabledMcp.filter((name) => name !== "playwright");
|
|
16146
16248
|
const wantsBgBash = !isManager && enabledMcp.includes("background-bash");
|
|
16147
16249
|
async function* runWithPlaywright() {
|
|
16250
|
+
if (historyBridgeError) {
|
|
16251
|
+
yield { type: "error", content: historyBridgeError };
|
|
16252
|
+
return;
|
|
16253
|
+
}
|
|
16148
16254
|
if (prepareSession && !sessionId) {
|
|
16149
16255
|
if (abortController.signal.aborted)
|
|
16150
16256
|
return;
|
|
@@ -16217,6 +16323,7 @@ ${playwrightNote}`;
|
|
|
16217
16323
|
systemPrompt: effectiveSystemPrompt,
|
|
16218
16324
|
sessionId,
|
|
16219
16325
|
userId,
|
|
16326
|
+
vaultUserId,
|
|
16220
16327
|
session: sessionName,
|
|
16221
16328
|
sessionType: sessionType ?? (isManager ? "manager" : "forum"),
|
|
16222
16329
|
abortController,
|
|
@@ -16250,11 +16357,10 @@ ${playwrightNote}`;
|
|
|
16250
16357
|
WsHub.get().broadcastTyping(topicId, "ai");
|
|
16251
16358
|
WsHub.get().broadcastAiActive(topicId, queryId);
|
|
16252
16359
|
}
|
|
16253
|
-
streamAgentEvents(topicId, topic.title, queryId, events, control, agentKind, resolvedModel, resolvedEffort, userId, !sessionRetried, onSessionId, { silent, peerBridge, sourceNode }).then(async (
|
|
16360
|
+
streamAgentEvents(topicId, topic.title, queryId, events, control, agentKind, resolvedModel, resolvedEffort, userId, !sessionRetried, onSessionId, { silent, peerBridge, sourceNode }).then(async (streamOutcome) => {
|
|
16361
|
+
let outcome = streamOutcome;
|
|
16254
16362
|
if (outcome.kind === "session-expired") {
|
|
16255
|
-
|
|
16256
|
-
WsHub.get().broadcastAborted(topicId, queryId, "stopped");
|
|
16257
|
-
const retrySessionId = resolveSessionRetryId({
|
|
16363
|
+
const retry = resolveSessionRetry({
|
|
16258
16364
|
topicId,
|
|
16259
16365
|
topicTitle: sessionName,
|
|
16260
16366
|
userId,
|
|
@@ -16267,55 +16373,66 @@ ${playwrightNote}`;
|
|
|
16267
16373
|
externalSessionOwner: Boolean(onSessionId),
|
|
16268
16374
|
onSessionReset
|
|
16269
16375
|
});
|
|
16270
|
-
|
|
16271
|
-
|
|
16272
|
-
|
|
16273
|
-
|
|
16274
|
-
|
|
16275
|
-
}
|
|
16276
|
-
|
|
16277
|
-
|
|
16278
|
-
|
|
16376
|
+
if (retry.kind === "failed") {
|
|
16377
|
+
outcome = {
|
|
16378
|
+
kind: "provider-error",
|
|
16379
|
+
error: "Provider session expired and conversation history could not be rebuilt; the existing session was preserved for retry."
|
|
16380
|
+
};
|
|
16381
|
+
} else {
|
|
16382
|
+
if (!silent)
|
|
16383
|
+
WsHub.get().broadcastAborted(topicId, queryId, "stopped");
|
|
16384
|
+
const retrySessionId = retry.sessionId;
|
|
16385
|
+
logger.info({
|
|
16386
|
+
topicId,
|
|
16387
|
+
prevQueryId: queryId,
|
|
16388
|
+
agent: agentKind,
|
|
16389
|
+
retrySessionId: retrySessionId ? retrySessionId.slice(0, 8) : null
|
|
16390
|
+
}, "ai: retrying query after session expiry");
|
|
16391
|
+
if (!retrySessionId && prepareSession && forkHandle) {
|
|
16392
|
+
cleanupAgentFork(forkHandle);
|
|
16393
|
+
forkHandle = undefined;
|
|
16394
|
+
}
|
|
16395
|
+
startAiTurn({
|
|
16396
|
+
topic,
|
|
16397
|
+
userId,
|
|
16398
|
+
vaultUserId,
|
|
16399
|
+
prompt,
|
|
16400
|
+
_userMessages: userMessages,
|
|
16401
|
+
_conversationPrompts: conversationPrompts,
|
|
16402
|
+
_loggedUserMessageCount: loggedUserMessageCount,
|
|
16403
|
+
_durableRequestIds: durableRequestIds,
|
|
16404
|
+
attachments: attachments2,
|
|
16405
|
+
allowAutoContinue,
|
|
16406
|
+
origin,
|
|
16407
|
+
onDispatched,
|
|
16408
|
+
requestId,
|
|
16409
|
+
depth,
|
|
16410
|
+
silent,
|
|
16411
|
+
contextId,
|
|
16412
|
+
agentOverride,
|
|
16413
|
+
modelOverride,
|
|
16414
|
+
effortOverride,
|
|
16415
|
+
sessionId: retrySessionId,
|
|
16416
|
+
sessionScope,
|
|
16417
|
+
turnConcurrency,
|
|
16418
|
+
forkHandle,
|
|
16419
|
+
prepareSession,
|
|
16420
|
+
cwd,
|
|
16421
|
+
sessionName,
|
|
16422
|
+
sessionType,
|
|
16423
|
+
visualTools,
|
|
16424
|
+
fileDeliveryTools,
|
|
16425
|
+
onSessionId,
|
|
16426
|
+
onSessionReset,
|
|
16427
|
+
bridgeSessionFromHistory,
|
|
16428
|
+
onSettled,
|
|
16429
|
+
peerBridge,
|
|
16430
|
+
askReplySources,
|
|
16431
|
+
_runtimeEpoch: runtimeEpoch,
|
|
16432
|
+
_sessionRetried: true
|
|
16433
|
+
});
|
|
16434
|
+
return;
|
|
16279
16435
|
}
|
|
16280
|
-
startAiTurn({
|
|
16281
|
-
topic,
|
|
16282
|
-
userId,
|
|
16283
|
-
prompt,
|
|
16284
|
-
_userMessages: userMessages,
|
|
16285
|
-
_conversationPrompts: conversationPrompts,
|
|
16286
|
-
_loggedUserMessageCount: loggedUserMessageCount,
|
|
16287
|
-
_durableRequestIds: durableRequestIds,
|
|
16288
|
-
attachments: attachments2,
|
|
16289
|
-
allowAutoContinue,
|
|
16290
|
-
origin,
|
|
16291
|
-
onDispatched,
|
|
16292
|
-
requestId,
|
|
16293
|
-
depth,
|
|
16294
|
-
silent,
|
|
16295
|
-
contextId,
|
|
16296
|
-
agentOverride,
|
|
16297
|
-
modelOverride,
|
|
16298
|
-
effortOverride,
|
|
16299
|
-
sessionId: retrySessionId,
|
|
16300
|
-
sessionScope,
|
|
16301
|
-
turnConcurrency,
|
|
16302
|
-
forkHandle,
|
|
16303
|
-
prepareSession,
|
|
16304
|
-
cwd,
|
|
16305
|
-
sessionName,
|
|
16306
|
-
sessionType,
|
|
16307
|
-
visualTools,
|
|
16308
|
-
fileDeliveryTools,
|
|
16309
|
-
onSessionId,
|
|
16310
|
-
onSessionReset,
|
|
16311
|
-
bridgeSessionFromHistory,
|
|
16312
|
-
onSettled,
|
|
16313
|
-
peerBridge,
|
|
16314
|
-
askReplySources,
|
|
16315
|
-
_runtimeEpoch: runtimeEpoch,
|
|
16316
|
-
_sessionRetried: true
|
|
16317
|
-
});
|
|
16318
|
-
return;
|
|
16319
16436
|
}
|
|
16320
16437
|
if (outcome.kind === "provider-error") {
|
|
16321
16438
|
if (!silent) {
|
|
@@ -17416,14 +17533,11 @@ function switchApiTopicAgent(opts) {
|
|
|
17416
17533
|
topicTitle: opts.topicTitle,
|
|
17417
17534
|
from: opts.fromAgent,
|
|
17418
17535
|
to: opts.agent
|
|
17419
|
-
}, "api-topic switch: rollout encoding failed \u2014
|
|
17420
|
-
|
|
17421
|
-
|
|
17422
|
-
|
|
17423
|
-
|
|
17424
|
-
return { ok: false, error: `failed to set agent for "${opts.topicTitle}"` };
|
|
17425
|
-
}
|
|
17426
|
-
return { ok: true, outcome: { kind: "fresh", agent: opts.agent, reason: "bridge-failed" } };
|
|
17536
|
+
}, "api-topic switch: rollout encoding failed \u2014 preserving current agent session");
|
|
17537
|
+
return {
|
|
17538
|
+
ok: false,
|
|
17539
|
+
error: `failed to preserve conversation while switching agent for "${opts.topicTitle}"`
|
|
17540
|
+
};
|
|
17427
17541
|
}
|
|
17428
17542
|
try {
|
|
17429
17543
|
appendConversationEventStrict(opts.userId, opts.topicTitle, opts.agent, {
|
|
@@ -17648,7 +17762,7 @@ function createSelfConfigCore(host, productOverrides = {}) {
|
|
|
17648
17762
|
return err(switched.error);
|
|
17649
17763
|
host.runtime.configChanged(topic.id, "agent");
|
|
17650
17764
|
ctx.onConfigChanged?.("agent");
|
|
17651
|
-
const sessionNote = switched.outcome.kind === "bridged" ? ` Conversation history was bridged into a ${agent} session (${switched.outcome.bridgedSessionId}).` :
|
|
17765
|
+
const sessionNote = switched.outcome.kind === "bridged" ? ` Conversation history was bridged into a ${agent} session (${switched.outcome.bridgedSessionId}).` : " No prior conversation history was found; the next turn starts fresh.";
|
|
17652
17766
|
return ok(`Agent for this topic set to '${agent}'. Model/effort reset to '${agent}' defaults.${sessionNote}`);
|
|
17653
17767
|
}
|
|
17654
17768
|
function getAgent(ctx) {
|
|
@@ -18179,4 +18293,4 @@ export {
|
|
|
18179
18293
|
DEFAULT_SELF_CONFIG_PRODUCT
|
|
18180
18294
|
};
|
|
18181
18295
|
|
|
18182
|
-
//# debugId=
|
|
18296
|
+
//# debugId=0A6AFB912A18777064756E2164756E21
|