openmausbot 0.1.87 → 0.1.89
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/README.md +2 -1
- package/dist/assets/{abnfDiagram-O67JEVCF-BTiuL79F.js → abnfDiagram-O67JEVCF-BwaMaZEm.js} +1 -1
- package/dist/assets/{arc-C88Blc2v.js → arc-Be-8GKxd.js} +1 -1
- package/dist/assets/{architectureDiagram-NJMV4G6O-BZsJIZrb.js → architectureDiagram-NJMV4G6O-Dp_U2Kv1.js} +1 -1
- package/dist/assets/{blockDiagram-BEXU5L5S-CVeMe9Ah.js → blockDiagram-BEXU5L5S-DQjUjYUk.js} +1 -1
- package/dist/assets/{c4Diagram-YGBWAQC7-Cedk3Bhx.js → c4Diagram-YGBWAQC7-Bd3aE4Kc.js} +1 -1
- package/dist/assets/channel-DqY_gMLg.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-D-VRqzTB.js → chunk-2Q5K7J3B-BijjTBeJ.js} +1 -1
- package/dist/assets/{chunk-3FUC2YCW-BChov01Y.js → chunk-3FUC2YCW-CMGdwvzW.js} +2 -2
- package/dist/assets/{chunk-5VM5RSS4-C-TfwvHy.js → chunk-5VM5RSS4-0G09BfsZ.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-Db7CGj5Y.js → chunk-GTNCS2PH-FOp3vSHe.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-CNvIfqKT.js → chunk-GWA4HPMP-Dv-20VIn.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-C_bEp5tq.js → chunk-JWPE2WC7-CRgrdFcz.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-Coi4BOls.js → chunk-NETBCI7D-3hP4f7QB.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-CzylEuL3.js → chunk-WEXAMYUT-CG98IjlG.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-m7p7ztaD.js → chunk-XXDRQBXY-BrSTC9xG.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-BMy6o9x2.js → classDiagram-v2-NBCMYWYE-rpQQQDFX.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-BDQ__0_J.js → cose-bilkent-JH36ORCC-G6RSafHA.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-Djv4ghUb.js → cynefin-OW5HDTMX-CXjv-IM2.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-BULrlGYH.js → cynefinDiagram-VND7K2PF-Dw07MR9f.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-D-jrT_hU.js → dagre-6A5THRUB-BwPnlTg2.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-p2UhQmfL.js → diagram-22UHCM2B-Cm6xcMiz.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-DHvGWZ7q.js → diagram-3UASUU5V-CDpogPbE.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-ChhKDot-.js → diagram-ATOU4E4O-BV8z3Pnc.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-B-T7A43R.js → diagram-CDSNMT55-CKCQhOOC.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-CJm8ENNY.js → diagram-MLGK6HIB-FGvJg69C.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-510tlza8.js → diagram-MPIPVDR6-CwATvgnS.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-PkvRPHel.js → ebnfDiagram-ZINNZB2B-CH_c3SSC.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-BVBVPoX3.js → elk-276RUBZZ-3guZi71R.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-BgYMVm7L.js → erDiagram-OPXOYQCR-BMHphR2n.js} +1 -1
- package/dist/assets/{flowDiagram-KWPJA3E3-BGqsnc_3.js → flowDiagram-KWPJA3E3-Vm5DkW5x.js} +1 -1
- package/dist/assets/{ganttDiagram-FUAMR5RP-D_-UeReH.js → ganttDiagram-FUAMR5RP-DO-1ct07.js} +1 -1
- package/dist/assets/{gitGraphDiagram-X574FWY7-D0uooONx.js → gitGraphDiagram-X574FWY7-nxeksEIN.js} +1 -1
- package/dist/assets/index-BIMdIbaw.css +1 -0
- package/dist/assets/index-BVPdbq2r.js +583 -0
- package/dist/assets/{index-zRsJERGJ.js → index-Oxk9x45i.js} +1 -1
- package/dist/assets/{infoDiagram-VRGFBTTK-B9e1z77f.js → infoDiagram-VRGFBTTK-Dkcvr0p7.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-BB4vNwvD.js → ishikawaDiagram-OU5B5YK6-BwVibkoO.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-DpkosOTB.js → journeyDiagram-ZHPQQLJL-ByiBZqgG.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-DbDP4kUu.js → kanban-definition-PNTS6WVX-qqjgXBF3.js} +1 -1
- package/dist/assets/{linear-DBJGPmjT.js → linear-CtJnKe6A.js} +1 -1
- package/dist/assets/{mermaid.core-DWPRi-ue.js → mermaid.core-BEiDklrY.js} +5 -5
- package/dist/assets/{mindmap-definition-NLK3R4M7-nszBbJmx.js → mindmap-definition-NLK3R4M7-DfIYHia9.js} +1 -1
- package/dist/assets/{pegDiagram-GJSIUBJH-CWykJWu_.js → pegDiagram-GJSIUBJH-CYyRqa1a.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-g3Da-Vnv.js → pieDiagram-5QR66LMP-hOToy_zH.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-Wzrlpzgh.js → quadrantDiagram-O4NWA36T-DKAsNN9I.js} +1 -1
- package/dist/assets/{railroadDiagram-XR7U4H2S-moHTFMC0.js → railroadDiagram-XR7U4H2S-BSA-d8JU.js} +1 -1
- package/dist/assets/{requirementDiagram-PLB6GJNP-CwfXdFH8.js → requirementDiagram-PLB6GJNP-CSmlvb-p.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-3ZROYvnq.js → sankeyDiagram-IPEJSGJF-CD5F6fnG.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-E1TBebP8.js → sequenceDiagram-PO4LG4MO-JUq5sx6d.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-DHKEU5pu.js → sizeCapture-INFHLROL-BjIS-Stk.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK-BguEj09D.js → stateDiagram-v2-GCMORJYK-DklkUxOg.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-C7IDYnhd.js → swimlanes-2SLR337P-CsjIU6be.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-Bk14M0_B.js +8 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-DCW-F5PW.js → timeline-definition-EJHVYXUP-BkFqK7AB.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-xBMFtWYV.js → usecaseDiagram-POWQR4AR-70gJB1EU.js} +1 -1
- package/dist/assets/{vennDiagram-UO4OBE2U-B5nIkuH8.js → vennDiagram-UO4OBE2U-83Tm62dN.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-DYFAhdbr.js → wardleyDiagram-VNRHLVJA-tTmseoX9.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-DeXwX6Cz.js → xychartDiagram-PMCCYNJV-R1LBA55a.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/companion/src/routes.js +8 -1
- package/dist-server/connector-proxy.js +105 -1
- package/dist-server/container-mcp.js +11 -1
- package/dist-server/drivers/agents-proxy.js +277 -22
- package/dist-server/index.js +23722 -20272
- package/dist-server/local-computer-proxy.js +2 -0
- package/dist-server/mcp-gate.js +2 -0
- package/dist-server/mcp-server.js +5 -3
- package/dist-server/message-search.worker.js +52 -0
- package/dist-server/openmausbot.js +755 -404
- package/dist-server/pair-cli.js +755 -404
- package/dist-server/server/admission.js +81 -0
- package/dist-server/server/aside-queue.js +294 -0
- package/dist-server/server/attachments.js +43 -1
- package/dist-server/server/automatic-recovery.js +15 -0
- package/dist-server/server/bot-attachment.js +107 -0
- package/dist-server/server/bot-overview.js +19 -0
- package/dist-server/server/composio.js +297 -1
- package/dist-server/server/config.js +34 -3
- package/dist-server/server/connector-advertisement.js +165 -0
- package/dist-server/server/connector-proxy.js +14 -2
- package/dist-server/server/connector-verdict.js +31 -4
- package/dist-server/server/container-computer.js +70 -0
- package/dist-server/server/contracts.js +32 -0
- package/dist-server/server/drivers/acp/core.js +148 -23
- package/dist-server/server/drivers/acp/cursor.js +3 -7
- package/dist-server/server/drivers/acp/droid.js +4 -7
- package/dist-server/server/drivers/acp/gemini.js +4 -4
- package/dist-server/server/drivers/acp/grok.js +4 -9
- package/dist-server/server/drivers/acp/hermes.js +3 -5
- package/dist-server/server/drivers/acp/opencode-go.js +2 -5
- package/dist-server/server/drivers/acp/qwen.js +12 -15
- package/dist-server/server/drivers/agents-call.js +137 -10
- package/dist-server/server/drivers/agents-catalog.js +100 -6
- package/dist-server/server/drivers/boxagent.js +3 -17
- package/dist-server/server/drivers/claude.js +31 -17
- package/dist-server/server/drivers/codex-catalog.js +14 -6
- package/dist-server/server/drivers/codex-identity.js +2 -1
- package/dist-server/server/drivers/codex-release.js +48 -0
- package/dist-server/server/drivers/codex.js +7 -25
- package/dist-server/server/drivers/openai-chat.js +31 -3
- package/dist-server/server/drivers/pi.js +237 -30
- package/dist-server/server/drivers/prompt-split.js +32 -9
- package/dist-server/server/drivers/session-idle.js +29 -0
- package/dist-server/server/env-path.js +15 -0
- package/dist-server/server/guarded-requests.js +2 -2
- package/dist-server/server/harness-capabilities.js +61 -0
- package/dist-server/server/index.js +1173 -262
- package/dist-server/server/message-db.js +22 -42
- package/dist-server/server/message-file.js +9 -1
- package/dist-server/server/message-search-query.js +43 -0
- package/dist-server/server/message-search-worker.js +80 -0
- package/dist-server/server/message-search.worker.js +21 -0
- package/dist-server/server/model-requests.js +248 -0
- package/dist-server/server/options-card.js +21 -0
- package/dist-server/server/org-library.js +15 -1
- package/dist-server/server/peer-delivery.js +60 -0
- package/dist-server/server/peer-roster.js +57 -0
- package/dist-server/server/presets.js +32 -32
- package/dist-server/server/profile-requests.js +66 -39
- package/dist-server/server/profile-revision.js +9 -1
- package/dist-server/server/profile-versions.js +6 -3
- package/dist-server/server/routine-requests.js +29 -7
- package/dist-server/server/skill-fetch.js +72 -14
- package/dist-server/server/skills.js +1 -0
- package/dist-server/server/store.js +32 -2
- package/dist-server/server/system-prompt.js +37 -2
- package/dist-server/server/team-setup-requests.js +124 -23
- package/dist-server/server/thread-events.js +3 -1
- package/dist-server/server/tightening-requests.js +405 -0
- package/dist-server/server/tts/chatterbox.js +27 -5
- package/dist-server/server/usage-ledger.js +7 -1
- package/dist-server/server/webhooks.js +21 -3
- package/dist-server/server/workspace-backup.js +40 -3
- package/dist-server/server/workspace-backup.worker.js +10 -0
- package/dist-server/shared/ask-question.js +19 -0
- package/dist-server/shared/credential-request.js +5 -1
- package/dist-server/shared/line-diff.js +23 -0
- package/dist-server/shared/model-request.js +1 -0
- package/dist-server/shared/options-card.js +51 -0
- package/dist-server/shared/profile-request.js +5 -4
- package/dist-server/shared/tightening-request.js +87 -0
- package/dist-server/src/lib/composer-attachments.js +42 -3
- package/dist-server/vps-container-mcp.js +11 -1
- package/dist-server/workspace-backup.worker.js +19482 -0
- package/package.json +1 -1
- package/dist/assets/channel-taCNRlUU.js +0 -1
- package/dist/assets/index-BwdMAQ6p.css +0 -1
- package/dist/assets/index-ByJennr7.js +0 -581
- package/dist/assets/swimlanesDiagram-TC7HE7FX-BpEdoB2e.js +0 -8
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { newEventId, newId } from "../contracts.js";
|
|
2
2
|
import { appendNative } from "./native.js";
|
|
3
|
-
import { OMB_ASK_TOOL, answerWithoutPreamble, askQuestionSummary, capAnswerEcho, parseOmbAskQuestions, questionChoices, stripOmbAskBlock, } from "../../shared/ask-question.js";
|
|
3
|
+
import { OMB_ASK_TOOL, answerWithoutPreamble, askQuestionSummary, capAnswerEcho, ombAskProtocolPrompt, parseOmbAskQuestions, questionChoices, stripOmbAskBlock, } from "../../shared/ask-question.js";
|
|
4
4
|
const DRIVER_KIND = "boxAgent";
|
|
5
5
|
// overridable so tests and a dev backend can be pointed at instead of the live provider
|
|
6
6
|
const BOX_API = process.env.OMB_BOX_API || "https://ascii.dev/api/box/v1";
|
|
@@ -12,20 +12,6 @@ const MODELS = {
|
|
|
12
12
|
{ id: "gpt-5.4", label: "GPT-5.4 (Codex) · on the box" },
|
|
13
13
|
],
|
|
14
14
|
};
|
|
15
|
-
/** The ask contract appended to every prompt. The box harness cannot pause
|
|
16
|
-
* mid-run, so a question rides the run's final output as a fenced block and
|
|
17
|
-
* OMB parses it at settle — the turn-held transport. */
|
|
18
|
-
const ASK_PROTOCOL = [
|
|
19
|
-
"",
|
|
20
|
-
"## Asking the person a question",
|
|
21
|
-
"When a decision belongs to the person, end your reply with a fenced block exactly like this:",
|
|
22
|
-
"",
|
|
23
|
-
"```omb-ask",
|
|
24
|
-
'{"questions":[{"question":"Ship the release now?","header":"Release","options":[{"label":"Ship now"},{"label":"Wait for the QA signoff"}]}]}',
|
|
25
|
-
"```",
|
|
26
|
-
"",
|
|
27
|
-
"The block must be the last thing in your reply. You may ask up to 6 questions at once, each with up to 12 options; the person can always answer in their own words. Their answers arrive on your next prompt as `Q:`/`A:` lines — never invent them.",
|
|
28
|
-
].join("\n");
|
|
29
15
|
/** Any fence whose info string names the ask protocol, even when its body
|
|
30
16
|
* does not parse: the marker for "the model tried to ask and failed". */
|
|
31
17
|
const ASK_FENCE_ANY = /(^|\n)[ \t]{0,3}(`{3,}|~{3,})[ \t]*omb-ask\b/;
|
|
@@ -118,7 +104,7 @@ export const BoxAgentDriver = {
|
|
|
118
104
|
const prompt = [
|
|
119
105
|
turn.system,
|
|
120
106
|
"You are working on the assigned cloud computer — use its desktop, Chrome, and shell within the access described above.",
|
|
121
|
-
|
|
107
|
+
ombAskProtocolPrompt(),
|
|
122
108
|
...(correction ? [correction] : []),
|
|
123
109
|
"",
|
|
124
110
|
turn.text,
|
|
@@ -388,7 +374,7 @@ export const BoxAgentDriver = {
|
|
|
388
374
|
snapshot,
|
|
389
375
|
adapter: {
|
|
390
376
|
provider: DRIVER_KIND,
|
|
391
|
-
capabilities: { sessionModelSwitch: "in-session" },
|
|
377
|
+
capabilities: { sessionModelSwitch: "in-session", remoteAgent: true, usesCloudComputer: true },
|
|
392
378
|
sendTurn,
|
|
393
379
|
interruptTurn: async (threadId) => active.get(threadId)?.cancel(),
|
|
394
380
|
respondToRequest: async (threadId, requestId, decision) => {
|
|
@@ -23,6 +23,8 @@ import { gateServer, resultBudget } from "../mcp-gate-config.js";
|
|
|
23
23
|
import { newEventId, newId } from "../contracts.js";
|
|
24
24
|
import { askInputSummary, commandSummary, toolDetailPreview } from "../tool-summary.js";
|
|
25
25
|
import { classifyError, computeBackoff, interruptibleDelay, RETRY_MAX_ATTEMPTS } from "./retry.js";
|
|
26
|
+
import { sessionIdlePolicy } from "./session-idle.js";
|
|
27
|
+
import { parseVersionTriple, versionAtLeast } from "./acp/core.js";
|
|
26
28
|
import { applyClaudeInject, decodeInjectId, mergeLocalInject, probeLocalInjects, resolveInjectId, } from "./local-inject.js";
|
|
27
29
|
import { appendNative } from "./native.js";
|
|
28
30
|
import { permissionCommand, permissionLaunchCwd } from "./permission-command.js";
|
|
@@ -109,6 +111,17 @@ export function claudeAuthFailure(frame, text) {
|
|
|
109
111
|
return false;
|
|
110
112
|
return frame.error === "authentication_failed" || classifyError({ text }).reason === "auth";
|
|
111
113
|
}
|
|
114
|
+
/** A model newer than the installed Claude Code: the API refuses it and the
|
|
115
|
+
* CLI relays that as an api-error frame ("Claude Code 2.1.268 does not
|
|
116
|
+
* support this model; version 2.1.280 or newer is required. Run 'claude
|
|
117
|
+
* update'…"). It names no model, so it covers every model it happens for.
|
|
118
|
+
* Like a signed-out turn, it is fixed by changing the install, not by a
|
|
119
|
+
* retry, so the UI offers to run the update. */
|
|
120
|
+
export function claudeVersionTooOld(frame, text) {
|
|
121
|
+
if (frame.is_api_error_message !== true && typeof frame.error !== "string")
|
|
122
|
+
return false;
|
|
123
|
+
return /\bClaude Code v?\d+(?:\.\d+)+ does not support this model\b/i.test(text);
|
|
124
|
+
}
|
|
112
125
|
/** The CLI environment shared by auth probes and real turns.
|
|
113
126
|
*
|
|
114
127
|
* Subscription users can be billed pay-as-you-go if an inherited API key
|
|
@@ -275,17 +288,7 @@ export const CLAUDE_CONTEXT_CONTROL_MIN_VERSION = CLAUDE_FLAG_FLOORS["--system-p
|
|
|
275
288
|
* is the version. Null when nothing parses, e.g. a wrapper that prints its
|
|
276
289
|
* own banner first — see claudeCliSupports for how that is treated. */
|
|
277
290
|
export function parseClaudeCliVersion(stdout) {
|
|
278
|
-
|
|
279
|
-
if (!match)
|
|
280
|
-
return null;
|
|
281
|
-
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
282
|
-
}
|
|
283
|
-
function versionAtLeast(installed, floor) {
|
|
284
|
-
for (let i = 0; i < 3; i += 1) {
|
|
285
|
-
if (installed[i] !== floor[i])
|
|
286
|
-
return installed[i] > floor[i];
|
|
287
|
-
}
|
|
288
|
-
return true;
|
|
291
|
+
return parseVersionTriple(stdout ?? "");
|
|
289
292
|
}
|
|
290
293
|
/** Whether a CLI reporting `version` accepts `flag`. A version that could
|
|
291
294
|
* not be parsed counts as current: every CLI that predates a floor prints a
|
|
@@ -857,11 +860,7 @@ export const ClaudeDriver = {
|
|
|
857
860
|
// one active turn per thread; a second send while busy is a caller bug
|
|
858
861
|
const active = new Map();
|
|
859
862
|
const sessions = new Map();
|
|
860
|
-
const
|
|
861
|
-
const sessionIdleMinimum = Number.isFinite(configuredIdleMinimum) && configuredIdleMinimum > 0
|
|
862
|
-
? configuredIdleMinimum
|
|
863
|
-
: 10_000;
|
|
864
|
-
const SESSION_IDLE_MS = Math.max(sessionIdleMinimum, Number(process.env.OMB_CLAUDE_SESSION_IDLE_MS) || 10 * 60_000);
|
|
863
|
+
const { idleMs: SESSION_IDLE_MS } = sessionIdlePolicy("CLAUDE");
|
|
865
864
|
const stopSession = (session) => {
|
|
866
865
|
void killCliTree(session.child).then((stopped) => {
|
|
867
866
|
if (stopped)
|
|
@@ -1446,6 +1445,11 @@ export const ClaudeDriver = {
|
|
|
1446
1445
|
// A settled turn owns no retry budget. Retained CLI sessions may run
|
|
1447
1446
|
// many later turns on this thread, and each must start fresh.
|
|
1448
1447
|
retryState.delete(threadId);
|
|
1448
|
+
// Updating the executable cannot update code already loaded by this
|
|
1449
|
+
// pooled child. Retire it before announcing completion so an explicit
|
|
1450
|
+
// retry resumes on a fresh process; healthy sibling sessions stay warm.
|
|
1451
|
+
if (stopReason === "update_required")
|
|
1452
|
+
closeSession(threadId, "update required");
|
|
1449
1453
|
emit({ ...base(threadId, t.turnId), type: "turn.completed", ok, stopReason, cost, ...(usage ? { usage } : {}) });
|
|
1450
1454
|
if (session.child.exitCode === null && !session.closing)
|
|
1451
1455
|
armIdle(threadId);
|
|
@@ -1508,6 +1512,12 @@ export const ClaudeDriver = {
|
|
|
1508
1512
|
emit({ ...base(threadId, currentTurnId()), type: "runtime.error", message: text, setup: true });
|
|
1509
1513
|
break;
|
|
1510
1514
|
}
|
|
1515
|
+
if (claudeVersionTooOld(o, text)) {
|
|
1516
|
+
if (session.turn)
|
|
1517
|
+
session.turn.updateRequired = true;
|
|
1518
|
+
emit({ ...base(threadId, currentTurnId()), type: "runtime.error", message: text, setup: true, claudeUpdate: true });
|
|
1519
|
+
break;
|
|
1520
|
+
}
|
|
1511
1521
|
if (text.trim()) {
|
|
1512
1522
|
// The CLI's own report of any other API error is still shown,
|
|
1513
1523
|
// but marked: the model never produced it.
|
|
@@ -1570,7 +1580,11 @@ export const ClaudeDriver = {
|
|
|
1570
1580
|
// are billed (at the cache rate) and they fill the window — but
|
|
1571
1581
|
// they are reported separately too, so the UI can show how much
|
|
1572
1582
|
// of the figure was context re-read rather than new text.
|
|
1573
|
-
settle(o.is_error !== true, session.turn?.authFailed
|
|
1583
|
+
settle(o.is_error !== true, session.turn?.authFailed
|
|
1584
|
+
? "auth_required"
|
|
1585
|
+
: session.turn?.updateRequired
|
|
1586
|
+
? "update_required"
|
|
1587
|
+
: o.stop_reason ?? o.terminal_reason ?? null, o.total_cost_usd ?? null, o.usage
|
|
1574
1588
|
? {
|
|
1575
1589
|
input: (o.usage.input_tokens || 0) + (o.usage.cache_read_input_tokens || 0) + (o.usage.cache_creation_input_tokens || 0),
|
|
1576
1590
|
output: o.usage.output_tokens || 0,
|
|
@@ -6,11 +6,17 @@
|
|
|
6
6
|
import { readdirSync, readFileSync } from "node:fs";
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
import { basename, join } from "node:path";
|
|
9
|
+
import { qualifiedModelLabel } from "../contracts.js";
|
|
9
10
|
import { killCliTree, spawnCli } from "../procs.js";
|
|
11
|
+
import { codexHome } from "./codex-identity.js";
|
|
10
12
|
import { mergeLocalInject } from "./local-inject.js";
|
|
11
13
|
export const STATIC_CODEX_MODELS = {
|
|
12
14
|
default: "gpt-5.6-sol",
|
|
13
15
|
options: [
|
|
16
|
+
// Only used when live discovery fails; these rows do not establish account access.
|
|
17
|
+
{ id: "gpt-6-astra", label: "GPT-6 Astra (availability unverified)" },
|
|
18
|
+
{ id: "gpt-6-sol", label: "GPT-6 Sol (availability unverified)" },
|
|
19
|
+
{ id: "gpt-6-luna", label: "GPT-6 Luna (availability unverified)" },
|
|
14
20
|
{ id: "gpt-5.6-sol", label: "GPT-5.6 Sol" },
|
|
15
21
|
{ id: "gpt-5.6-terra", label: "GPT-5.6 Terra" },
|
|
16
22
|
{ id: "gpt-5.6-luna", label: "GPT-5.6 Luna" },
|
|
@@ -154,11 +160,6 @@ export function readCodexAppServerModelCatalog(cli, env, timeoutMs = 8_000) {
|
|
|
154
160
|
request("initialize", { clientInfo: { name: "openmausbot", version: "1" } }, "initialize");
|
|
155
161
|
});
|
|
156
162
|
}
|
|
157
|
-
export function codexHome(env) {
|
|
158
|
-
if (env.CODEX_HOME)
|
|
159
|
-
return env.CODEX_HOME;
|
|
160
|
-
return join(env.HOME || env.USERPROFILE || homedir(), ".codex");
|
|
161
|
-
}
|
|
162
163
|
function unquote(raw) {
|
|
163
164
|
let value = raw.trim();
|
|
164
165
|
const hash = value.indexOf(" #");
|
|
@@ -253,7 +254,10 @@ function niceLabel(model, provider, known, named) {
|
|
|
253
254
|
if (named.has(encoded))
|
|
254
255
|
return named.get(encoded);
|
|
255
256
|
const host = providerName(provider, known);
|
|
256
|
-
return
|
|
257
|
+
return qualifiedModelLabel(model, host, {
|
|
258
|
+
redundantWhen: () => host === provider,
|
|
259
|
+
decorate: (facet) => ` (${facet})`,
|
|
260
|
+
});
|
|
257
261
|
}
|
|
258
262
|
function collectCatalogNames(home) {
|
|
259
263
|
const named = new Map();
|
|
@@ -338,7 +342,11 @@ async function probeProviderModels(provider, env, fetchImpl) {
|
|
|
338
342
|
/** Local slugs Codex already knows, plus the three official cloud rows. */
|
|
339
343
|
export async function readCodexModelCatalog(env = process.env, fetchImpl = fetch, cli) {
|
|
340
344
|
const official = (cli ? await readCodexAppServerModelCatalog(cli, env) : null) ?? STATIC_CODEX_MODELS;
|
|
345
|
+
// Match the identity boundary: an explicitly relative CODEX_HOME or user
|
|
346
|
+
// home must not load a catalog from a different directory.
|
|
341
347
|
const home = codexHome(env);
|
|
348
|
+
if (!home)
|
|
349
|
+
return mergeLocalInject(official, env, fetchImpl);
|
|
342
350
|
const mainText = readText(join(home, "config.toml"));
|
|
343
351
|
if (!mainText)
|
|
344
352
|
return mergeLocalInject(official, env, fetchImpl);
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// old file's email. Only the protocol's display email leaves this helper.
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { isAbsolute, join, resolve } from "node:path";
|
|
6
|
+
import { userHome } from "../env-path.js";
|
|
6
7
|
import { killCliTree, spawnCli } from "../procs.js";
|
|
7
8
|
const MAX_OUTPUT = 16_384;
|
|
8
9
|
const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
@@ -11,7 +12,7 @@ const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
|
11
12
|
export function codexHome(env) {
|
|
12
13
|
if (env.CODEX_HOME)
|
|
13
14
|
return isAbsolute(env.CODEX_HOME) ? resolve(env.CODEX_HOME) : null;
|
|
14
|
-
const home = env
|
|
15
|
+
const home = userHome(env);
|
|
15
16
|
return isAbsolute(home) ? join(home, ".codex") : null;
|
|
16
17
|
}
|
|
17
18
|
/** Read-only account metadata. Unsupported CLIs and uncertain responses stay
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Release freshness is independent of account-specific model availability.
|
|
2
|
+
function parseCodexReleaseVersion(value) {
|
|
3
|
+
const match = /^(?:.*\bcodex-cli\s+)?v?(\d+)\.(\d+)\.(\d+)(-[0-9a-z.-]+)?(?:\+[0-9a-z.-]+)?$/i.exec(value.trim());
|
|
4
|
+
return match ? { parts: match.slice(1, 4).map(Number), prerelease: Boolean(match[4]) } : null;
|
|
5
|
+
}
|
|
6
|
+
export function codexVersionBehind(installed, latest) {
|
|
7
|
+
const a = parseCodexReleaseVersion(installed);
|
|
8
|
+
const b = parseCodexReleaseVersion(latest);
|
|
9
|
+
if (!a || !b || b.prerelease)
|
|
10
|
+
return false;
|
|
11
|
+
for (let i = 0; i < 3; i++) {
|
|
12
|
+
if (a.parts[i] !== b.parts[i])
|
|
13
|
+
return a.parts[i] < b.parts[i];
|
|
14
|
+
}
|
|
15
|
+
return a.prerelease;
|
|
16
|
+
}
|
|
17
|
+
export function createCodexReleaseReader(fetchImpl = fetch, now = Date.now) {
|
|
18
|
+
let cached;
|
|
19
|
+
let expires = 0;
|
|
20
|
+
return () => {
|
|
21
|
+
if (cached && now() < expires)
|
|
22
|
+
return cached;
|
|
23
|
+
expires = now() + 60 * 60 * 1000;
|
|
24
|
+
cached = (async () => {
|
|
25
|
+
try {
|
|
26
|
+
const response = await fetchImpl("https://registry.npmjs.org/@openai/codex/latest", {
|
|
27
|
+
signal: AbortSignal.timeout(3000),
|
|
28
|
+
headers: { Accept: "application/json" },
|
|
29
|
+
});
|
|
30
|
+
if (!response.ok)
|
|
31
|
+
throw new Error("Release lookup failed");
|
|
32
|
+
const payload = await response.json();
|
|
33
|
+
const version = typeof payload.version === "string" ? payload.version : "";
|
|
34
|
+
const parsed = parseCodexReleaseVersion(version);
|
|
35
|
+
if (!parsed || parsed.prerelease)
|
|
36
|
+
throw new Error("Invalid stable release");
|
|
37
|
+
return version;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Offline checks must not make an otherwise usable engine unavailable.
|
|
41
|
+
expires = now() + 5 * 60 * 1000;
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
})();
|
|
45
|
+
return cached;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export const readLatestCodexRelease = createCodexReleaseReader();
|
|
@@ -31,10 +31,9 @@ import { codexAccountEmail } from "./codex-identity.js";
|
|
|
31
31
|
import { classifyResumeFailure, mayReplay, recoveryPromptFor } from "../resume-recovery.js";
|
|
32
32
|
import { extractMcpImages } from "../mcp-tool-images.js";
|
|
33
33
|
import { parseProtocolAskQuestions, questionAnswersById, questionChoices } from "../../shared/ask-question.js";
|
|
34
|
+
import { codexVersionBehind, readLatestCodexRelease } from "./codex-release.js";
|
|
34
35
|
export { decodeCodexSelection, readCodexModelCatalog, STATIC_CODEX_MODELS } from "./codex-catalog.js";
|
|
35
36
|
const DRIVER_KIND = "codex";
|
|
36
|
-
const ASTRA_MODEL_ID = "gpt-6-astra";
|
|
37
|
-
const ASTRA_MIN_CODEX_VERSION = [0, 153, 1];
|
|
38
37
|
class CodexRpcError extends Error {
|
|
39
38
|
code;
|
|
40
39
|
constructor(error) {
|
|
@@ -49,23 +48,6 @@ function missingNativeCodexThread(error, cursor) {
|
|
|
49
48
|
return error instanceof CodexRpcError && error.code === -32600 &&
|
|
50
49
|
error.message === `no rollout found for thread id ${cursor}`;
|
|
51
50
|
}
|
|
52
|
-
/** Whether an installed Codex predates the release that exposes GPT-6 Astra
|
|
53
|
-
* through app-server. Unknown version formats stay quiet: a bad guess should
|
|
54
|
-
* never nag someone whose custom build may already support the model. */
|
|
55
|
-
export function codexPredatesAstra(version) {
|
|
56
|
-
const value = version.trim();
|
|
57
|
-
const match = /\bcodex-cli\s+v?(\d+)\.(\d+)\.(\d+)(?:[-+][0-9a-z.-]+)?(?![\d.])\b/i.exec(value)
|
|
58
|
-
?? /^v?(\d+)\.(\d+)\.(\d+)(?:[-+][0-9a-z.-]+)?$/i.exec(value);
|
|
59
|
-
if (!match)
|
|
60
|
-
return false;
|
|
61
|
-
const installed = match.slice(1, 4).map(Number);
|
|
62
|
-
for (let i = 0; i < ASTRA_MIN_CODEX_VERSION.length; i += 1) {
|
|
63
|
-
if (installed[i] !== ASTRA_MIN_CODEX_VERSION[i]) {
|
|
64
|
-
return installed[i] < ASTRA_MIN_CODEX_VERSION[i];
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
51
|
/** Ask the configured executable to update itself. This matters when the user
|
|
70
52
|
* selected a non-PATH Codex: installing a second global copy would leave
|
|
71
53
|
* OpenMausBot pointing at the old binary. */
|
|
@@ -85,13 +67,13 @@ export function codexUpdateCommand(cli, platform = process.platform) {
|
|
|
85
67
|
const command = (tokens.length > 0 ? tokens : [trimmed]).map(quote).join(" ");
|
|
86
68
|
return platform === "win32" ? `& ${command} update` : `${command} update`;
|
|
87
69
|
}
|
|
88
|
-
function
|
|
89
|
-
|
|
70
|
+
async function codexReleaseUpdate(version, cli) {
|
|
71
|
+
const latest = await readLatestCodexRelease();
|
|
72
|
+
if (!latest || !codexVersionBehind(version, latest))
|
|
90
73
|
return undefined;
|
|
91
|
-
}
|
|
92
74
|
return {
|
|
93
|
-
title:
|
|
94
|
-
message:
|
|
75
|
+
title: `Update Codex to ${latest}`,
|
|
76
|
+
message: `A newer stable Codex CLI is available (installed: ${version}). Update it, then refresh models. Model availability also depends on your signed-in account.`,
|
|
95
77
|
command: codexUpdateCommand(cli),
|
|
96
78
|
};
|
|
97
79
|
}
|
|
@@ -1579,7 +1561,7 @@ export const CodexDriver = {
|
|
|
1579
1561
|
version,
|
|
1580
1562
|
authenticated,
|
|
1581
1563
|
...(email ? { account: { email } } : {}),
|
|
1582
|
-
update:
|
|
1564
|
+
update: await codexReleaseUpdate(version, config.cli),
|
|
1583
1565
|
billing: "subscription",
|
|
1584
1566
|
};
|
|
1585
1567
|
};
|
|
@@ -332,7 +332,8 @@ export function createOpenAIChatRuntime(options) {
|
|
|
332
332
|
});
|
|
333
333
|
let resolveDone;
|
|
334
334
|
const done = new Promise((resolve) => { resolveDone = resolve; });
|
|
335
|
-
|
|
335
|
+
const turnEntry = { abort, turnId, done, approval, asides: [] };
|
|
336
|
+
active.set(turn.threadId, turnEntry);
|
|
336
337
|
emit({ ...base(turn.threadId, turnId), type: "turn.started" });
|
|
337
338
|
emit({ ...base(turn.threadId, turnId), type: "session.started", sessionId: null, model });
|
|
338
339
|
void (async () => {
|
|
@@ -358,6 +359,13 @@ export function createOpenAIChatRuntime(options) {
|
|
|
358
359
|
}
|
|
359
360
|
for (let round = 0; round < 16; round++) {
|
|
360
361
|
abort.signal.throwIfAborted();
|
|
362
|
+
// Drain parked mid-turn input here, before the next completion
|
|
363
|
+
// request: the previous round's tool results are complete, so a
|
|
364
|
+
// user message lands on a consistent array (never inside a tool
|
|
365
|
+
// batch) and the provider sees it as the newest input.
|
|
366
|
+
const parked = turnEntry.asides.splice(0);
|
|
367
|
+
if (parked.length)
|
|
368
|
+
messages.push({ role: "user", content: parked.join("\n\n") });
|
|
361
369
|
native("out", options.nativeLog.outgoing(turn, messages, model));
|
|
362
370
|
let attempt = 0;
|
|
363
371
|
let completion;
|
|
@@ -609,9 +617,16 @@ export function createOpenAIChatRuntime(options) {
|
|
|
609
617
|
: { state: "unavailable", reason: options.unavailableReason },
|
|
610
618
|
adapter: {
|
|
611
619
|
provider: options.driverKind,
|
|
612
|
-
capabilities: { ...(options.computerUse ? { computerMcp: options.tools !== false,
|
|
620
|
+
capabilities: { ...(options.computerUse ? { computerMcp: options.tools !== false,
|
|
621
|
+
// Same gate as cloudComputerMcp: with tools off the runtime cannot
|
|
622
|
+
// mount the leased Box descriptor either. The fleet invariant test
|
|
623
|
+
// pins usesCloudComputer === (remoteAgent || cloudComputerMcp).
|
|
624
|
+
usesCloudComputer: options.tools !== false, cloudComputerMcp: options.tools !== false, localComputerMcp: options.tools !== false,
|
|
613
625
|
browserMcp: options.tools !== false, nativeImageInput: true, images: true } : {}),
|
|
614
|
-
sessionModelSwitch: "in-session", customMcp: options.tools !== false, agentsMcp: options.tools !== false, composioMcp: options.tools !== false
|
|
626
|
+
sessionModelSwitch: "in-session", customMcp: options.tools !== false, agentsMcp: options.tools !== false, composioMcp: options.tools !== false,
|
|
627
|
+
// The runtime owns the whole tool loop, so it can always take a
|
|
628
|
+
// user message mid-turn: park it, deliver before the next completion.
|
|
629
|
+
queueing: true },
|
|
615
630
|
sendTurn,
|
|
616
631
|
interruptTurn: async (threadId, turnId) => {
|
|
617
632
|
const turn = active.get(threadId);
|
|
@@ -621,6 +636,19 @@ export function createOpenAIChatRuntime(options) {
|
|
|
621
636
|
await turn.done;
|
|
622
637
|
},
|
|
623
638
|
respondToRequest: async (threadId, requestId, decision) => active.get(threadId)?.approval.answer(requestId, decision.behavior, decision.message) ?? "unavailable",
|
|
639
|
+
steer: async (threadId, text) => {
|
|
640
|
+
// This engine has no external session to respect — parking the
|
|
641
|
+
// words in the live turn's entry IS delivery into the loop, so a
|
|
642
|
+
// successful park is "steered" and a missing turn is the only
|
|
643
|
+
// refusal. The words ride the array at the next round boundary;
|
|
644
|
+
// if the turn settles first, the harness's transcript record
|
|
645
|
+
// keeps them for the next turn (they are never re-sent here).
|
|
646
|
+
const running = active.get(threadId);
|
|
647
|
+
if (!running)
|
|
648
|
+
return "refused";
|
|
649
|
+
running.asides.push(text);
|
|
650
|
+
return "steered";
|
|
651
|
+
},
|
|
624
652
|
hasSession: (threadId) => active.has(threadId),
|
|
625
653
|
stopAll: async () => {
|
|
626
654
|
const turns = [...active.values()];
|