Move project and host queries into native MCP with host-owned OAuth/context, while retaining memory, skills, actions and publication plugins. Add nearby-model queries and explicit chat/task/learning model defaults. Adopt Responses HTTP/SSE and native steer/queue inbox delivery, preserving scoped session ownership, pending input edits and investigation communication. Disable local-provider discovery: Server /health allowed native vLLM to probe /v1/models on port 8000. A native before/after regression prevents recurrence. Validation: 170 focused Bun tests; service/plugin typechecks; native discovery, provider-discovery and input-inbox smoke checks; no paid model inference.
282 lines
17 KiB
Markdown
282 lines
17 KiB
Markdown
# Repository Guidelines
|
|
|
|
## Scope and Structure
|
|
|
|
This repository is the experimental TJWater supply Agent service. Keep drainage
|
|
data, workflows, layer IDs, and terminology in the drainage repositories.
|
|
Read [README.md](README.md) for setup, configuration, and operational behavior.
|
|
|
|
- `src/server.ts` assembles dependencies and starts the service.
|
|
- `src/routes/` owns HTTP/SSE wiring; keep orchestration thin.
|
|
- `src/context/` owns local and Server-verified request context.
|
|
- `src/product/` owns typed TJWater Server adapters and business contracts.
|
|
- `src/frontendAction/` owns controlled browser actions and their receipts.
|
|
- `src/uiEnvelope/` owns the `agent-ui@1` compatibility protocol and registry.
|
|
- `src/runtime/` owns OpenCode lifecycle, readiness, and workspace binding.
|
|
- `src/sandbox/` and `scripts/landlock_sandbox.py` own Shell enforcement.
|
|
- `src/sessions/`, `src/memory/`, and `src/learning/` retain their
|
|
respective persistence responsibilities.
|
|
- `.opencode/` contains trusted service instructions, plugins, and tools.
|
|
- `.opencode/agent-plugin.json` describes agents, plugins, Skill storage, and MCP
|
|
configuration sources; `.opencode/README.md` explains their management.
|
|
- `tests/` contains focused tests and opt-in native integration scripts.
|
|
|
|
Use TypeScript ESM, two-space indentation, kebab-case module filenames, and
|
|
explicit types at public boundaries. Preserve established service patterns
|
|
before adding new abstractions. Backend access must use typed tools/adapters;
|
|
do not introduce `cli/`, `tjwater_cli`, or a free-form business command bridge.
|
|
The existing approved sandbox Shell is the only local command execution path.
|
|
|
|
## Model Configuration
|
|
|
|
`agent-models.json` is the single model configuration source. An explicit
|
|
`AGENT_MODEL_CONFIG_PATH` selects a complete replacement; relative paths resolve
|
|
from the process working directory. `agent-models.local.json` is ignored by Git
|
|
and the Docker build.
|
|
|
|
- Define provider IDs, `api_url`, API key references, models, aliases, capabilities,
|
|
limits, and role defaults in that file. Group role defaults under `defaults`:
|
|
`chat`, `task` and `learning_gate` are all required explicit nonempty model
|
|
references, with no role inheritance. Keep model definitions under
|
|
`providers.<id>.models` and preserve public `provider/model` references.
|
|
Legacy top-level role keys are rejected.
|
|
- Generate OpenCode definitions and the public chat model menu from it through
|
|
`src/chat/modelConfig.ts` and `src/runtime/opencode2-config.ts`.
|
|
- Do not add independent model defaults in `opencode.json`, agent frontmatter,
|
|
routes, learning code, or environment variables. Legacy `OPENCODE_MODEL`,
|
|
`OPERATIONAL_TASK_MODEL`, and `OPENCODE_MODEL_OPTIONS` no longer select models.
|
|
- Use `model_id` when the Agent-facing name differs from the upstream name;
|
|
preserve existing public references when correcting upstream aliases.
|
|
- Keep real API keys in ignored `.local.env` or deployment environment variables;
|
|
use `{env:NAME}` references in model files. Existing system environment values
|
|
take precedence over `.local.env`.
|
|
- Never return provider URLs, keys, headers, or effective credential settings in
|
|
model menus, validation reports, status endpoints, or logs.
|
|
|
|
After model/provider changes, run `bun run check:models`. It launches an isolated
|
|
OpenCode and validates all non-disabled models, including hidden task/learning
|
|
models, against effective native definitions and upstream `/models` inventories.
|
|
A self-declared `model.list` entry alone is not validation. `disabled` skips a
|
|
model; `selectable: false` only hides it from the chat menu and does not skip it.
|
|
Do not use paid inference as the default configuration check.
|
|
|
|
The checker lives in `.opencode/model-check.ts`, with shared report/target types
|
|
in `src/runtime/model-check.ts` and the command in `scripts/check-models.ts`.
|
|
Preserve safe errors and cancellation. SIGINT/SIGTERM must abort model checking,
|
|
wait for its owned OpenCode process to exit, and clean its temporary directories.
|
|
Unsupported catalogs, absent upstream
|
|
names, authentication failures, and invalid inventories must fail validation.
|
|
Changes require an Agent restart; client mode also requires synchronized external
|
|
OpenCode configuration and plugins.
|
|
|
|
## Runtime and Security
|
|
|
|
Production chat always requires Server-verified Bearer authorization. Personal
|
|
chat uses `/api/v1/access-context` without a project; project chat also requires
|
|
`X-Project-Id` and `/api/v1/agent-auth-context`. Personal chat must never acquire
|
|
project business credentials or fall back from failed verification to local
|
|
identity. Use a user-specific personal key for memory and chat storage. Preserve
|
|
actor/scope/user ownership checks on session create,
|
|
read, actions, and MCP status. Development-only local context does not grant
|
|
business-data access. Never accept a tool-supplied identity, project scope,
|
|
authorization header, or arbitrary Server URL.
|
|
|
|
Complete the OS sandbox probe, OpenCode health check, warmup session, business
|
|
tool discovery, enabled-model validation, workspace-root check, and MCP
|
|
configuration check before HTTP starts listening. A failure must fail startup.
|
|
Preserve `/health` fields `ready`, `warmed_up`, and `sandbox`.
|
|
|
|
Use the V2 SDK and trusted plugin registration. A cold Location may need plugin
|
|
initialization before its tool/model catalog is usable. Verify actual tool IDs;
|
|
a version string or MCP connection status alone does not prove availability.
|
|
Keep native call IDs and the existing frontend SSE contracts.
|
|
|
|
Client mode requires the same stable `AGENT_INTERNAL_TOKEN` in both processes,
|
|
the external OpenCode server password, and trusted assets loaded through
|
|
`serviceOpencodeConfig`. Its absolute `TJWATER_AGENT_WORKSPACE_ROOT` must match
|
|
the Agent workspace root. Prefer `bun run start:integration:isolated` when an
|
|
existing user service must remain available. Stop only processes started for the
|
|
current task; do not terminate unrelated services.
|
|
The isolated integration launcher must pin `OPENCODE_DB` under its own data
|
|
root rather than inherit a caller's database override. After that environment
|
|
stops, its XDG cache may be regenerated; preserve native data, snapshots,
|
|
chat workspaces and audit records unless their deletion is explicitly requested.
|
|
|
|
Preserve static sensitive-path denials in `opencode.json`, explicit approval for
|
|
Shell/file edits, and disabled subagents for ordinary chat, task collaboration,
|
|
and learning. Only the trusted active `task_execution` coordinator may launch
|
|
`investigation-reader`, at most two foreground children and one level deep.
|
|
Readers inherit only task-scoped query access, never report/action publication,
|
|
Shell, file, or recursive delegation permissions. Do not replace enforcement with prompt
|
|
instructions. The Shell replacement must retain Landlock/seccomp, credential
|
|
isolation, cancellation, process-group cleanup, and denied alternate host Shell
|
|
entry points. Code Mode must not bypass these controls.
|
|
|
|
## Workspaces and Persistence
|
|
|
|
`CONVERSATION_WORKSPACE_ROOT` defaults to `data/session-workspaces/`.
|
|
`src/runtime/conversationWorkspace.ts` owns validated directory paths and native
|
|
bindings. Persistent sessions use `actor-*/personal/session-<sha256>/` or
|
|
`actor-*/project-*/session-<sha256>/`. Each contains host-owned `chat/` and a
|
|
`workspace/` child with writable `files/`, `tmp/`, and `.cache/`.
|
|
|
|
`src/sessions/workspace-store.ts` owns chat directories and immutable ownership
|
|
files. Inject the same store into metadata, UI state and transcript stores in
|
|
`src/server.ts`. Keep `data/session-metadata/` as the owner/history index; new
|
|
messages, transcripts and learning state go only to `chat/`. Preserve full identity checks on
|
|
reads and lists, session filename collision rejection, and serialized ownership
|
|
creation. Scope changes in the frontend start a new conversation and use
|
|
independent history caches; they do not silently reassign existing sessions.
|
|
|
|
Bind OpenCode only to `workspace/`. Its file tools must check the native session
|
|
hash as well as canonical paths; Shell cannot reach sibling `chat/`. Chat
|
|
directories are `0700`, record files `0600`; never place transcripts or bearer
|
|
credentials in model-writable directories. Unscoped `conversation-<uuid>`
|
|
directories are retained for temporary probes/generation and legacy migration.
|
|
|
|
Keep canonical-path and symlink checks, per-session access restrictions, native
|
|
session directory bindings, and empty independent workspaces for new branches.
|
|
Do not copy source code, credentials, or other sessions into a workspace.
|
|
|
|
Generate scoped native read/edit permissions for managed workspaces without
|
|
opening all of `data/`. Preserve sensitive-path denials and later administrator
|
|
rules; the plugin must still limit file access to the current session. Keep Shell
|
|
execution approval and kernel restrictions. Prefer session-relative file paths.
|
|
|
|
Legacy `.agent-workspaces/`, integration `conversation-workspaces/`, and flat
|
|
conversation directories are moved before continued execution, preserving files, then rebound
|
|
through the native Session API. Failed binding updates must be retryable after a
|
|
filesystem move, and existing destination directories must never be overwritten.
|
|
Read-only MCP status queries may resolve moved files without moving sessions.
|
|
|
|
At startup copy legacy UI states into the indexed owner's chat directory and
|
|
transcripts into their recorded actor/scope directory, and indexed learning state
|
|
into `chat/learning.json`. Archive original bytes under
|
|
`PERSISTENCE_BACKUP_DIR/legacy-chat/` only after verifying the backup. Keep a
|
|
versioned migration manifest; never overwrite new records during repeated migration or
|
|
guess an owner for an unindexed UI state. Legacy UI/transcript/learning storage
|
|
environment variables select migration sources, not a second active record store.
|
|
Verified migration backups may be removed when explicitly requested; preserve
|
|
the migration receipt and active scoped records. Do not recreate empty legacy
|
|
UI/transcript/learning directories when workspace storage is configured.
|
|
|
|
Workspaces contain persistent session files. They currently have no expiration
|
|
cleanup. Deleting a session marks its index and host-owned chat directory before
|
|
cancelling work, then removes native history, chat content, learning state,
|
|
calculation files and that session's legacy records/backups. Keep minimal owner
|
|
and deletion markers; queued writes and migration must not revive deleted chats.
|
|
Cleanup errors must return a retryable response, never success with unfinished
|
|
cleanup. Archived chats retain their records. Session lists use a scoped
|
|
in-process index updated by the owning store; one Agent process owns writes to
|
|
each data root. Do not mutate index files behind that cache.
|
|
Do not clean session files as build artifacts or describe them as automatically
|
|
disposable. Startup probes, warmup, title and learning helpers, and failed creation
|
|
clean their own native sessions and temporary workspaces. Cleanup must validate
|
|
scope, canonical paths and session hash and wait for native execution to stop.
|
|
Changing the root requires preserving directory bindings and associated native
|
|
session data; do not silently move or delete existing user files.
|
|
|
|
Deployment must preserve Agent data, Skill files, and the OpenCode session
|
|
database. With the default root, mounting `/app/data` also preserves session files;
|
|
custom workspace roots require their own persistent mount.
|
|
|
|
Independent result-reference storage, its TTL loop, and the result-ref read API
|
|
have been retired after checking that no business producer or consumer uses them.
|
|
Store file results in a session workspace and reference them by session ID plus
|
|
relative path. Do not replace scoped access checks with arbitrary host paths or
|
|
reintroduce a separate result-reference store without an actual consumer.
|
|
|
|
## MCP and Business Boundaries
|
|
|
|
Project and host read-only query tools use the native `mcp_tjwater` MCP connection.
|
|
Public web search uses OpenCode's native `websearch`; do not restore a Server
|
|
search endpoint, Bocha adapter, or duplicate MCP web-search tool.
|
|
Native `browser_*` automation tools are disabled. Preserve the separate TJWater
|
|
presentation tools, frontend action receipts, task presentation events, and
|
|
the complete `memory_manager` / `skill_manager` plugin interfaces.
|
|
Do not restore standalone plugin query registrations, legacy typed query tools,
|
|
or custom internal HTTP query routes. `memory_manager` and `skill_manager` retain
|
|
their complete read/write plugin interface; publication, actions, messaging writes,
|
|
map presentation and `todowrite` also remain plugins. OpenCode supplies native
|
|
session metadata on MCP calls; authenticate the private loopback channel before
|
|
resolving host ownership or verified direct task readers. Keep product OAuth
|
|
credentials and context handles in the Agent host. Project queries fail explicitly
|
|
when the upstream Server MCP is disabled; never fall back to legacy query bridges.
|
|
|
|
|
|
Keep the `.opencode/agent-plugin.json` descriptor and package descriptions aligned
|
|
with actual IDs and paths when agent/plugin assets change. The descriptor is a
|
|
documentation index, not a runtime configuration file or native OpenCode plugin
|
|
manifest. Do not duplicate model definitions, Skill inventories, MCP connections,
|
|
or credentials in it. Use the established configuration builder and source files.
|
|
Skill storage is shared by the deployment instance; do not describe it as isolated
|
|
per user or project, and preserve the existing validated persistence workflow.
|
|
|
|
Configure external MCP only through administrator-owned `mcp.servers` in
|
|
`opencode.json` or `OPENCODE_CONFIG_CONTENT`; server names use the `mcp_` prefix.
|
|
Preserve exact read-only tool allow rules and approval outside that allowlist.
|
|
Interactive chat follows the selected round mode: `request` asks the user;
|
|
`always` automatically answers pending native permission requests, including
|
|
Shell, file edits and MCP, with `once`. Do not persist automatic grants into later
|
|
rounds or bypass explicit denials, sandboxing, business authorization, read-only
|
|
background task access, or disabled MCP in learning review.
|
|
Broad wildcard allows and server `readOnlyHint` values are not read-only grants.
|
|
Apply the same policy to Code Mode and generic MCP resource access.
|
|
|
|
First-party product MCP OAuth discovers protected-resource and OAuth/OIDC
|
|
metadata, pins the configured resource and issuer, and sends the canonical
|
|
`resource` on both grants. Keep public discovery credential-free, bounded and
|
|
redirect-free. Send client secrets only to a validated issuer's advertised token
|
|
endpoint; preserve Basic/post authentication, metadata invalidation on 401, and
|
|
explicit failure for an incompatible resource indicator. Do not restore a
|
|
hardcoded Keycloak token path or an audience-only fallback.
|
|
|
|
Local stdio MCP is a trusted host process, not a sandbox Shell process. Preserve
|
|
its explicit environment filtering and never forward Agent/OpenCode credentials.
|
|
Return only sanitized server status and actual tool IDs to the browser.
|
|
|
|
Keep business state authoritative in Server. Background execution uses persisted
|
|
`task_id`, `run_id`, and `execution_request_id`, with explicit claim, heartbeat,
|
|
and complete/fail transitions. Plans, reports, browser receipts, and final text
|
|
are distinct from execution completion. See [docs/execution-plan.md](docs/execution-plan.md)
|
|
and [docs/agent-ui-protocol.md](docs/agent-ui-protocol.md).
|
|
|
|
## Commands and Verification
|
|
|
|
Run commands from this repository with Bun:
|
|
|
|
```bash
|
|
bun install
|
|
bun run dev
|
|
bun run start
|
|
bun run check
|
|
bun run check:models
|
|
bun run start:integration:isolated
|
|
```
|
|
|
|
`bun run check` type-checks both the service and `.opencode`; this repository has
|
|
no separate lint command. Run focused tests for the changed behavior. For model
|
|
and readiness changes, the relevant suite is:
|
|
|
|
```bash
|
|
bun test tests/chat/models.test.ts tests/opencode/modelCheck.test.ts tests/runtime/opencode.test.ts tests/runtime/modelCheckCommand.test.ts tests/frontendAction/callIdPlugin.test.ts
|
|
```
|
|
|
|
Do not over-test reversible low-impact edits. Documentation-only changes need
|
|
local link/path and command validation plus `git diff --check`, without starting
|
|
services or calling providers. Tests must not depend on mutable `data/` files.
|
|
Use opt-in native smoke scripts only when actual integration behavior needs
|
|
verification; configuration/catalog checks must not invoke model inference.
|
|
|
|
## Repository Hygiene and Delivery
|
|
|
|
Never commit secrets, private environment files, generated runtime data, logs,
|
|
dependencies, session workspaces, session dumps, or generated result files. Avoid
|
|
printing private configuration contents while inspecting a workspace.
|
|
|
|
Preserve unrelated dirty and untracked work. Keep `README.md` useful to people:
|
|
configuration locations, runnable setup steps, model checks, workspace retention,
|
|
and current capabilities. Update it when those operational behaviors change.
|
|
Use concise Conventional Commit subjects and scope commits to requested files.
|
|
Do not push or deploy without authorization for the current task.
|