@open-everywhere/create 0.7.0 → 0.8.1
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 +5 -3
- package/package.json +3 -3
- package/src/steps/build-deploy.ts +2 -2
- package/template/.dockerignore +35 -0
- package/template/AGENTS.md +7 -0
- package/template/Dockerfile +60 -0
- package/template/README.md +13 -0
- package/template/agent/agent.ts +25 -8
- package/template/agent/instructions.md +6 -0
- package/template/agent/lib/register-config.ts +5 -1
- package/template/agent/memory/config/domains/org.ts +2 -2
- package/template/agent/model/models.ts +17 -0
- package/template/agent/model/stacks.ts +23 -6
- package/template/agent/settings/settings.ts +21 -2
- package/template/agent/spend/model-pricing.ts +3 -1
- package/template/claude/skills/quickstart-open-everywhere/SKILL.md +96 -9
- package/template/claude/skills/upgrade-open-everywhere/SKILL.md +16 -3
- package/template/deploy/egress-proxy/Dockerfile +30 -0
- package/template/deploy/egress-proxy/control/acl.ts +228 -0
- package/template/deploy/egress-proxy/control/handlers.ts +174 -0
- package/template/deploy/egress-proxy/control/helper.cjs +108 -0
- package/template/deploy/egress-proxy/control/server.ts +122 -0
- package/template/deploy/egress-proxy/entrypoint.sh +62 -0
- package/template/deploy/egress-proxy/gen-ca.sh +42 -0
- package/template/deploy/egress-proxy/install-ca.sh +39 -0
- package/template/deploy/egress-proxy/squid.conf +93 -0
- package/template/deploy/egress-proxy/tier-b.conf +42 -0
- package/template/docker-compose.yml +135 -0
- package/template/gitignore +4 -0
- package/template/package.json +4 -3
- package/template/roster.lock.json +9 -1
- package/template/scripts/goldens/roster-plan.txt +8 -0
- package/template/scripts/model-config-check.ts +81 -0
- package/template/scripts/ops-mcp.ts +1 -1
package/README.md
CHANGED
|
@@ -38,9 +38,11 @@ coding agent and ask it to set up Open Everywhere. The agent reads the app's `AG
|
|
|
38
38
|
route first: **Vercel** (the `vercel` CLI plus Marketplace Neon) or **Docker** (a self-hosted
|
|
39
39
|
container that satisfies the
|
|
40
40
|
[deployment contract](https://github.com/open-everywhere/open-everywhere/blob/main/docs/deploy/contract.md)).
|
|
41
|
-
Vercel is the one route that the project runs end to end. The Docker route is partly proven
|
|
42
|
-
scaffolded app carries
|
|
43
|
-
|
|
41
|
+
Vercel is the one route that the project runs end to end. The Docker route is partly proven: a
|
|
42
|
+
scaffolded app carries its `Dockerfile` and a `docker-compose.yml` with the egress proxy and Postgres,
|
|
43
|
+
and the
|
|
44
|
+
[deployment contract](https://github.com/open-everywhere/open-everywhere/blob/main/docs/deploy/contract.md)
|
|
45
|
+
says which requirements that stack meets.
|
|
44
46
|
|
|
45
47
|
## Use
|
|
46
48
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-everywhere/create",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
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.
|
|
50
|
-
"@open-everywhere/provider-slack": "0.
|
|
49
|
+
"@open-everywhere/core": "0.8.1",
|
|
50
|
+
"@open-everywhere/provider-slack": "0.8.1",
|
|
51
51
|
"tsx": "^4.23.9"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
@@ -39,7 +39,7 @@ function deployOrigin(ctx: StepContext): string | undefined {
|
|
|
39
39
|
/** The command that deploys, per route. */
|
|
40
40
|
function deployCommand(ctx: StepContext): string {
|
|
41
41
|
return routeOf(ctx) === "docker"
|
|
42
|
-
? "
|
|
42
|
+
? "`docker compose up --build` on your server (the app's Dockerfile and docker-compose.yml; the deployment contract states what it must satisfy)"
|
|
43
43
|
: "npx vercel deploy --prod";
|
|
44
44
|
}
|
|
45
45
|
|
|
@@ -69,7 +69,7 @@ export const buildDeployStep: SetupStep = {
|
|
|
69
69
|
"`npm run build` (= `oe build`, then `eve build`) — must be green.",
|
|
70
70
|
vercel
|
|
71
71
|
? "Deploy is a SEPARATE, explicit step: `npx vercel deploy --prod` (confirm the scope and team first). A `git push` does NOT deploy."
|
|
72
|
-
: "Deploy is a SEPARATE, explicit step: build the image and
|
|
72
|
+
: "Deploy is a SEPARATE, explicit step: on your server, put the contract's environment in `.env` (with `OE_EGRESS_PROXY_TOKEN`), then run `docker compose up --build`. The app's `Dockerfile` builds the image, and `docker-compose.yml` puts it behind the egress proxy with Postgres. This stack runs the `justbash` sandbox backend: the agent's shell shares the app process's filesystem and environment. That is development grade, and it does not meet the contract's `sandbox-backend` requirement. See https://github.com/open-everywhere/open-everywhere/blob/main/docs/deploy/contract.md.",
|
|
73
73
|
"Then re-run `npx @open-everywhere/create --check --host https://<host>`: it probes the deployed Slack route.",
|
|
74
74
|
"The probe passes on the route's own `401 unauthorized`, which proves the function booted and the route is mounted. It does NOT prove the deployment holds SLACK_SIGNING_SECRET: the route answers the same without it. Only Event Subscriptions showing **Verified** in the Slack console proves that.",
|
|
75
75
|
],
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# The build context of this app's image (Dockerfile, docker-compose.yml).
|
|
2
|
+
#
|
|
3
|
+
# node_modules stays out: the image installs its own (musl) dependencies with `npm ci`. A host copy
|
|
4
|
+
# would put glibc native modules over them and break the runtime image.
|
|
5
|
+
node_modules
|
|
6
|
+
**/node_modules
|
|
7
|
+
|
|
8
|
+
# Version control and agent or editor state.
|
|
9
|
+
.git
|
|
10
|
+
.claude
|
|
11
|
+
.vscode
|
|
12
|
+
.idea
|
|
13
|
+
|
|
14
|
+
# Build output: the image runs its own `oe build` and `eve build`.
|
|
15
|
+
.vercel
|
|
16
|
+
.output
|
|
17
|
+
.nitro
|
|
18
|
+
.eve
|
|
19
|
+
|
|
20
|
+
# Local secrets and env values. An image layer must never carry one.
|
|
21
|
+
.env
|
|
22
|
+
.env.*
|
|
23
|
+
*.pem
|
|
24
|
+
*.key
|
|
25
|
+
|
|
26
|
+
# Local state from `npm run dev`, and the egress proxy's own build context.
|
|
27
|
+
.open-everywhere-data
|
|
28
|
+
.open-everywhere-memory
|
|
29
|
+
.workflow-data
|
|
30
|
+
deploy
|
|
31
|
+
|
|
32
|
+
# Logs and OS files.
|
|
33
|
+
*.log
|
|
34
|
+
npm-debug.log*
|
|
35
|
+
.DS_Store
|
package/template/AGENTS.md
CHANGED
|
@@ -65,6 +65,13 @@ Setup is yours to drive, with the user's consent at every command. Use the
|
|
|
65
65
|
- `npx oe build --check` verifies the slot tree without a write, and proves the overlays hold
|
|
66
66
|
only data. Run it before you merge a pull request that changes an overlay.
|
|
67
67
|
- `npm run typecheck` type-checks the app.
|
|
68
|
+
- `npm run check:model-config` reads the list of endpoints for each model from the model's target
|
|
69
|
+
(the AI Gateway, unless `models.targets` names another URL). It names a demand that no provider
|
|
70
|
+
can meet, two regions that conflict, a model id the gateway does not know, and a `via` that
|
|
71
|
+
names no target. Run it after a change to the model settings, before the deploy. It needs the
|
|
72
|
+
network and no credential.
|
|
73
|
+
- `npx oe sovereignty --host vercel` (or `--host docker`) prints where each piece of the app's data
|
|
74
|
+
lives, which region, and which company could be compelled to hand it over. It changes nothing.
|
|
68
75
|
- Deploy is a separate, explicit step. On the Vercel route that is `npx vercel deploy --prod`; a
|
|
69
76
|
`git push` does NOT deploy.
|
|
70
77
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# The Docker route's image for this app: the eve node-server build, run with `eve start`.
|
|
2
|
+
#
|
|
3
|
+
# Use it with docker-compose.yml, which puts the app behind the egress proxy (deploy/egress-proxy)
|
|
4
|
+
# and gives it Postgres. The requirements this image satisfies, and the ones it does not, are in the
|
|
5
|
+
# deployment contract:
|
|
6
|
+
# https://github.com/open-everywhere/open-everywhere/blob/main/docs/deploy/contract.md
|
|
7
|
+
#
|
|
8
|
+
# Run `npm install` once before you build: the build installs from package-lock.json with `npm ci`.
|
|
9
|
+
FROM node:24-alpine AS build
|
|
10
|
+
WORKDIR /app
|
|
11
|
+
|
|
12
|
+
# eve collects its own CLI usage telemetry, ON by default. Each `eve` command posts the command name,
|
|
13
|
+
# the eve version, the platform, a random installation id and a salted hash of the git remote URL or
|
|
14
|
+
# of the working directory to telemetry.vercel.com. This image turns it OFF:
|
|
15
|
+
# `EVE_TELEMETRY_DISABLED=1` is eve's switch, and both stages set it. Open Everywhere's own telemetry
|
|
16
|
+
# has its own switch, `OE_TELEMETRY` (see README.md § Telemetry).
|
|
17
|
+
ENV EVE_TELEMETRY_DISABLED=1
|
|
18
|
+
|
|
19
|
+
# git: a dependency or the `git` memory backing can need it at build time.
|
|
20
|
+
RUN apk add --no-cache git
|
|
21
|
+
|
|
22
|
+
# Install first, from the lockfile, so a source edit does not repeat the install.
|
|
23
|
+
# The app's `postinstall` (`oe-eve-patch`) puts the Open Everywhere patch into eve, and it exits
|
|
24
|
+
# non-zero when the patch does not apply. npm runs a dependency's install script only when
|
|
25
|
+
# package.json's `allowScripts` names it, so `node-liblzma` does not compile here (just-bash then
|
|
26
|
+
# has no `xz` command). The probe then proves the patch is in the eve this image runs: core refuses
|
|
27
|
+
# to boot on an unpatched eve, so a build that ends without it gives a dead container.
|
|
28
|
+
COPY package.json package-lock.json ./
|
|
29
|
+
RUN npm ci \
|
|
30
|
+
&& node --input-type=module -e 'const { probeEvePatch } = await import("@open-everywhere/core/agent/access/eve-patch-probe.js"); const r = await probeEvePatch(); if (r.status !== "patched") { console.error(`eve patch probe: ${r.status}`); process.exit(1); }'
|
|
31
|
+
|
|
32
|
+
# Build. VERCEL is unset here, so `eve build` writes the node-server bundle (.output/server).
|
|
33
|
+
# `npm run build` runs `oe build` first (the prebuild hook), then `eve build`.
|
|
34
|
+
# 🔴 eve provisions the sandbox template at BUILD time, on the backend the build resolves. This stage
|
|
35
|
+
# has no Docker daemon, so the app must declare `sandbox: { selfHostBackend: "justbash" }` in
|
|
36
|
+
# agent/settings/settings.ts (the template does). With `"docker"` the build fails with
|
|
37
|
+
# `spawn docker ENOENT`.
|
|
38
|
+
# The builder needs memory: `eve build` can take several GB, so the heap limit is raised.
|
|
39
|
+
COPY . .
|
|
40
|
+
RUN NODE_OPTIONS=--max-old-space-size=6144 npm run build
|
|
41
|
+
|
|
42
|
+
# ── Runtime image ──
|
|
43
|
+
FROM node:24-alpine AS runtime
|
|
44
|
+
WORKDIR /app
|
|
45
|
+
ENV NODE_ENV=production
|
|
46
|
+
# eve's CLI telemetry stays OFF for `eve start` too (see the build stage).
|
|
47
|
+
ENV EVE_TELEMETRY_DISABLED=1
|
|
48
|
+
# git and openssh-client for real git operations; ca-certificates for TLS egress.
|
|
49
|
+
RUN apk add --no-cache git openssh-client ca-certificates
|
|
50
|
+
|
|
51
|
+
# The whole tree: the build stage produced node_modules and .output.
|
|
52
|
+
COPY --from=build /app /app
|
|
53
|
+
|
|
54
|
+
# VERCEL stays UNSET, so the sandbox backend and the egress setup take the self-host branch.
|
|
55
|
+
ENV PORT=3000
|
|
56
|
+
EXPOSE 3000
|
|
57
|
+
|
|
58
|
+
# `eve start` serves the node-server build and runs the in-process routine dispatcher.
|
|
59
|
+
# It is the binary itself, not `npm start`: the `prestart` hook would run `oe build` again.
|
|
60
|
+
CMD ["npx", "eve", "start", "--host", "0.0.0.0"]
|
package/template/README.md
CHANGED
|
@@ -16,6 +16,11 @@ route first:
|
|
|
16
16
|
Gateway with OIDC, so it needs no gateway key.
|
|
17
17
|
- **Docker** — self-host a container that satisfies the
|
|
18
18
|
[deployment contract](https://github.com/open-everywhere/open-everywhere/blob/main/docs/deploy/contract.md).
|
|
19
|
+
This app carries the image (`Dockerfile`) and the stack (`docker-compose.yml`: the app behind the
|
|
20
|
+
egress proxy in `deploy/egress-proxy/`, with Postgres). Run `npm install` once, then
|
|
21
|
+
`docker compose up --build` on the server. This stack runs the `justbash` sandbox backend: the
|
|
22
|
+
agent's shell shares the app process's filesystem and environment. That is development grade, and
|
|
23
|
+
it does not meet the contract's `sandbox-backend` requirement.
|
|
19
24
|
|
|
20
25
|
These are the two supported routes. Some steps stay with you: creating the Slack app, approving
|
|
21
26
|
each deploy, and pasting each secret where the agent's tools can read it but the chat cannot.
|
|
@@ -42,6 +47,12 @@ never ships. Every event and attribute is listed in the
|
|
|
42
47
|
[telemetry disclosure](https://github.com/open-everywhere/open-everywhere/blob/main/docs/deploy/telemetry.md).
|
|
43
48
|
The `@open-everywhere/create` CLI sends none.
|
|
44
49
|
|
|
50
|
+
eve, the framework under the app, has its own CLI telemetry, ON by default in eve. This app turns it
|
|
51
|
+
off: the `dev`, `build` and `start` scripts and both stages of the `Dockerfile` set
|
|
52
|
+
`EVE_TELEMETRY_DISABLED=1`. Keep that prefix on any script you add that runs `eve`. The `dev`,
|
|
53
|
+
`build` and `start` scripts use the POSIX form `NAME=value command`. On Windows, use WSL, or set
|
|
54
|
+
npm's `script-shell` to a POSIX shell such as Git Bash.
|
|
55
|
+
|
|
45
56
|
## Develop locally
|
|
46
57
|
|
|
47
58
|
After the deploy, `npm run dev` runs the agent on this machine and talks to it in a terminal UI.
|
|
@@ -67,6 +78,8 @@ without a public tunnel.
|
|
|
67
78
|
| `agent/webhooks/hooks.ts` | Inbound webhook triggers. |
|
|
68
79
|
| `AGENTS.md`, `CLAUDE.md` | The guide a coding agent reads in this repository. |
|
|
69
80
|
| `.mcp.json`, `scripts/ops-mcp.ts` | The `open-everywhere-ops` MCP server: setup status, `.env.local` values and stored secrets, for a coding agent. |
|
|
81
|
+
| `Dockerfile`, `.dockerignore`, `docker-compose.yml` | The Docker route: the app's image, and the stack that runs it behind the egress proxy with Postgres. |
|
|
82
|
+
| `deploy/egress-proxy/` | The egress proxy's image: the only container on the Docker route with a route to the internet. It is a copy of the Open Everywhere repository's `deploy/egress-proxy`. |
|
|
70
83
|
| `scripts/env-pipe.mjs` | `npm run -s env:pipe`: writes one `.env.local` value to a pipe for `vercel env add`. It refuses a terminal or a file. Never run it bare. |
|
|
71
84
|
| `.claude/skills/quickstart-open-everywhere/` | The setup skill that drives that server. |
|
|
72
85
|
|
package/template/agent/agent.ts
CHANGED
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
// app. The model per ROLE lives in agent/model/stacks.ts; pick a stack with `models.stack` in
|
|
10
10
|
// agent/settings/settings.ts.
|
|
11
11
|
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
12
|
+
// What every model call demands of its provider (zero retention, no training, a region) is
|
|
13
|
+
// `models.dataPolicy` in agent/settings/settings.ts. The model adapter puts it on every call. The
|
|
14
|
+
// gateway's own team settings (AI Gateway → Settings) apply on top.
|
|
14
15
|
|
|
15
16
|
import { defineAgent } from "eve";
|
|
16
17
|
import { defineDynamic } from "eve/tools";
|
|
@@ -25,9 +26,13 @@ import { registerGatewayDreamJudge, registerGatewayFailureFacetJudge } from "@op
|
|
|
25
26
|
import { registerGatewayMeetingModels } from "@open-everywhere/adapters/agent/model/meeting-gateway.js";
|
|
26
27
|
import { registerGatewayApprovalJudge } from "@open-everywhere/adapters/agent/model/approval-judge.js";
|
|
27
28
|
import { registerGatewayImageDescriber } from "@open-everywhere/adapters/agent/model/gateway-vision.js";
|
|
29
|
+
import { registerModelConfigCheck } from "@open-everywhere/adapters/agent/model/gateway-probe.js";
|
|
30
|
+
import { getConfig } from "@open-everywhere/core/agent/access/config.js";
|
|
31
|
+
import { resolvableModels } from "@open-everywhere/core/agent/model/targets.js";
|
|
28
32
|
import { isModelUsable } from "@open-everywhere/core/agent/model/policy.js";
|
|
29
33
|
import {
|
|
30
34
|
JUDGE_MODEL,
|
|
35
|
+
MODELS,
|
|
31
36
|
PRIMARY_MODEL,
|
|
32
37
|
PRIMARY_MODEL_CONTEXT_WINDOW_TOKENS,
|
|
33
38
|
TRANSCRIPTION_ROLE,
|
|
@@ -52,14 +57,14 @@ const COMPACTION_THRESHOLD_PERCENT = getSettings().agent.compactionThresholdPerc
|
|
|
52
57
|
const COMPACTION_THRESHOLD_TOKENS = compactionThresholdTokens(PRIMARY_MODEL_CONTEXT_WINDOW_TOKENS, COMPACTION_THRESHOLD_PERCENT);
|
|
53
58
|
|
|
54
59
|
// The background judges: memory distillation, failure facets and the auto-approval judge.
|
|
55
|
-
registerGatewayDreamJudge({ id: UTILITY_MODEL, gateway: GATEWAY_DEFAULTS });
|
|
56
|
-
registerGatewayFailureFacetJudge({ id: UTILITY_MODEL, gateway: GATEWAY_DEFAULTS });
|
|
60
|
+
registerGatewayDreamJudge({ id: UTILITY_MODEL, via: MODELS.utility.via, gateway: GATEWAY_DEFAULTS });
|
|
61
|
+
registerGatewayFailureFacetJudge({ id: UTILITY_MODEL, via: MODELS.utility.via, gateway: GATEWAY_DEFAULTS });
|
|
57
62
|
|
|
58
63
|
// The meeting bot's model calls (OPE-2459): the `gateway` transcription route, and the tool-less recap
|
|
59
64
|
// completion on the same `utility` route as the dream judge. Inert until agent/meetings/policy.ts enables
|
|
60
65
|
// 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);
|
|
62
|
-
registerGatewayApprovalJudge({ id: JUDGE_MODEL, gateway: GATEWAY_DEFAULTS });
|
|
66
|
+
registerGatewayMeetingModels({ id: UTILITY_MODEL, via: MODELS.utility.via, gateway: GATEWAY_DEFAULTS }, TRANSCRIPTION_ROLE);
|
|
67
|
+
registerGatewayApprovalJudge({ id: JUDGE_MODEL, via: MODELS.judge.via, gateway: GATEWAY_DEFAULTS });
|
|
63
68
|
|
|
64
69
|
// The image describer. Fail at boot if the org model policy forbids the vision model, rather than
|
|
65
70
|
// fail every image later.
|
|
@@ -69,7 +74,19 @@ if (!isModelUsable(VISION_MODEL, loadModelPolicy())) {
|
|
|
69
74
|
"check agent/model/policy.ts, or re-point the `vision` role in agent/model/stacks.ts.",
|
|
70
75
|
);
|
|
71
76
|
}
|
|
72
|
-
registerGatewayImageDescriber({ id: VISION_MODEL });
|
|
77
|
+
registerGatewayImageDescriber({ id: VISION_MODEL, via: MODELS.vision.via });
|
|
78
|
+
|
|
79
|
+
// THE LIVE MODEL CONFIG CHECK (OPE-2556): the deployment status probe (`model-route`) reads the gateway's public
|
|
80
|
+
// endpoints of every model this deployment can resolve, and names a `models.dataPolicy` or `providers.only` demand no
|
|
81
|
+
// provider can meet. `create --check --host` and the setup skill read that row. Read lazily, at status time.
|
|
82
|
+
registerModelConfigCheck(() =>
|
|
83
|
+
resolvableModels(
|
|
84
|
+
[...Object.values(MODELS), ...[TRANSCRIPTION_ROLE.primary, TRANSCRIPTION_ROLE.fallback].flatMap((r) => (r?.via === "gateway" ? [{ id: r.id }] : []))],
|
|
85
|
+
getConfig().loadDomains(),
|
|
86
|
+
getConfig().loadRoles?.() ?? [],
|
|
87
|
+
getSettings().models.force,
|
|
88
|
+
),
|
|
89
|
+
);
|
|
73
90
|
|
|
74
91
|
function readAppVersion(): string {
|
|
75
92
|
const version = appPackageJson.version;
|
|
@@ -120,7 +137,7 @@ export default defineAgent({
|
|
|
120
137
|
model: defineDynamic({
|
|
121
138
|
events: {
|
|
122
139
|
"step.started": (() => {
|
|
123
|
-
const resolve = gatewayModelResolver({ id: PRIMARY_MODEL, gateway: GATEWAY_DEFAULTS });
|
|
140
|
+
const resolve = gatewayModelResolver({ id: PRIMARY_MODEL, via: MODELS.primary.via, gateway: GATEWAY_DEFAULTS });
|
|
124
141
|
return async (event: unknown, ctx: unknown) => primaryModelSelection(await resolve(event, ctx));
|
|
125
142
|
})(),
|
|
126
143
|
},
|
|
@@ -239,6 +239,12 @@ lookup first: search your memory, THEN reply. Never answer "I don't know" (or gu
|
|
|
239
239
|
something you could have stored without checking — a miss because you didn't look is the one
|
|
240
240
|
recall failure that's entirely on you.
|
|
241
241
|
|
|
242
|
+
**A fact this conversation already holds is not a lookup.** If a person said it earlier in
|
|
243
|
+
this conversation, or your own earlier reply said it, answer from that directly, in this
|
|
244
|
+
turn. This rule holds also when you saved the fact to memory: the save does not make the
|
|
245
|
+
conversation a less reliable source. Search memory only for what this conversation does not
|
|
246
|
+
hold.
|
|
247
|
+
|
|
242
248
|
**To recall a specific fact, grep the whole memory space in one command** — every silo for
|
|
243
249
|
this channel is materialised under `/workspace/memory/`, so one recursive search finds a fact
|
|
244
250
|
wherever it's filed, without walking silo by silo:
|
|
@@ -11,12 +11,13 @@
|
|
|
11
11
|
// before the first resolve.
|
|
12
12
|
|
|
13
13
|
import { registerConfig } from "@open-everywhere/core/agent/access/config.js";
|
|
14
|
-
import { registerSettings } from "@open-everywhere/core/agent/settings/settings.js";
|
|
14
|
+
import { getSettings, registerSettings } from "@open-everywhere/core/agent/settings/settings.js";
|
|
15
15
|
import { registerModelPricing } from "@open-everywhere/core/agent/spend/pricing.js";
|
|
16
16
|
import { loadModelPricing } from "../spend/model-pricing.js";
|
|
17
17
|
import { SETTINGS_OVERRIDES } from "../settings/settings.js";
|
|
18
18
|
import { setRegisteredConnectorCredentials } from "@open-everywhere/core/agent/access/connector-credentials.js";
|
|
19
19
|
import { assertRoutesWithinPolicy } from "@open-everywhere/core/agent/model/policy.js";
|
|
20
|
+
import { assertRouteTargets } from "@open-everywhere/core/agent/model/targets.js";
|
|
20
21
|
import { assertDomainCredentialsResolvable } from "@open-everywhere/core/agent/access/domain-credentials.js";
|
|
21
22
|
import {
|
|
22
23
|
assertWindowSourcesVouched,
|
|
@@ -165,6 +166,9 @@ setRegisteredConnectorCredentials(loadConnectorCredentials());
|
|
|
165
166
|
// policy is configured). Domains carry role defaults already (applyRoleDefaults inside
|
|
166
167
|
// loadDomains); roles are validated too since a role rung can carry its own route.
|
|
167
168
|
assertRoutesWithinPolicy(loadDomains(), loadRoles(), loadModelPolicy());
|
|
169
|
+
// Every route's `via` names a `models.targets` entry, and no route still carries the 0.7.0 key `providers` (renamed
|
|
170
|
+
// `providerOrder`, OPE-2556). Fail LOUD: a renamed key that is read as absent would drop the order hint silently.
|
|
171
|
+
assertRouteTargets(loadDomains(), loadRoles(), getSettings().models.targets);
|
|
168
172
|
|
|
169
173
|
// Multi-source WINDOW vouch (memory/domain.ts assertWindowSourcesVouched, window-tool-gating-design
|
|
170
174
|
// §Q5): any standing WINDOW holding more than one source requires every register-gated source to be
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
// changing the engaged thread sessions.
|
|
30
30
|
|
|
31
31
|
import { defineDomain } from "@open-everywhere/core/agent/memory/domain.js";
|
|
32
|
-
import { SUPERVISOR_MODEL } from "../../../model/models.js";
|
|
32
|
+
import { SUPERVISOR_MODEL, SUPERVISOR_VIA } from "../../../model/models.js";
|
|
33
33
|
|
|
34
34
|
export const domain = defineDomain({
|
|
35
35
|
name: "org",
|
|
@@ -37,5 +37,5 @@ export const domain = defineDomain({
|
|
|
37
37
|
memory: "org-brain",
|
|
38
38
|
nameDropWake: true,
|
|
39
39
|
admins: [],
|
|
40
|
-
models: { supervisor: { id: SUPERVISOR_MODEL, reasoning: "low" } },
|
|
40
|
+
models: { supervisor: { id: SUPERVISOR_MODEL, via: SUPERVISOR_VIA, reasoning: "low" } },
|
|
41
41
|
});
|
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
registerDefaultAgentModelId,
|
|
8
8
|
} from "@open-everywhere/core/agent/model/default-model.js";
|
|
9
9
|
import { getSettings } from "@open-everywhere/core/agent/settings/settings.js";
|
|
10
|
+
import { viaProblem, type ModelTargets } from "@open-everywhere/core/agent/model/targets.js";
|
|
11
|
+
import { registerBareModelRoutes } from "@open-everywhere/core/agent/model/bare-model-via.js";
|
|
10
12
|
import { SETTINGS_OVERRIDES } from "../settings/settings.js";
|
|
11
13
|
import { resolveStack, resolveTranscription, type ModelRole, type ModelRoleSpec, type ModelStack } from "./stacks.js";
|
|
12
14
|
|
|
@@ -19,6 +21,10 @@ const STACK: ModelStack = resolveStack(STACK_ALIAS, {});
|
|
|
19
21
|
|
|
20
22
|
// A deploy can retarget the primary (`models.force` / `models.primary` in agent/settings/settings.ts).
|
|
21
23
|
// Core owns that precedence, so read the primary back from it.
|
|
24
|
+
// Every role's `via` names a `models.targets` entry (OPE-2556). Fail LOUD at config load: an unknown target would
|
|
25
|
+
// otherwise fail every call of that role at run time.
|
|
26
|
+
assertStackVia(STACK, MODEL_SETTINGS.targets);
|
|
27
|
+
|
|
22
28
|
registerDefaultAgentModelId(STACK.primary.id);
|
|
23
29
|
|
|
24
30
|
export const MODELS = {
|
|
@@ -29,6 +35,9 @@ export const MODELS = {
|
|
|
29
35
|
judge: STACK.judge,
|
|
30
36
|
} as const satisfies Record<string, ModelRoleSpec>;
|
|
31
37
|
|
|
38
|
+
// A bare model id (a declared subagent's `model`) goes through the target of the role that names it, else the primary's.
|
|
39
|
+
registerBareModelRoutes(Object.values(MODELS));
|
|
40
|
+
|
|
32
41
|
/** The alias of the stack this process resolved — for `/oe status`. */
|
|
33
42
|
export const MODEL_STACK = STACK_ALIAS;
|
|
34
43
|
/** The meeting bot's speech-to-text role: a primary and a fallback route (stacks.ts TRANSCRIPTION). */
|
|
@@ -38,6 +47,8 @@ export const PRIMARY_MODEL = MODELS.primary.id;
|
|
|
38
47
|
export const UTILITY_MODEL = MODELS.utility.id;
|
|
39
48
|
export const VISION_MODEL = MODELS.vision.id;
|
|
40
49
|
export const SUPERVISOR_MODEL = MODELS.supervisor.id;
|
|
50
|
+
/** The supervisor role's `models.targets` entry (OPE-2556): the org domain's supervisor route carries it. */
|
|
51
|
+
export const SUPERVISOR_VIA = MODELS.supervisor.via;
|
|
41
52
|
export const JUDGE_MODEL = MODELS.judge.id;
|
|
42
53
|
export const PRIMARY_MODEL_CONTEXT_WINDOW_TOKENS = MODELS.primary.contextWindowTokens;
|
|
43
54
|
|
|
@@ -62,3 +73,9 @@ export function allowListForStack(stack: ModelStack, extra: readonly ModelPolicy
|
|
|
62
73
|
export function modelAllowList(extra: readonly ModelPolicyAllowEntry[] = []): ModelPolicyAllowEntry[] {
|
|
63
74
|
return allowListForStack(MODELS, extra);
|
|
64
75
|
}
|
|
76
|
+
|
|
77
|
+
/** Throw when a stack role's `via` names no `models.targets` entry. Exported for tests. */
|
|
78
|
+
export function assertStackVia(stack: ModelStack, targets: ModelTargets): void {
|
|
79
|
+
const bad = (Object.keys(stack) as ModelRole[]).flatMap((role) => viaProblem(stack[role].via, targets, `stack role "${role}"`) ?? []);
|
|
80
|
+
if (bad.length > 0) throw new Error(`open-everywhere: ${bad.join("; ")}`);
|
|
81
|
+
}
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
// judge — the auto-approval judge.
|
|
10
10
|
//
|
|
11
11
|
// Every id is an AI Gateway model id. Add a stack by adding an entry, and give each new id a rate in
|
|
12
|
-
// agent/spend/model-pricing.ts, or a USD spend cap cannot see its cost.
|
|
12
|
+
// agent/spend/model-pricing.ts, or a USD spend cap cannot see its cost. A role's `via` names the
|
|
13
|
+
// `models.targets` entry its calls go through: the European stack uses the `mistral` target in
|
|
14
|
+
// agent/settings/settings.ts (only the Mistral provider, zero retention).
|
|
13
15
|
|
|
14
16
|
import type { ModelTier } from "@open-everywhere/core/agent/memory/domain.js";
|
|
15
17
|
import type { TranscriptionRoleSpec } from "@open-everywhere/adapters/agent/model/transcription-routes.js";
|
|
@@ -21,17 +23,19 @@ export interface ModelRoleSpec {
|
|
|
21
23
|
readonly tier: ModelTier;
|
|
22
24
|
/** The model's context window. eve compacts at `agent.compactionThresholdPercent` of it. */
|
|
23
25
|
readonly contextWindowTokens: number;
|
|
26
|
+
/** The `models.targets` entry this role's calls go through (OPE-2556). Absent: `"gateway"`. An unknown name fails config load. */
|
|
27
|
+
readonly via?: string;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
export type ModelStack = Readonly<Record<ModelRole, ModelRoleSpec>>;
|
|
27
31
|
|
|
28
32
|
export const STACKS = {
|
|
29
33
|
"chinese-open-weights": {
|
|
30
|
-
primary: { id: "deepseek/deepseek-v4-flash
|
|
31
|
-
utility: { id: "deepseek/deepseek-v4-flash
|
|
34
|
+
primary: { id: "deepseek/deepseek-v4.1-flash", tier: "high", contextWindowTokens: 1_000_000 },
|
|
35
|
+
utility: { id: "deepseek/deepseek-v4.1-flash", tier: "low", contextWindowTokens: 1_000_000 },
|
|
32
36
|
vision: { id: "alibaba/qwen3-vl-instruct", tier: "low", contextWindowTokens: 131_072 },
|
|
33
|
-
supervisor: { id: "deepseek/deepseek-v4-flash
|
|
34
|
-
judge: { id: "deepseek/deepseek-v4-flash
|
|
37
|
+
supervisor: { id: "deepseek/deepseek-v4.1-flash", tier: "low", contextWindowTokens: 1_000_000 },
|
|
38
|
+
judge: { id: "deepseek/deepseek-v4.1-flash", tier: "low", contextWindowTokens: 1_000_000 },
|
|
35
39
|
},
|
|
36
40
|
"american-closed": {
|
|
37
41
|
primary: { id: "openai/gpt-6-luna", tier: "high", contextWindowTokens: 1_050_000 },
|
|
@@ -40,6 +44,14 @@ export const STACKS = {
|
|
|
40
44
|
supervisor: { id: "openai/gpt-6-luna", tier: "low", contextWindowTokens: 1_050_000 },
|
|
41
45
|
judge: { id: "openai/gpt-6-luna", tier: "low", contextWindowTokens: 1_050_000 },
|
|
42
46
|
},
|
|
47
|
+
// Mistral Small 4 (Apache 2.0) in every role, vision included, served by Mistral with zero retention.
|
|
48
|
+
"european-open-weights": {
|
|
49
|
+
primary: { id: "mistral/mistral-small", tier: "high", contextWindowTokens: 262_144, via: "mistral" },
|
|
50
|
+
utility: { id: "mistral/mistral-small", tier: "low", contextWindowTokens: 262_144, via: "mistral" },
|
|
51
|
+
vision: { id: "mistral/mistral-small", tier: "low", contextWindowTokens: 262_144, via: "mistral" },
|
|
52
|
+
supervisor: { id: "mistral/mistral-small", tier: "low", contextWindowTokens: 262_144, via: "mistral" },
|
|
53
|
+
judge: { id: "mistral/mistral-small", tier: "low", contextWindowTokens: 262_144, via: "mistral" },
|
|
54
|
+
},
|
|
43
55
|
} as const satisfies Record<string, ModelStack>;
|
|
44
56
|
|
|
45
57
|
export type StackAlias = keyof typeof STACKS;
|
|
@@ -78,7 +90,7 @@ export function resolveStack(alias: string, overrides: StackOverrides = {}): Mod
|
|
|
78
90
|
// Speech to text for the meeting bot: a primary and a fallback route per stack, on two different providers, so
|
|
79
91
|
// one provider outage falls through to the other. A route names its transport too: the AI Gateway, DeepInfra
|
|
80
92
|
// (key: `oe secret set DEEPINFRA_API_KEY`), or your own OpenAI-compatible speech server. These defaults are
|
|
81
|
-
// pending a bake-off (`
|
|
93
|
+
// pending a bake-off (the `bakeoff:stt` script of the Open Everywhere repository).
|
|
82
94
|
export const TRANSCRIPTION = {
|
|
83
95
|
"chinese-open-weights": {
|
|
84
96
|
primary: { via: "deepinfra", id: "Qwen/Qwen3-ASR-1.7B" },
|
|
@@ -88,6 +100,11 @@ export const TRANSCRIPTION = {
|
|
|
88
100
|
primary: { via: "gateway", id: "microsoft/mai-transcribe-2" },
|
|
89
101
|
fallback: { via: "gateway", id: "spacexai/grok-stt" },
|
|
90
102
|
},
|
|
103
|
+
// Mistral's Voxtral on your own speech server, then Qwen3-ASR on a second one: audio stays on your machines.
|
|
104
|
+
"european-open-weights": {
|
|
105
|
+
primary: { via: "self-hosted", id: "mistralai/Voxtral-Mini-3B-2507", baseUrl: "http://voxtral:8000/v1" },
|
|
106
|
+
fallback: { via: "self-hosted", id: "Qwen/Qwen3-ASR-1.7B", baseUrl: "http://qwen3-asr:8000/v1" },
|
|
107
|
+
},
|
|
91
108
|
} as const satisfies Record<StackAlias, TranscriptionRoleSpec>;
|
|
92
109
|
|
|
93
110
|
/** Fully local: your own speech server only. For a Docker deployment with no cloud model. */
|
|
@@ -9,8 +9,11 @@
|
|
|
9
9
|
//
|
|
10
10
|
// Examples:
|
|
11
11
|
// identity: { name: "Ada" }, // the name the agent answers to
|
|
12
|
-
// models: { stack: "chinese-open-weights" }, // every model role at once
|
|
13
|
-
//
|
|
12
|
+
// models: { stack: "chinese-open-weights" }, // every model role at once (or "european-open-weights")
|
|
13
|
+
// models: { dataPolicy: { zeroRetention: true } }, // every model call: only zero-retention providers
|
|
14
|
+
// models: { targets: { gateway: { kind: "vercel-ai-gateway", providers: { only: ["openai", "azure"] } } } }, // only these providers
|
|
15
|
+
// After a change to `models`, run `npm run check:model-config`: it names a model no provider can serve.
|
|
16
|
+
// sandbox: { selfHostBackend: "docker" }, // a per-session container; needs a Docker daemon at build time
|
|
14
17
|
//
|
|
15
18
|
// Every behaviour choice that used to be an env var lives here too (OPE-2411): the model pins
|
|
16
19
|
// (`models.*`), the admin control plane (`control.*`), the directory sync (`directory.*`), AWS
|
|
@@ -31,4 +34,20 @@ export const SETTINGS_OVERRIDES = defineSettings({
|
|
|
31
34
|
// refuses: no browser tool mounts, no host opens, and the boot logs one line that names the
|
|
32
35
|
// platform. Set `enabled: false` to turn the browser off, and to silence that line off Vercel.
|
|
33
36
|
browser: { enabled: true },
|
|
37
|
+
// The sandbox backend off Vercel. eve provisions the sandbox template at BUILD time, on this
|
|
38
|
+
// backend, so it decides where `npm run build` works: `justbash` needs nothing, and `docker` needs
|
|
39
|
+
// a running Docker daemon at build time. The image (Dockerfile) builds with no daemon, so the
|
|
40
|
+
// Docker route needs `justbash`, and so does a local build on a machine without Docker.
|
|
41
|
+
// justbash shares the app process's filesystem: development-grade isolation. On Vercel this line
|
|
42
|
+
// has no effect: the Vercel Sandbox always runs there.
|
|
43
|
+
sandbox: { selfHostBackend: "justbash" },
|
|
44
|
+
// Where model calls go. `gateway` is the shipped target. `mistral` is the European stack's route
|
|
45
|
+
// (`european-open-weights` in agent/model/stacks.ts): only the `mistral` provider, and every call
|
|
46
|
+
// demands zero retention. A role with no `via` uses `gateway`, so this entry changes no other call.
|
|
47
|
+
models: {
|
|
48
|
+
targets: {
|
|
49
|
+
gateway: { kind: "vercel-ai-gateway" },
|
|
50
|
+
mistral: { kind: "vercel-ai-gateway", providers: { only: ["mistral"] }, dataPolicy: { zeroRetention: true } },
|
|
51
|
+
},
|
|
52
|
+
},
|
|
34
53
|
});
|
|
@@ -11,11 +11,13 @@ import { defineModelPricing, assertModelPricingValid, type ModelPricingTable } f
|
|
|
11
11
|
|
|
12
12
|
const pricing: ModelPricingTable = defineModelPricing({
|
|
13
13
|
// chinese-open-weights: primary, utility, supervisor and judge.
|
|
14
|
-
"deepseek/deepseek-v4-flash
|
|
14
|
+
"deepseek/deepseek-v4.1-flash": { inPerMtok: 0.3, outPerMtok: 1.2 },
|
|
15
15
|
// chinese-open-weights: vision.
|
|
16
16
|
"alibaba/qwen3-vl-instruct": { inPerMtok: 0.2, outPerMtok: 0.88 },
|
|
17
17
|
// american-closed: vision.
|
|
18
18
|
"google/gemini-3.8-flash": { inPerMtok: 0.75, outPerMtok: 3.75 },
|
|
19
|
+
// european-open-weights: every role.
|
|
20
|
+
"mistral/mistral-small": { inPerMtok: 0.15, outPerMtok: 0.6 },
|
|
19
21
|
});
|
|
20
22
|
|
|
21
23
|
assertModelPricingValid(pricing);
|