@zeroroot-ai/gibson-mcp 0.1.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.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +160 -0
  3. package/dist/ambient.d.ts +7 -0
  4. package/dist/ambient.js +18 -0
  5. package/dist/ask.d.ts +70 -0
  6. package/dist/ask.js +111 -0
  7. package/dist/build.d.ts +83 -0
  8. package/dist/build.js +209 -0
  9. package/dist/cli.d.ts +88 -0
  10. package/dist/cli.js +186 -0
  11. package/dist/config.d.ts +45 -0
  12. package/dist/config.js +54 -0
  13. package/dist/discovery.d.ts +57 -0
  14. package/dist/discovery.js +132 -0
  15. package/dist/flags.d.ts +32 -0
  16. package/dist/flags.js +85 -0
  17. package/dist/generated/tools.d.ts +15 -0
  18. package/dist/generated/tools.js +276 -0
  19. package/dist/helpers/componentize.d.ts +19 -0
  20. package/dist/helpers/componentize.js +106 -0
  21. package/dist/helpers/context.d.ts +19 -0
  22. package/dist/helpers/context.js +19 -0
  23. package/dist/helpers/coverage.d.ts +23 -0
  24. package/dist/helpers/coverage.js +119 -0
  25. package/dist/helpers/delegate.d.ts +4 -0
  26. package/dist/helpers/delegate.js +182 -0
  27. package/dist/helpers/findings.d.ts +24 -0
  28. package/dist/helpers/findings.js +118 -0
  29. package/dist/helpers/index.d.ts +17 -0
  30. package/dist/helpers/index.js +24 -0
  31. package/dist/helpers/knowledge.d.ts +16 -0
  32. package/dist/helpers/knowledge.js +161 -0
  33. package/dist/helpers/tools.d.ts +113 -0
  34. package/dist/helpers/tools.js +80 -0
  35. package/dist/http.d.ts +57 -0
  36. package/dist/http.js +137 -0
  37. package/dist/inbox.d.ts +88 -0
  38. package/dist/inbox.js +176 -0
  39. package/dist/index.d.ts +23 -0
  40. package/dist/index.js +25 -0
  41. package/dist/log.d.ts +4 -0
  42. package/dist/log.js +5 -0
  43. package/dist/main.d.ts +2 -0
  44. package/dist/main.js +61 -0
  45. package/dist/mode.d.ts +32 -0
  46. package/dist/mode.js +21 -0
  47. package/dist/registry.d.ts +83 -0
  48. package/dist/registry.js +133 -0
  49. package/dist/resources.d.ts +63 -0
  50. package/dist/resources.js +98 -0
  51. package/dist/rpc.d.ts +80 -0
  52. package/dist/rpc.js +184 -0
  53. package/dist/schema.d.ts +31 -0
  54. package/dist/schema.js +143 -0
  55. package/dist/server.d.ts +22 -0
  56. package/dist/server.js +70 -0
  57. package/dist/session.d.ts +41 -0
  58. package/dist/session.js +170 -0
  59. package/dist/source.d.ts +30 -0
  60. package/dist/source.js +23 -0
  61. package/dist/state.d.ts +29 -0
  62. package/dist/state.js +37 -0
  63. package/dist/tls.d.ts +1 -0
  64. package/dist/tls.js +19 -0
  65. package/dist/tool.d.ts +17 -0
  66. package/dist/tool.js +22 -0
  67. package/dist/tools/connect.d.ts +24 -0
  68. package/dist/tools/connect.js +115 -0
  69. package/dist/tools/result.d.ts +7 -0
  70. package/dist/tools/result.js +16 -0
  71. package/dist/tools/status.d.ts +5 -0
  72. package/dist/tools/status.js +36 -0
  73. package/dist/turn.d.ts +75 -0
  74. package/dist/turn.js +95 -0
  75. package/package.json +58 -0
@@ -0,0 +1,132 @@
1
+ import { callGibsonTool, listGibsonPlugins, listGibsonTools, queryGibsonPlugin } from "@zeroroot-ai/sdk";
2
+ import { z } from "zod";
3
+ import { formatToolResult } from "./helpers/tools.js";
4
+ import { TAG } from "./log.js";
5
+ import { defineTool } from "./tool.js";
6
+ /**
7
+ * Checked-in platform tools and plugins, discovered at runtime.
8
+ *
9
+ * The fleet changes while a session runs: a tool a person enrols now should
10
+ * be callable in the same session. So discovery repeats, and the registry's
11
+ * `tools/list_changed` notification is what tells every attached host to
12
+ * ask again.
13
+ *
14
+ * Each discovered tool registers on its own, named `gibson_<tool>`, so the
15
+ * model sees its description rather than one opaque dispatcher. The catalog
16
+ * carries no JSON Schema, only a proto message type name, so the wrapper
17
+ * takes one free-form `input` object and names the type it must match.
18
+ * `gibson_call_tool` stays registered either way: discovery answers
19
+ * `Unimplemented` on some daemons (gibson#1186).
20
+ */
21
+ export const DISCOVERY_INTERVAL_MS = 60_000;
22
+ /** `gibson_<tool>`, with anything not legal in an MCP tool name replaced. */
23
+ export function toolKey(name) {
24
+ return `gibson_${name.replace(/[^A-Za-z0-9_-]/g, "_")}`;
25
+ }
26
+ export function pluginKey(name) {
27
+ return `gibson_plugin_${name.replace(/[^A-Za-z0-9_-]/g, "_")}`;
28
+ }
29
+ export function discoveredTool(session, t) {
30
+ const schemaNote = t.inputMessageType ? ` Input must match the Gibson message type ${t.inputMessageType}.` : "";
31
+ return defineTool({
32
+ name: toolKey(t.name),
33
+ description: `${t.description || `Gibson tool "${t.name}"`}${schemaNote} Runs through the Gibson harness, authorized and metered.`,
34
+ input: {
35
+ input: z.record(z.string(), z.unknown()).describe(`Input object for the ${t.name} tool.`),
36
+ timeout_ms: z.number().int().min(1).optional().describe("Per-call timeout in milliseconds."),
37
+ },
38
+ handler: async (args) => formatToolResult(t.name, await callGibsonTool(session.clients.component, { name: t.name, input: args.input, ...(args.timeout_ms ? { timeoutMs: args.timeout_ms } : {}) })),
39
+ });
40
+ }
41
+ export function discoveredPlugin(session, p) {
42
+ const methods = p.methods.length > 0 ? ` Methods: ${p.methods.join(", ")}.` : "";
43
+ return defineTool({
44
+ name: pluginKey(p.name),
45
+ description: `${p.description || `Gibson plugin "${p.name}"`}${methods} Runs through the Gibson harness, authorized and metered.`,
46
+ input: {
47
+ method: z.string().describe(`Method to call on the ${p.name} plugin.`),
48
+ params: z.record(z.string(), z.unknown()).optional().describe("Method parameters."),
49
+ },
50
+ handler: async (args) => formatToolResult(`${p.name}.${args.method}`, await queryGibsonPlugin(session.clients.component, { plugin: p.name, method: args.method, params: args.params ?? {} })),
51
+ });
52
+ }
53
+ /**
54
+ * Poll the catalog and keep the registered set equal to it.
55
+ *
56
+ * A pass that cannot reach the catalog leaves the set alone. Dropping every
57
+ * discovered tool because one poll failed would take working tools away from
58
+ * a session mid-task over a transient fault.
59
+ */
60
+ export function startDiscovery(opts) {
61
+ const { group, session, log } = opts;
62
+ const interval = opts.intervalMs ?? DISCOVERY_INTERVAL_MS;
63
+ const registered = new Map();
64
+ let stopped = false;
65
+ let timer;
66
+ const refresh = async () => {
67
+ const [discovery, plugins] = await Promise.all([
68
+ listGibsonTools(session.clients.component),
69
+ listGibsonPlugins(session.clients.component).catch(() => []),
70
+ ]);
71
+ if (discovery.unavailable && plugins.length === 0) {
72
+ return { tools: 0, plugins: 0, note: discovery.unavailable, changed: false };
73
+ }
74
+ const wanted = new Map();
75
+ for (const t of discovery.tools)
76
+ wanted.set(toolKey(t.name), discoveredTool(session, t));
77
+ for (const p of plugins)
78
+ wanted.set(pluginKey(p.name), discoveredPlugin(session, p));
79
+ // Collected before anything is removed: the map is mutated below, and
80
+ // deleting from it mid-iteration skips entries.
81
+ const gone = [];
82
+ for (const name of registered.keys()) {
83
+ if (!wanted.has(name))
84
+ gone.push(name);
85
+ }
86
+ let changed = false;
87
+ group.batch(() => {
88
+ for (const name of gone) {
89
+ group.remove(name);
90
+ registered.delete(name);
91
+ changed = true;
92
+ }
93
+ for (const [name, def] of wanted) {
94
+ if (registered.has(name))
95
+ continue;
96
+ // A helper or a generated tool already owns this name: leave it be.
97
+ if (group.has(name))
98
+ continue;
99
+ group.register(def);
100
+ registered.set(name, def);
101
+ changed = true;
102
+ }
103
+ });
104
+ return { tools: discovery.tools.length, plugins: plugins.length, ...(discovery.unavailable ? { note: discovery.unavailable } : {}), changed };
105
+ };
106
+ const tick = async () => {
107
+ if (stopped)
108
+ return;
109
+ try {
110
+ const outcome = await refresh();
111
+ if (outcome.changed)
112
+ log(`${TAG} discovery: ${outcome.tools} tool(s), ${outcome.plugins} plugin(s) registered`);
113
+ }
114
+ catch (e) {
115
+ // Keep what is registered: a transient fault must not take working
116
+ // tools away from a session mid-task.
117
+ log(`${TAG} discovery failed, keeping the current set: ${e.message}`);
118
+ }
119
+ };
120
+ if (interval > 0) {
121
+ timer = setInterval(() => void tick(), interval);
122
+ timer.unref?.();
123
+ }
124
+ return {
125
+ refresh,
126
+ stop: () => {
127
+ stopped = true;
128
+ if (timer)
129
+ clearInterval(timer);
130
+ },
131
+ };
132
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The bin's flags.
3
+ *
4
+ * - `--transport stdio` (default): the laptop path. The host spawns the
5
+ * server and talks over stdin and stdout.
6
+ * - `--transport http --listen 127.0.0.1:<port>`: the sandbox path. The
7
+ * member driver runs one server for the life of the sandbox and swaps
8
+ * the grant per turn through it (slice A4). Only a loopback address is
9
+ * accepted: a control endpoint that swaps credentials never sits on a
10
+ * network interface.
11
+ * - `--stream-limit <n>`: how many messages a server-streaming RPC tool
12
+ * collects before it returns with `truncated: true` (slice A2).
13
+ */
14
+ export type TransportKind = "stdio" | "http";
15
+ export interface Listen {
16
+ host: string;
17
+ port: number;
18
+ }
19
+ export interface Flags {
20
+ transport: TransportKind;
21
+ listen: Listen;
22
+ streamLimit: number;
23
+ help: boolean;
24
+ version: boolean;
25
+ }
26
+ export declare const DEFAULT_LISTEN = "127.0.0.1:7788";
27
+ export declare const DEFAULT_STREAM_LIMIT = 500;
28
+ export declare function isLoopback(host: string): boolean;
29
+ /** `host:port`, `[v6]:port`, or a bare port on 127.0.0.1. */
30
+ export declare function parseListen(raw: string): Listen;
31
+ export declare function parseFlags(argv: string[]): Flags;
32
+ export declare function usage(): string;
package/dist/flags.js ADDED
@@ -0,0 +1,85 @@
1
+ import { parseArgs } from "node:util";
2
+ export const DEFAULT_LISTEN = "127.0.0.1:7788";
3
+ export const DEFAULT_STREAM_LIMIT = 500;
4
+ const LOOPBACK_HOSTS = new Set(["127.0.0.1", "::1", "localhost"]);
5
+ export function isLoopback(host) {
6
+ return LOOPBACK_HOSTS.has(host) || host.startsWith("127.");
7
+ }
8
+ /** `host:port`, `[v6]:port`, or a bare port on 127.0.0.1. */
9
+ export function parseListen(raw) {
10
+ const s = raw.trim();
11
+ let host;
12
+ let portText;
13
+ const v6 = /^\[([^\]]+)\]:(\d+)$/.exec(s);
14
+ if (v6) {
15
+ host = v6[1];
16
+ portText = v6[2];
17
+ }
18
+ else if (/^\d+$/.test(s)) {
19
+ host = "127.0.0.1";
20
+ portText = s;
21
+ }
22
+ else {
23
+ const i = s.lastIndexOf(":");
24
+ if (i <= 0)
25
+ throw new Error(`--listen ${JSON.stringify(raw)} is not host:port`);
26
+ host = s.slice(0, i);
27
+ portText = s.slice(i + 1);
28
+ }
29
+ const port = Number(portText);
30
+ if (!Number.isInteger(port) || port < 0 || port > 65535)
31
+ throw new Error(`--listen ${JSON.stringify(raw)} has no valid port`);
32
+ if (!isLoopback(host)) {
33
+ throw new Error(`--listen ${JSON.stringify(raw)} is not a loopback address; the HTTP transport serves localhost only`);
34
+ }
35
+ return { host, port };
36
+ }
37
+ export function parseFlags(argv) {
38
+ const { values } = parseArgs({
39
+ args: argv,
40
+ strict: true,
41
+ allowPositionals: false,
42
+ options: {
43
+ transport: { type: "string", default: "stdio" },
44
+ listen: { type: "string", default: DEFAULT_LISTEN },
45
+ "stream-limit": { type: "string", default: String(DEFAULT_STREAM_LIMIT) },
46
+ help: { type: "boolean", short: "h", default: false },
47
+ version: { type: "boolean", default: false },
48
+ },
49
+ });
50
+ const transport = values.transport;
51
+ if (transport !== "stdio" && transport !== "http") {
52
+ throw new Error(`--transport must be stdio or http, got ${JSON.stringify(transport)}`);
53
+ }
54
+ const streamLimit = Number(values["stream-limit"]);
55
+ if (!Number.isInteger(streamLimit) || streamLimit < 1) {
56
+ throw new Error(`--stream-limit must be a positive integer, got ${JSON.stringify(values["stream-limit"])}`);
57
+ }
58
+ return {
59
+ transport,
60
+ listen: parseListen(values.listen),
61
+ streamLimit,
62
+ help: values.help,
63
+ version: values.version,
64
+ };
65
+ }
66
+ export function usage() {
67
+ return [
68
+ "gibson-mcp: the Gibson MCP server.",
69
+ "",
70
+ " gibson-mcp [--transport stdio]",
71
+ ` gibson-mcp --transport http [--listen ${DEFAULT_LISTEN}]`,
72
+ "",
73
+ "Flags:",
74
+ " --transport stdio|http stdio (default) for a host that spawns the server; http for a sandbox.",
75
+ ` --listen host:port loopback address for the HTTP transport (default ${DEFAULT_LISTEN}).`,
76
+ ` --stream-limit n messages a streaming RPC tool returns before it truncates (default ${DEFAULT_STREAM_LIMIT}).`,
77
+ " --version print the package version.",
78
+ " -h, --help this text.",
79
+ "",
80
+ "Check-in source, decided from the environment, never mixed:",
81
+ " GIBSON_CG_JWT + GIBSON_CALLBACK_ENDPOINT dispatched: the daemon launched this process.",
82
+ " GIBSON_BOOTSTRAP_TOKEN (no host key yet) pre-minted token: first check-in, host key thereafter.",
83
+ " otherwise enrolled: host key, or gibson_login then gibson_connect.",
84
+ ].join("\n");
85
+ }
@@ -0,0 +1,15 @@
1
+ import type { GenService } from "@bufbuild/protobuf/codegenv2";
2
+ export interface GeneratedMethodDoc {
3
+ /** The generated property name, which is also the client method. */
4
+ method: string;
5
+ /** The RPC's leading proto comment, as one line. */
6
+ description: string;
7
+ }
8
+ export interface GeneratedServiceDoc {
9
+ service: GenService<any>;
10
+ description: string;
11
+ methods: GeneratedMethodDoc[];
12
+ }
13
+ export declare const GENERATED_SERVICES: GeneratedServiceDoc[];
14
+ /** Every RPC of every service, counted at generate time. */
15
+ export declare const GENERATED_RPC_COUNT = 188;
@@ -0,0 +1,276 @@
1
+ import { AgentService } from "@zeroroot-ai/sdk/gen/gibson/agent/v1/agent_pb.js";
2
+ import { AgentIdentityService } from "@zeroroot-ai/sdk/gen/gibson/agentidentity/v1/agent_identity_pb.js";
3
+ import { BankService } from "@zeroroot-ai/sdk/gen/gibson/bank/v1/bank_pb.js";
4
+ import { ComponentService } from "@zeroroot-ai/sdk/gen/gibson/component/v1/component_pb.js";
5
+ import { DaemonService } from "@zeroroot-ai/sdk/gen/gibson/daemon/v1/daemon_pb.js";
6
+ import { GraphService } from "@zeroroot-ai/sdk/gen/gibson/graph/v1/graph_pb.js";
7
+ import { HarnessCallbackService } from "@zeroroot-ai/sdk/gen/gibson/harness/v1/harness_callback_pb.js";
8
+ import { IdentityService } from "@zeroroot-ai/sdk/gen/gibson/identity/v1/identity_pb.js";
9
+ import { JobService } from "@zeroroot-ai/sdk/gen/gibson/job/v1/job_pb.js";
10
+ import { PluginInvokeService } from "@zeroroot-ai/sdk/gen/gibson/plugin/v1/invoke_pb.js";
11
+ import { PluginAdminService } from "@zeroroot-ai/sdk/gen/gibson/pluginadmin/v1/plugin_admin_pb.js";
12
+ import { ToolService } from "@zeroroot-ai/sdk/gen/gibson/tool/v1/tool_pb.js";
13
+ export const GENERATED_SERVICES = [
14
+ {
15
+ service: AgentService,
16
+ description: "",
17
+ methods: [
18
+ { method: "getDescriptor", description: "" },
19
+ { method: "getSlotSchema", description: "" },
20
+ { method: "execute", description: "" },
21
+ { method: "health", description: "" },
22
+ ],
23
+ },
24
+ {
25
+ service: AgentIdentityService,
26
+ description: "AgentIdentityService provisions and manages machine identities for agents, tools, and plugins.",
27
+ methods: [
28
+ { method: "createAgentIdentity", description: "CreateAgentIdentity provisions a new machine identity for an agent, tool, or plugin. Returns a one-time capability-grant bootstrap_token that cannot be recovered after this call (ADR-0045 — the unified enrollment credential for every kind)." },
29
+ { method: "listAgentIdentities", description: "ListAgentIdentities returns all agent/tool/plugin identities provisioned in the caller's tenant, with optional kind filtering and pagination." },
30
+ { method: "revokeAgentIdentity", description: "RevokeAgentIdentity permanently revokes a machine identity. Existing JWTs stop validating within the IdP token TTL (<=60 seconds). Idempotent on already-revoked principals (returns NotFound)." },
31
+ ],
32
+ },
33
+ {
34
+ service: BankService,
35
+ description: "BankService creates, reads, updates and deletes banks, lists their members, and relays the in-sandbox subscription sign in to the owner. People and platform services call it. A dispatched component reaches banks through gibson.job.v1.JobService and the harness callback under its own grant, not through this service.",
36
+ methods: [
37
+ { method: "createBank", description: "CreateBank declares a bank. The daemon starts to launch members until desired_count of them run. The caller owns the bank unless tenant_owned is true." },
38
+ { method: "getBank", description: "GetBank returns one bank by id." },
39
+ { method: "listBanks", description: "ListBanks returns the banks of the caller's tenant, newest first." },
40
+ { method: "updateBank", description: "UpdateBank changes desired_count and the policies of a bank. A field that is absent keeps its value. The login shape, the agent and the model are fixed at creation." },
41
+ { method: "deleteBank", description: "DeleteBank drains every member, closes their open jobs with verdict ABANDONED, and removes the bank." },
42
+ { method: "listMembers", description: "ListMembers returns the members of a bank with their last reported status." },
43
+ { method: "startSignIn", description: "StartSignIn asks a member at NEEDS_SIGN_IN to run the Anthropic sign in inside its sandbox. Follow it with StreamSignIn to read the URL and the code prompt, then SubmitSignInCode. Only the owner signs in: the subscription is theirs." },
44
+ { method: "streamSignIn", description: "StreamSignIn relays the sign-in flow of one member: the URL to open, the code prompt when the flow asks for one, and done or error at the end. The stream closes after done or error." },
45
+ { method: "submitSignInCode", description: "SubmitSignInCode passes the code the person got from Anthropic to the sign-in flow that runs inside the member's sandbox." },
46
+ ],
47
+ },
48
+ {
49
+ service: ComponentService,
50
+ description: "ComponentService is the central gRPC service that all Gibson components (agents, tools, and plugins) connect to. Components register themselves, receive work via long-polling, submit results, and access harness operations (LLM completion, tool calls, plugin queries, findings, memory) through the proxy RPCs defined here.",
51
+ methods: [
52
+ { method: "registerComponent", description: "RegisterComponent announces a component to Gibson and receives connection configuration including heartbeat and poll intervals." },
53
+ { method: "heartbeat", description: "Heartbeat sends a periodic health pulse. The response indicates whether the component is still considered registered and may carry config updates." },
54
+ { method: "pollWork", description: "PollWork long-polls for work items assigned to this component instance. Returns when a work item is available or the server-side timeout expires. Authorized by can_poll_work, not can_execute: PollWork/SubmitResult are the receive-side of dispatch (how a component gets its work), distinct from CallTool/RunMission's drive-side (how a component directs the platform). Sharing can_execute would let a poll-only grant also drive missions. can_poll_work is modeled as `can_execute or can_receive_work`, so existing can_execute holders keep polling unchanged." },
55
+ { method: "submitResult", description: "SubmitResult returns the execution result for a previously polled work item. Authorized by can_poll_work — see PollWork's comment for why this receive-side RPC does not share can_execute with the drive-side RPCs." },
56
+ { method: "complete", description: "Complete proxies an LLM completion request through the agent harness." },
57
+ { method: "completeStream", description: "CompleteStream proxies a streaming LLM completion request through the agent harness." },
58
+ { method: "callTool", description: "CallTool proxies a tool execution request through the agent harness." },
59
+ { method: "queryPlugin", description: "QueryPlugin proxies a plugin query request through the agent harness." },
60
+ { method: "submitFinding", description: "SubmitFinding submits a security finding through the agent harness." },
61
+ { method: "listAvailablePlugins", description: "ListAvailablePlugins returns all plugins registered in the system along with their catalog metadata, health status, and configuration schema." },
62
+ { method: "enablePlugin", description: "EnablePlugin activates a plugin for the calling tenant, optionally supplying an initial configuration JSON blob." },
63
+ { method: "disablePlugin", description: "DisablePlugin deactivates a plugin for the calling tenant." },
64
+ { method: "updatePluginConfig", description: "UpdatePluginConfig replaces the configuration for an already-enabled plugin." },
65
+ { method: "getPluginConfig", description: "GetPluginConfig retrieves the current configuration and schema for a plugin." },
66
+ { method: "testPluginConnection", description: "TestPluginConnection validates connectivity and credentials for a plugin without persisting any state changes." },
67
+ { method: "listTenantPlugins", description: "ListTenantPlugins returns the plugin access records for the calling tenant." },
68
+ { method: "completeWithTools", description: "CompleteWithTools proxies an LLM completion with tool definitions for function-calling support. Returns the response including any tool calls." },
69
+ { method: "completeStructured", description: "CompleteStructured proxies an LLM completion requesting JSON output conforming to the supplied schema." },
70
+ { method: "callToolStream", description: "CallToolStream proxies a tool execution with server-side streaming of progress, partial results, warnings, and the final output." },
71
+ { method: "queueToolWork", description: "QueueToolWork submits a batch of tool invocations for parallel execution and returns a job ID for tracking." },
72
+ { method: "toolResults", description: "ToolResults streams results for a previously queued tool batch as each invocation completes." },
73
+ { method: "listTools", description: "ListTools returns descriptors for all tools visible to the caller's tenant." },
74
+ { method: "delegateToAgent", description: "DelegateToAgent dispatches a sub-task to another agent and returns its result." },
75
+ { method: "listAgents", description: "ListAgents returns descriptors for all agents visible to the caller's tenant." },
76
+ { method: "queryNodes", description: "QueryNodes searches the knowledge graph using hybrid vector + graph scoring." },
77
+ { method: "findSimilarAttacks", description: "FindSimilarAttacks returns attack patterns semantically similar to the given content." },
78
+ { method: "getAttackChains", description: "GetAttackChains returns multi-hop attack paths from a starting technique." },
79
+ { method: "findSimilarFindings", description: "FindSimilarFindings returns findings semantically similar to the given finding." },
80
+ { method: "getRelatedFindings", description: "GetRelatedFindings returns findings related to the given finding via graph edges." },
81
+ { method: "getFindings", description: "GetFindings queries previously submitted findings with optional filters." },
82
+ { method: "getRunFindings", description: "GetRunFindings queries findings scoped to a specific mission run or across all runs." },
83
+ { method: "createMission", description: "CreateMission creates a new sub-mission." },
84
+ { method: "runMission", description: "RunMission queues a mission for execution." },
85
+ { method: "getMissionStatus", description: "GetMissionStatus returns the current status of a mission." },
86
+ { method: "waitMission", description: "WaitMission blocks until a mission completes or the timeout expires." },
87
+ { method: "listMissions", description: "ListMissions returns missions matching the given filter." },
88
+ { method: "cancelMission", description: "CancelMission requests cancellation of a running mission." },
89
+ { method: "getMissionResults", description: "GetMissionResults returns the final results of a completed mission." },
90
+ { method: "getCredential", description: "GetCredential retrieves a tenant-scoped credential by name." },
91
+ { method: "getTaxonomySchema", description: "GetTaxonomySchema returns the current taxonomy definition." },
92
+ { method: "getMissionRunHistory", description: "GetMissionRunHistory returns summaries of previous mission runs." },
93
+ { method: "reportStepHints", description: "ReportStepHints reports planning step hints from an agent back to the orchestrator." },
94
+ ],
95
+ },
96
+ {
97
+ service: DaemonService,
98
+ description: "DaemonService provides the gRPC API for Gibson daemon client communication. This service exposes operational daemon functionality including mission execution, agent management, and real-time event streaming for the TUI and SDK clients.",
99
+ methods: [
100
+ { method: "connect", description: "Connect establishes a client connection to the daemon. Returns connection metadata and daemon version info." },
101
+ { method: "ping", description: "Ping checks if the daemon is responsive. Used for health checks and connection validation." },
102
+ { method: "status", description: "Status returns the current daemon status including uptime, service endpoints, and component counts." },
103
+ { method: "subscribe", description: "Subscribe establishes an event stream for TUI real-time updates. Streams mission events, agent events, finding events, etc." },
104
+ { method: "runMission", description: "RunMission starts a mission and streams execution events. The stream remains open until the mission completes or is stopped." },
105
+ { method: "stopMission", description: "StopMission gracefully stops a running mission." },
106
+ { method: "createMission", description: "CreateMission creates a new mission with target and mission-definition reference. Supports both referenced and inline configurations." },
107
+ { method: "createTarget", description: "CreateTarget registers a new target and returns its server-minted UUID. The id field of the supplied target is ignored; the daemon mints it." },
108
+ { method: "getTarget", description: "GetTarget returns a single target by its UUID." },
109
+ { method: "listTargets", description: "ListTargets returns the calling tenant's targets, narrowed by TargetFilter." },
110
+ { method: "updateTarget", description: "UpdateTarget replaces a target's metadata. The id field is the lookup key and is never changed." },
111
+ { method: "deleteTarget", description: "DeleteTarget removes a target by its UUID." },
112
+ { method: "listMissions", description: "ListMissions returns all missions (past and active)." },
113
+ { method: "pauseMission", description: "PauseMission pauses a running mission at the next clean checkpoint boundary. If force is true, pauses immediately without waiting for a clean boundary." },
114
+ { method: "resumeMission", description: "ResumeMission resumes a paused mission from its last checkpoint. Returns a stream of mission events as execution continues." },
115
+ { method: "getMissionHistory", description: "GetMissionHistory returns all runs for a mission name, showing the complete history of mission executions with the same mission name." },
116
+ { method: "listMissionDefinitions", description: "ListMissionDefinitions returns all installed mission definitions." },
117
+ { method: "createMissionDefinition", description: "CreateMissionDefinition registers a structured mission definition with the daemon. This is the API-only replacement for the removed InstallMission RPC: the daemon does not clone git repositories or parse YAML; callers submit a fully-formed MissionDefinition proto (serialized from JSON in the CLI via protojson, or constructed natively in the dashboard)." },
118
+ { method: "updateMissionDefinition", description: "UpdateMissionDefinition replaces the content of an existing mission definition. The name field of the embedded definition is the lookup key. All other fields replace the stored definition; the server-assigned ID and original timestamps are preserved. Returns codes.NotFound if no definition with that name exists. Spec: gibson#437." },
119
+ { method: "getMissionDefinition", description: "GetMissionDefinition returns the full structured proto for a single installed mission definition, looked up by name. Use this instead of ListMissionDefinitions when the caller knows the definition name and needs every author-facing field (workspace, constraints, per-node retry/data/reuse policies). Returns codes.NotFound when the name is not registered. Spec: mission-author-experience M5 (gibson#134)." },
120
+ { method: "getMissionGraph", description: "GetMissionGraph returns the renderable flow-chart projection of a mission definition: typed nodes (boxes), data-flow edges, derived entry/exit, and per-node positions. The daemon computes the topology and a deterministic auto-layout from the mission DAG, then overlays any saved layout from the mission layout store (SaveMissionLayout) so hand-arranged positions win. This keeps the dashboard a pure renderer — it never re-derives topology. Presentation only: nothing here affects mission execution. Spec: MissionGraph epic (sdk#278)." },
121
+ { method: "getMissionLayout", description: "GetMissionLayout returns the saved diagram layout (per-node positions + viewport) for a mission definition, or an empty layout when none has been saved. The layout store is separate from the mission definition record — the mission work-schema carries no presentation state. Keyed by mission_definition_id. Spec: MissionGraph epic (sdk#278)." },
122
+ { method: "saveMissionLayout", description: "SaveMissionLayout persists a hand-arranged diagram layout for a mission definition into the layout store. Layout-only: it never mutates the mission definition, its nodes/edges/configs, or its cue_source. The optional expected_version enables optimistic concurrency — a stale write (the layout changed underneath) is rejected rather than clobbering. Keyed by mission_definition_id. Spec: MissionGraph epic (sdk#278)." },
123
+ { method: "listAgents", description: "ListAgents returns all registered agents from the etcd registry." },
124
+ { method: "getAgentStatus", description: "GetAgentStatus returns the current status of a specific agent." },
125
+ { method: "listTools", description: "ListTools returns all registered tools from the etcd registry." },
126
+ { method: "listPlugins", description: "ListPlugins returns all registered plugins from the etcd registry." },
127
+ { method: "getCapabilityManifest", description: "GetCapabilityManifest returns the signed, versioned capability manifest for the calling principal in their resolved tenant. SDKs call this on session start and on invalidation events. The ADK calls it at scaffold time to discover what components, permissions, cross-component rules, and runtime limits apply." },
128
+ { method: "watchManifestInvalidations", description: "WatchManifestInvalidations streams ManifestInvalidationEvents to the caller whenever their resolved tenant's manifest is invalidated (FGA mutation, component registry change, tier update). Heartbeats are emitted periodically to keep the stream alive." },
129
+ { method: "queryPlugin", description: "QueryPlugin executes a method on a plugin and returns the result. The plugin must be registered in the etcd registry." },
130
+ { method: "startComponent", description: "StartComponent starts a component (agent, tool, or plugin) by kind and name. The component must be installed in the local database." },
131
+ { method: "stopComponent", description: "StopComponent stops a running component (agent, tool, or plugin) by kind and name. If force is true, sends SIGKILL immediately instead of graceful SIGTERM." },
132
+ { method: "buildComponent", description: "BuildComponent rebuilds a component (agent, tool, or plugin) from source. Useful for rebuilding after manual code changes." },
133
+ { method: "showComponent", description: "ShowComponent returns detailed information about a component. Includes manifest, status, paths, and lifecycle information." },
134
+ { method: "getComponentLogs", description: "GetComponentLogs streams log entries for a component. Supports follow mode for continuous streaming and line limits." },
135
+ { method: "getMyPermissions", description: "GetMyPermissions returns the current user's role, is_admin flag, component grants, and team memberships for the current tenant. Used by the dashboard's PermissionsCache to gate UI elements without per-click daemon calls. Auth: self-mode (spec: self-mode-authz). The hotfix `unauthenticated: true` is replaced with `self: true + allowed_identities: USER`. This RPC may be called before the active-tenant cookie is set (which made the earlier `tenant_from_identity` FGA check fail with \"no tenant derivable\"), so no FGA tuple lookup is performed. The four defense layers are preserved: (a) Envoy `jwt_authn` validates the Zitadel JWT before ext-authz. (b) ext-authz mints X-Gibson-Identity-Subject from the verified sub — clients cannot forge it. (c) The daemon's SPIFFE mTLS init is fail-closed (zero-trust-hardening Req 1) so non-Envoy callers cannot reach the listener. (d) ext-authz enforces allowed_identities: only USER tokens are accepted. The handler scopes the response strictly to the caller's verified subject." },
136
+ { method: "listMyMemberships", description: "ListMyMemberships returns every tenant the authenticated caller is a member of, with the caller's role per tenant. Identity comes from the call context; no tenant_id parameter — this RPC discovers the caller's tenants. Used by the dashboard at sign-in time to populate the tenant picker / set the active-tenant cookie. Auth: self-mode (spec: self-mode-authz). The hotfix `unauthenticated: true` is replaced with `self: true + allowed_identities: USER`. By definition this RPC runs before the caller's active tenant is known, so a `tenant_from_identity` deriver would always fail \"no tenant derivable\". The same four defense layers as GetMyPermissions apply; see that method for the full contract. The handler self-scopes the membership list to the verified caller subject." },
137
+ { method: "renewCapabilityGrant", description: "RenewCapabilityGrant mints a fresh capability-grant JWT for an ongoing mission task whose existing CG-JWT is approaching its ≤30-minute expiry. Long-running missions (vuln research, broad recon sweeps, multi-host fuzzing) call this before their current CG-JWT expires so callbacks keep flowing without dispatching a fresh task. Authorization: the caller MUST present a valid (non-expired) CG-JWT whose subject matches the request's agent_id and whose mission_id/task_id match the request. Renewal is rate-limited per agent to prevent abuse. Spec: unified-identity-and-authorization Requirement 5.8." },
138
+ { method: "validateMissionCUE", description: "ValidateMissionCUE compiles a CUE mission snippet and returns diagnostics. Powers the dashboard CUE editor's real-time error squiggles, and is now callable by ADK/SDK users with a single user token. An empty diagnostics list means the source is valid." },
139
+ { method: "completeMissionCUE", description: "CompleteMissionCUE returns completion candidates at a cursor position. Powers the dashboard CUE editor's auto-complete." },
140
+ { method: "hoverMissionCUE", description: "HoverMissionCUE returns type and documentation for a position in CUE source. Powers the dashboard CUE editor's hover tooltip." },
141
+ ],
142
+ },
143
+ {
144
+ service: GraphService,
145
+ description: "GraphService serves per-tenant knowledge-graph reads and a server-streaming update feed. Every RPC routes through Pool.For(tenant).Neo4j() server-side and is gated by the FGA tenant.member relation at ext-authz.",
146
+ methods: [
147
+ { method: "getTenantGraph", description: "GetTenantGraph returns the full per-tenant subgraph subject to a server-side node-count cap. Use limit + include_labels to narrow scope." },
148
+ { method: "getMissionGraph", description: "GetMissionGraph returns the subgraph touched by a single mission run. Mission ownership is enforced by FGA + by `WHERE m.tenant_id = $tenant` in the underlying Cypher (defense in depth)." },
149
+ { method: "queryPaths", description: "QueryPaths runs a bounded path query from `from_node_id` to either a specific `to_node_id` or any node of `to_node_kind`, up to `max_depth`. Server caps: depth ≤ 10, paths ≤ 100, query timeout 5s." },
150
+ { method: "watchGraphUpdates", description: "WatchGraphUpdates server-streams new node/edge writes for the calling tenant. Subscribers should treat the stream as a UX hint, not a source of truth — drops are possible under load. Reconnect with exponential backoff; fall back to polling GetTenantGraph if the stream stays unhealthy." },
151
+ { method: "getFindingCounts", description: "GetFindingCounts returns finding counts grouped by severity or category. Replaces the dashboard's prior direct-Neo4j paths in gibson-client.ts (getKPIs, getFindingsBySeverity, getFindingsByCategory) and app/api/findings/counts/route.ts." },
152
+ { method: "getFindingTimeSeries", description: "GetFindingTimeSeries returns finding counts bucketed by day for the last N days (default 30, max 365). Missing days are returned as zero buckets." },
153
+ { method: "getGraphStats", description: "GetGraphStats returns aggregate stats for the per-tenant knowledge graph: node counts by label, total edges, last-write timestamp." },
154
+ { method: "getGraphSummary", description: "GetGraphSummary returns an LLM-friendly text summary of the per-tenant graph plus structured stats. Server-side caches results for 60s per tenant." },
155
+ { method: "getGraphContext", description: "GetGraphContext returns a focus node and its bounded neighborhood, used by the chatbot to enrich its system prompt. Returns an empty response (focus_node unset) on missing node or NotProvisioned — does NOT error, so the chatbot prompt never breaks." },
156
+ { method: "getFindings", description: "GetFindings returns a paginated, filterable list of findings (and vulnerabilities) for the calling tenant. Replaces the dashboard's direct-Neo4j paths in app/api/findings/route.ts, app/api/missions/[id]/findings/route.ts, and the iteration backing findings export. Spec: dashboard-neo4j-crud-removal Req 1." },
157
+ ],
158
+ },
159
+ {
160
+ service: HarnessCallbackService,
161
+ description: "HarnessCallbackService provides the harness interface for agents executing in standalone mode. The SDK's CallbackHarness forwards all harness operations to the orchestrator via this service.",
162
+ methods: [
163
+ { method: "lLMComplete", description: "LLM Operations" },
164
+ { method: "lLMCompleteWithTools", description: "" },
165
+ { method: "lLMCompleteStructured", description: "" },
166
+ { method: "lLMStream", description: "" },
167
+ { method: "callToolProto", description: "Tool Operations" },
168
+ { method: "callToolProtoStream", description: "" },
169
+ { method: "listTools", description: "" },
170
+ { method: "searchTools", description: "SearchTools returns a small, ranked, authz-filtered set of tools matching a query — the meta-tool surface agents use instead of receiving every tool (ADR-0047 facet 5). Per-tool authorization is enforced inside the handler; this RPC-level gate only checks that the caller may use the harness." },
171
+ { method: "queueToolWork", description: "Tool Work Queue Operations" },
172
+ { method: "toolResults", description: "" },
173
+ { method: "queryPlugin", description: "Plugin Operations" },
174
+ { method: "listPlugins", description: "" },
175
+ { method: "delegateToAgent", description: "Agent Operations" },
176
+ { method: "listAgents", description: "" },
177
+ { method: "submitFinding", description: "Finding Operations" },
178
+ { method: "observe", description: "Observe emits a typed observation into the World (ADR-0007). The brain resolves identity and topology; scope is derived server-side from context." },
179
+ { method: "worldView", description: "WorldView returns the caller's server-projected slice of the tenant World (ADR-0012, sdk#341's read half). It is the counterpart to Observe: Observe is the agent's only write, WorldView its only read. The slice is projected by the daemon from the mission record it created — the tenant that owns the World and the scope that bounds the slice are read there, never from this request. WorldViewRequest carries no tenant field and no scope field, so an agent cannot name another tenant's World or a wider slice: both are unrepresentable rather than rejected." },
180
+ { method: "queryNodes", description: "Knowledge Operations The knowledge-graph READ surface. It exists on ComponentService too, and that duplication is deliberate: without it here, a dispatched run holding only its task-scoped callback grant cannot read the tenant graph, and the agent would have to keep a component-scoped grant alive purely to call recall — which defeats the point of scoping the dispatch at all. See zerocool-plugins ADR-0006 and docs/adr/0001-callback-knowledge-reads.md. Read-only by construction. The write half is NOT mirrored: the projector is the sole graph writer (ADR-0012), and sdk#451 already removed the generic graph-write RPC from ComponentService." },
181
+ { method: "findSimilarAttacks", description: "" },
182
+ { method: "getAttackChains", description: "" },
183
+ { method: "findSimilarFindings", description: "" },
184
+ { method: "getRelatedFindings", description: "" },
185
+ { method: "getFindings", description: "" },
186
+ { method: "applicationFindings", description: "ApplicationFindings answers the one lifecycle question the reads above cannot: for this Application, what is still open, and does anything actually run the code it is in (gibson#1669). The reads above are hybrid vector-and-graph SEARCH — text or embedding, a node-type filter, top-k. Reachability is a TRAVERSAL: is this Package inside an Image that a Deployment of this Application runs, and does that Deployment expose a Host. No top-k over a node-type filter answers it. It is one bounded question rather than a Cypher surface, deliberately: an agent able to send arbitrary Cypher would be a second unaudited path into the tenant graph, and the traversal an agent needs is knowable in advance." },
187
+ { method: "getRunFindings", description: "" },
188
+ { method: "getMissionRunHistory", description: "" },
189
+ { method: "getPlanContext", description: "Planning Operations" },
190
+ { method: "reportStepHints", description: "" },
191
+ { method: "recordSpan", description: "Distributed Tracing Operations" },
192
+ { method: "recordSpans", description: "" },
193
+ { method: "getCredential", description: "Credential Operations" },
194
+ { method: "getTaxonomySchema", description: "Taxonomy Operations" },
195
+ { method: "generateNodeID", description: "" },
196
+ { method: "validateFinding", description: "" },
197
+ { method: "validateGraphNode", description: "" },
198
+ { method: "validateRelationship", description: "" },
199
+ { method: "createMission", description: "Mission Management Operations These enable agents to autonomously create, run, and manage missions" },
200
+ { method: "runMission", description: "" },
201
+ { method: "getMissionStatus", description: "" },
202
+ { method: "waitForMission", description: "" },
203
+ { method: "listMissions", description: "" },
204
+ { method: "cancelMission", description: "" },
205
+ { method: "getMissionResults", description: "" },
206
+ { method: "authorize", description: "Authorization Operations Authorize checks whether the calling component's current work execution is permitted to perform action on resource. The daemon resolves the run_id to a (user_id, tenant_id) pair and consults FGA." },
207
+ { method: "workspaceList", description: "WorkspaceList returns metadata for every workspace configured for the calling component's active mission. Returns an empty list when the mission has no workspaces." },
208
+ { method: "workspaceGetInfo", description: "WorkspaceGetInfo returns name + path for a single workspace. An empty name resolves to the mission's primary workspace (single-repo case). Returns NOT_FOUND when no workspace with that name exists." },
209
+ { method: "workspaceReadFile", description: "WorkspaceReadFile reads a file from the named workspace. Path is relative to the workspace root. Files larger than 16 MB return RESOURCE_EXHAUSTED; a streaming variant is deferred to a follow-on spec." },
210
+ { method: "workspaceWriteFile", description: "WorkspaceWriteFile writes content to a file in the named workspace. Path is relative to the workspace root. Content larger than 16 MB returns RESOURCE_EXHAUSTED; a streaming variant is deferred." },
211
+ { method: "workspaceListFiles", description: "WorkspaceListFiles returns paths matching the given glob pattern, relative to the workspace root. Result sets larger than 10,000 paths are truncated to the first 10,000 with truncated=true on the response." },
212
+ { method: "workspaceCommit", description: "WorkspaceCommit stages all changes in the workspace and creates a commit with the given message. Returns the commit SHA." },
213
+ { method: "workspacePush", description: "WorkspacePush pushes committed changes to the remote configured for the workspace. Returns PERMISSION_DENIED on auth failure against the remote." },
214
+ { method: "devboxExec", description: "DevboxExec runs one command in the caller's session Devbox — a session-lifetime sandbox resolved (and lazily created on first call) by (tenant, session_id), then REUSED across calls in that session. This is deliberately distinct from the per-call SANDBOXED tool path (CallToolProto), which stays one microVM per call; a Devbox holds working state (checkouts, build caches) that per-call isolation would throw away between commands." },
215
+ { method: "putSessionContext", description: "Session-context store: an opaque, versioned blob per (tenant, session_id), persisted in the per-tenant dataplane store. The daemon never interprets the bytes — the component owns its own format. This is the TRUSTED home for a session's local context; it must never be written to the untrusted Devbox volume (that invariant is the client's to keep, but this store is why keeping it costs nothing). Writes are guarded by an etag (If-Match) so concurrent writers cannot clobber each other; the server enforces a TTL and a size cap." },
216
+ { method: "getSessionContext", description: "" },
217
+ { method: "deleteSessionContext", description: "" },
218
+ { method: "subscribeInput", description: "SubscribeInput streams the inputs for the calling member, in order, across every job it holds. Each Input carries the per-turn grant the member uses for every tool call of that turn. The stream stays open for the life of the member." },
219
+ { method: "pullJob", description: "PullJob returns the next queued job of the calling member's bank, or nothing when the queue is empty. A member with a free slot calls it after each heartbeat. The daemon assigns the job to the member." },
220
+ { method: "reportJobState", description: "ReportJobState tells the daemon a job moved to WORKING or WAITING, and names the Claude Code session that holds it so a restart can resume it. A member never reports CLOSED: a scorer closes the job." },
221
+ { method: "reportDeliverable", description: "ReportDeliverable records one outward result the driver performed at wrap-up: a pushed branch or an opened merge request." },
222
+ { method: "openJob", description: "OpenJob opens a job on a bank from a dispatched agent, under its grant. It mirrors gibson.job.v1.JobService.OpenJob." },
223
+ { method: "sendInput", description: "SendInput sends the next message to an open job from a dispatched agent, under its grant. It mirrors gibson.job.v1.JobService.SendInput." },
224
+ { method: "closeJob", description: "CloseJob closes a job with a verdict and a score from a dispatched agent, under its grant. A verification agent is the usual caller. It mirrors gibson.job.v1.JobService.CloseJob." },
225
+ ],
226
+ },
227
+ {
228
+ service: IdentityService,
229
+ description: "IdentityService exposes the \"describe me\" RPC.",
230
+ methods: [
231
+ { method: "whoAmI", description: "WhoAmI returns the caller's effective FGA grants. When target_principal_id is set, the caller MUST be tenant_admin on the target's tenant — otherwise the daemon returns PermissionDenied. Identity is derived from ext-authz-emitted headers, never from the request body." },
232
+ ],
233
+ },
234
+ {
235
+ service: JobService,
236
+ description: "JobService opens jobs on a bank, sends them input, closes them, and reads them back. People and platform services call it. A dispatched component uses the mirrored OpenJob, SendInput and CloseJob on gibson.harness.v1.HarnessCallbackService under its own grant.",
237
+ methods: [
238
+ { method: "openJob", description: "OpenJob opens a job on a bank. The daemon routes it to a member with a free slot, or queues it, or spills it per the bank policy. Set member_id to pin the job to one member of that bank." },
239
+ { method: "sendInput", description: "SendInput sends the next message to an open job. The daemon mints the per-turn grant and delivers the input to the member that holds the job." },
240
+ { method: "closeJob", description: "CloseJob closes a job with a verdict and a score. The member runs one wrap-up turn, performs the deliverables, removes the worktrees, and archives the transcript. Only a scorer closes a job: a person, a verification agent, or the job node executor." },
241
+ { method: "getJob", description: "GetJob returns one job by id." },
242
+ { method: "listJobs", description: "ListJobs returns jobs of the caller's tenant, newest first. Filter by bank, by member, or by state." },
243
+ { method: "streamJobEvents", description: "StreamJobEvents follows one job: inputs, state changes, deliverables and the close. The stream starts with the backlog after since_seq, then follows live until the job closes." },
244
+ ],
245
+ },
246
+ {
247
+ service: PluginInvokeService,
248
+ description: "PluginInvokeService is the tool-callable RPC for invoking plugin methods. Tools call PluginInvoke; the daemon validates authz, looks up an active plugin install, enqueues a work item via the existing ComponentService PollWork model, awaits SubmitResult, and forwards the result to the tool. Plugin business methods themselves are NOT defined here — the plugin's manifest declares its own method set per the plugin-runtime manifest spec, and the dispatch is by method-name string carried in PluginInvokeRequest.",
249
+ methods: [
250
+ { method: "pluginInvoke", description: "PluginInvoke routes a typed invocation to a serving plugin install." },
251
+ ],
252
+ },
253
+ {
254
+ service: PluginAdminService,
255
+ description: "PluginAdminService manages plugin installs and their secret bindings.",
256
+ methods: [
257
+ { method: "listPluginInstalls", description: "ListPluginInstalls returns all plugin installs for the tenant." },
258
+ { method: "getPluginInstall", description: "GetPluginInstall returns one install by ID." },
259
+ { method: "registerPlugin", description: "RegisterPlugin atomically registers a plugin per Spec 2 R3.1: validates manifest, creates the Zitadel plugin_principal SA, writes per-binding FGA can_resolve tuples (creating any inline secrets in the broker), returns the bootstrap token. Any partial failure rolls back all created state." },
260
+ { method: "editPluginSecretBinding", description: "EditPluginSecretBinding modifies an existing binding (rebind to a different existing secret). Used by the plugin detail page's bindings table." },
261
+ { method: "revokePluginSecretBinding", description: "RevokePluginSecretBinding removes an FGA can_resolve tuple between the plugin and a secret. Emits a secret_access_revoked audit event." },
262
+ ],
263
+ },
264
+ {
265
+ service: ToolService,
266
+ description: "",
267
+ methods: [
268
+ { method: "getDescriptor", description: "" },
269
+ { method: "execute", description: "" },
270
+ { method: "health", description: "" },
271
+ { method: "streamExecute", description: "" },
272
+ ],
273
+ },
274
+ ];
275
+ /** Every RPC of every service, counted at generate time. */
276
+ export const GENERATED_RPC_COUNT = 188;
@@ -0,0 +1,19 @@
1
+ import { type ComponentSpec } from "@zeroroot-ai/sdk";
2
+ import type { ToolDefinition } from "../registry.js";
3
+ import type { HelperContext } from "./context.js";
4
+ export interface SpecArgs {
5
+ kind: string;
6
+ name: string;
7
+ version: string;
8
+ image?: string;
9
+ language?: string;
10
+ capabilities?: string[];
11
+ methods?: string[];
12
+ input_message_type?: string;
13
+ output_message_type?: string;
14
+ config_schema_json?: string;
15
+ }
16
+ export declare function toSpec(args: SpecArgs): ComponentSpec;
17
+ export declare function componentizeTools(ctx: HelperContext): ToolDefinition[];
18
+ /** Kept exported so a caller can inspect the manifest a spec would produce. */
19
+ export declare function manifestOf(args: SpecArgs): unknown;