@open-everywhere/create 0.4.0 → 0.6.0

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 CHANGED
@@ -58,7 +58,7 @@ prompts. The directory must be new or empty. A fresh clone of an empty repositor
58
58
  and it counts as empty: run `npx @open-everywhere/create .` inside it. The scaffold refuses a
59
59
  directory that holds anything else. Run inside an empty clone named `my-agent`,
60
60
  `npx @open-everywhere/create my-agent` would make `my-agent/my-agent/`, so it refuses and prints the
61
- `.` command instead. To nest on purpose, create the inner directory first. The app depends on the eight `@open-everywhere/*` libraries at
61
+ `.` command instead. To nest on purpose, create the inner directory first. The app depends on the nine `@open-everywhere/*` libraries at
62
62
  this CLI's own version — plus this package itself, as a dev dependency, for its `check` script — and
63
63
  on `eve` at the pin core declares; its `postinstall` applies the eve patch. Its roster mounts the launch surfaces: Slack, Notion and GitHub. The app carries `AGENTS.md`,
64
64
  a `.mcp.json` for the `open-everywhere-ops` MCP server and the `quickstart-open-everywhere` skill,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-everywhere/create",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "description": "npx scaffold and setup check for Open Everywhere — one step engine, reported by --check for an agent-led setup.",
@@ -46,8 +46,8 @@
46
46
  "test:env-pipe": "tsx scripts/env-pipe-test.ts"
47
47
  },
48
48
  "dependencies": {
49
- "@open-everywhere/core": "0.4.0",
50
- "@open-everywhere/provider-slack": "0.4.0",
49
+ "@open-everywhere/core": "0.6.0",
50
+ "@open-everywhere/provider-slack": "0.6.0",
51
51
  "tsx": "^4.23.9"
52
52
  },
53
53
  "devDependencies": {
package/src/scaffold.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  // THE SCAFFOLD — `npx @open-everywhere/create <dir>` copies the quickstart app (this package's
2
2
  // `template/` directory) into a new or empty `<dir>`, and then prints the next steps.
3
3
  //
4
- // The template is a minimal npm-consumer app: the eight `@open-everywhere/*` libraries and `eve`,
4
+ // The template is a minimal npm-consumer app: the nine `@open-everywhere/*` libraries and `eve`,
5
5
  // a roster of the launch surfaces (Slack, Notion, GitHub), and the agent config. Every
6
6
  // `@open-everywhere/*` dependency, `@open-everywhere/create` itself, and the versions in
7
7
  // `roster.lock.json` carry the {@link VERSION_PLACEHOLDER}. The scaffold replaces it with this
8
- // CLI's own version: all nine packages publish in lockstep, so this CLI's version IS the version
8
+ // CLI's own version: all ten packages publish in lockstep, so this CLI's version IS the version
9
9
  // of every library it installs.
10
10
  //
11
11
  // THREE PACKING RULES SHAPE THE TEMPLATE:
@@ -0,0 +1,44 @@
1
+ // The A2A AGENTS this app declares (OPE-2425) — config-as-code, changed by pull request only.
2
+ //
3
+ // MCP servers (./mcp-servers.ts) give the agent TOOLS AND DATA. A2A agents give it OTHER AGENTS: a
4
+ // focused enterprise agent — an Agentforce, ServiceNow, Workday or Copilot Studio agent — that takes a
5
+ // whole task in plain language and reports back. Each entry here is one such agent:
6
+ // • The agent reads the entry's published AGENT CARD and reaches ONLY the card URL's origin (scheme,
7
+ // host and port). A card that names an endpoint anywhere else is refused.
8
+ // • `delegate_to_agent` sends it a task. Every delegation takes an approval first: a member's click on
9
+ // a card that shows the agent, its host and the exact task text, or a pre-approval rule or the
10
+ // approval judge. The task goes out with the asker's identity attached
11
+ // (platform, workspace and their verified id) in the message metadata, with the person who approved
12
+ // it, or the rule that lifted it.
13
+ // • What the agent answers is UNTRUSTED content: it is fenced like any connector result, and it taints
14
+ // the turn that reads it.
15
+ //
16
+ // Only an INTERNAL room may delegate: a shared (Slack Connect), public or guest room is refused, and so
17
+ // is a request a guest or external account took part in. `domains` narrows it further.
18
+ //
19
+ // A person cannot add an agent: a name that is not in this list is refused. No secret goes in this
20
+ // file — an agent that needs a token names its credential-store REF (`npx oe secret set <ref>`).
21
+ // See docs/extending/a2a-agents.md.
22
+
23
+ import { defineA2aAgents } from "@open-everywhere/core/agent/a2a/agents.js";
24
+
25
+ export const A2A_AGENTS = defineA2aAgents([
26
+ // Example — an agent with a public card and no credential:
27
+ //
28
+ // {
29
+ // name: "it-helpdesk",
30
+ // cardUrl: "https://<the vendor's agent host>/.well-known/agent-card.json",
31
+ // description: "The IT helpdesk agent: opens, reads and updates IT tickets for a person.",
32
+ // },
33
+ //
34
+ // Example — an agent that takes a bearer token. Store the token with `npx oe secret set <ref>` and
35
+ // name the REF here, never the value:
36
+ //
37
+ // {
38
+ // name: "hr-agent",
39
+ // cardUrl: "https://agents.hr.example/.well-known/agent-card.json",
40
+ // description: "The HR agent: answers questions about leave, payroll dates and benefits.",
41
+ // auth: { scheme: "bearer", secretRef: "db:a2a:hr-agent-token" },
42
+ // domains: ["people-ops"], // optional: only conversations in these memory domains may use it
43
+ // },
44
+ ]);
@@ -30,6 +30,7 @@ import { emptyAgentsFile } from "@open-everywhere/core/agent/memory/agents-codem
30
30
  import { loadBaseBundles, loadBaseBindings } from "./bundles.js";
31
31
  import { loadBaseConnectorCredentials } from "./connector-credentials.js";
32
32
  import { loadBaseWebhooks } from "../webhooks/hooks.js";
33
+ import { MCP_SERVERS } from "./mcp-servers.js";
33
34
  import { loadBaseDomains, loadBaseSilos, loadDomains } from "../memory/config/domains.js";
34
35
  import { loadGroups } from "../identity/groups.js";
35
36
 
@@ -61,6 +62,8 @@ export function loadAccessRequests(): AccessRequestsConfig | null {
61
62
  // baseline a candidate overlay is validated against before any PR opens.
62
63
  hooksPath: process.env.OE_WEBHOOK_HOOKS_PATH || undefined,
63
64
  baseWebhooks: loadBaseWebhooks,
65
+ // The declared MCP servers, so `propose_mcp_server` validates a candidate entry against them (OPE-2420).
66
+ baseMcpServers: () => MCP_SERVERS,
64
67
  // The `create_agent` PR path (create_agent flow, increment 2) rides this SAME config repo, branch
65
68
  // and bot credential — only the machine-written file differs (the agents overlay). `baseAgents` is
66
69
  // the code-declared agents baseline (empty — agents live only in the overlay); `baseDomains` /
@@ -17,8 +17,10 @@ import {
17
17
  type Bundle,
18
18
  } from "@open-everywhere/core/agent/access/bundle.js";
19
19
  import { mcpServerBindings, mcpServerBundles } from "@open-everywhere/core/agent/connections/mcp-servers.js";
20
+ import { domainRuleBindings, domainRuleBundles } from "@open-everywhere/core/agent/access/domain-rules.js";
20
21
  import { loadGrantsOverlay } from "./grants.js";
21
22
  import { MCP_SERVERS } from "./mcp-servers.js";
23
+ import { DOMAIN_RULES } from "./domain-rules.js";
22
24
  import { PRE_APPROVAL_RULES } from "./pre-approval-rules.js";
23
25
  import { loadAgentsOverlay } from "../memory/config/agents-overlay.js";
24
26
  import { loadGroups } from "../identity/groups.js";
@@ -66,6 +68,10 @@ const bindings: Binding[] = [
66
68
  // every person's OWN DM (the built-in `oe:everyone` group, or the groups the entry names). Never a channel.
67
69
  bundles.push(...mcpServerBundles(MCP_SERVERS));
68
70
  bindings.push(...mcpServerBindings(MCP_SERVERS));
71
+ // The domain rules people asked for from approval cards and an admin merged (./domain-rules.ts, OPE-2456): each
72
+ // domain's rules in one bundle, bound at that domain's scope only.
73
+ bundles.push(...domainRuleBundles(DOMAIN_RULES));
74
+ bindings.push(...domainRuleBindings(DOMAIN_RULES));
69
75
 
70
76
  const overlay = loadGrantsOverlay();
71
77
  const agentsOverlay = loadAgentsOverlay();
@@ -0,0 +1,18 @@
1
+ // Domain rules — the acts one domain's people asked to allow without a card, merged by an admin.
2
+ //
3
+ // A person in a domain room can choose "Approve, and allow things like this" on an approval card. After they confirm
4
+ // the rule in plain words, the agent opens a pull request that adds one line here (or widens the line for the same
5
+ // tool and kind of action). Nothing applies until an admin merges it. The pull request names who asked, from which
6
+ // card, and quotes the act.
7
+ //
8
+ // Each line is ONE JSON entry: the domain, a stable id, the rule the approval judge reads, and the structured policy
9
+ // the rule was written from. A rule states policy — the tool, the kind of action, the kind of object — and never one
10
+ // object: config load refuses a rule that names a URL, an id or a numbered item. Delete a line to remove its rule.
11
+ // Keep one entry per line, with a trailing comma: the agent's pull requests edit this file line by line.
12
+ //
13
+ // The full reference is docs/how-it-works/pre-approval-rules.md in the Open Everywhere repository.
14
+
15
+ import { defineDomainRules } from "@open-everywhere/core/agent/access/domain-rules.js";
16
+
17
+ export const DOMAIN_RULES = defineDomainRules([
18
+ ]);
@@ -16,10 +16,23 @@
16
16
  // which needs no client id and no secret. Only a vendor WITHOUT dynamic registration takes a static
17
17
  // client, and then this file names the secret's credential-store REF, never its value.
18
18
  // See docs/extending/connectors.md#declare-an-mcp-server-in-your-app.
19
+ //
20
+ // "SEARCH SLACK AS YOU" (OPE-2421) is a built-in entry: `slackAsYou({ clientId })` mounts Slack's own MCP
21
+ // server on each person's own Slack sign-in, in their Slack DM only. The agent answers with the bot first and
22
+ // offers the person's connection only when the bot cannot see what was asked; every post as the person takes a
23
+ // card. It reuses the org's own Slack app: name its public Client ID here, and store its Client Secret with
24
+ // `npx oe secret set db:slack:oauth-client-secret`. The Slack app also needs the MCP toggle (its Agents section)
25
+ // and the scopes in the manifest `surface_manifest` returns once this entry is declared.
26
+ // See docs/deploy/surfaces/slack.md#search-slack-as-you.
19
27
 
20
28
  import { defineMcpServers } from "@open-everywhere/core/agent/connections/mcp-servers.js";
21
29
 
22
30
  export const MCP_SERVERS = defineMcpServers([
31
+ // Example — "search Slack as you" on your own Slack app. Import it first:
32
+ // import { slackAsYou } from "@open-everywhere/provider-slack/agent/slack-as-you.js";
33
+ //
34
+ // slackAsYou({ clientId: "<your Slack app's Client ID>" }),
35
+ //
23
36
  // Example — a vendor with dynamic client registration (the common case). Take the URL from the
24
37
  // vendor's own MCP documentation:
25
38
  //
@@ -27,6 +40,9 @@ export const MCP_SERVERS = defineMcpServers([
27
40
  // name: "fullstory",
28
41
  // url: "https://<the vendor's hosted MCP endpoint>",
29
42
  // description: "FullStory: search sessions, read session summaries and product analytics.",
43
+ // // Optional. The OAuth scopes the sign-in asks for; absent ⇒ no scope parameter is sent and the
44
+ // // vendor's default applies. Name the read scopes first: a person's token holds only these.
45
+ // signIn: { scopes: ["read:sessions"] },
30
46
  // },
31
47
  //
32
48
  // Example — a vendor WITHOUT dynamic registration. Create the client in the vendor's developer
@@ -43,6 +59,7 @@ export const MCP_SERVERS = defineMcpServers([
43
59
  // clientId: "public-client-id",
44
60
  // clientSecretRef: "db:acme:oauth-client-secret",
45
61
  // bodyEncoding: "form",
62
+ // scopes: ["accounts.read", "deals.read"], // optional: the scopes the authorize URL asks for
46
63
  // },
47
64
  // },
48
65
  ]);
@@ -22,6 +22,7 @@ import "./lib/register-adapters.js";
22
22
  import "@open-everywhere/core/agent/access/eve-patch-probe-boot.js";
23
23
  import { gatewayModelResolver } from "@open-everywhere/adapters/agent/model/gateway.js";
24
24
  import { registerGatewayDreamJudge, registerGatewayFailureFacetJudge } from "@open-everywhere/adapters/agent/model/dream-judge.js";
25
+ import { registerGatewayMeetingModels } from "@open-everywhere/adapters/agent/model/meeting-gateway.js";
25
26
  import { registerGatewayApprovalJudge } from "@open-everywhere/adapters/agent/model/approval-judge.js";
26
27
  import { registerGatewayImageDescriber } from "@open-everywhere/adapters/agent/model/gateway-vision.js";
27
28
  import { isModelUsable } from "@open-everywhere/core/agent/model/policy.js";
@@ -29,6 +30,7 @@ import {
29
30
  JUDGE_MODEL,
30
31
  PRIMARY_MODEL,
31
32
  PRIMARY_MODEL_CONTEXT_WINDOW_TOKENS,
33
+ TRANSCRIPTION_ROLE,
32
34
  UTILITY_MODEL,
33
35
  VISION_MODEL,
34
36
  } from "./model/models.js";
@@ -52,6 +54,11 @@ const COMPACTION_THRESHOLD_TOKENS = compactionThresholdTokens(PRIMARY_MODEL_CONT
52
54
  // The background judges: memory distillation, failure facets and the auto-approval judge.
53
55
  registerGatewayDreamJudge({ id: UTILITY_MODEL, gateway: GATEWAY_DEFAULTS });
54
56
  registerGatewayFailureFacetJudge({ id: UTILITY_MODEL, gateway: GATEWAY_DEFAULTS });
57
+
58
+ // The meeting bot's model calls (OPE-2459): the `gateway` transcription route, and the tool-less recap
59
+ // completion on the same `utility` route as the dream judge. Inert until agent/meetings/policy.ts enables
60
+ // meetings; without this line the `gateway` route is unregistered and `meeting_join` refuses before its card.
61
+ registerGatewayMeetingModels({ id: UTILITY_MODEL, gateway: GATEWAY_DEFAULTS }, TRANSCRIPTION_ROLE);
55
62
  registerGatewayApprovalJudge({ id: JUDGE_MODEL, gateway: GATEWAY_DEFAULTS });
56
63
 
57
64
  // The image describer. Fail at boot if the org model policy forbids the vision model, rather than
@@ -0,0 +1,40 @@
1
+ // THIS DEPLOYMENT'S EMAIL POLICY — the agent's own email address (OPE-2440). Config-as-code: a change here is a
2
+ // reviewed pull request. The schema and its validation are `@open-everywhere/core/agent/email/policy.js`; the
3
+ // operator guide is the "Email" surface page of the manual (`read_docs`).
4
+ //
5
+ // Each field is a decision about who may write to the agent:
6
+ // enabled — the master switch. Off refuses every inbound mail.
7
+ // transport — the registered transport that carries the mail ("agentmail" or "agentmail-eu" ship).
8
+ // orgDomains — the mail domains this org controls. Every inbox must sit on one of them (white-label: OE
9
+ // never hands out an address on a domain it runs), and an authenticated sender on one of
10
+ // them binds internal. Everyone else binds external.
11
+ // inboxes — `room`: mail wakes the domain whose `reach` lists `email/<domain>/<local part>`.
12
+ // `personal`: mail from a person declared in agent/identity/principals.ts (ids.email) wakes
13
+ // that person's own DM seat.
14
+ // allowedSenders — who may write to a room inbox: exact addresses or "*@domain". Add one by asking the agent
15
+ // (an approval card opens a pull request to agent/email/senders.ts) or by hand here.
16
+ // maxRecipients — the most addresses one reply may go to (a reply-all is limited to people allowed anyway).
17
+ //
18
+ // 🔴 NOTHING HERE IS A SECRET. The transport's API key and webhook secret live in the secret store
19
+ // (`oe secret set AGENTMAIL_API_KEY`, `oe secret set AGENTMAIL_WEBHOOK_SECRET`), never in this file.
20
+
21
+ import { DEFAULT_EMAIL_POLICY, registerEmailPolicy, type EmailPolicy } from "@open-everywhere/core/agent/email/policy.js";
22
+ import { EMAIL_SENDERS } from "./senders.js";
23
+
24
+ const emailPolicy: EmailPolicy = {
25
+ ...DEFAULT_EMAIL_POLICY,
26
+ enabled: false,
27
+ transport: "agentmail",
28
+ orgDomains: [
29
+ // Your own mail domain(s). Example, inert while `enabled` is false:
30
+ // "acme.com",
31
+ ],
32
+ inboxes: [
33
+ // Example inboxes, inert while `enabled` is false. Put the room inbox's surface key
34
+ // ("email/agent.acme.com/launch") in the launch domain's `reach`.
35
+ // { address: "launch@agent.acme.com", kind: "room", allowedSenders: ["*@acme.com"], why: "The launch team's shared inbox." },
36
+ // { address: "oe@agent.acme.com", kind: "personal", allowedSenders: [], why: "Each person's own chief of staff by email." },
37
+ ],
38
+ };
39
+
40
+ registerEmailPolicy(emailPolicy, EMAIL_SENDERS);
@@ -0,0 +1,14 @@
1
+ // Email senders — the allowed senders people asked to add to a room inbox, merged by an admin (OPE-2440).
2
+ //
3
+ // A person asks the agent to let someone email a room inbox. The agent posts an approval card; a tap opens a pull
4
+ // request that adds one line here. Nothing applies until an admin merges it. The pull request names who asked and
5
+ // who approved.
6
+ //
7
+ // Each line is ONE JSON entry: the room inbox, the sender (an exact address or "*@domain"), why, and the
8
+ // provenance. Delete a line to remove the sender. Keep one entry per line, with a trailing comma: the agent's pull
9
+ // requests edit this file line by line. The inbox must be a room inbox declared in ./policy.ts, or the boot fails.
10
+
11
+ import { defineEmailSenders } from "@open-everywhere/core/agent/email/policy.js";
12
+
13
+ export const EMAIL_SENDERS = defineEmailSenders([
14
+ ]);
@@ -72,6 +72,10 @@ set up, they just ask. Pick the move that fits the shape of the ask:
72
72
  offer to set it up with `create_routine` so it just happens without anyone asking.
73
73
  - **Make it a skill.** A multi-step way-of-doing-things recurs: capture it with
74
74
  `create_skill` so next time it's one named move.
75
+ - **Set up a new connector.** "Set up FullStory" or "connect <vendor>" asks for a NEW connector, and that is
76
+ a pull request, not a grant. A worker researches the vendor's hosted MCP server (`web_search`,
77
+ `web_fetch`) and proposes it with `propose_mcp_server`: one entry in `agent/access/mcp-servers.ts`
78
+ on the deployment's config repo, for an admin to merge. It is not set up until the merge.
75
79
 
76
80
  Some of these verbs live on workers rather than on a conversational seat. If a move above
77
81
  names a tool you don't hold, that is not a "can't" — delegate it (`agent`) with a brief
@@ -40,6 +40,8 @@ import { loadSpendCheckpoints } from "../spend/checkpoints.js";
40
40
  import { loadModelPolicy } from "../model/policy.js";
41
41
  import { loadPrincipals } from "../identity/principals.js";
42
42
  import { loadGroups } from "../identity/groups.js";
43
+ import { registerA2aAgents } from "@open-everywhere/core/agent/a2a/agents.js";
44
+ import { A2A_AGENTS } from "../access/a2a-agents.js";
43
45
  // Side effect: register the operator's per-surface trust registry (identity/trust.ts) at
44
46
  // boot, before any binding resolves. Self-registering (calls registerSurfaces on
45
47
  // import); nothing to thread through the AppConfig object.
@@ -60,6 +62,18 @@ import "../identity/surfaces.js";
60
62
  // `agent/sandbox/sandbox.ts` through this module's import and nothing else. Drop this import and the
61
63
  // declaration is empty at bake time, so the capability silently refuses to arm.
62
64
  import "../access/browser-hosts.js";
65
+ // Side effect: register this deployment's MEETING POLICY (agent/meetings/policy.ts, OPE-2459) — which
66
+ // meeting hosts the bot may join, the retention windows, the bot's box and the transcription route. Off
67
+ // unless the file enables it; a bad policy fails the boot rather than arming a bot nobody reviewed.
68
+ import "../meetings/policy.js";
69
+ // Side effect: register this deployment's EMAIL POLICY (agent/email/policy.ts, OPE-2440) — the agent's own
70
+ // inboxes, who may write to each, and how wide a reply may go. Off unless the file enables it; a bad policy
71
+ // (an inbox off the org's domains, a bare wildcard sender, a sender line naming no room inbox) fails the boot.
72
+ import "../email/policy.js";
73
+ // Side effect: register this deployment's PERSONAL BROWSER POLICY (agent/personal-browser/policy.ts, OPE-2441) —
74
+ // whether a person may have their own browser that keeps their logins, its box, and how long its sessions,
75
+ // logins, take-overs and act approvals last. Off unless the file enables it; a bad policy fails the boot.
76
+ import "../personal-browser/policy.js";
63
77
  // Side effect: register the operator's per-AUTHOR trust register (identity/author-trust.ts) — the
64
78
  // AUTHORITY plane, the sibling of the surfaces AUDIENCE plane above. Self-registering, and it must
65
79
  // come AFTER the groups module loads (it expands `{ group: … }` members through loadGroups at
@@ -96,6 +110,12 @@ registerSettings(SETTINGS_OVERRIDES);
96
110
  // prices during boot sees a half-wired deployment.
97
111
  registerModelPricing(loadModelPricing());
98
112
 
113
+ // The A2A AGENTS this app declares (OPE-2425, agent/access/a2a-agents.ts) — the only agents
114
+ // `delegate_to_agent` can reach. Registered on core's own slot rather than carried on AppConfig: the
115
+ // list is validated at import (defineA2aAgents throws on a bad entry), and an unregistered list is
116
+ // empty, so a core-only process delegates to nobody.
117
+ registerA2aAgents(A2A_AGENTS);
118
+
99
119
  registerConfig({
100
120
  loadBundles,
101
121
  loadBindings,
@@ -0,0 +1,44 @@
1
+ // THIS DEPLOYMENT'S MEETING POLICY — OE's own meeting bot (OPE-2459). Config-as-code: a change here is a
2
+ // reviewed pull request. The schema and its validation are `@open-everywhere/core/agent/meetings/policy.js`;
3
+ // the operator guide is the "Meetings" page of the manual (`read_docs`).
4
+ //
5
+ // Each field is a decision about other people's meetings:
6
+ // enabled — the master switch. Off refuses every "join this meeting" ask.
7
+ // hosts — the meeting hosts the bot may join. A Teams rule lists the organiser tenants OE may
8
+ // join; the tenant id is read from the link and is not verified with Microsoft, so it
9
+ // refuses an outside link pasted as it is, not an edited one. A Meet link names no
10
+ // organiser, so a Meet rule must accept that in writing (`owner: "unverifiable"`).
11
+ // retention — how long OE keeps audio (0 = never stored) and the transcript.
12
+ // box — the bot image and its size. It runs on this deployment's own sandbox backend.
13
+ // transcription — `gateway` (audio through this deployment's AI Gateway) or `box` (text only).
14
+ // native — recaps from the platform's OWN transcript (Teams, Meet, Zoom), no bot (OPE-2471). Each
15
+ // platform block names the ORGANISATION's credential: identifiers here, the secret's value
16
+ // in the db credential store (`oe secret set <name>`). Off until OPE-2461 and OPE-2464 land.
17
+ //
18
+ // 🔴 NOTHING HERE IS A SECRET. A tenant id, a client id and a secret's NAME are identifiers, not credentials.
19
+ //
20
+ // To turn meetings on: build and publish the bot image (the manual's Meetings page says how), set
21
+ // `box.image`, add your host rules, and set `enabled: true`. Until OPE-2461 (per-row expiry for the shared
22
+ // store) and OPE-2464 (untrusted provenance for the recap post) land, `enabled: true` is REFUSED: the boot
23
+ // fails and names both tickets.
24
+
25
+ import { DEFAULT_MEETING_POLICY, registerMeetingPolicy, type MeetingPolicy } from "@open-everywhere/core/agent/meetings/policy.js";
26
+
27
+ const meetingPolicy: MeetingPolicy = {
28
+ ...DEFAULT_MEETING_POLICY,
29
+ enabled: false,
30
+ hosts: [
31
+ // Example rules, inert while `enabled` is false. Replace the tenant id with your own.
32
+ // { platform: "meet", host: "meet.google.com", owner: "unverifiable", why: "Our team meets on Google Meet." },
33
+ // { platform: "teams", host: "teams.microsoft.com", tenants: ["00000000-0000-0000-0000-000000000000"], why: "Our own Teams tenant only." },
34
+ ],
35
+ native: {
36
+ ...DEFAULT_MEETING_POLICY.native,
37
+ // Example blocks, inert while `native.enabled` is false. See the manual's Meetings page for what each admin grants.
38
+ // teams: { tenants: ["00000000-0000-0000-0000-000000000000"], clientId: "00000000-0000-0000-0000-000000000000", clientSecretRef: "TEAMS_TRANSCRIPT_SECRET", why: "Our own Teams meetings." },
39
+ // meet: { domains: ["example.org"], serviceAccountKeyRef: "MEET_TRANSCRIPT_KEY", why: "Our own Workspace meetings." },
40
+ // zoom: { accountId: "<Zoom account id>", clientId: "<S2S app client id>", clientSecretRef: "ZOOM_TRANSCRIPT_SECRET", why: "Our own Zoom account." },
41
+ },
42
+ };
43
+
44
+ registerMeetingPolicy(meetingPolicy);
@@ -70,6 +70,12 @@
70
70
  // Every member outside Slack must be declared internal in agent/identity/surfaces.ts, or the boot
71
71
  // refuses the window (memory/domain.ts assertWindowSourcesVouched).
72
72
  //
73
+ // 🔴 THE DEFAULT IS UNIFIED. A declared domain that omits `mode` shares ONE window across its
74
+ // rooms (OPE-2412). It falls back to solo, and the boot logs why, when its reach holds a tenant or
75
+ // glob entry or a room outside Slack is not declared internal. A one-room domain stays solo. To
76
+ // keep each room in its own seat, write `mode: "solo"` on the domain. `org`, `public` and `dm`
77
+ // below are always solo.
78
+ //
73
79
  // --- CRITERIA PSEUDO-DOMAINS (optional) — selected by trust/mode, not a channel list.
74
80
  // Each is a singleton (at most one per match) with the full domain toolkit.
75
81
  //
@@ -8,7 +8,7 @@ import {
8
8
  } from "@open-everywhere/core/agent/model/default-model.js";
9
9
  import { getSettings } from "@open-everywhere/core/agent/settings/settings.js";
10
10
  import { SETTINGS_OVERRIDES } from "../settings/settings.js";
11
- import { resolveStack, type ModelRole, type ModelRoleSpec, type ModelStack } from "./stacks.js";
11
+ import { resolveStack, resolveTranscription, type ModelRole, type ModelRoleSpec, type ModelStack } from "./stacks.js";
12
12
 
13
13
  export type { ModelRoleSpec, ModelStack } from "./stacks.js";
14
14
 
@@ -31,6 +31,8 @@ export const MODELS = {
31
31
 
32
32
  /** The alias of the stack this process resolved — for `/oe status`. */
33
33
  export const MODEL_STACK = STACK_ALIAS;
34
+ /** The meeting bot's speech-to-text role: a primary and a fallback route (stacks.ts TRANSCRIPTION). */
35
+ export const TRANSCRIPTION_ROLE = resolveTranscription(STACK_ALIAS);
34
36
 
35
37
  export const PRIMARY_MODEL = MODELS.primary.id;
36
38
  export const UTILITY_MODEL = MODELS.utility.id;
@@ -12,6 +12,7 @@
12
12
  // agent/spend/model-pricing.ts, or a USD spend cap cannot see its cost.
13
13
 
14
14
  import type { ModelTier } from "@open-everywhere/core/agent/memory/domain.js";
15
+ import type { TranscriptionRoleSpec } from "@open-everywhere/adapters/agent/model/transcription-routes.js";
15
16
 
16
17
  export type ModelRole = "primary" | "utility" | "vision" | "supervisor" | "judge";
17
18
 
@@ -35,7 +36,7 @@ export const STACKS = {
35
36
  "american-closed": {
36
37
  primary: { id: "openai/gpt-6-luna", tier: "high", contextWindowTokens: 1_050_000 },
37
38
  utility: { id: "openai/gpt-6-luna", tier: "low", contextWindowTokens: 1_050_000 },
38
- vision: { id: "google/gemini-3.7-flash", tier: "low", contextWindowTokens: 1_000_000 },
39
+ vision: { id: "google/gemini-3.8-flash", tier: "low", contextWindowTokens: 1_000_000 },
39
40
  supervisor: { id: "openai/gpt-6-luna", tier: "low", contextWindowTokens: 1_050_000 },
40
41
  judge: { id: "openai/gpt-6-luna", tier: "low", contextWindowTokens: 1_050_000 },
41
42
  },
@@ -73,3 +74,33 @@ export function resolveStack(alias: string, overrides: StackOverrides = {}): Mod
73
74
  }
74
75
  return out;
75
76
  }
77
+
78
+ // Speech to text for the meeting bot: a primary and a fallback route per stack, on two different providers, so
79
+ // one provider outage falls through to the other. A route names its transport too: the AI Gateway, DeepInfra
80
+ // (key: `oe secret set DEEPINFRA_API_KEY`), or your own OpenAI-compatible speech server. These defaults are
81
+ // pending a bake-off (`npm run bakeoff:stt` in the Open Everywhere repository).
82
+ export const TRANSCRIPTION = {
83
+ "chinese-open-weights": {
84
+ primary: { via: "deepinfra", id: "Qwen/Qwen3-ASR-1.7B" },
85
+ fallback: { via: "self-hosted", id: "Qwen/Qwen3-ASR-1.7B", baseUrl: "http://qwen3-asr:8000/v1" },
86
+ },
87
+ "american-closed": {
88
+ primary: { via: "gateway", id: "microsoft/mai-transcribe-2" },
89
+ fallback: { via: "gateway", id: "spacexai/grok-stt" },
90
+ },
91
+ } as const satisfies Record<StackAlias, TranscriptionRoleSpec>;
92
+
93
+ /** Fully local: your own speech server only. For a Docker deployment with no cloud model. */
94
+ export const LOCAL_TRANSCRIPTION = {
95
+ primary: { via: "self-hosted", id: "Qwen/Qwen3-ASR-1.7B", baseUrl: "http://qwen3-asr:8000/v1" },
96
+ } as const satisfies TranscriptionRoleSpec;
97
+
98
+ /** `stack` follows `models.stack`; `local` uses LOCAL_TRANSCRIPTION. */
99
+ export const TRANSCRIPTION_PLACEMENT: "stack" | "local" = "stack";
100
+
101
+ /** The transcription role for `alias`. Throws on an unknown alias. */
102
+ export function resolveTranscription(alias: string, placement: "stack" | "local" = TRANSCRIPTION_PLACEMENT): TranscriptionRoleSpec {
103
+ if (placement === "local") return LOCAL_TRANSCRIPTION;
104
+ if (!isStackAlias(alias)) throw new Error(`open-everywhere: unknown model stack "${alias}" — no transcription role`);
105
+ return TRANSCRIPTION[alias];
106
+ }
@@ -0,0 +1,33 @@
1
+ // THIS DEPLOYMENT'S PERSONAL BROWSER POLICY — each person's own browser, which keeps their logins (OPE-2441).
2
+ // Config-as-code: a change here is a reviewed pull request. The schema and its validation are
3
+ // `@open-everywhere/core/agent/personal-browser/policy.js`; the operator guide is the "Personal browser" page
4
+ // of the manual (`read_docs`).
5
+ //
6
+ // Each field is a decision about a person's signed-in accounts:
7
+ // enabled — the master switch. Off refuses every use.
8
+ // box — the browser image and its size. It runs on this deployment's own sandbox backend: a
9
+ // persistent Vercel Sandbox per person, or a docker container on a named volume.
10
+ // `dockerNetwork` (docker only) is the network the app and the box share.
11
+ // sessionMinutes — the hard cap on one live session of the box.
12
+ // idleMinutes — the box stops after this long with nobody using or watching it. Logins stay.
13
+ // keepLoginsDays — how long the profile (the logins) survives with no use.
14
+ // takeoverMinutes — the longest a person holds the controls before they return to the agent.
15
+ // actGrantMinutes — how long one "let the agent click and type on this site" approval lasts.
16
+ //
17
+ // WHERE THE BROWSER MAY GO is not declared here: it reaches exactly the hosts the person's DM's egress
18
+ // declaration admits (agent/access — bundles and network access), the same firewall every other call uses.
19
+ //
20
+ // 🔴 NOTHING HERE IS A SECRET.
21
+ //
22
+ // To turn it on: build and publish the browser image (the manual's Personal browser page says how), set
23
+ // `box.image`, and set `enabled: true`. The watch page also needs the page signing key
24
+ // (OE_INTAKE_PAGE_SIGNING_KEY) and a public URL, which the credential intake page already uses.
25
+
26
+ import { DEFAULT_PERSONAL_BROWSER_POLICY, registerPersonalBrowserPolicy, type PersonalBrowserPolicy } from "@open-everywhere/core/agent/personal-browser/policy.js";
27
+
28
+ const personalBrowserPolicy: PersonalBrowserPolicy = {
29
+ ...DEFAULT_PERSONAL_BROWSER_POLICY,
30
+ enabled: false,
31
+ };
32
+
33
+ registerPersonalBrowserPolicy(personalBrowserPolicy);
@@ -15,7 +15,7 @@ const pricing: ModelPricingTable = defineModelPricing({
15
15
  // chinese-open-weights: vision.
16
16
  "alibaba/qwen3-vl-instruct": { inPerMtok: 0.2, outPerMtok: 0.88 },
17
17
  // american-closed: vision.
18
- "google/gemini-3.7-flash": { inPerMtok: 0.75, outPerMtok: 3.75 },
18
+ "google/gemini-3.8-flash": { inPerMtok: 0.75, outPerMtok: 3.75 },
19
19
  });
20
20
 
21
21
  assertModelPricingValid(pricing);
@@ -2,8 +2,8 @@
2
2
  // inbound-webhook surface (docs/capabilities/proactivity-and-routines.md in the Open Everywhere repository "Inbound
3
3
  // webhook triggers"). Declare a hook: which signature scheme its sender uses (`source`),
4
4
  // where its shared secret lives (`secretRef` + the `broker` that ref resolves through),
5
- // which `slack/TEAM/CHANNEL` surface it fires into, and what the agent should do when it
6
- // fires. Point the provider at `POST /webhooks/<id>` (channels/webhooks.ts).
5
+ // where it fires (a room on Slack, Discord or Teams, or a person's DM), and what the agent
6
+ // should do when it fires. Point the provider at `POST /webhooks/<id>` (channels/webhooks.ts).
7
7
  //
8
8
  // SIGNATURE SCHEME: `source` names the registered verifier. `"oe-standard-webhook"` is
9
9
  // OUR own contract — the public standard-webhooks spec (`webhook-id` + `webhook-timestamp`
@@ -50,8 +50,8 @@
50
50
  // the seam a DB/dashboard would swap behind, same as memory/config/domains.ts and access/
51
51
  // bundles.ts. After adding a hook: `npm run typecheck` plus a boot (assertWebhooksValid runs
52
52
  // at import time and throws on a malformed entry — id collisions, a missing source, a
53
- // blank/empty secretRef, a missing broker, a target that isn't a well-formed
54
- // slack/TEAM/CHANNEL triple).
53
+ // blank/empty secretRef, a missing broker, a target that is neither a room
54
+ // "<platform>/<workspace>/<surface>" nor a person "dm:<principal key>").
55
55
 
56
56
  import { defineWebhook, assertWebhooksValid, type WebhookHook } from "@open-everywhere/core/agent/webhooks/config.js";
57
57
  import { loadHooksOverlay } from "./hooks-overlay.js";
@@ -64,7 +64,7 @@ const hooks: WebhookHook[] = [
64
64
  // source: "oe-standard-webhook", // OUR contract — a sender you control signs it
65
65
  // secretRef: "DEPLOY_STATUS_WEBHOOK_SECRET", // shared secret REF (env var NAME here, not the value)
66
66
  // broker: "env", // the home that ref resolves through ("env" | "db" | ...)
67
- // target: "slack/<team-id>/<channel-id>", // fully-qualified slack/TEAM/CHANNEL surface
67
+ // target: "slack/<team-id>/<channel-id>", // a room on any surface, or "dm:<principal key>" for a person
68
68
  // readableBy: [{ level: "domain", domain: "engineering" }], // WHO may read its events — REQUIRED, no default
69
69
  // posture: "projection", // HOW MUCH of the payload reaches the model
70
70
  // project: ["status", "branch"], // …under "projection": exactly these normalizer field keys
@@ -37,7 +37,7 @@ npm view @open-everywhere/core version
37
37
  **Read the installed one, not the pin.** `oe upgrade` writes the pins before it installs, so an
38
38
  upgrade that stopped part way leaves a manifest that names a version the app is not running.
39
39
 
40
- The nine packages publish in lockstep, so one version answers for all of them. Agree the target
40
+ The ten packages publish in lockstep, so one version answers for all of them. Agree the target
41
41
  with the user before you go on. Taking the latest is the normal choice.
42
42
 
43
43
  ## 2. Read the notes between the two versions
@@ -33,6 +33,7 @@ node_modules/
33
33
  /agent/skills/
34
34
  /agent/connections/
35
35
  /agent/lib/providers.generated.ts
36
+ /agent/lib/app-code-fingerprint.generated.ts
36
37
 
37
38
  # Misc
38
39
  *.log
@@ -40,3 +41,6 @@ node_modules/
40
41
  *.tsbuildinfo
41
42
  *.pem
42
43
  *.key
44
+
45
+ # A built Teams app package (`surface_app_package`): rebuilt from config, never committed.
46
+ teams-app/*.zip
@@ -26,6 +26,7 @@
26
26
  "@open-everywhere/adapters": "{{OE_VERSION}}",
27
27
  "@open-everywhere/core": "{{OE_VERSION}}",
28
28
  "@open-everywhere/provider-discord": "{{OE_VERSION}}",
29
+ "@open-everywhere/provider-email": "{{OE_VERSION}}",
29
30
  "@open-everywhere/provider-github": "{{OE_VERSION}}",
30
31
  "@open-everywhere/provider-linear": "{{OE_VERSION}}",
31
32
  "@open-everywhere/provider-notion": "{{OE_VERSION}}",
@@ -29,8 +29,8 @@ export default defineRoster({
29
29
  core: true,
30
30
  adapters: true,
31
31
 
32
- // One provider bundle per surface. Add a line to add a surface (`discord`, `teams` and `linear`
33
- // are installed too), then `npx oe build --accept`.
32
+ // One provider bundle per surface. Add a line to add a surface (`discord`, `teams`, `linear` and
33
+ // `email` are installed too), then `npx oe build --accept`.
34
34
  providers: {
35
35
  slack: true,
36
36
  notion: true,
@@ -22,8 +22,10 @@
22
22
  "channel/control-mcp": "mounted",
23
23
  "channel/control-read": "mounted",
24
24
  "channel/credential-intake-page": "mounted",
25
+ "channel/meeting-callback": "mounted",
25
26
  "channel/oe-oauth-callback": "mounted",
26
27
  "channel/oe-status": "mounted",
28
+ "channel/personal-browser": "mounted",
27
29
  "channel/policy-pages": "mounted",
28
30
  "channel/test-wake": "mounted",
29
31
  "channel/transcript-relay": "mounted",
@@ -58,18 +60,24 @@
58
60
  "hook/spend": "mounted",
59
61
  "hook/stall-guard": "mounted",
60
62
  "hook/telemetry": "mounted",
63
+ "hook/tool-usage": "mounted",
61
64
  "hook/turn-anchor": "mounted",
62
65
  "hook/wake-accounting-turn": "mounted",
63
66
  "hook/wake-turn-start": "mounted",
64
67
  "hook/window-routing": "mounted",
68
+ "schedule/audit-export": "mounted",
65
69
  "schedule/directory-sync": "mounted",
66
70
  "schedule/dispatch": "mounted",
67
71
  "schedule/dream-mode3": "mounted",
72
+ "schedule/meeting-sweep": "mounted",
73
+ "schedule/personal-browser-sweep": "mounted",
68
74
  "schedule/tidy": "mounted",
69
75
  "skill/scoped": "mounted",
70
76
  "tool/add_label": "mounted",
71
77
  "tool/add_routine_rule": "mounted",
72
78
  "tool/agent_version": "mounted",
79
+ "tool/answer_domain_question": "mounted",
80
+ "tool/ask_domain": "mounted",
73
81
  "tool/ask_question": "mounted",
74
82
  "tool/ask_room": "mounted",
75
83
  "tool/assign": "mounted",
@@ -78,6 +86,7 @@
78
86
  "tool/cancel_routine": "mounted",
79
87
  "tool/cancel_work": "mounted",
80
88
  "tool/capture_events": "mounted",
89
+ "tool/check_agent_task": "mounted",
81
90
  "tool/check_pr_status": "mounted",
82
91
  "tool/checkout_repo": "mounted",
83
92
  "tool/clear_connector_override": "mounted",
@@ -91,6 +100,7 @@
91
100
  "tool/create_skill": "mounted",
92
101
  "tool/create_ticket": "mounted",
93
102
  "tool/declare_webhook": "mounted",
103
+ "tool/delegate_to_agent": "mounted",
94
104
  "tool/delete_skill": "mounted",
95
105
  "tool/describe_image": "mounted",
96
106
  "tool/dm_person": "mounted",
@@ -107,15 +117,22 @@
107
117
  "tool/list_routines": "mounted",
108
118
  "tool/list_skills": "mounted",
109
119
  "tool/list_webhooks": "mounted",
120
+ "tool/meeting_join": "mounted",
121
+ "tool/meeting_leave": "mounted",
122
+ "tool/meeting_recap": "mounted",
110
123
  "tool/memory_admin_list": "mounted",
111
124
  "tool/memory_admin_read": "mounted",
112
125
  "tool/message_thread_seat": "mounted",
126
+ "tool/my_browser": "mounted",
127
+ "tool/my_browser_handoff": "mounted",
113
128
  "tool/no_reply": "mounted",
114
129
  "tool/open_pull_request": "mounted",
115
130
  "tool/permission_fit_report": "mounted",
116
131
  "tool/post_file": "mounted",
117
132
  "tool/post_message": "mounted",
118
133
  "tool/post_pr_walkthrough": "mounted",
134
+ "tool/propose_email_sender": "mounted",
135
+ "tool/propose_mcp_server": "mounted",
119
136
  "tool/purge_memory": "mounted",
120
137
  "tool/query_analytics": "mounted",
121
138
  "tool/react": "mounted",
@@ -156,6 +173,7 @@
156
173
  "tool/unlink_identity": "mounted",
157
174
  "tool/update_status": "mounted",
158
175
  "tool/usage_report": "mounted",
176
+ "tool/waiting_on_me": "mounted",
159
177
  "tool/walk_pr": "mounted",
160
178
  "tool/work": "mounted"
161
179
  }
@@ -3,9 +3,11 @@ agent/channels/control-mcp.ts mounted @open-everywhere/core
3
3
  agent/channels/control-read.ts mounted @open-everywhere/core
4
4
  agent/channels/credential-intake-page.ts mounted @open-everywhere/core
5
5
  agent/channels/github.ts mounted @open-everywhere/provider-github
6
+ agent/channels/meeting-callback.ts mounted @open-everywhere/core
6
7
  agent/channels/notion.ts mounted @open-everywhere/provider-notion
7
8
  agent/channels/oe-oauth-callback.ts mounted @open-everywhere/core
8
9
  agent/channels/oe-status.ts mounted @open-everywhere/core
10
+ agent/channels/personal-browser.ts mounted @open-everywhere/core
9
11
  agent/channels/policy-pages.ts mounted @open-everywhere/core
10
12
  agent/channels/slack-commands.ts mounted @open-everywhere/provider-slack
11
13
  agent/channels/slack-setup-page.ts mounted @open-everywhere/provider-slack
@@ -44,20 +46,26 @@ agent/hooks/spawn-taint.ts mounted @open-everywhere/core
44
46
  agent/hooks/spend.ts mounted @open-everywhere/core
45
47
  agent/hooks/stall-guard.ts mounted @open-everywhere/core
46
48
  agent/hooks/telemetry.ts mounted @open-everywhere/core
49
+ agent/hooks/tool-usage.ts mounted @open-everywhere/core
47
50
  agent/hooks/turn-anchor.ts mounted @open-everywhere/core
48
51
  agent/hooks/wake-accounting-turn.ts mounted @open-everywhere/core
49
52
  agent/hooks/wake-turn-start.ts mounted @open-everywhere/core
50
53
  agent/hooks/window-routing.ts mounted @open-everywhere/core
51
54
  agent/lib/providers.generated.ts providers slack,notion,github
55
+ agent/schedules/audit-export.ts mounted @open-everywhere/core
52
56
  agent/schedules/directory-sync.ts mounted @open-everywhere/core
53
57
  agent/schedules/dispatch.ts mounted @open-everywhere/core
54
58
  agent/schedules/dream-mode3.ts mounted @open-everywhere/core
59
+ agent/schedules/meeting-sweep.ts mounted @open-everywhere/core
55
60
  agent/schedules/notion-ingress.ts mounted @open-everywhere/provider-notion
61
+ agent/schedules/personal-browser-sweep.ts mounted @open-everywhere/core
56
62
  agent/schedules/tidy.ts mounted @open-everywhere/core
57
63
  agent/skills/scoped.ts mounted @open-everywhere/core
58
64
  agent/tools/add_label.ts mounted @open-everywhere/core
59
65
  agent/tools/add_routine_rule.ts mounted @open-everywhere/core
60
66
  agent/tools/agent_version.ts mounted @open-everywhere/core
67
+ agent/tools/answer_domain_question.ts mounted @open-everywhere/core
68
+ agent/tools/ask_domain.ts mounted @open-everywhere/core
61
69
  agent/tools/ask_question.ts mounted @open-everywhere/core
62
70
  agent/tools/ask_room.ts mounted @open-everywhere/core
63
71
  agent/tools/assign.ts mounted @open-everywhere/core
@@ -67,6 +75,7 @@ agent/tools/browser_tools.ts mounted @open-everywhere/core
67
75
  agent/tools/cancel_routine.ts mounted @open-everywhere/core
68
76
  agent/tools/cancel_work.ts mounted @open-everywhere/core
69
77
  agent/tools/capture_events.ts mounted @open-everywhere/core
78
+ agent/tools/check_agent_task.ts mounted @open-everywhere/core
70
79
  agent/tools/check_pr_status.ts mounted @open-everywhere/core
71
80
  agent/tools/checkout_repo.ts mounted @open-everywhere/core
72
81
  agent/tools/clear_connector_override.ts mounted @open-everywhere/core
@@ -80,6 +89,7 @@ agent/tools/create_routine.ts mounted @open-everywhere/core
80
89
  agent/tools/create_skill.ts mounted @open-everywhere/core
81
90
  agent/tools/create_ticket.ts mounted @open-everywhere/core
82
91
  agent/tools/declare_webhook.ts mounted @open-everywhere/core
92
+ agent/tools/delegate_to_agent.ts mounted @open-everywhere/core
83
93
  agent/tools/delete_skill.ts mounted @open-everywhere/core
84
94
  agent/tools/describe_image.ts mounted @open-everywhere/core
85
95
  agent/tools/dm_person.ts mounted @open-everywhere/core
@@ -97,9 +107,14 @@ agent/tools/list_repos.ts mounted @open-everywhere/core
97
107
  agent/tools/list_routines.ts mounted @open-everywhere/core
98
108
  agent/tools/list_skills.ts mounted @open-everywhere/core
99
109
  agent/tools/list_webhooks.ts mounted @open-everywhere/core
110
+ agent/tools/meeting_join.ts mounted @open-everywhere/core
111
+ agent/tools/meeting_leave.ts mounted @open-everywhere/core
112
+ agent/tools/meeting_recap.ts mounted @open-everywhere/core
100
113
  agent/tools/memory_admin_list.ts mounted @open-everywhere/core
101
114
  agent/tools/memory_admin_read.ts mounted @open-everywhere/core
102
115
  agent/tools/message_thread_seat.ts mounted @open-everywhere/core
116
+ agent/tools/my_browser_handoff.ts mounted @open-everywhere/core
117
+ agent/tools/my_browser.ts mounted @open-everywhere/core
103
118
  agent/tools/no_reply.ts mounted @open-everywhere/core
104
119
  agent/tools/notion_search.ts mounted @open-everywhere/provider-notion
105
120
  agent/tools/open_pull_request.ts mounted @open-everywhere/core
@@ -107,6 +122,8 @@ agent/tools/permission_fit_report.ts mounted @open-everywhere/core
107
122
  agent/tools/post_file.ts mounted @open-everywhere/core
108
123
  agent/tools/post_message.ts mounted @open-everywhere/core
109
124
  agent/tools/post_pr_walkthrough.ts mounted @open-everywhere/core
125
+ agent/tools/propose_email_sender.ts mounted @open-everywhere/core
126
+ agent/tools/propose_mcp_server.ts mounted @open-everywhere/core
110
127
  agent/tools/purge_memory.ts mounted @open-everywhere/core
111
128
  agent/tools/query_analytics.ts mounted @open-everywhere/core
112
129
  agent/tools/react.ts mounted @open-everywhere/core
@@ -150,6 +167,7 @@ agent/tools/subscribe_pr.ts mounted @open-everywhere/core
150
167
  agent/tools/unlink_identity.ts mounted @open-everywhere/core
151
168
  agent/tools/update_status.ts mounted @open-everywhere/core
152
169
  agent/tools/usage_report.ts mounted @open-everywhere/core
170
+ agent/tools/waiting_on_me.ts mounted @open-everywhere/core
153
171
  agent/tools/walk_pr.ts mounted @open-everywhere/core
154
172
  agent/tools/web_fetch.ts framework-disabled
155
173
  agent/tools/work.ts mounted @open-everywhere/core
@@ -47,5 +47,5 @@ registerSurfaceTrustPreflight();
47
47
  const server = buildOpsServer();
48
48
  await server.connect(new StdioServerTransport());
49
49
  process.stderr.write(
50
- "open-everywhere-ops MCP server ready on stdio (preflight · surface_manifest · scaffold_env · scaffold_credential_key · secret_set/list/rm)\n",
50
+ "open-everywhere-ops MCP server ready on stdio (preflight · surface_manifest · surface_app_package · scaffold_env · scaffold_credential_key · secret_set/list/rm)\n",
51
51
  );