openmausbot 0.1.91 → 0.1.92
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/assets/DesktopViewer-Do6lBa4C.js +5 -0
- package/dist/assets/{abnfDiagram-O67JEVCF-yP-w8pRq.js → abnfDiagram-O67JEVCF-Cd3X7Hxc.js} +1 -1
- package/dist/assets/{arc-DsivfZEh.js → arc-DIYc7Ymx.js} +1 -1
- package/dist/assets/{architectureDiagram-NJMV4G6O-CILw7lzT.js → architectureDiagram-NJMV4G6O-DBmRGVyP.js} +1 -1
- package/dist/assets/{blockDiagram-BEXU5L5S-DEQUV3HF.js → blockDiagram-BEXU5L5S-CPFePSEE.js} +1 -1
- package/dist/assets/{c4Diagram-YGBWAQC7-lSXCZoKA.js → c4Diagram-YGBWAQC7-DD_DO1qy.js} +1 -1
- package/dist/assets/channel-Dtk5Mtic.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-QCD5bgWP.js → chunk-2Q5K7J3B-BNEeNSjX.js} +1 -1
- package/dist/assets/{chunk-3FUC2YCW-C5-iZJuL.js → chunk-3FUC2YCW-BbinLaqH.js} +2 -2
- package/dist/assets/{chunk-5VM5RSS4-cxsawMpY.js → chunk-5VM5RSS4-BBgYrgDR.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-DV8tV8H7.js → chunk-GTNCS2PH-ezXj-kA-.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-CJFZyfbW.js → chunk-GWA4HPMP-9FxZ3diV.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-BigbMXco.js → chunk-JWPE2WC7-CO-40JpI.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-oVxTfpDy.js → chunk-NETBCI7D-BEhjslYE.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-CH4BiroW.js → chunk-WEXAMYUT-CNeq4n7_.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-KEDa6gax.js → chunk-XXDRQBXY-OsCY7KSi.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-BiPjfqVv.js → classDiagram-v2-NBCMYWYE-CloRUEtk.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-Caxew3OY.js → cose-bilkent-JH36ORCC-D1BGZhp4.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-B_Pui369.js → cynefin-OW5HDTMX-CUf4T2CN.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-X64ydIhP.js → cynefinDiagram-VND7K2PF-DPEZrFGA.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-TqYCI1E1.js → dagre-6A5THRUB-bDe6GIv5.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-TeOt3rAQ.js → diagram-22UHCM2B-CdcUQz55.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-D70BLhDA.js → diagram-3UASUU5V-BZpqGg7c.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-BbQDLwUC.js → diagram-ATOU4E4O-DvGM6ugp.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-ChNJZ1u-.js → diagram-CDSNMT55-DSTmMm0s.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-DMBl2APa.js → diagram-MLGK6HIB-15cucnMn.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-CEqDchOp.js → diagram-MPIPVDR6-tMbuaE6n.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-BVDKRp_-.js → ebnfDiagram-ZINNZB2B-BYxYzjHI.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-CRkofO9B.js → elk-276RUBZZ-v6bj852W.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-DruOMh5e.js → erDiagram-OPXOYQCR-5lBBlnd2.js} +1 -1
- package/dist/assets/{flowDiagram-KWPJA3E3-8GMQeB7t.js → flowDiagram-KWPJA3E3-CZyl1zIj.js} +1 -1
- package/dist/assets/{ganttDiagram-FUAMR5RP-ORE9jcsM.js → ganttDiagram-FUAMR5RP-BiAI-ltZ.js} +1 -1
- package/dist/assets/{gitGraphDiagram-X574FWY7-BmsedsWh.js → gitGraphDiagram-X574FWY7-BAZISkj3.js} +1 -1
- package/dist/assets/index-Cdmjrl28.css +1 -0
- package/dist/assets/{index-DKNEM8Q6.js → index-DGBaEXrM.js} +1 -1
- package/dist/assets/index-DR7YPMkW.js +597 -0
- package/dist/assets/{infoDiagram-VRGFBTTK-E_p7AooJ.js → infoDiagram-VRGFBTTK-B6ooOy-Z.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-Y-B2mKda.js → ishikawaDiagram-OU5B5YK6-BZ9ksi-B.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-eA8RELcK.js → journeyDiagram-ZHPQQLJL-DWqlzIEe.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-DqIL2e8e.js → kanban-definition-PNTS6WVX-JX6piDwL.js} +1 -1
- package/dist/assets/{linear-yC_29R6e.js → linear-CmOWzX0P.js} +1 -1
- package/dist/assets/{mermaid.core-VhEMkSND.js → mermaid.core-CF1zwoQ-.js} +5 -5
- package/dist/assets/{mindmap-definition-NLK3R4M7-oJ7kyQZy.js → mindmap-definition-NLK3R4M7-CGh2K4fW.js} +1 -1
- package/dist/assets/{pegDiagram-GJSIUBJH-BmfYcY0O.js → pegDiagram-GJSIUBJH-CDwYN0s7.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-DKvgP7w9.js → pieDiagram-5QR66LMP-1rfjJocB.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-BEKL3W0L.js → quadrantDiagram-O4NWA36T-tKPvPiok.js} +1 -1
- package/dist/assets/{railroadDiagram-XR7U4H2S-BEsiqR5s.js → railroadDiagram-XR7U4H2S-BHLm5vXR.js} +1 -1
- package/dist/assets/{requirementDiagram-PLB6GJNP-DWBcWvAP.js → requirementDiagram-PLB6GJNP-CB45STp7.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-rpZ2_O4s.js → sankeyDiagram-IPEJSGJF-55QA_huC.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-B99HQdJR.js → sequenceDiagram-PO4LG4MO-BckFmQHn.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-BjN0qT5T.js → sizeCapture-INFHLROL-BFCOJF11.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK-BbsVgGsC.js → stateDiagram-v2-GCMORJYK-CZvmiVVN.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-DG3gKkeR.js → swimlanes-2SLR337P-BpDUCEKy.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-M0GYW9Y3.js +8 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-BTHOdtF_.js → timeline-definition-EJHVYXUP-BpxHq6IK.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-acLSLFwc.js → usecaseDiagram-POWQR4AR-Cj66vm02.js} +1 -1
- package/dist/assets/{vennDiagram-UO4OBE2U-C5JzNX9G.js → vennDiagram-UO4OBE2U-C9nRkWE_.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-DL1-PrR_.js → wardleyDiagram-VNRHLVJA-D-me_nWj.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-Bvr1477p.js → xychartDiagram-PMCCYNJV-Dc_fL4mk.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/novnc-NOTICE.txt +118 -0
- package/dist-server/cloud-home-start.js +112 -14
- package/dist-server/container-mcp.js +195 -15
- package/dist-server/drivers/agents-proxy.js +5 -4
- package/dist-server/enterprise/server/index.js +5 -1
- package/dist-server/index.js +9300 -4155
- package/dist-server/local-computer-proxy.js +128 -7
- package/dist-server/mcp-gate.js +11 -5
- package/dist-server/model-catalog/models-dev.snapshot.json +7299 -0
- package/dist-server/openmausbot.js +3910 -1182
- package/dist-server/pair-cli.js +3910 -1182
- package/dist-server/server/boat.js +2 -2
- package/dist-server/server/browser-engine.js +10 -1
- package/dist-server/server/browser-live.js +46 -2
- package/dist-server/server/browser-runtime.js +74 -8
- package/dist-server/server/browser-tool-shape.js +6 -4
- package/dist-server/server/chief-of-staff.js +1 -1
- package/dist-server/server/cloud-home-start.js +118 -26
- package/dist-server/server/cloud-home.js +39 -3
- package/dist-server/server/cloud-lending.js +74 -10
- package/dist-server/server/cloud-owner.js +196 -0
- package/dist-server/server/cloud-secrets-boot.js +6 -0
- package/dist-server/server/cloud-secrets.js +65 -0
- package/dist-server/server/config.js +174 -14
- package/dist-server/server/container-computer.js +3 -2
- package/dist-server/server/desktop-viewer-targets.js +26 -0
- package/dist-server/server/drivers/acp/core.js +167 -21
- package/dist-server/server/drivers/acp/opencode-go.js +614 -85
- package/dist-server/server/drivers/agents-catalog.js +6 -6
- package/dist-server/server/drivers/antigravity-acp.js +69 -11
- package/dist-server/server/drivers/antigravity-temp.js +360 -0
- package/dist-server/server/drivers/antigravity.js +21 -0
- package/dist-server/server/drivers/chatgpt-plan-auth.js +481 -0
- package/dist-server/server/drivers/claude.js +312 -34
- package/dist-server/server/drivers/codex.js +228 -18
- package/dist-server/server/drivers/grok.js +2 -2
- package/dist-server/server/drivers/mistral.js +4 -3
- package/dist-server/server/drivers/openai-chat-protocol.js +4 -3
- package/dist-server/server/drivers/openai-chat.js +41 -3
- package/dist-server/server/drivers/openai-compat.js +73 -23
- package/dist-server/server/engine-install.js +46 -12
- package/dist-server/server/env-path.js +15 -3
- package/dist-server/server/harness/registry.js +2 -2
- package/dist-server/server/index.js +1338 -184
- package/dist-server/server/lending-memory.js +479 -0
- package/dist-server/server/local-vm-idle.js +24 -6
- package/dist-server/server/managed-desktop.js +3 -0
- package/dist-server/server/mcp-bridge.js +5 -3
- package/dist-server/server/mcp-http.js +6 -3
- package/dist-server/server/mcp-oauth-discovery.js +133 -0
- package/dist-server/server/mcp-oauth-store.js +70 -0
- package/dist-server/server/mcp-oauth.js +550 -0
- package/dist-server/server/mcp-probe.js +14 -1
- package/dist-server/server/mcp-registry.js +43 -0
- package/dist-server/server/memory-upkeep.js +94 -84
- package/dist-server/server/model-catalog/catalog.js +374 -0
- package/dist-server/server/model-catalog/presets.js +176 -0
- package/dist-server/server/model-catalog/trim.js +219 -0
- package/dist-server/server/provider-key-check.js +4 -2
- package/dist-server/server/recent-work.js +1 -1
- package/dist-server/server/request-auth.js +23 -0
- package/dist-server/server/routes/antigravity-leftovers.js +45 -0
- package/dist-server/server/routes/bot-memory.js +31 -6
- package/dist-server/server/routes/desktop-viewer.js +194 -0
- package/dist-server/server/routine-requests.js +1 -1
- package/dist-server/server/routines.js +1 -1
- package/dist-server/server/sessions.js +56 -9
- package/dist-server/server/skills.js +25 -10
- package/dist-server/server/store.js +62 -122
- package/dist-server/server/thread-events.js +2 -0
- package/dist-server/server/thread-starters.js +33 -0
- package/dist-server/server/thread-title.js +46 -0
- package/dist-server/server/tts/fish.js +4 -5
- package/dist-server/server/tts/index.js +5 -3
- package/dist-server/server/vps-computer.js +79 -12
- package/dist-server/server/webhooks.js +18 -1
- package/dist-server/server/workspace-backup-policy.js +3 -0
- package/dist-server/server/workspace-backup.js +3 -0
- package/dist-server/server/workspace.js +65 -15
- package/dist-server/shared/workspace-backup-client.js +1 -0
- package/dist-server/src/lib/citations.js +177 -0
- package/dist-server/src/lib/composer-attachments.js +7 -1
- package/dist-server/vps-container-mcp.js +196 -15
- package/dist-server/workspace-backup.worker.js +73 -9
- package/enterprise/server/index.js +5 -1
- package/package.json +1 -1
- package/dist/assets/channel-DrfGMu7X.js +0 -1
- package/dist/assets/index-BlnCEcU6.js +0 -588
- package/dist/assets/index-DVxjOvID.css +0 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-BCZhxl7e.js +0 -8
|
@@ -52,6 +52,10 @@ import { redactSecretsInText } from "../../redact.js";
|
|
|
52
52
|
import { recoveryPromptFor } from "../../resume-recovery.js";
|
|
53
53
|
import { sessionIdlePolicy } from "../session-idle.js";
|
|
54
54
|
import { classifyError } from "../retry.js";
|
|
55
|
+
const ACCOUNT_ERROR_CODES = new Set([
|
|
56
|
+
"invalid_credentials", "inactive_subscription", "insufficient_funds", "quota_or_region_restriction",
|
|
57
|
+
]);
|
|
58
|
+
const isAccountError = (code) => code !== undefined && ACCOUNT_ERROR_CODES.has(code);
|
|
55
59
|
/** ACP vendors put the actionable cause in error.data while keeping the
|
|
56
60
|
* JSON-RPC message generic. Only surface known text fields, never a response
|
|
57
61
|
* body/config dump, and redact before bounding the displayed diagnostic. */
|
|
@@ -141,6 +145,24 @@ function acpVariantOption(result) {
|
|
|
141
145
|
};
|
|
142
146
|
}
|
|
143
147
|
const TOOL_LOG_TEXT_LIMIT = 64_000;
|
|
148
|
+
/** Every model id a session config snapshot offers under `configId`,
|
|
149
|
+
* flattening grouped options. Empty when the agent listed none. */
|
|
150
|
+
export function offeredModels(result, configId) {
|
|
151
|
+
const option = (Array.isArray(result?.configOptions) ? result.configOptions : []).find((entry) => entry?.id === configId);
|
|
152
|
+
const ids = [];
|
|
153
|
+
const visit = (entries) => {
|
|
154
|
+
if (!Array.isArray(entries))
|
|
155
|
+
return;
|
|
156
|
+
for (const entry of entries) {
|
|
157
|
+
if (typeof entry?.value === "string")
|
|
158
|
+
ids.push(entry.value);
|
|
159
|
+
else if (Array.isArray(entry?.options))
|
|
160
|
+
visit(entry.options);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
visit(option?.options);
|
|
164
|
+
return ids;
|
|
165
|
+
}
|
|
144
166
|
async function readAcpImageBlocks(images) {
|
|
145
167
|
return Promise.all(images.map(async (image) => ({
|
|
146
168
|
type: "image",
|
|
@@ -250,6 +272,11 @@ export function versionAtLeast(installed, floor) {
|
|
|
250
272
|
return true;
|
|
251
273
|
}
|
|
252
274
|
export function createAcpDriver(support) {
|
|
275
|
+
// Without a per-turn configureSession nothing would ever apply the mode
|
|
276
|
+
// that sessionScopedApproval keeps out of the spawn contract.
|
|
277
|
+
if (support.sessionScopedApproval && !support.configureSession) {
|
|
278
|
+
throw new Error(`${support.displayName}: sessionScopedApproval needs configureSession to apply each turn's mode`);
|
|
279
|
+
}
|
|
253
280
|
const DRIVER_KIND = support.driverKind;
|
|
254
281
|
const SOURCE = support.nativeSource;
|
|
255
282
|
const decodeConfig = decodeAcpConfig(support.defaultCli);
|
|
@@ -289,13 +316,18 @@ export function createAcpDriver(support) {
|
|
|
289
316
|
return env;
|
|
290
317
|
};
|
|
291
318
|
let models = support.models;
|
|
319
|
+
// Models this account itself offered (see sessionModelEnv): the
|
|
320
|
+
// discovered catalog, never the static one, or a live session's list.
|
|
321
|
+
let accountModels = null;
|
|
292
322
|
const refreshModels = async () => {
|
|
293
323
|
if (!support.resolveModels)
|
|
294
324
|
return;
|
|
295
325
|
try {
|
|
296
326
|
const resolved = await support.resolveModels(childEnv(), config, instanceId);
|
|
297
|
-
if (resolved.options.length)
|
|
327
|
+
if (resolved.options.length) {
|
|
298
328
|
models = resolved;
|
|
329
|
+
accountModels = new Set(resolved.options.map((option) => option.id));
|
|
330
|
+
}
|
|
299
331
|
}
|
|
300
332
|
catch {
|
|
301
333
|
// Keep the last usable catalog when an optional discovery source is down.
|
|
@@ -1026,8 +1058,12 @@ export function createAcpDriver(support) {
|
|
|
1026
1058
|
return { turnId };
|
|
1027
1059
|
}
|
|
1028
1060
|
const resolvedModel = support.resolveTurnModel?.(turn.model, env);
|
|
1029
|
-
support.applyTurnEnv?.(env, {
|
|
1030
|
-
|
|
1061
|
+
support.applyTurnEnv?.(env, {
|
|
1062
|
+
model: resolvedModel, requestedModel: turn.model, fullAuto: turnConfig.fullAuto === true, botId: turn.botId, cwd,
|
|
1063
|
+
});
|
|
1064
|
+
// Rebound once, before the prompt, when a fallbackModel support swaps
|
|
1065
|
+
// a model this session does not offer for one it does.
|
|
1066
|
+
let cliTurn = resolvedModel !== undefined && resolvedModel !== turn.model
|
|
1031
1067
|
? { ...turn, model: resolvedModel }
|
|
1032
1068
|
: turn;
|
|
1033
1069
|
const mcpServers = acpMcpServers(turn);
|
|
@@ -1067,7 +1103,31 @@ export function createAcpDriver(support) {
|
|
|
1067
1103
|
// riding a child spawned under the old env. Hash it so secrets
|
|
1068
1104
|
// never sit in the key itself.
|
|
1069
1105
|
const envFingerprint = createHash("sha256").update(JSON.stringify(spawnEnv)).digest("hex").slice(0, 16);
|
|
1070
|
-
|
|
1106
|
+
// A support that applies the approval mode to the session on every
|
|
1107
|
+
// turn gets the same process whatever the mode: an approval change
|
|
1108
|
+
// is one RPC, not a cold start.
|
|
1109
|
+
const contractKey = JSON.stringify([
|
|
1110
|
+
launch.command, launch.args ?? [], spawnArgs, cwd,
|
|
1111
|
+
support.sessionScopedApproval ? null : turnConfig.fullAuto === true, envFingerprint,
|
|
1112
|
+
support.spawnFingerprint?.(spawnEnv) ?? null,
|
|
1113
|
+
]);
|
|
1114
|
+
// The new-session default model rides the spawn env but stays out of
|
|
1115
|
+
// the fingerprint above: only session/new reads it, and a pooled
|
|
1116
|
+
// process switches later models over the wire as before.
|
|
1117
|
+
const launchModel = support.sessionModelEnv && cliTurn.model && /^[\w.:/-]{1,200}$/u.test(cliTurn.model)
|
|
1118
|
+
&& accountModels?.has(cliTurn.model) ? cliTurn.model : undefined;
|
|
1119
|
+
const launchEnv = support.sessionModelEnv && launchModel
|
|
1120
|
+
? { ...spawnEnv, [support.sessionModelEnv]: launchModel }
|
|
1121
|
+
: spawnEnv;
|
|
1122
|
+
// whether this turn's process was started by this turn: its model
|
|
1123
|
+
// list is then current, not a pooled process's older one
|
|
1124
|
+
let launchedThisTurn = false;
|
|
1125
|
+
const launchSession = () => {
|
|
1126
|
+
const opened = openSession(threadId, launch, [...(launch.args ?? []), ...spawnArgs], launchEnv, cwd, contractKey);
|
|
1127
|
+
opened.launchModel = launchModel;
|
|
1128
|
+
launchedThisTurn = true;
|
|
1129
|
+
return opened;
|
|
1130
|
+
};
|
|
1071
1131
|
const sessionKey = JSON.stringify(mcpServers);
|
|
1072
1132
|
if (turn.sessionReset) {
|
|
1073
1133
|
closeSession(threadId, "reset");
|
|
@@ -1092,7 +1152,7 @@ export function createAcpDriver(support) {
|
|
|
1092
1152
|
else
|
|
1093
1153
|
closeSession(threadId, "contract");
|
|
1094
1154
|
}
|
|
1095
|
-
session =
|
|
1155
|
+
session = launchSession();
|
|
1096
1156
|
sessions.set(threadId, session);
|
|
1097
1157
|
}
|
|
1098
1158
|
// `session` rebinds mid-turn: when the establishment retry below
|
|
@@ -1107,6 +1167,16 @@ export function createAcpDriver(support) {
|
|
|
1107
1167
|
rejectStartup = reject;
|
|
1108
1168
|
}) : null;
|
|
1109
1169
|
const asks = new Map();
|
|
1170
|
+
// What this turn reports and applies. A model fallback (below)
|
|
1171
|
+
// replaces both: a saved variant belongs to the model it replaced.
|
|
1172
|
+
let reportedModel = turn.model;
|
|
1173
|
+
let variant = turn.variant;
|
|
1174
|
+
// set below when this process lists fewer models than the catalog
|
|
1175
|
+
let staleProcess = false;
|
|
1176
|
+
// Set while a sessionScopedApproval support applies this turn's mode.
|
|
1177
|
+
// Still set if that throws: the pooled session may be left on an
|
|
1178
|
+
// earlier turn's looser mode, so its process must not be reused.
|
|
1179
|
+
let approvalUnconfirmed = false;
|
|
1110
1180
|
const modelOf = (result) => {
|
|
1111
1181
|
const option = (Array.isArray(result?.configOptions) ? result.configOptions : []).find((entry) => entry?.id === (support.selectModel?.configId ?? "model"));
|
|
1112
1182
|
return typeof option?.currentValue === "string" ? option.currentValue : null;
|
|
@@ -1122,7 +1192,7 @@ export function createAcpDriver(support) {
|
|
|
1122
1192
|
emit({
|
|
1123
1193
|
...base(threadId, turnId),
|
|
1124
1194
|
type: "session.model-variants",
|
|
1125
|
-
model: nativeModel === cliTurn.model ? (
|
|
1195
|
+
model: nativeModel === cliTurn.model ? (reportedModel ?? nativeModel) : nativeModel,
|
|
1126
1196
|
variants: {
|
|
1127
1197
|
options: option?.options ?? [],
|
|
1128
1198
|
...(option?.currentValue !== undefined ? { currentValue: option.currentValue } : {}),
|
|
@@ -1133,8 +1203,8 @@ export function createAcpDriver(support) {
|
|
|
1133
1203
|
if (!support.modelVariants)
|
|
1134
1204
|
throw new Error(`${support.displayName} does not support model variants`);
|
|
1135
1205
|
const option = acpVariantOption(session.sessionConfigResult);
|
|
1136
|
-
if (!option || !option.options.some((entry) => entry.id ===
|
|
1137
|
-
throw new Error(`${support.displayName} does not advertise variant ${
|
|
1206
|
+
if (!option || !option.options.some((entry) => entry.id === variant)) {
|
|
1207
|
+
throw new Error(`${support.displayName} does not advertise variant ${variant} for this model`);
|
|
1138
1208
|
}
|
|
1139
1209
|
return option;
|
|
1140
1210
|
};
|
|
@@ -1304,7 +1374,7 @@ export function createAcpDriver(support) {
|
|
|
1304
1374
|
// session/new on the replacement child.
|
|
1305
1375
|
session.current = null;
|
|
1306
1376
|
closeSession(threadId, "reestablish");
|
|
1307
|
-
session =
|
|
1377
|
+
session = launchSession();
|
|
1308
1378
|
sessions.set(threadId, session);
|
|
1309
1379
|
session.current = current;
|
|
1310
1380
|
runtimeAcceptsImages = await handshake();
|
|
@@ -1352,6 +1422,43 @@ export function createAcpDriver(support) {
|
|
|
1352
1422
|
if (support.selectModel) {
|
|
1353
1423
|
const { configId } = support.selectModel;
|
|
1354
1424
|
selectedModel = modelOf(session.sessionConfigResult);
|
|
1425
|
+
const wanted = cliTurn.model;
|
|
1426
|
+
const offered = offeredModels(session.sessionConfigResult, configId);
|
|
1427
|
+
// A process started without a default model lists exactly
|
|
1428
|
+
// what this account offers.
|
|
1429
|
+
if (support.sessionModelEnv && !session.launchModel && offered.length)
|
|
1430
|
+
accountModels = new Set(offered);
|
|
1431
|
+
// The instance catalog lists the model but a pooled process
|
|
1432
|
+
// does not: the process is older than the catalog (a provider
|
|
1433
|
+
// added since it started). That is no reason to run another
|
|
1434
|
+
// model. The switch below fails with the runtime's own words,
|
|
1435
|
+
// and the process is closed so the retry starts on a fresh
|
|
1436
|
+
// one. A process this turn started is current, so there the
|
|
1437
|
+
// catalog is the stale one and the fallback applies.
|
|
1438
|
+
staleProcess = Boolean(support.fallbackModel && wanted && !launchedThisTurn && offered.length
|
|
1439
|
+
&& !offered.includes(wanted) && models.options.some((option) => option.id === wanted));
|
|
1440
|
+
// A local inject writes its provider for this turn, so a
|
|
1441
|
+
// process started earlier may not list it yet: never replace it.
|
|
1442
|
+
if (support.fallbackModel && offered.length && !skipSubscriptionAuthForLocalInject(turn.model)
|
|
1443
|
+
&& !staleProcess && (!wanted || !offered.includes(wanted))) {
|
|
1444
|
+
const fallback = support.fallbackModel(session.sessionConfigResult, models.default);
|
|
1445
|
+
if (fallback && fallback !== wanted && offered.includes(fallback)) {
|
|
1446
|
+
const notice = wanted ? `${wanted}\n${fallback}` : null;
|
|
1447
|
+
if (notice && session.fallbackNotice !== notice) {
|
|
1448
|
+
session.fallbackNotice = notice;
|
|
1449
|
+
emit({
|
|
1450
|
+
...base(threadId, turnId),
|
|
1451
|
+
type: "runtime.notice",
|
|
1452
|
+
message: `${support.displayName} no longer offers ${wanted}, so this conversation uses ${fallback}. ` +
|
|
1453
|
+
"Choose another model for this bot to stop seeing this.",
|
|
1454
|
+
});
|
|
1455
|
+
}
|
|
1456
|
+
cliTurn = { ...cliTurn, model: fallback };
|
|
1457
|
+
current.turn = cliTurn;
|
|
1458
|
+
reportedModel = fallback;
|
|
1459
|
+
variant = undefined;
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1355
1462
|
if (cliTurn.model && cliTurn.model !== selectedModel) {
|
|
1356
1463
|
sessionResult = await request("session/set_config_option", { sessionId, configId, value: cliTurn.model }, INIT_TIMEOUT, receiveModelVariants);
|
|
1357
1464
|
selectedModel = modelOf(session.sessionConfigResult);
|
|
@@ -1363,6 +1470,7 @@ export function createAcpDriver(support) {
|
|
|
1363
1470
|
}
|
|
1364
1471
|
}
|
|
1365
1472
|
if (support.configureSession) {
|
|
1473
|
+
approvalUnconfirmed = support.sessionScopedApproval === true;
|
|
1366
1474
|
await support.configureSession({
|
|
1367
1475
|
request: (method, params, timeoutMs) => request(method, params, timeoutMs ?? SESSION_CONFIG_TIMEOUT),
|
|
1368
1476
|
sessionId,
|
|
@@ -1372,17 +1480,18 @@ export function createAcpDriver(support) {
|
|
|
1372
1480
|
? sessionResult.models.availableModels
|
|
1373
1481
|
: [],
|
|
1374
1482
|
});
|
|
1483
|
+
approvalUnconfirmed = false;
|
|
1375
1484
|
// initialize's currentModelId is the CLI default,
|
|
1376
1485
|
// not the model this turn asked for. After a successful pin,
|
|
1377
1486
|
// report the slug we set so the UI does not claim otherwise.
|
|
1378
1487
|
if (!selectedModel && cliTurn.model)
|
|
1379
1488
|
selectedModel = cliTurn.model;
|
|
1380
1489
|
}
|
|
1381
|
-
if (
|
|
1490
|
+
if (variant !== undefined) {
|
|
1382
1491
|
const option = requestedVariantOption();
|
|
1383
|
-
await request("session/set_config_option", { sessionId, configId: option.configId, value:
|
|
1384
|
-
if (requestedVariantOption().currentValue !==
|
|
1385
|
-
throw new Error(`${support.displayName} did not apply variant ${
|
|
1492
|
+
await request("session/set_config_option", { sessionId, configId: option.configId, value: variant }, SESSION_CONFIG_TIMEOUT, receiveModelVariants);
|
|
1493
|
+
if (requestedVariantOption().currentValue !== variant) {
|
|
1494
|
+
throw new Error(`${support.displayName} did not apply variant ${variant}`);
|
|
1386
1495
|
}
|
|
1387
1496
|
}
|
|
1388
1497
|
}
|
|
@@ -1421,7 +1530,7 @@ export function createAcpDriver(support) {
|
|
|
1421
1530
|
if (support.modelVariants && cliTurn.model && modelOf(session.sessionConfigResult) !== cliTurn.model) {
|
|
1422
1531
|
throw new Error(`${support.displayName} changed model before the prompt`);
|
|
1423
1532
|
}
|
|
1424
|
-
if (
|
|
1533
|
+
if (variant !== undefined && requestedVariantOption().currentValue !== variant) {
|
|
1425
1534
|
throw new Error(`${support.displayName} changed variant before the prompt`);
|
|
1426
1535
|
}
|
|
1427
1536
|
if (state.settled || state.stopped || session.closing)
|
|
@@ -1441,11 +1550,31 @@ export function createAcpDriver(support) {
|
|
|
1441
1550
|
// gemini put it under _meta. Read both rather than lose the count.
|
|
1442
1551
|
const usage = result?.usage ?? result?._meta ?? {};
|
|
1443
1552
|
if (typeof usage.inputTokens === "number" || typeof usage.outputTokens === "number") {
|
|
1553
|
+
// opencode 1.18.25 keeps only the uncached share in inputTokens
|
|
1554
|
+
// and reports cache reads beside it (omitted when 0); some
|
|
1555
|
+
// agents count them inside inputTokens. `input` is the whole
|
|
1556
|
+
// prompt with `cachedInput` naming its cached part — the same
|
|
1557
|
+
// convention as the Claude driver and the store — and
|
|
1558
|
+
// totalTokens tells the two wire shapes apart. Cache writes
|
|
1559
|
+
// stay out of `input`, exactly as in the Claude driver, and an
|
|
1560
|
+
// absent cache count emits the bare shape with no cachedInput
|
|
1561
|
+
// key: absent is not zero.
|
|
1562
|
+
const input = usage.inputTokens ?? 0;
|
|
1563
|
+
const output = usage.outputTokens ?? 0;
|
|
1564
|
+
const cachedRead = typeof usage.cachedReadTokens === "number" && usage.cachedReadTokens > 0
|
|
1565
|
+
? usage.cachedReadTokens
|
|
1566
|
+
: null;
|
|
1567
|
+
const exclusive = cachedRead !== null
|
|
1568
|
+
&& (typeof usage.totalTokens !== "number"
|
|
1569
|
+
|| usage.totalTokens >= input + output + cachedRead);
|
|
1444
1570
|
emit({
|
|
1445
1571
|
...base(threadId, turnId),
|
|
1446
1572
|
type: "thread.token-usage.updated",
|
|
1447
|
-
input:
|
|
1448
|
-
output
|
|
1573
|
+
input: cachedRead !== null && exclusive ? input + cachedRead : input,
|
|
1574
|
+
output,
|
|
1575
|
+
...(cachedRead !== null
|
|
1576
|
+
? { cachedInput: exclusive ? cachedRead : Math.min(cachedRead, input) }
|
|
1577
|
+
: {}),
|
|
1449
1578
|
});
|
|
1450
1579
|
}
|
|
1451
1580
|
const reason = result?.stopReason;
|
|
@@ -1475,12 +1604,19 @@ export function createAcpDriver(support) {
|
|
|
1475
1604
|
// classifier is preferred; loginNote remains a compatibility
|
|
1476
1605
|
// fallback for existing ACP supports.
|
|
1477
1606
|
const needsAuth = code === "invalid_credentials" || code === "inactive_subscription"
|
|
1478
|
-
|| message === support.loginNote;
|
|
1607
|
+
|| code === "insufficient_funds" || message === support.loginNote;
|
|
1608
|
+
// A key, credit, quota or subscription problem is the account's,
|
|
1609
|
+
// not the process's: the child that reported it is healthy, so it
|
|
1610
|
+
// stays pooled and the retry after a fix starts warm.
|
|
1611
|
+
const accountError = isAccountError(code);
|
|
1479
1612
|
const failure = classifyError({ text: message });
|
|
1480
1613
|
const transientStartup = failure.transient || (failure.reason === "unknown" && code === "upstream_outage");
|
|
1481
1614
|
const denied = /\b(?:(?:permission|access) denied|(?:approval|permission) (?:required|denied|rejected)|requires? (?:approval|permission)|policy (?:restriction|violation)|(?:blocked|denied|restricted) by (?:the )?policy)\b/i.test(message);
|
|
1482
|
-
|
|
1483
|
-
|
|
1615
|
+
// An unconfirmed approval switch sent no prompt and its process
|
|
1616
|
+
// is discarded either way, so a fresh one can take the turn.
|
|
1617
|
+
const retryableStartup = approvalUnconfirmed || (transientStartup && (!code || code === "upstream_outage")
|
|
1618
|
+
&& ![-32700, -32600, -32601, -32602].includes(e?.code));
|
|
1619
|
+
if (turn.startupRecovery && retryableStartup && !needsAuth && !accountError && !denied
|
|
1484
1620
|
&& !state.promptSent && !state.startupActivity && !state.producedItem && !state.text
|
|
1485
1621
|
&& !state.stopped && !session.closing && !asks.size && !current.runningTools.size) {
|
|
1486
1622
|
// Quiesce before killing: the child's close event must not
|
|
@@ -1508,7 +1644,7 @@ export function createAcpDriver(support) {
|
|
|
1508
1644
|
emit({
|
|
1509
1645
|
...base(threadId, turnId),
|
|
1510
1646
|
type: "runtime.error",
|
|
1511
|
-
message,
|
|
1647
|
+
message: accountError ? support.describeAccountError?.(code, cliTurn.model) ?? message : message,
|
|
1512
1648
|
...(needsAuth ? { setup: true } : {}),
|
|
1513
1649
|
});
|
|
1514
1650
|
// Internal RPC failures can leave a live child poisoned just
|
|
@@ -1516,10 +1652,20 @@ export function createAcpDriver(support) {
|
|
|
1516
1652
|
// start the next turn. Never replay this accepted prompt: it
|
|
1517
1653
|
// may already have executed tools. The next explicit turn can
|
|
1518
1654
|
// resume the recorded session on a fresh process.
|
|
1519
|
-
if (!needsAuth && (e?.acpPromptStall === true || e?.acpSessionFailure === true
|
|
1655
|
+
if (!needsAuth && !accountError && (e?.acpPromptStall === true || e?.acpSessionFailure === true
|
|
1520
1656
|
|| code === "upstream_outage") && session.child.exitCode === null && !session.closing) {
|
|
1521
1657
|
closeSession(threadId, e?.acpPromptStall === true ? "prompt-stall" : "rpc-failure");
|
|
1522
1658
|
}
|
|
1659
|
+
else if (approvalUnconfirmed && session.child.exitCode === null && !session.closing) {
|
|
1660
|
+
// Fail closed: the session may still hold a looser mode from
|
|
1661
|
+
// an earlier turn. Nothing was prompted; the next turn starts
|
|
1662
|
+
// a fresh process, whose session begins in the runtime's own
|
|
1663
|
+
// default mode, and applies its mode there.
|
|
1664
|
+
closeSession(threadId, "approval-unconfirmed");
|
|
1665
|
+
}
|
|
1666
|
+
else if (staleProcess && !state.promptSent && session.child.exitCode === null && !session.closing) {
|
|
1667
|
+
closeSession(threadId, "stale-models");
|
|
1668
|
+
}
|
|
1523
1669
|
settle(threadId, session, false, needsAuth ? "auth_required" : "rpc_error");
|
|
1524
1670
|
}
|
|
1525
1671
|
}
|