Files
open-multi-agent/AGENTS.md
T
Jack Chen 6815e891e9 docs(agents): discover instead of enumerate, cover release-bot and egress (#582)
The subsystem directory list and the subpath export list in AGENTS.md had drifted since #457: approval/, journal/, utils/ and the /shell entry point landed without either sentence being updated. Replace both lists
with discovery commands and name the things that do have to move together when an entry point changes: the exports declaration, the backing source file, and the required entry-point list in the CI package job.

Add a Release bot row to the validation matrix, add docs/egress-policy.md to the subsystem table, and extend the adapter checklist with the egress decision that prepareProviderBaseURL forces on every provider.
2026-09-05 13:03:54 +08:00

15 KiB

AGENTS.md

This file is the canonical working agreement for AI agents editing this repository. Keep durable execution rules, high-risk invariants, and verification expectations here. Keep conceptual architecture and subsystem behavior in packages/core/README.md and docs/ so this file stays concise and accurate.

Repository map

This is a private npm-workspaces root. Run the commands below from the repository root unless a workspace-specific command is shown.

Workspace Purpose Main paths
@open-multi-agent/core Multi-agent orchestration framework and oma CLI packages/core/src/, packages/core/tests/, packages/core/examples/
@open-multi-agent/otel Optional OpenTelemetry adapter; versioned independently from core packages/otel/src/, packages/otel/tests/
create-oma-app Published scaffolder and starter templates packages/create-oma-app/src/, packages/create-oma-app/templates/, packages/create-oma-app/tests/
@open-multi-agent/release-bot Private OMA-powered release planning and deterministic publication automation; never published packages/release-bot/src/, packages/release-bot/tests/, .github/workflows/release-bot.yml, .github/workflows/publish.yml

Root-level README.md, docs/, .github/, and scripts/ apply across workspaces. Paths in this file are repository-relative; do not assume an unprefixed src/ or tests/ means the workspace you intend.

Commands

npm run build          # Compile every workspace
npm run lint           # Type-check every workspace
npm test               # Run unit tests in every workspace (no API keys required)
npm run test:scaffold  # End-to-end create-oma-app scaffold smoke test
npm run test:example-catalog  # Validate example catalog metadata and coverage

npm run dev            # Watch-mode compilation for @open-multi-agent/core
npm run test:watch     # Core Vitest watch mode
npm run test:coverage  # Core coverage suite
npm run test:e2e       # Core provider E2E; requires real API keys

node packages/core/dist/cli/oma.js help  # After build; `oma` when installed from npm

Examples and core E2E tests may require real provider credentials. Unit tests mock provider SDKs and external processes and should run without network access or API keys.

Working rules

  • Use strict TypeScript and ESM imports with .js extensions: import { X } from './foo.js' even when the source file is TypeScript. There is no eslint/prettier configuration; match nearby style.
  • Change source files, tests, templates, or docs rather than generated dist/ output.
  • Add or update tests for behavior changes. Update user-facing docs and examples when public behavior changes, or state why they are not applicable.
  • Keep dependency ownership explicit. Core must remain importable and runnable without optional integrations.
  • Pin what stays in the repository; keep ranges on what a consumer installs. Every devDependencies entry and the private release bot's dependencies are pinned exactly, and the root .npmrc sets save-exact=true so a new one arrives pinned. The published blocks stay semver ranges — core's dependencies, every peerDependencies, and otel's @open-multi-agent/core — because an exact version there overrides a consumer's own resolution and blocks their npm audit fix until we cut a release. save-exact will pin a new dependency added to one of those blocks, so widen it by hand; the package job in .github/workflows/ci.yml fails when one is left exact.
  • Add optional provider SDKs as peer dependencies and load them lazily with dynamic import(). Do not maintain a fixed dependency or adapter count in documentation.
  • OpenTelemetry APIs, SDKs, semantic-convention packages, and exporters belong in @open-multi-agent/otel, never in the core root import. The application owns its tracer/provider lifecycle unless an API explicitly says otherwise.
  • Treat docs/ as the source of truth for subsystem behavior. Keep this file to rules and concise invariants; link to docs instead of copying long explanations.
  • The package READMEs are landing pages, not guides. A new feature earns at most one short paragraph plus a link to its docs/ page in packages/core/README.md; anything that needs more belongs in docs/. Update packages/core/README_zh.md in the same change or the translation drifts. Per-contributor credits live in CONTRIBUTORS.md, not in a README.
  • Follow conventional commits when a commit is requested. Reference a PR or issue when one exists. The full contribution flow is in .github/CONTRIBUTING.md.
  • Publishing spans three packages with independent version tracks, a fixed publish order, and template pins that must move with the core version. Do not infer any of that from this file: .github/RELEASING.md is the source of truth.
  • Managed Git worktrees share repository metadata. Never repair access by changing .git ownership or permissions. Use the current tool's normal Git and approval flow.
  • Keep worktree dependencies isolated. Run npm ci in each worktree and never symlink node_modules from another checkout.

Validation by change type

Always inspect the focused diff and run git diff --check. Run the smallest relevant checks first, then broaden in proportion to the change.

  • Documentation-only: git diff --check; tests are not required unless commands, generated artifacts, or executable examples changed.
  • Core code: relevant core tests, then npm run lint -w @open-multi-agent/core and npm run test -w @open-multi-agent/core. Run npm run build -w @open-multi-agent/core when public entry points, declarations, package output, or CLI output may be affected.
  • OpenTelemetry adapter: relevant tests, then npm run lint -w @open-multi-agent/otel, npm run test -w @open-multi-agent/otel, and build when package output or public types may be affected.
  • Release bot: relevant tests, then npm run lint -w @open-multi-agent/release-bot and npm run test -w @open-multi-agent/release-bot; both build core first through their pre scripts, so they also catch a core API change that breaks the bot. Workflow changes follow .github/RELEASING.md.
  • Scaffolder or templates: relevant tests, then npm run lint -w create-oma-app, npm run test -w create-oma-app, and npm run typecheck:template -w create-oma-app. Run npm run test:scaffold -w create-oma-app when generated-project behavior changes.
  • Examples or catalog metadata: npm run test:example-catalog and npm run lint -w @open-multi-agent/core, which type-checks packages/core/examples/ alongside src/ via packages/core/tsconfig.lint.json; add a runnable example smoke test when executable behavior changes. That config excludes examples/fixtures/ and the self-contained example projects that carry their own package.json/tsconfig.json; add a new example of that shape to its exclude list, since core lint cannot resolve a package-name import of @open-multi-agent/core before dist exists.
  • Cross-workspace or dependency changes: npm run lint, npm test, and npm run build; add the package/import/template smoke checks relevant to the changed surface.
  • Provider E2E: run only when the changed surface requires real-provider verification and the necessary credentials are safely available. Never expose credential values.

Before finishing, report every command run and its outcome. If a relevant check was skipped or could not run, state the reason and residual risk. CI remains the source of truth for the complete Node 20/22/24 pre-merge matrix.

Architecture entry points

packages/core/src/ is organized one directory per subsystem; run ls packages/core/src/ to locate code rather than relying on any list of those directories, and use the linked docs below for behavior and contracts. Inside orchestrator/, orchestrator.ts is the entry point that the peer modules in that directory hang off.

The published surface is packages/core/src/index.ts plus the subpath entry points declared under exports in packages/core/package.json; list them with jq '.exports | keys' packages/core/package.json rather than relying on any copy of that list. When adding or renaming one, keep that declaration, the backing source file, and the required entry-point list in the package job of .github/workflows/ci.yml in sync.

OpenMultiAgent exposes three primary modes: runAgent() for a one-shot agent, runTeam() for coordinator-generated task DAGs, and runTasks() for explicit dependency pipelines. See the core package README for the conceptual architecture.

Non-obvious invariants

These constraints span multiple files and can cause behavioral or compatibility bugs when missed:

  • Tool errors are values: tool failures are returned as ToolResult with isError: true; they do not throw through the runner. LLM API failures propagate. Task failures cascade to dependents while independent tasks may continue.
  • Built-in tools are default-deny: a built-in is granted only through AgentConfig.tools, toolPreset, or OrchestratorConfig.defaultToolPreset. Registered custom/runtime tools are granted by registration but still honor disallowedTools. Ungranted calls return an error rather than executing. See tool configuration.
  • Per-call gates run below grants: onToolCall runs after Zod validation and before execution. Denial returns an error ToolResult; throwing or invalid gates fail closed. Ungranted tools never reach the gate. AgentConfig.onToolCall overrides the orchestrator default. The optional shell classifier is exported from /classifiers.
  • Delegation is orchestration-only and separately granted: delegate_to_agent exists only in runTeam() and runTasks() workers and must be explicitly granted. Standalone runAgent() and the simple-goal short circuit do not register it. Self-delegation, cycles, unknown targets, excess depth, and unavailable pool capacity are rejected; delegated usage counts against the parent budget.
  • Filesystem tools are sandboxed; bash is not: filesystem built-ins resolve paths and symlinks within AgentConfig.cwd or OrchestratorConfig.defaultCwd, defaulting to <cwd>/.agent-workspace. null disables that sandbox and process.cwd() widens it. Shell execution has no equivalent filesystem boundary.
  • Reasoning is dropped unless opted in: provider-native reasoning blocks that the target adapter cannot echo are discarded unless preserveReasoningAsText is enabled. Inline <thinking> text is never reconstructed into a signed reasoning block. See context management.
  • Native tool calls win: the local-model text extractor runs only when a server emits no native tool calls.
  • External backends replace the LLM runner: process and ACP backends perform their own work in cwd; the runner tool loop, sandbox, and context strategy do not apply, while queue, scheduler, memory, and budget behavior remain backend-agnostic. ACP permissions default to auto-approve and its cumulative context usage is recorded as per-turn deltas when updates exist. See external agents.
  • Telemetry is not execution state: losing telemetry must not roll back a durable run. Deleting traces must not delete checkpoints, shared memory, or remotely exported OpenTelemetry data. Observability delivery/export failures do not become agent, task, or run failures. See observability.
  • Evaluation observes results: offline evaluation is separate; online sampling, scoring, and persistence are best-effort and isolated from the business response. Scorer failures become scorer_error and are excluded from score aggregates rather than converted to zero. See evaluation.
  • Secrets and PII are redacted best-effort: traces, shell output, and dashboard payloads pass through redaction, but callers must still avoid deliberately persisting or logging secrets.

Subsystem documentation

This table is a curated selection, not a complete index. docs/ contains more files than are listed here. Run ls docs/ for the full set before concluding that a topic is undocumented.

Topic Source of truth
Context strategies and reasoning round-tripping docs/context-management.md
Tool grants, presets, sandbox, delegation, MCP, and gates docs/tool-configuration.md
Providers, environment variables, local servers, and AI SDK docs/providers.md
LLM egress policy, enforcement matrix, and fail-closed surfaces docs/egress-policy.md
Shared memory and custom stores docs/shared-memory.md
Checkpoint and restore docs/checkpoint.md
Run event journal, lineage, and the model-visible boundary docs/run-journal.md
Tracing, stores, progress, Run Viewer, privacy, and OpenTelemetry docs/observability.md
Evaluation, scorers, stores, reports, sampling, and gates docs/evaluation.md
CLI commands and JSON schemas docs/cli.md
Process and ACP backends docs/external-agents.md
Runtime footprint, network scope, state locations, and air-gapped deployment docs/self-hosting.md
Routing, scheduling, consensus, recovery, and replay model-routing, execution-routing, task-scheduling, consensus, adaptive-recovery, plan-replay

Adding an LLM adapter

Implement LLMAdapter.chat() and LLMAdapter.stream(), add the provider to SupportedProvider, and register it in packages/core/src/llm/adapter.ts through dynamic import() so unused SDKs never resolve. OpenAI-compatible providers should accept baseURL and reuse openai-common.ts. Decide how the provider behaves under egressPolicy in prepareProviderBaseURL: an OpenAI-compatible provider needs an entry in PROVIDER_DEFAULT_BASE_URLS or a provider endpoint environment variable so its origin can be resolved and checked, and a provider whose SDK opens connections OMA cannot guard must be declared unsupported so it fails closed. Add the outcome to the enforcement matrix in docs/egress-policy.md. Add focused adapter tests and update docs/providers.md without introducing a hard-coded provider count.