Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e8b6829
feat(rutis): pi extensions adapter — pi tools and tool policies in yo…
yuanhao Oct 8, 2026
10eedb1
fix(rutis): pi adapter review findings — pi semantics on the less com…
yuanhao Oct 8, 2026
b36ef32
fix(rutis): pi adapter re-review — prepare on a copy, strict at load,…
yuanhao Oct 8, 2026
1841463
fix(rutis): pi adapter fails closed — full review round
yuanhao Oct 8, 2026
e769e5f
fix(rutis): pi adapter re-review — setActiveTools scope, built-in-onl…
yuanhao Oct 8, 2026
e25caeb
fix(rutis): pi adapter — getActiveTools sees tools registered after s…
yuanhao Oct 8, 2026
28c656e
ci: cache npm downloads in the yoagent-rutis languages job
yuanhao Oct 8, 2026
a40c3c7
feat(rutis): image tool results across TypeScript, Python, dsh and ru…
yuanhao Oct 8, 2026
eb30bee
Merge branch 'feat/image-tool-results' into feat/pi-extensions-adapter
yuanhao Oct 8, 2026
c85a886
feat(rutis): pi adapter passes images through
yuanhao Oct 8, 2026
d5a9dbf
fix(rutis): image review — strict base64, provider image types, size …
yuanhao Oct 8, 2026
850fe3c
Merge branch 'feat/image-tool-results' into feat/pi-extensions-adapter
yuanhao Oct 8, 2026
49cb4ef
fix(rutis): an after_tool edit may keep an image yoagent let in
yuanhao Oct 8, 2026
7b706d0
Merge branch 'feat/image-tool-results' into feat/pi-extensions-adapter
yuanhao Oct 8, 2026
94f6148
Merge remote-tracking branch 'origin/main' into feat/pi-extensions-ad…
yuanhao Oct 8, 2026
10dcc42
feat(rutis): plugin logs to the host, pi executeTool, mid-run checks,…
yuanhao Oct 8, 2026
469426f
fix(rutis): review — pi-latest version print, nested errors through t…
yuanhao Oct 8, 2026
d60f6ac
fix(rutis): pi appendEntry and sendMessage are recorded, not refused
yuanhao Oct 8, 2026
abad3a4
fix(rutis): pi sendMessage stored as pi stores it (custom_message)
yuanhao Oct 8, 2026
c0ba3a4
Merge main into feat/pi-extensions-adapter: log from #268; pi marked …
yuanhao Oct 8, 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
13 changes: 13 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,13 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 24
# npm's download cache (~/.npm), keyed on the three lockfiles: the
# installs below (pi's alone is ~185 MB) stop re-downloading.
cache: npm
cache-dependency-path: |
integrations/yoagent-rutis/plugins/package-lock.json
integrations/yoagent-rutis/plugins/dsh/package-lock.json
integrations/yoagent-rutis/plugins/pi/package-lock.json
- uses: actions/setup-python@v5
with:
python-version: "3.12"
Expand All @@ -299,6 +306,10 @@ jobs:
# dsh-system-prompt and the adapter's other pins, in their own install.
- name: Install the dsh npm packages
run: npm ci --prefix "$PLUGINS/dsh"
# The pi adapter's end-to-end test (tests/pi_test.rs): pi's own
# packages (its extension loader, TypeBox), in their own install.
- name: Install the pi npm packages
run: npm ci --prefix "$PLUGINS/pi"
- name: Install the rutis Python package
run: |
python -m venv "$PLUGINS/.venv"
Expand All @@ -314,6 +325,8 @@ jobs:
run: cargo test --manifest-path "$MANIFEST" --features node,python,websocket
- name: Example
run: cargo run --manifest-path "$MANIFEST" --features node,python --example language_plugins
- name: pi extensions example (scripted, the fixture extension)
run: cargo run --manifest-path "$MANIFEST" --features node --example pi_extensions

# yoagent claims to be a tested GASP-conformant runtime — this job is the
# test: emit an agent repo with the gasp bridge (mock provider, no network)
Expand Down
54 changes: 54 additions & 0 deletions .github/workflows/pi-latest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: pi latest

# The pi extensions adapter (integrations/yoagent-rutis/plugins/pi) pins pi
# exactly (1.1.0) and imports pi's extension loader by its internal file path
# (dist/core/extensions/loader.js), which pi's export map does not promise.
# This job runs the adapter's end-to-end tests against the LATEST pi packages
# weekly, so a pi release that breaks the adapter shows up here before anyone
# bumps the pin. A red run means: check the adapter against that pi release.
# It never changes the pin or the lockfile.
on:
schedule:
# Tuesdays 04:23 UTC.
- cron: "23 4 * * 2"
workflow_dispatch:

env:
CARGO_TERM_COLOR: always
MANIFEST: integrations/yoagent-rutis/Cargo.toml
PI: integrations/yoagent-rutis/plugins/pi

permissions:
contents: read

jobs:
pi-latest:
name: pi adapter vs latest pi
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
workspaces: integrations/yoagent-rutis
- uses: actions/setup-node@v4
with:
node-version: 24
- name: Install the pinned packages, then the latest pi on top
run: |
npm ci --prefix "$PI"
npm install --prefix "$PI" --no-save \
@earendil-works/pi-coding-agent@latest \
@earendil-works/pi-ai@latest \
@earendil-works/pi-tui@latest \
typebox@latest
# Versions read from the installed packages: `npm ls` exits 1 on a
# version that differs from package.json's exact pin, which is the point here.
for p in @earendil-works/pi-coding-agent @earendil-works/pi-ai @earendil-works/pi-tui typebox; do
echo "$p $(node -p "require('./$PI/node_modules/$p/package.json').version")"
done
- name: The adapter's tests against it
env:
YOAGENT_RUTIS_REQUIRE_RUNTIMES: "1"
run: cargo test --manifest-path "$MANIFEST" --features node --test pi_test
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ adheres to [Semantic Versioning](https://semver.org/).

### yoagent-rutis

- **pi extensions** (`plugins/pi/pi-extensions-adapter.ts`): the tools and tool policies of [pi](https://github.com/earendil-works/pi) coding-agent extensions, loaded unchanged with pi's own loader, as one yoagent handler following pi 1.1.0's semantics. `registerTool` → tools (pi's activation and first-registration-wins, `prepareArguments` and validation before the policies, only the judged arguments run), `setActiveTools` → an enforced allowlist, `tool_call` → `before_tool` (yoagent's built-ins under pi's names, arguments translated both ways, relative paths resolved where the policies look; an override of a built-in is enforced; `terminate` stops the run), `tool_result` → `after_tool` (a failing handler withholds the result), `input` → `on_input`, `before_agent_start` additions → a turn note. Fails closed: a load error, a failing `session_start`, an unfired deciding event (`context`, `message_end`, ...; unless `allowUnmapped`) or a pi tool named like a yoagent built-in (unless `withoutBuiltins`) refuses the load; other unmapped API (observer and session events, commands, renderers, providers, MCP servers) is reported, or refuses with `strict`. Example `pi_extensions` (any extension files; `--live` with DeepSeek; `--without`), test `pi_test`. `ctx.executeTool` runs pi tools as nested calls (pi's pipeline; never rejects); a deciding event registered mid-run stops that run; adapter warnings reach the host's logs. `pi.appendEntry` / `pi.sendMessage` are recorded in the adapter's in-memory session instead of throwing (pi tools that record their result this way no longer fail after the work). CI: a weekly `pi latest` job runs the adapter's tests against the newest pi.
- **Images in tool results, across ecosystems.** TypeScript and Python handlers' `call_tool` results and `after_tool` edits take `content` blocks in yoagent's JSON shape (`{"type": "image", "data": <base64>, "mimeType"}` next to text blocks) instead of `text`, so pictures cross the bridge both ways (`after_tool` already saw `output.content`). The dsh adapter turns dsh image blocks (references into dsh's attachment store) into yoagent images through the `attachments` service when one is loaded; the rutis-agent example reads a runner's `{"content": [...]}` value as blocks (rutis-agent results are otherwise text). Rust handlers already returned full yoagent tool results. Tests: `content_blocks` unit test, TypeScript/Python round trip in `languages_test`, dsh with and without a store in `dsh_test`.
- **Plugin logs reach the host.** The `yoagent` service gains `log(level, message, { run_id }?)`: a TypeScript/Python plugin's diagnostics go to the host's `tracing` output (target `yoagent_rutis::plugin`, with the run as a `run_id` field) instead of the runtime process's stderr. It never rejects (a rejected, uncaught call would end the plugin's runtime): any message, extra context fields ignored. In Python it is a coroutine to `await`. The dsh adapter uses it. Test: `tests/plugin_log_test.rs`.
- **Docs: one contract, other ecosystems.** The extensions guide shows DSH and rutis-agent plugging in through `Extension` and the bridge with no core change (pi in review, #265); the bridge README states the adapter contract (map what the host honours, refuse what would decide unenforced, deny on failing policies, withhold on failing redactions, report what is ignored). CI now also runs the rutis-agent example.
Expand Down
Loading
Loading