Skip to content

feat: add Antigravity subscription CLI as a provider#356

Open
Sanjay Ramadugu (sanjay3290) wants to merge 4 commits into
langchain-ai:mainfrom
sanjay3290:feat/antigravity-agent-cli
Open

feat: add Antigravity subscription CLI as a provider#356
Sanjay Ramadugu (sanjay3290) wants to merge 4 commits into
langchain-ai:mainfrom
sanjay3290:feat/antigravity-agent-cli

Conversation

@sanjay3290

Copy link
Copy Markdown

Summary

Adds an antigravity provider that runs OpenWiki documentation jobs through the local Antigravity CLI (agy) using a subscription/session login instead of a metered API key.

This builds on the agent-CLI foundation introduced for Grok Build (see also #280): OpenWiki spawns the vendor binary headlessly, streams stdout, and reuses the CLI session for interactive follow-ups.

What changed

  • antigravity agent-cli provider (agy binary, override via OPENWIKI_ANTIGRAVITY_BINARY)
  • Plain-text stream format on the shared agent-CLI runner (Antigravity print mode is not NDJSON)
  • Session resume via --conversation <id>, recovered from a per-run --log-file
  • Model presets use exact agy models display strings (e.g. Gemini 3.5 Flash (Medium)); isValidModelId now allows spaces/parentheses for those names
  • Docs + tests for adapter, provider config, and text-stream runner path

Config

Env var Purpose
OPENWIKI_PROVIDER=antigravity Select the provider
OPENWIKI_ANTIGRAVITY_BINARY Binary path when agy is not on PATH
OPENWIKI_MODEL_ID Exact agy models display name (default Gemini 3.5 Flash (Medium))
OPENWIKI_AGENT_CLI_TIMEOUT_SECONDS Run / print-timeout budget (default 1800)

Runtime flags

agy -p <prompt> --dangerously-skip-permissions --mode accept-edits \
  --print-timeout <n>m<n>s --log-file <tmp> --add-dir <cwd> \
  [--model <display name>] [--conversation <id>]

Testing

  • pnpm run typecheck — clean
  • pnpm run lint:check — clean
  • Full suite: 296/296 tests pass

Notes

  • Local/subscription only — not a drop-in for CI examples that expect API keys
  • Model ids must match agy models verbatim; near-misses can no-op with empty stdout
  • Includes the agent-CLI infrastructure also present in feat: add Grok Build subscription CLI as a provider #280 (Grok Build). Happy to re-split or rebase if preferred.

Review

Brace Sproul (@bracesproul) Colin Francis (@colifran) — would appreciate a review when you have a chance.

Add a `grok-build` provider that runs documentation jobs through the
local Grok Build CLI (`grok`) using a subscription login instead of a
metered API key. OpenWiki spawns the binary headlessly with
`--always-approve --output-format streaming-json`, streams and coalesces
its NDJSON events, and reuses the CLI's own session for follow-ups.

- Model provider config becomes a discriminated union (api | agent-cli)
  so agent-CLI providers carry a binary/install hint instead of an API
  key; onboarding, credential setup, and startup checks skip the key
  collection for them.
- New engines module: a generic CLI runner (temp prompt file, detached
  process group with SIGTERM/SIGKILL timeout, stream parsing) plus a
  Grok Build adapter and streaming-json parser.
- Extract shared prepareAgentRun/finalizeAgentRun so the CLI and
  DeepAgents run paths share snapshot/metadata handling.
- Docs and tests for the new provider, adapter, and runner.
Wire OpenWiki to the local Antigravity CLI (`agy`) as an agent-cli provider
alongside Grok Build. Runs use print mode with auto-approved edits, recover
conversation ids from the per-run log, and accept `agy models` display names.

@corridor-security corridor-security Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both agent-CLI providers (Grok Build, Antigravity) are launched with auto-approval flags and inherit the full process environment, creating two exploitable issues: prompt-injected content can write files outside the docs-only boundary, and it can read or exfiltrate all credentials loaded from ~/.openwiki/.env.

Comment thread src/agent/index.ts

// Path discipline for agent-cli lives in createSystemPrompt(..., "agent-cli");
// the user message reuses the same root/cwd framing as DeepAgents.
const prompt = `${createSystemPrompt(command, outputMode, "agent-cli")}\n\n---\n\n${createRunUserMessage(command, cwd, context, options)}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The agent-CLI execution path has no enforced filesystem boundary. The DeepAgents path uses OpenWikiLocalShellBackend to hard-reject writes outside openwiki/ at the process level; the agent-CLI path relies on a prompt instruction that can be overridden by injected content in repository files or connector data.

// Path discipline for agent-cli lives in createSystemPrompt(..., "agent-cli");
// the user message reuses the same root/cwd framing as DeepAgents.
const prompt = `${createSystemPrompt(command, outputMode, "agent-cli")}\n\n---\n\n${createRunUserMessage(command, cwd, context, options)}`;

Both adapters launch with auto-approval flags (--always-approve for Grok Build, --dangerously-skip-permissions --mode accept-edits for Antigravity), so the vendor CLI will write any file without prompting.

Remediation: Enforce the docs-only boundary outside the model — sandbox the vendor CLI so only openwiki/ is writable (chroot, overlay FS, or temp-copy approach). Do not rely on prompt instructions for path authorization.

Attack Path
  1. Attacker embeds write instruction in a repository file or connector-ingested content (Slack, Notion, etc.).
  2. User runs openwiki --update with OPENWIKI_PROVIDER=grok-build or antigravity.
  3. runAgentCliRun() builds a prompt that includes the attacker-controlled content.
  4. Vendor binary is spawned with auto-approval flags; it writes the injected target path without any permission check.
  5. OpenWiki records a successful run.

For more details, see the finding in Corridor.

Provide feedback: Reply with whether this is a valid vulnerability or false positive to help improve Corridor's accuracy.

Comment thread src/agent/engines/runner.ts
Address Corridor findings on the agent-CLI path:
- spawn vendor CLIs with a minimal env allowlist so OpenWiki-managed
  credentials never reach the child process
- after repository init/update runs, reject any writes outside openwiki/
  (plus AGENTS.md / CLAUDE.md) based on post-run mtime checks
@sanjay3290

Copy link
Copy Markdown
Author

Addressed both Corridor findings in 16a452e:

  1. Env scrubbing (src/agent/engines/child-env.ts + runner.ts): vendor agent CLIs are now spawned with an explicit allowlisted env (PATH/HOME/tmp/locale/proxy/CA only). All MANAGED_ENV_KEYS, secret-like keys, and OPENWIKI_* / LANGCHAIN_* / LANGSMITH_* vars are dropped so credentials loaded by loadOpenWikiEnv() never reach the child.

  2. Docs-only write boundary (src/agent/engines/write-boundary.ts + runner.ts): repository init/update runs set writeBoundary: "docs-only". After a successful agent-CLI exit, OpenWiki scans the cwd for files mtime-touched during the run and fails the run if any path is outside openwiki/ (plus root AGENTS.md / CLAUDE.md). Chat and local-wiki runs keep writeBoundary: "none" (cwd is already the wiki root for local-wiki).

Tests: test/agent-cli-security.test.ts covers scrubbing + allowed/disallowed writes. Full suite green (303 tests).

@corridor-security — treating both findings as valid; mitigated as above (post-run mtime boundary rather than full OS sandbox, which is the practical control for a local CLI integration).

Stop skipping VCS metadata trees during the post-run path scan so
prompt-injected git hook or config writes under .git/ are rejected.
@sanjay3290

Copy link
Copy Markdown
Author

Also landed the .git write-boundary scan fix here (same as #280): VCS metadata trees are no longer skipped, so planted hooks under .git/hooks/ fail docs-only runs.

@corridor-security corridor-security Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs-only write-boundary scanner follows symlinks and records only the symlink's own path, allowing a symlink placed under openwiki/ to target a file outside the repo (e.g. .git/hooks/pre-commit) while passing the allowlist check — a prompt-injected or malicious agent CLI can exploit this to silently modify VCS hooks or arbitrary host files.

*/
export async function findDisallowedWrites(
rootDir: string,
sinceMs: number,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs-only write-boundary check follows symlinks and records only the path under the repo, allowing a bypass. A symlink created in openwiki/ pointing outside the repository (e.g., .git/hooks/pre-commit or ../../some/host/file) passes the allowlist because the recorded path is openwiki/…, while the actual write lands on the target outside the boundary — undetected.

The vulnerable logic accepts symlinks and calls stat() (which follows the link), pushing the symlink path into results:

if (!entry.isFile() && !entry.isSymbolicLink()) {
  continue;
}

const fileStat = await stat(absolutePath);
if (fileStat.mtimeMs >= sinceMs) {
  results.push(relativePath);
}

Because findDisallowedWrites filters with isAllowedDocsOnlyWritePath(relativePath), a modified symlink under openwiki/ is always approved even if its target is outside the repo. Commit d869510 fixed direct .git/ writes but left this symlink path open.

Remediation: Import lstat and realpath; use lstat() for the mtime check and, when the entry is a symlink, reject it if realpath() resolves outside rootDir. Alternatively, treat any modified symlink as disallowed during docs-only runs.

Attack Path
  1. Attacker prompt-injects the agent CLI during a docs-only repository init/update run.
  2. Agent creates openwiki/evil-link → .git/hooks/pre-commit (symlink write is inside the allowed zone).
  3. Agent writes attacker payload through the symlink, modifying the hook on disk.
  4. Post-run scan records openwiki/evil-link; isAllowedDocsOnlyWritePath approves it.
  5. No error is thrown; .git/hooks/pre-commit now executes attacker code on the next git commit.

For more details, see the finding in Corridor.

Provide feedback: Reply with whether this is a valid vulnerability or false positive to help improve Corridor's accuracy.

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