feat: multi-host agent integrations, MCP server + test harnesses - #1
Merged
Conversation
Codex repo marketplaces (e.g. hashgraph-online/awesome-codex-plugins) resolve installable plugin bundles by locating .codex-plugin/plugin.json in the repo archive. Add the canonical manifest under packages/codex-adapter/ so the marketplace generator can mirror the adapter bundle (skills/ + scripts/), keep the legacy plugin.json for compatibility, and extend the manifest test to assert the two stay in sync.
…missed
Full live validation on OpenCode CLI 1.2.27 (headless opencode run, real
host-model round-trips, zero ODW-side keys): plugin loads from
.opencode/plugins/, the ultracode trigger fires, and the embedded engine
completed a real multi-agent workflow (93 round-trips, ~190s) ending in a
clean synthesized audit. The live runs + an adversarial review pass found
three critical bugs that mocked tests had certified as correct:
1. noReply echo bug: session.prompt({noReply:true}) means context-only/no
generation and resolves with the USER parts echoed back — every agent
was talking to itself. Removed noReply; the mock now asserts its absence.
2. chat.message recursion storm: the hook fires for ODW's own child-session
prompts (they contain workflow-intent words), nesting runs exponentially.
Guarded by onSessionCreate-registered child-session IDs plus a
synchronously checked-and-set embeddedActive mutex; a losing trigger
gets the native-orchestration directive instead of a silent drop, and
per-run IDs are pruned so the guard set cannot grow unboundedly.
3. Cross-agent history contamination: without noReply, prompts append to
session history, so the round-robin pool leaked each agent's
conversation into the next. invoke() now uses a fresh single-use child
session; deletion is deferred to dispose() because an immediate
per-call delete races OpenCode's internal async work on the session
(live-verified NotFoundError + workflow stall).
Also: ODW_HOST_MODEL (providerID/modelID) forces the embedded agents'
model; ODW_DEBUG=1 gates new diagnostics (import failures, workflow id +
elapsed, first-call SDK payload shape); empty reply with a host-reported
error now throws retryable service_unavailable; .opencode/plugins/odw.js
dogfooding shim loads the workspace plugin in this repo.
Ops notes encoded in CHANGELOG: headless opencode run needs stdin from
NUL on Windows; project plugins load from the PLURAL .opencode/plugins/.
139/139 workspace tests pass; eslint clean.
…, hardened perms - Daemon now generates ~/.odw/daemon.token (0600 on POSIX) at startup and requires `authorization: Bearer <token>` on every route except GET /health (docker healthcheck + CLI preflight depend on it). WS upgrades are checked BEFORE the workflow-existence lookup so unauthenticated callers cannot use 404-vs-101 as a workflow-ID oracle. Comparison is timingSafeEqual over sha256 digests. auth.mode 'none' opt-out in ~/.odw/config.json; forced back to 'token' with a warning when listening on a non-loopback host. - web_fetch gains an SSRF guard: private/link-local/CGNAT v4, v6 incl. IPv4-mapped forms, metadata hostnames, DNS-resolved addresses, and manual redirect following that re-validates every hop (max 5). Opt-out via safety.web.allowPrivateNetwork for local docs servers. - In-memory fixed-window rate limiting on mutating POST routes and WS upgrades (config.rateLimit; sweep interval unref'd and cleared in close()). - Glob patterns are complexity-limited (length + wildcard count) before compiling to RegExp. - setup.mjs chmods config.json to 0600 on POSIX (apiKeys live there) and seeds the token file; docker-compose now binds 127.0.0.1 and the compose file documents ODW_DAEMON_TOKEN passthrough. - All clients (CLI, VS Code extension, OpenCode plugin, Codex/OpenClaw bridges) attach the token from ODW_DAEMON_TOKEN or ~/.odw/daemon.token and surface 401 as needs-token guidance instead of "daemon offline". The two zero-dep bridges stay byte-identical, enforced by a new sync test. - Fix: POST /workflows/exec now passes plan.roles through to the runtime (embedded.js already did) - daemon-run workflows no longer lose role system prompts.
New package packages/mcp-server exposing the daemon over the Model Context Protocol so any MCP client (Claude Desktop, Cursor, Codex, future agents) can plan/run/inspect ODW workflows without a per-tool adapter: - Tools: odw_health, odw_plan (caches the full plan in-process and returns a compact summary - planId, topology, agent count, cost/minutes estimate, hasVerification, script length), odw_run (by prompt or cached planId, with optional result-polling wait), odw_status, odw_result, odw_control, odw_list. - src/index.js is the only file importing @modelcontextprotocol/sdk (low-level Server + stdio transport); tool definitions and handlers live SDK-free in src/tools.js so the test suite runs without the dependency installed. - daemon-client implements the shared auth contract: lazy bearer-token resolution (ODW_DAEMON_TOKEN, then ~/.odw/daemon.token, BOM-strip + trim), token attached to every request, 401 mapped to needs-token guidance and ECONNREFUSED to daemon-offline guidance. - README documents Claude Desktop / Cursor / Codex configuration and env vars (ODW_DAEMON_PORT, ODW_DAEMON_TOKEN, ODW_HOME). The existing per-agent adapters become convenience wrappers; MCP is the universal path.
The model side of agent() can now use tools, not just the orchestration
script. agent({tools:['read_file','run_bash',...]}) runs a provider
tool-call loop instead of a single completion:
- Neutral transcript format owned by agent-queue; anthropic/openai/ollama
each gained callWithTools() mapping it to their native wire shape
(tool_use blocks / chat tool_calls / ollama tools). Structured-output
params are never sent on tool turns (APIs reject forced format + tools);
the JSON schema is enforced on a final tool-free turn with the existing
extractJson + ajv validation, and corrections replay ONLY that final turn
so side-effectful tools never re-execute.
- Usage is summed across every call in the loop (the budget hard-stop and
workflow totals depend on it); per-call timeout reset so one long
run_bash does not spuriously expire a multi-turn loop; mid-loop context
overflow compacts the largest tool result before Layer-2 restart.
- Tool failures (approval gate, unknown tool, malformed provider args)
feed back to the model as error tool-results instead of failing the
agent. Providers without tool support (host/embedded) fall back to the
single-call path with tools stripped - existing examples keep working.
- Runtime bridge whitelists tool names against the new TOOL_MANIFEST,
resolves 'planning'/'default'/'fallback' model-tier aliases from config
for all agents, and bypasses the resume cache for tool-using nodes (a
resumed run must not silently skip side effects).
- verify() accepts testCommand: the command runs in parallel with the
critic fan-out and the gate becomes quorumPassed AND exitCode === 0 -
the compiler/test suite outranks LLM critics. Fail-closed when the tool
bridge cannot run (approval gate). New safety.allowTestCommands
exact-match allowlist lets test commands run headless without opening
arbitrary shell.
Daemon suite: 115 tests passing (84 pre-existing kept green).
An orchestration script can now call replan(prompt, opts) when it discovers
its initial plan no longer fits reality (too many findings, shrinking
budget, a topology that should change). The planner produces a fresh
sub-plan whose script runs inside the SAME workflow:
- Guest primitive replan(prompt, opts) → __host_replan bridge. The sandbox
installs the bridge with a loud-reject default ('replan is not available
in this engine — update odw-daemon') so an old composition root fails
clearly instead of throwing a bare ReferenceError.
- Runtime wires the existing composed planner into createRuntime (shared
with createServer — one definition); the embedded path wires a heuristic
planner (createPlan without an LLM decomposer, the documented degradation
since embedded runs have no planning model).
- The sub-script runs in a fresh QuickJS sandbox that REUSES the same host
bridges, so budget tracking, abort/pause, and agent-node caching all flow
through the parent workflow automatically; the sub-sandbox is disposed in
finally (each is a 256MB WASM runtime).
- Bounds come from the root run's strategy.replan { maxReplans, maxDepth }
(clamped in mergeStrategy to <=5 / <=2); a sub-plan cannot raise its own
limits, and the depth check precedes the count increment so a rejected
nested call costs no budget.
- Resume determinism: LLM planning is non-deterministic, so each replan
call site persists its generated sub-script under a deterministic
checkpoint key sha1(workflowId|replan|depth|count|prompt); a resumed
workflow replays the SAME sub-script (cache-hit) instead of replanning
differently. checkpointByKey added to both the SQLite store and the
in-memory store.
Daemon suite 128 tests, core suite 47 tests — all passing.
Install ODW + ultracode MCP/skill/command bundles for the agents, cursor, gemini, kimi, zed, and opencode hosts, plus root AGENTS.md/GEMINI.md host instructions and .mcp.json config. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the live Azure/Kimi smoke test, the ODW endurance campaign runner/monitor, and the real-projects builder harness (build-until-100). Ignore generated Tests/results/ run output (built demo projects, campaign dumps, monitor status) via .gitignore. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ase duplicates) main's af6080a and 82a8ea7 are pre-rebase versions of a56db5a / 9712009 already on this branch. The branch is a strict superset (+69 / -503 vs main), so this is recorded with -s ours: main is marked merged, the branch tree is kept byte-for-byte, and PR #1 becomes mergeable without reintroducing superseded content. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
These .mcp.json / .cursor/mcp.json / .agents/**/mcp_config.json / .zed/settings.json / .opencode/plugins/odw.mjs files are generated per-checkout by the integration installer (packages/daemon/src/integrations.js), which bakes in an absolute path to the local repo that breaks on every other machine (the doctor even re-validates 'path does not match this checkout'). Untrack them (kept on disk) and gitignore so they regenerate per-machine via 'odw-daemon doctor' — same treatment as generated Tests/results/ output. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ng-capable host Adds a host-agnostic keyless path mirroring the OpenCode embedded seam: createMcpSamplingBackend issues MCP sampling/createMessage back to the connecting client so its OWN authenticated model runs every agent — no ODW key, no daemon. A new engine-hosting server variant (bin: odw-mcp-embedded) probes the client's sampling capability and runs the real engine in-process when present, transparently falling back to the daemon proxy otherwise. Wired behind ODW_MCP_SAMPLING via a single point in mcpServerCommand(), so Codex/Antigravity — which do not advertise sampling today (OpenAI closed codex#5116 to stop subscription-credit arbitrage; Antigravity implements only tools+roots) — auto-upgrade to keyless the instant they ship it, with zero further config. Proven end-to-end: stdio request-during-request reentrancy (sampling fired from inside an in-flight tool call), the embedded engine driven entirely through sampling, and a real sampling-capable MCP client running odw_run keyless with the daemon never touched. 303/303 tests pass, eslint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ors, docs, packaging Audit + review fixes across six areas (full suite 327/327, eslint clean): - doctor: 'odw-daemon doctor all' exits 0 on a clean install — never-installed adapters are skipped (not failed); only daemon-down or a partially-installed adapter fails. - security: the git tool is scoped to a subcommand allowlist (assertSafeGitArgs) so the git_commit approval can never smuggle push --force / reset --hard / -c core.hooksPath RCE. Fails closed; negative-tested. - providers: dynamic 'temperature not supported' detection + bounded retry-without-temperature (replaces the stale opus-4-(7|8) allowlist); 401/403 now yields an actionable auth_failed 'verify your key' message across anthropic/openai/ollama. - docs: document the daemon's loopback + Bearer-token auth (HANDOFF no longer says 'unauthenticated'); unify README/HANDOFF Quick Start on 'npm run setup'; one truthful concurrency number; scrub a personal Azure resource name from Tests/README.md; correct the deep-research example comment. - packaging: engines.node>=20 on cursor/gemini/kimi/zed adapters; coherent single-Anthropic-key starter config; drop the Windows DEP0190 warning in setup.mjs. - lifecycle/portability: clear the stale daemon.pid on Windows stop; broaden the daemon-bridge drift guard to every tracked copy; add POSIX .sh twins for the .ps1 monitor launchers. Deferred as tracked TECH_DEBT: bridge-file dedup, per-run agent cap, OpenCode capability probe, web_search resilience, per-package files fields for npm publish. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Suraj1235
added a commit
that referenced
this pull request
Jul 9, 2026
…ase duplicates) main's af6080a and 82a8ea7 are pre-rebase versions of a56db5a / 9712009 already on this branch. The branch is a strict superset (+69 / -503 vs main), so this is recorded with -s ours: main is marked merged, the branch tree is kept byte-for-byte, and PR #1 becomes mergeable without reintroducing superseded content.
Suraj1235
added a commit
that referenced
this pull request
Jul 9, 2026
feat: multi-host agent integrations, MCP server + test harnesses
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Brings the
feat/security-mcp-tool-loopbranch up tomain(40 commits). Highlights:verify()test gate, and mid-executionreplan()Newly added in this sync (2 commits)
feat: add multi-host agent integration bundles— 40 previously-untracked integration config files (.agents/,.cursor/,.gemini/,.kimi/,.zed/,.opencode/additions,.mcp.json,AGENTS.md,GEMINI.md)test: add odw endurance and real-projects harnesses— 22 harness source files; generatedTests/results/output (748 built demo projects + campaign dumps) is gitignored and stays localNote on divergence
maincurrently has 2 commits not on this branch (af6080a,82a8ea7) — the same change-sets as this branch'sa56db5a/9712009(pre-rebase versions). Please review the merge strategy before merging.🤖 Generated with Claude Code