agent-trellis 0.7.0 → 0.8.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/README.md +97 -38
- package/dist/adapters/claude-code.js +3 -2
- package/dist/adapters/codex.js +3 -2
- package/dist/adapters/jsonMcp.d.ts +3 -1
- package/dist/adapters/jsonMcp.js +18 -4
- package/dist/adapters/kimi-code.d.ts +21 -0
- package/dist/adapters/kimi-code.js +107 -0
- package/dist/adapters/kiro.js +3 -2
- package/dist/adapters/mcpPlan.d.ts +6 -1
- package/dist/adapters/mcpPlan.js +106 -8
- package/dist/adapters/pi.js +22 -3
- package/dist/adapters/symlinkPlan.d.ts +7 -0
- package/dist/adapters/symlinkPlan.js +16 -7
- package/dist/cli.js +121 -21
- package/dist/commands/doctor.d.ts +7 -2
- package/dist/commands/doctor.js +38 -2
- package/dist/commands/init.d.ts +4 -2
- package/dist/commands/init.js +26 -13
- package/dist/commands/kimi.d.ts +16 -0
- package/dist/commands/kimi.js +64 -0
- package/dist/commands/manage.d.ts +39 -0
- package/dist/commands/manage.js +126 -0
- package/dist/commands/mcp.d.ts +22 -1
- package/dist/commands/mcp.js +65 -2
- package/dist/commands/mcpGateway.d.ts +6 -0
- package/dist/commands/mcpGateway.js +34 -14
- package/dist/commands/mcpImport.d.ts +27 -0
- package/dist/commands/mcpImport.js +336 -0
- package/dist/commands/memory.d.ts +5 -3
- package/dist/commands/memory.js +12 -8
- package/dist/commands/migrate.d.ts +16 -5
- package/dist/commands/migrate.js +90 -17
- package/dist/commands/onboard.d.ts +74 -2
- package/dist/commands/onboard.js +458 -71
- package/dist/commands/rollback.js +4 -1
- package/dist/commands/skill.d.ts +17 -0
- package/dist/commands/skill.js +47 -2
- package/dist/commands/sync.js +2 -0
- package/dist/core/canonical.d.ts +15 -7
- package/dist/core/canonical.js +101 -13
- package/dist/core/capabilitySelection.d.ts +45 -0
- package/dist/core/capabilitySelection.js +124 -0
- package/dist/core/types.d.ts +25 -2
- package/dist/core/types.js +7 -0
- package/dist/lib/backup.d.ts +2 -0
- package/dist/lib/backup.js +4 -0
- package/dist/lib/builtinSkills.d.ts +2 -0
- package/dist/lib/builtinSkills.js +4 -0
- package/dist/lib/cliMetadata.d.ts +1 -0
- package/dist/lib/cliMetadata.js +14 -0
- package/dist/lib/gatewayBackend.d.ts +28 -0
- package/dist/lib/gatewayBackend.js +72 -1
- package/dist/lib/installAgent.d.ts +7 -5
- package/dist/lib/installAgent.js +4 -3
- package/dist/lib/mcpConnect.js +5 -1
- package/dist/lib/mcpMigrateRead.d.ts +1 -0
- package/dist/lib/mcpMigrateRead.js +3 -0
- package/dist/lib/mcpRuntime.d.ts +46 -0
- package/dist/lib/mcpRuntime.js +185 -0
- package/dist/lib/mcpStatusProvider.d.ts +10 -0
- package/dist/lib/mcpStatusProvider.js +25 -0
- package/dist/lib/mcpToolRegistry.d.ts +18 -0
- package/dist/lib/mcpToolRegistry.js +107 -14
- package/dist/lib/memoryGraph.d.ts +1 -1
- package/dist/lib/memoryGraph.js +9 -3
- package/dist/lib/memoryProvider.d.ts +58 -0
- package/dist/lib/memoryProvider.js +175 -0
- package/dist/lib/memoryStatus.d.ts +17 -0
- package/dist/lib/memoryStatus.js +88 -0
- package/dist/lib/nativeMemory.d.ts +19 -0
- package/dist/lib/nativeMemory.js +129 -0
- package/dist/lib/runtimeControlProvider.d.ts +16 -0
- package/dist/lib/runtimeControlProvider.js +168 -0
- package/dist/lib/secretEnv.d.ts +2 -1
- package/dist/lib/secretEnv.js +6 -2
- package/dist/lib/skillProvider.d.ts +13 -0
- package/dist/lib/skillProvider.js +210 -0
- package/dist/lib/taskProvider.d.ts +7 -0
- package/dist/lib/taskProvider.js +43 -0
- package/dist/lib/taskStore.d.ts +46 -0
- package/dist/lib/taskStore.js +156 -0
- package/dist/lib/terminalPicker.d.ts +23 -49
- package/dist/lib/terminalPicker.js +53 -218
- package/dist/pi-bridge/bundle.js +475 -37
- package/dist/pi-bridge/index.js +42 -21
- package/dist/pi-bridge/schemaTranslate.d.ts +4 -8
- package/dist/pi-bridge/schemaTranslate.js +4 -8
- package/dist/probes/kimi-code.d.ts +12 -0
- package/dist/probes/kimi-code.js +55 -0
- package/dist/sdk.d.ts +1 -1
- package/docs/architecture.md +224 -26
- package/docs/assets/agent-trellis-architecture-horizontal.svg +80 -0
- package/docs/assets/agent-trellis-architecture-vertical.svg +82 -0
- package/docs/assets/trellis-mark.svg +12 -0
- package/docs/getting-started.md +328 -35
- package/docs/overview.md +131 -0
- package/docs/overview.zh-CN.md +100 -0
- package/docs/research.md +59 -0
- package/docs/roadmap.md +9 -5
- package/package.json +3 -2
- package/schema/builtin-skills/trellis-runtime/SKILL.md +117 -0
- package/schema/capability-selection.example.yaml +23 -0
- package/schema/scope.example.yaml +1 -1
- package/schema/servers.example.yaml +15 -1
package/dist/pi-bridge/index.js
CHANGED
|
@@ -15,29 +15,17 @@ import { homedir } from "node:os";
|
|
|
15
15
|
import { loadCanonicalSource } from "../core/canonical.js";
|
|
16
16
|
import { resolveMcpPlan } from "../adapters/mcpPlan.js";
|
|
17
17
|
import { connectServer, withTimeout, DEFAULT_CONNECT_TIMEOUT_MS } from "../lib/mcpConnect.js";
|
|
18
|
+
import { McpToolRegistry } from "../lib/mcpToolRegistry.js";
|
|
19
|
+
import { withOAuthHeader } from "../lib/gatewayBackend.js";
|
|
18
20
|
import { ALL_AGENTS } from "../core/types.js";
|
|
19
|
-
import {
|
|
21
|
+
import { toParametersSchema, toPiContent } from "./schemaTranslate.js";
|
|
20
22
|
/** Re-exported so `trellis-mcp-connect-timeout`'s existing test keeps
|
|
21
23
|
* importing it from the module it was written against — proving the
|
|
22
24
|
* extraction changed nothing it could observe. */
|
|
23
25
|
export { withTimeout };
|
|
24
26
|
const CLIENT_INFO = { name: "trellis-mcp-bridge", version: "0.0.0" };
|
|
25
|
-
function
|
|
26
|
-
return withTimeout(client.listTools(), timeoutMs, `listTools timed out after ${timeoutMs}ms`).then((result) =>
|
|
27
|
-
for (const tool of result.tools) {
|
|
28
|
-
pi.registerTool({
|
|
29
|
-
name: bridgedToolName(serverName, tool.name),
|
|
30
|
-
label: tool.name,
|
|
31
|
-
description: tool.description ?? `MCP tool "${tool.name}" from server "${serverName}"`,
|
|
32
|
-
parameters: toParametersSchema(tool.inputSchema),
|
|
33
|
-
async execute(_toolCallId, params) {
|
|
34
|
-
const result = await client.callTool({ name: tool.name, arguments: params });
|
|
35
|
-
const content = Array.isArray(result.content) ? result.content : [];
|
|
36
|
-
return { content: toPiContent(content), details: result };
|
|
37
|
-
},
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
});
|
|
27
|
+
function listServerTools(client, timeoutMs) {
|
|
28
|
+
return withTimeout(client.listTools(), timeoutMs, `listTools timed out after ${timeoutMs}ms`).then((result) => result.tools);
|
|
41
29
|
}
|
|
42
30
|
/**
|
|
43
31
|
* `homeDir`/`connectTimeoutMs` default to the real `~` / 10s — only
|
|
@@ -57,6 +45,7 @@ export default async function trellisMcpBridge(pi, homeDir = homedir(), connectT
|
|
|
57
45
|
// is pi reading canonical directly at its own runtime, P4's own concern.
|
|
58
46
|
const { desired } = resolveMcpPlan("pi", canonical.mcp, ALL_AGENTS, canonical.secretsPolicy);
|
|
59
47
|
const clients = new Set();
|
|
48
|
+
const registry = new McpToolRegistry();
|
|
60
49
|
const closeClient = async (client) => {
|
|
61
50
|
if (!clients.delete(client))
|
|
62
51
|
return;
|
|
@@ -83,25 +72,57 @@ export default async function trellisMcpBridge(pi, homeDir = homedir(), connectT
|
|
|
83
72
|
// pi 0.85.x emits this before tearing down an extension runtime. Keep the
|
|
84
73
|
// hook optional so the bridge remains loadable in older compatible hosts.
|
|
85
74
|
pi.on?.("session_shutdown", closeAllClients);
|
|
86
|
-
await Promise.all(desired.map(async ({ name, def }) => {
|
|
75
|
+
const discovered = await Promise.all(desired.map(async ({ name, def }) => {
|
|
87
76
|
let client;
|
|
88
77
|
try {
|
|
89
|
-
|
|
78
|
+
const effectiveDef = def.auth === "oauth" && def.url
|
|
79
|
+
? await withOAuthHeader(name, def, homeDir)
|
|
80
|
+
: def;
|
|
81
|
+
client = await connectServer(effectiveDef, canonical.secretsPolicy, connectTimeoutMs, CLIENT_INFO);
|
|
90
82
|
}
|
|
91
83
|
catch (err) {
|
|
92
84
|
// One unreachable/misconfigured (including permanently hanging —
|
|
93
85
|
// trellis-mcp-connect-timeout) server must never prevent every
|
|
94
86
|
// other server's tools from registering (tasks.md 3.2).
|
|
95
87
|
console.error(`trellis-mcp-bridge: failed to connect to MCP server "${name}": ${err instanceof Error ? err.message : String(err)}`);
|
|
96
|
-
return;
|
|
88
|
+
return undefined;
|
|
97
89
|
}
|
|
98
90
|
clients.add(client);
|
|
99
91
|
try {
|
|
100
|
-
await
|
|
92
|
+
const tools = await listServerTools(client, connectTimeoutMs);
|
|
93
|
+
return { name, client, tools };
|
|
101
94
|
}
|
|
102
95
|
catch (err) {
|
|
103
96
|
console.error(`trellis-mcp-bridge: failed to list tools for MCP server "${name}": ${err instanceof Error ? err.message : String(err)}`);
|
|
104
97
|
await closeClient(client);
|
|
98
|
+
return undefined;
|
|
105
99
|
}
|
|
106
100
|
}));
|
|
101
|
+
// Add in canonical plan order, not completion order, so allocation remains
|
|
102
|
+
// stable when multiple upstreams answer concurrently.
|
|
103
|
+
for (const result of discovered) {
|
|
104
|
+
if (!result)
|
|
105
|
+
continue;
|
|
106
|
+
registry.add(result.name, result.client, result.tools);
|
|
107
|
+
}
|
|
108
|
+
for (const tool of registry.listTools()) {
|
|
109
|
+
const target = registry.resolve(tool.name);
|
|
110
|
+
if (!target)
|
|
111
|
+
continue;
|
|
112
|
+
const originalName = target.toolName;
|
|
113
|
+
const serverName = target.serverName;
|
|
114
|
+
pi.registerTool({
|
|
115
|
+
name: tool.name,
|
|
116
|
+
label: originalName,
|
|
117
|
+
description: tool.description ?? `MCP tool "${originalName}" from server "${serverName}"`,
|
|
118
|
+
parameters: toParametersSchema(tool.inputSchema),
|
|
119
|
+
async execute(_toolCallId, params) {
|
|
120
|
+
const result = await registry.callTool(tool.name, params);
|
|
121
|
+
const content = Array.isArray(result.content)
|
|
122
|
+
? result.content
|
|
123
|
+
: [];
|
|
124
|
+
return { content: toPiContent(content), details: result };
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
}
|
|
107
128
|
}
|
|
@@ -50,12 +50,8 @@ export declare function toParametersSchema(inputSchema: unknown): TSchema;
|
|
|
50
50
|
* own `AgentToolResult.content` has no representation for them
|
|
51
51
|
* (design.md D3). */
|
|
52
52
|
export declare function toPiContent(items: McpContentItem[]): PiContent[];
|
|
53
|
-
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* than two identical ones: the naming convention is the contract between
|
|
58
|
-
* a prefixed name and the server it routes to, and two copies of it could
|
|
59
|
-
* drift into routing tools to the wrong upstream
|
|
60
|
-
* (trellis-mcp-gateway-hosting design.md D3). */
|
|
53
|
+
/** Legacy direct prefix helper kept for callers/tests that need to render a
|
|
54
|
+
* source-qualified name. The live bridge uses `McpToolRegistry` so it can
|
|
55
|
+
* allocate short names across all connected servers, normalize invalid
|
|
56
|
+
* characters, and retain the raw name for routing. */
|
|
61
57
|
export { prefixedToolName as bridgedToolName } from "../lib/mcpToolRegistry.js";
|
|
@@ -33,12 +33,8 @@ export function toPiContent(items) {
|
|
|
33
33
|
}
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
|
-
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* than two identical ones: the naming convention is the contract between
|
|
41
|
-
* a prefixed name and the server it routes to, and two copies of it could
|
|
42
|
-
* drift into routing tools to the wrong upstream
|
|
43
|
-
* (trellis-mcp-gateway-hosting design.md D3). */
|
|
36
|
+
/** Legacy direct prefix helper kept for callers/tests that need to render a
|
|
37
|
+
* source-qualified name. The live bridge uses `McpToolRegistry` so it can
|
|
38
|
+
* allocate short names across all connected servers, normalize invalid
|
|
39
|
+
* characters, and retain the raw name for routing. */
|
|
44
40
|
export { prefixedToolName as bridgedToolName } from "../lib/mcpToolRegistry.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kimi Code probe. Kimi 2.x stores user MCP declarations in
|
|
3
|
+
* `~/.kimi-code/mcp.json`, user Skills in `~/.kimi-code/skills`, and global
|
|
4
|
+
* instructions in `~/.kimi-code/AGENTS.md`. Kimi also discovers the generic
|
|
5
|
+
* `~/.agents/skills` root; it is reported so doctor can observe it, while the
|
|
6
|
+
* Runtime-only launcher can explicitly isolate it later.
|
|
7
|
+
*/
|
|
8
|
+
import type { AgentSnapshot } from "../core/types.js";
|
|
9
|
+
export interface ProbeOptions {
|
|
10
|
+
probeMcp?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function probe(homeDir?: string, opts?: ProbeOptions): Promise<AgentSnapshot>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kimi Code probe. Kimi 2.x stores user MCP declarations in
|
|
3
|
+
* `~/.kimi-code/mcp.json`, user Skills in `~/.kimi-code/skills`, and global
|
|
4
|
+
* instructions in `~/.kimi-code/AGENTS.md`. Kimi also discovers the generic
|
|
5
|
+
* `~/.agents/skills` root; it is reported so doctor can observe it, while the
|
|
6
|
+
* Runtime-only launcher can explicitly isolate it later.
|
|
7
|
+
*/
|
|
8
|
+
import { execFileSync } from "node:child_process";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { probeMcpServer } from "../lib/mcpProbe.js";
|
|
12
|
+
import { pathRef, readJsonFile, resolveEnvRefs, scanSkillRoot } from "../lib/probeCommon.js";
|
|
13
|
+
function kimiEnv(homeDir) {
|
|
14
|
+
return { ...process.env, HOME: homeDir, KIMI_CODE_HOME: join(homeDir, ".kimi-code") };
|
|
15
|
+
}
|
|
16
|
+
export async function probe(homeDir = homedir(), opts = {}) {
|
|
17
|
+
const root = join(homeDir, ".kimi-code");
|
|
18
|
+
const mcpPath = join(root, "mcp.json");
|
|
19
|
+
const mcpConfig = readJsonFile(mcpPath);
|
|
20
|
+
const kimiRoot = pathRef(root);
|
|
21
|
+
if (kimiRoot === undefined) {
|
|
22
|
+
return { agent: "kimi-code", present: false, skillRoots: [], mcpServers: [], diagnostics: [] };
|
|
23
|
+
}
|
|
24
|
+
let version;
|
|
25
|
+
try {
|
|
26
|
+
version = execFileSync("kimi", ["--version"], { encoding: "utf-8", timeout: 5_000, env: kimiEnv(homeDir) }).trim();
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// A config-only fixture remains probeable without a binary on PATH.
|
|
30
|
+
}
|
|
31
|
+
const mcpServers = Object.entries(mcpConfig?.mcpServers ?? {}).map(([name, def]) => ({
|
|
32
|
+
name,
|
|
33
|
+
transport: def.url ? (def.transport === "sse" ? "sse" : "http") : "stdio",
|
|
34
|
+
}));
|
|
35
|
+
if (opts.probeMcp) {
|
|
36
|
+
await Promise.all(Object.entries(mcpConfig?.mcpServers ?? {}).map(async ([name, def], index) => {
|
|
37
|
+
if (def.url || !def.command)
|
|
38
|
+
return;
|
|
39
|
+
mcpServers[index].probe = await probeMcpServer({ transport: "stdio", command: def.command, args: def.args }, resolveEnvRefs(def.env, kimiEnv(homeDir)));
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
const skillRoots = [
|
|
43
|
+
scanSkillRoot(join(root, "skills")),
|
|
44
|
+
scanSkillRoot(join(homeDir, ".agents", "skills")),
|
|
45
|
+
].filter((entry) => entry !== undefined);
|
|
46
|
+
return {
|
|
47
|
+
agent: "kimi-code",
|
|
48
|
+
present: true,
|
|
49
|
+
version,
|
|
50
|
+
skillRoots,
|
|
51
|
+
mcpServers,
|
|
52
|
+
instructionsFile: pathRef(join(root, "AGENTS.md")),
|
|
53
|
+
diagnostics: [],
|
|
54
|
+
};
|
|
55
|
+
}
|
package/dist/sdk.d.ts
CHANGED
|
@@ -11,4 +11,4 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export { loadCanonicalSource } from "./core/canonical.js";
|
|
13
13
|
export { ALL_AGENTS, resolveScope } from "./core/types.js";
|
|
14
|
-
export type { AgentId, AgentProfile, CanonicalSource, HubConfig, McpConfig, McpServerDef, MemoryEntry, Scope, SecretsPolicy, SkillRef, Transport, } from "./core/types.js";
|
|
14
|
+
export type { AgentId, AgentProfile, CapabilityDelivery, CanonicalSource, HubConfig, McpConfig, McpRuntimeConfig, McpServerDef, MemoryEntry, Scope, SecretsPolicy, SkillRef, Transport, } from "./core/types.js";
|
package/docs/architecture.md
CHANGED
|
@@ -18,7 +18,7 @@ required reading for that.
|
|
|
18
18
|
│ trellis sync / trellis doctor
|
|
19
19
|
┌───────┼────────┬─────────────┬──────────────┐
|
|
20
20
|
▼ ▼ ▼ ▼
|
|
21
|
-
Claude Code Codex Kiro pi
|
|
21
|
+
Claude Code Codex Kiro pi Kimi Code
|
|
22
22
|
(symlink adapter) (incremental (symlink adapter) (bridge
|
|
23
23
|
TOML writer) extension)
|
|
24
24
|
```
|
|
@@ -57,7 +57,7 @@ Aligned to the `.agents Protocol` draft, extended where the draft is silent:
|
|
|
57
57
|
## Global vs. workspace scope
|
|
58
58
|
|
|
59
59
|
**Global only, for now.** Trellis manages `~/.trellis` — one canonical
|
|
60
|
-
source per machine, applied to that machine's
|
|
60
|
+
source per machine, applied to that machine's five supported agents. It does not yet
|
|
61
61
|
read or merge a project-local `.trellis/` in a specific repo.
|
|
62
62
|
|
|
63
63
|
The `.agents Protocol` draft (see `research.md`) describes a two-layer
|
|
@@ -72,11 +72,11 @@ that's scope creep against this section, not a natural extension.
|
|
|
72
72
|
|
|
73
73
|
## Private / agent-specific capabilities
|
|
74
74
|
|
|
75
|
-
Not everything belongs to all
|
|
75
|
+
Not everything belongs to all five agents. A skill built around Claude
|
|
76
76
|
Code's Task-based subagent delegation has no equivalent to delegate to on
|
|
77
77
|
Codex; an MCP server might only make sense for one agent's workflow. Every
|
|
78
78
|
scopable item — skill, subagent profile, memory entry, MCP server —
|
|
79
|
-
defaults to "shared with all
|
|
79
|
+
defaults to "shared with all managed agents," and can be restricted with an explicit
|
|
80
80
|
`scope` (skills/agents/memories) or inline `agents:` (MCP servers, see
|
|
81
81
|
below). Restriction is the exception you declare, not something you
|
|
82
82
|
configure for the common case.
|
|
@@ -173,8 +173,22 @@ the hub instead — meaningfully less code and one fewer class of failure
|
|
|
173
173
|
the project that is an agent runtime extension, not a config generator,
|
|
174
174
|
regardless of hub mode.
|
|
175
175
|
|
|
176
|
+
The pi bridge symlink follows the currently executing Trellis installation.
|
|
177
|
+
When switching from a checkout to a global package (or between installation
|
|
178
|
+
roots), sync can repair an outside-root link only if its target ends in
|
|
179
|
+
`dist/pi-bridge/bundle.js` and its bytes match the current bridge bundle.
|
|
180
|
+
The repair is backed up, including the old symlink target. Modified, unrelated,
|
|
181
|
+
or dangling outside-root links remain conflicts; this exception applies only
|
|
182
|
+
to the bridge extension, not to Skill or instruction symlinks.
|
|
183
|
+
|
|
176
184
|
## MCP hub mode
|
|
177
185
|
|
|
186
|
+
For onboarding, the short rule is: **Gateway is the recommended local
|
|
187
|
+
Trellis mode; Hub is for an already-running external service**. Gateway is a
|
|
188
|
+
local stdio subprocess that Trellis starts for the Agent session. Hub is an
|
|
189
|
+
HTTP endpoint operated outside this process; Trellis only writes the URL and
|
|
190
|
+
does not deploy, configure, or health-manage that Hub.
|
|
191
|
+
|
|
178
192
|
Every agent's MCP surface can be either N direct server definitions
|
|
179
193
|
(default) or one static entry pointing at a single HTTP endpoint — `mcp.hub.url`
|
|
180
194
|
in `mcp/servers.yaml` is the field, but the supported way to set it is
|
|
@@ -197,7 +211,7 @@ to the code writing one entry that points at it.
|
|
|
197
211
|
- Every adapter writes ONE entry instead of N. This is most of the value:
|
|
198
212
|
adding a new backend server means editing wherever the hub's own config
|
|
199
213
|
lives (this project doesn't prescribe that either) and never touching
|
|
200
|
-
any of the
|
|
214
|
+
any of the five agents' configs or restarting them, if the hub supports
|
|
201
215
|
live reload (mcp-hub does, via SSE).
|
|
202
216
|
- The collision check against `known_host_injected` (docs/research.md
|
|
203
217
|
§"Codex — three hard constraints") shrinks to checking one name instead
|
|
@@ -245,23 +259,29 @@ Codex's `codex mcp login`/`logout` pair, Kiro's `oauth`/`oauthScopes`
|
|
|
245
259
|
fields — and pi, whose bridge is Trellis's own code, simply couldn't
|
|
246
260
|
reach such a server at all.
|
|
247
261
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
262
|
+
Gateway mode does not automatically take ownership of OAuth servers. Mark a
|
|
263
|
+
server explicitly with `auth: oauth` (or `trellis mcp set <name> --auth oauth`)
|
|
264
|
+
and gateway planning keeps it direct. This preserves the native Agent OAuth
|
|
265
|
+
flow for Claude Code/Codex/Kiro; Pi's bridge is the client for its direct OAuth
|
|
266
|
+
connection and may use Trellis's own OAuth store. Trellis never infers this
|
|
267
|
+
classification from a URL or a transient 401.
|
|
268
|
+
|
|
269
|
+
Trellis still implements the client half itself — discovery (RFC 8414 /
|
|
270
|
+
RFC 9728), dynamic client registration (RFC 7591), authorization code with
|
|
271
|
+
PKCE S256 (RFC 7636), and the refresh grant — in `src/lib/oauth/`, but that
|
|
272
|
+
path is currently used for Pi/direct bridge connections rather than silently
|
|
273
|
+
replacing native Agent authorization.
|
|
254
274
|
|
|
255
275
|
Two rules shape it, both from the constraint that the gateway is spawned
|
|
256
276
|
silently by an agent with no terminal attached:
|
|
257
277
|
|
|
258
|
-
- **Only
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
278
|
+
- **Only an explicit human authorization command opens a browser.** Native
|
|
279
|
+
clients use their own login command for direct OAuth entries. Pi's direct
|
|
280
|
+
bridge uses `trellis mcp auth <server>`. The gateway never initiates an
|
|
281
|
+
interactive flow and never connects an OAuth-marked server.
|
|
262
282
|
- **Refreshing is silent, and serialized by a lock.** A `refresh_token`
|
|
263
|
-
grant needs neither a browser nor a callback, so the
|
|
264
|
-
at connect time. The lock (`~/.trellis/mcp/oauth/<name>.lock`) exists
|
|
283
|
+
grant needs neither a browser nor a callback, so the Pi direct bridge can
|
|
284
|
+
do it at connect time. The lock (`~/.trellis/mcp/oauth/<name>.lock`) exists
|
|
265
285
|
because most authorization servers rotate the refresh token and
|
|
266
286
|
invalidate its predecessor — two concurrent refreshes would leave one
|
|
267
287
|
party holding a credential that is already dead.
|
|
@@ -272,8 +292,9 @@ mode 0600, never in `servers.yaml` and never in the OS keychain (macOS's
|
|
|
272
292
|
non-interactive, no-GUI-session context the gateway runs in — measured,
|
|
273
293
|
not assumed).
|
|
274
294
|
|
|
275
|
-
In direct mode nothing here
|
|
276
|
-
|
|
295
|
+
In direct mode nothing here changes: each agent keeps using its own native
|
|
296
|
+
OAuth flow. In gateway mode, an OAuth-marked server is also kept direct; only
|
|
297
|
+
ordinary MCP servers enter the gateway.
|
|
277
298
|
|
|
278
299
|
## MCP gateway mode
|
|
279
300
|
|
|
@@ -311,15 +332,25 @@ true rather than aspirational:
|
|
|
311
332
|
would have written for that agent (same `resolveMcpPlan`, so per-server
|
|
312
333
|
`agents:` scope, `enabled: false`, host-injected collisions,
|
|
313
334
|
literal-secret refusals and unresolved `env` names all behave
|
|
314
|
-
identically), connects each one, and exposes their tools
|
|
315
|
-
|
|
335
|
+
identically), connects each one, and exposes their tools through the shared
|
|
336
|
+
Agent-safe name allocator. Names contain only `[A-Za-z0-9_-]` and are at most
|
|
337
|
+
64 characters; unique short names stay short, while duplicate names retain a
|
|
338
|
+
normalized `<server>__<tool>` identity. Secrets resolve through
|
|
339
|
+
`resolveSecretEnv` exactly as
|
|
316
340
|
in direct mode — the gateway introduces no second path by which a value
|
|
317
341
|
could reach disk.
|
|
318
342
|
|
|
319
343
|
**What it changes for the agents:** Codex's single-`bearer_token_env_var`
|
|
320
344
|
limitation stops applying, because Codex no longer receives any server's
|
|
321
|
-
`headers` — the gateway holds them.
|
|
322
|
-
|
|
345
|
+
`headers` — the gateway holds them. OAuth-marked servers are intentionally
|
|
346
|
+
kept direct; ordinary remote servers remain shared through the gateway. An
|
|
347
|
+
upstream tool keeps its original name when that name is unique and longer than
|
|
348
|
+
the short-name threshold; generic short names receive a compact
|
|
349
|
+
`<server>__<tool>` prefix even when unique. A tool already carrying its source
|
|
350
|
+
prefix is not repeated. Dotted or otherwise invalid names are normalized, and long names
|
|
351
|
+
receive a deterministic hash suffix within the 64-character budget. If
|
|
352
|
+
normalized names still collide, Trellis adds a stable suffix instead of
|
|
353
|
+
dropping a tool.
|
|
323
354
|
|
|
324
355
|
**The seam.** `src/commands/mcpGateway.ts` depends on a `GatewayBackend`
|
|
325
356
|
interface and nothing below it — never a connection, transport, or
|
|
@@ -332,12 +363,117 @@ substitution at one construction site: the entry written into each
|
|
|
332
363
|
agent's config is identical either way, so converging later requires no
|
|
333
364
|
re-sync and nothing the user notices.
|
|
334
365
|
|
|
366
|
+
## MCP Runtime direction
|
|
367
|
+
|
|
368
|
+
Gateway mode is the first consumer of a broader runtime shape. The runtime
|
|
369
|
+
edge is available as `trellis mcp-runtime --agent <id>`. Existing
|
|
370
|
+
`trellis mcp-gateway --agent <id>` entries are a compatibility alias and now
|
|
371
|
+
use the same provider registry. Both edges can expose Trellis-owned
|
|
372
|
+
capabilities and selected upstream MCP capabilities:
|
|
373
|
+
|
|
374
|
+
```text
|
|
375
|
+
Agent
|
|
376
|
+
│ one MCP connection
|
|
377
|
+
▼
|
|
378
|
+
Trellis MCP Runtime
|
|
379
|
+
├─ BuiltinRegistry
|
|
380
|
+
│ ├─ SkillProvider (read/search canonical skills)
|
|
381
|
+
│ ├─ RuntimeMemoryProvider
|
|
382
|
+
│ │ └─ CanonicalMemoryProvider (read-only canonical Markdown)
|
|
383
|
+
│ └─ StatusProvider (future read-only diagnostics)
|
|
384
|
+
└─ UpstreamProvider
|
|
385
|
+
└─ existing GatewayBackend / future RemoteBackend
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
The runtime is a protocol edge; providers are the capability boundary. A
|
|
389
|
+
provider may expose tools, resources, prompts, or a combination. The current
|
|
390
|
+
`GatewayBackend` remains the upstream tool aggregation implementation and is
|
|
391
|
+
mounted as an `UpstreamProvider`, so connection, OAuth, timeout, namespace,
|
|
392
|
+
and cleanup logic stay in one place.
|
|
393
|
+
|
|
394
|
+
Native skills remain the default delivery. Runtime MCP delivery is configured
|
|
395
|
+
per agent under `mcp.runtime.delivery` and supports `native`, `mcp`, or
|
|
396
|
+
`both`. The built-in providers are intentionally read-only and scope-filtered:
|
|
397
|
+
SkillProvider reads canonical skills, while RuntimeMemoryProvider adapts a
|
|
398
|
+
CanonicalMemoryProvider that reads canonical Markdown memories through
|
|
399
|
+
`trellis.memory.search`, `trellis.memory.read`, and
|
|
400
|
+
`trellis://memories/<name>.md`. Neither executes scripts or mutates canonical
|
|
401
|
+
state. When the documented `memory` MCP backend is enabled, Agent-created
|
|
402
|
+
entities and observations are written through that upstream MCP server; the
|
|
403
|
+
Runtime provider does not duplicate its graph protocol. `trellis memory
|
|
404
|
+
extract` is the explicit, conflict-safe graph-to-canonical path. A future
|
|
405
|
+
local-graph/OpenViking source can implement the same MemoryProvider contract,
|
|
406
|
+
and native/private Agent stores are never scraped implicitly.
|
|
407
|
+
|
|
408
|
+
`trellis.runtime.status` reports Memory readiness separately: backend
|
|
409
|
+
configuration, graph existence/readiness, canonical count, write authority,
|
|
410
|
+
and which managed Agents receive the shared route. This prevents an empty
|
|
411
|
+
canonical directory from being mistaken for a missing or unusable backend.
|
|
412
|
+
|
|
413
|
+
Onboarding keeps two Memory decisions independent:
|
|
414
|
+
|
|
415
|
+
- `--memory-migrate on|off` imports supported native Markdown memory into
|
|
416
|
+
canonical `memories/*.md`.
|
|
417
|
+
- `--memory on|off` enables the shared Memory MCP backend and graph.
|
|
418
|
+
|
|
419
|
+
The first adapter is Claude Code's exact current-workspace project-memory
|
|
420
|
+
directory. Kimi Code and pi session stores, and Kiro databases, are reported as
|
|
421
|
+
unsupported until a format-specific adapter is reviewed; Trellis does not
|
|
422
|
+
interpret sessions or caches as memory.
|
|
423
|
+
|
|
424
|
+
Kimi Code is Runtime-first. Its adapter writes one `trellis` entry to
|
|
425
|
+
`~/.kimi-code/mcp.json`; it does not copy canonical Skills into
|
|
426
|
+
`~/.kimi-code/skills` when delivery is `mcp`. Use `trellis kimi` for this mode:
|
|
427
|
+
the launcher passes Kimi's documented `--skills-dir` override an empty
|
|
428
|
+
temporary directory, preventing Kimi's automatic `~/.agents/skills` discovery
|
|
429
|
+
from duplicating the Runtime SkillProvider. Kimi's native mode remains
|
|
430
|
+
available when delivery is `native` or `both`.
|
|
431
|
+
|
|
432
|
+
Kimi's user MCP registry is `~/.kimi-code/mcp.json`; the adapter preserves
|
|
433
|
+
unowned entries and uses the existing MCP ownership ledger for the one entry
|
|
434
|
+
Trellis owns. The Kimi CLI itself remains responsible for login and OAuth.
|
|
435
|
+
|
|
436
|
+
## Built-in Trellis Skill lifecycle
|
|
437
|
+
|
|
438
|
+
`schema/builtin-skills/trellis-runtime/SKILL.md` is part of the Trellis
|
|
439
|
+
package, not a user-specific copy. `trellis init` bootstraps it into the
|
|
440
|
+
canonical source at `~/.trellis/skills/trellis-runtime/SKILL.md`; subsequent
|
|
441
|
+
changes are made once there and distributed through the normal adapter path.
|
|
442
|
+
|
|
443
|
+
The Skill has two contracts:
|
|
444
|
+
|
|
445
|
+
- **Takeover contract:** describe the safe operator flow — inspect, dry-run,
|
|
446
|
+
choose the migration source and managed boundary, select capabilities,
|
|
447
|
+
authorize OAuth, sync, audit, verify, and rollback. This is a procedure for
|
|
448
|
+
the human or an Agent assisting the human; it is not a license for an Agent
|
|
449
|
+
to silently rewrite native configuration.
|
|
450
|
+
- **Runtime contract:** describe how a managed Agent consumes
|
|
451
|
+
`SkillProvider`, `RuntimeMemoryProvider`, the instruction provider, status
|
|
452
|
+
providers, and task handoff providers through the current MCP edge.
|
|
453
|
+
|
|
454
|
+
Delivery determines how the second contract reaches an Agent:
|
|
455
|
+
|
|
456
|
+
| Delivery | Projection | Runtime Skill source |
|
|
457
|
+
| --- | --- | --- |
|
|
458
|
+
| `native` | Trellis-owned native symlink | Agent's native Skill discovery |
|
|
459
|
+
| `mcp` | No native Trellis Skill projection | `SkillProvider` through `trellis mcp-runtime` |
|
|
460
|
+
| `both` | Native projection plus Runtime entry | Both, intentionally during compatibility or transition |
|
|
461
|
+
|
|
462
|
+
This keeps takeover instructions and post-takeover capability instructions in
|
|
463
|
+
one versioned Skill while preserving the security boundary: Runtime providers
|
|
464
|
+
are external context, native instructions remain authoritative at startup,
|
|
465
|
+
and task mutations require explicit confirmation. The MCP client's generated
|
|
466
|
+
namespace (for example `mcp__trellis__skills_search`) is not part of the
|
|
467
|
+
portable Skill contract.
|
|
468
|
+
|
|
335
469
|
## What Trellis explicitly does not build
|
|
336
470
|
|
|
337
471
|
- A resident MCP gateway daemon. Gateway mode above is a per-session
|
|
338
472
|
subprocess with no lifecycle; hub mode points at something you operate.
|
|
339
473
|
Neither is a service Trellis starts, supervises, or keeps running.
|
|
340
|
-
- A memory backend (
|
|
474
|
+
- A semantic/external memory backend (the built-in provider only exposes
|
|
475
|
+
read-only canonical Markdown; the default graph backend remains
|
|
476
|
+
`@modelcontextprotocol/server-memory`,
|
|
341
477
|
turned on with `trellis onboard --memory on` (trellis-onboard-mcp-mode)
|
|
342
478
|
rather than hand-editing `servers.yaml`, documented in
|
|
343
479
|
`schema/servers.example.yaml`; mem0/OpenMemory and totalrecallai-class
|
|
@@ -419,6 +555,53 @@ once; it is never where you repeatedly verify against it.
|
|
|
419
555
|
```
|
|
420
556
|
scripts/sandbox.sh # interactive shell in the sandbox
|
|
421
557
|
scripts/sandbox.sh npm run dev doctor # run a command in the sandbox
|
|
558
|
+
scripts/sandbox.sh --runtime # divergent multi-agent Runtime lab
|
|
559
|
+
scripts/sandbox.sh --migration # Kiro -> Codex post-migration lab
|
|
560
|
+
scripts/sandbox.sh --management # steady-state unified management lab
|
|
561
|
+
scripts/sandbox.sh --failure # hanging-upstream isolation lab
|
|
562
|
+
scripts/sandbox-matrix.sh # run the full scenario matrix
|
|
563
|
+
scripts/agent-sandbox.sh # real Codex/Claude/Kiro/pi/Kimi CLI config smoke
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
`agent-sandbox.sh` 默认使用一次性 HOME。需要进行授权时,显式使用专用
|
|
567
|
+
Docker volume(默认名为 `trellis-agent-auth-home`):
|
|
568
|
+
|
|
569
|
+
如果宿主机已经登录 Codex 或 pi,可以显式复用单个认证文件:
|
|
570
|
+
|
|
571
|
+
```
|
|
572
|
+
scripts/agent-sandbox.sh --host-auth codex
|
|
573
|
+
scripts/agent-sandbox.sh --host-auth pi
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
这两个命令只读取宿主机对应的 `auth.json`,不读取整个 agent 配置目录。
|
|
577
|
+
|
|
578
|
+
```
|
|
579
|
+
scripts/agent-sandbox.sh --login codex
|
|
580
|
+
scripts/agent-sandbox.sh --login claude
|
|
581
|
+
scripts/agent-sandbox.sh --login kiro
|
|
582
|
+
scripts/agent-sandbox.sh --status codex
|
|
583
|
+
scripts/agent-sandbox.sh --status claude
|
|
584
|
+
scripts/agent-sandbox.sh --status kiro
|
|
585
|
+
scripts/agent-sandbox.sh --auth codex exec --help
|
|
586
|
+
scripts/agent-sandbox.sh --auth claude -p --help
|
|
587
|
+
scripts/agent-sandbox.sh --prepare
|
|
588
|
+
scripts/agent-sandbox.sh --host-auth codex
|
|
589
|
+
scripts/agent-sandbox.sh --host-auth pi
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
授权只写入这个 Docker volume,不会复制宿主机的登录态,也不会进入
|
|
593
|
+
Git、镜像或 fixture。`--login` 会分别执行真实 agent 的登录命令:
|
|
594
|
+
Codex 使用 device auth,Claude Code 使用 Claude subscription 登录,
|
|
595
|
+
Kiro CLI 使用 free license 的 device flow。Kiro 在未登录时会明确拒绝
|
|
596
|
+
`mcp list`,这是预期的授权前置条件。需要清空授权 volume 时,应由用户
|
|
597
|
+
显式执行 `docker volume rm <volume-name>`;Trellis 不会自动删除它。
|
|
598
|
+
|
|
599
|
+
授权后若要在同一登录态中运行真实 agent,使用 `--auth` 前缀,例如:
|
|
600
|
+
|
|
601
|
+
```
|
|
602
|
+
scripts/agent-sandbox.sh --prepare
|
|
603
|
+
scripts/agent-sandbox.sh --auth codex exec ...
|
|
604
|
+
scripts/agent-sandbox.sh --auth claude -p ...
|
|
422
605
|
```
|
|
423
606
|
|
|
424
607
|
Backed by OrbStack (this machine's Docker context — a Mac-native,
|
|
@@ -449,5 +632,20 @@ known findings, not just to look plausible:
|
|
|
449
632
|
exercise the case-sensitivity check.
|
|
450
633
|
- `test/fixtures/sample-mcp-server.js` — a minimal real MCP server (reads
|
|
451
634
|
`initialize` over stdio, replies with fixed `serverInfo`) so
|
|
452
|
-
|
|
453
|
-
|
|
635
|
+
`probeMcpServer` has something deterministic to handshake against inside
|
|
636
|
+
the container without depending on a real npm package or network access.
|
|
637
|
+
|
|
638
|
+
For the MCP Runtime phase, the sandbox has a separate intentionally
|
|
639
|
+
divergent fixture rather than expanding the baseline one:
|
|
640
|
+
|
|
641
|
+
```
|
|
642
|
+
scripts/sandbox.sh --runtime
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
This scenario gives Claude Code, Codex, Kiro, pi, and Kimi Code different native
|
|
646
|
+
skills, instructions, and MCP entries; adds canonical scope, per-agent
|
|
647
|
+
direct/gateway routes, `native`/`mcp`/`both` delivery, a deliberate
|
|
648
|
+
host-injected collision, and canonical memory content. The lab then runs
|
|
649
|
+
native sync, MCP sync, memory sync, secrets audit, and real MCP client
|
|
650
|
+
handshakes against Claude/Codex/pi/Kimi runtime views. All writes stay inside
|
|
651
|
+
the container-local HOME.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 900" role="img" aria-labelledby="title desc">
|
|
2
|
+
<title id="title">Agent Trellis — Unified Code Agent Runtime</title>
|
|
3
|
+
<desc id="desc">A horizontal architecture diagram showing Code Agents running on top of Agent Trellis Runtime, which manages Skills, MCP, Memory, and Instructions.</desc>
|
|
4
|
+
<defs>
|
|
5
|
+
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
|
6
|
+
<stop offset="0" stop-color="#0b1118"/>
|
|
7
|
+
<stop offset="1" stop-color="#111c24"/>
|
|
8
|
+
</linearGradient>
|
|
9
|
+
<linearGradient id="base" x1="0" y1="0" x2="0" y2="1">
|
|
10
|
+
<stop offset="0" stop-color="#273742"/>
|
|
11
|
+
<stop offset="1" stop-color="#17232c"/>
|
|
12
|
+
</linearGradient>
|
|
13
|
+
<filter id="shadow" x="-20%" y="-20%" width="140%" height="160%">
|
|
14
|
+
<feDropShadow dx="0" dy="18" stdDeviation="16" flood-color="#000" flood-opacity=".35"/>
|
|
15
|
+
</filter>
|
|
16
|
+
<marker id="arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
|
17
|
+
<path d="M 0 0 L 10 5 L 0 10 z" fill="#a9efd2"/>
|
|
18
|
+
</marker>
|
|
19
|
+
<style>
|
|
20
|
+
.sans { font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
|
|
21
|
+
.mono { font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace; }
|
|
22
|
+
.title { fill: #f5efe3; font-size: 42px; font-weight: 720; letter-spacing: -.8px; }
|
|
23
|
+
.subtitle { fill: #a9efd2; font-size: 18px; font-weight: 620; letter-spacing: 2px; }
|
|
24
|
+
.label { fill: #f5efe3; font-size: 19px; font-weight: 680; }
|
|
25
|
+
.small { fill: #9aaab4; font-size: 14px; }
|
|
26
|
+
.tiny { fill: #7f919d; font-size: 12px; }
|
|
27
|
+
</style>
|
|
28
|
+
</defs>
|
|
29
|
+
|
|
30
|
+
<rect width="1600" height="900" fill="url(#bg)"/>
|
|
31
|
+
<path d="M0 120H1600M0 780H1600M120 0V900M1480 0V900" stroke="#24333d" stroke-width="1" opacity=".55"/>
|
|
32
|
+
|
|
33
|
+
<g class="sans">
|
|
34
|
+
<text x="100" y="94" class="title">Agent Trellis</text>
|
|
35
|
+
<text x="102" y="127" class="subtitle">UNIFIED CODE AGENT RUNTIME</text>
|
|
36
|
+
<text x="1500" y="96" text-anchor="end" class="small">one runtime · many coding agents</text>
|
|
37
|
+
|
|
38
|
+
<!-- Agent layer -->
|
|
39
|
+
<text x="800" y="190" text-anchor="middle" class="small" letter-spacing="2">CODE AGENTS</text>
|
|
40
|
+
<g filter="url(#shadow)">
|
|
41
|
+
<rect x="110" y="224" width="240" height="112" rx="16" fill="#17242c" stroke="#a9efd2" stroke-width="2"/>
|
|
42
|
+
<rect x="380" y="224" width="240" height="112" rx="16" fill="#17242c" stroke="#a9efd2" stroke-width="2"/>
|
|
43
|
+
<rect x="650" y="224" width="300" height="112" rx="16" fill="#17242c" stroke="#f2b35b" stroke-width="3"/>
|
|
44
|
+
<rect x="980" y="224" width="240" height="112" rx="16" fill="#17242c" stroke="#a9efd2" stroke-width="2"/>
|
|
45
|
+
<rect x="1250" y="224" width="240" height="112" rx="16" fill="#17242c" stroke="#a9efd2" stroke-width="2"/>
|
|
46
|
+
</g>
|
|
47
|
+
<text x="230" y="274" text-anchor="middle" class="label">Claude Code</text>
|
|
48
|
+
<text x="230" y="303" text-anchor="middle" class="tiny">native adapter</text>
|
|
49
|
+
<text x="500" y="274" text-anchor="middle" class="label">Codex</text>
|
|
50
|
+
<text x="500" y="303" text-anchor="middle" class="tiny">native adapter</text>
|
|
51
|
+
<text x="800" y="274" text-anchor="middle" class="label">Kimi Code</text>
|
|
52
|
+
<text x="800" y="303" text-anchor="middle" class="tiny">Runtime-first</text>
|
|
53
|
+
<text x="1100" y="274" text-anchor="middle" class="label">Kiro</text>
|
|
54
|
+
<text x="1100" y="303" text-anchor="middle" class="tiny">native adapter</text>
|
|
55
|
+
<text x="1370" y="274" text-anchor="middle" class="label">pi</text>
|
|
56
|
+
<text x="1370" y="303" text-anchor="middle" class="tiny">bridge adapter</text>
|
|
57
|
+
|
|
58
|
+
<path d="M230 338V405M500 338V405M800 338V405M1100 338V405M1370 338V405" stroke="#a9efd2" stroke-width="2" marker-end="url(#arrow)"/>
|
|
59
|
+
|
|
60
|
+
<!-- Runtime foundation -->
|
|
61
|
+
<g filter="url(#shadow)">
|
|
62
|
+
<rect x="140" y="420" width="1320" height="220" rx="24" fill="url(#base)" stroke="#f2b35b" stroke-width="2"/>
|
|
63
|
+
<rect x="165" y="445" width="1270" height="68" rx="14" fill="#0e1820" stroke="#41515b"/>
|
|
64
|
+
<rect x="165" y="528" width="1270" height="68" rx="14" fill="#0e1820" stroke="#41515b"/>
|
|
65
|
+
</g>
|
|
66
|
+
<text x="800" y="475" text-anchor="middle" class="label" font-size="24">Agent Trellis Runtime</text>
|
|
67
|
+
<text x="800" y="501" text-anchor="middle" class="tiny">canonical source · policy · providers · adapters</text>
|
|
68
|
+
<text x="325" y="570" text-anchor="middle" class="label">Skills</text>
|
|
69
|
+
<text x="625" y="570" text-anchor="middle" class="label">MCP</text>
|
|
70
|
+
<text x="925" y="570" text-anchor="middle" class="label">Memory</text>
|
|
71
|
+
<text x="1225" y="570" text-anchor="middle" class="label">Instructions</text>
|
|
72
|
+
<path d="M325 540v-20M625 540v-20M925 540v-20M1225 540v-20" stroke="#f2b35b" stroke-width="3"/>
|
|
73
|
+
|
|
74
|
+
<!-- Canonical source -->
|
|
75
|
+
<path d="M800 640V700" stroke="#f2b35b" stroke-width="3" marker-end="url(#arrow)"/>
|
|
76
|
+
<rect x="400" y="714" width="800" height="86" rx="18" fill="#101a22" stroke="#53636c"/>
|
|
77
|
+
<text x="800" y="750" text-anchor="middle" class="label">Canonical Source</text>
|
|
78
|
+
<text x="800" y="777" text-anchor="middle" class="mono small">~/.trellis · one source of truth</text>
|
|
79
|
+
</g>
|
|
80
|
+
</svg>
|