@zeroroot-ai/zerocool 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dispatch.d.ts +2 -1
- package/dist/dispatch.js +22 -6
- package/dist/findings.d.ts +21 -2
- package/dist/findings.js +1 -80
- package/dist/index.d.ts +11 -5
- package/dist/index.js +33 -38
- package/dist/knowledge.d.ts +0 -9
- package/dist/knowledge.js +1 -53
- package/dist/mcp-config.d.ts +20 -0
- package/dist/mcp-config.js +29 -0
- package/dist/watch.d.ts +47 -33
- package/dist/watch.js +59 -69
- package/package.json +3 -3
- 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/watch.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { remember, taskKnowledge } from "@zeroroot-ai/sdk";
|
|
1
|
+
import { buildCreateMissionRequest, remember, taskKnowledge } from "@zeroroot-ai/sdk";
|
|
2
2
|
import { TRIGGER_STATUS } from "./gitlab.js";
|
|
3
3
|
/**
|
|
4
4
|
* The always-on watch loop (zerocool-plugins#88).
|
|
@@ -222,81 +222,47 @@ export function worldCheckpoints(harness, application) {
|
|
|
222
222
|
export function checkpointQueryText(application) {
|
|
223
223
|
return `${CHECKPOINT_KIND} application=${application}`;
|
|
224
224
|
}
|
|
225
|
+
// ---------------------------------------------------------------------------
|
|
226
|
+
// The Scan mission
|
|
227
|
+
// ---------------------------------------------------------------------------
|
|
225
228
|
/**
|
|
226
|
-
* The Scan mission
|
|
229
|
+
* The name of the checked-in Scan mission in gibson's mission catalog.
|
|
227
230
|
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
|
|
231
|
-
|
|
231
|
+
* One file in `internal/platform/missioncatalog/missions/` is one mission,
|
|
232
|
+
* named for its file: `scan.cue` is `"scan"`.
|
|
233
|
+
*/
|
|
234
|
+
export const SCAN_CATALOG_MISSION = "scan";
|
|
235
|
+
/**
|
|
236
|
+
* The seven parameters the checked-in Scan mission is rendered with.
|
|
232
237
|
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
* no
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
238
|
+
* These names are the catalog's, not ours: `missioncatalog.Params.fields()` is
|
|
239
|
+
* the one declaration of them, and the daemon REFUSES an unrecognised key
|
|
240
|
+
* rather than ignoring it. That refusal is the smuggling defence — `Params` has
|
|
241
|
+
* no target or host field, so the runtime target binds from the mission's
|
|
242
|
+
* `target_id` alone and a caller cannot point a scan at a host the tenant never
|
|
243
|
+
* registered. A key renamed here without renaming it there fails loudly at the
|
|
244
|
+
* next launch, which is the intended outcome.
|
|
240
245
|
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
246
|
+
* Every one is required. CUE refuses an incomplete render rather than
|
|
247
|
+
* substituting an empty string, so a missing commit fails instead of scanning
|
|
248
|
+
* HEAD and a missing image fails instead of scanning nothing.
|
|
244
249
|
*/
|
|
245
|
-
export function
|
|
246
|
-
const {
|
|
247
|
-
const context = {
|
|
248
|
-
"zerocool.task": "source-analysis",
|
|
249
|
-
application,
|
|
250
|
-
"pipeline.id": String(pipeline.id),
|
|
251
|
-
"pipeline.url": pipeline.webUrl,
|
|
252
|
-
"repository.url": inputs.repositoryUrl,
|
|
253
|
-
"repository.ref": pipeline.ref,
|
|
254
|
-
"repository.commit": pipeline.sha,
|
|
255
|
-
...(inputs.imageRef ? { "image.ref": inputs.imageRef } : {}),
|
|
256
|
-
...(inputs.targetId ? { "target.id": inputs.targetId } : {}),
|
|
257
|
-
};
|
|
250
|
+
export function scanCatalogParams(inputs) {
|
|
251
|
+
const { pipeline } = inputs;
|
|
258
252
|
return {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
type: "NODE_TYPE_AGENT",
|
|
267
|
-
name: "scan",
|
|
268
|
-
description: "Source analysis of the commit the pipeline built.",
|
|
269
|
-
agentConfig: {
|
|
270
|
-
agentName: "zerocool",
|
|
271
|
-
task: {
|
|
272
|
-
id: "scan",
|
|
273
|
-
goal: `Scan the ${application} repository at commit ${pipeline.sha} and submit a Finding for ` +
|
|
274
|
-
"every real weakness you can support with evidence.",
|
|
275
|
-
context: typedContext(context),
|
|
276
|
-
},
|
|
277
|
-
},
|
|
278
|
-
},
|
|
279
|
-
},
|
|
280
|
-
entryPoints: ["scan"],
|
|
253
|
+
application: inputs.application,
|
|
254
|
+
repositoryUrl: inputs.repositoryUrl,
|
|
255
|
+
ref: pipeline.ref,
|
|
256
|
+
commit: pipeline.sha,
|
|
257
|
+
pipelineId: String(pipeline.id),
|
|
258
|
+
pipelineUrl: pipeline.webUrl,
|
|
259
|
+
imageRef: inputs.imageRef,
|
|
281
260
|
};
|
|
282
261
|
}
|
|
283
262
|
/** The child mission's name, which is also how a person finds it in the dashboard. */
|
|
284
263
|
export function scanMissionName(inputs) {
|
|
285
264
|
return `scan ${inputs.application} pipeline ${inputs.pipeline.id}`;
|
|
286
265
|
}
|
|
287
|
-
/**
|
|
288
|
-
* Render a string map as `map<string, gibson.common.v1.TypedValue>` in
|
|
289
|
-
* protojson. Every value the loop passes is a string, so every entry is a
|
|
290
|
-
* `stringValue`.
|
|
291
|
-
*/
|
|
292
|
-
export function typedContext(ctx) {
|
|
293
|
-
const out = {};
|
|
294
|
-
for (const [k, v] of Object.entries(ctx)) {
|
|
295
|
-
if (v !== "")
|
|
296
|
-
out[k] = { stringValue: v };
|
|
297
|
-
}
|
|
298
|
-
return out;
|
|
299
|
-
}
|
|
300
266
|
/**
|
|
301
267
|
* A {@link ScanLauncher} over the harness callbacks, on the dispatch grant.
|
|
302
268
|
*
|
|
@@ -304,19 +270,43 @@ export function typedContext(ctx) {
|
|
|
304
270
|
* daemon resolves the parent from this harness, so the Scan mission is a child
|
|
305
271
|
* of the watch mission and inherits its tenant. Nothing in the request names a
|
|
306
272
|
* parent, a tenant or an originator, so nothing here can widen them.
|
|
273
|
+
*
|
|
274
|
+
* THE MISSION IS NAMED, NOT BUILT (ADR-0018, gibson#1688). The Scan mission is
|
|
275
|
+
* checked into gibson's `missioncatalog` and that copy is authoritative; this
|
|
276
|
+
* launcher sends its name and seven parameters. It used to build its own graph,
|
|
277
|
+
* because `CreateMission` had no way to ask for the catalog's copy — two
|
|
278
|
+
* descriptions of one mission, which is the parallel definition ADR-0027
|
|
279
|
+
* forbids. There is no fallback to the old path: a definition that only one of
|
|
280
|
+
* the two knew about is the drift the single copy exists to prevent.
|
|
281
|
+
*
|
|
282
|
+
* The request goes through the SDK's `buildCreateMissionRequest` rather than
|
|
283
|
+
* being assembled here, because that builder normalises the one trap in this
|
|
284
|
+
* call. `JSON.stringify(undefined)` encodes to zero bytes but
|
|
285
|
+
* `JSON.stringify(null)` encodes to the four-byte string `"null"`, so a graph
|
|
286
|
+
* field filled in unconditionally puts a non-empty body on the wire beside the
|
|
287
|
+
* catalog name; the daemon refuses the pair, and the failure reads as "the
|
|
288
|
+
* catalog path does not work" rather than "you passed a null definition".
|
|
289
|
+
*
|
|
290
|
+
* The response is then sent by hand rather than through `createTaskMission`,
|
|
291
|
+
* for one reason: `CreateMissionResponse` carries an in-band `error`, and the
|
|
292
|
+
* daemon puts its most useful refusals there — an unknown parameter key naming
|
|
293
|
+
* the key and listing the known set, or every missing parameter reported at
|
|
294
|
+
* once. `createTaskMission` reads only `mission`, so those would surface as
|
|
295
|
+
* "returned no mission" and the operator would lose the sentence that says what
|
|
296
|
+
* to fix.
|
|
307
297
|
*/
|
|
308
298
|
export function harnessScanLauncher(harness, opts) {
|
|
309
|
-
const build = opts.definition ?? scanMissionDefinition;
|
|
310
299
|
const timeoutMs = BigInt(opts.scanTimeoutMs ?? DEFAULT_SCAN_TIMEOUT_MS);
|
|
311
300
|
return {
|
|
312
301
|
async launch(pipeline) {
|
|
313
|
-
const inputs = { ...opts.inputs, pipeline
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
302
|
+
const inputs = { ...opts.inputs, pipeline };
|
|
303
|
+
const wire = buildCreateMissionRequest({
|
|
304
|
+
catalogMission: SCAN_CATALOG_MISSION,
|
|
305
|
+
catalogParams: scanCatalogParams(inputs),
|
|
317
306
|
targetId: opts.targetId,
|
|
318
307
|
name: scanMissionName(inputs),
|
|
319
308
|
});
|
|
309
|
+
const created = await harness.client.createMission({ context: harness.context, ...wire });
|
|
320
310
|
if (created.error) {
|
|
321
311
|
throw new Error(`CreateMission refused: ${created.error.message}`);
|
|
322
312
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeroroot-ai/zerocool",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "opencode
|
|
3
|
+
"version": "0.9.0",
|
|
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
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -23,7 +23,7 @@
|
|
|
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.11.0"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
29
|
"@opencode-ai/plugin": "*"
|
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;
|
package/dist/componentize.js
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import { tool } from "@opencode-ai/plugin";
|
|
2
|
-
import { buildComponentManifest, enrollComponent, enrollmentSupported, validateComponentSpec, } from "@zeroroot-ai/sdk";
|
|
3
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
4
|
-
import { dirname, resolve } from "node:path";
|
|
5
|
-
/**
|
|
6
|
-
* Componentize produced artifacts into the tenant fleet (zerocool-plugins#11).
|
|
7
|
-
*
|
|
8
|
-
* The factory payoff: an artifact the agent built becomes a registered Gibson
|
|
9
|
-
* component. The contract is language-neutral — `RegisterComponent` describes a
|
|
10
|
-
* component by kind, name, version and contract fields, never by the language
|
|
11
|
-
* it was written in.
|
|
12
|
-
*
|
|
13
|
-
* Two tools, because the work splits at a real boundary:
|
|
14
|
-
*
|
|
15
|
-
* - `gibson_componentize` writes the manifest to disk without touching the
|
|
16
|
-
* platform. It is the step that can always run, and the artefact it produces
|
|
17
|
-
* is what a build pipeline consumes.
|
|
18
|
-
* - `gibson_enroll_component` registers the manifest with the daemon.
|
|
19
|
-
*
|
|
20
|
-
* WHAT THIS DOES NOT DO. Image build and publish is a build-system concern, not
|
|
21
|
-
* an RPC; the OCI reference is an input here, carried in `metadata.image`. And
|
|
22
|
-
* enrollment currently registers under the CALLING agent's identity — the
|
|
23
|
-
* policy-bounded RPC that would give a produced component its own short-TTL
|
|
24
|
-
* credential is gibson#1185 and does not exist yet. The tool says so in its
|
|
25
|
-
* output rather than implying the artifact is independently dispatchable.
|
|
26
|
-
*/
|
|
27
|
-
const KINDS = ["agent", "tool", "plugin"];
|
|
28
|
-
/** Shared argument shape for both tools. */
|
|
29
|
-
function specArgs() {
|
|
30
|
-
const z = tool.schema;
|
|
31
|
-
return {
|
|
32
|
-
kind: z.enum(KINDS).describe("Component kind."),
|
|
33
|
-
name: z.string().describe("Component name, as it will appear in the tenant registry."),
|
|
34
|
-
version: z.string().describe('Semantic version of the artifact, e.g. "0.1.0".'),
|
|
35
|
-
image: z
|
|
36
|
-
.string()
|
|
37
|
-
.optional()
|
|
38
|
-
.describe("OCI image reference of the built artifact. Required before it can be dispatched."),
|
|
39
|
-
language: z.string().optional().describe("Language the artifact is written in, for the catalog."),
|
|
40
|
-
capabilities: z
|
|
41
|
-
.array(z.string())
|
|
42
|
-
.optional()
|
|
43
|
-
.describe('Agent capabilities. Required when kind is "agent".'),
|
|
44
|
-
methods: z.array(z.string()).optional().describe('Plugin method names. Required when kind is "plugin".'),
|
|
45
|
-
input_message_type: z
|
|
46
|
-
.string()
|
|
47
|
-
.optional()
|
|
48
|
-
.describe('Fully-qualified proto input type. Required when kind is "tool".'),
|
|
49
|
-
output_message_type: z
|
|
50
|
-
.string()
|
|
51
|
-
.optional()
|
|
52
|
-
.describe('Fully-qualified proto output type. Required when kind is "tool".'),
|
|
53
|
-
config_schema_json: z.string().optional().describe("JSON Schema for a plugin's configuration."),
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
/** Build a ComponentSpec from tool arguments. */
|
|
57
|
-
function toSpec(args) {
|
|
58
|
-
const metadata = {};
|
|
59
|
-
if (args.image)
|
|
60
|
-
metadata.image = args.image;
|
|
61
|
-
if (args.language)
|
|
62
|
-
metadata.language = args.language;
|
|
63
|
-
return {
|
|
64
|
-
kind: args.kind,
|
|
65
|
-
name: args.name,
|
|
66
|
-
version: args.version,
|
|
67
|
-
metadata,
|
|
68
|
-
...(args.capabilities ? { capabilities: args.capabilities } : {}),
|
|
69
|
-
...(args.methods ? { methods: args.methods } : {}),
|
|
70
|
-
...(args.input_message_type ? { inputMessageType: args.input_message_type } : {}),
|
|
71
|
-
...(args.output_message_type ? { outputMessageType: args.output_message_type } : {}),
|
|
72
|
-
...(args.config_schema_json ? { configSchemaJson: args.config_schema_json } : {}),
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Write a validated component manifest to disk. Available with or without a
|
|
77
|
-
* Gibson session — the manifest is the artifact a build pipeline needs, and
|
|
78
|
-
* producing it must not require a platform connection.
|
|
79
|
-
*/
|
|
80
|
-
export function componentizeTool() {
|
|
81
|
-
const z = tool.schema;
|
|
82
|
-
return tool({
|
|
83
|
-
description: "Turn a built artifact into a Gibson component manifest and write it to disk. " +
|
|
84
|
-
"Use this after building a tool or agent you want to add to the fleet. Validates " +
|
|
85
|
-
"the artifact against Gibson's component contract; does not build or push an image.",
|
|
86
|
-
args: {
|
|
87
|
-
...specArgs(),
|
|
88
|
-
path: z
|
|
89
|
-
.string()
|
|
90
|
-
.optional()
|
|
91
|
-
.describe('Where to write the manifest. Defaults to "gibson-component.json".'),
|
|
92
|
-
},
|
|
93
|
-
async execute(args, ctx) {
|
|
94
|
-
const spec = toSpec(args);
|
|
95
|
-
const problems = validateComponentSpec(spec);
|
|
96
|
-
if (problems.length > 0) {
|
|
97
|
-
return {
|
|
98
|
-
title: "invalid component spec",
|
|
99
|
-
output: `This artifact does not satisfy Gibson's ${spec.kind} contract:\n${problems.map((p) => `- ${p}`).join("\n")}`,
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
const manifest = buildComponentManifest(spec);
|
|
103
|
-
const target = resolve(ctx.directory, args.path ?? "gibson-component.json");
|
|
104
|
-
await mkdir(dirname(target), { recursive: true });
|
|
105
|
-
await writeFile(target, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
106
|
-
const missingImage = spec.metadata?.image
|
|
107
|
-
? ""
|
|
108
|
-
: "\n\nNo image reference was given. Build and push an image, then re-run with " +
|
|
109
|
-
"image=<oci-ref> before enrolling — a component without an image cannot be dispatched.";
|
|
110
|
-
return {
|
|
111
|
-
title: `${spec.kind} manifest: ${spec.name}`,
|
|
112
|
-
output: `Wrote a valid ${spec.kind} manifest for ${spec.name}@${spec.version} to ${target}.${missingImage}`,
|
|
113
|
-
metadata: { path: target, kind: spec.kind, name: spec.name },
|
|
114
|
-
};
|
|
115
|
-
},
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
/** Register a produced artifact with the tenant registry. */
|
|
119
|
-
export function enrollComponentTool(session) {
|
|
120
|
-
return tool({
|
|
121
|
-
description: "Register a produced artifact with Gibson so it joins the tenant fleet. Run " +
|
|
122
|
-
"gibson_componentize first to check the artifact against the component contract.",
|
|
123
|
-
args: specArgs(),
|
|
124
|
-
async execute(args) {
|
|
125
|
-
const spec = toSpec(args);
|
|
126
|
-
const problems = validateComponentSpec(spec);
|
|
127
|
-
if (problems.length > 0) {
|
|
128
|
-
return {
|
|
129
|
-
title: "invalid component spec",
|
|
130
|
-
output: `This artifact does not satisfy Gibson's ${spec.kind} contract:\n${problems.map((p) => `- ${p}`).join("\n")}`,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
const result = await enrollComponent(session.clients.component, spec);
|
|
134
|
-
const { reason } = enrollmentSupported();
|
|
135
|
-
return {
|
|
136
|
-
title: `enrolled ${spec.name}`,
|
|
137
|
-
output: `Registered ${spec.kind} ${spec.name}@${spec.version} as instance ${result.instanceId}.\n\n` +
|
|
138
|
-
`Note: ${reason}. It is visible in the tenant registry, but it will drop out ` +
|
|
139
|
-
`when heartbeats stop (every ${Math.round(result.heartbeatIntervalMs / 1000)}s) ` +
|
|
140
|
-
`unless the artifact itself runs and checks in.`,
|
|
141
|
-
metadata: {
|
|
142
|
-
instance_id: result.instanceId,
|
|
143
|
-
has_own_identity: result.hasOwnIdentity,
|
|
144
|
-
kind: spec.kind,
|
|
145
|
-
},
|
|
146
|
-
};
|
|
147
|
-
},
|
|
148
|
-
});
|
|
149
|
-
}
|
package/dist/delegate.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { ToolDefinition } from "@opencode-ai/plugin";
|
|
2
|
-
import { type GibsonSession } from "@zeroroot-ai/sdk";
|
|
3
|
-
export declare function listAgentsTool(session: GibsonSession): ToolDefinition;
|
|
4
|
-
export declare function delegateTool(session: GibsonSession): ToolDefinition;
|
|
5
|
-
export declare function createMissionTool(session: GibsonSession): ToolDefinition;
|
|
6
|
-
export declare function runMissionTool(session: GibsonSession): ToolDefinition;
|
|
7
|
-
export declare function missionStatusTool(session: GibsonSession): ToolDefinition;
|
|
8
|
-
export declare function missionResultsTool(session: GibsonSession): ToolDefinition;
|
|
9
|
-
export declare function cancelMissionTool(session: GibsonSession): ToolDefinition;
|
|
10
|
-
/** Every delegation and mission tool, keyed for the opencode `tool` hook. */
|
|
11
|
-
export declare function delegationTools(session: GibsonSession): Record<string, ToolDefinition>;
|
package/dist/delegate.js
DELETED
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
import { tool } from "@opencode-ai/plugin";
|
|
2
|
-
import { cancelMission, createMission, delegateToAgent, getMissionResults, getMissionStatus, isSeamUnavailable, listAgents, newTask, runMission, waitMission, } from "@zeroroot-ai/sdk";
|
|
3
|
-
/**
|
|
4
|
-
* Delegation and missions — the factory's dispatch and recursion
|
|
5
|
-
* (zerocool-plugins#10).
|
|
6
|
-
*
|
|
7
|
-
* Rules-of-engagement, authz and budget denials come back as daemon errors.
|
|
8
|
-
* They are surfaced verbatim to the model rather than retried: a denial is a
|
|
9
|
-
* policy decision, and an agent that retries a denied delegation is just
|
|
10
|
-
* burning budget against a wall.
|
|
11
|
-
*
|
|
12
|
-
* Every RPC here needs the daemon's `agentDelegator` / `missionMgr` seams,
|
|
13
|
-
* which are not wired (gibson#1186). Each tool reports that state plainly
|
|
14
|
-
* instead of surfacing a bare "unimplemented" to the model.
|
|
15
|
-
*/
|
|
16
|
-
const UNWIRED = "This daemon has no agent delegation or mission management wired (gibson#1186). " +
|
|
17
|
-
"Complete the work directly instead.";
|
|
18
|
-
export function listAgentsTool(session) {
|
|
19
|
-
return tool({
|
|
20
|
-
description: "List the Gibson agents in this tenant that work can be delegated to, with " +
|
|
21
|
-
"their capabilities and the target types they handle.",
|
|
22
|
-
args: {},
|
|
23
|
-
async execute() {
|
|
24
|
-
const { agents, unavailable } = await listAgents(session.clients.component);
|
|
25
|
-
if (unavailable)
|
|
26
|
-
return { title: "delegation unavailable", output: UNWIRED };
|
|
27
|
-
if (agents.length === 0) {
|
|
28
|
-
return { title: "no agents", output: "No agents are registered for this tenant." };
|
|
29
|
-
}
|
|
30
|
-
const lines = agents.map((a) => {
|
|
31
|
-
const caps = a.capabilities.length > 0 ? ` — capabilities: ${a.capabilities.join(", ")}` : "";
|
|
32
|
-
const targets = a.targetTypes.length > 0 ? ` — targets: ${a.targetTypes.join(", ")}` : "";
|
|
33
|
-
return `- ${a.name} (${a.version}) ${a.description}${caps}${targets}`;
|
|
34
|
-
});
|
|
35
|
-
return {
|
|
36
|
-
title: `${agents.length} agent${agents.length === 1 ? "" : "s"}`,
|
|
37
|
-
output: lines.join("\n"),
|
|
38
|
-
metadata: { agent_count: agents.length },
|
|
39
|
-
};
|
|
40
|
-
},
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
export function delegateTool(session) {
|
|
44
|
-
const z = tool.schema;
|
|
45
|
-
return tool({
|
|
46
|
-
description: "Delegate a sub-task to another Gibson agent and wait for its result. Use " +
|
|
47
|
-
"gibson_list_agents first to pick an agent whose capabilities match the task.",
|
|
48
|
-
args: {
|
|
49
|
-
agent: z.string().describe("Name of the agent to delegate to."),
|
|
50
|
-
goal: z.string().describe("What the delegate should accomplish."),
|
|
51
|
-
context: z
|
|
52
|
-
.record(z.string(), z.any())
|
|
53
|
-
.optional()
|
|
54
|
-
.describe("Target details, prior findings, or other context the delegate needs."),
|
|
55
|
-
max_turns: z.number().optional().describe("Cap on the delegate's LLM turns."),
|
|
56
|
-
allowed_tools: z.array(z.string()).optional().describe("Restrict the delegate to these tools."),
|
|
57
|
-
},
|
|
58
|
-
async execute(args) {
|
|
59
|
-
const constraints = {
|
|
60
|
-
...(args.max_turns ? { MaxTurns: args.max_turns } : {}),
|
|
61
|
-
...(args.allowed_tools ? { AllowedTools: args.allowed_tools } : {}),
|
|
62
|
-
};
|
|
63
|
-
const task = newTask(args.goal, {
|
|
64
|
-
...(args.context ? { Context: args.context } : {}),
|
|
65
|
-
...(Object.keys(constraints).length > 0 ? { Constraints: constraints } : {}),
|
|
66
|
-
});
|
|
67
|
-
try {
|
|
68
|
-
const result = await delegateToAgent(session.clients.component, args.agent, task);
|
|
69
|
-
const output = typeof result.Output === "string" ? result.Output : JSON.stringify(result.Output, null, 2);
|
|
70
|
-
const findings = result.Findings && result.Findings.length > 0
|
|
71
|
-
? `\n\nFindings recorded: ${result.Findings.join(", ")}`
|
|
72
|
-
: "";
|
|
73
|
-
const failure = result.error?.message ? `\n\nError: ${result.error.message}` : "";
|
|
74
|
-
return {
|
|
75
|
-
title: `${args.agent}: ${result.Status}`,
|
|
76
|
-
output: `${output ?? "(no output)"}${findings}${failure}`,
|
|
77
|
-
metadata: { status: result.Status, finding_ids: result.Findings ?? [] },
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
catch (e) {
|
|
81
|
-
if (isSeamUnavailable(e))
|
|
82
|
-
return { title: "delegation unavailable", output: UNWIRED };
|
|
83
|
-
// Authz / RoE / budget denials land here. Show the daemon's reason.
|
|
84
|
-
return {
|
|
85
|
-
title: `${args.agent}: denied`,
|
|
86
|
-
output: `Delegation to ${args.agent} was refused: ${e.message}`,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
export function createMissionTool(session) {
|
|
93
|
-
const z = tool.schema;
|
|
94
|
-
return tool({
|
|
95
|
-
description: "Create a Gibson mission from a mission definition, bound to a target. The " +
|
|
96
|
-
"mission is not started — call gibson_run_mission to queue it.",
|
|
97
|
-
args: {
|
|
98
|
-
definition: z.record(z.string(), z.any()).describe("Mission definition object."),
|
|
99
|
-
target_id: z.string().describe("Identifier of the target the mission runs against."),
|
|
100
|
-
opts: z.record(z.string(), z.any()).optional().describe("Optional mission creation options."),
|
|
101
|
-
},
|
|
102
|
-
async execute(args) {
|
|
103
|
-
try {
|
|
104
|
-
const info = await createMission(session.clients.component, args.definition, args.target_id, args.opts);
|
|
105
|
-
const id = (info.id ?? info.mission_id ?? info.missionId);
|
|
106
|
-
return {
|
|
107
|
-
title: id ? `mission ${id}` : "mission created",
|
|
108
|
-
output: JSON.stringify(info, null, 2),
|
|
109
|
-
...(id ? { metadata: { mission_id: id } } : {}),
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
catch (e) {
|
|
113
|
-
if (isSeamUnavailable(e))
|
|
114
|
-
return { title: "missions unavailable", output: UNWIRED };
|
|
115
|
-
throw e;
|
|
116
|
-
}
|
|
117
|
-
},
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
export function runMissionTool(session) {
|
|
121
|
-
const z = tool.schema;
|
|
122
|
-
return tool({
|
|
123
|
-
description: "Queue a created Gibson mission for execution. Returns as soon as it is queued.",
|
|
124
|
-
args: { mission_id: z.string().describe("Mission to run.") },
|
|
125
|
-
async execute(args) {
|
|
126
|
-
try {
|
|
127
|
-
await runMission(session.clients.component, args.mission_id);
|
|
128
|
-
return { title: "queued", output: `Mission ${args.mission_id} is queued for execution.` };
|
|
129
|
-
}
|
|
130
|
-
catch (e) {
|
|
131
|
-
if (isSeamUnavailable(e))
|
|
132
|
-
return { title: "missions unavailable", output: UNWIRED };
|
|
133
|
-
throw e;
|
|
134
|
-
}
|
|
135
|
-
},
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
export function missionStatusTool(session) {
|
|
139
|
-
const z = tool.schema;
|
|
140
|
-
return tool({
|
|
141
|
-
description: "Check a Gibson mission's status, or block until it finishes. Use wait=true " +
|
|
142
|
-
"only when you intend to stop and wait for the result.",
|
|
143
|
-
args: {
|
|
144
|
-
mission_id: z.string().describe("Mission to inspect."),
|
|
145
|
-
wait: z.boolean().optional().describe("Block until the mission reaches a terminal state."),
|
|
146
|
-
timeout_ms: z
|
|
147
|
-
.number()
|
|
148
|
-
.optional()
|
|
149
|
-
.describe("How long to block when wait is true. Defaults to 5 minutes."),
|
|
150
|
-
},
|
|
151
|
-
async execute(args) {
|
|
152
|
-
try {
|
|
153
|
-
const state = args.wait
|
|
154
|
-
? await waitMission(session.clients.component, args.mission_id, args.timeout_ms ?? 300_000)
|
|
155
|
-
: await getMissionStatus(session.clients.component, args.mission_id);
|
|
156
|
-
return {
|
|
157
|
-
title: `mission ${args.mission_id}`,
|
|
158
|
-
output: JSON.stringify(state, null, 2),
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
catch (e) {
|
|
162
|
-
if (isSeamUnavailable(e))
|
|
163
|
-
return { title: "missions unavailable", output: UNWIRED };
|
|
164
|
-
throw e;
|
|
165
|
-
}
|
|
166
|
-
},
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
export function missionResultsTool(session) {
|
|
170
|
-
const z = tool.schema;
|
|
171
|
-
return tool({
|
|
172
|
-
description: "Read the final results of a completed Gibson mission.",
|
|
173
|
-
args: { mission_id: z.string().describe("Mission whose results to read.") },
|
|
174
|
-
async execute(args) {
|
|
175
|
-
try {
|
|
176
|
-
const results = await getMissionResults(session.clients.component, args.mission_id);
|
|
177
|
-
return { title: `results ${args.mission_id}`, output: JSON.stringify(results, null, 2) };
|
|
178
|
-
}
|
|
179
|
-
catch (e) {
|
|
180
|
-
if (isSeamUnavailable(e))
|
|
181
|
-
return { title: "missions unavailable", output: UNWIRED };
|
|
182
|
-
throw e;
|
|
183
|
-
}
|
|
184
|
-
},
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
export function cancelMissionTool(session) {
|
|
188
|
-
const z = tool.schema;
|
|
189
|
-
return tool({
|
|
190
|
-
description: "Request cancellation of a running Gibson mission.",
|
|
191
|
-
args: { mission_id: z.string().describe("Mission to cancel.") },
|
|
192
|
-
async execute(args) {
|
|
193
|
-
try {
|
|
194
|
-
await cancelMission(session.clients.component, args.mission_id);
|
|
195
|
-
return { title: "cancelled", output: `Cancellation requested for mission ${args.mission_id}.` };
|
|
196
|
-
}
|
|
197
|
-
catch (e) {
|
|
198
|
-
if (isSeamUnavailable(e))
|
|
199
|
-
return { title: "missions unavailable", output: UNWIRED };
|
|
200
|
-
throw e;
|
|
201
|
-
}
|
|
202
|
-
},
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
/** Every delegation and mission tool, keyed for the opencode `tool` hook. */
|
|
206
|
-
export function delegationTools(session) {
|
|
207
|
-
return {
|
|
208
|
-
gibson_list_agents: listAgentsTool(session),
|
|
209
|
-
gibson_delegate: delegateTool(session),
|
|
210
|
-
gibson_create_mission: createMissionTool(session),
|
|
211
|
-
gibson_run_mission: runMissionTool(session),
|
|
212
|
-
gibson_mission_status: missionStatusTool(session),
|
|
213
|
-
gibson_mission_results: missionResultsTool(session),
|
|
214
|
-
gibson_cancel_mission: cancelMissionTool(session),
|
|
215
|
-
};
|
|
216
|
-
}
|
package/dist/gibson-tools.d.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import type { ToolDefinition } from "@opencode-ai/plugin";
|
|
2
|
-
import { type GibsonSession, type GibsonTool } from "@zeroroot-ai/sdk";
|
|
3
|
-
/**
|
|
4
|
-
* Gibson tools and plugins as opencode tools (zerocool-plugins#9).
|
|
5
|
-
*
|
|
6
|
-
* Discovered tools are registered **individually** — one opencode tool per
|
|
7
|
-
* Gibson tool, named `gibson_<tool>` — so the model sees them in its tool list
|
|
8
|
-
* with their own descriptions, rather than having to know that a generic
|
|
9
|
-
* dispatcher exists and what to pass it.
|
|
10
|
-
*
|
|
11
|
-
* ON SCHEMAS — the catalog carries no JSON Schema. `ToolDescriptorProto` only
|
|
12
|
-
* names the proto message types of a tool's input and output
|
|
13
|
-
* (`gibson.tool.v1.ScanRequest`), and there is no RPC that resolves those to a
|
|
14
|
-
* schema. So a per-tool wrapper takes a single free-form `input` object and
|
|
15
|
-
* names the expected message type in its description. Inventing a Zod schema
|
|
16
|
-
* from a message type name would be a guess, and a wrong guess would reject
|
|
17
|
-
* valid input before it ever reached the tool.
|
|
18
|
-
*
|
|
19
|
-
* A generic `gibson_call_tool` is always registered as well: discovery is
|
|
20
|
-
* `Unimplemented` on the current daemon (gibson#1186), and without it there
|
|
21
|
-
* would be no way to reach a tool whose name the operator already knows.
|
|
22
|
-
*/
|
|
23
|
-
/** Sanitise a Gibson tool name into an opencode tool key. */
|
|
24
|
-
export declare function toolKey(name: string): string;
|
|
25
|
-
/** Wrap one discovered Gibson tool as an opencode tool. */
|
|
26
|
-
export declare function wrapGibsonTool(session: GibsonSession, t: GibsonTool): ToolDefinition;
|
|
27
|
-
/** The always-present dispatcher for tools the catalog did not surface. */
|
|
28
|
-
export declare function callToolTool(session: GibsonSession): ToolDefinition;
|
|
29
|
-
/** List what Gibson exposes to this tenant — tools and enabled plugins. */
|
|
30
|
-
export declare function listToolsTool(session: GibsonSession): ToolDefinition;
|
|
31
|
-
/** Invoke a method on an enabled Gibson plugin. */
|
|
32
|
-
export declare function queryPluginTool(session: GibsonSession): ToolDefinition;
|
|
33
|
-
/**
|
|
34
|
-
* Discover the tenant's tools and build one opencode tool for each, plus the
|
|
35
|
-
* always-present dispatcher, catalog and plugin tools.
|
|
36
|
-
*
|
|
37
|
-
* Discovery runs once at plugin load. opencode reads the `tool` hook as a
|
|
38
|
-
* static object, so a tool registered later in the session would not be visible
|
|
39
|
-
* to the model anyway.
|
|
40
|
-
*/
|
|
41
|
-
export declare function buildGibsonTools(session: GibsonSession): Promise<{
|
|
42
|
-
tools: Record<string, ToolDefinition>;
|
|
43
|
-
discovered: number;
|
|
44
|
-
note?: string;
|
|
45
|
-
}>;
|