@bivy/bivy 0.15.0-staging.36 → 0.15.0-staging.37
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/auth-errors.js +9 -0
- package/dist/server.js +31 -15
- package/package.json +1 -1
|
@@ -77,3 +77,12 @@ export function authProviderForSession(runtimeId, modelProvider) {
|
|
|
77
77
|
return provider;
|
|
78
78
|
return undefined;
|
|
79
79
|
}
|
|
80
|
+
/** Convert a provider failure into the safe wire shape consumed by clients.
|
|
81
|
+
* Raw provider text can contain stack frames, paths, or tokens and must never
|
|
82
|
+
* be used as the user-facing auth error. */
|
|
83
|
+
export function classifyModelAuthError(raw, runtimeId, modelProvider) {
|
|
84
|
+
if (!isModelAuthError(raw))
|
|
85
|
+
return null;
|
|
86
|
+
const provider = authProviderForSession(runtimeId, modelProvider);
|
|
87
|
+
return provider ? { kind: "model_auth", provider } : null;
|
|
88
|
+
}
|
package/dist/server.js
CHANGED
|
@@ -34,7 +34,7 @@ import { InMemoryLocationRegistry } from "./runtime/location-registry.js";
|
|
|
34
34
|
import { ControlPlaneSessionLocationRegistry, LayeredSessionLocationRegistry } from "./runtime/control-plane-location.js";
|
|
35
35
|
import { attachAdoptedSessions, classifyAttachFailure } from "./runtime/adoption.js";
|
|
36
36
|
import { createCredentialStore, testProviderCredential } from "./runtime/credentials.js";
|
|
37
|
-
import { isModelAuthError, authProviderForSession } from "./runtime/auth-errors.js";
|
|
37
|
+
import { isModelAuthError, authProviderForSession, classifyModelAuthError } from "./runtime/auth-errors.js";
|
|
38
38
|
import { createCredentialVault, migrateVaultDir } from "./runtime/credential-store.js";
|
|
39
39
|
import { probeAnthropicAccess } from "./runtime/anthropic-preflight.js";
|
|
40
40
|
import { createSessionNamer, fallbackSessionName } from "./session/session-namer.js";
|
|
@@ -930,6 +930,7 @@ function harnessDirFor(record) {
|
|
|
930
930
|
function harnessBeginTurn(record) {
|
|
931
931
|
const dir = harnessDirFor(record);
|
|
932
932
|
record.harnessTurnReady = undefined;
|
|
933
|
+
record.harnessTurnFinished = false;
|
|
933
934
|
if (!dir)
|
|
934
935
|
return;
|
|
935
936
|
const previous = record.workspaceState === "dirty" ? "dirty" : "clean";
|
|
@@ -954,6 +955,14 @@ function harnessBeginTurn(record) {
|
|
|
954
955
|
})();
|
|
955
956
|
}
|
|
956
957
|
/** After a turn, snapshot again and broadcast the structured diff it produced. */
|
|
958
|
+
function finishHarnessTurn(record) {
|
|
959
|
+
if (record.harnessTurnFinished)
|
|
960
|
+
return;
|
|
961
|
+
record.harnessTurnFinished = true;
|
|
962
|
+
void harnessEndTurn(record).finally(() => {
|
|
963
|
+
void replication.onTurnComplete(record.id);
|
|
964
|
+
});
|
|
965
|
+
}
|
|
957
966
|
async function harnessEndTurn(record) {
|
|
958
967
|
const previous = record.workspaceState === "dirty" ? "dirty" : "clean";
|
|
959
968
|
try {
|
|
@@ -2845,7 +2854,7 @@ const RELAY_COMMANDS = {
|
|
|
2845
2854
|
// this the relay client (PWA) is stranded on "Working…" forever with
|
|
2846
2855
|
// only a session.error toast. Clear working so a terminal state reaches it.
|
|
2847
2856
|
clearSessionWorking(record);
|
|
2848
|
-
broadcast({ type: "session.error", sessionId: record.id, error:
|
|
2857
|
+
broadcast({ type: "session.error", sessionId: record.id, error: sessionAgentError(record, error) });
|
|
2849
2858
|
});
|
|
2850
2859
|
},
|
|
2851
2860
|
async "session.new"(msg) {
|
|
@@ -5962,6 +5971,7 @@ async function sessionListRows() {
|
|
|
5962
5971
|
agent: meta?.runtimeId ?? s.agent,
|
|
5963
5972
|
agentName: meta?.agentName ?? s.agentName,
|
|
5964
5973
|
source: rec?.source ?? meta?.source,
|
|
5974
|
+
bivyCreated: Boolean(meta),
|
|
5965
5975
|
forkedFrom: rec?.forkedFrom ?? meta?.forkedFrom,
|
|
5966
5976
|
branch: rec?.worktree?.branch ?? meta?.branch,
|
|
5967
5977
|
sandbox: rec?.sandbox ?? normalizeSandboxTier(meta?.sandbox),
|
|
@@ -7178,6 +7188,11 @@ function actionableAgentError(runtimeId, error) {
|
|
|
7178
7188
|
}
|
|
7179
7189
|
return raw;
|
|
7180
7190
|
}
|
|
7191
|
+
function sessionAgentError(record, error) {
|
|
7192
|
+
const raw = humanizeAgentError(error instanceof Error ? error.message : String(error));
|
|
7193
|
+
return classifyModelAuthError(raw, record.runtimeId, record.session.getCurrentModel()?.provider)
|
|
7194
|
+
?? actionableAgentError(record.runtimeId, error);
|
|
7195
|
+
}
|
|
7181
7196
|
/**
|
|
7182
7197
|
* A turn that ended in a *terminal* model/provider failure the runtime would
|
|
7183
7198
|
* otherwise swallow. `agent_end` carries the turn's messages and whether the
|
|
@@ -7234,7 +7249,7 @@ function attachSessionListeners(record) {
|
|
|
7234
7249
|
policy: sessionRunPolicy,
|
|
7235
7250
|
onNotice: (n) => broadcast({ type: "session.notice", sessionId: record.id, level: n.level, message: n.message }),
|
|
7236
7251
|
onModelChanged: () => broadcast({ type: "model.updated", sessionId: record.id, model: publicModel(record.session.getCurrentModel(), record.session.getCurrentModel()) }),
|
|
7237
|
-
onFailed: (message) => broadcast({ type: "session.error", sessionId: record.id, error: message }),
|
|
7252
|
+
onFailed: (message) => broadcast({ type: "session.error", sessionId: record.id, error: sessionAgentError(record, message) }),
|
|
7238
7253
|
});
|
|
7239
7254
|
}
|
|
7240
7255
|
record.unsubscribe = record.session.subscribe((event) => {
|
|
@@ -7331,9 +7346,14 @@ function attachSessionListeners(record) {
|
|
|
7331
7346
|
transcripts.resolveInlineImages(record);
|
|
7332
7347
|
}
|
|
7333
7348
|
// Durably persist the throttled sidecars at the turn boundary so a crash
|
|
7334
|
-
// loses at most the in-flight turn's UI detail, not the whole turn.
|
|
7335
|
-
|
|
7349
|
+
// loses at most the in-flight turn's UI detail, not the whole turn. A
|
|
7350
|
+
// runtime may keep its agent process alive and never emit agent_end (Pi),
|
|
7351
|
+
// so turn_end is also authoritative for the interactive working state.
|
|
7352
|
+
if (event.type === "turn_end") {
|
|
7336
7353
|
eventLog.flush(record.id);
|
|
7354
|
+
clearSessionWorking(record);
|
|
7355
|
+
finishHarnessTurn(record);
|
|
7356
|
+
}
|
|
7337
7357
|
// AskUserQuestion is intercepted and answered by the daemon's guardian /
|
|
7338
7358
|
// QuestionManager (see guardianInterceptor), which broadcasts
|
|
7339
7359
|
// session.question(.resolved) from its own listeners — runtimes no longer
|
|
@@ -7368,14 +7388,9 @@ function attachSessionListeners(record) {
|
|
|
7368
7388
|
transcripts.clearLiveIntermediate(record.id);
|
|
7369
7389
|
clearSessionWorking(record);
|
|
7370
7390
|
void refreshSessionUsage(record);
|
|
7371
|
-
// Snapshot
|
|
7372
|
-
//
|
|
7373
|
-
|
|
7374
|
-
// (so the shipped frame carries this turn's transcript AND its checkpoint).
|
|
7375
|
-
// Gated on session sync — inert by default.
|
|
7376
|
-
void harnessEndTurn(record).finally(() => {
|
|
7377
|
-
void replication.onTurnComplete(record.id);
|
|
7378
|
-
});
|
|
7391
|
+
// Snapshot/diff at whichever completion boundary arrived first. Pi keeps
|
|
7392
|
+
// its process alive after turn_end; other runtimes also emit agent_end.
|
|
7393
|
+
finishHarnessTurn(record);
|
|
7379
7394
|
// A turn that ended in a terminal model/provider error (e.g. an expired
|
|
7380
7395
|
// credential or a 4xx from the API) otherwise vanished: working cleared,
|
|
7381
7396
|
// no reply, no signal. Surface it as a session-scoped error so the client
|
|
@@ -7437,7 +7452,7 @@ function attachSessionListeners(record) {
|
|
|
7437
7452
|
scheduleAdvertise();
|
|
7438
7453
|
broadcast({ type: "session.failed", sessionId: record.id, failedAt: record.lastFailureAt });
|
|
7439
7454
|
if (messageError)
|
|
7440
|
-
broadcast({ type: "session.error", sessionId: record.id, error:
|
|
7455
|
+
broadcast({ type: "session.error", sessionId: record.id, error: sessionAgentError(record, messageError) });
|
|
7441
7456
|
// If the terminal error is an auth failure (expired key/token → 4xx),
|
|
7442
7457
|
// also raise the sign-in sheet for the failing provider.
|
|
7443
7458
|
maybeSignalAuthRequired(record, turnError);
|
|
@@ -8061,7 +8076,7 @@ async function createSession(workspace = defaultWorkspace, sessionFile, opts = {
|
|
|
8061
8076
|
void refreshSessionUsage(record);
|
|
8062
8077
|
if (makeActive)
|
|
8063
8078
|
active = record;
|
|
8064
|
-
broadcast({ type: "session.created", sessionId, name: record.session.getName(), workspace: sessionWorkspace, sessionFile: record.sessionFile, source: record.source, branch: worktree?.branch, prUrl: record.prUrl, runtimeId: rt.id, agentName: rt.displayName, modelFallbackMessage, bivySession: bivySessionEnvelope(record), capabilities: capabilitiesWithCommands(rt.id, record.session) });
|
|
8079
|
+
broadcast({ type: "session.created", sessionId, name: record.session.getName(), workspace: sessionWorkspace, sessionFile: record.sessionFile, source: record.source, bivyCreated: true, branch: worktree?.branch, prUrl: record.prUrl, runtimeId: rt.id, agentName: rt.displayName, modelFallbackMessage, bivySession: bivySessionEnvelope(record), capabilities: capabilitiesWithCommands(rt.id, record.session) });
|
|
8065
8080
|
void maybeNotifyBivyUpdate();
|
|
8066
8081
|
scheduleAdvertise();
|
|
8067
8082
|
return record;
|
|
@@ -9856,6 +9871,7 @@ app.get("/api/sessions", async (_req, res, next) => {
|
|
|
9856
9871
|
agent: meta?.runtimeId ?? s.agent,
|
|
9857
9872
|
agentName: meta?.agentName ?? s.agentName,
|
|
9858
9873
|
source: rec?.source ?? meta?.source,
|
|
9874
|
+
bivyCreated: Boolean(meta),
|
|
9859
9875
|
forkedFrom: rec?.forkedFrom ?? meta?.forkedFrom,
|
|
9860
9876
|
branch: rec?.worktree?.branch ?? meta?.branch,
|
|
9861
9877
|
prUrl: rec?.prUrl ?? meta?.prUrl,
|
package/package.json
CHANGED