Skip to content

SUP-1535: Add Codex jarvOS adapter#30

Merged
levineam merged 1 commit into
mainfrom
SUP-1535-codex-jarvos-adapter
May 11, 2026
Merged

SUP-1535: Add Codex jarvOS adapter#30
levineam merged 1 commit into
mainfrom
SUP-1535-codex-jarvos-adapter

Conversation

@levineam

@levineam levineam commented May 11, 2026

Copy link
Copy Markdown
Owner

Summary

  • add @jarvos/agent-context shared adapter with Paperclip current work, GBrain recall, startup brief, and verified Obsidian note creation
  • expose a local stdio MCP server for Codex with jarvOS current-work, recall, note, and startup-brief tools
  • add Codex runtime setup docs/script and secondbrain journal-link export used by runtime note capture

Verification

  • node --test modules/jarvos-agent-context/test/*.test.js
  • node --test modules/jarvos-secondbrain/tests/*.test.js
  • npm test
  • git diff --check
  • ./runtimes/codex/setup.sh
  • codex mcp get jarvos
  • node modules/jarvos-agent-context/scripts/jarvos-mcp.js startup-brief

Summary by CodeRabbit

Release Notes

  • New Features

    • Added @jarvos/agent-context module enabling agent clients to access jarvOS capabilities including current work tracking, recall, note creation, and startup briefing through MCP tools.
    • Added Codex CLI runtime adapter for native jarvOS integration.
  • Documentation

    • Expanded documentation with module descriptions, setup guides, and runtime integration instructions.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This pull request introduces the @jarvos/agent-context module, a shared runtime-facing adapter that exposes jarvOS functionality (current work queries, knowledge recall, note creation, startup briefings) as MCP tools. It includes a JSON-RPC MCP server, a note capture contract validator, integration with the Codex CLI runtime, and refactored journal-linking utilities.

Changes

jarvOS Agent-Context Module and Codex Runtime

Layer / File(s) Summary
Note Capture Contract
modules/jarvos-agent-context/src/note-contract.js
Defines frontmatter schema (status, type, project, created, updated, author), sanitizes note titles, parses YAML frontmatter, extracts markdown sections, and verifies notes are stored correctly with required fields and optional journal back-linking under specified sections.
Agent-Context Core Logic
modules/jarvos-agent-context/src/index.js
Implements currentWork (Paperclip issue query/filter), recall (GBrain bundle generation), createNote (file writing + journal linking + contract verification), and startupBrief (composed markdown with character budget). Includes Paperclip auth loading, issue normalization, and jarvOS module loaders.
MCP Server Implementation
modules/jarvos-agent-context/scripts/jarvos-mcp.js
Exposes four tools (jarvos_current_work, jarvos_recall, jarvos_create_note, jarvos_startup_brief) via JSON-RPC 2.0 routing (initialize, tools/list, tools/call). Includes CLI shortcut for startup-brief invocation and stdin-based JSON-RPC message handling.
Refactored Journal Linker
modules/jarvos-secondbrain/bridge/provenance/src/link-to-journal.js
Extracted reusable linkNoteToJournal function supporting journal file creation, markdown section management, and link deduplication for agent-context note integration.
Package Manifest
modules/jarvos-agent-context/package.json
Defines @jarvos/agent-context package metadata, entry points, MCP binary wiring, npm scripts, optional peer dependencies (@jarvos/gbrain, @jarvos/secondbrain), and Node >=18 requirement.
Codex Runtime Integration
runtimes/codex/README.md, runtimes/codex/setup.sh
Documents Codex CLI runtime adapter bridging local sessions to jarvOS MCP server and provides setup script to register jarvos MCP server with Codex for stdio tool invocation. Establishes operating rule that Codex treats jarvOS as source of truth for memory/capture.
Module and Repository Documentation
README.md, modules/README.md, modules/jarvos-agent-context/README.md
Updates repo tree/architecture table to include new module and Codex runtime; documents agent-context as shared runtime adapter; lists wrapped modules (GBrain, Secondbrain, Paperclip), MCP tool names, and setup instructions across project hierarchy.
Tests
modules/jarvos-agent-context/test/agent-context.test.js, tests/modules-smoke-test.js
Validates frontmatter defaults, note file creation, automatic journal file creation and linking, MCP tool ordering, and end-to-end note creation via MCP. Adds smoke test for module loading and tool presence.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • levineam/jarvOS#16: Earlier refactoring of link-to-journal.js timezone handling; this PR further refactors the same file to expose linkNoteToJournal function with createIfMissing support.
  • levineam/jarvOS#27: Introduces GBrain recall and recall-bundle APIs that agent-context directly wraps and exposes via MCP.
  • levineam/jarvOS#11: Establishes module stubs and smoke-test infrastructure that this PR builds upon by adding agent-context module implementation and MCP tool definitions.

Suggested labels

jarvis-managed

Poem

🐰 A new adapter hops into the fray,
Bundling work and knowledge every day,
Notes are linked and journals stay aware,
Codex whispers through the MCP air,
jarvOS shines—no memory despair! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically refers to the main objective of adding a Codex adapter for jarvOS, which is supported by the substantial changes introducing the @jarvos/agent-context module, MCP server setup, and Codex runtime integration.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch SUP-1535-codex-jarvos-adapter

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f41d1fe1c3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +48 to +52
JARVOS_ROOT,
'modules',
'jarvos-secondbrain',
'packages',
'jarvos-secondbrain-notes',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Resolve secondbrain internals from the installed package

When @jarvos/agent-context is installed as an npm/tarball package alongside @jarvos/secondbrain, MODULE_ROOT/../.. resolves to node_modules, so this hard-coded node_modules/modules/jarvos-secondbrain/... path does not exist and createNote/jarvos_create_note throw before writing anything. I reproduced this by packing and installing both new packages locally; the failure is Cannot find module '/tmp/.../node_modules/modules/jarvos-secondbrain/packages/.../write-to-vault.js'. Use require.resolve('@jarvos/secondbrain/...', { paths: [...] }) or an exported secondbrain API before falling back to the monorepo source path.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai 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.

Actionable comments posted: 4

🧹 Nitpick comments (1)
tests/modules-smoke-test.js (1)

271-273: ⚡ Quick win

Assert the full MCP tool contract in smoke tests.

This check only verifies 2 tools, so regressions in jarvos_current_work or jarvos_startup_brief won’t fail smoke.

Proposed patch
-  const toolNames = mcp.TOOLS.map((tool) => tool.name);
-  if (toolNames.includes('jarvos_recall') && toolNames.includes('jarvos_create_note')) {
-    ok('jarvos MCP server exposes recall and note tools');
+  const toolNames = new Set(mcp.TOOLS.map((tool) => tool.name));
+  const expectedTools = [
+    'jarvos_current_work',
+    'jarvos_recall',
+    'jarvos_create_note',
+    'jarvos_startup_brief',
+  ];
+  if (expectedTools.every((name) => toolNames.has(name))) {
+    ok('jarvos MCP server exposes expected tool set');
   } else {
-    bad('jarvos MCP tools', new Error(JSON.stringify(toolNames)));
+    bad('jarvos MCP tools', new Error(JSON.stringify([...toolNames])));
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/modules-smoke-test.js` around lines 271 - 273, The smoke test currently
only checks for two tools; update the assertion to validate the full MCP tool
contract by comparing mcp.TOOLS (or its mapped toolNames) against the expected
list of tool names (e.g., include 'jarvos_recall', 'jarvos_create_note',
'jarvos_current_work', 'jarvos_startup_brief') — ensure you assert both presence
and that no extra/missing tools exist (exact match or same-size+contains) so
regressions in jarvos_current_work or jarvos_startup_brief will fail the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@modules/jarvos-agent-context/scripts/jarvos-mcp.js`:
- Line 100: The condition `if (!id && String(method ||
'').startsWith('notifications/')) return;` treats 0 as "no id" and can drop
valid JSON-RPC requests; change the check to explicitly test for null/undefined
(e.g., `if (id == null && String(method || '').startsWith('notifications/'))
return;` or `if ((id === undefined || id === null) && String(method ||
'').startsWith('notifications/')) return;`) so the variables `id` and `method`
are handled correctly.

In `@modules/jarvos-agent-context/src/index.js`:
- Around line 153-154: The code converts options.limit with Number(...) and can
produce NaN which is sent to paperclipJson; update the logic around the const
limit = Number(options.limit || 200) so it guards non-numeric inputs by
validating and falling back to a safe default (e.g., 200) or clamping to a
max/min before building the query string; locate the limit construction and the
paperclipJson call (references: options.limit, limit,
paperclipJson(`/companies/${auth.companyId}/issues?limit=${limit}`, auth)) and
replace with validation that ensures limit is a valid integer before passing it
into the URL.
- Around line 114-116: Wrap the fetch call that assigns const response in an
AbortController: create const controller = new AbortController(), pass signal:
controller.signal into fetch, and start a timer (e.g., const timeout =
setTimeout(() => controller.abort(), 10000)) to abort after 10s; after fetch
completes clearTimeout(timeout). Also handle the abort case by catching the
thrown DOMException/AbortError and surface a clear error (or rethrow) so
upstream code can handle timeouts. Ensure you preserve the existing URL
construction using auth.apiUrl, auth.apiKey and pathname when calling fetch.

In `@modules/jarvos-agent-context/src/note-contract.js`:
- Line 15: The frontmatter regex assigned to const match currently only matches
LF newlines; update the pattern used in note-contract.js (the
/^---\n([\s\S]*?)\n---\n?/ regex) to accept CRLF by replacing each \n with \r?\n
(e.g., use /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/), so frontmatter parsing becomes
CRLF-safe while preserving existing behavior for LF.

---

Nitpick comments:
In `@tests/modules-smoke-test.js`:
- Around line 271-273: The smoke test currently only checks for two tools;
update the assertion to validate the full MCP tool contract by comparing
mcp.TOOLS (or its mapped toolNames) against the expected list of tool names
(e.g., include 'jarvos_recall', 'jarvos_create_note', 'jarvos_current_work',
'jarvos_startup_brief') — ensure you assert both presence and that no
extra/missing tools exist (exact match or same-size+contains) so regressions in
jarvos_current_work or jarvos_startup_brief will fail the test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9fc28231-304f-4e10-9e53-c26d01055315

📥 Commits

Reviewing files that changed from the base of the PR and between 64c5564 and f41d1fe.

📒 Files selected for processing (12)
  • README.md
  • modules/README.md
  • modules/jarvos-agent-context/README.md
  • modules/jarvos-agent-context/package.json
  • modules/jarvos-agent-context/scripts/jarvos-mcp.js
  • modules/jarvos-agent-context/src/index.js
  • modules/jarvos-agent-context/src/note-contract.js
  • modules/jarvos-agent-context/test/agent-context.test.js
  • modules/jarvos-secondbrain/bridge/provenance/src/link-to-journal.js
  • runtimes/codex/README.md
  • runtimes/codex/setup.sh
  • tests/modules-smoke-test.js

async function handle(message) {
if (!message || typeof message !== 'object') return;
const { id, method, params } = message;
if (!id && String(method || '').startsWith('notifications/')) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use explicit id-absence check for notifications.

Line 100 uses !id, which treats 0 as “no id”. Prefer checking for undefined/null explicitly to avoid misclassifying valid JSON-RPC ids.

Proposed fix
-  if (!id && String(method || '').startsWith('notifications/')) return;
+  if ((id === undefined || id === null) && String(method || '').startsWith('notifications/')) return;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!id && String(method || '').startsWith('notifications/')) return;
if ((id === undefined || id === null) && String(method || '').startsWith('notifications/')) return;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/jarvos-agent-context/scripts/jarvos-mcp.js` at line 100, The
condition `if (!id && String(method || '').startsWith('notifications/'))
return;` treats 0 as "no id" and can drop valid JSON-RPC requests; change the
check to explicitly test for null/undefined (e.g., `if (id == null &&
String(method || '').startsWith('notifications/')) return;` or `if ((id ===
undefined || id === null) && String(method || '').startsWith('notifications/'))
return;`) so the variables `id` and `method` are handled correctly.

Comment on lines +114 to +116
const response = await fetch(`${auth.apiUrl}${pathname.startsWith('/api') ? pathname : `/api${pathname}`}`, {
headers: { Authorization: `Bearer ${auth.apiKey}` },
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n "async function paperclipJson|fetch\\(|AbortController|signal\\s*:" modules/jarvos-agent-context/src/index.js -n -C2

Repository: levineam/jarvOS

Length of output: 418


Add a request timeout for Paperclip fetch calls.

This fetch call has no timeout and can hang indefinitely if the upstream server stalls, blocking MCP responses.

Wrap the fetch in an AbortController with a reasonable timeout (e.g., 10 seconds):

async function paperclipJson(pathname, auth) {
  if (!auth.apiUrl || !auth.apiKey) {
    throw new Error('Paperclip API is not configured');
  }
+ const controller = new AbortController();
+ const timeout = setTimeout(() => controller.abort(), 10000);
+ let response;
+ try {
-  const response = await fetch(`${auth.apiUrl}${pathname.startsWith('/api') ? pathname : `/api${pathname}`}`, {
+   response = await fetch(`${auth.apiUrl}${pathname.startsWith('/api') ? pathname : `/api${pathname}`}`, {
     headers: { Authorization: `Bearer ${auth.apiKey}` },
+    signal: controller.signal,
   });
+ } finally {
+   clearTimeout(timeout);
+ }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const response = await fetch(`${auth.apiUrl}${pathname.startsWith('/api') ? pathname : `/api${pathname}`}`, {
headers: { Authorization: `Bearer ${auth.apiKey}` },
});
async function paperclipJson(pathname, auth) {
if (!auth.apiUrl || !auth.apiKey) {
throw new Error('Paperclip API is not configured');
}
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 10000);
let response;
try {
response = await fetch(`${auth.apiUrl}${pathname.startsWith('/api') ? pathname : `/api${pathname}`}`, {
headers: { Authorization: `Bearer ${auth.apiKey}` },
signal: controller.signal,
});
} finally {
clearTimeout(timeout);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/jarvos-agent-context/src/index.js` around lines 114 - 116, Wrap the
fetch call that assigns const response in an AbortController: create const
controller = new AbortController(), pass signal: controller.signal into fetch,
and start a timer (e.g., const timeout = setTimeout(() => controller.abort(),
10000)) to abort after 10s; after fetch completes clearTimeout(timeout). Also
handle the abort case by catching the thrown DOMException/AbortError and surface
a clear error (or rethrow) so upstream code can handle timeouts. Ensure you
preserve the existing URL construction using auth.apiUrl, auth.apiKey and
pathname when calling fetch.

Comment on lines +153 to +154
const limit = Number(options.limit || 200);
const payload = await paperclipJson(`/companies/${auth.companyId}/issues?limit=${limit}`, auth);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard limit against non-numeric values.

If options.limit is non-numeric, Line 154 sends limit=NaN, which can fail upstream requests.

Proposed fix
-  const limit = Number(options.limit || 200);
+  const parsedLimit = Number(options.limit);
+  const limit = Number.isFinite(parsedLimit) && parsedLimit > 0 ? parsedLimit : 200;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/jarvos-agent-context/src/index.js` around lines 153 - 154, The code
converts options.limit with Number(...) and can produce NaN which is sent to
paperclipJson; update the logic around the const limit = Number(options.limit ||
200) so it guards non-numeric inputs by validating and falling back to a safe
default (e.g., 200) or clamping to a max/min before building the query string;
locate the limit construction and the paperclipJson call (references:
options.limit, limit,
paperclipJson(`/companies/${auth.companyId}/issues?limit=${limit}`, auth)) and
replace with validation that ensures limit is a valid integer before passing it
into the URL.

}

function parseFrontmatter(content) {
const match = /^---\n([\s\S]*?)\n---\n?/.exec(String(content || ''));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Frontmatter parser is not CRLF-safe.

Line 15 only matches LF newlines. Notes saved with CRLF can fail contract verification even when frontmatter is valid.

Proposed fix
-  const match = /^---\n([\s\S]*?)\n---\n?/.exec(String(content || ''));
+  const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(String(content || ''));
🧰 Tools
🪛 OpenGrep (1.20.0)

[ERROR] 15-15: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/jarvos-agent-context/src/note-contract.js` at line 15, The
frontmatter regex assigned to const match currently only matches LF newlines;
update the pattern used in note-contract.js (the /^---\n([\s\S]*?)\n---\n?/
regex) to accept CRLF by replacing each \n with \r?\n (e.g., use
/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/), so frontmatter parsing becomes CRLF-safe
while preserving existing behavior for LF.

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