@cotal-ai/connector-opencode 0.55.0 → 0.56.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/plugin.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { MeshAgent } from "@cotal-ai/connector-core";
1
2
  import type { Plugin } from "@opencode-ai/plugin";
2
3
  /** The machine-stable half of the retirement line. The suite asserts on THIS, never on the sentence
3
4
  * around it: a guard keyed on human prose dies the first time someone rewords a log message, and it
@@ -13,6 +14,28 @@ export declare const WAL_REAPED = "opencode-wal-reaped";
13
14
  * This is the token the reaping cell grades, because the lifetime is the claim and the deletion is
14
15
  * only the easy half of it. */
15
16
  export declare const WAL_KEPT = "opencode-wal-kept";
17
+ /** The shared shape of a pre-join model refusal: `"<model>: <detail>. Refusing before join"`.
18
+ * Exported so the 2.x adapter's own check (a different route, `/api/model`) produces the same
19
+ * sentence shape as `verifyServerModel` below without duplicating it. */
20
+ export declare function modelRefusalSentence(selectedModel: string, detail: string): string;
16
21
  export declare function verifyServerModel(serverUrl: string, serverAuth: string, selectedModel: string): Promise<void>;
22
+ /**
23
+ * Races `work` against a `ms` timeout so a stuck settle is abandoned out loud rather than waited on
24
+ * forever (see the note at its call sites in `quiesce`). Exported: the 2.x adapter's own teardown
25
+ * uses the same bounded-settle shape rather than a second copy of it.
26
+ */
27
+ declare function settleWithin(work: Promise<unknown> | undefined, ms: number, what: string): Promise<boolean>;
28
+ export { settleWithin };
29
+ /**
30
+ * The buffered-inbox + pending-run-turn composition that goes out ahead of a native prompt. Shared
31
+ * by the 1.x text-part mutator (`injectIntoPrompt` below) and the 2.x `session.hook("prompt")`
32
+ * mutator, so the two callers cannot drift on what "inject" means. Returns `undefined` when there is
33
+ * nothing to prepend.
34
+ */
35
+ export declare function computeInjection(agent: MeshAgent): {
36
+ prefix: string;
37
+ surfacedIds: string[];
38
+ turnIds: string[];
39
+ } | undefined;
17
40
  export declare const cotal: Plugin;
18
41
  //# sourceMappingURL=plugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAoEA,OAAO,KAAK,EAAE,MAAM,EAAyB,MAAM,qBAAqB,CAAC;AAgBzE;;;0EAG0E;AAC1E,eAAO,MAAM,eAAe,6BAA6B,CAAC;AAC1D,gGAAgG;AAChG,eAAO,MAAM,gBAAgB,8BAA8B,CAAC;AAC5D;iEACiE;AACjE,eAAO,MAAM,UAAU,wBAAwB,CAAC;AAChD;;gCAEgC;AAChC,eAAO,MAAM,QAAQ,sBAAsB,CAAC;AAC5C,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYnH;AA8BD,eAAO,MAAM,KAAK,EAAE,MA0yCnB,CAAC"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAuCA,OAAO,EAGL,SAAS,EAoBV,MAAM,0BAA0B,CAAC;AAMlC,OAAO,KAAK,EAAE,MAAM,EAAyB,MAAM,qBAAqB,CAAC;AAgBzE;;;0EAG0E;AAC1E,eAAO,MAAM,eAAe,6BAA6B,CAAC;AAC1D,gGAAgG;AAChG,eAAO,MAAM,gBAAgB,8BAA8B,CAAC;AAC5D;iEACiE;AACjE,eAAO,MAAM,UAAU,wBAAwB,CAAC;AAChD;;gCAEgC;AAChC,eAAO,MAAM,QAAQ,sBAAsB,CAAC;AAC5C;;0EAE0E;AAC1E,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAElF;AACD,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYnH;AA8BD;;;;GAIG;AACH,iBAAe,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAqB1G;AACD,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,GAAG;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,GAAG,SAAS,CAQZ;AAED,eAAO,MAAM,KAAK,EAAE,MAixCnB,CAAC"}
@@ -1,6 +1,8 @@
1
- /** The plugin *bundle* entry (esbuild → `dist/plugin.bundle.js`). OpenCode's loader treats every
2
- * export of a plugin module as a plugin factory and calls it, so the bundle must export exactly
3
- * one symbol: the plugin. The log-marker constants stay exported from `./plugin.ts` for the
4
- * smokes, which import the source — they must never reach this surface. */
5
- export { cotal } from "./plugin.js";
1
+ import { setupCotal } from "./plugin2.js";
2
+ declare const _default: {
3
+ id: string;
4
+ server: import("@opencode-ai/plugin").Plugin;
5
+ setup: typeof setupCotal;
6
+ };
7
+ export default _default;
6
8
  //# sourceMappingURL=plugin.entry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.entry.d.ts","sourceRoot":"","sources":["../src/plugin.entry.ts"],"names":[],"mappings":"AAAA;;;4EAG4E;AAC5E,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"plugin.entry.d.ts","sourceRoot":"","sources":["../src/plugin.entry.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;;;;;;AAC1C,wBAAiE"}
@@ -0,0 +1,12 @@
1
+ import type { OpenCode2Context } from "./opencode2-types.js";
2
+ /** 2.x model check: `GET /api/model` lists the configured model directly (no `/provider.all`
3
+ * nesting to walk, unlike 1.x's `verifyServerModel`). Same refusal sentence shape, different
4
+ * route and body — measured in fx105/measurements.md (routes). The server answers `/api/model`
5
+ * with an empty listing for up to about a second after the location boot its first `/api/config`
6
+ * triggers (fx105/brief-g.md measurement), so an empty listing is polled every 200 ms until the
7
+ * provider appears rather than refused on the first read. The wait is bounded: a server that
8
+ * never lists anything within the deadline is a real refusal, not a hang. */
9
+ export declare const MODEL_LIST_SETTLE_MS = 10000;
10
+ /** The 2.x `setup(context)` entry point. */
11
+ export declare function setupCotal(ctx: OpenCode2Context): Promise<(() => Promise<void>) | void>;
12
+ //# sourceMappingURL=plugin2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin2.d.ts","sourceRoot":"","sources":["../src/plugin2.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAiB7D;;;;;;8EAM8E;AAC9E,eAAO,MAAM,oBAAoB,QAAS,CAAC;AAoC3C,4CAA4C;AAC5C,wBAAsB,UAAU,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAmR7F"}
package/dist/serve.js CHANGED
@@ -5,6 +5,18 @@ import { once } from "node:events";
5
5
  import { randomBytes } from "node:crypto";
6
6
  import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
7
7
  import { delimiter, join } from "node:path";
8
+
9
+ // src/opencode-line.ts
10
+ function opencodeLine(raw, bin) {
11
+ const m = raw.match(/(?:^|v)(\d+)\.\d+/);
12
+ const major = m ? Number(m[1]) : NaN;
13
+ if (major === 1 || major === 2) return major;
14
+ throw new Error(
15
+ `opencode connector: unsupported OpenCode version "${raw}" from ${bin}; this connector supports the 1.x (opencode-ai) and 2.x (@opencode/cli) lines`
16
+ );
17
+ }
18
+
19
+ // src/serve.ts
8
20
  function resolveOpencodeBin() {
9
21
  const override = process.env.COTAL_OPENCODE_BIN?.trim() || process.env.COTAL_OPENCODE_RESOLVED_BIN?.trim();
10
22
  if (override) return override;
@@ -84,11 +96,14 @@ async function main() {
84
96
  throw new Error(`agent "${name}" is already running (opencode serve pid ${pid}) \u2014 kill it first`);
85
97
  rmSync(pidFile);
86
98
  }
99
+ const versionRaw = execFileSync(BIN, ["--version"], { encoding: "utf8" }).trim();
100
+ const line = opencodeLine(versionRaw, BIN);
87
101
  mkdirSync(agentHome, { recursive: true });
88
102
  const serve = spawn(BIN, ["serve", "--hostname", "127.0.0.1", "--port", port], {
89
103
  env: {
90
104
  ...process.env,
91
105
  COTAL_OPENCODE_SERVER_URL: url,
106
+ COTAL_OPENCODE_LINE: String(line),
92
107
  OPENCODE_SERVER_USERNAME: USERNAME,
93
108
  OPENCODE_SERVER_PASSWORD: SECRET,
94
109
  OPENCODE_DB: dbPath
@@ -116,10 +131,11 @@ async function main() {
116
131
  if (!attached) process.exit(code ?? (signal ? 1 : 0));
117
132
  });
118
133
  const auth = `Basic ${Buffer.from(`${USERNAME}:${SECRET}`).toString("base64")}`;
134
+ const pokePath = line === 2 ? "/api/config" : "/session";
119
135
  void (async () => {
120
136
  for (let i = 0; i < 300 && !sessionId; i++) {
121
137
  try {
122
- await fetch(`${url}/session`, { headers: { authorization: auth }, signal: AbortSignal.timeout(1500) });
138
+ await fetch(`${url}${pokePath}`, { headers: { authorization: auth }, signal: AbortSignal.timeout(1500) });
123
139
  } catch {
124
140
  }
125
141
  await new Promise((r) => setTimeout(r, 200));
@@ -154,10 +170,7 @@ async function main() {
154
170
  delete tuiEnv.OPENCODE_CONFIG_CONTENT;
155
171
  for (const k of Object.keys(tuiEnv)) if (k.startsWith("COTAL_")) delete tuiEnv[k];
156
172
  attached = true;
157
- const tui = spawn(BIN, ["attach", url, "--session", id, "--password", SECRET], {
158
- env: tuiEnv,
159
- stdio: "inherit"
160
- });
173
+ const tui = line === 2 ? spawn(BIN, ["--server", url, "--session", id], { env: tuiEnv, stdio: "inherit" }) : spawn(BIN, ["attach", url, "--session", id, "--password", SECRET], { env: tuiEnv, stdio: "inherit" });
161
174
  for (const sig of ["SIGINT", "SIGTERM"])
162
175
  process.on(sig, () => {
163
176
  tui.kill(sig);
package/dist/tools.d.ts CHANGED
@@ -15,6 +15,15 @@
15
15
  */
16
16
  import type { ToolDefinition } from "@opencode-ai/plugin";
17
17
  import { type MeshAgent, type AgentConfig } from "@cotal-ai/connector-core";
18
+ import type { OpenCode2ToolEditor } from "./opencode2-types.js";
18
19
  /** Build the cotal_* tool map wired to one mesh agent, rendered from the shared specs. */
19
20
  export declare function buildCotalTools(agent: MeshAgent, config: AgentConfig): Record<string, ToolDefinition>;
21
+ /** Build the cotal_* tool list for OpenCode 2.x's `ctx.tool.transform(ed => ed.add(...))`, from
22
+ * the SAME shared specs {@link buildCotalTools} renders for 1.x. 2.x's `Tool.Info.input` accepts
23
+ * a zod 4 object directly (a StandardSchema — measured, `fx105/measurements.md`), so the closed
24
+ * schema goes across unmodified; there is no 1.x-style raw-shape flattening needed here. The
25
+ * closed-object refusal at dispatch is the same guard as {@link buildCotalTools} keeps: this host
26
+ * also hands `execute` the caller's object UNVALIDATED, so a stray `owner`/`actor` is refused by
27
+ * name here too, never silently reaching `run`. */
28
+ export declare function buildCotalTools2(agent: MeshAgent, config: AgentConfig): Parameters<OpenCode2ToolEditor["add"]>[0][];
20
29
  //# sourceMappingURL=tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAgD,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE1H,0FAA0F;AAC1F,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA2CrG"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAgD,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC1H,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,0FAA0F;AAC1F,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA2CrG;AAED;;;;;;oDAMoD;AACpD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,GAAG,UAAU,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAmCnH"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cotal-ai/connector-opencode",
3
3
  "description": "Cotal connector for OpenCode: a native in-process plugin that joins a session to the mesh.",
4
- "version": "0.55.0",
4
+ "version": "0.56.0",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
@@ -21,11 +21,12 @@
21
21
  "@cotal-ai/core": ">=0.1.0"
22
22
  },
23
23
  "devDependencies": {
24
+ "@nats-io/transport-node": "^3.4.0",
24
25
  "@opencode-ai/plugin": "^1.16.2",
25
26
  "esbuild": "^0.28.0",
26
27
  "tsx": "^4.22.4",
27
- "@cotal-ai/connector-core": "0.55.0",
28
- "@cotal-ai/core": "0.55.0",
28
+ "@cotal-ai/connector-core": "0.56.0",
29
+ "@cotal-ai/core": "0.56.0",
29
30
  "@cotal-ai/smoke-kit": "0.0.0"
30
31
  },
31
32
  "files": [
@@ -36,8 +37,8 @@
36
37
  },
37
38
  "scripts": {
38
39
  "test": "tsx smoke/tool-input-closed.smoke.ts",
39
- "typecheck": "tsc -p tsconfig.json --noEmit",
40
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.smoke.json",
40
41
  "build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json --emitDeclarationOnly && pnpm run bundle",
41
- "bundle": "esbuild src/index.ts --bundle --platform=node --format=esm --target=node20 --outfile=dist/index.js --external:@cotal-ai/core && esbuild src/plugin.entry.ts --bundle --platform=node --format=esm --target=node20 --outfile=dist/plugin.bundle.js && esbuild src/serve.ts --bundle --platform=node --format=esm --target=node20 --outfile=dist/serve.js"
42
+ "bundle": "esbuild src/index.ts --bundle --platform=node --format=esm --target=node20 --outfile=dist/index.js --external:@cotal-ai/core && esbuild src/plugin.entry.ts --bundle --platform=node --format=esm --target=node20 --outfile=dist/plugin/index.js && esbuild src/serve.ts --bundle --platform=node --format=esm --target=node20 --outfile=dist/serve.js"
42
43
  }
43
44
  }