@zeroroot-ai/zerocool 0.8.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dispatch.d.ts +69 -74
- package/dist/dispatch.js +77 -129
- package/dist/findings.d.ts +21 -2
- package/dist/findings.js +3 -80
- package/dist/fix.js +2 -0
- package/dist/gitlab.js +2 -0
- package/dist/http-probe.js +2 -0
- package/dist/index.d.ts +11 -5
- package/dist/index.js +39 -39
- package/dist/knowledge-source.d.ts +34 -9
- package/dist/knowledge-source.js +33 -12
- package/dist/knowledge.d.ts +0 -9
- package/dist/knowledge.js +3 -53
- package/dist/mcp-config.d.ts +20 -0
- package/dist/mcp-config.js +31 -0
- package/dist/opencode-run.js +2 -0
- package/dist/semgrep.js +2 -0
- package/dist/serve-agent.js +2 -0
- package/dist/serve.js +2 -0
- package/dist/source-analysis.js +2 -0
- package/dist/watch.js +2 -0
- package/package.json +5 -5
- package/dist/componentize.d.ts +0 -10
- package/dist/componentize.js +0 -149
- package/dist/delegate.d.ts +0 -11
- package/dist/delegate.js +0 -216
- package/dist/gibson-tools.d.ts +0 -45
- package/dist/gibson-tools.js +0 -175
package/dist/http-probe.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Elastic-2.0
|
|
2
|
+
// Copyright 2026 Zero Root AI
|
|
1
3
|
import { decodeToolInput } from "@zeroroot-ai/sdk";
|
|
2
4
|
export const HTTP_PROBE_TIMEOUT_MS = 15_000;
|
|
3
5
|
/** Perform the probe. Exported separately from the handler so it is testable without a work item. */
|
package/dist/index.d.ts
CHANGED
|
@@ -20,11 +20,17 @@ import type { Plugin } from "@opencode-ai/plugin";
|
|
|
20
20
|
* - checks in as a Gibson component (Capability Grant + RegisterComponent + heartbeat),
|
|
21
21
|
* - starts a local OpenAI-compatible shim over the harness,
|
|
22
22
|
* - auto-registers a zero-config `gibson` provider via the `config` hook (#6),
|
|
23
|
-
* -
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
23
|
+
* - reads the tenant knowledge graph and injects an ambient block (#8).
|
|
24
|
+
*
|
|
25
|
+
* THE TOOLS ARE NOT HERE. Every Gibson tool — findings, knowledge, delegation,
|
|
26
|
+
* componentize, and one per RPC of every SDK service — comes from the Gibson
|
|
27
|
+
* MCP server, which this plugin registers in opencode's own `mcp` block
|
|
28
|
+
* (ADR-0008). opencode spawns it, so the tools reach the model the same way
|
|
29
|
+
* they reach Claude Code, Cursor and the rest, and a new SDK release adds
|
|
30
|
+
* tools with no change here.
|
|
31
|
+
*
|
|
32
|
+
* What is left is what only a plugin can do: the model provider, the ambient
|
|
33
|
+
* knowledge injection, and the session id.
|
|
28
34
|
*
|
|
29
35
|
* The plugin fails open throughout. A coding agent that cannot reach its
|
|
30
36
|
* platform must still be a working coding agent, so every platform failure
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Elastic-2.0
|
|
2
|
+
// Copyright 2026 Zero Root AI
|
|
1
3
|
import { connectGibson, startCompletionsShim } from "@zeroroot-ai/sdk";
|
|
2
4
|
import { existsSync } from "node:fs";
|
|
3
5
|
import { homedir } from "node:os";
|
|
4
6
|
import { join } from "node:path";
|
|
5
|
-
import {
|
|
6
|
-
import { delegationTools } from "./delegate.js";
|
|
7
|
-
import { buildGibsonTools } from "./gibson-tools.js";
|
|
8
|
-
import { ambientKnowledge, recallTool } from "./knowledge.js";
|
|
7
|
+
import { ambientKnowledge } from "./knowledge.js";
|
|
9
8
|
import { selectKnowledgeSource } from "./knowledge-source.js";
|
|
10
|
-
import {
|
|
9
|
+
import { gibsonMcpServer } from "./mcp-config.js";
|
|
11
10
|
/**
|
|
12
11
|
* @zeroroot-ai/zerocool — the main Gibson plugin.
|
|
13
12
|
*
|
|
@@ -29,11 +28,17 @@ import { gibsonFindingsBackend, localFindingsBackend, submitFindingTool, } from
|
|
|
29
28
|
* - checks in as a Gibson component (Capability Grant + RegisterComponent + heartbeat),
|
|
30
29
|
* - starts a local OpenAI-compatible shim over the harness,
|
|
31
30
|
* - auto-registers a zero-config `gibson` provider via the `config` hook (#6),
|
|
32
|
-
* -
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
31
|
+
* - reads the tenant knowledge graph and injects an ambient block (#8).
|
|
32
|
+
*
|
|
33
|
+
* THE TOOLS ARE NOT HERE. Every Gibson tool — findings, knowledge, delegation,
|
|
34
|
+
* componentize, and one per RPC of every SDK service — comes from the Gibson
|
|
35
|
+
* MCP server, which this plugin registers in opencode's own `mcp` block
|
|
36
|
+
* (ADR-0008). opencode spawns it, so the tools reach the model the same way
|
|
37
|
+
* they reach Claude Code, Cursor and the rest, and a new SDK release adds
|
|
38
|
+
* tools with no change here.
|
|
39
|
+
*
|
|
40
|
+
* What is left is what only a plugin can do: the model provider, the ambient
|
|
41
|
+
* knowledge injection, and the session id.
|
|
37
42
|
*
|
|
38
43
|
* The plugin fails open throughout. A coding agent that cannot reach its
|
|
39
44
|
* platform must still be a working coding agent, so every platform failure
|
|
@@ -57,12 +62,11 @@ export const GibsonPlugin = async () => {
|
|
|
57
62
|
// the operator to keep a spent credential in their environment forever.
|
|
58
63
|
const checkedIn = existsSync(hostKeyPath);
|
|
59
64
|
const bootstrapToken = checkedIn ? undefined : process.env.GIBSON_BOOTSTRAP_TOKEN;
|
|
60
|
-
// Standalone:
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
});
|
|
65
|
+
// Standalone: the MCP server still runs. It decides its own posture from
|
|
66
|
+
// what is present, reports it through gibson_status, and offers the tools
|
|
67
|
+
// that need no platform. A coding agent that cannot reach Gibson is still a
|
|
68
|
+
// working coding agent.
|
|
69
|
+
const standalone = () => ({ config: async (config) => addMcpServer(config) });
|
|
66
70
|
// Standalone unless we can authenticate: a platform URL plus either a token
|
|
67
71
|
// for the first check-in or an already-registered host key.
|
|
68
72
|
if (!platformURL || (!bootstrapToken && !checkedIn)) {
|
|
@@ -72,7 +76,7 @@ export const GibsonPlugin = async () => {
|
|
|
72
76
|
"GIBSON_BOOTSTRAP_TOKEN=<one-time token>. After that the host key at " +
|
|
73
77
|
`${hostKeyPath} is enough — you can drop the token.`);
|
|
74
78
|
}
|
|
75
|
-
return
|
|
79
|
+
return standalone();
|
|
76
80
|
}
|
|
77
81
|
let session;
|
|
78
82
|
let shim;
|
|
@@ -102,34 +106,19 @@ export const GibsonPlugin = async () => {
|
|
|
102
106
|
// Fail open to standalone: a coding agent must still work if the platform is unreachable.
|
|
103
107
|
console.error(`[zerocool] Gibson connect failed; continuing standalone: ${e.message}`);
|
|
104
108
|
await shim?.close();
|
|
105
|
-
return
|
|
109
|
+
return standalone();
|
|
106
110
|
}
|
|
107
111
|
const shimUrl = shim.url;
|
|
108
112
|
const live = session;
|
|
109
|
-
// Tool discovery runs once at load — opencode reads the `tool` hook as a
|
|
110
|
-
// static object, so a tool registered mid-session would never reach the model.
|
|
111
113
|
// Which grant this process reads the knowledge graph with. A dispatched run
|
|
112
114
|
// reads as the TASK; an interactive one keeps the component grant. Chosen once
|
|
113
115
|
// here so nothing downstream has to ask.
|
|
114
|
-
const { knowledge, scope: knowledgeScope } = selectKnowledgeSource(live);
|
|
116
|
+
const { knowledge, scope: knowledgeScope, stop: stopKnowledge } = selectKnowledgeSource(live);
|
|
115
117
|
console.error(`[zerocool] knowledge reads use the ${knowledgeScope} grant`);
|
|
116
|
-
const gibson = await buildGibsonTools(live);
|
|
117
|
-
if (gibson.note)
|
|
118
|
-
console.error(`[zerocool] Gibson tool discovery: ${gibson.note}`);
|
|
119
|
-
else
|
|
120
|
-
console.error(`[zerocool] ${gibson.discovered} Gibson tool(s) registered`);
|
|
121
|
-
const tools = {
|
|
122
|
-
submit_finding: submitFindingTool(gibsonFindingsBackend(live), context),
|
|
123
|
-
recall: recallTool(knowledge),
|
|
124
|
-
gibson_componentize: componentizeTool(),
|
|
125
|
-
gibson_enroll_component: enrollComponentTool(live),
|
|
126
|
-
...gibson.tools,
|
|
127
|
-
...delegationTools(live),
|
|
128
|
-
};
|
|
129
118
|
const injectKnowledge = ambientKnowledge(knowledge, process.env.ZEROCOOL_AMBIENT_QUERY ?? "prior findings and security facts for this codebase");
|
|
130
119
|
return {
|
|
131
|
-
tool:
|
|
132
|
-
//
|
|
120
|
+
// Zero-config LLM, and the one tool surface: inject both at config-load
|
|
121
|
+
// time (#6, ADR-0008).
|
|
133
122
|
//
|
|
134
123
|
// `shimUrl` is always live here, and that is a type-level guarantee rather
|
|
135
124
|
// than a timing assumption: `Plugin` returns `Promise<Hooks>`, so opencode
|
|
@@ -146,22 +135,33 @@ export const GibsonPlugin = async () => {
|
|
|
146
135
|
options: { baseURL: shimUrl, apiKey: "gibson" },
|
|
147
136
|
};
|
|
148
137
|
}
|
|
138
|
+
addMcpServer(config);
|
|
149
139
|
},
|
|
150
140
|
// Ambient knowledge (#8): one cached GraphRAG lookup per session, injected
|
|
151
141
|
// into the system prompt from the second turn on.
|
|
152
142
|
"experimental.chat.system.transform": injectKnowledge,
|
|
153
|
-
// Track the session
|
|
154
|
-
//
|
|
155
|
-
// would fill the tenant graph with noise a human
|
|
143
|
+
// Track the session id, which the ambient block and any local state key
|
|
144
|
+
// off. This hook emits nothing: a file edit is not a security finding, and
|
|
145
|
+
// inventing one would fill the tenant graph with noise a human must triage.
|
|
156
146
|
event: async ({ event }) => {
|
|
157
147
|
const sessionID = event.properties?.sessionID;
|
|
158
148
|
if (sessionID)
|
|
159
149
|
context.sessionID = sessionID;
|
|
160
150
|
},
|
|
161
151
|
dispose: async () => {
|
|
152
|
+
// A task-scoped source holds a grant-renewal timer. Leaving it running
|
|
153
|
+
// would keep renewing a grant for a session that has ended.
|
|
154
|
+
stopKnowledge();
|
|
162
155
|
session?.stop();
|
|
163
156
|
await shim?.close();
|
|
164
157
|
},
|
|
165
158
|
};
|
|
166
159
|
};
|
|
160
|
+
/** Register the Gibson MCP server in opencode's own `mcp` block. */
|
|
161
|
+
function addMcpServer(config) {
|
|
162
|
+
const cfg = config;
|
|
163
|
+
cfg.mcp = cfg.mcp ?? {};
|
|
164
|
+
if (!cfg.mcp.gibson)
|
|
165
|
+
cfg.mcp.gibson = gibsonMcpServer();
|
|
166
|
+
}
|
|
167
167
|
export default GibsonPlugin;
|
|
@@ -1,12 +1,35 @@
|
|
|
1
|
-
import { type GibsonSession, type KnowledgeSource } from "@zeroroot-ai/sdk";
|
|
1
|
+
import { type GibsonSession, type KnowledgeSource, type OpenTaskHarnessOptions, type TaskHarness } from "@zeroroot-ai/sdk";
|
|
2
|
+
/** The chosen knowledge source, and the way to release what it opened. */
|
|
3
|
+
export interface SelectedKnowledge {
|
|
4
|
+
knowledge: KnowledgeSource;
|
|
5
|
+
scope: "task" | "component";
|
|
6
|
+
/**
|
|
7
|
+
* Release the task harness. Safe to call on either scope, and safe to call
|
|
8
|
+
* twice. A component-scoped run opens nothing, so this does nothing.
|
|
9
|
+
*/
|
|
10
|
+
stop: () => void;
|
|
11
|
+
}
|
|
12
|
+
/** Seams, so a test can read what a dispatched run actually sends. */
|
|
13
|
+
export interface KnowledgeSourceDeps {
|
|
14
|
+
/** Opens the task harness. Defaults to `openTaskHarness` on the dispatch grant. */
|
|
15
|
+
openHarness?: (opts: OpenTaskHarnessOptions) => TaskHarness;
|
|
16
|
+
}
|
|
2
17
|
/**
|
|
3
18
|
* Pick the grant this process should read the knowledge graph with.
|
|
4
19
|
*
|
|
5
20
|
* A DISPATCHED run must read as the TASK, not as the component. gibson mints a
|
|
6
|
-
* per-dispatch capability grant and
|
|
7
|
-
* GIBSON_CALLBACK_ENDPOINT / GIBSON_CALLBACK_TOKEN. Reading with the
|
|
8
|
-
* own grant instead is broader authority than the dispatch
|
|
9
|
-
* no per-task attribution on anything the run reads — the
|
|
21
|
+
* per-dispatch capability grant, and the driver passes it to this opencode
|
|
22
|
+
* child as GIBSON_CALLBACK_ENDPOINT / GIBSON_CALLBACK_TOKEN. Reading with the
|
|
23
|
+
* component's own grant instead is broader authority than the dispatch
|
|
24
|
+
* intended and leaves no per-task attribution on anything the run reads — the
|
|
25
|
+
* gap ADR-0006 recorded.
|
|
26
|
+
*
|
|
27
|
+
* THESE TWO NAMES ARE THE CHILD CONTRACT, NOT THE LAUNCHER CONTRACT. The
|
|
28
|
+
* launcher writes GIBSON_CG_JWT and GIBSON_AGENT_TASK_B64 into the sandbox,
|
|
29
|
+
* and `readSandboxDispatch` in `@zeroroot-ai/sdk` is the one reader of those
|
|
30
|
+
* (see `dispatch.ts`). This module runs one process further down, inside
|
|
31
|
+
* opencode, whose environment `dispatchChildEnv` writes. It never sees the
|
|
32
|
+
* launch environment, so it reads the child names and nothing else.
|
|
10
33
|
*
|
|
11
34
|
* An INTERACTIVE run has no callback seam and keeps the component grant, which
|
|
12
35
|
* is correct: a human started it, and there is no task to scope to.
|
|
@@ -14,8 +37,10 @@ import { type GibsonSession, type KnowledgeSource } from "@zeroroot-ai/sdk";
|
|
|
14
37
|
* A dispatch that carries an endpoint but no token FAILS rather than falling
|
|
15
38
|
* back. Falling back is how the gap reappears silently, and a caller who wanted
|
|
16
39
|
* task-scoped reads would get component-scoped ones without being told.
|
|
40
|
+
*
|
|
41
|
+
* `openTaskHarness` derives the `ContextInfo` every callback RPC carries from
|
|
42
|
+
* the grant's own claims, so a task-scoped read reaches the daemon naming the
|
|
43
|
+
* mission it belongs to. It also renews the grant on a timer, which is why the
|
|
44
|
+
* caller must {@link SelectedKnowledge.stop} the source when the session ends.
|
|
17
45
|
*/
|
|
18
|
-
export declare function selectKnowledgeSource(session: GibsonSession):
|
|
19
|
-
knowledge: KnowledgeSource;
|
|
20
|
-
scope: "task" | "component";
|
|
21
|
-
};
|
|
46
|
+
export declare function selectKnowledgeSource(session: GibsonSession, deps?: KnowledgeSourceDeps): SelectedKnowledge;
|
package/dist/knowledge-source.js
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Elastic-2.0
|
|
2
|
+
// Copyright 2026 Zero Root AI
|
|
1
3
|
import { componentKnowledge, openTaskHarness, taskKnowledge, } from "@zeroroot-ai/sdk";
|
|
2
4
|
/**
|
|
3
5
|
* Pick the grant this process should read the knowledge graph with.
|
|
4
6
|
*
|
|
5
7
|
* A DISPATCHED run must read as the TASK, not as the component. gibson mints a
|
|
6
|
-
* per-dispatch capability grant and
|
|
7
|
-
* GIBSON_CALLBACK_ENDPOINT / GIBSON_CALLBACK_TOKEN. Reading with the
|
|
8
|
-
* own grant instead is broader authority than the dispatch
|
|
9
|
-
* no per-task attribution on anything the run reads — the
|
|
8
|
+
* per-dispatch capability grant, and the driver passes it to this opencode
|
|
9
|
+
* child as GIBSON_CALLBACK_ENDPOINT / GIBSON_CALLBACK_TOKEN. Reading with the
|
|
10
|
+
* component's own grant instead is broader authority than the dispatch
|
|
11
|
+
* intended and leaves no per-task attribution on anything the run reads — the
|
|
12
|
+
* gap ADR-0006 recorded.
|
|
13
|
+
*
|
|
14
|
+
* THESE TWO NAMES ARE THE CHILD CONTRACT, NOT THE LAUNCHER CONTRACT. The
|
|
15
|
+
* launcher writes GIBSON_CG_JWT and GIBSON_AGENT_TASK_B64 into the sandbox,
|
|
16
|
+
* and `readSandboxDispatch` in `@zeroroot-ai/sdk` is the one reader of those
|
|
17
|
+
* (see `dispatch.ts`). This module runs one process further down, inside
|
|
18
|
+
* opencode, whose environment `dispatchChildEnv` writes. It never sees the
|
|
19
|
+
* launch environment, so it reads the child names and nothing else.
|
|
10
20
|
*
|
|
11
21
|
* An INTERACTIVE run has no callback seam and keeps the component grant, which
|
|
12
22
|
* is correct: a human started it, and there is no task to scope to.
|
|
@@ -14,25 +24,36 @@ import { componentKnowledge, openTaskHarness, taskKnowledge, } from "@zeroroot-a
|
|
|
14
24
|
* A dispatch that carries an endpoint but no token FAILS rather than falling
|
|
15
25
|
* back. Falling back is how the gap reappears silently, and a caller who wanted
|
|
16
26
|
* task-scoped reads would get component-scoped ones without being told.
|
|
27
|
+
*
|
|
28
|
+
* `openTaskHarness` derives the `ContextInfo` every callback RPC carries from
|
|
29
|
+
* the grant's own claims, so a task-scoped read reaches the daemon naming the
|
|
30
|
+
* mission it belongs to. It also renews the grant on a timer, which is why the
|
|
31
|
+
* caller must {@link SelectedKnowledge.stop} the source when the session ends.
|
|
17
32
|
*/
|
|
18
|
-
export function selectKnowledgeSource(session) {
|
|
33
|
+
export function selectKnowledgeSource(session, deps = {}) {
|
|
19
34
|
const endpoint = process.env.GIBSON_CALLBACK_ENDPOINT;
|
|
20
35
|
const token = process.env.GIBSON_CALLBACK_TOKEN;
|
|
21
36
|
if (!endpoint) {
|
|
22
|
-
return {
|
|
37
|
+
return {
|
|
38
|
+
knowledge: componentKnowledge(session.clients.component),
|
|
39
|
+
scope: "component",
|
|
40
|
+
stop: () => { },
|
|
41
|
+
};
|
|
23
42
|
}
|
|
24
43
|
if (!token) {
|
|
25
44
|
throw new Error("GIBSON_CALLBACK_ENDPOINT is set but GIBSON_CALLBACK_TOKEN is not. A dispatched run " +
|
|
26
45
|
"must read with its task grant; falling back to the component grant would silently " +
|
|
27
46
|
"widen this run's authority.");
|
|
28
47
|
}
|
|
48
|
+
const harness = (deps.openHarness ?? openTaskHarness)({
|
|
49
|
+
endpoint,
|
|
50
|
+
token,
|
|
51
|
+
// A local or kind daemon may serve the callback listener without TLS.
|
|
52
|
+
insecure: process.env.GIBSON_CALLBACK_INSECURE === "1",
|
|
53
|
+
});
|
|
29
54
|
return {
|
|
30
|
-
knowledge: taskKnowledge(
|
|
31
|
-
endpoint,
|
|
32
|
-
token,
|
|
33
|
-
// A local or kind daemon may serve the callback listener without TLS.
|
|
34
|
-
insecure: process.env.GIBSON_CALLBACK_INSECURE === "1",
|
|
35
|
-
})),
|
|
55
|
+
knowledge: taskKnowledge(harness),
|
|
36
56
|
scope: "task",
|
|
57
|
+
stop: () => harness.stop(),
|
|
37
58
|
};
|
|
38
59
|
}
|
package/dist/knowledge.d.ts
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
import type { ToolDefinition } from "@opencode-ai/plugin";
|
|
2
1
|
import { type KnowledgeSource } from "@zeroroot-ai/sdk";
|
|
3
|
-
export declare function recallTool(knowledge: KnowledgeSource): ToolDefinition;
|
|
4
|
-
/**
|
|
5
|
-
* Per-session ambient knowledge for the system prompt.
|
|
6
|
-
*
|
|
7
|
-
* Returns a transform that injects a short context block once a session has
|
|
8
|
-
* produced one. The lookup is fired once and cached, so a session pays for at
|
|
9
|
-
* most one GraphRAG query no matter how many turns it runs.
|
|
10
|
-
*/
|
|
11
2
|
export declare function ambientKnowledge(knowledge: KnowledgeSource, seedQuery: string): (input: {
|
|
12
3
|
sessionID?: string;
|
|
13
4
|
}, output: {
|
package/dist/knowledge.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// SPDX-License-Identifier: Elastic-2.0
|
|
2
|
+
// Copyright 2026 Zero Root AI
|
|
3
|
+
import { formatKnowledgeForPrompt, } from "@zeroroot-ai/sdk";
|
|
3
4
|
/**
|
|
4
5
|
* Knowledge — read the tenant graph so the agent sharpens across runs
|
|
5
6
|
* (zerocool-plugins#8).
|
|
@@ -22,57 +23,6 @@ import { formatKnowledgeForPrompt, isSeamUnavailable, } from "@zeroroot-ai/sdk";
|
|
|
22
23
|
*/
|
|
23
24
|
/** How many hits the ambient block injects. Kept small — it is prompt overhead. */
|
|
24
25
|
const AMBIENT_LIMIT = 5;
|
|
25
|
-
export function recallTool(knowledge) {
|
|
26
|
-
const z = tool.schema;
|
|
27
|
-
return tool({
|
|
28
|
-
description: "Search this tenant's Gibson knowledge graph for prior findings, targets and " +
|
|
29
|
-
"security facts recorded by earlier runs. Use it before starting work on an " +
|
|
30
|
-
"unfamiliar area, or to check whether an issue was already reported.",
|
|
31
|
-
args: {
|
|
32
|
-
query: z.string().describe("What to look for, in natural language."),
|
|
33
|
-
limit: z.number().min(1).max(50).optional().describe("Maximum hits to return. Defaults to 10."),
|
|
34
|
-
node_types: z
|
|
35
|
-
.array(z.string())
|
|
36
|
-
.optional()
|
|
37
|
-
.describe('Restrict to node types, e.g. ["finding"] or ["host", "technique"].'),
|
|
38
|
-
},
|
|
39
|
-
async execute(args) {
|
|
40
|
-
let hits;
|
|
41
|
-
try {
|
|
42
|
-
hits = await knowledge.query({
|
|
43
|
-
text: args.query,
|
|
44
|
-
topK: args.limit ?? 10,
|
|
45
|
-
...(args.node_types ? { nodeTypes: args.node_types } : {}),
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
catch (e) {
|
|
49
|
-
if (isSeamUnavailable(e)) {
|
|
50
|
-
return {
|
|
51
|
-
title: "recall unavailable",
|
|
52
|
-
output: "The Gibson knowledge graph is not available on this daemon: the GraphRAG " +
|
|
53
|
-
"seam is not wired (gibson#1186). Continue without prior context.",
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
throw e;
|
|
57
|
-
}
|
|
58
|
-
if (hits.length === 0) {
|
|
59
|
-
return { title: "no matches", output: `Nothing in the tenant graph matches "${args.query}".` };
|
|
60
|
-
}
|
|
61
|
-
return {
|
|
62
|
-
title: `${hits.length} match${hits.length === 1 ? "" : "es"}`,
|
|
63
|
-
output: formatKnowledgeForPrompt(hits),
|
|
64
|
-
metadata: { hit_count: hits.length, node_types: [...new Set(hits.map((h) => h.type))] },
|
|
65
|
-
};
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Per-session ambient knowledge for the system prompt.
|
|
71
|
-
*
|
|
72
|
-
* Returns a transform that injects a short context block once a session has
|
|
73
|
-
* produced one. The lookup is fired once and cached, so a session pays for at
|
|
74
|
-
* most one GraphRAG query no matter how many turns it runs.
|
|
75
|
-
*/
|
|
76
26
|
export function ambientKnowledge(knowledge, seedQuery) {
|
|
77
27
|
const cache = new Map();
|
|
78
28
|
const inFlight = new Map();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How opencode reaches the Gibson MCP server (ADR-0008, slice A7).
|
|
3
|
+
*
|
|
4
|
+
* opencode spawns a local MCP server from its `mcp` block, the same way every
|
|
5
|
+
* other host does from its own config file. The plugin writes that entry in
|
|
6
|
+
* the `config` hook, so a user configures nothing.
|
|
7
|
+
*
|
|
8
|
+
* The command is the published package, not a path inside this plugin: the
|
|
9
|
+
* server is shared by every host and released on the SDK train, so pinning it
|
|
10
|
+
* to whatever this plugin happens to bundle would freeze the tool set.
|
|
11
|
+
*/
|
|
12
|
+
export declare const GIBSON_MCP_PACKAGE = "@zeroroot-ai/gibson-mcp@latest";
|
|
13
|
+
export interface OpencodeMcpLocal {
|
|
14
|
+
type: "local";
|
|
15
|
+
command: string[];
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
environment?: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
/** The `mcp.gibson` entry. `environment` carries nothing secret. */
|
|
20
|
+
export declare function gibsonMcpServer(env?: NodeJS.ProcessEnv): OpencodeMcpLocal;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Elastic-2.0
|
|
2
|
+
// Copyright 2026 Zero Root AI
|
|
3
|
+
/**
|
|
4
|
+
* How opencode reaches the Gibson MCP server (ADR-0008, slice A7).
|
|
5
|
+
*
|
|
6
|
+
* opencode spawns a local MCP server from its `mcp` block, the same way every
|
|
7
|
+
* other host does from its own config file. The plugin writes that entry in
|
|
8
|
+
* the `config` hook, so a user configures nothing.
|
|
9
|
+
*
|
|
10
|
+
* The command is the published package, not a path inside this plugin: the
|
|
11
|
+
* server is shared by every host and released on the SDK train, so pinning it
|
|
12
|
+
* to whatever this plugin happens to bundle would freeze the tool set.
|
|
13
|
+
*/
|
|
14
|
+
export const GIBSON_MCP_PACKAGE = "@zeroroot-ai/gibson-mcp@latest";
|
|
15
|
+
/** The `mcp.gibson` entry. `environment` carries nothing secret. */
|
|
16
|
+
export function gibsonMcpServer(env = process.env) {
|
|
17
|
+
const environment = {};
|
|
18
|
+
// Addressing only. The server does its own check-in, and a grant is never
|
|
19
|
+
// written into a config file.
|
|
20
|
+
for (const name of ["GIBSON_PLATFORM_URL", "GIBSON_DAEMON_URL", "GIBSON_TARGET_ID", "GIBSON_CA_CERT"]) {
|
|
21
|
+
const value = env[name];
|
|
22
|
+
if (value)
|
|
23
|
+
environment[name] = value;
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
type: "local",
|
|
27
|
+
command: ["npx", "--yes", "--package", GIBSON_MCP_PACKAGE, "gibson-mcp", "--transport", "stdio"],
|
|
28
|
+
enabled: true,
|
|
29
|
+
...(Object.keys(environment).length > 0 ? { environment } : {}),
|
|
30
|
+
};
|
|
31
|
+
}
|
package/dist/opencode-run.js
CHANGED
package/dist/semgrep.js
CHANGED
package/dist/serve-agent.js
CHANGED
package/dist/serve.js
CHANGED
package/dist/source-analysis.js
CHANGED
package/dist/watch.js
CHANGED
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeroroot-ai/zerocool",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "opencode
|
|
5
|
-
"license": "
|
|
3
|
+
"version": "0.9.1",
|
|
4
|
+
"description": "opencode host adapter for Gibson (zerocool): zero-config LLM through the harness, ambient knowledge, and the Gibson MCP server registered for you. The tools come from @zeroroot-ai/gibson-mcp.",
|
|
5
|
+
"license": "Elastic-2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/zeroroot-ai/zerocool-plugins.git",
|
|
@@ -23,13 +23,13 @@
|
|
|
23
23
|
"test": "tsc -p tsconfig.test.json && node --test \"dist-test/**/*.test.js\""
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@zeroroot-ai/sdk": "^0.
|
|
26
|
+
"@zeroroot-ai/sdk": "^0.12.0"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
29
|
"@opencode-ai/plugin": "*"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@opencode-ai/plugin": "^1.18.
|
|
32
|
+
"@opencode-ai/plugin": "^1.18.27"
|
|
33
33
|
},
|
|
34
34
|
"bin": {
|
|
35
35
|
"zerocool-serve": "dist/serve.js",
|
package/dist/componentize.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { ToolDefinition } from "@opencode-ai/plugin";
|
|
2
|
-
import { type GibsonSession } from "@zeroroot-ai/sdk";
|
|
3
|
-
/**
|
|
4
|
-
* Write a validated component manifest to disk. Available with or without a
|
|
5
|
-
* Gibson session — the manifest is the artifact a build pipeline needs, and
|
|
6
|
-
* producing it must not require a platform connection.
|
|
7
|
-
*/
|
|
8
|
-
export declare function componentizeTool(): ToolDefinition;
|
|
9
|
-
/** Register a produced artifact with the tenant registry. */
|
|
10
|
-
export declare function enrollComponentTool(session: GibsonSession): ToolDefinition;
|