@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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ZeroRoot
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,160 @@
1
+ # @zeroroot-ai/gibson-mcp
2
+
3
+ The Gibson MCP server. One tool surface for every coding agent host: Claude
4
+ Code, opencode, Cursor, Codex CLI, Gemini CLI and Windsurf. The host is a thin
5
+ adapter and holds no tools of its own.
6
+
7
+ ## Install
8
+
9
+ Every host runs it through `npx`, so nothing has to be installed by hand.
10
+ This is the canonical block. It is the same for every host that reads the
11
+ `mcpServers` shape.
12
+
13
+ ```json
14
+ {
15
+ "mcpServers": {
16
+ "gibson": {
17
+ "command": "npx",
18
+ "args": ["--yes", "--package", "@zeroroot-ai/gibson-mcp@latest", "gibson-mcp"]
19
+ }
20
+ }
21
+ }
22
+ ```
23
+
24
+ | Host | Where the block goes |
25
+ |---|---|
26
+ | Claude Code | `.mcp.json` in the project, or the `zerocool` plugin, which carries it |
27
+ | opencode | the `zerocool` opencode plugin, which carries it |
28
+ | Cursor | `.cursor/mcp.json` |
29
+ | Codex CLI | `~/.codex/config.toml`, as a `[mcp_servers.gibson]` table with the same command and args |
30
+ | Gemini CLI | `~/.gemini/settings.json`, under `mcpServers` |
31
+ | Windsurf | `~/.codeium/windsurf/mcp_config.json`, under `mcpServers` |
32
+
33
+ A host is an adapter and holds no tools of its own (ADR-0008). The per-host
34
+ snippets and their smoke tests live in
35
+ [`zerocool-plugins`](https://github.com/zeroroot-ai/zerocool-plugins).
36
+
37
+ ## The tool surface
38
+
39
+ The surface is 1:1 with everything the SDK produces. One flat tier, no
40
+ curated subset.
41
+
42
+ | Where a tool comes from | Naming | How many |
43
+ |---|---|---|
44
+ | every RPC of every SDK service, generated from the proto descriptors | `<service>_<method>` in snake case, e.g. `harness_callback_service_world_view` | 188 |
45
+ | every SDK helper | the helper's own name: `remember`, `recall`, `world_view`, `submit_finding`, `delegate` | 30 |
46
+ | every checked-in platform tool and plugin, discovered at runtime | `gibson_<tool>`, `gibson_plugin_<plugin>` | whatever the tenant has |
47
+
48
+ An SDK bump regenerates the first group. Discovery repeats every 60 seconds
49
+ and emits `tools/list_changed`, so a tool a person enrols now is callable in
50
+ the same session.
51
+
52
+ A posture registers only the tools its credential can reach. With no
53
+ platform the server still serves `submit_finding`, `componentize` and
54
+ `validate_component`, plus `gibson_login` and `gibson_connect`.
55
+
56
+ ## Resources and prompts
57
+
58
+ | URI | What it holds |
59
+ |---|---|
60
+ | `gibson://ambient` | one GraphRAG lookup per session: what the tenant already knows about this codebase |
61
+ | `gibson://session` | the check-in source, the posture, and the mission, run and callback endpoint a session-end hook needs |
62
+
63
+ A host with a hook surface injects the ambient block itself. A host without
64
+ one calls the `gibson_ambient` prompt instead. Either way it is one lookup.
65
+
66
+ ## Transports
67
+
68
+ | Flag | Where it runs | Why |
69
+ |---|---|---|
70
+ | `--transport stdio` (default) | a laptop | the host spawns the server and owns its lifetime |
71
+ | `--transport http --listen 127.0.0.1:7788` | a sandbox | one server for the life of the sandbox, so the driver can swap the grant per turn |
72
+
73
+ `--listen` accepts a loopback address only, and DNS rebinding protection is
74
+ on. `--stream-limit` (default 500) caps how many messages a server-streaming
75
+ RPC tool returns before it reports `truncated`.
76
+
77
+ ### The HTTP routes
78
+
79
+ | Route | What it does |
80
+ |---|---|
81
+ | `POST /mcp` | starts an MCP session with `initialize`; later requests carry `mcp-session-id` |
82
+ | `GET /mcp` | the session's notification stream |
83
+ | `DELETE /mcp` | ends a session |
84
+ | `GET /healthz` | liveness, plus the check-in source, the posture, the tool count and the open job |
85
+ | `POST /turn` | puts a dispatch's grant in force |
86
+ | `GET /turn` | reports the turn in force |
87
+ | `DELETE /turn` | ends it |
88
+
89
+ ### Per-turn grants
90
+
91
+ A member sandbox serves many dispatches over its life, and each input
92
+ message carries the task grant of its own dispatch. The driver calls
93
+ `POST /turn` before it feeds Claude a message:
94
+
95
+ ```
96
+ POST /turn
97
+ {"job_id": "job-1", "grant": "<CG-JWT>", "callback_endpoint": "daemon:50001"}
98
+ -> 200 {"job_id": "job-1", "endpoint": "daemon:50001"}
99
+ ```
100
+
101
+ Every tool call that follows runs under that grant. `DELETE /turn` ends the
102
+ turn, and calls fall back to the **base grant** from the launch, which is
103
+ used for the lifetime RPCs only: the inbox subscription, reading repository
104
+ credentials, and the checkpoint writes. A single request may instead carry
105
+ `x-gibson-turn-grant`, which applies to that request alone and wins over the
106
+ turn in force; that is how a driver runs two turns at once.
107
+
108
+ Both `job_id` and `grant` are required. A grant with no job attributes the
109
+ work to nothing.
110
+
111
+ `/turn` exists only where there is a task grant to swap. Elsewhere it is a
112
+ 404.
113
+
114
+ ## Check-in sources
115
+
116
+ The server picks one credential source at start from what is present. It never
117
+ mixes them, and it never mints identity.
118
+
119
+ 1. **Dispatched grant.** `GIBSON_CG_JWT` and `GIBSON_CALLBACK_ENDPOINT` are
120
+ set, so the daemon launched this process. The server joins the run it was
121
+ launched for. No enrollment, no state file, no mission. This source wins
122
+ over every other.
123
+ 2. **Pre-minted token.** `GIBSON_BOOTSTRAP_TOKEN` is set and this host has no
124
+ key yet. The server checks in once with the token. The host key carries
125
+ every later start, and the token is spent.
126
+ 3. **Enrolled host key.** The key at `GIBSON_HOST_KEY_PATH` (default
127
+ `~/.zerocool/host.key`) is the credential.
128
+ 4. **Nothing yet.** The server offers `gibson_login` and `gibson_connect`: a
129
+ person signs in through the `gibson` CLI device flow, and the server
130
+ enrolls the host, picks the target and starts the live mission without a
131
+ restart.
132
+
133
+ Call `gibson_status` to see the source, the posture, the platform, the tenant,
134
+ the target and the mission.
135
+
136
+ ## Postures
137
+
138
+ A platform the server cannot reach never stops a session. Each posture below
139
+ carries fewer tools than the one before it.
140
+
141
+ - `task`: a dispatched run. Reads and writes use the dispatch grant.
142
+ - `live`: checked in, and this session is a mission of its own.
143
+ - `component`: checked in, no mission. Reads and findings only.
144
+ - `standalone`: no platform. Findings go to a local log.
145
+
146
+ ## Environment
147
+
148
+ | Variable | Meaning |
149
+ |---|---|
150
+ | `GIBSON_PLATFORM_URL` | the platform to check in to |
151
+ | `GIBSON_TARGET_ID` | the target the live mission binds to |
152
+ | `GIBSON_BOOTSTRAP_TOKEN` | a one-time enrollment token |
153
+ | `GIBSON_HOST_KEY_PATH` | the host key (default `~/.zerocool/host.key`) |
154
+ | `GIBSON_CA_CERT` | a private CA to trust |
155
+ | `GIBSON_CALLBACK_INSECURE` | `1` dials the callback endpoint without TLS. Local daemons only. |
156
+ | `ZEROCOOL_STATE_DIR` | the state directory (default `~/.zerocool`) |
157
+
158
+ ## License
159
+
160
+ MIT.
@@ -0,0 +1,7 @@
1
+ import { type KnowledgeSource } from "@zeroroot-ai/sdk";
2
+ /**
3
+ * One GraphRAG lookup per session, read by a session-start hook or the gibson://ambient resource. A
4
+ * knowledge failure never breaks a start: the agent works without prior
5
+ * context, it just works less well.
6
+ */
7
+ export declare function ambientBlock(knowledge: KnowledgeSource, seedQuery: string): Promise<string>;
@@ -0,0 +1,18 @@
1
+ import { formatKnowledgeForPrompt } from "@zeroroot-ai/sdk";
2
+ /** How many hits the ambient block carries. Small: it is prompt overhead on every session. */
3
+ const AMBIENT_LIMIT = 5;
4
+ /**
5
+ * One GraphRAG lookup per session, read by a session-start hook or the gibson://ambient resource. A
6
+ * knowledge failure never breaks a start: the agent works without prior
7
+ * context, it just works less well.
8
+ */
9
+ export async function ambientBlock(knowledge, seedQuery) {
10
+ try {
11
+ const hits = await knowledge.query({ text: seedQuery, topK: AMBIENT_LIMIT });
12
+ const body = formatKnowledgeForPrompt(hits);
13
+ return body ? `Prior context from the Gibson knowledge graph (recall for more):\n${body}` : "";
14
+ }
15
+ catch {
16
+ return "";
17
+ }
18
+ }
package/dist/ask.d.ts ADDED
@@ -0,0 +1,70 @@
1
+ import { type Inbox, type JobInput } from "./inbox.js";
2
+ import { type Log } from "./log.js";
3
+ import type { ToolDefinition } from "./registry.js";
4
+ /**
5
+ * `ask`: the one way a job reaches a person (gibson#1706, decision 16).
6
+ *
7
+ * The wire has no `INPUT_KIND_QUESTION`. `gibson.job.v1.InputKind` carries
8
+ * `TURN`, `ANSWER` and `WRAP_UP`, and issue #59 expected a
9
+ * `JobService.SendEvent` that B1 did not ship. So the question is signalled
10
+ * two ways with what exists: `ReportJobState(job_id, WAITING)` is the state
11
+ * the console renders and the stale-limit reaper reads, and the text rides
12
+ * on an input, which is the only field on the wire that carries prose. The
13
+ * id the daemon returns for that input is remembered, so a member that is
14
+ * handed back its own question says so instead of answering itself.
15
+ *
16
+ * A job runs Claude Code with `--dangerously-skip-permissions`; the gVisor
17
+ * sandbox and the per-turn grant are the controls. So there is no permission
18
+ * dialog, and a question has to travel the same path as every other input:
19
+ * out through the job, back through the inbox. The driver wires this tool as
20
+ * `--permission-prompt-tool mcp__gibson__ask`.
21
+ *
22
+ * The job enters `waiting` while the question is outstanding, and the next
23
+ * input for that job is the answer. Input for another job is not an answer
24
+ * to this question, so it is left in the stream for its own turn.
25
+ *
26
+ * The reply is the shape Claude Code's permission-prompt contract expects:
27
+ * `{"behavior":"allow","updatedInput":{...}}` or
28
+ * `{"behavior":"deny","message":"..."}`. A free-text answer to a plain
29
+ * question comes back as an allow carrying the text, because that is how a
30
+ * question that is not about a tool call is answered.
31
+ */
32
+ /** What Claude Code reads back from a permission-prompt tool. */
33
+ export type PermissionDecision = {
34
+ behavior: "allow";
35
+ updatedInput: Record<string, unknown>;
36
+ } | {
37
+ behavior: "deny";
38
+ message: string;
39
+ };
40
+ export declare function decisionFrom(answer: string, toolInput: Record<string, unknown>): PermissionDecision;
41
+ /** A source of answers: the next input for one job. */
42
+ export interface AnswerSource {
43
+ /** Resolve with the next input for `jobId`, or reject when it cannot come. */
44
+ next(jobId: string, signal?: AbortSignal): Promise<JobInput>;
45
+ }
46
+ /**
47
+ * Route inbox messages to whoever is waiting for that job.
48
+ *
49
+ * One reader of the stream, many waiters: two jobs can each hold an open
50
+ * question at the same time, and a message for one must not wake the other.
51
+ */
52
+ export declare class AnswerRouter implements AnswerSource {
53
+ private readonly waiting;
54
+ private readonly pending;
55
+ /** Offer a message. Returns true when a waiter took it. */
56
+ offer(input: JobInput): boolean;
57
+ /** Hold a message that arrived before anyone asked for it. */
58
+ hold(input: JobInput): void;
59
+ next(jobId: string, signal?: AbortSignal): Promise<JobInput>;
60
+ }
61
+ export interface AskOptions {
62
+ /** The job the current turn belongs to. */
63
+ jobId: () => string | undefined;
64
+ inbox: Pick<Inbox, "send" | "reportState">;
65
+ answers: AnswerSource;
66
+ /** The Claude Code session this job runs, when the driver knows it. */
67
+ claudeSessionId?: () => string | undefined;
68
+ log?: Log;
69
+ }
70
+ export declare function askTool(opts: AskOptions): ToolDefinition;
package/dist/ask.js ADDED
@@ -0,0 +1,111 @@
1
+ import { z } from "zod";
2
+ import { InputKind, JobState } from "./inbox.js";
3
+ import { TAG } from "./log.js";
4
+ import { defineTool } from "./tool.js";
5
+ import { failure, text } from "./tools/result.js";
6
+ /** Words an answer may use to refuse. Anything else allows. */
7
+ const DENIALS = /^\s*(no|deny|denied|reject|rejected|refuse|refused|stop|do not|don't)\b/i;
8
+ export function decisionFrom(answer, toolInput) {
9
+ if (DENIALS.test(answer))
10
+ return { behavior: "deny", message: answer.trim() };
11
+ return { behavior: "allow", updatedInput: toolInput };
12
+ }
13
+ /**
14
+ * Route inbox messages to whoever is waiting for that job.
15
+ *
16
+ * One reader of the stream, many waiters: two jobs can each hold an open
17
+ * question at the same time, and a message for one must not wake the other.
18
+ */
19
+ export class AnswerRouter {
20
+ waiting = new Map();
21
+ pending = new Map();
22
+ /** Offer a message. Returns true when a waiter took it. */
23
+ offer(input) {
24
+ const queue = this.waiting.get(input.jobId);
25
+ if (!queue)
26
+ return false;
27
+ const resolve = queue.shift();
28
+ if (!resolve)
29
+ return false;
30
+ if (queue.length === 0)
31
+ this.waiting.delete(input.jobId);
32
+ resolve(input);
33
+ return true;
34
+ }
35
+ /** Hold a message that arrived before anyone asked for it. */
36
+ hold(input) {
37
+ if (this.offer(input))
38
+ return;
39
+ const queue = this.pending.get(input.jobId) ?? [];
40
+ queue.push(input);
41
+ this.pending.set(input.jobId, queue);
42
+ }
43
+ next(jobId, signal) {
44
+ const held = this.pending.get(jobId);
45
+ const first = held?.shift();
46
+ if (first) {
47
+ if (held && held.length === 0)
48
+ this.pending.delete(jobId);
49
+ return Promise.resolve(first);
50
+ }
51
+ return new Promise((resolve, reject) => {
52
+ const queue = this.waiting.get(jobId) ?? [];
53
+ queue.push(resolve);
54
+ this.waiting.set(jobId, queue);
55
+ signal?.addEventListener("abort", () => {
56
+ const q = this.waiting.get(jobId);
57
+ const i = q?.indexOf(resolve) ?? -1;
58
+ if (q && i >= 0)
59
+ q.splice(i, 1);
60
+ reject(new Error("the question was cancelled before an answer arrived"));
61
+ }, { once: true });
62
+ });
63
+ }
64
+ }
65
+ export function askTool(opts) {
66
+ return defineTool({
67
+ name: "ask",
68
+ description: "Ask the person or the agent that opened this job a question, and wait for the answer. Use it " +
69
+ "when you need a decision you cannot make yourself: a choice between two approaches, a missing " +
70
+ "fact, or permission for something outside the job's declared deliverable. The job waits while " +
71
+ "the question is open, so ask once and ask precisely.",
72
+ input: {
73
+ question: z.string().describe("The question, in one or two sentences. Say what you will do with each answer."),
74
+ tool_name: z.string().optional().describe("When this is a permission prompt, the tool Claude Code wants to run."),
75
+ input: z.record(z.string(), z.unknown()).optional().describe("When this is a permission prompt, the input Claude Code wants to run it with."),
76
+ },
77
+ handler: async (args, ctx) => {
78
+ const jobId = opts.jobId();
79
+ if (!jobId) {
80
+ return failure("no job is open", "ask reaches the person through the job's inbox, and this turn belongs to no job. " +
81
+ "The driver sets the job with POST /turn before it feeds a message.");
82
+ }
83
+ const question = args.tool_name ? `${args.question}\n\nClaude Code wants to run ${args.tool_name} with:\n${JSON.stringify(args.input ?? {}, null, 2)}` : args.question;
84
+ try {
85
+ // The wait is armed before the question is sent. Arming it after
86
+ // would drop an answer that came back faster than this call returns.
87
+ const answer = opts.answers.next(jobId, ctx.signal);
88
+ // WAITING is the signal that a question is open: it is what the
89
+ // console renders and what the stale-limit reaper reads. The text
90
+ // rides on an input, because that is the only field on the wire that
91
+ // carries prose.
92
+ await opts.inbox.reportState(jobId, JobState.WAITING, opts.claudeSessionId?.());
93
+ const sent = await opts.inbox.send(jobId, question, InputKind.TURN);
94
+ opts.log?.(`${TAG} job ${jobId} is waiting on a question`);
95
+ const input = await answer.finally(async () => {
96
+ await opts.inbox.reportState(jobId, JobState.WORKING, opts.claudeSessionId?.()).catch(() => { });
97
+ });
98
+ opts.log?.(`${TAG} job ${jobId} got its answer from ${input.sender || "the inbox"}`);
99
+ if (input.id && input.id === sent.id) {
100
+ return failure("ask heard its own question", "The inbox returned the question this job just posted. The daemon must not deliver a member its own input.");
101
+ }
102
+ if (args.tool_name)
103
+ return text("", JSON.stringify(decisionFrom(input.message, args.input ?? {})));
104
+ return text("", input.message);
105
+ }
106
+ catch (e) {
107
+ return failure("ask failed", e.message);
108
+ }
109
+ },
110
+ });
111
+ }
@@ -0,0 +1,83 @@
1
+ import type { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
+ import type { Transport as ConnectTransport } from "@connectrpc/connect";
3
+ import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
4
+ import { ToolRegistry, type ToolDefinition, type ToolGroup } from "./registry.js";
5
+ import type { TurnRoute } from "./http.js";
6
+ import { type Discovery } from "./discovery.js";
7
+ import { type Inbox } from "./inbox.js";
8
+ import { type RpcChannels } from "./rpc.js";
9
+ import { type AmbientSource } from "./resources.js";
10
+ import { type Gibson } from "./session.js";
11
+ import { type ConnectDeps } from "./tools/connect.js";
12
+ /**
13
+ * @zeroroot-ai/gibson-mcp: the one MCP server every coding agent host loads
14
+ * (gibson#1706, decisions 1 to 4). It exposes what the SDK produces and adds
15
+ * Gibson to the session: one live mission per session (ADR-0007 decision 3),
16
+ * memory and knowledge, findings, tools, delegation. It never routes LLM
17
+ * traffic and never reads a model provider key.
18
+ *
19
+ * The surface is one tool registry that every attached protocol server
20
+ * reads. `upgrade` swaps the connection (after `gibson_connect`) and
21
+ * re-registers the posture's tools, and every attached session hears
22
+ * `tools/list_changed`.
23
+ */
24
+ export interface BuildDeps extends ConnectDeps {
25
+ streamLimit?: number;
26
+ /** How often to look for newly checked-in platform tools. `0` runs one pass. */
27
+ discoveryIntervalMs?: number;
28
+ }
29
+ export interface Surface {
30
+ registry: ToolRegistry;
31
+ /** The current connection. Changes on gibson_connect. */
32
+ current(): Gibson;
33
+ /** Replace the connection and re-register the posture's tools. */
34
+ upgrade(next: Gibson): Promise<void>;
35
+ /** Bind a new protocol server to a transport. */
36
+ attach(transport: Transport): Promise<Server>;
37
+ /** Small JSON for /healthz. */
38
+ health(): Record<string, unknown>;
39
+ /**
40
+ * The per-turn grant control, when this posture holds a task grant. The
41
+ * HTTP transport serves it at `/turn`; the stdio transport does not expose
42
+ * it, because a host that spawns one process per session has one grant.
43
+ */
44
+ turn?: TurnRoute;
45
+ /** The member inbox, when this posture holds a task grant. */
46
+ inbox?: Inbox;
47
+ /** End the mission, stop the heartbeat, drop the state file. Idempotent. */
48
+ close(): Promise<void>;
49
+ }
50
+ export declare function buildSurface(env: NodeJS.ProcessEnv, cwd: string, deps?: BuildDeps): Promise<Surface>;
51
+ export interface PostureContext {
52
+ env: NodeJS.ProcessEnv;
53
+ cwd: string;
54
+ streamLimit: number;
55
+ /** The session's ambient block, for the hook handoff. */
56
+ ambient?: AmbientSource;
57
+ discoveryIntervalMs?: number;
58
+ /** The per-turn transport, when this posture serves turns. */
59
+ taskTransport?: ConnectTransport;
60
+ /** The `ask` tool, when this posture has an inbox to ask through. */
61
+ ask?: ToolDefinition;
62
+ }
63
+ /**
64
+ * Which transports this posture holds, for the generated RPC tools.
65
+ *
66
+ * `taskTransport` is the per-turn transport when there is one. The harness
67
+ * object is passed through whole, because the RPC tools also read its
68
+ * `context` to fill `ContextInfo`; only the transport is swapped.
69
+ */
70
+ export declare function channelsOf(gibson: Gibson, taskTransport?: ConnectTransport): RpcChannels;
71
+ /**
72
+ * The tools a posture carries: one per RPC, one per SDK helper, one per
73
+ * checked-in platform tool.
74
+ *
75
+ * A standalone posture holds no transport, so it carries no RPC tools: a
76
+ * tool with no daemon behind it answers every call with a dial error, which
77
+ * reads to a model like a broken platform rather than an unconnected
78
+ * session. Its helper tools are the ones that need no platform.
79
+ *
80
+ * Returns the discovery poller when this posture has a catalog to poll, so
81
+ * the caller can stop it on upgrade or close.
82
+ */
83
+ export declare function registerPosture(group: ToolGroup, gibson: Gibson, ctx: PostureContext): Promise<Discovery | undefined>;