openmausbot 0.1.56 → 0.1.58

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.
Files changed (49) hide show
  1. package/dist/assets/index-B433sv8K.css +1 -0
  2. package/dist/assets/{index-Dq2j4Ikr.js → index-CE7W1AIk.js} +1 -1
  3. package/dist/assets/index-r-WPT-Pu.js +264 -0
  4. package/dist/index.html +2 -2
  5. package/dist-server/companion/src/routes.js +6 -1
  6. package/dist-server/connector-proxy.js +31 -12
  7. package/dist-server/container-mcp.js +49 -31
  8. package/dist-server/drivers/agents-proxy.js +56 -2
  9. package/dist-server/index.js +2126 -487
  10. package/dist-server/openmausbot.js +15219 -305
  11. package/dist-server/pair-cli.js +15219 -305
  12. package/dist-server/server/auto-approve.js +24 -0
  13. package/dist-server/server/bot-folder.js +102 -0
  14. package/dist-server/server/bot-overview.js +206 -0
  15. package/dist-server/server/bot-package.js +5 -0
  16. package/dist-server/server/bot-profile.js +7 -0
  17. package/dist-server/server/browser-engine-release.js +44 -0
  18. package/dist-server/server/browser-engine.js +185 -0
  19. package/dist-server/server/cli.js +55 -1
  20. package/dist-server/server/connector-proxy.js +37 -16
  21. package/dist-server/server/container-computer.js +9 -2
  22. package/dist-server/server/drivers/agents-proxy.js +63 -2
  23. package/dist-server/server/drivers/antigravity-acp.js +119 -20
  24. package/dist-server/server/drivers/antigravity-runtime.js +47 -3
  25. package/dist-server/server/drivers/antigravity.js +23 -2
  26. package/dist-server/server/drivers/claude.js +7 -1
  27. package/dist-server/server/index.js +634 -107
  28. package/dist-server/server/mcp-bridge.js +73 -44
  29. package/dist-server/server/package-export.js +1 -0
  30. package/dist-server/server/profile-requests.js +332 -0
  31. package/dist-server/server/profile-revision.js +15 -0
  32. package/dist-server/server/profile-versions.js +154 -0
  33. package/dist-server/server/routine-requests.js +34 -1
  34. package/dist-server/server/routines.js +70 -2
  35. package/dist-server/server/setup-mode.js +63 -0
  36. package/dist-server/server/skill-fetch.js +67 -11
  37. package/dist-server/server/skills.js +1 -1
  38. package/dist-server/server/store.js +87 -7
  39. package/dist-server/server/system-prompt.js +49 -0
  40. package/dist-server/server/team-backup.js +2 -2
  41. package/dist-server/server/team-manifest.js +7 -0
  42. package/dist-server/shared/bot-profile.js +5 -1
  43. package/dist-server/shared/line-diff.js +42 -0
  44. package/dist-server/shared/profile-request.js +9 -0
  45. package/dist-server/shared/team-backup.js +4 -0
  46. package/dist-server/vps-container-mcp.js +49 -31
  47. package/package.json +1 -1
  48. package/dist/assets/index-CJBrtDbC.css +0 -1
  49. package/dist/assets/index-D0xt96rv.js +0 -263
package/dist/index.html CHANGED
@@ -5,8 +5,8 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <link rel="icon" type="image/svg+xml" href="/app-icon.svg" />
7
7
  <title>OpenMausBot</title>
8
- <script type="module" crossorigin src="/assets/index-D0xt96rv.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-CJBrtDbC.css">
8
+ <script type="module" crossorigin src="/assets/index-r-WPT-Pu.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-B433sv8K.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
@@ -84,8 +84,13 @@ const ALLOWED = [
84
84
  { method: "POST", path: /^\/api\/bots\/[\w-]+\/tasks\/[\w-]+$/ },
85
85
  { method: "PATCH", path: /^\/api\/bots\/[\w-]+\/tasks\/[\w-]+$/ },
86
86
  { method: "DELETE", path: /^\/api\/bots\/[\w-]+\/tasks\/[\w-]+$/ },
87
+ // Read-only summary: who the bot is, what it does and won't do, and its
88
+ // recent activity. No settings, no transcript — read on open and on
89
+ // pull-to-refresh.
90
+ { method: "GET", path: /^\/api\/bots\/[\w-]+\/overview$/ },
87
91
  // Paired-safe profile subset. The harness route itself rejects fields
88
- // outside identity, avatar, notifications, and voice preferences.
92
+ // outside identity, standing instructions (soul, byte-capped), avatar,
93
+ // notifications, and voice preferences.
89
94
  { method: "PATCH", path: /^\/api\/bots\/[\w-]+\/profile$/ },
90
95
  // Full model selection, but no other bot settings. The harness validates
91
96
  // the live catalog and refuses changes while the bot is working.
@@ -96,20 +96,38 @@ function connectorAdds(args) {
96
96
  if (!args || typeof args !== "object" || Array.isArray(args)) return [];
97
97
  const toolkits = args.toolkits;
98
98
  if (!Array.isArray(toolkits)) return [];
99
- return [...new Set(toolkits.flatMap((item) => {
100
- if (typeof item === "string") return [item.toLowerCase()];
101
- if (!item || typeof item !== "object" || Array.isArray(item)) return [];
99
+ const seen = /* @__PURE__ */ new Set();
100
+ const requests = [];
101
+ for (const item of toolkits) {
102
+ if (typeof item === "string") {
103
+ const slug2 = item.trim().toLowerCase();
104
+ const key2 = JSON.stringify([slug2, ""]);
105
+ if (!seen.has(key2)) {
106
+ seen.add(key2);
107
+ requests.push({ slug: slug2 });
108
+ }
109
+ continue;
110
+ }
111
+ if (!item || typeof item !== "object" || Array.isArray(item)) continue;
102
112
  const row = item;
103
- const slug = typeof row.toolkit === "string" ? row.toolkit : row.name;
104
113
  const action = String(row.action ?? "add").toLowerCase();
105
- return typeof slug === "string" && ["add", "connect", "initiate"].includes(action) ? [slug.toLowerCase()] : [];
106
- }))];
114
+ if (!["add", "connect", "initiate"].includes(action)) continue;
115
+ const slug = typeof row.toolkit === "string" ? row.toolkit : row.name;
116
+ if (typeof slug !== "string") continue;
117
+ const normalized = slug.trim().toLowerCase();
118
+ const alias = (typeof row.alias === "string" ? row.alias : typeof row.account === "string" ? row.account : "").trim();
119
+ const key = JSON.stringify([normalized, alias.toLowerCase()]);
120
+ if (seen.has(key)) continue;
121
+ seen.add(key);
122
+ requests.push({ slug: normalized, ...alias ? { alias } : {} });
123
+ }
124
+ return requests;
107
125
  }
108
- async function showConnectorCards(slugs) {
126
+ async function showConnectorCards(items) {
109
127
  const response = await fetch(`${HARNESS}/api/internal/connectors/request`, {
110
128
  method: "POST",
111
129
  headers: { "content-type": "application/json", authorization: `Bearer ${TOKEN}` },
112
- body: JSON.stringify({ botId: BOT_ID, threadId: THREAD_ID, slugs, resumeKey: randomUUID() }),
130
+ body: JSON.stringify({ botId: BOT_ID, threadId: THREAD_ID, items, resumeKey: randomUUID() }),
113
131
  signal: AbortSignal.timeout(3e4)
114
132
  });
115
133
  if (!response.ok) {
@@ -141,12 +159,13 @@ async function handle(message) {
141
159
  if (method === "tools/call") {
142
160
  const params = message.params ?? {};
143
161
  const name = String(params.name ?? "");
144
- const slugs = /MANAGE_CONNECTIONS$/i.test(name) ? connectorAdds(params.arguments) : [];
145
- if (slugs.length) {
146
- await showConnectorCards(slugs);
162
+ const requests = /MANAGE_CONNECTIONS$/i.test(name) ? connectorAdds(params.arguments) : [];
163
+ if (requests.length) {
164
+ await showConnectorCards(requests);
165
+ const labels = requests.map((r) => r.alias ? `${r.slug} (${r.alias})` : r.slug).join(", ");
147
166
  send(textResult(
148
167
  id,
149
- `OpenMausBot showed the user a secure connection card for ${slugs.join(", ")}. End this turn now. The app will continue the task automatically after the connection finishes.`
168
+ `OpenMausBot showed the user a secure connection card for ${labels}. End this turn now. The app will continue the task automatically after the connection finishes.`
150
169
  ));
151
170
  return;
152
171
  }
@@ -15082,6 +15082,28 @@ function createGateInterceptor(options) {
15082
15082
  });
15083
15083
  };
15084
15084
  }
15085
+ function createMcpBridgeInterceptor(options) {
15086
+ const afterPing = options.gate ? createGateInterceptor({
15087
+ isHeld: options.gate.isHeld,
15088
+ forward: options.forward,
15089
+ refuse: options.answer,
15090
+ refusalText: options.gate.refusalText
15091
+ }) : options.forward;
15092
+ return (line) => {
15093
+ let frame = null;
15094
+ try {
15095
+ frame = JSON.parse(line);
15096
+ } catch {
15097
+ }
15098
+ if (frame && frame.method === "ping") {
15099
+ if (frame.id !== void 0) {
15100
+ options.answer(JSON.stringify({ jsonrpc: "2.0", id: frame.id, result: {} }));
15101
+ }
15102
+ return;
15103
+ }
15104
+ afterPing(line);
15105
+ };
15106
+ }
15085
15107
  function runMcpBridge(options) {
15086
15108
  const child = spawn(options.command, options.args, {
15087
15109
  shell: false,
@@ -15091,37 +15113,33 @@ function runMcpBridge(options) {
15091
15113
  child.stdin.on("error", () => {
15092
15114
  });
15093
15115
  child.stderr.pipe(process.stderr);
15094
- let detach;
15095
- if (options.gate) {
15096
- const client = createControlClient({ url: options.gate.url, token: options.gate.token });
15097
- const inbound = createLineSplitter(
15098
- createGateInterceptor({
15099
- isHeld: async () => (await client.state(true)).held,
15100
- forward: (line) => child.stdin.write(line + "\n"),
15101
- refuse: (line) => process.stdout.write(line + "\n")
15102
- })
15103
- );
15104
- const onStdin = (chunk) => inbound.push(chunk);
15105
- process.stdin.on("data", onStdin);
15106
- process.stdin.on("end", () => {
15107
- inbound.flush();
15108
- child.stdin.end();
15109
- });
15110
- const outbound = createLineSplitter((line) => process.stdout.write(line + "\n"));
15111
- child.stdout.on("data", (chunk) => outbound.push(chunk));
15112
- child.stdout.on("end", () => outbound.flush());
15113
- detach = () => {
15114
- process.stdin.off("data", onStdin);
15115
- process.stdin.pause();
15116
- };
15117
- } else {
15118
- process.stdin.pipe(child.stdin);
15119
- child.stdout.pipe(process.stdout);
15120
- detach = () => {
15121
- process.stdin.unpipe(child.stdin);
15122
- process.stdin.pause();
15123
- };
15124
- }
15116
+ const client = options.gate ? createControlClient({ url: options.gate.url, token: options.gate.token }) : null;
15117
+ const answer = (line) => process.stdout.write(line + "\n");
15118
+ const forward = (line) => child.stdin.write(line + "\n");
15119
+ const inbound = createLineSplitter(
15120
+ createMcpBridgeInterceptor({
15121
+ answer,
15122
+ forward,
15123
+ ...options.gate ? {
15124
+ gate: {
15125
+ isHeld: async () => (await client.state(true)).held
15126
+ }
15127
+ } : {}
15128
+ })
15129
+ );
15130
+ const onStdin = (chunk) => inbound.push(chunk);
15131
+ process.stdin.on("data", onStdin);
15132
+ process.stdin.on("end", () => {
15133
+ inbound.flush();
15134
+ child.stdin.end();
15135
+ });
15136
+ const outbound = createLineSplitter((line) => process.stdout.write(line + "\n"));
15137
+ child.stdout.on("data", (chunk) => outbound.push(chunk));
15138
+ child.stdout.on("end", () => outbound.flush());
15139
+ const detach = () => {
15140
+ process.stdin.off("data", onStdin);
15141
+ process.stdin.pause();
15142
+ };
15125
15143
  let watchdog = null;
15126
15144
  if (options.liveness) {
15127
15145
  const liveness = options.liveness;
@@ -192,6 +192,10 @@ var ROUTINE_FIELDS_SCHEMA = {
192
192
  clear_timeout: {
193
193
  type: "boolean",
194
194
  description: "Only for updates: set true to remove an existing safety limit. Do not combine with timeout_minutes."
195
+ },
196
+ continuity: {
197
+ type: "boolean",
198
+ description: "Opt in to using the latest completed run's bounded report as historical context. Defaults to false; set false in an update to start fresh again. Shown on the confirmation card."
195
199
  }
196
200
  };
197
201
  var TOOLS = [
@@ -371,6 +375,31 @@ var TOOLS = [
371
375
  required: ["routine_id", "action"]
372
376
  }
373
377
  },
378
+ {
379
+ name: "propose_profile",
380
+ description: "Propose changes to your own name, title, description, standing instructions (SOUL.md), or working folder (cwd). This only creates a confirmation card; nothing changes until the user approves it. After calling it, end the turn and do not claim the change is applied. Keep SOUL.md short \u2014 who you are and the rules you never break; put step-by-step procedure into a skill instead. A Chief of Staff may pass for_bot_id (from list_bots) to propose a change for another bot in its section.",
381
+ inputSchema: {
382
+ type: "object",
383
+ additionalProperties: false,
384
+ properties: {
385
+ name: { type: "string", maxLength: 100, description: "New display name." },
386
+ title: { type: "string", maxLength: 200, description: "New role or title." },
387
+ description: { type: "string", maxLength: 4e3, description: "New one-line blurb shown in rosters." },
388
+ soul: { type: "string", description: "Full replacement text for SOUL.md, at most 24000 bytes." },
389
+ cwd: {
390
+ type: "string",
391
+ maxLength: 1024,
392
+ description: "Absolute path of the folder your tools read and write in (for example /Users/me/Projects/site). It must already exist. An empty string means your private workspace."
393
+ },
394
+ reason: { type: "string", minLength: 1, maxLength: 500, description: "One sentence the user will see explaining why." },
395
+ for_bot_id: {
396
+ type: "string",
397
+ description: "Chief of Staff only: the id of another bot in your section whose profile this changes. Omit to change your own."
398
+ }
399
+ },
400
+ required: ["reason"]
401
+ }
402
+ },
374
403
  {
375
404
  name: "skills_list",
376
405
  description: "List this bot's imported skills (enabled and disabled) and any staged skill writes waiting for the user to confirm. Use this before skill_manage to avoid duplicate names. Listing does not enable anything.",
@@ -445,16 +474,17 @@ function routineFields(args) {
445
474
  if (typeof args.run_on === "string") fields.runOn = args.run_on;
446
475
  if (args.clear_timeout === true) fields.timeoutMinutes = null;
447
476
  else if (typeof args.timeout_minutes === "number") fields.timeoutMinutes = args.timeout_minutes;
477
+ if (typeof args.continuity === "boolean") fields.continuity = args.continuity;
448
478
  return { fields };
449
479
  }
450
- function confirmationResult(r, fallback) {
480
+ function confirmationResult(r, fallback, noun = "routine") {
451
481
  const summary = typeof r.summary === "string" && r.summary.trim() ? `
452
482
 
453
483
  ${r.summary.trim()}` : "";
454
484
  return {
455
485
  text: `A confirmation card is now visible to the user for ${fallback}.${summary}
456
486
 
457
- This change has not been applied yet. End this turn and wait for the user to confirm or deny the card; do not claim the routine was created or changed before confirmation.`
487
+ This change has not been applied yet. End this turn and wait for the user to confirm or deny the card; do not claim the ${noun} was created or changed before confirmation.`
458
488
  };
459
489
  }
460
490
  function recallSpeaker(hit) {
@@ -723,6 +753,30 @@ ${JSON.stringify(routines, null, 2)}`
723
753
  });
724
754
  return confirmationResult(r, `${action.replace("_", " ")} on routine ${routineId}`);
725
755
  }
756
+ if (name === "propose_profile") {
757
+ const changes = {};
758
+ if (typeof args.name === "string") changes.name = args.name.trim();
759
+ if (typeof args.title === "string") changes.title = args.title.trim();
760
+ if (typeof args.description === "string") changes.description = args.description.trim();
761
+ if (typeof args.soul === "string") changes.soul = args.soul;
762
+ if (typeof args.cwd === "string") changes.cwd = args.cwd.trim();
763
+ if (!Object.keys(changes).length) {
764
+ return { text: "propose_profile needs at least one of name, title, description, soul, or cwd.", isError: true };
765
+ }
766
+ const forBotId = String(args.for_bot_id ?? "").trim();
767
+ const r = await api("/api/internal/profile-requests", {
768
+ method: "POST",
769
+ body: JSON.stringify({
770
+ fromBotId: BOT_ID,
771
+ fromThreadId: THREAD_ID,
772
+ changes,
773
+ reason: args.reason,
774
+ // JSON.stringify drops the key entirely when no target was named
775
+ forBotId: forBotId || void 0
776
+ })
777
+ });
778
+ return confirmationResult(r, "the profile change", "profile");
779
+ }
726
780
  if (name === "session_search") {
727
781
  const q = String(args.query ?? "").trim();
728
782
  if (!q) return { text: 'session_search needs a query, for example {"query":"site audit broken links"}.', isError: true };