@clawdreyhepburn/carapace 0.3.2 → 0.4.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.
@@ -0,0 +1,29 @@
1
+ /**
2
+ * MCP Aggregator — connects to multiple upstream MCP servers,
3
+ * discovers their tools, and proxies calls through Cedar authorization.
4
+ */
5
+ import type { Logger, ServerConfig, McpTool, ServerStatus, CedarEngineInterface } from "./types.js";
6
+ interface AggregatorOpts {
7
+ servers: Record<string, ServerConfig>;
8
+ cedar: CedarEngineInterface;
9
+ logger: Logger;
10
+ }
11
+ export declare class McpAggregator {
12
+ private servers;
13
+ private serverConfigs;
14
+ private cedar;
15
+ private logger;
16
+ constructor(opts: AggregatorOpts);
17
+ /** Connect to all configured MCP servers and discover tools */
18
+ connectAll(): Promise<void>;
19
+ /** Disconnect all servers */
20
+ disconnectAll(): Promise<void>;
21
+ /** List all discovered tools across all servers */
22
+ listTools(serverFilter?: string): McpTool[];
23
+ /** Get status of all servers */
24
+ getServerStatus(): Record<string, ServerStatus>;
25
+ /** Call a tool on the appropriate upstream server */
26
+ callTool(qualifiedName: string, args: Record<string, unknown>): Promise<any>;
27
+ private connectServer;
28
+ }
29
+ export {};
@@ -0,0 +1,144 @@
1
+ /**
2
+ * MCP Aggregator — connects to multiple upstream MCP servers,
3
+ * discovers their tools, and proxies calls through Cedar authorization.
4
+ */
5
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
6
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
7
+ export class McpAggregator {
8
+ servers = new Map();
9
+ serverConfigs;
10
+ cedar;
11
+ logger;
12
+ constructor(opts) {
13
+ this.serverConfigs = opts.servers;
14
+ this.cedar = opts.cedar;
15
+ this.logger = opts.logger;
16
+ }
17
+ /** Connect to all configured MCP servers and discover tools */
18
+ async connectAll() {
19
+ const entries = Object.entries(this.serverConfigs);
20
+ this.logger.info(`Connecting to ${entries.length} MCP server(s)...`);
21
+ await Promise.allSettled(entries.map(([name, config]) => this.connectServer(name, config)));
22
+ const total = this.listTools().length;
23
+ const connected = [...this.servers.values()].filter((s) => s.connected).length;
24
+ this.logger.info(`Connected: ${connected}/${entries.length} servers, ${total} tools discovered`);
25
+ }
26
+ /** Disconnect all servers */
27
+ async disconnectAll() {
28
+ for (const [name, server] of this.servers) {
29
+ try {
30
+ await server.transport?.close?.();
31
+ this.logger.debug?.(`Disconnected: ${name}`);
32
+ }
33
+ catch (err) {
34
+ this.logger.warn(`Error disconnecting ${name}: ${err}`);
35
+ }
36
+ }
37
+ this.servers.clear();
38
+ }
39
+ /** List all discovered tools across all servers */
40
+ listTools(serverFilter) {
41
+ const tools = [];
42
+ for (const server of this.servers.values()) {
43
+ if (serverFilter && server.name !== serverFilter)
44
+ continue;
45
+ for (const tool of server.tools) {
46
+ tools.push({
47
+ ...tool,
48
+ enabled: this.cedar.isToolEnabled(tool.qualifiedName),
49
+ });
50
+ }
51
+ }
52
+ return tools;
53
+ }
54
+ /** Get status of all servers */
55
+ getServerStatus() {
56
+ const status = {};
57
+ for (const [name, server] of this.servers) {
58
+ status[name] = {
59
+ connected: server.connected,
60
+ toolCount: server.tools.length,
61
+ error: server.error,
62
+ };
63
+ }
64
+ return status;
65
+ }
66
+ /** Call a tool on the appropriate upstream server */
67
+ async callTool(qualifiedName, args) {
68
+ const [serverName, toolName] = qualifiedName.split("/", 2);
69
+ const server = this.servers.get(serverName);
70
+ if (!server) {
71
+ return {
72
+ content: [{ type: "text", text: `Server not found: ${serverName}` }],
73
+ isError: true,
74
+ };
75
+ }
76
+ if (!server.connected) {
77
+ return {
78
+ content: [{ type: "text", text: `Server not connected: ${serverName}` }],
79
+ isError: true,
80
+ };
81
+ }
82
+ try {
83
+ const result = await server.client.callTool({ name: toolName, arguments: args });
84
+ return result;
85
+ }
86
+ catch (err) {
87
+ return {
88
+ content: [{ type: "text", text: `Tool call failed: ${err.message}` }],
89
+ isError: true,
90
+ };
91
+ }
92
+ }
93
+ // --- Private ---
94
+ async connectServer(name, config) {
95
+ const entry = {
96
+ name,
97
+ config,
98
+ client: new Client({ name: `mcp-cedar-proxy/${name}`, version: "0.1.0" }),
99
+ transport: null,
100
+ tools: [],
101
+ connected: false,
102
+ };
103
+ try {
104
+ if (config.transport === "stdio") {
105
+ if (!config.command)
106
+ throw new Error("stdio transport requires 'command'");
107
+ entry.transport = new StdioClientTransport({
108
+ command: config.command,
109
+ args: config.args ?? [],
110
+ env: { ...process.env, ...(config.env ?? {}) },
111
+ });
112
+ }
113
+ else if (config.transport === "http" || config.transport === "sse") {
114
+ if (!config.url)
115
+ throw new Error(`${config.transport} transport requires 'url'`);
116
+ // TODO: Add HTTP/SSE transport support
117
+ // For now, only stdio is implemented
118
+ throw new Error(`${config.transport} transport not yet implemented — use stdio`);
119
+ }
120
+ else {
121
+ throw new Error(`Unknown transport: ${config.transport}`);
122
+ }
123
+ await entry.client.connect(entry.transport);
124
+ entry.connected = true;
125
+ // Discover tools
126
+ const toolsResult = await entry.client.listTools();
127
+ entry.tools = (toolsResult.tools ?? []).map((t) => ({
128
+ name: t.name,
129
+ qualifiedName: `${name}/${t.name}`,
130
+ server: name,
131
+ description: t.description ?? "",
132
+ inputSchema: t.inputSchema,
133
+ enabled: false, // Will be resolved against Cedar policies
134
+ }));
135
+ this.logger.info(`Connected to ${name}: ${entry.tools.length} tools`);
136
+ }
137
+ catch (err) {
138
+ entry.error = err.message;
139
+ this.logger.warn(`Failed to connect to ${name}: ${err.message}`);
140
+ }
141
+ this.servers.set(name, entry);
142
+ }
143
+ }
144
+ //# sourceMappingURL=mcp-aggregator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-aggregator.js","sourceRoot":"","sources":["../src/mcp-aggregator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * PolicySource — exposes Carapace's deployment-level Cedar policies
3
+ * so that OVID-ME can query the effective policy ceiling.
4
+ */
5
+ /**
6
+ * Interface matching @clawdreyhepburn/ovid-me's PolicySource.
7
+ * Defined locally to avoid circular dependencies.
8
+ */
9
+ export interface PolicySource {
10
+ getEffectivePolicy(principal: string): Promise<string | null>;
11
+ }
12
+ /**
13
+ * Reads all .cedar files from the policy directory and returns
14
+ * the concatenated policy text. Carapace policies are deployment-wide
15
+ * (not per-principal), so all policies apply to all principals.
16
+ */
17
+ export declare class CarapacePolicySource implements PolicySource {
18
+ private policyDir;
19
+ constructor(policyDir?: string);
20
+ getEffectivePolicy(_principal: string): Promise<string | null>;
21
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * PolicySource — exposes Carapace's deployment-level Cedar policies
3
+ * so that OVID-ME can query the effective policy ceiling.
4
+ */
5
+ import { readFileSync, readdirSync, existsSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ import { homedir } from "node:os";
8
+ /**
9
+ * Reads all .cedar files from the policy directory and returns
10
+ * the concatenated policy text. Carapace policies are deployment-wide
11
+ * (not per-principal), so all policies apply to all principals.
12
+ */
13
+ export class CarapacePolicySource {
14
+ policyDir;
15
+ constructor(policyDir) {
16
+ this.policyDir = (policyDir ?? "~/.openclaw/mcp-policies/").replace("~", homedir());
17
+ }
18
+ async getEffectivePolicy(_principal) {
19
+ if (!existsSync(this.policyDir))
20
+ return null;
21
+ const files = readdirSync(this.policyDir).filter(f => f.endsWith(".cedar"));
22
+ if (files.length === 0)
23
+ return null;
24
+ const policies = files.map(f => readFileSync(join(this.policyDir, f), "utf-8"));
25
+ return policies.join("\n\n");
26
+ }
27
+ }
28
+ //# sourceMappingURL=policy-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy-source.js","sourceRoot":"","sources":["../src/policy-source.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAUlC;;;;GAIG;AACH,MAAM,OAAO,oBAAoB;IACvB,SAAS,CAAS;IAE1B,YAAY,SAAkB;QAC5B,IAAI,CAAC,SAAS,GAAG,CAAC,SAAS,IAAI,2BAA2B,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;IACtF,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,UAAkB;QACzC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC;YAAE,OAAO,IAAI,CAAC;QAE7C,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC5E,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAEpC,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QAChF,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;CACF"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Type definitions for the MCP Cedar Proxy plugin.
3
+ */
4
+ export interface Logger {
5
+ info(msg: string, ...args: any[]): void;
6
+ warn(msg: string, ...args: any[]): void;
7
+ error(msg: string, ...args: any[]): void;
8
+ debug?(msg: string, ...args: any[]): void;
9
+ }
10
+ export interface ToolDef {
11
+ name: string;
12
+ description: string;
13
+ parameters: Record<string, any>;
14
+ handler(args: any): Promise<any>;
15
+ }
16
+ export interface PluginConfig {
17
+ guiPort?: number;
18
+ servers?: Record<string, ServerConfig>;
19
+ policyDir?: string;
20
+ defaultPolicy?: "deny-all" | "allow-all";
21
+ verify?: boolean;
22
+ /** LLM proxy configuration — sits between agent and LLM provider */
23
+ proxy?: {
24
+ enabled?: boolean;
25
+ port?: number;
26
+ upstream?: {
27
+ anthropic?: {
28
+ url?: string;
29
+ apiKey: string;
30
+ };
31
+ openai?: {
32
+ url?: string;
33
+ apiKey: string;
34
+ };
35
+ };
36
+ };
37
+ }
38
+ export interface ServerConfig {
39
+ transport: "stdio" | "http" | "sse";
40
+ command?: string;
41
+ args?: string[];
42
+ env?: Record<string, string>;
43
+ url?: string;
44
+ }
45
+ export interface McpTool {
46
+ name: string;
47
+ qualifiedName: string;
48
+ server: string;
49
+ description: string;
50
+ inputSchema?: Record<string, any>;
51
+ enabled: boolean;
52
+ }
53
+ /** A gated resource — tools, shell commands, or APIs */
54
+ export interface GatedResource {
55
+ id: string;
56
+ type: "tool" | "shell" | "api";
57
+ name: string;
58
+ description: string;
59
+ source: string;
60
+ enabled: boolean;
61
+ metadata?: Record<string, unknown>;
62
+ }
63
+ export interface ShellRule {
64
+ id: string;
65
+ pattern: string;
66
+ description: string;
67
+ enabled: boolean;
68
+ }
69
+ export interface ApiRule {
70
+ id: string;
71
+ pattern: string;
72
+ method?: string;
73
+ description: string;
74
+ enabled: boolean;
75
+ }
76
+ export interface ServerStatus {
77
+ connected: boolean;
78
+ toolCount: number;
79
+ lastSeen?: number;
80
+ error?: string;
81
+ }
82
+ export interface CedarDecision {
83
+ decision: "allow" | "deny";
84
+ reasons: string[];
85
+ }
86
+ export interface AuthzRequest {
87
+ principal: string;
88
+ action: string;
89
+ resource: string;
90
+ context?: Record<string, unknown>;
91
+ }
92
+ /** Common interface for Cedar engines (homebrew and Cedarling) */
93
+ export interface CedarEngineInterface {
94
+ init(): Promise<void>;
95
+ authorize(request: AuthzRequest): Promise<CedarDecision>;
96
+ enableTool(qualifiedName: string): void;
97
+ disableTool(qualifiedName: string): void;
98
+ isToolEnabled(qualifiedName: string): boolean;
99
+ savePolicy(id: string, raw: string): void;
100
+ deletePolicy(id: string): boolean;
101
+ getDefaultPolicy(): "deny-all" | "allow-all";
102
+ getPolicies(): Array<{
103
+ id: string;
104
+ effect: string;
105
+ raw: string;
106
+ }>;
107
+ getSchema(): CedarSchemaInfo;
108
+ saveSchema(raw: string): void;
109
+ verify(): Promise<VerifyResult>;
110
+ }
111
+ export interface VerifyResult {
112
+ ok: boolean;
113
+ issues: string[];
114
+ durationMs: number;
115
+ }
116
+ export interface CedarSchemaInfo {
117
+ entities: SchemaEntity[];
118
+ actions: SchemaAction[];
119
+ raw: string;
120
+ }
121
+ export interface SchemaEntity {
122
+ name: string;
123
+ parents: string[];
124
+ attributes: SchemaAttribute[];
125
+ }
126
+ export interface SchemaAttribute {
127
+ name: string;
128
+ type: string;
129
+ optional: boolean;
130
+ }
131
+ export interface SchemaAction {
132
+ name: string;
133
+ principalTypes: string[];
134
+ resourceTypes: string[];
135
+ }
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Type definitions for the MCP Cedar Proxy plugin.
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,96 @@
1
+ <svg width="100%" viewBox="0 0 680 400" xmlns="http://www.w3.org/2000/svg" style="background:#0a0a0a;border-radius:12px">
2
+ <defs>
3
+ <marker id="ag" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
4
+ <path d="M2 1L8 5L2 9" fill="none" stroke="#c4a87c" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
5
+ </marker>
6
+ <marker id="ar" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
7
+ <path d="M2 1L8 5L2 9" fill="none" stroke="#e06060" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
8
+ </marker>
9
+ <marker id="at" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
10
+ <path d="M2 1L8 5L2 9" fill="none" stroke="#81d8d0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
11
+ </marker>
12
+ <mask id="imagine-text-gaps-g13kxz" maskUnits="userSpaceOnUse"><rect x="0" y="0" width="680" height="400" fill="white"/><rect x="19.763973236083984" y="12.194276809692383" width="118.4720458984375" height="18.639095306396484" fill="black" rx="2"/><rect x="270.71435546875" y="12.194276809692383" width="138.5712890625" height="18.639095306396484" fill="black" rx="2"/><rect x="538.2554321289062" y="12.194276809692383" width="125.48906707763672" height="18.639095306396484" fill="black" rx="2"/><rect x="39.7894287109375" y="62.7220458984375" width="78.42113494873047" height="19.111324310302734" fill="black" rx="2"/><rect x="43.7000732421875" y="106.72203826904297" width="70.5998420715332" height="19.111324310302734" fill="black" rx="2"/><rect x="35.8787841796875" y="150.7220458984375" width="86.24242401123047" height="19.111324310302734" fill="black" rx="2"/><rect x="211.489990234375" y="70.66650390625" width="73.02001190185547" height="18.166866302490234" fill="black" rx="2"/><rect x="215.101806640625" y="108.66651153564453" width="65.79638671875" height="18.166866302490234" fill="black" rx="2"/><rect x="207.87818908691406" y="146.66648864746094" width="80.24363708496094" height="18.166866302490234" fill="black" rx="2"/><rect x="329.021484375" y="138.08319091796875" width="37.9570198059082" height="16.27795124053955" fill="black" rx="2"/><rect x="325.6605529785156" y="150.08319091796875" width="44.67890167236328" height="16.27795124053955" fill="black" rx="2"/><rect x="401.4899597167969" y="81.66650390625" width="73.02001190185547" height="18.166866302490234" fill="black" rx="2"/><rect x="374.82843017578125" y="81.66650390625" width="17.171570777893066" height="18.166866302490234" fill="black" rx="2"/><rect x="397.8781433105469" y="123.66650390625" width="80.24363708496094" height="18.166866302490234" fill="black" rx="2"/><rect x="374.82843017578125" y="123.66650390625" width="17.171570777893066" height="18.166866302490234" fill="black" rx="2"/><rect x="315.101806640625" y="215.66650390625" width="65.79638671875" height="18.166866302490234" fill="black" rx="2"/><rect x="327.4830627441406" y="242.08319091796875" width="41.03388595581055" height="16.27795124053955" fill="black" rx="2"/><rect x="550.706787109375" y="64.777587890625" width="100.58637237548828" height="20.528011322021484" fill="black" rx="2"/><rect x="564.489990234375" y="98.66650390625" width="73.02001190185547" height="18.166866302490234" fill="black" rx="2"/><rect x="560.878173828125" y="138.66650390625" width="80.24363708496094" height="18.166866302490234" fill="black" rx="2"/></mask></defs>
13
+
14
+ <!-- ── Column headers ── -->
15
+ <text x="79" y="26" text-anchor="middle" font-family="sans-serif" font-size="13" font-weight="500" fill="#888" letter-spacing="0.06em" style="fill:rgb(136, 136, 136);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:sans-serif;font-size:13px;font-weight:500;text-anchor:middle;dominant-baseline:auto">LLM RESPONSE</text>
16
+ <text x="340" y="26" text-anchor="middle" font-family="sans-serif" font-size="13" font-weight="500" fill="#888" letter-spacing="0.06em" style="fill:rgb(136, 136, 136);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:sans-serif;font-size:13px;font-weight:500;text-anchor:middle;dominant-baseline:auto">CARAPACE PROXY</text>
17
+ <text x="601" y="26" text-anchor="middle" font-family="sans-serif" font-size="13" font-weight="500" fill="#888" letter-spacing="0.06em" style="fill:rgb(136, 136, 136);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:sans-serif;font-size:13px;font-weight:500;text-anchor:middle;dominant-baseline:auto">AGENT RUNTIME</text>
18
+
19
+ <!-- Lane dividers -->
20
+ <line x1="146" y1="34" x2="146" y2="390" stroke="#1e1e1e" stroke-width="1" stroke-dasharray="3 4" style="fill:rgb(0, 0, 0);stroke:rgb(30, 30, 30);color:rgb(0, 0, 0);stroke-width:1px;stroke-dasharray:3px, 4px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
21
+ <line x1="534" y1="34" x2="534" y2="390" stroke="#1e1e1e" stroke-width="1" stroke-dasharray="3 4" style="fill:rgb(0, 0, 0);stroke:rgb(30, 30, 30);color:rgb(0, 0, 0);stroke-width:1px;stroke-dasharray:3px, 4px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
22
+
23
+ <!-- ── LLM Response box ── -->
24
+ <rect x="14" y="42" width="130" height="152" rx="10" fill="none" stroke="#333" stroke-width="1" style="fill:none;stroke:rgb(51, 51, 51);color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
25
+ <rect x="24" y="54" width="110" height="36" rx="5" fill="#1a1508" stroke="#c4a87c" stroke-width="1" style="fill:rgb(26, 21, 8);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
26
+ <text x="79" y="77" text-anchor="middle" font-family="monospace" font-size="13" font-weight="500" fill="#c4a87c" style="fill:rgb(196, 168, 124);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:monospace;font-size:13px;font-weight:500;text-anchor:middle;dominant-baseline:auto">read_file</text>
27
+ <rect x="24" y="98" width="110" height="36" rx="5" fill="#1a1508" stroke="#c4a87c" stroke-width="1" style="fill:rgb(26, 21, 8);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
28
+ <text x="79" y="121" text-anchor="middle" font-family="monospace" font-size="13" font-weight="500" fill="#c4a87c" style="fill:rgb(196, 168, 124);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:monospace;font-size:13px;font-weight:500;text-anchor:middle;dominant-baseline:auto">rm -rf /</text>
29
+ <rect x="24" y="142" width="110" height="36" rx="5" fill="#1a1508" stroke="#c4a87c" stroke-width="1" style="fill:rgb(26, 21, 8);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
30
+ <text x="79" y="165" text-anchor="middle" font-family="monospace" font-size="13" font-weight="500" fill="#c4a87c" style="fill:rgb(196, 168, 124);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:monospace;font-size:13px;font-weight:500;text-anchor:middle;dominant-baseline:auto">git status</text>
31
+
32
+ <!-- Arrow LLM → Proxy -->
33
+ <line x1="144" y1="118" x2="186" y2="118" stroke="#c4a87c" stroke-width="1.5" marker-end="url(#ag)" style="fill:rgb(0, 0, 0);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
34
+
35
+ <!-- ── Carapace Proxy outer box ── -->
36
+ <rect x="188" y="42" width="304" height="240" rx="12" fill="none" stroke="#81d8d0" stroke-width="1.5" style="fill:none;stroke:rgb(129, 216, 208);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
37
+
38
+ <!-- Incoming calls listed on left side of proxy -->
39
+ <rect x="200" y="64" width="96" height="30" rx="5" fill="#1a1508" stroke="#c4a87c" stroke-width="0.8" style="fill:rgb(26, 21, 8);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:0.8px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
40
+ <text x="248" y="84" text-anchor="middle" font-family="monospace" font-size="12" fill="#c4a87c" style="fill:rgb(196, 168, 124);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:monospace;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">read_file</text>
41
+ <rect x="200" y="102" width="96" height="30" rx="5" fill="#1a1508" stroke="#c4a87c" stroke-width="0.8" style="fill:rgb(26, 21, 8);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:0.8px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
42
+ <text x="248" y="122" text-anchor="middle" font-family="monospace" font-size="12" fill="#c4a87c" style="fill:rgb(196, 168, 124);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:monospace;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">rm -rf /</text>
43
+ <rect x="200" y="140" width="96" height="30" rx="5" fill="#1a1508" stroke="#c4a87c" stroke-width="0.8" style="fill:rgb(26, 21, 8);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:0.8px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
44
+ <text x="248" y="160" text-anchor="middle" font-family="monospace" font-size="12" fill="#c4a87c" style="fill:rgb(196, 168, 124);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:monospace;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">git status</text>
45
+
46
+ <!-- Cedar policy doc (center of proxy) -->
47
+ <rect x="326" y="82" width="44" height="56" rx="4" fill="#0f0f0a" stroke="#c4a87c" stroke-width="0.9" style="fill:rgb(15, 15, 10);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:0.9px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
48
+ <path d="M358 82 L370 94 L358 94 Z" fill="#1a1508" stroke="#c4a87c" stroke-width="0.8" style="fill:rgb(26, 21, 8);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:0.8px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
49
+ <line x1="334" y1="104" x2="358" y2="104" stroke="#c4a87c" stroke-width="0.7" opacity="0.6" style="fill:rgb(0, 0, 0);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:0.7px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.6;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
50
+ <line x1="334" y1="112" x2="358" y2="112" stroke="#c4a87c" stroke-width="0.7" opacity="0.6" style="fill:rgb(0, 0, 0);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:0.7px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.6;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
51
+ <line x1="334" y1="120" x2="350" y2="120" stroke="#c4a87c" stroke-width="0.7" opacity="0.6" style="fill:rgb(0, 0, 0);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:0.7px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.6;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
52
+ <text x="348" y="150" text-anchor="middle" font-family="sans-serif" font-size="11" fill="#c4a87c" opacity="0.8" style="fill:rgb(196, 168, 124);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.8;font-family:sans-serif;font-size:11px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Cedar</text>
53
+ <text x="348" y="162" text-anchor="middle" font-family="sans-serif" font-size="11" fill="#c4a87c" opacity="0.8" style="fill:rgb(196, 168, 124);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.8;font-family:sans-serif;font-size:11px;font-weight:400;text-anchor:middle;dominant-baseline:auto">policies</text>
54
+
55
+ <!-- Arrows: incoming calls → Cedar -->
56
+ <line x1="296" y1="79" x2="324" y2="99" stroke="#c4a87c" stroke-width="0.8" stroke-dasharray="3 2" opacity="0.5" marker-end="url(#ag)" style="fill:rgb(0, 0, 0);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:0.8px;stroke-dasharray:3px, 2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.5;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
57
+ <line x1="296" y1="117" x2="324" y2="112" stroke="#c4a87c" stroke-width="0.8" stroke-dasharray="3 2" opacity="0.5" marker-end="url(#ag)" style="fill:rgb(0, 0, 0);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:0.8px;stroke-dasharray:3px, 2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.5;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
58
+ <line x1="296" y1="155" x2="324" y2="126" stroke="#c4a87c" stroke-width="0.8" stroke-dasharray="3 2" opacity="0.5" marker-end="url(#ag)" style="fill:rgb(0, 0, 0);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:0.8px;stroke-dasharray:3px, 2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.5;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
59
+
60
+ <!-- Permitted outputs (right side of proxy) -->
61
+ <rect x="396" y="75" width="84" height="30" rx="5" fill="#1a1508" stroke="#c4a87c" stroke-width="1" style="fill:rgb(26, 21, 8);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
62
+ <text x="438" y="95" text-anchor="middle" font-family="monospace" font-size="12" font-weight="500" fill="#c4a87c" style="fill:rgb(196, 168, 124);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:monospace;font-size:12px;font-weight:500;text-anchor:middle;dominant-baseline:auto">read_file</text>
63
+ <text x="388" y="95" text-anchor="end" font-family="sans-serif" font-size="12" fill="#c4a87c" opacity="0.7" style="fill:rgb(196, 168, 124);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:sans-serif;font-size:12px;font-weight:400;text-anchor:end;dominant-baseline:auto">✓</text>
64
+
65
+ <rect x="396" y="117" width="84" height="30" rx="5" fill="#1a1508" stroke="#c4a87c" stroke-width="1" style="fill:rgb(26, 21, 8);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
66
+ <text x="438" y="137" text-anchor="middle" font-family="monospace" font-size="12" font-weight="500" fill="#c4a87c" style="fill:rgb(196, 168, 124);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:monospace;font-size:12px;font-weight:500;text-anchor:middle;dominant-baseline:auto">git status</text>
67
+ <text x="388" y="137" text-anchor="end" font-family="sans-serif" font-size="12" fill="#c4a87c" opacity="0.7" style="fill:rgb(196, 168, 124);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:sans-serif;font-size:12px;font-weight:400;text-anchor:end;dominant-baseline:auto">✓</text>
68
+
69
+ <!-- Arrows: Cedar → permitted outputs -->
70
+ <line x1="370" y1="101" x2="394" y2="90" stroke="#c4a87c" stroke-width="0.8" stroke-dasharray="3 2" opacity="0.5" marker-end="url(#ag)" mask="url(#imagine-text-gaps-g13kxz)" style="fill:rgb(0, 0, 0);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:0.8px;stroke-dasharray:3px, 2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.5;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
71
+ <line x1="370" y1="113" x2="394" y2="132" stroke="#c4a87c" stroke-width="0.8" stroke-dasharray="3 2" opacity="0.5" marker-end="url(#ag)" mask="url(#imagine-text-gaps-g13kxz)" style="fill:rgb(0, 0, 0);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:0.8px;stroke-dasharray:3px, 2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.5;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
72
+
73
+ <!-- Red drop arrow: Cedar → denied block -->
74
+ <path d="M348,138 L348,205" fill="none" stroke="#e06060" stroke-width="1.4" stroke-dasharray="4 2" marker-end="url(#ar)" mask="url(#imagine-text-gaps-g13kxz)" style="fill:none;stroke:rgb(224, 96, 96);color:rgb(0, 0, 0);stroke-width:1.4px;stroke-dasharray:4px, 2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
75
+
76
+ <!-- Denied block -->
77
+ <rect x="296" y="207" width="104" height="34" rx="5" fill="#130808" stroke="#e06060" stroke-width="1" style="fill:rgb(19, 8, 8);stroke:rgb(224, 96, 96);color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
78
+ <text x="348" y="229" text-anchor="middle" font-family="monospace" font-size="12" font-weight="500" fill="#e06060" style="fill:rgb(224, 96, 96);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:monospace;font-size:12px;font-weight:500;text-anchor:middle;dominant-baseline:auto">rm -rf /</text>
79
+ <line x1="298" y1="224" x2="398" y2="224" stroke="#e06060" stroke-width="1.5" stroke-linecap="round" mask="url(#imagine-text-gaps-g13kxz)" style="fill:rgb(0, 0, 0);stroke:rgb(224, 96, 96);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:round;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
80
+ <!-- X mark -->
81
+ <line x1="282" y1="214" x2="292" y2="224" stroke="#e06060" stroke-width="1.8" stroke-linecap="round" style="fill:rgb(0, 0, 0);stroke:rgb(224, 96, 96);color:rgb(0, 0, 0);stroke-width:1.8px;stroke-linecap:round;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
82
+ <line x1="292" y1="214" x2="282" y2="224" stroke="#e06060" stroke-width="1.8" stroke-linecap="round" style="fill:rgb(0, 0, 0);stroke:rgb(224, 96, 96);color:rgb(0, 0, 0);stroke-width:1.8px;stroke-linecap:round;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
83
+ <text x="348" y="254" text-anchor="middle" font-family="sans-serif" font-size="11" fill="#c05050" style="fill:rgb(192, 80, 80);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:sans-serif;font-size:11px;font-weight:400;text-anchor:middle;dominant-baseline:auto">denied</text>
84
+
85
+ <!-- Arrow Proxy → Agent -->
86
+ <line x1="480" y1="105" x2="534" y2="105" stroke="#c4a87c" stroke-width="1.5" marker-end="url(#ag)" style="fill:rgb(0, 0, 0);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
87
+
88
+ <!-- ── Agent Runtime box ── -->
89
+ <rect x="536" y="42" width="130" height="152" rx="10" fill="#161208" stroke="#c4a87c" stroke-width="1.2" style="fill:rgb(22, 18, 8);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:1.2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
90
+ <text x="601" y="80" text-anchor="middle" font-family="sans-serif" font-size="14" font-weight="500" fill="#c4a87c" style="fill:rgb(196, 168, 124);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">Agent Runtime</text>
91
+ <rect x="552" y="92" width="98" height="30" rx="5" fill="#1a1508" stroke="#c4a87c" stroke-width="0.8" style="fill:rgb(26, 21, 8);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:0.8px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
92
+ <text x="601" y="112" text-anchor="middle" font-family="monospace" font-size="12" fill="#c4a87c" style="fill:rgb(196, 168, 124);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:monospace;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">read_file</text>
93
+ <rect x="552" y="132" width="98" height="30" rx="5" fill="#1a1508" stroke="#c4a87c" stroke-width="0.8" style="fill:rgb(26, 21, 8);stroke:rgb(196, 168, 124);color:rgb(0, 0, 0);stroke-width:0.8px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Anthropic Sans&quot;, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
94
+ <text x="601" y="152" text-anchor="middle" font-family="monospace" font-size="12" fill="#c4a87c" style="fill:rgb(196, 168, 124);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:monospace;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">git status</text>
95
+
96
+ </svg>