ndomo 0.1.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/.bun-version +1 -0
- package/.dockerignore +79 -0
- package/.editorconfig +18 -0
- package/.env.example +19 -0
- package/.github/CODEOWNERS +8 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +62 -0
- package/.github/ISSUE_TEMPLATE/config.yml +2 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +34 -0
- package/.github/dependabot.yml +36 -0
- package/.github/pull_request_template.md +24 -0
- package/.github/release.yml +30 -0
- package/.github/workflows/gitleaks.yml +28 -0
- package/.github/workflows/release-please.yml +27 -0
- package/.github/workflows/smoke.yml +29 -0
- package/.husky/commit-msg +1 -0
- package/CHANGELOG.md +114 -0
- package/Dockerfile +32 -0
- package/README.es.md +174 -0
- package/README.md +187 -0
- package/agents/chronicler.md +98 -0
- package/agents/ci-smith.md +136 -0
- package/agents/craftsman.md +341 -0
- package/agents/deploy-smith.md +138 -0
- package/agents/foreman.md +377 -0
- package/agents/go-smith.md +164 -0
- package/agents/guild.md +188 -0
- package/agents/inspector.md +83 -0
- package/agents/js-smith.md +127 -0
- package/agents/ops-scout.md +173 -0
- package/agents/painter.md +200 -0
- package/agents/python-smith.md +120 -0
- package/agents/ranger.md +307 -0
- package/agents/release-smith.md +165 -0
- package/agents/rust-smith.md +159 -0
- package/agents/sage.md +178 -0
- package/agents/scout.md +144 -0
- package/agents/scribe.md +156 -0
- package/agents/smith.md +201 -0
- package/agents/vue-smith.md +155 -0
- package/agents/warden.md +216 -0
- package/agents/zig-smith.md +156 -0
- package/bin/ndomo-analyses.ts +4 -0
- package/bin/ndomo-status.ts +4 -0
- package/biome.json +57 -0
- package/bun.lock +514 -0
- package/commitlint.config.js +3 -0
- package/config/ndomo.config.json +258 -0
- package/config/ndomo.schema.json +166 -0
- package/docs/agents.md +375 -0
- package/docs/bugs/plan-create-orphan-fk.md +131 -0
- package/docs/bugs/task_create_batch-order-index-collision.md +158 -0
- package/docs/configuration.md +276 -0
- package/docs/database.md +364 -0
- package/docs/features/feature-flexible-builder-v1.md +724 -0
- package/docs/features/feature-flexible-builder-v2.md +882 -0
- package/docs/features/feature-flexible-builder.md +974 -0
- package/docs/http-server.md +244 -0
- package/docs/installation.md +259 -0
- package/docs/integrations.md +129 -0
- package/docs/operations/anti-pattern-sub-agent-verify-2026-06-21.md +32 -0
- package/docs/operations/audit-v1.md +417 -0
- package/docs/operations/audit-v2.md +197 -0
- package/docs/operations/audit-v3.md +306 -0
- package/docs/operations/db-optimize-foundations.md +123 -0
- package/docs/operations/verify-gate-architecture.md +82 -0
- package/docs/workflows.md +448 -0
- package/opencode.json +5 -0
- package/package.json +65 -0
- package/release-please-config.json +11 -0
- package/scripts/dev-bust-cache.sh +164 -0
- package/scripts/install.sh +688 -0
- package/scripts/smoke-e2e.ts +704 -0
- package/scripts/smoke-hot.ts +417 -0
- package/scripts/smoke-http.sh +228 -0
- package/scripts/smoke-v4.ts +256 -0
- package/scripts/smoke-v5.ts +397 -0
- package/scripts/smoke.sh +9 -0
- package/scripts/uninstall.sh +224 -0
- package/skills/api-security-best-practices/SKILL.md +915 -0
- package/skills/bash-scripting/SKILL.md +201 -0
- package/skills/bun/SKILL.md +313 -0
- package/skills/cavecrew/SKILL.md +82 -0
- package/skills/caveman/SKILL.md +74 -0
- package/skills/caveman-review/README.md +33 -0
- package/skills/caveman-review/SKILL.md +55 -0
- package/skills/find-skills/SKILL.md +142 -0
- package/skills/frontend-design/LICENSE.txt +177 -0
- package/skills/frontend-design/SKILL.md +55 -0
- package/skills/golang-patterns/SKILL.md +674 -0
- package/skills/golang-security/SKILL.md +185 -0
- package/skills/golang-security/evals/evals.json +595 -0
- package/skills/golang-security/references/architecture.md +268 -0
- package/skills/golang-security/references/checklist.md +80 -0
- package/skills/golang-security/references/cookies.md +200 -0
- package/skills/golang-security/references/cryptography.md +424 -0
- package/skills/golang-security/references/filesystem.md +285 -0
- package/skills/golang-security/references/injection.md +315 -0
- package/skills/golang-security/references/logging.md +163 -0
- package/skills/golang-security/references/memory-safety.md +241 -0
- package/skills/golang-security/references/network.md +253 -0
- package/skills/golang-security/references/secrets.md +189 -0
- package/skills/golang-security/references/third-party.md +159 -0
- package/skills/golang-security/references/threat-modeling.md +189 -0
- package/skills/golang-testing/SKILL.md +720 -0
- package/skills/grill-me/SKILL.md +7 -0
- package/skills/javascript-testing-patterns/SKILL.md +537 -0
- package/skills/javascript-testing-patterns/references/advanced-testing-patterns.md +513 -0
- package/skills/modern-javascript-patterns/SKILL.md +43 -0
- package/skills/modern-javascript-patterns/references/advanced-patterns.md +487 -0
- package/skills/modern-javascript-patterns/references/details.md +457 -0
- package/skills/python-anti-patterns/SKILL.md +349 -0
- package/skills/python-design-patterns/SKILL.md +85 -0
- package/skills/python-design-patterns/references/details.md +353 -0
- package/skills/python-error-handling/SKILL.md +193 -0
- package/skills/python-error-handling/references/details.md +171 -0
- package/skills/python-testing-patterns/SKILL.md +278 -0
- package/skills/python-testing-patterns/references/advanced-patterns.md +411 -0
- package/skills/python-testing-patterns/references/details.md +349 -0
- package/skills/rust-patterns/SKILL.md +500 -0
- package/skills/rust-testing/SKILL.md +501 -0
- package/skills/security-review/SKILL.md +504 -0
- package/skills/security-review/cloud-infrastructure-security.md +361 -0
- package/skills/vue-best-practices/SKILL.md +154 -0
- package/skills/vue-best-practices/references/animation-class-based-technique.md +254 -0
- package/skills/vue-best-practices/references/animation-state-driven-technique.md +291 -0
- package/skills/vue-best-practices/references/component-async.md +97 -0
- package/skills/vue-best-practices/references/component-data-flow.md +307 -0
- package/skills/vue-best-practices/references/component-fallthrough-attrs.md +174 -0
- package/skills/vue-best-practices/references/component-keep-alive.md +137 -0
- package/skills/vue-best-practices/references/component-slots.md +216 -0
- package/skills/vue-best-practices/references/component-suspense.md +228 -0
- package/skills/vue-best-practices/references/component-teleport.md +108 -0
- package/skills/vue-best-practices/references/component-transition-group.md +128 -0
- package/skills/vue-best-practices/references/component-transition.md +125 -0
- package/skills/vue-best-practices/references/composables.md +290 -0
- package/skills/vue-best-practices/references/directives.md +162 -0
- package/skills/vue-best-practices/references/perf-avoid-component-abstraction-in-lists.md +159 -0
- package/skills/vue-best-practices/references/perf-v-once-v-memo-directives.md +182 -0
- package/skills/vue-best-practices/references/perf-virtualize-large-lists.md +187 -0
- package/skills/vue-best-practices/references/plugins.md +166 -0
- package/skills/vue-best-practices/references/reactivity.md +344 -0
- package/skills/vue-best-practices/references/render-functions.md +201 -0
- package/skills/vue-best-practices/references/sfc.md +310 -0
- package/skills/vue-best-practices/references/state-management.md +135 -0
- package/skills/vue-best-practices/references/updated-hook-performance.md +187 -0
- package/skills/vue-pinia-best-practices/SKILL.md +21 -0
- package/skills/vue-pinia-best-practices/reference/pinia-no-active-pinia-error.md +248 -0
- package/skills/vue-pinia-best-practices/reference/pinia-setup-store-return-all-state.md +227 -0
- package/skills/vue-pinia-best-practices/reference/pinia-store-destructuring-breaks-reactivity.md +193 -0
- package/skills/vue-pinia-best-practices/reference/state-url-for-ephemeral-filters.md +238 -0
- package/skills/vue-pinia-best-practices/reference/state-use-pinia-for-large-apps.md +262 -0
- package/skills/vue-pinia-best-practices/reference/store-method-binding-parentheses.md +191 -0
- package/skills/zig-0.16/SKILL.md +840 -0
- package/skills/zig-0.16/scripts/check-zig-version.sh +21 -0
- package/src/cli/analyses.ts +280 -0
- package/src/cli/index.ts +108 -0
- package/src/cli/serve.ts +192 -0
- package/src/cli/smoke.ts +131 -0
- package/src/cli/status.test.ts +204 -0
- package/src/cli/status.ts +263 -0
- package/src/cli/vacuum.test.ts +82 -0
- package/src/cli/vacuum.ts +96 -0
- package/src/config/schema.test.ts +88 -0
- package/src/config/schema.ts +64 -0
- package/src/db/analyses-migration.test.ts +210 -0
- package/src/db/analyses.test.ts +466 -0
- package/src/db/analyses.ts +375 -0
- package/src/db/auto-checkpoint.ts +131 -0
- package/src/db/client.test.ts +129 -0
- package/src/db/client.ts +55 -0
- package/src/db/fts-escape.ts +20 -0
- package/src/db/incidents.test.ts +201 -0
- package/src/db/incidents.ts +93 -0
- package/src/db/index.ts +86 -0
- package/src/db/migrations-v13.test.ts +141 -0
- package/src/db/migrations-v8.test.ts +301 -0
- package/src/db/migrations.ts +147 -0
- package/src/db/plan-archive.test.ts +180 -0
- package/src/db/plan-archive.ts +274 -0
- package/src/db/plan-create.test.ts +276 -0
- package/src/db/plan-create.ts +78 -0
- package/src/db/plan-files.test.ts +289 -0
- package/src/db/plan-update-status.ts +287 -0
- package/src/db/plans.test.ts +490 -0
- package/src/db/plans.ts +534 -0
- package/src/db/resolve-project-dir.test.ts +143 -0
- package/src/db/resolve-project-dir.ts +75 -0
- package/src/db/rollbacks.test.ts +150 -0
- package/src/db/rollbacks.ts +67 -0
- package/src/db/schema.ts +907 -0
- package/src/db/sessions.test.ts +80 -0
- package/src/db/sessions.ts +135 -0
- package/src/db/shutdown.test.ts +147 -0
- package/src/db/shutdown.ts +45 -0
- package/src/db/tasks.test.ts +921 -0
- package/src/db/tasks.ts +747 -0
- package/src/db/types.ts +619 -0
- package/src/http/__tests__/auth.test.ts +196 -0
- package/src/http/__tests__/routes.test.ts +465 -0
- package/src/http/__tests__/sse.test.ts +317 -0
- package/src/http/auth.ts +72 -0
- package/src/http/middleware/cors.ts +53 -0
- package/src/http/middleware/security-headers.ts +21 -0
- package/src/http/routes/events.ts +112 -0
- package/src/http/routes/health.ts +51 -0
- package/src/http/routes/plans.ts +66 -0
- package/src/http/routes/sessions.ts +50 -0
- package/src/http/routes/tasks.ts +60 -0
- package/src/http/server.ts +95 -0
- package/src/http/sse.ts +116 -0
- package/src/index.ts +37 -0
- package/src/lib.ts +65 -0
- package/src/mem/scoped.ts +65 -0
- package/src/orchestrator/background.test.ts +268 -0
- package/src/orchestrator/background.ts +293 -0
- package/src/orchestrator/memory-hook.ts +182 -0
- package/src/orchestrator/reconciler.ts +123 -0
- package/src/orchestrator/scheduler.test.ts +300 -0
- package/src/orchestrator/scheduler.ts +243 -0
- package/src/plugin.test.ts +2574 -0
- package/src/plugin.ts +1690 -0
- package/src/sdk/client.ts +66 -0
- package/src/worktrees/manager.ts +236 -0
- package/src/worktrees/state.ts +87 -0
- package/tests/integration/ranger-flow.test.ts +257 -0
- package/tools/analysis_archive.ts +28 -0
- package/tools/analysis_create.ts +55 -0
- package/tools/analysis_get.ts +33 -0
- package/tools/analysis_link_plan.ts +44 -0
- package/tools/analysis_list.ts +48 -0
- package/tools/analysis_search.ts +36 -0
- package/tools/analysis_update.ts +44 -0
- package/tools/plan_approve.ts +31 -0
- package/tools/plan_create.ts +58 -0
- package/tools/plan_get.ts +40 -0
- package/tools/plan_list.ts +37 -0
- package/tools/plan_search.ts +34 -0
- package/tools/plan_update_status.ts +71 -0
- package/tools/session_checkpoint.ts +31 -0
- package/tools/session_end.ts +26 -0
- package/tools/session_start.ts +43 -0
- package/tools/task_create_batch.ts +70 -0
- package/tools/task_list.ts +35 -0
- package/tools/task_next_for_agent.ts +30 -0
- package/tools/task_search.ts +34 -0
- package/tools/task_update_status.ts +37 -0
- package/tsconfig.json +31 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# HTTP Server (Phase 1)
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The optional **ndomo HTTP server** exposes the plugin's SQLite state (plans, tasks, sessions) as a read-only REST API and bridges the OpenCode SDK event stream as Server-Sent Events. Built on [Elysia](https://elysiajs.com/) with `@opencode-ai/sdk` for upstream connectivity.
|
|
6
|
+
|
|
7
|
+
**Phase 1 scope:** read-only REST endpoints + live SSE event relay. No HTTP writes to the DB, no WebSocket, no JWT. Phase 2 will introduce peer-spawning actions.
|
|
8
|
+
|
|
9
|
+
**Feature flag:** `NDOMO_HTTP_ENABLED=false` by default. The server does not bind a port unless explicitly enabled. CLI `--force` flag can override.
|
|
10
|
+
|
|
11
|
+
**Why it exists:** lets external clients (custom dashboards, scripts, browsers) observe ndomo's multi-agent activity without running inside OpenCode. Decouples consumer clients from OpenCode's session lifecycle.
|
|
12
|
+
|
|
13
|
+
## Quickstart
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# 1. Configure (only OPENCODE_SERVER_PASSWORD is mandatory when auth enabled)
|
|
17
|
+
export NDOMO_HTTP_ENABLED=true
|
|
18
|
+
export NDOMO_HTTP_PORT=4097
|
|
19
|
+
export OPENCODE_SERVER_PASSWORD='pick-a-strong-passphrase'
|
|
20
|
+
|
|
21
|
+
# 2. Start the server from your project root (where .ndomo/state.db lives)
|
|
22
|
+
bun run src/cli/serve.ts
|
|
23
|
+
|
|
24
|
+
# 3. Liveness probe (no auth)
|
|
25
|
+
curl -fsS localhost:4097/health
|
|
26
|
+
|
|
27
|
+
# 4. Authenticated read
|
|
28
|
+
curl -fsS -u "user:$OPENCODE_SERVER_PASSWORD" localhost:4097/api/plans
|
|
29
|
+
|
|
30
|
+
# 5. Live SSE stream (Ctrl-C to disconnect)
|
|
31
|
+
curl -N -u "user:$OPENCODE_SERVER_PASSWORD" localhost:4097/api/events
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Configuration
|
|
35
|
+
|
|
36
|
+
All settings have sensible defaults; only `OPENCODE_SERVER_PASSWORD` is required when auth is enabled.
|
|
37
|
+
|
|
38
|
+
| Env var | Default | Purpose |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| `NDOMO_HTTP_ENABLED` | `false` | Master feature flag. Server binds port only when `true`. |
|
|
41
|
+
| `NDOMO_HTTP_PORT` | `4097` | TCP port. Avoids OpenCode default `4096`. |
|
|
42
|
+
| `NDOMO_HTTP_CORS_ORIGINS` | `*` | Comma-separated allowlist. `*` permits all origins (no credentials). |
|
|
43
|
+
| `NDOMO_HTTP_AUTH_REQUIRED` | `true` | HTTP Basic auth gate. Set `false` for local dev. |
|
|
44
|
+
| `OPENCODE_SERVER_PASSWORD` | (unset) | The HTTP Basic password. Server returns `503 auth_not_configured` if required + unset. |
|
|
45
|
+
| `OPENCODE_SERVER_URL` | `http://localhost:4096` | Upstream OpenCode server for SDK event relay. |
|
|
46
|
+
|
|
47
|
+
See [`.env.example`](.env.example) for full annotations.
|
|
48
|
+
|
|
49
|
+
## CLI usage
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
bun run src/cli/serve.ts [options]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Or via the unified CLI:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
bun run src/cli/index.ts serve [options]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
| Flag | Default | Effect |
|
|
62
|
+
|---|---|---|
|
|
63
|
+
| `--port <n>` | config (4097) | Override port (1-65535). |
|
|
64
|
+
| `--no-auth` | auth required | Disable HTTP Basic auth check (still loads `HttpConfig`). |
|
|
65
|
+
| `--cors <origins>` | config (`*`) | Comma-separated CORS origins, e.g. `https://app.example.com,https://admin.example.com`. |
|
|
66
|
+
| `--force` | off | Start even when `NDOMO_HTTP_ENABLED` is not `true`. |
|
|
67
|
+
| `--help`, `-h` | — | Print help and exit. |
|
|
68
|
+
|
|
69
|
+
**Examples:**
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Default config (auth enabled, CORS *)
|
|
73
|
+
bun run src/cli/serve.ts
|
|
74
|
+
|
|
75
|
+
# Local dev with auth off
|
|
76
|
+
bun run src/cli/serve.ts --no-auth --port 4098
|
|
77
|
+
|
|
78
|
+
# Production: explicit CORS allowlist
|
|
79
|
+
bun run src/cli/serve.ts --cors "https://app.example.com,https://admin.example.com"
|
|
80
|
+
|
|
81
|
+
# Bypass feature flag (for ad-hoc runs)
|
|
82
|
+
bun run src/cli/serve.ts --force --port 4099
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Graceful shutdown:** `SIGINT` and `SIGTERM` close the listener and DB cleanly. Exit code `0` on clean shutdown, `1` on startup failure.
|
|
86
|
+
|
|
87
|
+
## API reference
|
|
88
|
+
|
|
89
|
+
All `/api/*` endpoints require HTTP Basic auth (unless `--no-auth`). `/health` is always public.
|
|
90
|
+
|
|
91
|
+
| Method | Path | Auth | Query params | Response |
|
|
92
|
+
|---|---|---|---|---|
|
|
93
|
+
| `GET` | `/health` | no | — | `{ status, version, uptime, timestamp, dbHealthy }` |
|
|
94
|
+
| `GET` | `/api/plans` | yes | `status`, `sessionId`, `limit` (1-500) | `Plan[]` |
|
|
95
|
+
| `GET` | `/api/plans/search` | yes | `q` (required), `limit` (1-100) | `Plan[]` (FTS5) |
|
|
96
|
+
| `GET` | `/api/plans/:id` | yes | — | `Plan` or `404` |
|
|
97
|
+
| `GET` | `/api/tasks` | yes | `planId` (**required**), `status` | `Task[]` (422 if `planId` missing) |
|
|
98
|
+
| `GET` | `/api/tasks/search` | yes | `q` (required), `limit` (1-100) | `Task[]` (FTS5) |
|
|
99
|
+
| `GET` | `/api/tasks/:id` | yes | — | `Task` or `404` |
|
|
100
|
+
| `GET` | `/api/sessions` | yes | `planId`, `limit` (1-100) | `Session[]` |
|
|
101
|
+
| `GET` | `/api/sessions/active` | yes | — | `Session[]` (`endedAt === null`) |
|
|
102
|
+
| `GET` | `/api/sessions/:id` | yes | — | `Session` or `404` |
|
|
103
|
+
| `GET` | `/api/events` | yes | `types` (csv filter) | `text/event-stream` (SSE) |
|
|
104
|
+
|
|
105
|
+
### Health response shape
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"status": "ok",
|
|
110
|
+
"version": "0.1.0",
|
|
111
|
+
"uptime": 12345,
|
|
112
|
+
"timestamp": 1735689600000,
|
|
113
|
+
"dbHealthy": true
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`status` is `"ok"` when the DB responds to `SELECT 1`, otherwise `"degraded"`.
|
|
118
|
+
|
|
119
|
+
### SSE response
|
|
120
|
+
|
|
121
|
+
`GET /api/events` returns `Content-Type: text/event-stream` with:
|
|
122
|
+
|
|
123
|
+
- A `hello` event on connect.
|
|
124
|
+
- Forwarded SDK events as `event: <type>` + `data: <json>` lines (default: all types).
|
|
125
|
+
- Optional filter: `?types=session.idle,session.error` (comma-separated type allowlist).
|
|
126
|
+
- A `: keepalive` comment every **30 seconds** to keep proxies from closing idle connections.
|
|
127
|
+
- The stream closes cleanly on client disconnect (abort signal) or SDK error.
|
|
128
|
+
|
|
129
|
+
If the SDK client is unreachable, the endpoint returns `503 sdk_unavailable` (no stream).
|
|
130
|
+
|
|
131
|
+
**Browser example:**
|
|
132
|
+
|
|
133
|
+
```js
|
|
134
|
+
const es = new EventSource("/api/events", { withCredentials: true });
|
|
135
|
+
// NOTE: EventSource cannot set Authorization. Use a short-lived token via query string (Phase 3).
|
|
136
|
+
es.addEventListener("session.idle", (e) => console.log("session idle:", e.data));
|
|
137
|
+
es.addEventListener("error", (e) => console.error("SSE error:", e));
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
For auth in the browser, Phase 3 will introduce short-lived SSE tokens via query string. Today, use HTTP Basic + a reverse proxy that injects `Authorization`, or call from server-side scripts only.
|
|
141
|
+
|
|
142
|
+
## Security notes
|
|
143
|
+
|
|
144
|
+
**HTTP Basic is plaintext over the wire.** Always run behind TLS in production (reverse proxy with nginx/Caddy, or `--force` over a VPN/localhost). HTTP Basic sends `base64(user:password)` — not encrypted, only obfuscated.
|
|
145
|
+
|
|
146
|
+
**CORS:**
|
|
147
|
+
- `NDOMO_HTTP_CORS_ORIGINS=*` permits any origin to call `/api/*` but **does not** send `Access-Control-Allow-Credentials`. Browser credentialed requests (cookies, `withCredentials`) will be rejected.
|
|
148
|
+
- For a real frontend, set explicit origins: `NDOMO_HTTP_CORS_ORIGINS=https://app.example.com`. Credentials are then allowed.
|
|
149
|
+
|
|
150
|
+
**Security headers** applied to every response (from `SECURITY_HEADERS` in `src/config/schema.ts`):
|
|
151
|
+
|
|
152
|
+
| Header | Value |
|
|
153
|
+
|---|---|
|
|
154
|
+
| `X-Content-Type-Options` | `nosniff` |
|
|
155
|
+
| `X-Frame-Options` | `DENY` |
|
|
156
|
+
| `X-XSS-Protection` | `1; mode=block` |
|
|
157
|
+
| `Referrer-Policy` | `strict-origin-when-cross-origin` |
|
|
158
|
+
| `Content-Security-Policy` | `default-src 'none'; frame-ancestors 'none'` |
|
|
159
|
+
| `Permissions-Policy` | `interest-cohort=()` |
|
|
160
|
+
| `X-Powered-By` | `ndomo` |
|
|
161
|
+
| `Strict-Transport-Security` | `max-age=31536000; includeSubDomains` (when `NODE_ENV=production`) |
|
|
162
|
+
|
|
163
|
+
**Localhost-only recommendation:** the server has no rate limiting and no per-user audit trail. For multi-tenant exposure, put it behind a reverse proxy that enforces auth, rate limits, and request logging.
|
|
164
|
+
|
|
165
|
+
**Password strength:** `OPENCODE_SERVER_PASSWORD` is the only gate. Use a passphrase ≥ 24 chars, or wire in a secret manager. Rotate via env reload + process restart.
|
|
166
|
+
|
|
167
|
+
**503 when password missing:** if `auth.required=true` and `OPENCODE_SERVER_PASSWORD` is unset, `/api/*` returns `503 auth_not_configured` with `WWW-Authenticate: Basic realm="ndomo"`. The `/health` endpoint stays public.
|
|
168
|
+
|
|
169
|
+
**No CSRF:** HTTP Basic is CSRF-immune (browser auto-supplies credentials only for same-origin requests, and cross-origin requests need explicit `withCredentials: true` + matching `Allow-Credentials`). For a frontend served from a different origin, set explicit CORS origins (not `*`).
|
|
170
|
+
|
|
171
|
+
## Troubleshooting
|
|
172
|
+
|
|
173
|
+
**Port already in use (`EADDRINUSE`).**
|
|
174
|
+
Another process bound the port. Check with `lsof -i :4097` or `ss -lntp | grep 4097`. Either kill the conflicting process or use `--port <free>`.
|
|
175
|
+
|
|
176
|
+
**`error: HTTP server is disabled (NDOMO_HTTP_ENABLED is not 'true').`**
|
|
177
|
+
Feature flag off. Either set `NDOMO_HTTP_ENABLED=true` or pass `--force` to the CLI.
|
|
178
|
+
|
|
179
|
+
**`503 auth_not_configured` on every request.**
|
|
180
|
+
`auth.required=true` but `OPENCODE_SERVER_PASSWORD` is empty. Export the env var and restart.
|
|
181
|
+
|
|
182
|
+
**CORS error in browser console: "No 'Access-Control-Allow-Origin' header".**
|
|
183
|
+
The request's `Origin` is not in `NDOMO_HTTP_CORS_ORIGINS` and `*` is not set. Either add the origin to the allowlist or set `*` for development (no credentials).
|
|
184
|
+
|
|
185
|
+
**SSE appears to hang / no events arrive.**
|
|
186
|
+
Two common causes:
|
|
187
|
+
1. **nginx buffering.** SSE responses need `X-Accel-Buffering: no` (the server sets this header, but a proxy might strip it). Configure your proxy: `proxy_buffering off;` (nginx) or `flush_interval -1` (HAProxy).
|
|
188
|
+
2. **OpenCode server unreachable.** The `/api/events` endpoint depends on `OPENCODE_SERVER_URL` (default `http://localhost:4096`). Verify with `curl -fsS localhost:4096/config`. If the OpenCode server is down, the endpoint returns `503 sdk_unavailable`.
|
|
189
|
+
|
|
190
|
+
**Connection drops after ~60s behind a load balancer.**
|
|
191
|
+
The server sends `: keepalive` every 30s. If you still see drops, the proxy is closing idle TCP connections faster than 30s. Either lower the keepalive in your proxy or configure TCP keepalive at the OS level.
|
|
192
|
+
|
|
193
|
+
**`422 validation_error: planId is required` from `/api/tasks`.**
|
|
194
|
+
`planId` is required (unlike `/api/sessions` which makes it optional). Pass `?planId=<uuid>`.
|
|
195
|
+
|
|
196
|
+
## Architecture
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
┌──────────┐ ┌──────────────────────────┐ ┌─────────────────────┐
|
|
200
|
+
│ Browser │ HTTP+SSE│ ndomo Elysia server │ SDK │ OpenCode server │
|
|
201
|
+
│ / curl │────────▶│ :4097 (Phase 1) │───────▶│ :4096 │
|
|
202
|
+
│ / script │ basic │ ┌────────────────────┐ │ HTTP │ ┌───────────────┐ │
|
|
203
|
+
│ │◀────────│ │ securityHeaders │ │◀───────│ │ event.subscribe│ │
|
|
204
|
+
│ │ │ │ corsMiddleware │ │ SSE │ │ session.* │ │
|
|
205
|
+
│ │ │ │ httpBasicAuth │ │ │ │ ... │ │
|
|
206
|
+
│ │ │ │ /health (public) │ │ │ └───────────────┘ │
|
|
207
|
+
│ │ │ │ /api/plans (auth) │ │ └─────────────────────┘
|
|
208
|
+
│ │ │ │ /api/tasks (auth) │ │
|
|
209
|
+
│ │ │ │ /api/sessions │ │ ┌─────────────────────┐
|
|
210
|
+
│ │ │ │ /api/events (SSE) │──────────│ .ndomo/state.db │
|
|
211
|
+
│ │ │ └────────────────────┘ │ SQL │ (plans, tasks, ... )│
|
|
212
|
+
│ │ └──────────────────────────┘ └─────────────────────┘
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
- **Inbound:** `Elysia` handles auth (Basic), CORS, security headers, then dispatches to REST handlers or SSE stream.
|
|
216
|
+
- **REST handlers** are thin adapters — they delegate to `src/db/*.ts` (SQLite) and return JSON. No business logic in routes.
|
|
217
|
+
- **SSE** opens an async iterator on `sdkClient.event.subscribe()`, writes events through `SseWriter`, and cleans up on abort.
|
|
218
|
+
- **Outbound to OpenCode:** `getSdkClient()` creates a singleton `createOpencodeClient` configured with `baseUrl: OPENCODE_SERVER_URL` and `directory: process.cwd()` (project scoping via `x-opencode-directory` header).
|
|
219
|
+
|
|
220
|
+
## Files
|
|
221
|
+
|
|
222
|
+
| File | Purpose |
|
|
223
|
+
|---|---|
|
|
224
|
+
| `src/http/server.ts` | Elysia app builder + listen wrapper |
|
|
225
|
+
| `src/http/auth.ts` | HTTP Basic auth middleware (timing-safe compare, 503 on missing password) |
|
|
226
|
+
| `src/http/middleware/security-headers.ts` | OWASP baseline headers |
|
|
227
|
+
| `src/http/middleware/cors.ts` | CORS preflight + `Allow-Origin` logic |
|
|
228
|
+
| `src/http/routes/health.ts` | `GET /health` |
|
|
229
|
+
| `src/http/routes/plans.ts` | `/api/plans/*` |
|
|
230
|
+
| `src/http/routes/tasks.ts` | `/api/tasks/*` |
|
|
231
|
+
| `src/http/routes/sessions.ts` | `/api/sessions/*` |
|
|
232
|
+
| `src/http/routes/events.ts` | `/api/events` SSE relay |
|
|
233
|
+
| `src/http/sse.ts` | SSE format + writer + keepalive |
|
|
234
|
+
| `src/sdk/client.ts` | OpenCode SDK singleton (with health check) |
|
|
235
|
+
| `src/cli/serve.ts` | CLI entry point with flags |
|
|
236
|
+
| `src/config/schema.ts` | `loadHttpConfig()` + `SECURITY_HEADERS` |
|
|
237
|
+
| `src/http/__tests__/` | Unit + integration tests (528 tests total in repo) |
|
|
238
|
+
| `scripts/smoke-http.sh` | End-to-end smoke (5 curl assertions + headers) |
|
|
239
|
+
|
|
240
|
+
## See also
|
|
241
|
+
|
|
242
|
+
- [`.env.example`](.env.example) — annotated env reference
|
|
243
|
+
- [`docs/database.md`](database.md) — schema for `Plan`, `Task`, `Session`
|
|
244
|
+
- [`README.md`](../README.md) — quickstart + features
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# Installation Guide
|
|
2
|
+
|
|
3
|
+
## Prerequisites
|
|
4
|
+
|
|
5
|
+
- **bun** >= 1.1.0 — [install bun](https://bun.sh)
|
|
6
|
+
- **OpenCode** installed and configured
|
|
7
|
+
- At least one **provider authenticated** in OpenCode (the agents will use provider models)
|
|
8
|
+
|
|
9
|
+
Verify prerequisites:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bun --version # >= 1.1.0
|
|
13
|
+
opencode --version
|
|
14
|
+
opencode config list providers # should show at least one authenticated provider
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Install via curl/wget
|
|
18
|
+
|
|
19
|
+
The install script supports being piped directly from a URL, useful for quick setups, CI/CD pipelines, and ephemeral environments. When piped, the script detects it is running from stdin, clones the repository to `/tmp`, and re-executes itself.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Quick install (interactive, will prompt for provider)
|
|
23
|
+
curl -fsSL https://raw.githubusercontent.com/darrenhinde/OpenAgentsControl/main/install.sh | bash
|
|
24
|
+
|
|
25
|
+
# Non-interactive with provider preset
|
|
26
|
+
curl -fsSL https://raw.githubusercontent.com/darrenhinde/OpenAgentsControl/main/install.sh | bash -s -- --provider=opencode --no-provider-prompt
|
|
27
|
+
|
|
28
|
+
# With budget preset + DCP
|
|
29
|
+
curl -fsSL https://raw.githubusercontent.com/darrenhinde/OpenAgentsControl/main/install.sh | bash -s -- --preset=budget --with-dcp
|
|
30
|
+
|
|
31
|
+
# Install from a fork or dev branch
|
|
32
|
+
curl -fsSL https://raw.githubusercontent.com/darrenhinde/OpenAgentsControl/main/install.sh | bash -s -- \
|
|
33
|
+
--repo=https://github.com/myorg/ndomo-fork \
|
|
34
|
+
--branch=dev \
|
|
35
|
+
--provider=opencode
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The `--repo` and `--branch` flags are only relevant in piped mode; they are ignored when running from a local clone.
|
|
39
|
+
|
|
40
|
+
## Install from Git Clone
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# 1. Clone the repository
|
|
44
|
+
git clone <repo-url> ndomo
|
|
45
|
+
cd ndomo
|
|
46
|
+
|
|
47
|
+
# 2. Install dependencies
|
|
48
|
+
bun install
|
|
49
|
+
|
|
50
|
+
# 3. Run the install script
|
|
51
|
+
./scripts/install.sh
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The install script:
|
|
55
|
+
1. Copies the configuration to `~/.config/opencode/ndomo.json`
|
|
56
|
+
2. Registers the plugin with OpenCode
|
|
57
|
+
3. Verifies all agent definitions in `agents/` are valid
|
|
58
|
+
4. Links the bundled skills (bash-scripting, caveman, grill-me, and 20+ others from `skills/`) to the OpenCode skills directory
|
|
59
|
+
5. Applies the active preset (`presets[PRESET]` from `ndomo.config.json`) to every agent's `model:` and `temperature:` frontmatter.
|
|
60
|
+
6. Registers `ndomo` as a local `file:` dependency in `~/.config/opencode/package.json` and installs it to `~/.config/opencode/node_modules/ndomo/` via `bun install`. This is what allows OpenCode to resolve the plugin from `plugin: ["ndomo", ...]` and register its tools. See [OpenCode plugin docs](https://opencode.ai/docs/es/plugins/) and [custom tools docs](https://opencode.ai/docs/es/custom-tools/).
|
|
61
|
+
7. Symlinks the bundled custom tools (14 DB access tools: `plan_*`, `task_*`, `session_*`) from `tools/` to `~/.config/opencode/tools/`. See [OpenCode custom tools docs](https://opencode.ai/docs/es/custom-tools/).
|
|
62
|
+
|
|
63
|
+
**Note:** The `reasoning_effort` field (optional, `low`|`medium`|`high`|`xhigh`) is supported for reasoning-capable models (DeepSeek, MiMo, OpenAI). Omit it for non-reasoning models.
|
|
64
|
+
|
|
65
|
+
## Development Workflow
|
|
66
|
+
|
|
67
|
+
When developing ndomo (editing agents, skills, config, or source files), use these commands to apply changes:
|
|
68
|
+
|
|
69
|
+
| Command | When to use | What it does |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| `./scripts/install.sh` | First install, after cloning, or after changing agents/skills/config | Copies agents, skills, and config to `~/.config/opencode/`. Installs ndomo package. Always preferred for structural changes. |
|
|
72
|
+
| `bun run dev:bust` | After editing only `src/*.ts` source files (no agent/skill/config changes) | Quick cache bust: removes stale Bun transpiler cache entries referencing ndomo, bumps mtime on all `src/*.ts` files. Does not kill opencode. |
|
|
73
|
+
| `bun run dev:reset` | Same as `dev:bust` but when opencode is running and you need a clean restart | `dev:bust` + kills running opencode processes. Run this after editing source to ensure the next `opencode` picks up fresh code. |
|
|
74
|
+
|
|
75
|
+
### Daily dev loop
|
|
76
|
+
|
|
77
|
+
1. Edit source files in `src/`.
|
|
78
|
+
2. Run `bun run dev:reset` to kill opencode + bust Bun cache + bump mtimes.
|
|
79
|
+
3. Start opencode: `opencode`.
|
|
80
|
+
4. Repeat.
|
|
81
|
+
|
|
82
|
+
### Why symlinks cause stale cache
|
|
83
|
+
|
|
84
|
+
Bun caches transpiled TypeScript modules in `~/.bun/install/cache/` keyed by the **resolved path** of the module. When ndomo is installed via a symlink (e.g., `~/.config/opencode/node_modules/ndomo → /home/nico/ndomo`), the cache key uses the symlink path. Editing source files on the target filesystem does **not** change the symlink path, so Bun serves stale code from cache.
|
|
85
|
+
|
|
86
|
+
The `file:` dep strategy in `install_ndomo_package()` avoids this by creating a real copy in `node_modules` (no symlink → no stale cache). If you ever end up with a symlink install (from an older `install.sh` version or a manual `ln -s`), re-run `./scripts/install.sh` — it detects the symlink, removes it, and reinstalls as a real copy (`scripts/install.sh` lines 247–254).
|
|
87
|
+
|
|
88
|
+
### Manual cache busting (advanced)
|
|
89
|
+
|
|
90
|
+
Run the cache bust script directly for more control:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Dry-run — inspect what would be removed
|
|
94
|
+
./scripts/dev-bust-cache.sh
|
|
95
|
+
|
|
96
|
+
# Apply — remove stale cache entries + bump mtimes
|
|
97
|
+
./scripts/dev-bust-cache.sh --apply
|
|
98
|
+
|
|
99
|
+
# Kill opencode first, then bust cache
|
|
100
|
+
./scripts/dev-bust-cache.sh --apply --kill
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The script (`scripts/dev-bust-cache.sh`):
|
|
104
|
+
1. Optionally kills running opencode processes (`--kill`)
|
|
105
|
+
2. Removes Bun cache entries referencing "ndomo"
|
|
106
|
+
3. Removes Bun cache entries referencing the ndomo source path (`$PROJECT_ROOT/src`)
|
|
107
|
+
4. Touches all `src/*.ts` files to bump mtime (forces re-transpilation)
|
|
108
|
+
|
|
109
|
+
It is **idempotent** — safe to run multiple times. No-op if the cache is already clean.
|
|
110
|
+
|
|
111
|
+
## Install Flags
|
|
112
|
+
|
|
113
|
+
| Flag | Description |
|
|
114
|
+
|---|---|
|
|
115
|
+
| `--provider=ID` | Override the provider prefix for all agents. The model ID is taken from the active preset; only the `provider/` segment of the `model:` field is swapped. Example: preset gives `opencode-go/minimax-m2.7`, `--provider=opencode` rewrites to `opencode/minimax-m2.7`. |
|
|
116
|
+
| `--no-provider-prompt` | Skip the interactive provider prompt. The preset is still applied; no provider prefix override is performed. |
|
|
117
|
+
| `--with-dcp` | Install and configure the DCP plugin (opencode-dynamic-context-pruning) as an optional peer dependency |
|
|
118
|
+
| `--preset=NAME` | Select preset from `config/ndomo.config.json::presets[NAME]`. The preset is the source of truth for agent models at install time. (default: `default`, options: `default`, `budget`) |
|
|
119
|
+
| `--repo=URL` | Override the repository URL (for piped installs from a fork or mirror). Ignored in local clones. |
|
|
120
|
+
| `--branch=NAME` | Override the repository branch (for piped installs from `dev`/`feature/*` branches). Ignored in local clones. |
|
|
121
|
+
|
|
122
|
+
**Environment variable:** `NDOMO_SKIP_PACKAGE_INSTALL=1` — skip the package installation step (`bun install` in `~/.config/opencode/`). Useful if you manage the OpenCode plugin directory manually or if the install step is causing conflicts.
|
|
123
|
+
|
|
124
|
+
Example with all flags:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# Local clone with all flags
|
|
128
|
+
./scripts/install.sh --with-dcp --preset=budget
|
|
129
|
+
|
|
130
|
+
# Piped install with provider, fork, and custom branch
|
|
131
|
+
curl -fsSL https://raw.githubusercontent.com/darrenhinde/OpenAgentsControl/main/install.sh | bash -s -- \
|
|
132
|
+
--repo=https://github.com/myorg/ndomo-fork \
|
|
133
|
+
--branch=dev \
|
|
134
|
+
--provider=opencode \
|
|
135
|
+
--no-provider-prompt
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Provider Override
|
|
139
|
+
|
|
140
|
+
When `install.sh` runs without `--provider` and without `--no-provider-prompt`, it shows the active preset and asks for confirmation. The active preset is the single source of truth for agent models; `--provider=ID` only changes the provider prefix.
|
|
141
|
+
|
|
142
|
+
TTY flow:
|
|
143
|
+
|
|
144
|
+
1. The script prints a table of `(agent, preset model, current provider prefix)` derived from `config/ndomo.config.json`.
|
|
145
|
+
2. It asks: `Apply preset '$PRESET' as configured? [Y/n/override]`
|
|
146
|
+
3. `Y` (or Enter) applies the preset, no prefix override.
|
|
147
|
+
4. `n` skips preset application (warn).
|
|
148
|
+
5. `override` enters the interactive provider picker from models.dev and applies a prefix override to every agent's `model:` field. The model ID comes from the preset; only the `provider/` segment is swapped.
|
|
149
|
+
|
|
150
|
+
To skip the prompt and apply the preset silently:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
./scripts/install.sh --no-provider-prompt
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
To override the provider prefix non-interactively (e.g., use `opencode` instead of `opencode-go` for all agents):
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
./scripts/install.sh --provider=opencode
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The provider override works in piped mode:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
curl -fsSL https://raw.githubusercontent.com/darrenhinde/OpenAgentsControl/main/install.sh | bash -s -- --provider=opencode --no-provider-prompt
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Verify Installation
|
|
169
|
+
|
|
170
|
+
1. Start OpenCode:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
opencode
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
2. Inside the OpenCode session, test agent communication:
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
ping all agents
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Expected output: each of the 22 agents responds with a status confirmation.
|
|
183
|
+
|
|
184
|
+
3. Check that the Foreman is the primary agent:
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
/agent
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Should show `foreman` as the active agent.
|
|
191
|
+
|
|
192
|
+
4. Verify the ndomo DB was created:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
ls -la .ndomo/state.db
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The plugin creates this SQLite database automatically on first load. It
|
|
199
|
+
stores plans, tasks, and sessions. See [docs/database.md](docs/database.md)
|
|
200
|
+
for details.
|
|
201
|
+
|
|
202
|
+
## Uninstall
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
./scripts/uninstall.sh
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Removes:
|
|
209
|
+
- The config file at `~/.config/opencode/ndomo.json`
|
|
210
|
+
- Plugin registration from OpenCode
|
|
211
|
+
- Skill symlinks
|
|
212
|
+
|
|
213
|
+
**Flag:**
|
|
214
|
+
|
|
215
|
+
| Flag | Description |
|
|
216
|
+
|---|---|
|
|
217
|
+
| `--keep-data` | Remove plugin config but preserve memory files in `~/.ndomo/mem/` |
|
|
218
|
+
|
|
219
|
+
## Troubleshooting
|
|
220
|
+
|
|
221
|
+
### bun not found
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
bun: command not found
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Install bun: `curl -fsSL https://bun.sh/install | bash`. Restart your shell after installation.
|
|
228
|
+
|
|
229
|
+
### Provider not authenticated
|
|
230
|
+
|
|
231
|
+
```
|
|
232
|
+
Error: No authenticated provider found
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Configure a provider in OpenCode: `opencode config set provider <provider-name>` and follow the authentication flow. At least one provider must be authenticated before ndomo agents can make API calls.
|
|
236
|
+
|
|
237
|
+
### Agent not responding
|
|
238
|
+
|
|
239
|
+
If `ping all agents` shows no response from one or more agents:
|
|
240
|
+
|
|
241
|
+
1. Verify the config file exists: `ls ~/.config/opencode/ndomo.json`
|
|
242
|
+
2. Validate the config against the schema: `cat ~/.config/opencode/ndomo.json`
|
|
243
|
+
3. Re-run the install script: `./scripts/install.sh`
|
|
244
|
+
4. Check OpenCode logs for model routing errors — the agent's `model` field in the config must match a model available through your authenticated provider.
|
|
245
|
+
|
|
246
|
+
### Permission denied on scripts
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
chmod +x scripts/install.sh scripts/uninstall.sh
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Plugin not loading
|
|
253
|
+
|
|
254
|
+
If OpenCode doesn't detect ndomo as a plugin:
|
|
255
|
+
|
|
256
|
+
1. Ensure `ndomo` is listed in `config/ndomo.config.json` under `plugins`
|
|
257
|
+
2. Check that the package is installed: `ls ~/.config/opencode/node_modules/ndomo/` — if missing, re-run `./scripts/install.sh` or symlink manually: `ln -sfn $(pwd) ~/.config/opencode/node_modules/ndomo`
|
|
258
|
+
3. Verify the plugin entry point (`src/index.ts`) compiles without errors: `bun run build`
|
|
259
|
+
4. Check that the local node_modules were installed: `ls node_modules/ndomo` (or the symlink target)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Integration Guide
|
|
2
|
+
|
|
3
|
+
## opencode-mem (required)
|
|
4
|
+
|
|
5
|
+
opencode-mem is a persistent memory system for OpenCode. It provides a local vector database (SQLite + USearch) with semantic search across sessions.
|
|
6
|
+
|
|
7
|
+
**License:** MIT
|
|
8
|
+
|
|
9
|
+
### What it is
|
|
10
|
+
|
|
11
|
+
opencode-mem stores and retrieves developer knowledge across sessions. ndomo uses it as the primary persistence layer — every agent stores and searches memories before planning or executing tasks.
|
|
12
|
+
|
|
13
|
+
### How ndomo uses it
|
|
14
|
+
|
|
15
|
+
The foreman searches memory before every planning cycle:
|
|
16
|
+
|
|
17
|
+
1. **Project search** — `memory({mode:"search", query, scope:"project"})` retrieves past decisions from the current project.
|
|
18
|
+
2. **Cross-project search** — `memory({mode:"search", query, scope:"all-projects"})` retrieves knowledge from all projects.
|
|
19
|
+
3. **Compressed storage** — before calling `memory({mode:"add"})`, ndomo compresses content using caveman regex patterns (0 LLM tokens).
|
|
20
|
+
|
|
21
|
+
### Tool usage
|
|
22
|
+
|
|
23
|
+
| Mode | Call | Purpose |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| search | `memory({mode:"search", query, scope:"project"})` | Search current project memories |
|
|
26
|
+
| search | `memory({mode:"search", query, scope:"all-projects"})` | Search all project memories |
|
|
27
|
+
| add | `memory({mode:"add", content, topic})` | Store a new memory entry |
|
|
28
|
+
| add | `memory({mode:"add", content, topic, tags})` | Store with tags for filtering |
|
|
29
|
+
|
|
30
|
+
### Web UI
|
|
31
|
+
|
|
32
|
+
opencode-mem includes a web UI at `http://localhost:4747` for browsing and managing memory entries.
|
|
33
|
+
|
|
34
|
+
### Config
|
|
35
|
+
|
|
36
|
+
See [configuration.md](configuration.md#memory-config) for memory-specific settings (`storagePath`, `defaultScope`, `autoCaptureEnabled`, `cavemanCompress`).
|
|
37
|
+
|
|
38
|
+
## DCP (optional)
|
|
39
|
+
|
|
40
|
+
Dynamic Context Pruning (`@tarquinen/opencode-dcp`) is an optional plugin that compresses conversation context by removing low-value tool outputs while preserving critical information.
|
|
41
|
+
|
|
42
|
+
**License:** AGPL-3.0
|
|
43
|
+
|
|
44
|
+
### What it is
|
|
45
|
+
|
|
46
|
+
DCP monitors context token usage and, on request or automatically, prunes low-value content from the conversation window. This extends session life in long-running tasks.
|
|
47
|
+
|
|
48
|
+
### How to install
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
./scripts/install.sh --with-dcp
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
This installs `@tarquinen/opencode-dcp` as an optional peer dependency.
|
|
55
|
+
|
|
56
|
+
### How ndomo uses it
|
|
57
|
+
|
|
58
|
+
The foreman monitors context size:
|
|
59
|
+
|
|
60
|
+
- **~50k tokens** (foreman `minContextLimit`) — suggests `/dcp-compress` to the user.
|
|
61
|
+
- **~100k tokens** (foreman `maxContextLimit`) — invokes `compress` tool automatically at a natural pause point.
|
|
62
|
+
- **If DCP not installed** — falls back to native OpenCode context compaction.
|
|
63
|
+
|
|
64
|
+
### Context thresholds
|
|
65
|
+
|
|
66
|
+
Per-agent thresholds in `dcp_overrides` (only when DCP installed):
|
|
67
|
+
|
|
68
|
+
| Agent | minContextLimit | maxContextLimit |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| scout | 30,000 | 80,000 |
|
|
71
|
+
| scribe | 30,000 | 80,000 |
|
|
72
|
+
| foreman | 50,000 | 100,000 |
|
|
73
|
+
| sage | 50,000 | 100,000 |
|
|
74
|
+
| guild | 50,000 | 100,000 |
|
|
75
|
+
| inspector | 40,000 | 90,000 |
|
|
76
|
+
|
|
77
|
+
Agents without overrides use DCP defaults.
|
|
78
|
+
|
|
79
|
+
### Protected tools
|
|
80
|
+
|
|
81
|
+
The `compress` tool is listed in `protectedTools` — it cannot be pruned from context or disabled by subagents. This ensures DCP can always function when needed.
|
|
82
|
+
|
|
83
|
+
## Caveman + Memory
|
|
84
|
+
|
|
85
|
+
Memories are compressed before storage using regex-based caveman compression (`src/orchestrator/memory-hook.ts`).
|
|
86
|
+
|
|
87
|
+
### Compression rules
|
|
88
|
+
|
|
89
|
+
- **Protected:** Fenced code blocks (`` ``` ``), URLs (http, https, git, ssh).
|
|
90
|
+
- **Removed:** Articles (a, an, the, el, la, los, las, un, una), filler words (just, really, basically, actually, simply, etc.), leading conjunctions (and, but, or, so, then, also), filler phrases ("in order to", "it is important to note that", etc.), excess whitespace.
|
|
91
|
+
|
|
92
|
+
### Regex-only
|
|
93
|
+
|
|
94
|
+
All compression is regex-based — zero LLM tokens consumed for compression. The `COMPRESSION_PATTERNS` array in `memory-hook.ts` defines all patterns, applied sequentially.
|
|
95
|
+
|
|
96
|
+
### Limitations
|
|
97
|
+
|
|
98
|
+
- **Non-English text:** Spanish articles (el, la, los, las, un, una, unos, unas) are included in the pattern set. Other languages are not explicitly handled — their articles and fillers may survive compression.
|
|
99
|
+
- **Bilingual content:** Mixed-language content is compressed with English + Spanish rules only. Additional languages may require new patterns in `COMPRESSION_PATTERNS`.
|
|
100
|
+
- **Preserved content:** Code blocks and URLs are always preserved verbatim, even if they contain patterns that would otherwise be stripped.
|
|
101
|
+
|
|
102
|
+
## Troubleshooting
|
|
103
|
+
|
|
104
|
+
### opencode-mem not found
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
Error: Cannot find module 'opencode-mem'
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Ensure opencode-mem is installed. ndomo lists it as a dependency in `package.json` — `bun install` should install it automatically. If not: `bun add opencode-mem`.
|
|
111
|
+
|
|
112
|
+
### Web UI not accessible
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
curl http://localhost:4747
|
|
116
|
+
Connection refused
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Verify opencode-mem is running. Start it manually: `npx opencode-mem serve`. Default port is 4747.
|
|
120
|
+
|
|
121
|
+
### DCP commands not available
|
|
122
|
+
|
|
123
|
+
If `/dcp-compress` doesn't work:
|
|
124
|
+
|
|
125
|
+
1. Verify DCP is installed: check `~/.config/opencode/node_modules/@tarquinen/opencode-dcp` exists.
|
|
126
|
+
2. Verify DCP is registered as an optional plugin in `config/ndomo.config.json`: `"optionalPlugins": ["@tarquinen/opencode-dcp"]`.
|
|
127
|
+
3. Restart OpenCode after installing.
|
|
128
|
+
|
|
129
|
+
DCP is optional — ndomo functions without it, but long sessions may exhaust context without pruning.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Anti-pattern: Sub-agents marking plans done without DB verification
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-06-21
|
|
4
|
+
**Plan context:** a7e75f8b (foreman-verify-protocol-v1)
|
|
5
|
+
**Severity:** process — schema drift / audit gaps
|
|
6
|
+
|
|
7
|
+
## Incidents
|
|
8
|
+
|
|
9
|
+
### 1. ops-audit-v2
|
|
10
|
+
A sub-agent closed the plan as `completed` after reporting audit findings, but never ran an objective verification pass against the DB state. The completion status became self-certified rather than evidence-based.
|
|
11
|
+
|
|
12
|
+
- **Root cause:** no explicit verify step before `plan_update_status("completed")`.
|
|
13
|
+
- **Impact:** plan marked done while DB consistency checks were unverified; audit trail gap.
|
|
14
|
+
|
|
15
|
+
### 2. craftsman-db-optimize-v1
|
|
16
|
+
Migration v12 was not auto-applied in the execution environment, yet the plan was marked `completed`. This is the second occurrence of a schema migration being skipped while the plan status advanced.
|
|
17
|
+
|
|
18
|
+
- **Root cause:** craftsman trusted implementation-side success (tests green, code merged) without checking `PRAGMA user_version`.
|
|
19
|
+
- **Impact:** schema drift between code expectations and runtime DB; runtime queries could hit missing views/indexes.
|
|
20
|
+
|
|
21
|
+
## Pattern
|
|
22
|
+
|
|
23
|
+
Both cases share the same failure mode: the executing sub-agent acted as both implementer and certifier. It assumed "I wrote it / tests passed" meant "system state matches intent," bypassing objective verification of the actual DB and runtime artifacts.
|
|
24
|
+
|
|
25
|
+
This is a governance anti-pattern: the agent that performs the work cannot also be the sole authority that declares the work correct. Objective verification must come from an independent check of the produced state.
|
|
26
|
+
|
|
27
|
+
## Mitigation
|
|
28
|
+
|
|
29
|
+
- Enforce the **foreman verify protocol** (see `agents/foreman.md`): no plan moves to `completed` until verify checks are recorded.
|
|
30
|
+
- Add a **gate agent** (inspector) as an independent post-plan verification step; see `verify-gate-architecture.md`.
|
|
31
|
+
- Craftsman must not self-certify completion; gate verdict is binary and write-once.
|
|
32
|
+
- For DB-touching plans, require `PRAGMA user_version` and a smoke query against the changed schema as gate checks.
|