agent-trellis 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/pi.d.ts +46 -7
- package/dist/adapters/pi.js +151 -8
- package/dist/adapters/piMcp.d.ts +35 -0
- package/dist/adapters/piMcp.js +58 -0
- package/dist/cli.js +17 -1
- package/dist/commands/doctor.d.ts +22 -2
- package/dist/commands/doctor.js +51 -3
- package/dist/commands/mcp.js +6 -0
- package/dist/commands/mcpGateway.d.ts +1 -0
- package/dist/commands/mcpGateway.js +32 -4
- package/dist/commands/mcpRenew.d.ts +52 -0
- package/dist/commands/mcpRenew.js +104 -0
- package/dist/lib/gatewayBackend.d.ts +5 -0
- package/dist/lib/gatewayBackend.js +2 -1
- package/dist/lib/mcpStatusProvider.d.ts +35 -1
- package/dist/lib/mcpStatusProvider.js +27 -4
- package/dist/lib/oauth/callbackPage.d.ts +11 -0
- package/dist/lib/oauth/callbackPage.js +89 -0
- package/dist/lib/oauth/flow.js +12 -9
- package/dist/lib/oauth/renew.d.ts +75 -0
- package/dist/lib/oauth/renew.js +86 -0
- package/dist/probes/pi.d.ts +7 -4
- package/dist/probes/pi.js +16 -11
- package/docs/getting-started.md +31 -0
- package/docs/mcp-authorization-ownership.md +22 -3
- package/docs/mcp-oauth-renewal.md +89 -0
- package/docs/research.md +7 -4
- package/package.json +1 -1
package/dist/probes/pi.js
CHANGED
|
@@ -5,16 +5,26 @@
|
|
|
5
5
|
* and `dist/core/resource-loader.js` (`loadContextFileFromDir(resolvedAgentDir)`),
|
|
6
6
|
* not by assumption. See design.md D5 (openspec/changes/trellis-doctor-p0).
|
|
7
7
|
*
|
|
8
|
-
* pi has
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* pi >= 1.1.0 has a native MCP client (`~/.pi/agent/mcp.json`), which the
|
|
9
|
+
* adapter manages (trellis-pi-native-mcp-migration); the probe still
|
|
10
|
+
* reports `mcpServers: []` because server health is the runtime's job,
|
|
11
|
+
* not the probe's. `probePiVersion` is split out so the adapter's
|
|
12
|
+
* native-vs-bridge version gate shares exactly this exec and tests can
|
|
13
|
+
* inject a seam.
|
|
12
14
|
*/
|
|
13
15
|
import { execFileSync } from "node:child_process";
|
|
14
16
|
import { existsSync } from "node:fs";
|
|
15
17
|
import { homedir } from "node:os";
|
|
16
18
|
import { join } from "node:path";
|
|
17
19
|
import { pathRef, scanSkillRoot } from "../lib/probeCommon.js";
|
|
20
|
+
export function probePiVersion() {
|
|
21
|
+
try {
|
|
22
|
+
return execFileSync("pi", ["--version"], { encoding: "utf-8", timeout: 5_000 }).trim();
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
18
28
|
/** Priority order pi itself checks (`loadContextFileFromDir`), first match wins. */
|
|
19
29
|
const INSTRUCTIONS_CANDIDATES = ["AGENTS.override.md", "AGENTS.md", "AGENTS.MD", "CLAUDE.md", "CLAUDE.MD"];
|
|
20
30
|
export async function probe(homeDir = homedir()) {
|
|
@@ -25,13 +35,8 @@ export async function probe(homeDir = homedir()) {
|
|
|
25
35
|
if (!present) {
|
|
26
36
|
return { agent: "pi", present: false, skillRoots: [], mcpServers: [], diagnostics: [] };
|
|
27
37
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
version = execFileSync("pi", ["--version"], { encoding: "utf-8", timeout: 5_000 }).trim();
|
|
31
|
-
}
|
|
32
|
-
catch {
|
|
33
|
-
// config exists without the binary on PATH — unusual, still probeable
|
|
34
|
-
}
|
|
38
|
+
// config can exist without the binary on PATH — unusual, still probeable
|
|
39
|
+
const version = probePiVersion();
|
|
35
40
|
const skillRoot = scanSkillRoot(skillsPath);
|
|
36
41
|
let instructionsFile;
|
|
37
42
|
for (const candidate of INSTRUCTIONS_CANDIDATES) {
|
package/docs/getting-started.md
CHANGED
|
@@ -291,6 +291,28 @@ that advertises `--prompt`, `--resume`, and `--output-format`. A desktop-only
|
|
|
291
291
|
profile can still receive managed configuration; Trellis never launches the
|
|
292
292
|
desktop application's private helper or bundled runtime path.
|
|
293
293
|
|
|
294
|
+
## pi native MCP (pi ≥ 1.1.0)
|
|
295
|
+
|
|
296
|
+
pi 1.1.0 added a native MCP client, and Trellis delivers to it the same way
|
|
297
|
+
as every other JSON-config agent: `trellis mcp sync` merges Trellis-managed
|
|
298
|
+
entries into `~/.pi/agent/mcp.json` — the single `trellis` edge entry in
|
|
299
|
+
gateway mode, plus a bare `{url}` entry for each agent-owned OAuth server.
|
|
300
|
+
Servers you added yourself (anything Trellis never wrote) are preserved
|
|
301
|
+
untouched, and the legacy bridge extension (`~/.pi/agent/extensions/
|
|
302
|
+
trellis-mcp-bridge.js`) is removed in the same run, after the config write.
|
|
303
|
+
|
|
304
|
+
Two pi-specific behaviors to know:
|
|
305
|
+
|
|
306
|
+
- pi does not watch `mcp.json`: after a sync that changed it, restart pi or
|
|
307
|
+
run `/reload` in an open session (sync prints this hint).
|
|
308
|
+
- Agent-owned OAuth servers (e.g. `figma`) are authorized by pi itself:
|
|
309
|
+
run `pi mcp login <name>` once; the grant lives in pi's own token store
|
|
310
|
+
(`~/.pi/agent/mcp-auth.json`), and `trellis.mcp.status` reports them as
|
|
311
|
+
`agent-managed`.
|
|
312
|
+
|
|
313
|
+
On pi older than 1.1.0 nothing changes: the bridge extension remains the
|
|
314
|
+
delivery mechanism and `mcp.json` is never written.
|
|
315
|
+
|
|
294
316
|
## Trellis Runtime Skill
|
|
295
317
|
|
|
296
318
|
`trellis-runtime` is the built-in operating guide that Trellis installs into
|
|
@@ -907,6 +929,15 @@ An Agent can call the read-only `trellis.mcp.status` Runtime tool to explain
|
|
|
907
929
|
this partial state to the user. It reports unavailable or authorization-
|
|
908
930
|
required servers and gives the next safe action, such as `trellis mcp auth
|
|
909
931
|
figma`, without exposing tokens or opening a browser from the Agent process.
|
|
932
|
+
Each connected upstream also carries a structured `toolCount`, and an `oauth`
|
|
933
|
+
section reports credential health with explicit ownership: Trellis-owned
|
|
934
|
+
OAuth servers (which connect through the gateway on a Trellis-store token)
|
|
935
|
+
report `valid` / `no-token` / `expired-renewable` / `needs-auth` /
|
|
936
|
+
`unreadable` with `expiresAt` when known; agent-owned servers report
|
|
937
|
+
`agent-managed`, since their authorization lives in the Agent's own client
|
|
938
|
+
and is never inferred from Trellis's store. The classification is read-only
|
|
939
|
+
and shared with `trellis mcp renew` and `trellis doctor`, so the three can
|
|
940
|
+
never disagree.
|
|
910
941
|
|
|
911
942
|
This command only applies to `http`/`sse` servers. stdio servers get
|
|
912
943
|
their credentials from `env`/`env_aliases` as described above; running
|
|
@@ -49,11 +49,30 @@ so a cancelled grant cannot overwrite a concurrent refresh.
|
|
|
49
49
|
The gateway can silently refresh its own hosted credentials; it never opens a
|
|
50
50
|
browser. Agents with native connections continue using their own flows.
|
|
51
51
|
|
|
52
|
+
## Browser callback feedback
|
|
53
|
+
|
|
54
|
+
The local callback page shows a compact Trellis/service label, one status message,
|
|
55
|
+
one short instruction and a close action, with Chinese or English copy based on
|
|
56
|
+
the browser's preferred language. Receiving an
|
|
57
|
+
authorization response is an intermediate step: validation, token exchange and
|
|
58
|
+
credential storage still determine the final result shown in Trellis or the
|
|
59
|
+
terminal. Cancellation, provider errors and incomplete responses have separate
|
|
60
|
+
messages and guidance for starting again.
|
|
61
|
+
|
|
62
|
+
The page uses no external assets and does not display authorization parameters
|
|
63
|
+
or raw provider descriptions. Responses disable caching and referrer disclosure;
|
|
64
|
+
when JavaScript is available, the page also removes the query and fragment from
|
|
65
|
+
the current browser history entry. The close action offers manual-close guidance
|
|
66
|
+
when a browser blocks it, and manual guidance remains visible without JavaScript.
|
|
67
|
+
|
|
52
68
|
## Verification
|
|
53
69
|
|
|
54
70
|
Run core tests and typecheck, GUI sidecar tests/typecheck and GUI smoke tests.
|
|
55
71
|
`npm run test:oauth-browser --workspace @trellis/gui` additionally uses installed
|
|
56
|
-
Google Chrome headlessly with a local authorization-server fixture.
|
|
57
|
-
|
|
58
|
-
|
|
72
|
+
Google Chrome headlessly with a local authorization-server fixture. Set
|
|
73
|
+
`TRELLIS_OAUTH_BROWSER_EXECUTABLE` to use a compatible Chromium executable instead.
|
|
74
|
+
It exercises real browser CORS, PKCE callback, progress, cancellation, retry,
|
|
75
|
+
owner confirmation and callback feedback without contacting a real provider or
|
|
76
|
+
using personal credentials. Set `TRELLIS_OAUTH_SCREENSHOTS` to an output directory
|
|
77
|
+
to save callback desktop/mobile previews during that run.
|
|
59
78
|
Desktop packaging and installed-app acceptance happen after these checks.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Scheduled OAuth renewal — `trellis mcp renew`
|
|
2
|
+
|
|
3
|
+
The gateway/runtime edge already refreshes OAuth tokens silently before
|
|
4
|
+
every upstream call. `trellis mcp renew` moves that renewal *earlier*: run
|
|
5
|
+
on a schedule, it refreshes whatever is expiring and reports — with a
|
|
6
|
+
non-zero exit code — anything that needs a human, before an Agent session
|
|
7
|
+
discovers it as a 401.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
trellis mcp renew # refresh what's expiring, report health
|
|
11
|
+
trellis mcp renew --dry-run # report only; no token-endpoint call
|
|
12
|
+
trellis mcp renew --json # machine-readable (no credential material)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Scope is canonical OAuth servers with an explicit `owner: trellis` —
|
|
16
|
+
classification is explicit (mcp-auth-routing-policy), and ownership decides
|
|
17
|
+
whose problem the credential is (trellis-desktop-oauth-ownership): an
|
|
18
|
+
agent-owned server (the default, e.g. scalar `auth: oauth`) is authorized by
|
|
19
|
+
the Agent's own client, never reads Trellis's token store, and is neither
|
|
20
|
+
renewed nor reported here — for pi ≥ 1.1.0 that client-side authorization is
|
|
21
|
+
`pi mcp login <name>`, stored in pi's own `~/.pi/agent/mcp-auth.json`. The command never opens a browser and never
|
|
22
|
+
performs an initial grant; that remains `trellis mcp auth <name>`
|
|
23
|
+
(mcp-gateway-hosting design.md D9). Exit code: `0` when every Trellis-owned
|
|
24
|
+
server is valid or refreshed, `1` when any needs `trellis mcp auth`, has
|
|
25
|
+
never been authorized, or has an unreadable credential file.
|
|
26
|
+
|
|
27
|
+
`trellis doctor` reports the same per-server health read-only (no network,
|
|
28
|
+
no writes) and surfaces an `oauth-expired` finding — contributing to a
|
|
29
|
+
non-zero doctor exit — for credentials that are expired or absent.
|
|
30
|
+
|
|
31
|
+
## launchd setup (macOS)
|
|
32
|
+
|
|
33
|
+
Trellis has no daemon; periodic renewal belongs to the platform
|
|
34
|
+
scheduler. Save this as `~/Library/LaunchAgents/com.trellis.mcp-renew.plist`,
|
|
35
|
+
adjusting the `trellis` path (`which trellis`):
|
|
36
|
+
|
|
37
|
+
```xml
|
|
38
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
39
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
40
|
+
<plist version="1.0">
|
|
41
|
+
<dict>
|
|
42
|
+
<key>Label</key>
|
|
43
|
+
<string>com.trellis.mcp-renew</string>
|
|
44
|
+
<key>ProgramArguments</key>
|
|
45
|
+
<array>
|
|
46
|
+
<string>/usr/local/bin/trellis</string>
|
|
47
|
+
<string>mcp</string>
|
|
48
|
+
<string>renew</string>
|
|
49
|
+
<string>--json</string>
|
|
50
|
+
</array>
|
|
51
|
+
<key>StartInterval</key>
|
|
52
|
+
<integer>21600</integer>
|
|
53
|
+
<key>ThrottleInterval</key>
|
|
54
|
+
<integer>600</integer>
|
|
55
|
+
<key>StandardOutPath</key>
|
|
56
|
+
<string>/tmp/trellis-mcp-renew.log</string>
|
|
57
|
+
<key>StandardErrorPath</key>
|
|
58
|
+
<string>/tmp/trellis-mcp-renew.log</string>
|
|
59
|
+
</dict>
|
|
60
|
+
</plist>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Then:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.trellis.mcp-renew.plist
|
|
67
|
+
launchctl kickstart gui/$(id -u)/com.trellis.mcp-renew # run once now
|
|
68
|
+
launchctl bootout gui/$(id -u)/com.trellis.mcp-renew # uninstall
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Why these knobs:
|
|
72
|
+
|
|
73
|
+
- **`ThrottleInterval`** (design.md D3) caps how close together runs may
|
|
74
|
+
be. A machine waking from sleep with a launchd backlog runs once, not N
|
|
75
|
+
times — refresh-endpoint hammering is the failure mode that gets refresh
|
|
76
|
+
tokens revoked by rate-limiting providers, which is worse than the
|
|
77
|
+
disease this job exists to prevent.
|
|
78
|
+
- **`StartInterval` of 6h** is comfortably inside typical refresh-token
|
|
79
|
+
lifetimes (days to months) while keeping token-endpoint traffic near
|
|
80
|
+
zero: a valid token is a no-op *read*, so most runs make no network
|
|
81
|
+
calls at all.
|
|
82
|
+
- **Overlapping runs are safe**: refresh is serialized by the per-server
|
|
83
|
+
lock (`src/lib/oauth/lock.ts`). A second concurrent run waits, then
|
|
84
|
+
reads the token the first one just wrote.
|
|
85
|
+
- **`--json` to a log** gives you a parseable trail. A `1` exit means
|
|
86
|
+
"something needs `trellis mcp auth <name>`" — the JSON names which.
|
|
87
|
+
|
|
88
|
+
Check on demand: `trellis doctor` shows the same per-server credential
|
|
89
|
+
health.
|
package/docs/research.md
CHANGED
|
@@ -236,10 +236,13 @@ Reverse-engineered directly from the installed binary
|
|
|
236
236
|
- pi **natively discovers** `AGENTS.override.md`, `AGENTS.md`, `CLAUDE.md`
|
|
237
237
|
(in that priority order) — instructions and `SKILL.md`-format skills need
|
|
238
238
|
no adapter work at all.
|
|
239
|
-
- pi
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
239
|
+
- pi **gained a native MCP client in 1.1.0** (`pi mcp add/list/login`,
|
|
240
|
+
`~/.pi/agent/mcp.json`, stdio + streamable HTTP, `${VAR}` expansion in
|
|
241
|
+
`env`/`headers`, OAuth into its own `mcp-auth.json` store) — Trellis delivers
|
|
242
|
+
to it natively (trellis-pi-native-mcp-migration). Before 1.1.0 the only
|
|
243
|
+
`mcpServers` string in its bundle belonged to the Google Gen AI SDK's Vertex
|
|
244
|
+
tool-schema translator, which explicitly throws `"mcpServers parameter is
|
|
245
|
+
not supported"` — that era is why the bridge extension below exists.
|
|
243
246
|
- pi's capability surface is **extensions**: JS modules loaded via
|
|
244
247
|
`--extension`/discovery, with a real API (`createExtension`,
|
|
245
248
|
`registerTool`). Giving pi access to the same MCP servers every other agent
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-trellis",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "A unified Code Agent Runtime for Skills, MCP, Memory, and shared instructions across Claude Code, Codex, Kiro, pi, Kimi Code, and ZCode.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Paul Leo",
|