Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
a0670cc
feat(cli): add pi as a hook-path coding agent
yczhang-nv Aug 18, 2026
e17d997
fix(cli): map pi turn boundaries and serialize extension hook posts
yczhang-nv Aug 18, 2026
12d081f
fix(pi): do not end the gateway session on /reload
yczhang-nv Aug 18, 2026
18c6acf
fix(cli): stop claiming pi model traffic is redirected
yczhang-nv Aug 18, 2026
9430725
docs(pi): correct the turn hook row in the extension README
yczhang-nv Aug 18, 2026
b7f393a
feat(pi): open turn scopes at pi's boundary and attribute every hook
yczhang-nv Aug 18, 2026
839bccb
ci(pi): give the pi integration CI, a just recipe, and docs
yczhang-nv Aug 18, 2026
ebe97d2
docs(pi): state the tool-result truncation and the subagent gap
yczhang-nv Aug 18, 2026
d35d1b9
feat(pi): route pi model traffic through the gateway when it is safe
yczhang-nv Aug 18, 2026
184b416
docs(pi): drop tracker links from the extension README
yczhang-nv Aug 18, 2026
4410b3a
feat(pi): apply gateway argument transforms to pi tool calls
yczhang-nv Aug 19, 2026
27373c4
feat(pi): gate pi's bang-prefixed inline shell through the gateway
yczhang-nv Aug 19, 2026
18bbf19
feat(pi): add a doctor preflight, and harden the integration
yczhang-nv Aug 19, 2026
e4a8c39
docs(pi): record that the extension is not published to npm
yczhang-nv Aug 19, 2026
80ca784
fix(pi): correct two facts a review pass found wrong
yczhang-nv Aug 19, 2026
f2354af
docs(pi): drop a backwards reason from the inline-shell notes
yczhang-nv Aug 19, 2026
4c5f63a
fix(pi): close four review findings, two of them fail-open
yczhang-nv Aug 19, 2026
b7c8f46
fix(pi): close the remaining review findings
yczhang-nv Aug 19, 2026
de05c0f
fix(pi): identify this extension, and scope the session key by constr…
yczhang-nv Aug 19, 2026
9e94e4a
docs(pi): the tool schema is reachable; the transform design is a choice
yczhang-nv Aug 19, 2026
acd4290
fix(pi): launch from the same resolution doctor reports, and check it…
yczhang-nv Aug 19, 2026
e15aa27
fix(pi): publish an argument rewrite only once the tool start cannot …
yczhang-nv Aug 19, 2026
540b471
test(pi): exercise the three surfaces this PR added but left unasserted
yczhang-nv Aug 19, 2026
836e76b
fix(pi): close a lookup that fails open, and name the redirect skip h…
yczhang-nv Aug 19, 2026
9e9e679
chore(pi): declare the node types this workspace typechecks against
yczhang-nv Aug 19, 2026
e716494
docs(pi): complete the wire contract, the hook inventory, and the out…
yczhang-nv Aug 19, 2026
17ee262
test(pi): assert the guardrail saw the pre-rewrite arguments, not jus…
yczhang-nv Aug 19, 2026
27cf84d
docs(pi): name where each synthesized event actually comes from
yczhang-nv Aug 19, 2026
bda027f
docs(pi): evaluation is per model select; the mark is not
yczhang-nv Aug 19, 2026
3a9fa20
fix(pi): two copies load, and an installed one could not be seen
yczhang-nv Aug 19, 2026
a16c965
fix(pi): send the launcher's proxy credential on a redirected provider
yczhang-nv Aug 19, 2026
e7a6943
fix(pi): fold preflight findings into the status, configured or not
yczhang-nv Aug 19, 2026
01411c1
fix(pi): a gateway that answered was reached; say which fault happened
yczhang-nv Aug 19, 2026
9d6adb2
fix(pi): model pi's real extension set, and stop naming the wrong closer
yczhang-nv Aug 19, 2026
5616c35
chore(pi): remove four stray files committed by accident
yczhang-nv Aug 19, 2026
2063ed8
fix(pi): choose the copy pi already loads, and name the fault's origin
yczhang-nv Aug 19, 2026
8969d7f
fix(pi): find the extension when settings.json lists it directly
yczhang-nv Aug 19, 2026
47e88be
style(pi): drop the warning emoji from the extension's doc comments
yczhang-nv Aug 19, 2026
f097e06
fix(pi): report a pi minor above the verified band as unverified
yczhang-nv Aug 19, 2026
35ceac9
docs(pi): align the page with the Claude Code and Codex guides
yczhang-nv Aug 19, 2026
1c35825
docs(pi): name what is unavailable, and restore the heading blank lines
yczhang-nv Aug 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/ci-path-filters.yml
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,21 @@ node:
openclaw:
- 'integrations/openclaw/**'

# The pi extension is a hook client for the CLI gateway, so a change to the
# adapter it posts against should re-run the extension's own suite.
#
# Only the adapter paths are listed, deliberately. `test-pi` is a TypeScript
# typecheck plus Node tests against a *stub* gateway, so it cannot observe the
# `/hooks/pi` route, the event classifier, or the session manager no matter what
# changes there -- and `crates/cli/src/sessions/**` alone would put a full Node
# matrix on nearly every CLI change. Those files are covered by the `rust`
# filter, which matches every `crates/**/*.rs` file and runs the workspace tests
# that exercise the route for real.
pi:
- 'crates/cli/src/agents/pi/**'
- 'crates/cli/src/agents/shared/adapters.rs'
- 'integrations/pi/**'

python:
- 'crates/python/Cargo.toml'
- 'crates/python/src/**'
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ jobs:
ref_name: ${{ github.ref_name }}
run_package: ${{ needs.ci_changes.outputs.run_node_package == 'true' }}
run_openclaw: ${{ needs.ci_changes.outputs.run_openclaw == 'true' }}
run_pi: ${{ needs.ci_changes.outputs.run_pi == 'true' }}

ci_python:
name: Python
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/ci_changes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ on:
run_openclaw:
description: 'Whether OpenClaw integration jobs should run'
value: ${{ jobs.changes.outputs.run_openclaw }}
run_pi:
description: 'Whether pi integration jobs should run'
value: ${{ jobs.changes.outputs.run_pi }}
run_python:
description: 'Whether Python jobs should run'
value: ${{ jobs.changes.outputs.run_python }}
Expand Down Expand Up @@ -77,9 +80,10 @@ jobs:
run_docs: ${{ (inputs.full_ci || startsWith(inputs.ref_name, 'pull-request/')) && (inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.docs == 'true') }}
run_go: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.go == 'true' }}
run_installer: ${{ inputs.full_ci || steps.filter.outputs.installer == 'true' }}
run_node: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.node == 'true' || steps.filter.outputs.openclaw == 'true' }}
run_node: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.node == 'true' || steps.filter.outputs.openclaw == 'true' || steps.filter.outputs.pi == 'true' }}
run_node_package: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.node_package == 'true' }}
run_openclaw: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.node == 'true' || steps.filter.outputs.openclaw == 'true' }}
run_pi: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.node == 'true' || steps.filter.outputs.pi == 'true' }}
run_python: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.python == 'true' }}
run_python_integration_langchain: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.python_integration_langchain == 'true' }}
run_python_package: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.python_package == 'true' }}
Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/ci_node.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ on:
required: false
default: false
type: boolean
run_pi:
description: 'Whether to run pi integration checks'
required: false
default: false
type: boolean
secrets:
CODECOV_TOKEN:
required: false
Expand Down Expand Up @@ -122,6 +127,11 @@ jobs:
working-directory: ${{ env.NEMO_RELAY_CI_WORKSPACE }}
run: just --set ci true test-openclaw

- name: Run pi integration checks
if: ${{ inputs.run_pi }}
working-directory: ${{ env.NEMO_RELAY_CI_WORKSPACE }}
run: just --set ci true test-pi

- name: Upload Node coverage to Codecov
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6
if: ${{ !startsWith(matrix.platform, 'windows') }}
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,6 @@ CHANGELOG.md
# Relay
/.nemo-relay
/artifacts/

# Generated per machine by the dynamic-plugin recipe: local dylib name + sha256.
examples/**/relay-plugin.local.toml
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ coverage.
|:--|:--:|:--:|:--:|:--|
| Claude Code | Yes | Yes | Partial | Hook forwarding, pre-tool blocking, and gateway-routed LLM observability are supported. |
| Codex | Yes | Yes | Partial | Persistent install verifies the exact plugin hooks. Each `Stop` finalizes a turn snapshot; the supported generated schema does not install `SessionEnd`. |
| pi | Partial | Yes | No | Proof of concept. A Relay-authored pi extension forwards tool and turn activity, gates tool calls, and points the active model's provider at the gateway when the gateway fronts that provider — which is what enables model-call enforcement and LLM spans. |
| Hermes Agent | Yes | Yes | Partial | NeMo Relay is built into Hermes Agent, and Hermes Agent understands NeMo Relay plugin configurations. No separate observability plugin or Relay CLI setup is required. |

### Public API Integrations
Expand Down
6 changes: 6 additions & 0 deletions crates/cli/src/agents/claude/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ pub(crate) fn adapt(payload: Value, headers: &HeaderMap) -> AdapterOutcome {
"PermissionDenied",
"permissionDenied",
],
// Claude Code reports only the close of a turn (`Stop`); its turns stay
// lazily opened, and `PreCompact`/`PostCompact` are matched by the
// shared fallback rather than by an adapter-specific rule.
turn_start: &[],
turn_end: &["Stop", "stop"],
compaction: &[],
},
);
// Response shape is decided by the primary event (first in the vec); secondary events like
Expand Down
1 change: 1 addition & 0 deletions crates/cli/src/agents/claude/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub(super) const DESCRIPTOR: AgentDescriptor = AgentDescriptor {
hook_path: "/hooks/claude-code",
version_product: "Claude Code",
minimum_version: (2, 1, 121),
verified_through: None,
hook_events: &[
"SessionStart",
"UserPromptSubmit",
Expand Down
6 changes: 6 additions & 0 deletions crates/cli/src/agents/codex/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ pub(crate) fn adapt(payload: Value, headers: &HeaderMap) -> AdapterOutcome {
subagent_end: &["subagentStop", "subagentEnd", "subagent_stop"],
tool_start: &["preToolUse", "toolStarted", "tool_start"],
tool_end: &["postToolUse", "toolEnded", "tool_end", "toolFailed"],
// Codex reports only the close of a turn (`Stop`); its turns stay
// lazily opened, and `PreCompact`/`PostCompact` are matched by the
// shared fallback rather than by an adapter-specific rule.
turn_start: &[],
turn_end: &["Stop", "stop"],
compaction: &[],
},
);
AdapterOutcome {
Expand Down
1 change: 1 addition & 0 deletions crates/cli/src/agents/codex/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub(super) const DESCRIPTOR: AgentDescriptor = AgentDescriptor {
hook_path: "/hooks/codex",
version_product: "codex-cli",
minimum_version: (0, 143, 0),
verified_through: None,
hook_events: &[
"SessionStart",
"UserPromptSubmit",
Expand Down
Loading
Loading