@zeroroot-ai/zerocool 0.10.0 → 0.11.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.
@@ -59,7 +59,7 @@
59
59
  * {@link dispatchChildEnv} — that is the opencode plugin's own contract, fed from
60
60
  * the grant the SDK read.
61
61
  */
62
- import { type AgentOutcome, type SandboxDispatch, type TaskHarness } from "@zeroroot-ai/sdk";
62
+ import { parkAfterResult, Watcher, type AgentOutcome, type SandboxDispatch, type TaskHarness } from "@zeroroot-ai/sdk";
63
63
  import { type FindingsBackend } from "./findings.js";
64
64
  import { type FindingSource, type FindingStatusWriter, type FixPlanner, type Workspace } from "./fix.js";
65
65
  import { type GitLabClient, type GitLabWriter } from "./gitlab.js";
@@ -81,8 +81,13 @@ export interface CallbackGrant {
81
81
  callbackToken: string;
82
82
  /** Dial the callback listener without TLS. Only for a local or kind daemon. */
83
83
  insecure?: boolean;
84
+ /**
85
+ * The mission run. Required: the mission ID is the same for every run of a
86
+ * record, so a callback with no run cannot be told from a callback of another
87
+ * run, and the daemon refuses it (gibson#1235).
88
+ */
89
+ missionRunId: string;
84
90
  /** Provenance the platform correlates the run by. Empty when the caller set none. */
85
- missionRunId?: string;
86
91
  agentRunId?: string;
87
92
  traceId?: string;
88
93
  }
@@ -315,3 +320,21 @@ export declare function harnessCredential(harness: TaskHarness): (name: string)
315
320
  * carries it.
316
321
  */
317
322
  export declare function formatTerminalResult(outcome: AgentOutcome): string;
323
+ /** Seams of {@link runForkableDispatch}. */
324
+ export interface ForkableDispatchDeps extends DispatchDeps {
325
+ /** Made at process start, before a fork can happen. */
326
+ watcher: Watcher;
327
+ /** Called with the outcome of each run, before a park. */
328
+ onOutcome?: (outcome: AgentOutcome) => void;
329
+ /** Test seams. */
330
+ runOnce?: typeof runDispatch;
331
+ park?: typeof parkAfterResult;
332
+ }
333
+ /**
334
+ * Run the dispatch, and run the task of a fork when this run is forked (D74).
335
+ * A fork source (`GIBSON_FORKABLE=1`) parks after a passing result. The
336
+ * parent returns at the end of the park. A fork claims its own dispatch once
337
+ * and runs that task, and the outcome of the fork is returned. A failed run
338
+ * does not park.
339
+ */
340
+ export declare function runForkableDispatch(ctx: DispatchContext, env: NodeJS.ProcessEnv, deps: ForkableDispatchDeps): Promise<AgentOutcome>;
package/dist/dispatch.js CHANGED
@@ -61,7 +61,7 @@
61
61
  * {@link dispatchChildEnv} — that is the opencode plugin's own contract, fed from
62
62
  * the grant the SDK read.
63
63
  */
64
- import { observe, readSandboxDispatch, sandboxHarness, taskKnowledge, } from "@zeroroot-ai/sdk";
64
+ import { observe, parkAfterResult, readSandboxDispatch, sandboxHarness, Watcher, taskKnowledge, } from "@zeroroot-ai/sdk";
65
65
  import { join } from "node:path";
66
66
  import { taskFindingsBackend } from "./findings.js";
67
67
  import { harnessFindingSource, harnessFindingStatus, runFix, } from "./fix.js";
@@ -79,11 +79,14 @@ import { formatSourceAnalysis, harnessTriageModel, runSourceAnalysis, } from "./
79
79
  * same environment.
80
80
  */
81
81
  export function dispatchChildEnv(g) {
82
+ if (!g.missionRunId) {
83
+ throw new Error("dispatch carries no mission run id: the child's callbacks would be refused");
84
+ }
82
85
  return {
83
86
  ...(g.callbackEndpoint ? { GIBSON_CALLBACK_ENDPOINT: g.callbackEndpoint } : {}),
84
87
  ...(g.callbackToken ? { GIBSON_CALLBACK_TOKEN: g.callbackToken } : {}),
85
88
  ...(g.insecure ? { GIBSON_CALLBACK_INSECURE: "1" } : {}),
86
- ...(g.missionRunId ? { GIBSON_MISSION_RUN_ID: g.missionRunId } : {}),
89
+ GIBSON_MISSION_RUN_ID: g.missionRunId,
87
90
  ...(g.agentRunId ? { GIBSON_AGENT_RUN_ID: g.agentRunId } : {}),
88
91
  ...(g.traceId ? { GIBSON_TRACE_ID: g.traceId } : {}),
89
92
  };
@@ -543,7 +546,34 @@ export function formatTerminalResult(outcome) {
543
546
  metadata: outcome.metadata ?? {},
544
547
  });
545
548
  }
549
+ /**
550
+ * Run the dispatch, and run the task of a fork when this run is forked (D74).
551
+ * A fork source (`GIBSON_FORKABLE=1`) parks after a passing result. The
552
+ * parent returns at the end of the park. A fork claims its own dispatch once
553
+ * and runs that task, and the outcome of the fork is returned. A failed run
554
+ * does not park.
555
+ */
556
+ export async function runForkableDispatch(ctx, env, deps) {
557
+ const runOnce = deps.runOnce ?? runDispatch;
558
+ const parkFor = deps.park ?? parkAfterResult;
559
+ let current = ctx;
560
+ let launch = env;
561
+ for (;;) {
562
+ const outcome = await runOnce(current, deps);
563
+ deps.onOutcome?.(outcome);
564
+ if (outcome.success === false)
565
+ return outcome;
566
+ const next = await parkFor(launch, deps.watcher, { insecure: current.insecure });
567
+ if (!next)
568
+ return outcome;
569
+ console.error("[zerocool-dispatch] this process is a fork: running the task of the fork");
570
+ launch = next;
571
+ current = readDispatchContext(next);
572
+ }
573
+ }
546
574
  async function main() {
575
+ // Made before any run, so a fork of this process sees its new sandbox id (D74).
576
+ const watcher = Watcher.create();
547
577
  let ctx;
548
578
  try {
549
579
  ctx = readDispatchContext(process.env);
@@ -560,13 +590,16 @@ async function main() {
560
590
  console.error(`[zerocool-dispatch] run mission=${ctx.missionRunId || "-"} agent=${ctx.agentRunId || "-"} ` +
561
591
  `workspace=${ctx.workspace}; callbacks use the per-dispatch grant`);
562
592
  try {
563
- const outcome = await runDispatch(ctx, {
593
+ await runForkableDispatch(ctx, process.env, {
594
+ watcher,
564
595
  // Forward every opencode event to our stdout, live, for the console stream.
565
596
  onEvent: (line) => process.stdout.write(`${line}\n`),
597
+ onOutcome: (outcome) => {
598
+ process.stdout.write(`${formatTerminalResult(outcome)}\n`);
599
+ console.error(`[zerocool-dispatch] done reason=${outcome.metadata?.finish_reason ?? "-"} ` +
600
+ `tokens=${outcome.metadata?.tokens_total ?? "-"}`);
601
+ },
566
602
  });
567
- process.stdout.write(`${formatTerminalResult(outcome)}\n`);
568
- console.error(`[zerocool-dispatch] done reason=${outcome.metadata?.finish_reason ?? "-"} ` +
569
- `tokens=${outcome.metadata?.tokens_total ?? "-"}`);
570
603
  process.exit(0);
571
604
  }
572
605
  catch (e) {
@@ -20,7 +20,7 @@
20
20
  * time from tools/opencode/package-lock.json, hash pinned, and
21
21
  * `ZEROCOOL_MCP_BIN` names it, the same knob the Claude member driver reads.
22
22
  */
23
- export declare const GIBSON_MCP_PACKAGE = "@zeroroot-ai/gibson-mcp@0.3.0";
23
+ export declare const GIBSON_MCP_PACKAGE = "@zeroroot-ai/gibson-mcp@0.7.1";
24
24
  /** The command opencode spawns: the installed bin when the image names one, else the pinned package through npx. */
25
25
  export declare function gibsonMcpCommand(env?: NodeJS.ProcessEnv): string[];
26
26
  export interface OpencodeMcpLocal {
@@ -22,7 +22,7 @@
22
22
  * time from tools/opencode/package-lock.json, hash pinned, and
23
23
  * `ZEROCOOL_MCP_BIN` names it, the same knob the Claude member driver reads.
24
24
  */
25
- export const GIBSON_MCP_PACKAGE = "@zeroroot-ai/gibson-mcp@0.3.0";
25
+ export const GIBSON_MCP_PACKAGE = "@zeroroot-ai/gibson-mcp@0.7.1";
26
26
  /** The command opencode spawns: the installed bin when the image names one, else the pinned package through npx. */
27
27
  export function gibsonMcpCommand(env = process.env) {
28
28
  const bin = env.ZEROCOOL_MCP_BIN;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeroroot-ai/zerocool",
3
- "version": "0.10.0",
3
+ "version": "0.11.1",
4
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": "Elastic-2.0",
6
6
  "repository": {
@@ -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.13.0"
26
+ "@zeroroot-ai/sdk": "^0.18.1"
27
27
  },
28
28
  "peerDependencies": {
29
29
  "@opencode-ai/plugin": "*"
30
30
  },
31
31
  "devDependencies": {
32
- "@opencode-ai/plugin": "^1.18.34"
32
+ "@opencode-ai/plugin": "^1.19.2"
33
33
  },
34
34
  "bin": {
35
35
  "zerocool-serve": "dist/serve.js",