@bivy/bivy 0.17.2-staging.2 → 0.17.2-staging.4
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/runtime/model-catalog.js +13 -0
- package/dist/server.js +11 -38
- package/dist/session/target-session.js +31 -0
- package/package.json +1 -1
|
@@ -134,3 +134,16 @@ export function mergeProviderCatalog(base, catalog) {
|
|
|
134
134
|
}
|
|
135
135
|
return [...byId.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
136
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Reply to a models.list query. A catalog read failure must never answer with
|
|
139
|
+
* silence: the client (notably a Cloud launch validating its saved model) could
|
|
140
|
+
* only time out without a cause. Name the real reason on the session instead.
|
|
141
|
+
*/
|
|
142
|
+
export async function catalogReplyEvent(load, sessionId) {
|
|
143
|
+
try {
|
|
144
|
+
return await load();
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
return { type: "session.error", sessionId, error: `Couldn't read this machine's model catalog: ${error instanceof Error ? error.message : String(error)}` };
|
|
148
|
+
}
|
|
149
|
+
}
|
package/dist/server.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import { createNodeUpdateChecker, updateRegistryUrl } from "./node-update.js";
|
|
5
5
|
import { createRemoteSessionAdmission, RemoteSessionAdmissionError } from "./session/remote-session-admission.js";
|
|
6
|
+
import { requiresExistingSession, resolveTargetSession } from "./session/target-session.js";
|
|
6
7
|
import path from "node:path";
|
|
7
8
|
import os from "node:os";
|
|
8
9
|
import { spawn, spawnSync } from "node:child_process";
|
|
@@ -33,7 +34,7 @@ import { AppService } from "./apps/service.js";
|
|
|
33
34
|
import { createAppCommands } from "./controllers/app-commands.js";
|
|
34
35
|
import { bindClientCommandRoutes } from "./http/client-command-routes.js";
|
|
35
36
|
import { collectDiscoveredSessions, planNativeAdoption } from "./runtime/native-session-discovery.js";
|
|
36
|
-
import { aggregateModelCatalog, mergeProviderCatalog } from "./runtime/model-catalog.js";
|
|
37
|
+
import { aggregateModelCatalog, catalogReplyEvent, mergeProviderCatalog } from "./runtime/model-catalog.js";
|
|
37
38
|
import { RuntimeHost, enforcementLevelFor, remoteRuntimeEnabled } from "./runtime/host.js";
|
|
38
39
|
import { RemoteRuntime, RemoteRuntimeSession } from "./runtime/remote.js";
|
|
39
40
|
import { InMemorySessionLocationRegistry } from "./runtime/session-location.js";
|
|
@@ -2453,18 +2454,8 @@ const RELAY_COMMANDS = {
|
|
|
2453
2454
|
// this a models.list answered for one agent (e.g. Codex's models) could
|
|
2454
2455
|
// linger on the composer/picker after the user switched to another agent
|
|
2455
2456
|
// (e.g. Claude) — the "Claude shows Codex models" bug.
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
event = await modelsListEventFor(record);
|
|
2459
|
-
}
|
|
2460
|
-
catch (error) {
|
|
2461
|
-
// A catalog read failure must never answer with silence: the generic
|
|
2462
|
-
// relay catch would swallow the rejection and the client (notably a
|
|
2463
|
-
// Cloud launch waiting to validate its saved model) could only guess
|
|
2464
|
-
// via timeout. Name the real reason on the session instead.
|
|
2465
|
-
relay?.sendEvent({ type: "session.error", sessionId: requestedSessionId ?? record.id, error: `Couldn't read this machine's model catalog: ${error instanceof Error ? error.message : String(error)}` });
|
|
2466
|
-
return;
|
|
2467
|
-
}
|
|
2457
|
+
// A catalog read failure answers with the real error, never silence.
|
|
2458
|
+
const event = await catalogReplyEvent(() => modelsListEventFor(record), requestedSessionId ?? record.id);
|
|
2468
2459
|
relay?.sendEvent(event);
|
|
2469
2460
|
},
|
|
2470
2461
|
"models.prefetch"(msg) {
|
|
@@ -4915,31 +4906,13 @@ function assertSessionAccounts(record, labels) {
|
|
|
4915
4906
|
}
|
|
4916
4907
|
}
|
|
4917
4908
|
async function continueCorrelatedSession(item, prompt, report, opts) {
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
// handled (resolveOrResumeSession). Best-effort: an unresolvable session
|
|
4925
|
-
// falls through to the snapshot restore / fail handling below.
|
|
4926
|
-
record = await resolveOrResumeSession(item.targetSessionId).catch(() => undefined);
|
|
4927
|
-
}
|
|
4928
|
-
if (!record) {
|
|
4929
|
-
const restored = await restoreSessionFromSnapshot(item.targetSessionId);
|
|
4930
|
-
if (restored)
|
|
4931
|
-
record = await resolveOrResumeSession(item.targetSessionId).catch(() => undefined);
|
|
4932
|
-
}
|
|
4933
|
-
if (!record) {
|
|
4934
|
-
// Strict mode (a scheduled message to a specific session): the session must
|
|
4935
|
-
// be resumed in place, so a genuinely-unavailable one is surfaced as a
|
|
4936
|
-
// failed run rather than silently starting a new session. Everything else
|
|
4937
|
-
// keeps the established best-effort fall-through to a fresh pickup.
|
|
4938
|
-
if (opts?.resumeOnMissing) {
|
|
4939
|
-
throw new Error(`This Run could not continue session ${item.targetSessionId}: the session is not available on this Machine`);
|
|
4940
|
-
}
|
|
4909
|
+
const record = await resolveTargetSession(item, {
|
|
4910
|
+
open: (id) => openSessions.get(id),
|
|
4911
|
+
resume: (id) => resolveOrResumeSession(id),
|
|
4912
|
+
restoreSnapshot: async (id) => Boolean(await restoreSessionFromSnapshot(id)),
|
|
4913
|
+
}, opts?.resumeOnMissing === true);
|
|
4914
|
+
if (!record)
|
|
4941
4915
|
return false;
|
|
4942
|
-
}
|
|
4943
4916
|
assertSessionAccounts(record, opts?.credentialLabels);
|
|
4944
4917
|
const branch = record.worktree?.branch;
|
|
4945
4918
|
if (opts?.resumeOnMissing) {
|
|
@@ -5189,7 +5162,7 @@ async function executeWorkItem(item, report, signal) {
|
|
|
5189
5162
|
// Scheduled runs targeting an existing session are STRICT: the message must
|
|
5190
5163
|
// land in that session (resumed from disk if needed), never silently in a new
|
|
5191
5164
|
// one — so a session that can't be resumed fails the run instead.
|
|
5192
|
-
if (await continueCorrelatedSession(item, request, report, { resumeOnMissing: item
|
|
5165
|
+
if (await continueCorrelatedSession(item, request, report, { resumeOnMissing: requiresExistingSession(item), isMessage, signal, credentialLabels }))
|
|
5193
5166
|
return;
|
|
5194
5167
|
const requestedSandbox = normalizeSandboxTier(item.sandbox);
|
|
5195
5168
|
// Prepare an explicit repository before resolving its policy. Otherwise a
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (c) 2026 Petter André Sjulstad
|
|
3
|
+
//
|
|
4
|
+
// Resolving the Session a durable Run targets. A Run aimed at an existing
|
|
5
|
+
// Session (or any scheduled Run) is strict: it continues that Session, resumed
|
|
6
|
+
// from disk or a snapshot if needed, or fails visibly. It never silently cold-
|
|
7
|
+
// starts a new Session without the context the user pointed it at.
|
|
8
|
+
/** Scheduled Runs and Runs aimed at an existing Session must land in that Session. */
|
|
9
|
+
export function requiresExistingSession(item) {
|
|
10
|
+
return item.source === "schedule" || item.targetKind === "existing_session";
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The targeted Session, or null when the item targets none or (non-strict) it is
|
|
14
|
+
* unavailable, in which case the caller falls through to a fresh pickup. Throws
|
|
15
|
+
* when a strict target cannot be continued.
|
|
16
|
+
*/
|
|
17
|
+
export async function resolveTargetSession(item, resolvers, strict) {
|
|
18
|
+
if (item.targetKind !== "existing_session" || !item.targetSessionId)
|
|
19
|
+
return null;
|
|
20
|
+
const id = item.targetSessionId;
|
|
21
|
+
let record = resolvers.open(id);
|
|
22
|
+
if (!record && strict)
|
|
23
|
+
record = await resolvers.resume(id).catch(() => undefined);
|
|
24
|
+
if (!record && await resolvers.restoreSnapshot(id))
|
|
25
|
+
record = await resolvers.resume(id).catch(() => undefined);
|
|
26
|
+
if (record)
|
|
27
|
+
return record;
|
|
28
|
+
if (strict)
|
|
29
|
+
throw new Error(`This Run could not continue session ${id}: the session is not available on this Machine`);
|
|
30
|
+
return null;
|
|
31
|
+
}
|
package/package.json
CHANGED