openmausbot 0.1.94 → 0.1.96
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-bxM7NV3N.js → DesktopViewer-DsnA23KM.js} +1 -1
- package/dist/assets/{abnfDiagram-O67JEVCF-BhMdJhRJ.js → abnfDiagram-O67JEVCF-Dkbd2hcj.js} +1 -1
- package/dist/assets/{arc-B5itKDcx.js → arc-Duj5Coy2.js} +1 -1
- package/dist/assets/{architectureDiagram-NJMV4G6O-C5d7UY6U.js → architectureDiagram-NJMV4G6O-uYUsDawP.js} +1 -1
- package/dist/assets/{blockDiagram-BEXU5L5S-BrZIhMmu.js → blockDiagram-BEXU5L5S-CWDlTpXe.js} +1 -1
- package/dist/assets/{c4Diagram-YGBWAQC7-7I0ArfbD.js → c4Diagram-YGBWAQC7-B0t15PDc.js} +1 -1
- package/dist/assets/channel-BZxwC-Gw.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-BnEsyN36.js → chunk-2Q5K7J3B-BJSCL-HV.js} +1 -1
- package/dist/assets/{chunk-3FUC2YCW-BXATQ52s.js → chunk-3FUC2YCW-D3_ILccd.js} +2 -2
- package/dist/assets/{chunk-5VM5RSS4-B0BpTkEx.js → chunk-5VM5RSS4-D3dmwT_S.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-BtLTz6FR.js → chunk-GTNCS2PH-Br73HFPD.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-D6gcZOYt.js → chunk-GWA4HPMP-uSsjxubC.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-DCLrosvf.js → chunk-JWPE2WC7-C40VWFyr.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-B9JsknV9.js → chunk-NETBCI7D-D6WNNYwU.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-CsoTxw7z.js → chunk-WEXAMYUT-DOEhq64P.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-0F_frB0c.js → chunk-XXDRQBXY-BqJEi6Pi.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-DkrpnflX.js → classDiagram-v2-NBCMYWYE-ByU_GnxT.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-BNDwYBt9.js → cose-bilkent-JH36ORCC-D-Uglxqq.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-JUkNr1Mk.js → cynefin-OW5HDTMX-CZWO7aYj.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-DKeh9N4M.js → cynefinDiagram-VND7K2PF-C70-w0la.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-ve-6T_wX.js → dagre-6A5THRUB-CnhjhExB.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-BuJ2d8rT.js → diagram-22UHCM2B-bnjWCNb4.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-BgOs_Ni-.js → diagram-3UASUU5V-A4uTWoFV.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-DMK6VmcP.js → diagram-ATOU4E4O-Zi6m3NUl.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-DksASlcY.js → diagram-CDSNMT55-DtCcmd5k.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-Bgn8I5k6.js → diagram-MLGK6HIB-DofuETS3.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-zK0jqO8N.js → diagram-MPIPVDR6-Dw2T9PuU.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-vKrbzTwr.js → ebnfDiagram-ZINNZB2B-DbrwPiem.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-Bzv2mPNf.js → elk-276RUBZZ-BvCt1dCb.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-CvOyYuxu.js → erDiagram-OPXOYQCR-Y-QKqPnU.js} +1 -1
- package/dist/assets/{flowDiagram-KWPJA3E3-CtbnDbc0.js → flowDiagram-KWPJA3E3-CDkL1B5w.js} +1 -1
- package/dist/assets/{ganttDiagram-FUAMR5RP-c4dMRYyG.js → ganttDiagram-FUAMR5RP-CKV5D2F6.js} +1 -1
- package/dist/assets/{gitGraphDiagram-X574FWY7-B34uaQho.js → gitGraphDiagram-X574FWY7-nL9iFTCv.js} +1 -1
- package/dist/assets/{index-CPiqc_DN.js → index-DreDnjOu.js} +1 -1
- package/dist/assets/index-Du7o6Dsf.js +610 -0
- package/dist/assets/index-KxG4bWSr.css +1 -0
- package/dist/assets/{infoDiagram-VRGFBTTK-o64ma4CI.js → infoDiagram-VRGFBTTK-C7aHjzsf.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-DV4C9BAm.js → ishikawaDiagram-OU5B5YK6-KUZsNBTh.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-Bf0AHk1H.js → journeyDiagram-ZHPQQLJL-CGUuodFl.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-B1UMSKmE.js → kanban-definition-PNTS6WVX-Cy6r868Z.js} +1 -1
- package/dist/assets/{linear-BpKhuyLm.js → linear-Bl669148.js} +1 -1
- package/dist/assets/{mermaid.core-B_BKNCw5.js → mermaid.core-86Xqz5Mr.js} +5 -5
- package/dist/assets/{mindmap-definition-NLK3R4M7-DeARruT1.js → mindmap-definition-NLK3R4M7-G4DzHLrd.js} +1 -1
- package/dist/assets/{pegDiagram-GJSIUBJH-BOQEk_1D.js → pegDiagram-GJSIUBJH-K4eruzY0.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-EIfoiTZo.js → pieDiagram-5QR66LMP-DYdR9PPq.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-GgZwT-fi.js → quadrantDiagram-O4NWA36T-BCAh0nEy.js} +1 -1
- package/dist/assets/{railroadDiagram-XR7U4H2S-D9rDQomj.js → railroadDiagram-XR7U4H2S-p0dGRc4g.js} +1 -1
- package/dist/assets/{requirementDiagram-PLB6GJNP-DAh1MIeo.js → requirementDiagram-PLB6GJNP-tLeBG0aL.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-CPr6Uz64.js → sankeyDiagram-IPEJSGJF-QRuN16rV.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-BU6VIdcm.js → sequenceDiagram-PO4LG4MO-ueT1nVoa.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-qF2HO41r.js → sizeCapture-INFHLROL-wRS2zWg2.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK-DlJ8G4MD.js → stateDiagram-v2-GCMORJYK-CYzA_hE1.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-DzYE8I54.js → swimlanes-2SLR337P-kDnv6LMc.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-C7voMDle.js +8 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-B7wzEQ0A.js → timeline-definition-EJHVYXUP-D1y2UbbH.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-BvGgrxIP.js → usecaseDiagram-POWQR4AR-IqB4v9HX.js} +1 -1
- package/dist/assets/{vennDiagram-UO4OBE2U-CEECdgQg.js → vennDiagram-UO4OBE2U-BEi7KYVF.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-XQHA0P_P.js → wardleyDiagram-VNRHLVJA-Dkybo-Yf.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-B5mLcZI2.js → xychartDiagram-PMCCYNJV-BAPFJLpg.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/cloud-home-start.js +27 -13
- package/dist-server/companion/src/listener.js +39 -18
- package/dist-server/companion/src/routes.js +39 -0
- package/dist-server/container-mcp.js +16 -29
- package/dist-server/drivers/agents-proxy.js +161 -82
- package/dist-server/enterprise/server/index.js +2 -1
- package/dist-server/{browser-proxy.js → harness-mcp-proxy.js} +34 -19
- package/dist-server/index.js +4991 -4908
- package/dist-server/local-computer.js +2 -1
- package/dist-server/openmausbot.js +906 -1785
- package/dist-server/pair-cli.js +906 -1785
- package/dist-server/proxy-paths.js +2 -1
- package/dist-server/server/account-signin.js +0 -2
- package/dist-server/server/activity.js +201 -0
- package/dist-server/server/admin-activity.js +1 -4
- package/dist-server/server/auto-approve.js +25 -4
- package/dist-server/server/automatic-recovery.js +1 -1
- package/dist-server/server/boat-create-idempotency.js +0 -7
- package/dist-server/server/boat-delete-journal.js +0 -4
- package/dist-server/server/boat.js +4 -4
- package/dist-server/server/bot-package.js +1 -19
- package/dist-server/server/browser-runtime.js +1 -2
- package/dist-server/server/checkpoints.js +90 -148
- package/dist-server/server/chief-of-staff.js +9 -9
- package/dist-server/server/cli.js +33 -26
- package/dist-server/server/cloud-backend.js +0 -4
- package/dist-server/server/cloud-computer-tools.js +110 -0
- package/dist-server/server/cloud-home-start.js +12 -15
- package/dist-server/server/cloud-home.js +0 -5
- package/dist-server/server/cloud-lending.js +0 -3
- package/dist-server/server/cloud-move-http.js +44 -30
- package/dist-server/server/cloud-move.js +20 -18
- package/dist-server/server/composio.js +41 -5
- package/dist-server/server/computer-control.js +0 -15
- package/dist-server/server/computer-observation.js +0 -74
- package/dist-server/server/config.js +121 -65
- package/dist-server/server/container-computer.js +23 -24
- package/dist-server/server/control-client.js +4 -10
- 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/delegations.js +3 -2
- package/dist-server/server/drivers/acp/core.js +11 -8
- package/dist-server/server/drivers/acp/opencode-go.js +17 -16
- package/dist-server/server/drivers/agents-call.js +88 -92
- package/dist-server/server/drivers/agents-catalog.js +90 -11
- package/dist-server/server/drivers/agents-proxy.js +10 -31
- package/dist-server/server/drivers/antigravity.js +1 -4
- package/dist-server/server/drivers/boatagent.js +23 -4
- package/dist-server/server/drivers/cerebras.js +3 -3
- package/dist-server/server/drivers/chat-mcp-tools.js +4 -7
- package/dist-server/server/drivers/claude-login-auth.js +2 -11
- package/dist-server/server/drivers/claude.js +16 -14
- package/dist-server/server/drivers/codex-device-auth.js +3 -1
- package/dist-server/server/drivers/codex.js +5 -4
- 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 +18 -11
- package/dist-server/server/drivers/prompt-split.js +4 -4
- package/dist-server/server/drivers/retry.js +8 -4
- package/dist-server/server/engine-install.js +1 -5
- package/dist-server/server/harness/bus.js +49 -0
- package/dist-server/server/harness/registry.js +0 -1
- package/dist-server/server/{browser-proxy.js → harness-mcp-proxy.js} +40 -20
- package/dist-server/server/index.js +1190 -971
- package/dist-server/server/lending-memory.js +12 -8
- package/dist-server/server/mcp-registry.js +35 -0
- package/dist-server/server/memory-entries.js +38 -8
- package/dist-server/server/memory-organize.js +1 -8
- package/dist-server/server/memory-topics.js +4 -4
- package/dist-server/server/memory-upkeep.js +13 -15
- package/dist-server/server/message-file.js +9 -13
- package/dist-server/server/outbound-counts.js +79 -0
- package/dist-server/server/outbound-requests.js +60 -0
- package/dist-server/server/package-import.js +1 -1
- package/dist-server/server/peer-delivery.js +5 -19
- package/dist-server/server/peer-roster.js +30 -4
- package/dist-server/server/profile-versions.js +0 -31
- package/dist-server/server/provider-key-check.js +2 -1
- package/dist-server/server/proxy-paths.js +2 -1
- package/dist-server/server/restart.js +40 -0
- package/dist-server/server/room-handoffs.js +17 -13
- package/dist-server/server/routes/bot-memory.js +0 -24
- package/dist-server/server/routine-requests.js +2 -5
- package/dist-server/server/routines.js +10 -5
- package/dist-server/server/server-launcher.js +53 -0
- package/dist-server/server/setup-mode.js +0 -2
- package/dist-server/server/skill-learn.js +0 -1
- package/dist-server/server/skill-library.js +0 -15
- package/dist-server/server/store.js +10 -2
- package/dist-server/server/surface.js +69 -28
- package/dist-server/server/system-prompt.js +17 -7
- package/dist-server/server/team-manifest.js +1 -6
- package/dist-server/server/team-memory.js +209 -0
- package/dist-server/server/thread-events.js +2 -2
- package/dist-server/server/tunnel.js +4 -1
- 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/vps-computer.js +4 -14
- package/dist-server/server/workspace.js +87 -50
- package/dist-server/server-launcher.js +72 -0
- package/dist-server/shared/activity.js +3 -0
- package/dist-server/shared/ask-question.js +1 -1
- package/dist-server/shared/bot-avatar.js +8 -0
- package/dist-server/shared/cloud-computer.js +12 -0
- package/dist-server/shared/connector-scopes.js +78 -0
- package/dist-server/shared/failed-turn.js +25 -0
- package/dist-server/shared/live-approval.js +2 -2
- package/dist-server/shared/outbound.js +128 -0
- package/dist-server/shared/package-format.js +3 -3
- package/dist-server/shared/pairing-link.js +144 -0
- package/dist-server/shared/tool-surface.js +3 -2
- package/dist-server/vps-container-mcp.js +16 -29
- package/dist-server/workspace-backup.worker.js +14 -27
- package/enterprise/server/index.js +2 -1
- package/package.json +1 -1
- package/dist/assets/channel-CdJX6i8k.js +0 -1
- package/dist/assets/index-CPYrGsv1.js +0 -610
- package/dist/assets/index-CzT42GR8.css +0 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-D-U4GtLQ.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/chat-boat-tools.js +0 -90
- package/dist-server/server/drivers/grokagent.js +0 -4
- package/dist-server/server/openmaus-status-capsule.js +0 -329
- package/dist-server/server/tightening-requests.js +0 -435
- package/dist-server/shared/tightening-request.js +0 -87
|
@@ -8,7 +8,8 @@ function resolveProxy(relative) {
|
|
|
8
8
|
return existsSync(source) ? source : join(SERVER_ROOT, `${relative}.js`);
|
|
9
9
|
}
|
|
10
10
|
var SPAWNED_PROXIES = {
|
|
11
|
-
browser:
|
|
11
|
+
// The built-in browser and the cloud computer: argv[2] picks which.
|
|
12
|
+
harnessMcp: resolveProxy("harness-mcp-proxy"),
|
|
12
13
|
localComputer: resolveProxy("local-computer-proxy"),
|
|
13
14
|
permission: resolveProxy("permission-proxy"),
|
|
14
15
|
containerMcp: resolveProxy("container-mcp"),
|
|
@@ -11,8 +11,6 @@
|
|
|
11
11
|
// not expose the routes.
|
|
12
12
|
import { resolveCompanionControlPlaneURL } from "../electron/companion-account-service.mjs";
|
|
13
13
|
import { ControlPlaneError, createControlPlaneClient, normalizeAccountEmail } from "../electron/control-plane-client.mjs";
|
|
14
|
-
export const ADMIN_EMAILS_ENV = "OMB_SIGNIN_EMAILS";
|
|
15
|
-
export const MEMBER_EMAILS_ENV = "OMB_SIGNIN_MEMBER_EMAILS";
|
|
16
14
|
/** Commas, spaces or newlines between entries; case does not matter. */
|
|
17
15
|
export function parseAllowList(value) {
|
|
18
16
|
return (value ?? "")
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// A bot's activity log: what it did, in plain words, with the outcome.
|
|
2
|
+
//
|
|
3
|
+
// Nothing here is captured for the log's sake. Two records already on disk
|
|
4
|
+
// between them know everything a receipt needs: the per-thread runtime
|
|
5
|
+
// events (harness/bus.ts) say what ran and whether it finished, and the
|
|
6
|
+
// fleet-wide decision log (decision-log.ts) says what was asked, with which
|
|
7
|
+
// arguments, and who allowed it. This module reads both back and folds
|
|
8
|
+
// them into one row per thing that happened.
|
|
9
|
+
//
|
|
10
|
+
// Connector calls pass through the harness relay, whose approval and scope
|
|
11
|
+
// decisions share the decision log. Read those alongside native tool runs.
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { readDecisions } from "./decision-log.js";
|
|
14
|
+
import { isRuntimeEvent, readRecentLines } from "./thread-events.js";
|
|
15
|
+
/** Composio toolkit slugs whose display name is not just a capitalised word. */
|
|
16
|
+
const TOOLKIT_NAMES = {
|
|
17
|
+
GMAIL: "Gmail",
|
|
18
|
+
GITHUB: "GitHub",
|
|
19
|
+
GOOGLESHEETS: "Google Sheets",
|
|
20
|
+
GOOGLECALENDAR: "Google Calendar",
|
|
21
|
+
GOOGLEDRIVE: "Google Drive",
|
|
22
|
+
GOOGLEDOCS: "Google Docs",
|
|
23
|
+
HUBSPOT: "HubSpot",
|
|
24
|
+
LINKEDIN: "LinkedIn",
|
|
25
|
+
YOUTUBE: "YouTube",
|
|
26
|
+
TWITTER: "X",
|
|
27
|
+
WHATSAPP: "WhatsApp",
|
|
28
|
+
ONEDRIVE: "OneDrive",
|
|
29
|
+
};
|
|
30
|
+
/** The agent's own built-in tools, phrased as what they did. */
|
|
31
|
+
const BUILTIN_LABELS = {
|
|
32
|
+
Bash: "Ran a command",
|
|
33
|
+
Read: "Read a file",
|
|
34
|
+
Edit: "Edited a file",
|
|
35
|
+
MultiEdit: "Edited files",
|
|
36
|
+
Write: "Wrote a file",
|
|
37
|
+
Glob: "Searched files",
|
|
38
|
+
Grep: "Searched files",
|
|
39
|
+
LS: "Listed a folder",
|
|
40
|
+
WebSearch: "Searched the web",
|
|
41
|
+
WebFetch: "Fetched a page",
|
|
42
|
+
Skill: "Used a skill",
|
|
43
|
+
Task: "Ran a subagent",
|
|
44
|
+
Agent: "Ran a subagent",
|
|
45
|
+
AskUserQuestion: "Asked you a question",
|
|
46
|
+
TodoWrite: "Updated its task list",
|
|
47
|
+
};
|
|
48
|
+
/** "save_issue" → "Save issue", "TaskUpdate" → "Task update",
|
|
49
|
+
* "CREATE_AN_ISSUE" → "Create an issue". */
|
|
50
|
+
function humanize(raw) {
|
|
51
|
+
const spaced = raw
|
|
52
|
+
.replace(/_+/g, " ")
|
|
53
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
54
|
+
.replace(/\s+/g, " ")
|
|
55
|
+
.trim()
|
|
56
|
+
.toLowerCase();
|
|
57
|
+
return spaced ? spaced[0].toUpperCase() + spaced.slice(1) : raw;
|
|
58
|
+
}
|
|
59
|
+
function toolkitName(slug) {
|
|
60
|
+
return TOOLKIT_NAMES[slug] ?? slug[0] + slug.slice(1).toLowerCase();
|
|
61
|
+
}
|
|
62
|
+
/** Composio names its tools TOOLKIT_ACTION_WORDS, all upper case. */
|
|
63
|
+
const COMPOSIO_TOOL = /^([A-Z][A-Z0-9]+)_([A-Z0-9_]+)$/;
|
|
64
|
+
/** Turn a raw tool name into the app it touched and the action, in words. */
|
|
65
|
+
export function describeTool(name) {
|
|
66
|
+
const builtin = BUILTIN_LABELS[name];
|
|
67
|
+
if (builtin)
|
|
68
|
+
return { app: null, label: builtin };
|
|
69
|
+
if (name.startsWith("mcp__")) {
|
|
70
|
+
const [, server = "", ...rest] = name.split("__");
|
|
71
|
+
const action = rest.join("__");
|
|
72
|
+
if (server === "composio")
|
|
73
|
+
return describeTool(action);
|
|
74
|
+
if (server === "computer")
|
|
75
|
+
return { app: "Computer", label: humanize(action) };
|
|
76
|
+
if (server === "agents")
|
|
77
|
+
return { app: "Team", label: humanize(action) };
|
|
78
|
+
if (server.startsWith("claude_ai_")) {
|
|
79
|
+
return { app: server.slice("claude_ai_".length).replace(/_/g, " "), label: humanize(action) };
|
|
80
|
+
}
|
|
81
|
+
return { app: humanize(server), label: humanize(action) };
|
|
82
|
+
}
|
|
83
|
+
const composio = COMPOSIO_TOOL.exec(name);
|
|
84
|
+
if (composio)
|
|
85
|
+
return { app: toolkitName(composio[1]), label: humanize(composio[2]) };
|
|
86
|
+
return { app: null, label: humanize(name) };
|
|
87
|
+
}
|
|
88
|
+
/** How many decision rows to read back before filtering to one bot. The
|
|
89
|
+
* file is rotation-capped, so this is a ceiling on parse work, not a
|
|
90
|
+
* window that quietly drops history. */
|
|
91
|
+
const DECISION_SCAN = 20_000;
|
|
92
|
+
/** Runtime events read per thread, newest first; the reader tail-walks. */
|
|
93
|
+
const EVENTS_PER_THREAD = 2_000;
|
|
94
|
+
/** An approval is folded into the tool run it unblocked when the run
|
|
95
|
+
* started inside this window around the decision. A verdict is written
|
|
96
|
+
* as the request opens; the run follows it, occasionally by a clock skew's
|
|
97
|
+
* worth in the other direction. */
|
|
98
|
+
const MATCH_BEFORE_MS = 5_000;
|
|
99
|
+
const MATCH_AFTER_MS = 30_000;
|
|
100
|
+
const OUTCOME_OF_DECISION = {
|
|
101
|
+
"auto-approved": "allowed",
|
|
102
|
+
"auto-denied": "denied",
|
|
103
|
+
"user-approved": "allowed",
|
|
104
|
+
"review-would-approve": "allowed",
|
|
105
|
+
"user-denied": "denied",
|
|
106
|
+
"review-would-deny": "denied",
|
|
107
|
+
"card-shown": "waiting",
|
|
108
|
+
};
|
|
109
|
+
function describeRow(base) {
|
|
110
|
+
const { app, label } = describeTool(base.tool);
|
|
111
|
+
return { ...base, app, label };
|
|
112
|
+
}
|
|
113
|
+
/** One row per request: the decision log writes a row when a card is shown
|
|
114
|
+
* and another when it is answered, and the receipt wants the answer. */
|
|
115
|
+
function foldDecisions(rows) {
|
|
116
|
+
const byRequest = new Map();
|
|
117
|
+
rows.forEach((row, index) => {
|
|
118
|
+
const key = row.requestId ?? `row:${index}`;
|
|
119
|
+
const group = byRequest.get(key);
|
|
120
|
+
if (group)
|
|
121
|
+
group.push(row);
|
|
122
|
+
else
|
|
123
|
+
byRequest.set(key, [row]);
|
|
124
|
+
});
|
|
125
|
+
const folded = [];
|
|
126
|
+
for (const group of byRequest.values()) {
|
|
127
|
+
const opened = group[0];
|
|
128
|
+
const latest = group[group.length - 1];
|
|
129
|
+
const summary = group.find((row) => row.summary)?.summary;
|
|
130
|
+
folded.push(describeRow({
|
|
131
|
+
at: opened.at,
|
|
132
|
+
threadId: opened.threadId,
|
|
133
|
+
requestId: opened.requestId,
|
|
134
|
+
tool: latest.tool ?? opened.tool ?? "unknown",
|
|
135
|
+
...(summary ? { summary } : {}),
|
|
136
|
+
outcome: OUTCOME_OF_DECISION[latest.decision],
|
|
137
|
+
}));
|
|
138
|
+
}
|
|
139
|
+
return folded;
|
|
140
|
+
}
|
|
141
|
+
function readToolRuns(eventsDir, threadId) {
|
|
142
|
+
const { lines } = readRecentLines(join(eventsDir, `${threadId}.ndjson`), EVENTS_PER_THREAD, isRuntimeEvent);
|
|
143
|
+
const runs = new Map();
|
|
144
|
+
for (const event of lines) {
|
|
145
|
+
if (event.type === "item.started" && event.itemType === "tool") {
|
|
146
|
+
const key = JSON.stringify([event.turnId ?? "", event.itemId ?? event.eventId]);
|
|
147
|
+
runs.set(key, describeRow({
|
|
148
|
+
at: event.createdAt,
|
|
149
|
+
threadId,
|
|
150
|
+
...(event.turnId ? { turnId: event.turnId } : {}),
|
|
151
|
+
tool: event.title ?? "unknown",
|
|
152
|
+
outcome: "running",
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
else if (event.type === "item.completed" && event.itemType === "tool" && event.itemId) {
|
|
156
|
+
const run = runs.get(JSON.stringify([event.turnId ?? "", event.itemId]));
|
|
157
|
+
if (run)
|
|
158
|
+
run.outcome = event.ok ? "ran" : "failed";
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return [...runs.values()];
|
|
162
|
+
}
|
|
163
|
+
/** The bot's activity, newest first: every tool run in the threads named,
|
|
164
|
+
* and every decision the broker logged for the bot, with an approval folded
|
|
165
|
+
* into the run it unblocked wherever the two can be matched. */
|
|
166
|
+
export function readBotActivity(input) {
|
|
167
|
+
const { dataDir, eventsDir, botId, threadIds, limit } = input;
|
|
168
|
+
const runs = threadIds.flatMap((threadId) => readToolRuns(eventsDir, threadId));
|
|
169
|
+
const decisions = foldDecisions(readDecisions(dataDir, DECISION_SCAN).filter((row) => row.botId === botId));
|
|
170
|
+
const matched = new Set();
|
|
171
|
+
const rows = [...runs];
|
|
172
|
+
for (const decision of decisions) {
|
|
173
|
+
if (decision.outcome !== "allowed") {
|
|
174
|
+
rows.push(decision);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const askedAt = Date.parse(decision.at);
|
|
178
|
+
let best = null;
|
|
179
|
+
for (const run of runs) {
|
|
180
|
+
if (matched.has(run) || run.threadId !== decision.threadId || run.tool !== decision.tool)
|
|
181
|
+
continue;
|
|
182
|
+
const delta = Date.parse(run.at) - askedAt;
|
|
183
|
+
if (delta < -MATCH_BEFORE_MS || delta > MATCH_AFTER_MS)
|
|
184
|
+
continue;
|
|
185
|
+
if (!best || Math.abs(delta) < Math.abs(Date.parse(best.at) - askedAt))
|
|
186
|
+
best = run;
|
|
187
|
+
}
|
|
188
|
+
if (best) {
|
|
189
|
+
matched.add(best);
|
|
190
|
+
if (decision.summary)
|
|
191
|
+
best.summary = decision.summary;
|
|
192
|
+
if (decision.requestId)
|
|
193
|
+
best.requestId = decision.requestId;
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
rows.push(decision);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
rows.sort((a, b) => (a.at < b.at ? 1 : a.at > b.at ? -1 : 0));
|
|
200
|
+
return rows.slice(0, Math.max(1, limit));
|
|
201
|
+
}
|
|
@@ -20,16 +20,13 @@ import { appendFile, mkdir } from "node:fs/promises";
|
|
|
20
20
|
import { basename, join } from "node:path";
|
|
21
21
|
import { actorLabel, boundRetentionDays, pruneMonthFiles } from "./decision-log.js";
|
|
22
22
|
import { redactSecrets } from "./redact.js";
|
|
23
|
-
import { csvCell } from "./usage-ledger.js";
|
|
23
|
+
import { csvCell, monthKey } from "./usage-ledger.js";
|
|
24
24
|
export const ADMIN_ACTIVITY_CATEGORIES = ["config", "people", "session", "webhook", "mcp", "engine", "bot", "budget", "visibility"];
|
|
25
25
|
const DIR = "admin-activity";
|
|
26
26
|
const MONTH_FILE = /^(\d{4})-(\d{2})\.ndjson$/;
|
|
27
27
|
const PRUNE_EVERY_MS = 60 * 60_000;
|
|
28
28
|
const writeQueues = new Map();
|
|
29
29
|
const lastPrune = new Map();
|
|
30
|
-
function monthKey(at) {
|
|
31
|
-
return `${at.getUTCFullYear()}-${String(at.getUTCMonth() + 1).padStart(2, "0")}`;
|
|
32
|
-
}
|
|
33
30
|
export function adminActivityFileFor(dataDir, at) {
|
|
34
31
|
return join(dataDir, DIR, `${monthKey(at)}.ndjson`);
|
|
35
32
|
}
|
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
// Nothing here decides whether an action is safe. Each approval level is a
|
|
4
4
|
// provider's own permission mode passed straight through (Claude `auto`,
|
|
5
5
|
// Grok `--permission-mode`, Codex `approvalsReviewer`, …), and a request that
|
|
6
|
-
// reaches this process is one the provider left for a person. The
|
|
7
|
-
//
|
|
8
|
-
//
|
|
6
|
+
// reaches this process is one the provider left for a person. The grants the
|
|
7
|
+
// app applies are Full access and the person's exact saved commands. Approve
|
|
8
|
+
// for me also allows a web search, and that is the one action the app itself
|
|
9
|
+
// grants. Questions never come through here: a bot's question always reaches
|
|
10
|
+
// a human.
|
|
9
11
|
import { supportsApprovalMode } from "../shared/approval-mode.js";
|
|
12
|
+
import { isOutboundTool } from "../shared/outbound.js";
|
|
10
13
|
/** A failed delivery is a runtime error, not another permission decision.
|
|
11
14
|
* An expired ask must never become a fresh Allow/Deny card. */
|
|
12
15
|
export async function deliverFullAccessApproval(adapter, threadId, requestId, turnId, isCurrent = () => false) {
|
|
@@ -77,11 +80,19 @@ export function delegatedApprovalMode(input) {
|
|
|
77
80
|
// produce an answer: the CLI runs the tool with none and the model is told
|
|
78
81
|
// "The user did not answer the questions." — a question silently lost.
|
|
79
82
|
const ASKS_A_PERSON = new Set(["askuserquestion", "ask_user", "omb-ask"]);
|
|
83
|
+
// A web search is a low-level read. Approve for me allows these names and
|
|
84
|
+
// nothing wider: a bare search, a page fetch, and every other tool stay a
|
|
85
|
+
// prompt. The name is the same shape as a question — one mcp__<server>__
|
|
86
|
+
// prefix removed, then compared case-insensitively.
|
|
87
|
+
const WEB_SEARCH = new Set(["websearch", "web_search", "web-search", "search_web"]);
|
|
88
|
+
function bareToolName(tool) {
|
|
89
|
+
return tool.replace(/^mcp__[^_]+__/, "").toLowerCase();
|
|
90
|
+
}
|
|
80
91
|
export function autoVerdict(mode, tool, context) {
|
|
81
92
|
// A question is for a person, whatever channel it arrived on — and
|
|
82
93
|
// whatever the mode: even Full has no answer to give, only an approval
|
|
83
94
|
// that would run the tool with none.
|
|
84
|
-
if (ASKS_A_PERSON.has(tool
|
|
95
|
+
if (ASKS_A_PERSON.has(bareToolName(tool))) {
|
|
85
96
|
return { approve: null, source: "no-grant" };
|
|
86
97
|
}
|
|
87
98
|
// Full's promise is literal: even a sandbox widening is approved. Entering
|
|
@@ -91,8 +102,15 @@ export function autoVerdict(mode, tool, context) {
|
|
|
91
102
|
return { approve: `approved ${tool} (full access)`, source: "full-access" };
|
|
92
103
|
if (context?.requiresExplicitApproval)
|
|
93
104
|
return { approve: null, source: "explicit-approval-block" };
|
|
105
|
+
if (isOutboundTool(tool))
|
|
106
|
+
return { approve: null, source: "outbound-guard" };
|
|
94
107
|
if (context?.commandAllowed)
|
|
95
108
|
return { approve: `approved ${tool} (saved command)`, source: "command-allowlist" };
|
|
109
|
+
// The one action the app itself grants. A fetch of an arbitrary URL is not
|
|
110
|
+
// a search and stays a prompt, in this mode and every other.
|
|
111
|
+
if (mode === "auto" && WEB_SEARCH.has(bareToolName(tool))) {
|
|
112
|
+
return { approve: `approved ${tool} (web search)`, source: "web-search" };
|
|
113
|
+
}
|
|
96
114
|
if (mode === "auto" || mode === "custom")
|
|
97
115
|
return { approve: null, source: "native-approval" };
|
|
98
116
|
return { approve: null, source: "no-grant" };
|
|
@@ -105,6 +123,7 @@ export function autoVerdict(mode, tool, context) {
|
|
|
105
123
|
* the server keeps sending: cards saved before the key existed still render,
|
|
106
124
|
* and so do the free-text apply errors that have no key at all. */
|
|
107
125
|
export const HELD_NOTE = {
|
|
126
|
+
"approval.held.outbound": "This sends something on your behalf, so it always asks first.",
|
|
108
127
|
"approval.held.native": "The provider requires your approval for this action.",
|
|
109
128
|
"approval.held.sandbox": "This changes the provider sandbox, so only Full access can approve it automatically.",
|
|
110
129
|
"approval.held.undeliveredFull": "Full access couldn't deliver this approval.",
|
|
@@ -115,6 +134,8 @@ export const HELD_NOTE = {
|
|
|
115
134
|
export function approvalHeldNote(context) {
|
|
116
135
|
if (!context.permission)
|
|
117
136
|
return undefined;
|
|
137
|
+
if (context.source === "outbound-guard")
|
|
138
|
+
return "approval.held.outbound";
|
|
118
139
|
if (context.source === "explicit-approval-block")
|
|
119
140
|
return "approval.held.sandbox";
|
|
120
141
|
if (context.source === "native-approval")
|
|
@@ -7,7 +7,7 @@ export function recoveryCapabilityError(from, to) {
|
|
|
7
7
|
supportsWorkspaceFiles(from.driverKind) !== supportsWorkspaceFiles(to.driverKind)) {
|
|
8
8
|
return "The backup cannot use the same workspace.";
|
|
9
9
|
}
|
|
10
|
-
const tools = ["agentsMcp", "computerMcp", "
|
|
10
|
+
const tools = ["agentsMcp", "computerMcp", "composioMcp",
|
|
11
11
|
"phoneMcp", "browserMcp", "localComputerMcp", "customMcp", "images"];
|
|
12
12
|
if (tools.some(key => from.capabilities[key] === true && to.capabilities[key] !== true)) {
|
|
13
13
|
return "The backup does not support all of this engine's tools and attachments.";
|
|
@@ -388,13 +388,6 @@ export function resolveBoatCreate(request) {
|
|
|
388
388
|
return { ...resolved };
|
|
389
389
|
});
|
|
390
390
|
}
|
|
391
|
-
/** Read-only deletion guard. Both a key-only request with an ambiguous
|
|
392
|
-
* provider outcome and a known-but-not-yet-named Boat must keep its bot owner. */
|
|
393
|
-
export function hasUnresolvedBoatCreate(botId) {
|
|
394
|
-
if (!BOT_ID.test(botId))
|
|
395
|
-
throw new Error("invalid bot id for cloud computer creation");
|
|
396
|
-
return withJournalLock((requests) => (requests.some((request) => request.botId === botId && request.resolved !== true)));
|
|
397
|
-
}
|
|
398
391
|
/** Sanitized local recovery authority for configuration guards. The fresh,
|
|
399
392
|
* lock-protected read can prove a remembered provider id even while the
|
|
400
393
|
* account-wide LIST endpoint is eventually consistent. Provider request
|
|
@@ -442,7 +442,6 @@ export function markBoatDeletionBlocked(boxId, value) {
|
|
|
442
442
|
export function boatDeletionSnapshot() {
|
|
443
443
|
return withJournalLock((records) => records.map(clone));
|
|
444
444
|
}
|
|
445
|
-
export const listBoatDeletions = boatDeletionSnapshot;
|
|
446
445
|
export function getBoatDeletion(boxId) {
|
|
447
446
|
if (!BOAT_ID.test(boxId))
|
|
448
447
|
throw new Error("invalid cloud computer id for deletion");
|
|
@@ -451,9 +450,6 @@ export function getBoatDeletion(boxId) {
|
|
|
451
450
|
return record ? clone(record) : null;
|
|
452
451
|
});
|
|
453
452
|
}
|
|
454
|
-
export function isBoatDeletionPending(boxId) {
|
|
455
|
-
return getBoatDeletion(boxId) !== null;
|
|
456
|
-
}
|
|
457
453
|
export function hasPendingBoatDeletionForBot(botId) {
|
|
458
454
|
if (!BOT_ID.test(botId))
|
|
459
455
|
throw new Error("invalid bot id for cloud computer deletion");
|
|
@@ -88,9 +88,7 @@ function scopedBoatPrefix() {
|
|
|
88
88
|
}
|
|
89
89
|
/** Decide lifecycle work before a turn mounts Boat. Auto may observe and
|
|
90
90
|
* attach an already-ready Boat, but only explicit Cloud may create or wake. */
|
|
91
|
-
export function boatTurnLifecycleAction({ explicitCloud,
|
|
92
|
-
if (!canMount)
|
|
93
|
-
return "none";
|
|
91
|
+
export function boatTurnLifecycleAction({ explicitCloud, state, }) {
|
|
94
92
|
if (state && READY.has(state))
|
|
95
93
|
return "attach";
|
|
96
94
|
if (!explicitCloud)
|
|
@@ -828,7 +826,9 @@ export async function findBoat(cfg, botId) {
|
|
|
828
826
|
if (cachedId) {
|
|
829
827
|
let direct = null;
|
|
830
828
|
try {
|
|
831
|
-
|
|
829
|
+
// Bounded like every other Boat read: a relay that accepts the
|
|
830
|
+
// connection and stalls must not hold a turn's setup for minutes.
|
|
831
|
+
direct = await boatJson(cfg, `/boxes/${cachedId}`, { signal: AbortSignal.timeout(20_000) });
|
|
832
832
|
}
|
|
833
833
|
catch {
|
|
834
834
|
// A direct read can fail while the account listing still succeeds.
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
import { stringify as stringifyYaml } from "yaml";
|
|
2
2
|
import { cronScheduleLabel } from "../shared/cron-label.js";
|
|
3
|
-
import { BOTMRR_MARKDOWN_VERSION, isPackageDocument,
|
|
3
|
+
import { BOTMRR_MARKDOWN_VERSION, isPackageDocument, PACKAGE_V1_MAX_SKILLS, parsePackageV1, utf8Bytes, } from "../shared/package-format.js";
|
|
4
4
|
// The package file format (v1 and v2) lives in shared/package-format.ts: one
|
|
5
5
|
// validation gate for the server, the renderer and Admin. This module keeps
|
|
6
6
|
// the v1 names the v1 exporter and its Markdown playbook are written in, and
|
|
7
7
|
// re-exports the v2 reader so server code has one place to import from.
|
|
8
8
|
export { canonicalJson, downgradeToV1, PACKAGE_MAX_BYTES, PACKAGE_VERSION, PackageFormatError, packageKeys, packageScanFindings, packageSecretFindings, packageSummary, parsePackageDocument, redactPackageSecrets, upgradeV1, } from "../shared/package-format.js";
|
|
9
|
-
export const BOT_PACKAGE_FORMAT = PACKAGE_FORMAT;
|
|
10
|
-
export const BOT_PACKAGE_VERSION = PACKAGE_V1_VERSION;
|
|
11
|
-
export { BOTMRR_MARKDOWN_VERSION };
|
|
12
|
-
export const BOT_PACKAGE_SKILLS_VERSION = 1;
|
|
13
9
|
export const BOT_PACKAGE_MAX_SKILLS = PACKAGE_V1_MAX_SKILLS;
|
|
14
10
|
const BOT_PACKAGE_MARKDOWN_MAX_BYTES = 1_000_000;
|
|
15
11
|
export function isBotPackage(value) {
|
|
@@ -98,17 +94,3 @@ export function renderBotPackageMarkdown(document) {
|
|
|
98
94
|
throw new Error("The bot playbook is too large");
|
|
99
95
|
return markdown;
|
|
100
96
|
}
|
|
101
|
-
export function packageAgentAsMember(agent) {
|
|
102
|
-
return {
|
|
103
|
-
key: agent.key,
|
|
104
|
-
name: agent.name,
|
|
105
|
-
title: agent.title ?? "",
|
|
106
|
-
description: agent.description ?? "",
|
|
107
|
-
...(agent.soul !== undefined ? { soul: agent.soul } : {}),
|
|
108
|
-
appearance: {
|
|
109
|
-
color: agent.appearance.color,
|
|
110
|
-
...(agent.appearance.mascotExpression ? { mascotExpression: agent.appearance.mascotExpression } : {}),
|
|
111
|
-
...(agent.appearance.mascotBody ? { mascotBody: agent.appearance.mascotBody } : {}),
|
|
112
|
-
},
|
|
113
|
-
};
|
|
114
|
-
}
|
|
@@ -216,8 +216,7 @@ export class BrowserRuntime {
|
|
|
216
216
|
options;
|
|
217
217
|
closeBrowser;
|
|
218
218
|
constructor({ closeBrowser, ...options } = {}) {
|
|
219
|
-
|
|
220
|
-
this.options = { requestTimeoutMs: 120_000, takeoverTimeoutMs: 15_000, idleMs: 60_000, maxPending: 16, resultBudget: Number.isFinite(budget) && budget > 0 ? budget : DEFAULT_BROWSER_RESULT_BUDGET, ...options };
|
|
219
|
+
this.options = { requestTimeoutMs: 120_000, takeoverTimeoutMs: 15_000, idleMs: 60_000, maxPending: 16, resultBudget: DEFAULT_BROWSER_RESULT_BUDGET, ...options };
|
|
221
220
|
this.closeBrowser = closeBrowser ?? (async () => false);
|
|
222
221
|
}
|
|
223
222
|
gate(session) {
|