Skip to content

feat: multi-host agent integrations, MCP server + test harnesses - #1

Merged
Suraj1235 merged 44 commits into
mainfrom
feat/security-mcp-tool-loop
Jul 1, 2026
Merged

feat: multi-host agent integrations, MCP server + test harnesses#1
Suraj1235 merged 44 commits into
mainfrom
feat/security-mcp-tool-loop

Conversation

@Suraj1235

Copy link
Copy Markdown
Owner

Summary

Brings the feat/security-mcp-tool-loop branch up to main (40 commits). Highlights:

  • Security pack — bearer-token auth (HTTP + WS), SSRF guard, rate limiting, hardened permissions
  • odw-mcp-server — universal MCP integration layer for the daemon, with a real sub-agent tool-use loop, a verify() test gate, and mid-execution replan()
  • Multi-host agent integrations — ODW + ultracode MCP / skill / command bundles for Claude, OpenCode, Cursor, Gemini, Kimi, Zed, Codex, and Antigravity, plus a universal integration doctor and host smoke tests
  • Test harnesses — live Azure/Kimi smoke test, ODW endurance campaign runner/monitor, and the real-projects builder (build-until-100)

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; generated Tests/results/ output (748 built demo projects + campaign dumps) is gitignored and stays local

Note on divergence

main currently has 2 commits not on this branch (af6080a, 82a8ea7) — the same change-sets as this branch's a56db5a / 9712009 (pre-rebase versions). Please review the merge strategy before merging.

🤖 Generated with Claude Code

Suraj1235 added 30 commits June 11, 2026 00:18
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.
Suraj1235 and others added 14 commits July 1, 2026 04:20
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
Suraj1235 merged commit 32a2036 into main Jul 1, 2026
0 of 8 checks passed
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant