openmausbot 0.1.95 → 0.1.97
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-BwWpc4RK.js → DesktopViewer-CPidMCtt.js} +1 -1
- package/dist/assets/{abnfDiagram-O67JEVCF-jsQkIqTn.js → abnfDiagram-O67JEVCF-k1UCNXb7.js} +1 -1
- package/dist/assets/{arc-7kei4Hsu.js → arc-BUWGa9UU.js} +1 -1
- package/dist/assets/{architectureDiagram-NJMV4G6O-CRWDFDMq.js → architectureDiagram-NJMV4G6O-DiPor3UT.js} +1 -1
- package/dist/assets/{blockDiagram-BEXU5L5S-CTKW_IKs.js → blockDiagram-BEXU5L5S-Ag9nLW4H.js} +1 -1
- package/dist/assets/{c4Diagram-YGBWAQC7-BBTW7-6M.js → c4Diagram-YGBWAQC7-Bh0sjgUa.js} +1 -1
- package/dist/assets/channel-DR8eD6QW.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-M_Ox-MkD.js → chunk-2Q5K7J3B-BFBcIIqu.js} +1 -1
- package/dist/assets/{chunk-3FUC2YCW-D2YiN-Ud.js → chunk-3FUC2YCW-Dvfpex-f.js} +2 -2
- package/dist/assets/{chunk-5VM5RSS4-BmVYJ0f8.js → chunk-5VM5RSS4-CMjP2YZW.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-BtiLVVnZ.js → chunk-GTNCS2PH-BryoeTc7.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-DFYSb6J6.js → chunk-GWA4HPMP-BhfvcchO.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-mknKBzgV.js → chunk-JWPE2WC7-bcCVp8rp.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-DgSYGiOu.js → chunk-NETBCI7D-yKIjIlkQ.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-C7ZvuguH.js → chunk-WEXAMYUT-D3Zqbmup.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-CPqxINpA.js → chunk-XXDRQBXY-CjVoIMG6.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-Cd6xfJaC.js → classDiagram-v2-NBCMYWYE-mUCWdVyH.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-BSXY5qRh.js → cose-bilkent-JH36ORCC-BBmR1bbX.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-D_lhEMj1.js → cynefin-OW5HDTMX-BpRrxoO4.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-Da9FrFwk.js → cynefinDiagram-VND7K2PF-D3VBWCPK.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-T77jMOXe.js → dagre-6A5THRUB-DxEIACOO.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-Dkj96X5E.js → diagram-22UHCM2B-BANgZEOS.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-CMi9zlD_.js → diagram-3UASUU5V-Cnq7nJir.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-CXK6YUfS.js → diagram-ATOU4E4O-4TBDIWnz.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-BE7B1u9Q.js → diagram-CDSNMT55-CD_B39D4.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-CCMQphKr.js → diagram-MLGK6HIB-CzvnFhEC.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-iHJ_-tGH.js → diagram-MPIPVDR6-C4w2SNml.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-DONsCWQ7.js → ebnfDiagram-ZINNZB2B-gkUEBm2s.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-CU_WOaXk.js → elk-276RUBZZ-B_56X6mw.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-BeYwM04G.js → erDiagram-OPXOYQCR-C_oByoDS.js} +1 -1
- package/dist/assets/{flowDiagram-KWPJA3E3-Bdw9DqEl.js → flowDiagram-KWPJA3E3-CR9xXJkC.js} +1 -1
- package/dist/assets/{ganttDiagram-FUAMR5RP-C9iwq3qm.js → ganttDiagram-FUAMR5RP-gtYBTu0B.js} +1 -1
- package/dist/assets/{gitGraphDiagram-X574FWY7-Dx-VWzUg.js → gitGraphDiagram-X574FWY7-VJ6-Tp_p.js} +1 -1
- package/dist/assets/{index-KygHRE3E.js → index-Cp4S4NOL.js} +1 -1
- package/dist/assets/index-CpaIXRLp.js +614 -0
- package/dist/assets/index-Z81SMsgR.css +1 -0
- package/dist/assets/{infoDiagram-VRGFBTTK-BG7uaEd4.js → infoDiagram-VRGFBTTK-2Lu9te4D.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-D72WAXnM.js → ishikawaDiagram-OU5B5YK6-DR49XY-e.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-DKH1gjYT.js → journeyDiagram-ZHPQQLJL-ZoR0M305.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-3upm9JUT.js → kanban-definition-PNTS6WVX-DLKHxrO-.js} +1 -1
- package/dist/assets/{linear-CcR4pc0-.js → linear-BASsfWly.js} +1 -1
- package/dist/assets/{mermaid.core-C6yU5FSS.js → mermaid.core-BVidiPkk.js} +5 -5
- package/dist/assets/{mindmap-definition-NLK3R4M7-DOI2xEuv.js → mindmap-definition-NLK3R4M7-g_Qx0U5C.js} +1 -1
- package/dist/assets/{pegDiagram-GJSIUBJH-BVWJSgLd.js → pegDiagram-GJSIUBJH-DI9Cjs8w.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-jtbYezhq.js → pieDiagram-5QR66LMP-sRI0Uhr5.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-g0u8mtPX.js → quadrantDiagram-O4NWA36T-CamKTby3.js} +1 -1
- package/dist/assets/{railroadDiagram-XR7U4H2S-k8689Wny.js → railroadDiagram-XR7U4H2S-DOLlkbf3.js} +1 -1
- package/dist/assets/{requirementDiagram-PLB6GJNP-DsJ22PDt.js → requirementDiagram-PLB6GJNP-DrRQ5r97.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-CneWBRgG.js → sankeyDiagram-IPEJSGJF-DGS_lXcX.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-DWsFCWap.js → sequenceDiagram-PO4LG4MO-CkjWqwtf.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-Bvf0YE3S.js → sizeCapture-INFHLROL-CepRgful.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK-Bxb3Iei8.js → stateDiagram-v2-GCMORJYK-hykr-9RS.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-DLkk7W4A.js → swimlanes-2SLR337P-CKOHEZbI.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-D-8vqZ4-.js +8 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-KR9bIHYC.js → timeline-definition-EJHVYXUP-CB8pJ6AC.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-DfsOrj8t.js → usecaseDiagram-POWQR4AR-DrclJHdl.js} +1 -1
- package/dist/assets/{vennDiagram-UO4OBE2U-DRSTzG3h.js → vennDiagram-UO4OBE2U-zS23qjv8.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-DtEdhPgT.js → wardleyDiagram-VNRHLVJA-52jgeKxz.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-B-XhTxjG.js → xychartDiagram-PMCCYNJV-DSdl9rQ8.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/cloud-home-start.js +26 -13
- package/dist-server/companion/src/listener.js +39 -18
- package/dist-server/container-mcp.js +14 -27
- package/dist-server/drivers/agents-proxy.js +108 -49
- package/dist-server/harness-mcp-proxy.js +1 -2
- package/dist-server/index.js +4664 -4644
- package/dist-server/openmausbot.js +1425 -1437
- package/dist-server/pair-cli.js +1425 -1437
- package/dist-server/permission-proxy.js +0 -4
- package/dist-server/server/admin-activity.js +1 -4
- package/dist-server/server/auto-approve.js +20 -5
- package/dist-server/server/auto-vm-claims.js +13 -1
- package/dist-server/server/automatic-recovery.js +1 -2
- package/dist-server/server/boat.js +71 -87
- package/dist-server/server/bot-overview.js +1 -1
- package/dist-server/server/browser-engine.js +38 -1
- package/dist-server/server/browser-live.js +112 -25
- package/dist-server/server/browser-runtime.js +31 -1
- package/dist-server/server/checkpoints.js +340 -167
- package/dist-server/server/chief-of-staff.js +7 -6
- package/dist-server/server/cli-setup.js +3 -3
- package/dist-server/server/cli.js +33 -26
- package/dist-server/server/cloud-computer-tools.js +11 -4
- package/dist-server/server/cloud-home-start.js +13 -16
- package/dist-server/server/cloud-home.js +3 -13
- package/dist-server/server/cloud-move-http.js +44 -30
- package/dist-server/server/cloud-move.js +20 -18
- package/dist-server/server/cloud-owner.js +1 -1
- package/dist-server/server/composio.js +10 -3
- package/dist-server/server/computer-engine-removal.js +70 -0
- package/dist-server/server/computer-selection.js +18 -0
- package/dist-server/server/config.js +99 -75
- package/dist-server/server/connector-scope-hint.js +83 -0
- package/dist-server/server/container-computer.js +16 -5
- package/dist-server/server/contracts.js +0 -9
- package/dist-server/server/decider/jev.js +1 -4
- package/dist-server/server/decider/log.js +1 -3
- package/dist-server/server/decision-log.js +1 -4
- package/dist-server/server/default-model-selection.js +20 -14
- package/dist-server/server/delegations.js +14 -7
- package/dist-server/server/delta-context.js +6 -1
- package/dist-server/server/drivers/acp/core.js +5 -4
- package/dist-server/server/drivers/acp/opencode-go.js +19 -23
- package/dist-server/server/drivers/agents-call.js +47 -64
- package/dist-server/server/drivers/agents-catalog.js +69 -3
- package/dist-server/server/drivers/agents-proxy.js +2 -1
- package/dist-server/server/drivers/builtIn.js +0 -2
- package/dist-server/server/drivers/claude-login-auth.js +2 -11
- package/dist-server/server/drivers/claude.js +49 -30
- package/dist-server/server/drivers/codex-device-auth.js +3 -1
- package/dist-server/server/drivers/codex.js +7 -6
- package/dist-server/server/drivers/grok.js +1 -1
- package/dist-server/server/drivers/openai-chat-protocol.js +4 -2
- package/dist-server/server/drivers/openai-chat.js +113 -11
- package/dist-server/server/drivers/pi.js +184 -49
- package/dist-server/server/drivers/prompt-split.js +4 -4
- package/dist-server/server/harness/bus.js +49 -0
- package/dist-server/server/harness-mcp-proxy.js +1 -1
- package/dist-server/server/index.js +942 -980
- package/dist-server/server/live-call.js +11 -6
- package/dist-server/server/managed-policy.js +1 -1
- package/dist-server/server/mcp-registry.js +35 -0
- package/dist-server/server/message-file.js +4 -4
- package/dist-server/server/model-catalog/presets.js +8 -0
- package/dist-server/server/peer-delivery.js +4 -19
- package/dist-server/server/peer-roster.js +40 -6
- package/dist-server/server/profile-versions.js +0 -31
- package/dist-server/server/provider-key-check.js +2 -1
- package/dist-server/server/restart.js +40 -0
- package/dist-server/server/room-handoffs.js +12 -0
- package/dist-server/server/room-turn-end.js +15 -0
- package/dist-server/server/routes/bot-memory.js +0 -24
- package/dist-server/server/routes/live.js +3 -3
- package/dist-server/server/routine-requests.js +2 -5
- package/dist-server/server/routines.js +6 -5
- package/dist-server/server/screen-frame-gate.js +1 -3
- package/dist-server/server/server-launcher.js +53 -0
- package/dist-server/server/store.js +126 -2
- package/dist-server/server/surface.js +40 -64
- package/dist-server/server/system-prompt.js +23 -13
- package/dist-server/server/team-manifest.js +1 -6
- package/dist-server/server/tunnel.js +4 -1
- package/dist-server/server/turn-continuation.js +162 -0
- package/dist-server/server/turn-images.js +1 -21
- package/dist-server/server/turn-resources.js +19 -150
- package/dist-server/server/usage-ledger.js +3 -1
- package/dist-server/server/workspace.js +3 -4
- package/dist-server/server-launcher.js +72 -0
- package/dist-server/shared/ask-question.js +1 -71
- package/dist-server/shared/cloud-computer.js +10 -0
- package/dist-server/shared/credential-request.js +1 -1
- package/dist-server/shared/failed-turn.js +22 -0
- package/dist-server/shared/live-approval.js +2 -2
- package/dist-server/shared/package-format.js +3 -3
- package/dist-server/shared/pairing-link.js +144 -0
- package/dist-server/shared/place-view.js +409 -0
- package/dist-server/shared/tool-scope-support.js +1 -2
- package/dist-server/vps-container-mcp.js +14 -27
- package/dist-server/workspace-backup.worker.js +14 -27
- package/package.json +1 -1
- package/dist/assets/channel-DS2NbT5x.js +0 -1
- package/dist/assets/index-NF7XCHAy.css +0 -1
- package/dist/assets/index-X_5ydXIE.js +0 -610
- package/dist/assets/swimlanesDiagram-TC7HE7FX-C6bZtBXk.js +0 -8
- package/dist-server/server/claim-idle.js +0 -33
- package/dist-server/server/cloud-overflow.js +0 -167
- package/dist-server/server/drivers/boatagent.js +0 -443
- package/dist-server/server/tightening-requests.js +0 -435
- package/dist-server/shared/tightening-request.js +0 -87
|
@@ -40,6 +40,12 @@ const DRIVER_KIND = "piAgent";
|
|
|
40
40
|
const PI_ARGS = ["--mode", "rpc", "--no-session"];
|
|
41
41
|
const PI_MODEL_UPDATE_ARGS = ["update", "--models", "--no-approve"];
|
|
42
42
|
const NODE_ENV_FLAG = { ELECTRON_RUN_AS_NODE: "1" };
|
|
43
|
+
/** omp's RPC transport caps one frame at 1 MiB and only chunks oversized
|
|
44
|
+
* frames after the client negotiates protocol 2. Sent before the first
|
|
45
|
+
* command on every spawn: stdin order guarantees the declaration lands
|
|
46
|
+
* before the response to that command is encoded, and vanilla pi has no
|
|
47
|
+
* handshake frame to wait for, so nobody gates on a reply. */
|
|
48
|
+
const PI_NEGOTIATE_FRAME = { id: "negotiate", type: "negotiate_protocol", protocolVersion: 2 };
|
|
43
49
|
/** After this many bare turns on one pi session the full prompt rides
|
|
44
50
|
* again even without a compaction event: any history rewrite the events
|
|
45
51
|
* miss (an older pi, a missed line) still loses at most this many turns
|
|
@@ -135,12 +141,26 @@ export function buildMcpServers(turn) {
|
|
|
135
141
|
}
|
|
136
142
|
return Object.keys(servers).length ? servers : null;
|
|
137
143
|
}
|
|
144
|
+
/** One decoded frame → catalog, or null when it is not a successful
|
|
145
|
+
* `get_available_models` response. Every option is `custom` (pi is BYOK)
|
|
146
|
+
* and id is the `provider/modelId` composite the picker and `set_model`
|
|
147
|
+
* both use. */
|
|
148
|
+
function piCatalogFromFrame(frame, fallbackDefault) {
|
|
149
|
+
const res = frame;
|
|
150
|
+
if (res?.type !== "response" || res.command !== "get_available_models" || !res.success)
|
|
151
|
+
return null;
|
|
152
|
+
const options = [];
|
|
153
|
+
for (const m of res.data?.models ?? []) {
|
|
154
|
+
if (!m?.provider || !m?.id)
|
|
155
|
+
continue;
|
|
156
|
+
const id = `${m.provider}/${m.id}`;
|
|
157
|
+
options.push({ id, label: m.name ?? m.id, custom: true, provider: m.provider });
|
|
158
|
+
}
|
|
159
|
+
return { default: fallbackDefault || (options[0]?.id ?? ""), options };
|
|
160
|
+
}
|
|
138
161
|
/** Pure parser: turn a `get_available_models` stdout blob into a catalog.
|
|
139
|
-
*
|
|
140
|
-
* composite the picker and `set_model` both use. Exported for the test. */
|
|
162
|
+
* Exported for the test. */
|
|
141
163
|
export function parsePiCatalog(stdout, fallbackDefault = "") {
|
|
142
|
-
const options = [];
|
|
143
|
-
let def = fallbackDefault;
|
|
144
164
|
for (const line of stdout.split("\n")) {
|
|
145
165
|
if (!line.trim())
|
|
146
166
|
continue;
|
|
@@ -151,20 +171,142 @@ export function parsePiCatalog(stdout, fallbackDefault = "") {
|
|
|
151
171
|
catch {
|
|
152
172
|
continue;
|
|
153
173
|
}
|
|
154
|
-
const
|
|
155
|
-
if (
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
174
|
+
const catalog = piCatalogFromFrame(msg, fallbackDefault);
|
|
175
|
+
if (catalog)
|
|
176
|
+
return catalog;
|
|
177
|
+
}
|
|
178
|
+
return { default: fallbackDefault, options: [] };
|
|
179
|
+
}
|
|
180
|
+
/** omp's RPC v2 splits any frame larger than 1 MiB into base64 `rpc_chunk`
|
|
181
|
+
* pieces: one `chunkId`/`count`/`byteLength` for the whole frame, a
|
|
182
|
+
* sequential `index`, and `data` carrying one piece. Reassembles one
|
|
183
|
+
* logical frame at a time; any inconsistency drops the partial sequence so
|
|
184
|
+
* a corrupt stream can never feed a half-built frame to the parser.
|
|
185
|
+
* Exported for the test. */
|
|
186
|
+
export class PiRpcChunks {
|
|
187
|
+
/** omp's advertised cap on one reassembled frame. */
|
|
188
|
+
static MAX_BYTES = 64 * 1024 * 1024;
|
|
189
|
+
/** omp's cap on one decoded piece, which also bounds the piece count. */
|
|
190
|
+
static MAX_PIECE_BYTES = 256 * 1024;
|
|
191
|
+
static MAX_COUNT = PiRpcChunks.MAX_BYTES / PiRpcChunks.MAX_PIECE_BYTES;
|
|
192
|
+
chunkId = null;
|
|
193
|
+
count = 0;
|
|
194
|
+
byteLength = 0;
|
|
195
|
+
bytes = 0;
|
|
196
|
+
parts = [];
|
|
197
|
+
/** Feed one `rpc_chunk`; returns the reassembled frame when the last
|
|
198
|
+
* piece arrives, or null while the sequence is incomplete or broken. */
|
|
199
|
+
accept(message) {
|
|
200
|
+
const { chunkId, index, count, byteLength, data } = message;
|
|
201
|
+
if (typeof chunkId !== "string" || chunkId.length === 0 || typeof data !== "string" ||
|
|
202
|
+
// Bound the base64 before decoding it, so an oversized piece is
|
|
203
|
+
// rejected without allocating its bytes.
|
|
204
|
+
data.length === 0 || data.length > Math.ceil(PiRpcChunks.MAX_PIECE_BYTES / 3) * 4 ||
|
|
205
|
+
!Number.isInteger(index) || !Number.isInteger(count) || !Number.isInteger(byteLength) ||
|
|
206
|
+
index < 0 || count < 2 || count > PiRpcChunks.MAX_COUNT ||
|
|
207
|
+
index >= count ||
|
|
208
|
+
byteLength < 0 || byteLength > PiRpcChunks.MAX_BYTES) {
|
|
209
|
+
this.reset();
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
if (chunkId !== this.chunkId) {
|
|
213
|
+
// A new sequence may only start at index 0; a different chunkId
|
|
214
|
+
// mid-sequence means the previous one can never complete.
|
|
215
|
+
if (index !== 0) {
|
|
216
|
+
this.reset();
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
this.chunkId = chunkId;
|
|
220
|
+
this.count = count;
|
|
221
|
+
this.byteLength = byteLength;
|
|
222
|
+
this.parts = [];
|
|
223
|
+
this.bytes = 0;
|
|
224
|
+
}
|
|
225
|
+
else if (count !== this.count || byteLength !== this.byteLength) {
|
|
226
|
+
this.reset();
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
const part = Buffer.from(data, "base64");
|
|
230
|
+
// Every piece must carry bytes: an empty one would let a sequence grow
|
|
231
|
+
// `parts` without ever approaching `byteLength`.
|
|
232
|
+
if (index !== this.parts.length || part.length === 0 || part.length > PiRpcChunks.MAX_PIECE_BYTES ||
|
|
233
|
+
this.bytes + part.length > this.byteLength) {
|
|
234
|
+
this.reset();
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
this.parts.push(part);
|
|
238
|
+
this.bytes += part.length;
|
|
239
|
+
if (this.parts.length < this.count)
|
|
240
|
+
return null;
|
|
241
|
+
const complete = this.bytes === this.byteLength;
|
|
242
|
+
const text = Buffer.concat(this.parts).toString("utf8");
|
|
243
|
+
this.reset();
|
|
244
|
+
if (!complete)
|
|
245
|
+
return null;
|
|
246
|
+
try {
|
|
247
|
+
const frame = JSON.parse(text);
|
|
248
|
+
return frame && typeof frame === "object" && !Array.isArray(frame)
|
|
249
|
+
? frame
|
|
250
|
+
: null;
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
return null;
|
|
162
254
|
}
|
|
163
|
-
break;
|
|
164
255
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
256
|
+
reset() {
|
|
257
|
+
this.chunkId = null;
|
|
258
|
+
this.count = 0;
|
|
259
|
+
this.byteLength = 0;
|
|
260
|
+
this.bytes = 0;
|
|
261
|
+
this.parts = [];
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/** Decode one pi stdout line: a plain JSON frame, or one piece of an
|
|
265
|
+
* `rpc_chunk` sequence (omp's protocol v2) with the reassembled frame
|
|
266
|
+
* returned once complete. Non-JSON lines, scalars and broken sequences
|
|
267
|
+
* resolve null and are skipped. Exported for the test. */
|
|
268
|
+
export function decodePiFrame(line, chunks) {
|
|
269
|
+
let parsed;
|
|
270
|
+
try {
|
|
271
|
+
parsed = JSON.parse(line);
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
277
|
+
return null;
|
|
278
|
+
const frame = parsed;
|
|
279
|
+
return frame.type === "rpc_chunk" ? chunks.accept(frame) : frame;
|
|
280
|
+
}
|
|
281
|
+
/** Longest stdout line a reader buffers: omp's cap on one whole frame. */
|
|
282
|
+
const PI_MAX_LINE = PiRpcChunks.MAX_BYTES;
|
|
283
|
+
/** Split a pi child's stdout into non-blank lines for `onLine`, which
|
|
284
|
+
* returns true to stop reading. A line longer than PI_MAX_LINE is dropped
|
|
285
|
+
* through its newline instead of growing the buffer without bound.
|
|
286
|
+
* Exported for the test. */
|
|
287
|
+
export function piLineReader(onLine) {
|
|
288
|
+
let buf = "";
|
|
289
|
+
let dropping = false;
|
|
290
|
+
return (chunk) => {
|
|
291
|
+
buf += chunk;
|
|
292
|
+
let nl;
|
|
293
|
+
while ((nl = buf.indexOf("\n")) !== -1) {
|
|
294
|
+
const line = buf.slice(0, nl);
|
|
295
|
+
buf = buf.slice(nl + 1);
|
|
296
|
+
if (dropping) {
|
|
297
|
+
dropping = false;
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
if (line.length > PI_MAX_LINE || !line.trim())
|
|
301
|
+
continue;
|
|
302
|
+
if (onLine(line))
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (buf.length > PI_MAX_LINE) {
|
|
306
|
+
buf = "";
|
|
307
|
+
dropping = true;
|
|
308
|
+
}
|
|
309
|
+
};
|
|
168
310
|
}
|
|
169
311
|
/** Split a picker id into pi's `{provider, modelId}`. Accepts both the
|
|
170
312
|
* native `provider/modelId` composite and a live-host `host::model`
|
|
@@ -306,7 +448,6 @@ function readPiDefaultModel(env) {
|
|
|
306
448
|
export async function fetchPiModels(cli, env) {
|
|
307
449
|
const child = spawnCli(cli, PI_ARGS, { stdio: ["pipe", "pipe", "pipe"], env });
|
|
308
450
|
return new Promise((resolve) => {
|
|
309
|
-
let buf = "";
|
|
310
451
|
let done = false;
|
|
311
452
|
const fallbackDefault = readPiDefaultModel(env);
|
|
312
453
|
const finish = (catalog) => {
|
|
@@ -323,26 +464,20 @@ export async function fetchPiModels(cli, env) {
|
|
|
323
464
|
};
|
|
324
465
|
const timer = setTimeout(() => finish({ default: "", options: [] }), 15_000);
|
|
325
466
|
timer.unref?.();
|
|
467
|
+
const chunks = new PiRpcChunks();
|
|
326
468
|
child.stdout.setEncoding("utf8");
|
|
327
|
-
child.stdout.on("data", (
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const parsed = parsePiCatalog(line + "\n", fallbackDefault);
|
|
336
|
-
if (parsed.options.length || line.includes('"get_available_models"')) {
|
|
337
|
-
clearTimeout(timer);
|
|
338
|
-
finish(parsed);
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
});
|
|
469
|
+
child.stdout.on("data", piLineReader((line) => {
|
|
470
|
+
const frame = decodePiFrame(line, chunks);
|
|
471
|
+
if (frame?.command !== "get_available_models")
|
|
472
|
+
return false;
|
|
473
|
+
clearTimeout(timer);
|
|
474
|
+
finish(piCatalogFromFrame(frame, fallbackDefault) ?? { default: fallbackDefault, options: [] });
|
|
475
|
+
return true;
|
|
476
|
+
}));
|
|
343
477
|
child.on("error", () => finish({ default: "", options: [] }));
|
|
344
478
|
child.on("close", () => finish({ default: "", options: [] }));
|
|
345
479
|
try {
|
|
480
|
+
child.stdin.write(`${JSON.stringify(PI_NEGOTIATE_FRAME)}\n`);
|
|
346
481
|
child.stdin.write(JSON.stringify({ id: "catalog", type: "get_available_models" }) + "\n");
|
|
347
482
|
}
|
|
348
483
|
catch {
|
|
@@ -542,7 +677,6 @@ export const PiDriver = {
|
|
|
542
677
|
throw err;
|
|
543
678
|
}
|
|
544
679
|
})();
|
|
545
|
-
let buf = "";
|
|
546
680
|
let assistantText = "";
|
|
547
681
|
// set when a compaction event arrives this turn; gates the receipt
|
|
548
682
|
// write below so a compacted-around delivery is never claimed
|
|
@@ -575,6 +709,11 @@ export const PiDriver = {
|
|
|
575
709
|
appendNative(threadId, { dir: "out", source: "pi.rpc", msg: piNativeLogMessage(obj) });
|
|
576
710
|
child.stdin.write(JSON.stringify(obj) + "\n");
|
|
577
711
|
};
|
|
712
|
+
// Declare protocol 2 on the same stdin before the first command, so an
|
|
713
|
+
// omp-style runtime chunks any oversized frame it sends back. Vanilla
|
|
714
|
+
// pi refuses the unknown command; no waiter is keyed for the reply,
|
|
715
|
+
// so either answer is ignored.
|
|
716
|
+
send(PI_NEGOTIATE_FRAME);
|
|
578
717
|
/** Emit buffered assistant text as its own item, then clear it. */
|
|
579
718
|
const flushAssistantText = () => {
|
|
580
719
|
const text = assistantText;
|
|
@@ -911,23 +1050,19 @@ export const PiDriver = {
|
|
|
911
1050
|
return;
|
|
912
1051
|
}
|
|
913
1052
|
};
|
|
1053
|
+
const chunks = new PiRpcChunks();
|
|
914
1054
|
child.stdout.setEncoding("utf8");
|
|
915
|
-
child.stdout.on("data", (
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
if (!line.trim())
|
|
922
|
-
continue;
|
|
923
|
-
try {
|
|
924
|
-
onEvent(JSON.parse(line));
|
|
925
|
-
}
|
|
926
|
-
catch {
|
|
927
|
-
/* skip non-JSON line */
|
|
928
|
-
}
|
|
1055
|
+
child.stdout.on("data", piLineReader((line) => {
|
|
1056
|
+
const frame = decodePiFrame(line, chunks);
|
|
1057
|
+
if (!frame)
|
|
1058
|
+
return;
|
|
1059
|
+
try {
|
|
1060
|
+
onEvent(frame);
|
|
929
1061
|
}
|
|
930
|
-
|
|
1062
|
+
catch {
|
|
1063
|
+
/* skip a frame the event handler can't consume */
|
|
1064
|
+
}
|
|
1065
|
+
}));
|
|
931
1066
|
child.on("error", (err) => {
|
|
932
1067
|
const fail = describeSpawnFailure(err, config.cli);
|
|
933
1068
|
rejectWaiters(new Error(fail.message));
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// The stable/volatile prompt split, shared by the drivers that deliver it.
|
|
2
2
|
// The stable half is everything that must stay byte-identical for a
|
|
3
3
|
// provider's cached prefix (or a spawned CLI's session contract) to
|
|
4
|
-
// survive; the volatile half (
|
|
5
|
-
//
|
|
6
|
-
//
|
|
4
|
+
// survive; the volatile half (the sections in VOLATILE_SECTIONS,
|
|
5
|
+
// system-prompt.ts) legitimately changes mid-conversation and reaches the
|
|
6
|
+
// model inside the turn that changed it, after the cacheable prefix.
|
|
7
7
|
import { createHash } from "node:crypto";
|
|
8
8
|
import { mkdirSync, readFileSync, rmSync } from "node:fs";
|
|
9
9
|
import { join } from "node:path";
|
|
@@ -20,7 +20,7 @@ export function promptHalves(turn) {
|
|
|
20
20
|
return { stable: systemStable, volatile: systemVolatile };
|
|
21
21
|
}
|
|
22
22
|
export const VOLATILE_CONTEXT_NOTE_PREFIX = "Context from OpenMausBot updated since this conversation started; it replaces any earlier copy:";
|
|
23
|
-
export const VOLATILE_CONTEXT_CLEARED_NOTE = "The OpenMausBot context notes from earlier in this conversation
|
|
23
|
+
export const VOLATILE_CONTEXT_CLEARED_NOTE = "The OpenMausBot context notes from earlier in this conversation have been cleared; the standing instructions still apply.";
|
|
24
24
|
/** The labelled block that carries a changed volatile half inside a user
|
|
25
25
|
* turn. A half that is empty and always was needs no note; one that was
|
|
26
26
|
* cleared announces the removal so the model stops relying on it. */
|
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
// a per-thread canonical NDJSON log (the debugging trick both upstream and
|
|
5
5
|
// agentcal lean on), and delivered to subscribers (the SSE endpoint and
|
|
6
6
|
// the server-side message folder).
|
|
7
|
+
//
|
|
8
|
+
// Streamed text is merged here, once, for every engine on every kind of
|
|
9
|
+
// install: a provider sends a reply as hundreds of small text deltas, and
|
|
10
|
+
// each would otherwise be its own log line, listener call and SSE frame. The
|
|
11
|
+
// bus holds a thread's text for at most DELTA_MERGE_MS and publishes it as
|
|
12
|
+
// one delta, sooner when anything else happens on that thread, so the order
|
|
13
|
+
// of events never changes.
|
|
7
14
|
import { appendFileSync } from "node:fs";
|
|
8
15
|
import { join } from "node:path";
|
|
9
16
|
import { EVENTS_DIR } from "../config.js";
|
|
@@ -11,10 +18,19 @@ import { redactSecrets } from "../redact.js";
|
|
|
11
18
|
import { capThreadLog, currentThreadLogCap } from "../thread-log-rotation.js";
|
|
12
19
|
import { newId } from "../contracts.js";
|
|
13
20
|
const INCOMPLETE_LOG_MESSAGE = "Canonical event history is incomplete: OpenMausBot could not write one or more events to disk. Live updates will continue.";
|
|
21
|
+
/** How long a thread's streamed text waits to be merged with what follows. */
|
|
22
|
+
const DELTA_MERGE_MS = 50;
|
|
23
|
+
/** Two deltas are one stream when only their text, id and time differ. */
|
|
24
|
+
function sameStream(a, b) {
|
|
25
|
+
return a.provider === b.provider && a.providerInstanceId === b.providerInstanceId && a.turnId === b.turnId &&
|
|
26
|
+
a.itemId === b.itemId && a.streamKind === b.streamKind && a.synthetic === b.synthetic;
|
|
27
|
+
}
|
|
14
28
|
export class EventBus {
|
|
15
29
|
listeners = new Set();
|
|
16
30
|
unsubscribes = new Map();
|
|
17
31
|
pendingLogWarnings = new Map();
|
|
32
|
+
/** Per thread: the streamed text not yet published, and when it goes. */
|
|
33
|
+
pendingText = new Map();
|
|
18
34
|
appendLog;
|
|
19
35
|
constructor(appendLog = appendFileSync) {
|
|
20
36
|
this.appendLog = appendLog;
|
|
@@ -35,6 +51,38 @@ export class EventBus {
|
|
|
35
51
|
}
|
|
36
52
|
}
|
|
37
53
|
publish(event) {
|
|
54
|
+
const pending = this.pendingText.get(event.threadId);
|
|
55
|
+
if (event.type === "content.delta") {
|
|
56
|
+
if (pending && sameStream(pending.event, event)) {
|
|
57
|
+
pending.event = { ...pending.event, delta: pending.event.delta + event.delta };
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (pending)
|
|
61
|
+
this.flushThread(event.threadId);
|
|
62
|
+
const timer = setTimeout(() => this.flushThread(event.threadId), DELTA_MERGE_MS);
|
|
63
|
+
timer.unref?.();
|
|
64
|
+
this.pendingText.set(event.threadId, { event, timer });
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (pending)
|
|
68
|
+
this.flushThread(event.threadId);
|
|
69
|
+
this.write(event);
|
|
70
|
+
}
|
|
71
|
+
/** Publish every thread's waiting text now: on detach, and as the server exits. */
|
|
72
|
+
flush() {
|
|
73
|
+
for (const threadId of Array.from(this.pendingText.keys()))
|
|
74
|
+
this.flushThread(threadId);
|
|
75
|
+
}
|
|
76
|
+
flushThread(threadId) {
|
|
77
|
+
const pending = this.pendingText.get(threadId);
|
|
78
|
+
if (!pending)
|
|
79
|
+
return;
|
|
80
|
+
// gone from the map before delivery: a listener may publish on this thread
|
|
81
|
+
this.pendingText.delete(threadId);
|
|
82
|
+
clearTimeout(pending.timer);
|
|
83
|
+
this.write(pending.event);
|
|
84
|
+
}
|
|
85
|
+
write(event) {
|
|
38
86
|
const pendingWarning = this.pendingLogWarnings.get(event.threadId);
|
|
39
87
|
const persistedEvents = pendingWarning ? [pendingWarning, redactSecrets(event)] : [redactSecrets(event)];
|
|
40
88
|
try {
|
|
@@ -91,5 +139,6 @@ export class EventBus {
|
|
|
91
139
|
detach(instanceId) {
|
|
92
140
|
this.unsubscribes.get(instanceId)?.();
|
|
93
141
|
this.unsubscribes.delete(instanceId);
|
|
142
|
+
this.flush();
|
|
94
143
|
}
|
|
95
144
|
}
|
|
@@ -13,7 +13,7 @@ const RECOVERY = {
|
|
|
13
13
|
browser: " This failure does not establish that all browsers are unavailable. Inspect available connections with select_computer if that tool is present. Keep the requested computer and account; do not bypass a permission refusal or human takeover, and do not repeat an uncertain action without checking its result.",
|
|
14
14
|
computer: " Keep working on the assigned cloud computer only; do not bypass a person's control of it, and take a fresh screenshot before repeating an action whose result is uncertain.",
|
|
15
15
|
};
|
|
16
|
-
|
|
16
|
+
function parseHarnessMcpKind(value) {
|
|
17
17
|
return value === "browser" || value === "computer" ? value : null;
|
|
18
18
|
}
|
|
19
19
|
function failure(id, method, message, code = -32603) {
|