@cotal-ai/connector-opencode 0.8.1 → 0.8.3
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/extension.d.ts +2 -2
- package/dist/extension.d.ts.map +1 -1
- package/dist/extension.js +24 -5
- package/dist/extension.js.map +1 -1
- package/dist/plugin.bundle.js +295 -11
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +169 -20
- package/dist/plugin.js.map +1 -1
- package/dist/serve.js +45 -5
- package/dist/serve.js.map +1 -1
- package/dist/transcript.d.ts +42 -0
- package/dist/transcript.d.ts.map +1 -0
- package/dist/transcript.js +107 -0
- package/dist/transcript.js.map +1 -0
- package/package.json +3 -3
package/dist/extension.d.ts
CHANGED
|
@@ -4,8 +4,8 @@ import { type Connector } from "@cotal-ai/core";
|
|
|
4
4
|
* OpenCode's client/server split (see serve.ts). The Cotal mesh bridge runs as an in-process plugin
|
|
5
5
|
* inside a headless `opencode serve`: it holds the {@link MeshAgent}, registers the cotal_* tools
|
|
6
6
|
* natively (from the shared specs, at parity with Claude Code), reports presence off the event bus,
|
|
7
|
-
* and owns ONE session it drives — injecting each incoming peer batch
|
|
8
|
-
*
|
|
7
|
+
* and owns ONE session it drives — injecting each incoming peer batch through the authenticated
|
|
8
|
+
* OpenCode server API on the same serve process the TUI attaches to. The shim then attaches a
|
|
9
9
|
* foreground TUI to that session, so a human watching sees the agent work and can type into it.
|
|
10
10
|
*
|
|
11
11
|
* Config rides in `OPENCODE_CONFIG_CONTENT` (inline JSON, the highest merge layer), so the
|
package/dist/extension.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AAEA,OAAO,EAA2B,KAAK,SAAS,EAAoC,MAAM,gBAAgB,CAAC;AAa3G;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AAEA,OAAO,EAA2B,KAAK,SAAS,EAAoC,MAAM,gBAAgB,CAAC;AAa3G;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAqG/B,CAAC"}
|
package/dist/extension.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { fileURLToPath } from "node:url";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { loadAgentFile, registry } from "@cotal-ai/core";
|
|
4
|
-
import { aclEnv, launchEnv, MODEL_PROVIDER_KEYS } from "@cotal-ai/connector-core";
|
|
4
|
+
import { aclEnv, launchEnv, controlEndpoint, MODEL_PROVIDER_KEYS, transcriptChannel } from "@cotal-ai/connector-core";
|
|
5
5
|
/** The bundled in-process plugin (esbuild → `dist/plugin.bundle.js`). `opencode serve` loads it by
|
|
6
6
|
* absolute path from the inline config, so it runs *inside* the server and shares its SDK client.
|
|
7
7
|
* Resolved relative to this module — beside the built `dist/extension.js`, so the connector must be
|
|
@@ -15,8 +15,8 @@ const SERVE_SHIM = fileURLToPath(new URL("./serve.js", import.meta.url));
|
|
|
15
15
|
* OpenCode's client/server split (see serve.ts). The Cotal mesh bridge runs as an in-process plugin
|
|
16
16
|
* inside a headless `opencode serve`: it holds the {@link MeshAgent}, registers the cotal_* tools
|
|
17
17
|
* natively (from the shared specs, at parity with Claude Code), reports presence off the event bus,
|
|
18
|
-
* and owns ONE session it drives — injecting each incoming peer batch
|
|
19
|
-
*
|
|
18
|
+
* and owns ONE session it drives — injecting each incoming peer batch through the authenticated
|
|
19
|
+
* OpenCode server API on the same serve process the TUI attaches to. The shim then attaches a
|
|
20
20
|
* foreground TUI to that session, so a human watching sees the agent work and can type into it.
|
|
21
21
|
*
|
|
22
22
|
* Config rides in `OPENCODE_CONFIG_CONTENT` (inline JSON, the highest merge layer), so the
|
|
@@ -27,6 +27,7 @@ const SERVE_SHIM = fileURLToPath(new URL("./serve.js", import.meta.url));
|
|
|
27
27
|
export const opencodeConnector = {
|
|
28
28
|
kind: "connector",
|
|
29
29
|
name: "opencode",
|
|
30
|
+
transcriptChannel, // the shared `tr-<name>` convention (connector-core), exposed via the contract
|
|
30
31
|
requires: ["opencode"],
|
|
31
32
|
buildLaunch(opts) {
|
|
32
33
|
// Tool-sharing isn't wired for opencode: its OPENCODE_CONFIG_CONTENT is a merge layer, so an
|
|
@@ -57,6 +58,8 @@ export const opencodeConnector = {
|
|
|
57
58
|
env.COTAL_CREDS = opts.creds;
|
|
58
59
|
if (opts.servers)
|
|
59
60
|
env.COTAL_SERVERS = opts.servers;
|
|
61
|
+
if (opts.transcript === true)
|
|
62
|
+
env.COTAL_TRANSCRIPT = "1"; // gate the plugin's transcript mirror (parity with Claude)
|
|
60
63
|
// Where serve.ts roots this agent's SQLite DB + serve pidfile. Pin it to the manager's
|
|
61
64
|
// workspace root so a per-agent launch cwd (which the manager can point at any repo) doesn't
|
|
62
65
|
// drop `.cotal/opencode/<name>` into the target tree. Standalone `cotal spawn` has no manager
|
|
@@ -93,15 +96,31 @@ export const opencodeConnector = {
|
|
|
93
96
|
if (face)
|
|
94
97
|
env.COTAL_FACE_PERSONA = face; // shim swaps the TUI for the face viewer
|
|
95
98
|
}
|
|
96
|
-
// The `--model` flag wins over the agent file, and applies even with no agent file.
|
|
97
|
-
|
|
99
|
+
// The `--model` flag wins over the agent file, and applies even with no agent file. Pin it to a
|
|
100
|
+
// dedicated primary agent made the default, so an operator's own `default_agent` in
|
|
101
|
+
// ~/.config/opencode (with its own model) can't override the model a Cotal spawn asks for — the
|
|
102
|
+
// session the plugin drives runs the persona's model, not the operator's default agent's.
|
|
103
|
+
if (model) {
|
|
98
104
|
config.model = model;
|
|
105
|
+
config.agent = { cotal: { mode: "primary", model } };
|
|
106
|
+
config.default_agent = "cotal";
|
|
107
|
+
}
|
|
99
108
|
env.OPENCODE_CONFIG_CONTENT = JSON.stringify(config);
|
|
109
|
+
// Local control endpoint: the manager sends a cooperative {op:"shutdown"} here on a signal-less
|
|
110
|
+
// runtime (ConPTY/Windows), where a hard kill skips cleanup and the agent lingers until its
|
|
111
|
+
// presence TTL expires. The plugin (in the opencode server process) starts the control server and
|
|
112
|
+
// leaves the mesh cleanly on shutdown. Minted here; passed to the plugin in the child env (the
|
|
113
|
+
// token never on argv/logs) — opencode serve inherits this process env, the attached TUI strips
|
|
114
|
+
// COTAL_*. Returned in the LaunchSpec so the manager holds it in memory to drive the stop.
|
|
115
|
+
const control = controlEndpoint(opts.space, opts.name);
|
|
116
|
+
env.COTAL_CONTROL_SOCKET = control.path;
|
|
117
|
+
env.COTAL_CONTROL_TOKEN = control.token;
|
|
100
118
|
// Run the shim (node dist/serve.js): `opencode serve` + an attached foreground TUI.
|
|
101
119
|
return {
|
|
102
120
|
command: process.execPath,
|
|
103
121
|
args: [SERVE_SHIM],
|
|
104
122
|
env,
|
|
123
|
+
control,
|
|
105
124
|
};
|
|
106
125
|
},
|
|
107
126
|
};
|
package/dist/extension.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAoD,MAAM,gBAAgB,CAAC;AAC3G,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"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"}
|
package/dist/plugin.bundle.js
CHANGED
|
@@ -2993,7 +2993,7 @@ var require_nkeys = __commonJS({
|
|
|
2993
2993
|
exports.createAccount = createAccount2;
|
|
2994
2994
|
exports.createUser = createUser2;
|
|
2995
2995
|
exports.createCluster = createCluster;
|
|
2996
|
-
exports.createServer =
|
|
2996
|
+
exports.createServer = createServer2;
|
|
2997
2997
|
exports.createCurve = createCurve;
|
|
2998
2998
|
exports.fromPublic = fromPublic2;
|
|
2999
2999
|
exports.fromCurveSeed = fromCurveSeed;
|
|
@@ -3021,7 +3021,7 @@ var require_nkeys = __commonJS({
|
|
|
3021
3021
|
function createCluster() {
|
|
3022
3022
|
return createPair(Prefix2.Cluster);
|
|
3023
3023
|
}
|
|
3024
|
-
function
|
|
3024
|
+
function createServer2() {
|
|
3025
3025
|
return createPair(Prefix2.Server);
|
|
3026
3026
|
}
|
|
3027
3027
|
function createCurve() {
|
|
@@ -14944,6 +14944,8 @@ function assertValidName(name) {
|
|
|
14944
14944
|
throw new Error(`invalid name ${JSON.stringify(name)}: must be a single line`);
|
|
14945
14945
|
if (name.includes("/"))
|
|
14946
14946
|
throw new Error(`invalid name ${JSON.stringify(name)}: "/" is reserved (the owner/name separator)`);
|
|
14947
|
+
if (name.includes("\\"))
|
|
14948
|
+
throw new Error(`invalid name ${JSON.stringify(name)}: "\\" is reserved (a Windows path separator)`);
|
|
14947
14949
|
}
|
|
14948
14950
|
|
|
14949
14951
|
// ../../packages/core/dist/link.js
|
|
@@ -17038,6 +17040,9 @@ function loadAgentFile(path) {
|
|
|
17038
17040
|
};
|
|
17039
17041
|
}
|
|
17040
17042
|
|
|
17043
|
+
// ../../packages/core/dist/secret-fs.js
|
|
17044
|
+
var isWin = process.platform === "win32";
|
|
17045
|
+
|
|
17041
17046
|
// ../../packages/core/dist/endpoint.js
|
|
17042
17047
|
import { EventEmitter } from "node:events";
|
|
17043
17048
|
import { randomUUID } from "node:crypto";
|
|
@@ -19321,7 +19326,7 @@ function configFromEnv(env = process.env) {
|
|
|
19321
19326
|
description: def?.description,
|
|
19322
19327
|
tags: def?.tags,
|
|
19323
19328
|
meta: def?.meta,
|
|
19324
|
-
capabilities: def?.capabilities,
|
|
19329
|
+
capabilities: splitList(env.COTAL_CAPABILITIES).length ? splitList(env.COTAL_CAPABILITIES) : def?.capabilities,
|
|
19325
19330
|
model: env.COTAL_MODEL?.trim() || def?.model || void 0,
|
|
19326
19331
|
servers: env.COTAL_SERVERS?.trim() || link?.servers || DEFAULT_SERVER,
|
|
19327
19332
|
subscribe: resolvedSubscribe,
|
|
@@ -19889,6 +19894,11 @@ ${lines.join("\n")}`;
|
|
|
19889
19894
|
}
|
|
19890
19895
|
};
|
|
19891
19896
|
|
|
19897
|
+
// ../connector-core/dist/launch.js
|
|
19898
|
+
function transcriptChannel(name) {
|
|
19899
|
+
return `tr-${name.toLowerCase().replace(/[^a-z0-9_-]+/g, "-")}`;
|
|
19900
|
+
}
|
|
19901
|
+
|
|
19892
19902
|
// ../connector-core/dist/tool-specs.js
|
|
19893
19903
|
import { execFileSync } from "node:child_process";
|
|
19894
19904
|
|
|
@@ -34914,6 +34924,16 @@ ${info}${caught}`);
|
|
|
34914
34924
|
}
|
|
34915
34925
|
|
|
34916
34926
|
// ../connector-core/dist/control.js
|
|
34927
|
+
import { createServer } from "node:net";
|
|
34928
|
+
import { existsSync, unlinkSync } from "node:fs";
|
|
34929
|
+
import { createHash, timingSafeEqual } from "node:crypto";
|
|
34930
|
+
var MAX_FRAME_BYTES = 1 << 20;
|
|
34931
|
+
var AUTH_DEADLINE_MS = 5e3;
|
|
34932
|
+
function tokenMatches(presented, digest) {
|
|
34933
|
+
if (typeof presented !== "string")
|
|
34934
|
+
return false;
|
|
34935
|
+
return timingSafeEqual(createHash("sha256").update(presented).digest(), digest);
|
|
34936
|
+
}
|
|
34917
34937
|
function who(i) {
|
|
34918
34938
|
return i.fromRole ? `${i.fromName}/${i.fromRole}` : i.fromName;
|
|
34919
34939
|
}
|
|
@@ -34934,6 +34954,76 @@ function formatInjection(items) {
|
|
|
34934
34954
|
${items.map(fmtItem2).join("\n")}
|
|
34935
34955
|
${tail}`;
|
|
34936
34956
|
}
|
|
34957
|
+
function startControlServer(agent, endpoint, handle, opts = {}) {
|
|
34958
|
+
const { path } = endpoint;
|
|
34959
|
+
const digest = createHash("sha256").update(endpoint.token).digest();
|
|
34960
|
+
if (process.platform !== "win32" && existsSync(path)) {
|
|
34961
|
+
try {
|
|
34962
|
+
unlinkSync(path);
|
|
34963
|
+
} catch {
|
|
34964
|
+
}
|
|
34965
|
+
}
|
|
34966
|
+
const server = createServer((sock) => {
|
|
34967
|
+
let buf = "";
|
|
34968
|
+
let handled = false;
|
|
34969
|
+
sock.setEncoding("utf8");
|
|
34970
|
+
const deadline = setTimeout(() => sock.destroy(), AUTH_DEADLINE_MS);
|
|
34971
|
+
deadline.unref?.();
|
|
34972
|
+
sock.on("close", () => clearTimeout(deadline));
|
|
34973
|
+
sock.on("data", async (d) => {
|
|
34974
|
+
if (handled)
|
|
34975
|
+
return;
|
|
34976
|
+
buf += d;
|
|
34977
|
+
if (buf.length > MAX_FRAME_BYTES) {
|
|
34978
|
+
sock.destroy();
|
|
34979
|
+
return;
|
|
34980
|
+
}
|
|
34981
|
+
const nl = buf.indexOf("\n");
|
|
34982
|
+
if (nl < 0)
|
|
34983
|
+
return;
|
|
34984
|
+
handled = true;
|
|
34985
|
+
clearTimeout(deadline);
|
|
34986
|
+
let frame = {};
|
|
34987
|
+
try {
|
|
34988
|
+
frame = JSON.parse(buf.slice(0, nl) || "{}");
|
|
34989
|
+
} catch {
|
|
34990
|
+
}
|
|
34991
|
+
if (!tokenMatches(frame.token, digest)) {
|
|
34992
|
+
sock.destroy();
|
|
34993
|
+
return;
|
|
34994
|
+
}
|
|
34995
|
+
if (frame.op === "shutdown") {
|
|
34996
|
+
try {
|
|
34997
|
+
sock.end(JSON.stringify({ ok: true }) + "\n");
|
|
34998
|
+
} catch {
|
|
34999
|
+
}
|
|
35000
|
+
opts.onShutdown?.();
|
|
35001
|
+
return;
|
|
35002
|
+
}
|
|
35003
|
+
const ev = frame.event ?? {};
|
|
35004
|
+
const reply = await handle(agent, ev);
|
|
35005
|
+
try {
|
|
35006
|
+
sock.end(JSON.stringify(reply) + "\n");
|
|
35007
|
+
} catch {
|
|
35008
|
+
}
|
|
35009
|
+
});
|
|
35010
|
+
sock.on("error", () => {
|
|
35011
|
+
});
|
|
35012
|
+
});
|
|
35013
|
+
let bound = false;
|
|
35014
|
+
server.on("error", (e) => {
|
|
35015
|
+
process.stderr.write(`[cotal-connector] control server error: ${e.message}
|
|
35016
|
+
`);
|
|
35017
|
+
if (opts.fatalBind && !bound)
|
|
35018
|
+
process.exit(1);
|
|
35019
|
+
});
|
|
35020
|
+
server.listen(path, () => {
|
|
35021
|
+
bound = true;
|
|
35022
|
+
process.stderr.write(`[cotal-connector] control socket: ${path}
|
|
35023
|
+
`);
|
|
35024
|
+
});
|
|
35025
|
+
return server;
|
|
35026
|
+
}
|
|
34937
35027
|
|
|
34938
35028
|
// src/tools.ts
|
|
34939
35029
|
import { tool } from "@opencode-ai/plugin";
|
|
@@ -34964,13 +35054,99 @@ function buildCotalTools(agent, config2) {
|
|
|
34964
35054
|
return tools;
|
|
34965
35055
|
}
|
|
34966
35056
|
|
|
35057
|
+
// src/transcript.ts
|
|
35058
|
+
var MAX_PREVIEW = 700;
|
|
35059
|
+
var MAX_CHUNK = 6e3;
|
|
35060
|
+
function truncate(s, max) {
|
|
35061
|
+
return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
|
|
35062
|
+
}
|
|
35063
|
+
function salient(input) {
|
|
35064
|
+
const i = input ?? {};
|
|
35065
|
+
const v = i.command ?? i.filePath ?? i.file_path ?? i.path ?? i.url ?? i.pattern ?? i.description;
|
|
35066
|
+
const s = typeof v === "string" ? v : Object.keys(i).length ? JSON.stringify(i) : "";
|
|
35067
|
+
return s ? `: ${truncate(s, 300)}` : "";
|
|
35068
|
+
}
|
|
35069
|
+
function condensePart(part) {
|
|
35070
|
+
switch (part?.type) {
|
|
35071
|
+
case "text":
|
|
35072
|
+
if (part.synthetic || part.ignored || !part.text?.trim()) return [];
|
|
35073
|
+
return [part.text.trim()];
|
|
35074
|
+
// assistant text in FULL — chunkLines splits it across messages
|
|
35075
|
+
case "tool": {
|
|
35076
|
+
const head = `\u2692 ${part.tool}${salient(part.state?.input)}`;
|
|
35077
|
+
const status = part.state?.status;
|
|
35078
|
+
if (status === "completed")
|
|
35079
|
+
return [head, `\u2192 ${truncate(String(part.state.output ?? ""), MAX_PREVIEW) || "(no output)"}`];
|
|
35080
|
+
if (status === "error") return [head, `\u2192 ERROR: ${truncate(String(part.state.error ?? ""), MAX_PREVIEW)}`];
|
|
35081
|
+
return [head];
|
|
35082
|
+
}
|
|
35083
|
+
default:
|
|
35084
|
+
return [];
|
|
35085
|
+
}
|
|
35086
|
+
}
|
|
35087
|
+
function chunkLines(lines, max) {
|
|
35088
|
+
const chunks = [];
|
|
35089
|
+
let cur = "";
|
|
35090
|
+
for (const ln of lines) {
|
|
35091
|
+
if (ln.length > max) {
|
|
35092
|
+
if (cur) {
|
|
35093
|
+
chunks.push(cur);
|
|
35094
|
+
cur = "";
|
|
35095
|
+
}
|
|
35096
|
+
for (let i = 0; i < ln.length; i += max) chunks.push(ln.slice(i, i + max));
|
|
35097
|
+
continue;
|
|
35098
|
+
}
|
|
35099
|
+
if (cur && cur.length + ln.length + 1 > max) {
|
|
35100
|
+
chunks.push(cur);
|
|
35101
|
+
cur = "";
|
|
35102
|
+
}
|
|
35103
|
+
cur = cur ? `${cur}
|
|
35104
|
+
${ln}` : ln;
|
|
35105
|
+
}
|
|
35106
|
+
if (cur) chunks.push(cur);
|
|
35107
|
+
return chunks;
|
|
35108
|
+
}
|
|
35109
|
+
function createTranscriptMirror(agent, channel) {
|
|
35110
|
+
const assistantMsgs = /* @__PURE__ */ new Set();
|
|
35111
|
+
const condensed = /* @__PURE__ */ new Map();
|
|
35112
|
+
const clear = () => {
|
|
35113
|
+
condensed.clear();
|
|
35114
|
+
assistantMsgs.clear();
|
|
35115
|
+
};
|
|
35116
|
+
return {
|
|
35117
|
+
record(message) {
|
|
35118
|
+
const info = message?.info ?? message;
|
|
35119
|
+
if (info?.role === "assistant" && typeof info.id === "string") assistantMsgs.add(info.id);
|
|
35120
|
+
},
|
|
35121
|
+
observe(part) {
|
|
35122
|
+
if (!part?.id || typeof part.messageID !== "string") return;
|
|
35123
|
+
const key = `${part.messageID}:${part.id}`;
|
|
35124
|
+
const lines = condensePart(part);
|
|
35125
|
+
if (lines.length) condensed.set(key, { messageID: part.messageID, lines });
|
|
35126
|
+
else condensed.delete(key);
|
|
35127
|
+
},
|
|
35128
|
+
async flush() {
|
|
35129
|
+
const lines = [];
|
|
35130
|
+
for (const { messageID, lines: ls } of condensed.values())
|
|
35131
|
+
if (assistantMsgs.has(messageID)) lines.push(...ls);
|
|
35132
|
+
clear();
|
|
35133
|
+
for (const chunk of chunkLines(lines, MAX_CHUNK)) await agent.send(chunk, channel);
|
|
35134
|
+
},
|
|
35135
|
+
reset() {
|
|
35136
|
+
clear();
|
|
35137
|
+
}
|
|
35138
|
+
};
|
|
35139
|
+
}
|
|
35140
|
+
|
|
34967
35141
|
// src/plugin.ts
|
|
34968
35142
|
function log(msg) {
|
|
34969
35143
|
process.stderr.write(`[cotal-connector] ${msg}
|
|
34970
35144
|
`);
|
|
34971
35145
|
}
|
|
34972
35146
|
var guard = globalThis;
|
|
34973
|
-
var
|
|
35147
|
+
var ERROR_RETRY_INITIAL_MS = 1e3;
|
|
35148
|
+
var ERROR_RETRY_MAX_MS = 3e4;
|
|
35149
|
+
var cotal = async () => {
|
|
34974
35150
|
if (!hasIdentity()) {
|
|
34975
35151
|
log("no COTAL_NAME \u2014 not a managed session; staying off the mesh");
|
|
34976
35152
|
return {};
|
|
@@ -34978,8 +35154,27 @@ var cotal = async ({ client }) => {
|
|
|
34978
35154
|
if (guard.__cotalOpencodeHooks) return guard.__cotalOpencodeHooks;
|
|
34979
35155
|
const config2 = configFromEnv();
|
|
34980
35156
|
config2.connector = "opencode";
|
|
35157
|
+
const serverUrl = process.env.COTAL_OPENCODE_SERVER_URL?.trim();
|
|
35158
|
+
const serverUsername = process.env.OPENCODE_SERVER_USERNAME?.trim() || "opencode";
|
|
35159
|
+
const serverPassword = process.env.OPENCODE_SERVER_PASSWORD?.trim();
|
|
35160
|
+
if (!serverUrl || !serverPassword) throw new Error("opencode connector: missing COTAL_OPENCODE_SERVER_URL/OPENCODE_SERVER_PASSWORD");
|
|
35161
|
+
const serverAuth = `Basic ${Buffer.from(`${serverUsername}:${serverPassword}`).toString("base64")}`;
|
|
34981
35162
|
const agent = new MeshAgent(config2);
|
|
34982
35163
|
agent.start();
|
|
35164
|
+
async function opencodeApi(path, init, timeoutMs = 1e4) {
|
|
35165
|
+
const res = await fetch(`${serverUrl}${path}`, {
|
|
35166
|
+
...init,
|
|
35167
|
+
signal: init?.signal ?? AbortSignal.timeout(timeoutMs),
|
|
35168
|
+
headers: {
|
|
35169
|
+
authorization: serverAuth,
|
|
35170
|
+
"content-type": "application/json",
|
|
35171
|
+
...init?.headers ?? {}
|
|
35172
|
+
}
|
|
35173
|
+
});
|
|
35174
|
+
if (!res.ok) throw new Error(`OpenCode HTTP ${res.status} ${res.statusText} for ${path}`);
|
|
35175
|
+
if (res.status === 204) return void 0;
|
|
35176
|
+
return await res.json();
|
|
35177
|
+
}
|
|
34983
35178
|
const def = process.env.COTAL_AGENT_FILE?.trim() ? loadAgentFile(process.env.COTAL_AGENT_FILE.trim()) : void 0;
|
|
34984
35179
|
const facePrompt = process.env.COTAL_FACE_PERSONA?.trim() ? 'You speak through an animated pixel-art avatar of yourself. Hard formatting rule: the text you pass to the cotal send tools (cotal_send / cotal_dm) must START with an emotion tag [[face:X]], where X is one of: neutral, happy, sad, angry, surprised \u2014 pick the one matching your mood, and insert another tag mid-text whenever your mood shifts. Example: "[[face:angry]] That race condition cost me three days. [[face:happy]] But watch it run now." Never mention or explain the tags \u2014 they are stripped before peers see your message.' : void 0;
|
|
34985
35180
|
const persona = [def?.persona, facePrompt].filter(Boolean).join("\n\n") || void 0;
|
|
@@ -34991,15 +35186,58 @@ var cotal = async ({ client }) => {
|
|
|
34991
35186
|
let briefed = false;
|
|
34992
35187
|
let surfaced = [];
|
|
34993
35188
|
let awaitingTurnEnd = false;
|
|
35189
|
+
let errorRetryTimer;
|
|
35190
|
+
let errorRetryMs = ERROR_RETRY_INITIAL_MS;
|
|
35191
|
+
const transcript = /^(1|true|yes|on)$/i.test(process.env.COTAL_TRANSCRIPT ?? "") ? createTranscriptMirror(agent, transcriptChannel(config2.name)) : void 0;
|
|
34994
35192
|
const safeStatus = async (status, activity) => {
|
|
34995
35193
|
try {
|
|
34996
35194
|
if (agent.connected) await agent.setStatus(status, activity);
|
|
34997
35195
|
} catch {
|
|
34998
35196
|
}
|
|
34999
35197
|
};
|
|
35198
|
+
let controlServer;
|
|
35199
|
+
const shutdown = async () => {
|
|
35200
|
+
try {
|
|
35201
|
+
controlServer?.close();
|
|
35202
|
+
} catch {
|
|
35203
|
+
}
|
|
35204
|
+
try {
|
|
35205
|
+
await safeStatus("offline");
|
|
35206
|
+
await agent.stop();
|
|
35207
|
+
} finally {
|
|
35208
|
+
process.exit(0);
|
|
35209
|
+
}
|
|
35210
|
+
};
|
|
35211
|
+
const controlPath = process.env.COTAL_CONTROL_SOCKET?.trim();
|
|
35212
|
+
const controlToken = process.env.COTAL_CONTROL_TOKEN?.trim();
|
|
35213
|
+
if (controlPath && controlToken) {
|
|
35214
|
+
const handle = async () => ({
|
|
35215
|
+
ok: false,
|
|
35216
|
+
error: "opencode runs cotal hooks in-process; only the shutdown control op is supported"
|
|
35217
|
+
});
|
|
35218
|
+
controlServer = startControlServer(agent, { path: controlPath, token: controlToken }, handle, {
|
|
35219
|
+
fatalBind: true,
|
|
35220
|
+
onShutdown: () => void shutdown()
|
|
35221
|
+
});
|
|
35222
|
+
}
|
|
35000
35223
|
function pendingForWake() {
|
|
35001
35224
|
return agent.pendingWake();
|
|
35002
35225
|
}
|
|
35226
|
+
function clearErrorRetry(resetDelay = false) {
|
|
35227
|
+
if (errorRetryTimer) clearTimeout(errorRetryTimer);
|
|
35228
|
+
errorRetryTimer = void 0;
|
|
35229
|
+
if (resetDelay) errorRetryMs = ERROR_RETRY_INITIAL_MS;
|
|
35230
|
+
}
|
|
35231
|
+
function scheduleErrorRetry() {
|
|
35232
|
+
if (errorRetryTimer || pendingForWake() === 0) return;
|
|
35233
|
+
const delay = errorRetryMs;
|
|
35234
|
+
errorRetryMs = Math.min(errorRetryMs * 2, ERROR_RETRY_MAX_MS);
|
|
35235
|
+
errorRetryTimer = setTimeout(() => {
|
|
35236
|
+
errorRetryTimer = void 0;
|
|
35237
|
+
if (!busy && pendingForWake() > 0) void drive();
|
|
35238
|
+
}, delay);
|
|
35239
|
+
errorRetryTimer.unref?.();
|
|
35240
|
+
}
|
|
35003
35241
|
function adoptSession(id, reason) {
|
|
35004
35242
|
if (sessionID === id) return;
|
|
35005
35243
|
const previous = sessionID;
|
|
@@ -35011,6 +35249,8 @@ var cotal = async ({ client }) => {
|
|
|
35011
35249
|
briefed = false;
|
|
35012
35250
|
surfaced = [];
|
|
35013
35251
|
awaitingTurnEnd = false;
|
|
35252
|
+
clearErrorRetry(true);
|
|
35253
|
+
transcript?.reset();
|
|
35014
35254
|
if (previous) {
|
|
35015
35255
|
log(`adopted opencode session ${id} after ${reason}; mesh identity unchanged`);
|
|
35016
35256
|
if (pendingForWake() > 0) void drive();
|
|
@@ -35018,8 +35258,11 @@ var cotal = async ({ client }) => {
|
|
|
35018
35258
|
}
|
|
35019
35259
|
const sessionReady = (async () => {
|
|
35020
35260
|
try {
|
|
35021
|
-
const res = await
|
|
35022
|
-
|
|
35261
|
+
const res = await opencodeApi("/session", {
|
|
35262
|
+
method: "POST",
|
|
35263
|
+
body: JSON.stringify({ title: `cotal:${config2.space}:${config2.name}` })
|
|
35264
|
+
}, 1e4);
|
|
35265
|
+
const id = res.id;
|
|
35023
35266
|
if (id) {
|
|
35024
35267
|
adoptSession(id, "boot");
|
|
35025
35268
|
process.stderr.write(`[cotal-session] ${id}
|
|
@@ -35064,13 +35307,14 @@ ${ORIENTATION_BOOTSTRAP}`;
|
|
|
35064
35307
|
busy = true;
|
|
35065
35308
|
surfaced = ids;
|
|
35066
35309
|
awaitingTurnEnd = true;
|
|
35067
|
-
await
|
|
35310
|
+
await opencodeApi(`/session/${encodeURIComponent(id)}/prompt_async`, { method: "POST", body: JSON.stringify(body) }, 1e4);
|
|
35068
35311
|
primed = true;
|
|
35069
35312
|
} catch (e) {
|
|
35070
35313
|
busy = false;
|
|
35071
35314
|
surfaced = [];
|
|
35072
35315
|
awaitingTurnEnd = false;
|
|
35073
35316
|
log(`drive failed: ${e.message}`);
|
|
35317
|
+
scheduleErrorRetry();
|
|
35074
35318
|
} finally {
|
|
35075
35319
|
driving = false;
|
|
35076
35320
|
}
|
|
@@ -35083,6 +35327,26 @@ ${ORIENTATION_BOOTSTRAP}`;
|
|
|
35083
35327
|
if (n > 0) agent.drainInbox(n);
|
|
35084
35328
|
surfaced = [];
|
|
35085
35329
|
}
|
|
35330
|
+
function abandonSurfaced() {
|
|
35331
|
+
surfaced = [];
|
|
35332
|
+
}
|
|
35333
|
+
function injectIntoPrompt(output) {
|
|
35334
|
+
if (driving || awaitingTurnEnd) return;
|
|
35335
|
+
const items = agent.peekInbox();
|
|
35336
|
+
if (items.length === 0) return;
|
|
35337
|
+
const inj = formatInjection(items);
|
|
35338
|
+
if (!inj) return;
|
|
35339
|
+
const textPart = output.parts?.find(
|
|
35340
|
+
(p) => typeof p === "object" && p !== null && p.type === "text" && typeof p.text === "string"
|
|
35341
|
+
);
|
|
35342
|
+
if (!textPart) return;
|
|
35343
|
+
textPart.text = `${inj}
|
|
35344
|
+
|
|
35345
|
+
${textPart.text}`;
|
|
35346
|
+
surfaced = items.map((i) => i.id);
|
|
35347
|
+
awaitingTurnEnd = true;
|
|
35348
|
+
busy = true;
|
|
35349
|
+
}
|
|
35086
35350
|
function completeTurn() {
|
|
35087
35351
|
if (!busy && !awaitingTurnEnd) return;
|
|
35088
35352
|
busy = false;
|
|
@@ -35090,6 +35354,7 @@ ${ORIENTATION_BOOTSTRAP}`;
|
|
|
35090
35354
|
awaitingTurnEnd = false;
|
|
35091
35355
|
ackSurfaced();
|
|
35092
35356
|
}
|
|
35357
|
+
clearErrorRetry(true);
|
|
35093
35358
|
if (pendingForWake() > 0) void drive();
|
|
35094
35359
|
}
|
|
35095
35360
|
agent.on("incoming", (item) => {
|
|
@@ -35111,6 +35376,10 @@ ${ORIENTATION_BOOTSTRAP}`;
|
|
|
35111
35376
|
};
|
|
35112
35377
|
const hooks = {
|
|
35113
35378
|
tool: buildCotalTools(agent, config2),
|
|
35379
|
+
"chat.message": async (input, output) => {
|
|
35380
|
+
if (!ours(input.sessionID)) return;
|
|
35381
|
+
injectIntoPrompt(output);
|
|
35382
|
+
},
|
|
35114
35383
|
event: async ({ event }) => {
|
|
35115
35384
|
if (event.type === "permission.asked") {
|
|
35116
35385
|
const p = event.properties;
|
|
@@ -35121,11 +35390,21 @@ ${ORIENTATION_BOOTSTRAP}`;
|
|
|
35121
35390
|
case "session.created":
|
|
35122
35391
|
if (!event.properties.info.parentID) adoptSession(event.properties.info.id, "top-level session create");
|
|
35123
35392
|
break;
|
|
35124
|
-
case "
|
|
35125
|
-
if (
|
|
35393
|
+
case "message.updated":
|
|
35394
|
+
if (transcript && ours(event.properties.info.sessionID)) transcript.record(event.properties.info);
|
|
35395
|
+
break;
|
|
35396
|
+
case "message.part.updated":
|
|
35397
|
+
if (transcript && ours(event.properties.part.sessionID)) transcript.observe(event.properties.part);
|
|
35398
|
+
break;
|
|
35399
|
+
case "session.idle": {
|
|
35400
|
+
const idleSession = event.properties.sessionID;
|
|
35401
|
+
if (!ours(idleSession)) return;
|
|
35126
35402
|
await safeStatus("idle");
|
|
35403
|
+
if (transcript)
|
|
35404
|
+
await transcript.flush().catch((e) => log(`transcript mirror publish failed: ${e.message}`));
|
|
35127
35405
|
completeTurn();
|
|
35128
35406
|
break;
|
|
35407
|
+
}
|
|
35129
35408
|
case "session.status": {
|
|
35130
35409
|
if (!ours(event.properties.sessionID)) return;
|
|
35131
35410
|
const s = event.properties.status;
|
|
@@ -35145,10 +35424,10 @@ ${ORIENTATION_BOOTSTRAP}`;
|
|
|
35145
35424
|
busy = false;
|
|
35146
35425
|
if (awaitingTurnEnd) {
|
|
35147
35426
|
awaitingTurnEnd = false;
|
|
35148
|
-
|
|
35427
|
+
abandonSurfaced();
|
|
35149
35428
|
}
|
|
35150
35429
|
await safeStatus("idle");
|
|
35151
|
-
|
|
35430
|
+
scheduleErrorRetry();
|
|
35152
35431
|
break;
|
|
35153
35432
|
case "session.deleted":
|
|
35154
35433
|
if (!ours(event.properties.info.id)) return;
|
|
@@ -35162,7 +35441,12 @@ ${ORIENTATION_BOOTSTRAP}`;
|
|
|
35162
35441
|
await safeStatus("working", input.tool);
|
|
35163
35442
|
},
|
|
35164
35443
|
dispose: async () => {
|
|
35444
|
+
try {
|
|
35445
|
+
controlServer?.close();
|
|
35446
|
+
} catch {
|
|
35447
|
+
}
|
|
35165
35448
|
await safeStatus("offline");
|
|
35449
|
+
clearErrorRetry(true);
|
|
35166
35450
|
await agent.stop();
|
|
35167
35451
|
}
|
|
35168
35452
|
};
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,MAAM,EAAS,MAAM,qBAAqB,CAAC;AAgBzD,eAAO,MAAM,KAAK,EAAE,MAqanB,CAAC"}
|
package/dist/plugin.js
CHANGED
|
@@ -8,19 +8,19 @@
|
|
|
8
8
|
* • registers the cotal_* tools natively, rendered from the SHARED {@link cotalToolSpecs}
|
|
9
9
|
* (`./tools.ts`) — same surface as Claude Code, incl. channels / join / leave / channel_info;
|
|
10
10
|
* • maps OpenCode bus events to presence (idle | working | waiting | offline);
|
|
11
|
-
* • owns ONE session (created at boot) and drives it: it injects each inbox batch as a turn
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* behavior), then drives.
|
|
11
|
+
* • owns ONE session (created at boot) and drives it: it injects each inbox batch as a turn through
|
|
12
|
+
* the authenticated OpenCode server HTTP API (the same server the TUI is attached to), acking ON
|
|
13
|
+
* TURN COMPLETION (so a crash/error redelivers). Pending peer messages are also injected into
|
|
14
|
+
* the next native prompt creation when a human/API prompt starts in the attached session.
|
|
15
|
+
* Delivery is **attention-aware** (open/dnd/focus) and never interrupts a running turn.
|
|
17
16
|
*
|
|
18
17
|
* Identity comes from COTAL_* env (the plugin runs in the opencode process and inherits it).
|
|
19
18
|
* No identity → inert, so an operator's own `opencode` never joins as a stray peer.
|
|
20
19
|
*/
|
|
21
20
|
import { loadAgentFile } from "@cotal-ai/core";
|
|
22
|
-
import { configFromEnv, hasIdentity, MeshAgent, formatInjection, fmtFrom, ORIENTATION_BOOTSTRAP, } from "@cotal-ai/connector-core";
|
|
21
|
+
import { configFromEnv, hasIdentity, MeshAgent, startControlServer, formatInjection, fmtFrom, ORIENTATION_BOOTSTRAP, transcriptChannel, } from "@cotal-ai/connector-core";
|
|
23
22
|
import { buildCotalTools } from "./tools.js";
|
|
23
|
+
import { createTranscriptMirror } from "./transcript.js";
|
|
24
24
|
function log(msg) {
|
|
25
25
|
process.stderr.write(`[cotal-connector] ${msg}\n`);
|
|
26
26
|
}
|
|
@@ -29,7 +29,9 @@ function log(msg) {
|
|
|
29
29
|
* call wires up the agent, and every call returns the *same* hooks (the same tools, bound to that
|
|
30
30
|
* one agent), whichever scope opencode ends up using. */
|
|
31
31
|
const guard = globalThis;
|
|
32
|
-
|
|
32
|
+
const ERROR_RETRY_INITIAL_MS = 1_000;
|
|
33
|
+
const ERROR_RETRY_MAX_MS = 30_000;
|
|
34
|
+
export const cotal = async () => {
|
|
33
35
|
// No identity → a plain `opencode`, not a launcher-spawned agent. Stay inert.
|
|
34
36
|
if (!hasIdentity()) {
|
|
35
37
|
log("no COTAL_NAME — not a managed session; staying off the mesh");
|
|
@@ -39,8 +41,30 @@ export const cotal = async ({ client }) => {
|
|
|
39
41
|
return guard.__cotalOpencodeHooks; // one agent; reuse the hooks
|
|
40
42
|
const config = configFromEnv();
|
|
41
43
|
config.connector = "opencode"; // advertise the host harness on our AgentCard (meta.connector)
|
|
44
|
+
const serverUrl = process.env.COTAL_OPENCODE_SERVER_URL?.trim();
|
|
45
|
+
const serverUsername = process.env.OPENCODE_SERVER_USERNAME?.trim() || "opencode";
|
|
46
|
+
const serverPassword = process.env.OPENCODE_SERVER_PASSWORD?.trim();
|
|
47
|
+
if (!serverUrl || !serverPassword)
|
|
48
|
+
throw new Error("opencode connector: missing COTAL_OPENCODE_SERVER_URL/OPENCODE_SERVER_PASSWORD");
|
|
49
|
+
const serverAuth = `Basic ${Buffer.from(`${serverUsername}:${serverPassword}`).toString("base64")}`;
|
|
42
50
|
const agent = new MeshAgent(config);
|
|
43
51
|
agent.start(); // background connect with retry — never blocks startup
|
|
52
|
+
async function opencodeApi(path, init, timeoutMs = 10_000) {
|
|
53
|
+
const res = await fetch(`${serverUrl}${path}`, {
|
|
54
|
+
...init,
|
|
55
|
+
signal: init?.signal ?? AbortSignal.timeout(timeoutMs),
|
|
56
|
+
headers: {
|
|
57
|
+
authorization: serverAuth,
|
|
58
|
+
"content-type": "application/json",
|
|
59
|
+
...(init?.headers ?? {}),
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
if (!res.ok)
|
|
63
|
+
throw new Error(`OpenCode HTTP ${res.status} ${res.statusText} for ${path}`);
|
|
64
|
+
if (res.status === 204)
|
|
65
|
+
return undefined;
|
|
66
|
+
return (await res.json());
|
|
67
|
+
}
|
|
44
68
|
const def = process.env.COTAL_AGENT_FILE?.trim() ? loadAgentFile(process.env.COTAL_AGENT_FILE.trim()) : undefined;
|
|
45
69
|
// Face-hosted (`face:` in the agent file → the shim attaches the face viewer): teach the agent
|
|
46
70
|
// to steer its avatar's expression with inline [[face:X]] tags in the text it passes to the
|
|
@@ -67,11 +91,21 @@ export const cotal = async ({ client }) => {
|
|
|
67
91
|
let sessionID;
|
|
68
92
|
let busy = false; // a turn is running (ours via drive(), OR the human's via session.status) → don't
|
|
69
93
|
// prompt: opencode would COALESCE onto it (no reject). Released at EVERY turn end (completeTurn).
|
|
70
|
-
let driving = false; // re-entrancy guard around an in-flight
|
|
94
|
+
let driving = false; // re-entrancy guard around an in-flight server prompt
|
|
71
95
|
let primed = false; // persona is prepended to the first turn's text once
|
|
72
96
|
let briefed = false; // the boot channel briefing is prepended once, on the first turn
|
|
73
97
|
let surfaced = []; // ids surfaced into the current turn, acked on completion (by id, not count)
|
|
74
98
|
let awaitingTurnEnd = false; // a turn is in flight → ignore a duplicate idle that isn't its end
|
|
99
|
+
let errorRetryTimer;
|
|
100
|
+
let errorRetryMs = ERROR_RETRY_INITIAL_MS;
|
|
101
|
+
// Transcript mirror → `tr-<name>`: opt-in via COTAL_TRANSCRIPT (the connector's buildLaunch / the
|
|
102
|
+
// manager set it for managed sessions; a personal opencode never mirrors). EVENT-DRIVEN — fed from
|
|
103
|
+
// the OpenCode event hook below (message.updated → assistant roles, message.part.updated → parts)
|
|
104
|
+
// and flushed at session.idle, so it never re-reads the whole session. The manager grants this agent
|
|
105
|
+
// pub rights on the same `tr-<name>` channel.
|
|
106
|
+
const transcript = /^(1|true|yes|on)$/i.test(process.env.COTAL_TRANSCRIPT ?? "")
|
|
107
|
+
? createTranscriptMirror(agent, transcriptChannel(config.name))
|
|
108
|
+
: undefined;
|
|
75
109
|
const safeStatus = async (status, activity) => {
|
|
76
110
|
try {
|
|
77
111
|
if (agent.connected)
|
|
@@ -81,9 +115,65 @@ export const cotal = async ({ client }) => {
|
|
|
81
115
|
/* presence is best-effort — never throw into opencode */
|
|
82
116
|
}
|
|
83
117
|
};
|
|
118
|
+
// Cooperative shutdown. The manager sends an authenticated {op:"shutdown"} to this agent's local
|
|
119
|
+
// control endpoint on a signal-less runtime (ConPTY/Windows), where a hard kill would skip cleanup
|
|
120
|
+
// and leave the agent online until its presence TTL expires. We leave the mesh cleanly instead, then
|
|
121
|
+
// exit (the runtime hard-kills as a backstop). The endpoint (path + token) is minted by the
|
|
122
|
+
// connector's buildLaunch and arrives in the child env; the plugin runs inside the opencode server
|
|
123
|
+
// process, so it reads it there. Hooks are in-process (no external relay connects), so only the
|
|
124
|
+
// shutdown op is used — the handle path is inert. fatalBind: a managed agent MUST own its control
|
|
125
|
+
// endpoint, so a squatter (or a runtime that can't host the pipe) fails loud rather than running a
|
|
126
|
+
// hijacked or absent control plane.
|
|
127
|
+
let controlServer;
|
|
128
|
+
const shutdown = async () => {
|
|
129
|
+
try {
|
|
130
|
+
controlServer?.close();
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
/* ignore */
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
await safeStatus("offline");
|
|
137
|
+
await agent.stop();
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
process.exit(0);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
const controlPath = process.env.COTAL_CONTROL_SOCKET?.trim();
|
|
144
|
+
const controlToken = process.env.COTAL_CONTROL_TOKEN?.trim();
|
|
145
|
+
if (controlPath && controlToken) {
|
|
146
|
+
const handle = async () => ({
|
|
147
|
+
ok: false,
|
|
148
|
+
error: "opencode runs cotal hooks in-process; only the shutdown control op is supported",
|
|
149
|
+
});
|
|
150
|
+
controlServer = startControlServer(agent, { path: controlPath, token: controlToken }, handle, {
|
|
151
|
+
fatalBind: true,
|
|
152
|
+
onShutdown: () => void shutdown(),
|
|
153
|
+
});
|
|
154
|
+
}
|
|
84
155
|
function pendingForWake() {
|
|
85
156
|
return agent.pendingWake(); // mode-and-channel-aware: excludes held dnd/quiet ambient
|
|
86
157
|
}
|
|
158
|
+
function clearErrorRetry(resetDelay = false) {
|
|
159
|
+
if (errorRetryTimer)
|
|
160
|
+
clearTimeout(errorRetryTimer);
|
|
161
|
+
errorRetryTimer = undefined;
|
|
162
|
+
if (resetDelay)
|
|
163
|
+
errorRetryMs = ERROR_RETRY_INITIAL_MS;
|
|
164
|
+
}
|
|
165
|
+
function scheduleErrorRetry() {
|
|
166
|
+
if (errorRetryTimer || pendingForWake() === 0)
|
|
167
|
+
return;
|
|
168
|
+
const delay = errorRetryMs;
|
|
169
|
+
errorRetryMs = Math.min(errorRetryMs * 2, ERROR_RETRY_MAX_MS);
|
|
170
|
+
errorRetryTimer = setTimeout(() => {
|
|
171
|
+
errorRetryTimer = undefined;
|
|
172
|
+
if (!busy && pendingForWake() > 0)
|
|
173
|
+
void drive();
|
|
174
|
+
}, delay);
|
|
175
|
+
errorRetryTimer.unref?.();
|
|
176
|
+
}
|
|
87
177
|
function adoptSession(id, reason) {
|
|
88
178
|
if (sessionID === id)
|
|
89
179
|
return;
|
|
@@ -96,6 +186,8 @@ export const cotal = async ({ client }) => {
|
|
|
96
186
|
briefed = false;
|
|
97
187
|
surfaced = [];
|
|
98
188
|
awaitingTurnEnd = false;
|
|
189
|
+
clearErrorRetry(true);
|
|
190
|
+
transcript?.reset(); // hard session boundary: drop any buffered parts so `/new` never mirrors them
|
|
99
191
|
if (previous) {
|
|
100
192
|
log(`adopted opencode session ${id} after ${reason}; mesh identity unchanged`);
|
|
101
193
|
if (pendingForWake() > 0)
|
|
@@ -108,8 +200,11 @@ export const cotal = async ({ client }) => {
|
|
|
108
200
|
* can't be picked. Awaited by ensureSession before the first drive. */
|
|
109
201
|
const sessionReady = (async () => {
|
|
110
202
|
try {
|
|
111
|
-
const res = await
|
|
112
|
-
|
|
203
|
+
const res = await opencodeApi("/session", {
|
|
204
|
+
method: "POST",
|
|
205
|
+
body: JSON.stringify({ title: `cotal:${config.space}:${config.name}` }),
|
|
206
|
+
}, 10_000);
|
|
207
|
+
const id = res.id;
|
|
113
208
|
if (id) {
|
|
114
209
|
adoptSession(id, "boot");
|
|
115
210
|
process.stderr.write(`[cotal-session] ${id}\n`);
|
|
@@ -127,7 +222,7 @@ export const cotal = async ({ client }) => {
|
|
|
127
222
|
return sessionID ?? (await sessionReady);
|
|
128
223
|
}
|
|
129
224
|
/** Drive a turn carrying the current inbox batch (and the boot briefing once) into the visible
|
|
130
|
-
* session via the
|
|
225
|
+
* session via the server API — server-side, so it can't race like the TUI input box, and the TUI
|
|
131
226
|
* renders it live (it subscribes to that session's events). Surfaces the items but does NOT ack
|
|
132
227
|
* them — ackSurfaced runs on turn completion, so a crash/error redelivers. `override` replaces
|
|
133
228
|
* the body (a bare nudge, e.g. a focus @mention pull) and surfaces nothing to ack. Self-guards
|
|
@@ -171,10 +266,10 @@ export const cotal = async ({ client }) => {
|
|
|
171
266
|
body.system = `${persona}\n\n${ORIENTATION_BOOTSTRAP}`;
|
|
172
267
|
busy = true;
|
|
173
268
|
surfaced = ids;
|
|
174
|
-
// Arm BEFORE the await: a turn-end signal can land before
|
|
269
|
+
// Arm BEFORE the await: a turn-end signal can land before the server request resolves, and
|
|
175
270
|
// completeTurn bails unless armed — arming after would drop it and wedge the agent.
|
|
176
271
|
awaitingTurnEnd = true;
|
|
177
|
-
await
|
|
272
|
+
await opencodeApi(`/session/${encodeURIComponent(id)}/prompt_async`, { method: "POST", body: JSON.stringify(body) }, 10_000);
|
|
178
273
|
primed = true;
|
|
179
274
|
}
|
|
180
275
|
catch (e) {
|
|
@@ -182,6 +277,7 @@ export const cotal = async ({ client }) => {
|
|
|
182
277
|
surfaced = [];
|
|
183
278
|
awaitingTurnEnd = false;
|
|
184
279
|
log(`drive failed: ${e.message}`);
|
|
280
|
+
scheduleErrorRetry();
|
|
185
281
|
}
|
|
186
282
|
finally {
|
|
187
283
|
driving = false;
|
|
@@ -203,6 +299,30 @@ export const cotal = async ({ client }) => {
|
|
|
203
299
|
agent.drainInbox(n);
|
|
204
300
|
surfaced = [];
|
|
205
301
|
}
|
|
302
|
+
function abandonSurfaced() {
|
|
303
|
+
surfaced = [];
|
|
304
|
+
}
|
|
305
|
+
/** Native TUI / API prompts enter through OpenCode's chat.message hook before the model loop
|
|
306
|
+
* starts. This is the real "next turn" boundary for human-typed input: prepend the buffered Cotal
|
|
307
|
+
* batch to the user's text, then ack it when the resulting turn ends. We only mutate an existing
|
|
308
|
+
* text part so we don't need to manufacture OpenCode's internal part IDs. */
|
|
309
|
+
function injectIntoPrompt(output) {
|
|
310
|
+
if (driving || awaitingTurnEnd)
|
|
311
|
+
return; // drive() already injected, or one surfaced batch is open
|
|
312
|
+
const items = agent.peekInbox();
|
|
313
|
+
if (items.length === 0)
|
|
314
|
+
return;
|
|
315
|
+
const inj = formatInjection(items);
|
|
316
|
+
if (!inj)
|
|
317
|
+
return;
|
|
318
|
+
const textPart = output.parts?.find((p) => typeof p === "object" && p !== null && p.type === "text" && typeof p.text === "string");
|
|
319
|
+
if (!textPart)
|
|
320
|
+
return;
|
|
321
|
+
textPart.text = `${inj}\n\n${textPart.text}`;
|
|
322
|
+
surfaced = items.map((i) => i.id);
|
|
323
|
+
awaitingTurnEnd = true;
|
|
324
|
+
busy = true;
|
|
325
|
+
}
|
|
206
326
|
/** A turn ended — ANY turn, ours (a driven inbox batch) OR the human's (typing into the attached
|
|
207
327
|
* TUI, a `/reconnect`, etc). Clear `busy` regardless of who drove it: it's the COALESCE guard, so
|
|
208
328
|
* a turn the connector didn't drive must still release it or every later push wedges behind a
|
|
@@ -218,6 +338,7 @@ export const cotal = async ({ client }) => {
|
|
|
218
338
|
awaitingTurnEnd = false;
|
|
219
339
|
ackSurfaced(); // our driven turn: ack the surfaced batch (the sole ack site)
|
|
220
340
|
}
|
|
341
|
+
clearErrorRetry(true);
|
|
221
342
|
if (pendingForWake() > 0)
|
|
222
343
|
void drive();
|
|
223
344
|
}
|
|
@@ -228,7 +349,7 @@ export const cotal = async ({ client }) => {
|
|
|
228
349
|
// (ack-dropped at ingest); in `focus`, ambient/@mentions never reach "incoming" either.
|
|
229
350
|
agent.on("incoming", (item) => {
|
|
230
351
|
if (busy)
|
|
231
|
-
return; // buffer; completeTurn drives at
|
|
352
|
+
return; // buffer; chat.message or completeTurn drives at the next safe boundary
|
|
232
353
|
const directed = item.kind !== "channel" || item.mentionsMe;
|
|
233
354
|
const quiet = item.kind === "channel" && agent.channelMode(item.channel) === "quiet";
|
|
234
355
|
if (directed || (!quiet && agent.attention === "open"))
|
|
@@ -254,6 +375,11 @@ export const cotal = async ({ client }) => {
|
|
|
254
375
|
};
|
|
255
376
|
const hooks = {
|
|
256
377
|
tool: buildCotalTools(agent, config),
|
|
378
|
+
"chat.message": async (input, output) => {
|
|
379
|
+
if (!ours(input.sessionID))
|
|
380
|
+
return;
|
|
381
|
+
injectIntoPrompt(output);
|
|
382
|
+
},
|
|
257
383
|
event: async ({ event }) => {
|
|
258
384
|
// The server emits `permission.asked` (the SDK's `permission.updated` type ships but never
|
|
259
385
|
// fires — #11616), so match the real runtime name out of band. With permission:"allow" this
|
|
@@ -271,12 +397,29 @@ export const cotal = async ({ client }) => {
|
|
|
271
397
|
if (!event.properties.info.parentID)
|
|
272
398
|
adoptSession(event.properties.info.id, "top-level session create");
|
|
273
399
|
break;
|
|
274
|
-
case "
|
|
275
|
-
|
|
400
|
+
case "message.updated":
|
|
401
|
+
// Feed the transcript mirror: which messages on OUR session are assistant-authored.
|
|
402
|
+
if (transcript && ours(event.properties.info.sessionID))
|
|
403
|
+
transcript.record(event.properties.info);
|
|
404
|
+
break;
|
|
405
|
+
case "message.part.updated":
|
|
406
|
+
// Feed the transcript mirror: buffer each part as it streams (no per-turn refetch).
|
|
407
|
+
if (transcript && ours(event.properties.part.sessionID))
|
|
408
|
+
transcript.observe(event.properties.part);
|
|
409
|
+
break;
|
|
410
|
+
case "session.idle": {
|
|
411
|
+
const idleSession = event.properties.sessionID;
|
|
412
|
+
if (!ours(idleSession))
|
|
276
413
|
return;
|
|
277
414
|
await safeStatus("idle");
|
|
415
|
+
// Publish what the agent did this turn BEFORE driving the next batch, so a fresh turn's parts
|
|
416
|
+
// don't bleed in. The mirror is an observability side-channel: surface a publish failure but
|
|
417
|
+
// keep it OFF the turn loop, so a transport hiccup can never wedge the agent.
|
|
418
|
+
if (transcript)
|
|
419
|
+
await transcript.flush().catch((e) => log(`transcript mirror publish failed: ${e.message}`));
|
|
278
420
|
completeTurn(); // the sole turn-end site: ack-on-surface + drive the next batch
|
|
279
421
|
break;
|
|
422
|
+
}
|
|
280
423
|
case "session.status": {
|
|
281
424
|
if (!ours(event.properties.sessionID))
|
|
282
425
|
return;
|
|
@@ -305,11 +448,10 @@ export const cotal = async ({ client }) => {
|
|
|
305
448
|
busy = false;
|
|
306
449
|
if (awaitingTurnEnd) {
|
|
307
450
|
awaitingTurnEnd = false;
|
|
308
|
-
|
|
451
|
+
abandonSurfaced(); // failed turn: leave inbox unacked so the batch can retry on a later safe turn
|
|
309
452
|
}
|
|
310
453
|
await safeStatus("idle");
|
|
311
|
-
|
|
312
|
-
void drive();
|
|
454
|
+
scheduleErrorRetry();
|
|
313
455
|
break;
|
|
314
456
|
case "session.deleted":
|
|
315
457
|
if (!ours(event.properties.info.id))
|
|
@@ -325,7 +467,14 @@ export const cotal = async ({ client }) => {
|
|
|
325
467
|
await safeStatus("working", input.tool);
|
|
326
468
|
},
|
|
327
469
|
dispose: async () => {
|
|
470
|
+
try {
|
|
471
|
+
controlServer?.close();
|
|
472
|
+
}
|
|
473
|
+
catch {
|
|
474
|
+
/* ignore */
|
|
475
|
+
}
|
|
328
476
|
await safeStatus("offline");
|
|
477
|
+
clearErrorRetry(true);
|
|
329
478
|
await agent.stop();
|
|
330
479
|
},
|
|
331
480
|
};
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
|
package/dist/serve.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* 2. poke it once so the lazily-loaded plugin initializes (joins the mesh, creates ONE session);
|
|
7
7
|
* 3. the plugin announces that session's id on stderr (`[cotal-session] <id>`);
|
|
8
8
|
* 4. launch a foreground `opencode attach <url> --session <id>` — the TUI opens straight onto the
|
|
9
|
-
* agent's session, and every turn the plugin drives
|
|
9
|
+
* agent's session, and every turn the plugin drives through this exact server renders live.
|
|
10
10
|
*
|
|
11
11
|
* The attach TUI is a pure viewer (it connects to the running server); its env strips the plugin
|
|
12
12
|
* config + COTAL_* so it never loads a *second* mesh endpoint.
|
|
@@ -22,8 +22,30 @@ import { createServer } from "node:net";
|
|
|
22
22
|
import { once } from "node:events";
|
|
23
23
|
import { randomBytes } from "node:crypto";
|
|
24
24
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
25
|
-
import { join } from "node:path";
|
|
26
|
-
|
|
25
|
+
import { delimiter, join } from "node:path";
|
|
26
|
+
/** The opencode binary to spawn. `COTAL_OPENCODE_BIN` overrides. On Windows, `opencode` on PATH is
|
|
27
|
+
* an npm `.cmd` shim that child_process can't spawn, and the real `opencode.exe` it wraps
|
|
28
|
+
* (`<bindir>/node_modules/opencode-ai/bin/opencode.exe`) isn't itself on PATH — resolve and spawn
|
|
29
|
+
* it directly, so there's no `cmd.exe` wrapper to orphan the server on kill. POSIX spawns the name
|
|
30
|
+
* as-is. Unresolved on Windows → returns the bare name so spawn fails with a clear ENOENT. */
|
|
31
|
+
function resolveOpencodeBin() {
|
|
32
|
+
const override = process.env.COTAL_OPENCODE_BIN?.trim();
|
|
33
|
+
if (override)
|
|
34
|
+
return override;
|
|
35
|
+
if (process.platform !== "win32")
|
|
36
|
+
return "opencode";
|
|
37
|
+
for (const dir of (process.env.PATH ?? "").split(delimiter)) {
|
|
38
|
+
if (!dir)
|
|
39
|
+
continue;
|
|
40
|
+
for (const exe of [join(dir, "opencode.exe"), join(dir, "node_modules", "opencode-ai", "bin", "opencode.exe")]) {
|
|
41
|
+
if (existsSync(exe))
|
|
42
|
+
return exe;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return "opencode";
|
|
46
|
+
}
|
|
47
|
+
const BIN = resolveOpencodeBin();
|
|
48
|
+
const USERNAME = "opencode";
|
|
27
49
|
/** Per-launch secret gating the spawned server's HTTP API (see SECURITY above). */
|
|
28
50
|
const SECRET = randomBytes(24).toString("hex");
|
|
29
51
|
/** Ask the OS for a free port (bind :0, read it, release) so co-located peers don't collide. */
|
|
@@ -82,7 +104,13 @@ async function main() {
|
|
|
82
104
|
}
|
|
83
105
|
mkdirSync(agentHome, { recursive: true });
|
|
84
106
|
const serve = spawn(BIN, ["serve", "--hostname", "127.0.0.1", "--port", port], {
|
|
85
|
-
env: {
|
|
107
|
+
env: {
|
|
108
|
+
...process.env,
|
|
109
|
+
COTAL_OPENCODE_SERVER_URL: url,
|
|
110
|
+
OPENCODE_SERVER_USERNAME: USERNAME,
|
|
111
|
+
OPENCODE_SERVER_PASSWORD: SECRET,
|
|
112
|
+
OPENCODE_DB: dbPath,
|
|
113
|
+
},
|
|
86
114
|
stdio: ["ignore", "pipe", "pipe"],
|
|
87
115
|
});
|
|
88
116
|
writeFileSync(pidFile, String(serve.pid));
|
|
@@ -115,7 +143,7 @@ async function main() {
|
|
|
115
143
|
// bootstrap that loads the plugin. Each poke carries its own abort timeout: a request that
|
|
116
144
|
// lands in the early-boot window can hang with no response, and an un-timed fetch would pin
|
|
117
145
|
// the loop on it forever (undici queues later requests behind it on the pooled connection).
|
|
118
|
-
const auth = `Basic ${Buffer.from(
|
|
146
|
+
const auth = `Basic ${Buffer.from(`${USERNAME}:${SECRET}`).toString("base64")}`;
|
|
119
147
|
void (async () => {
|
|
120
148
|
for (let i = 0; i < 300 && !sessionId; i++) {
|
|
121
149
|
try {
|
|
@@ -139,8 +167,20 @@ async function main() {
|
|
|
139
167
|
await killServe(serve);
|
|
140
168
|
process.exit(1);
|
|
141
169
|
}
|
|
170
|
+
// Headless mode (COTAL_SERVE_HEADLESS=1): no foreground TUI. Hand the running server back to a
|
|
171
|
+
// non-terminal host (a web studio, an automated harness) via one machine-readable handshake line
|
|
172
|
+
// on stdout, then keep the serve alive. The host drives the session over HTTP (basic auth with the
|
|
173
|
+
// password below) and tails its event stream. `attached` stays false, so the `serve.on("exit")`
|
|
174
|
+
// handler above still exits us if the server dies; SIGTERM tears the server down for real.
|
|
175
|
+
if (process.env.COTAL_SERVE_HEADLESS?.trim() === "1") {
|
|
176
|
+
process.stdout.write(`[cotal-serve] ${JSON.stringify({ port: Number(port), session: id, password: SECRET })}\n`);
|
|
177
|
+
for (const sig of ["SIGINT", "SIGTERM"])
|
|
178
|
+
process.on(sig, () => void killServe(serve).then(() => process.exit(0)));
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
142
181
|
const tuiEnv = {
|
|
143
182
|
...process.env,
|
|
183
|
+
OPENCODE_SERVER_USERNAME: USERNAME,
|
|
144
184
|
OPENCODE_SERVER_PASSWORD: SECRET,
|
|
145
185
|
OPENCODE_DB: dbPath,
|
|
146
186
|
};
|
package/dist/serve.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACrF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode transcript mirror — publishes the agent's OWN session output to the per-agent `tr-<name>`
|
|
3
|
+
* channel (assistant text in full, tool calls as one-liners, tool results truncated, reasoning
|
|
4
|
+
* omitted) so observers can read what an OpenCode agent ACTUALLY did — not only what it narrates on
|
|
5
|
+
* the work channels (the Claude connector has had this; OpenCode did not).
|
|
6
|
+
*
|
|
7
|
+
* EVENT-DRIVEN, not poll-based. The connector plugin already receives OpenCode's bus events
|
|
8
|
+
* in-process, so the mirror taps them (the same live stream the frontier-faces studio proxies to its
|
|
9
|
+
* browser, instead of re-reading the session each frame):
|
|
10
|
+
* • record(msg) ← message.updated: learn which messageIDs are ASSISTANT (mirror the agent's
|
|
11
|
+
* own output, never the injected user/peer turns).
|
|
12
|
+
* • observe(part) ← message.part.updated: condense the part NOW and buffer its lines (tool results
|
|
13
|
+
* truncated to a preview, assistant text kept in full;
|
|
14
|
+
* keyed by `${messageID}:${partID}`, replaced as it streams).
|
|
15
|
+
* • flush() ← session.idle: publish the turn's settled assistant lines, then clear.
|
|
16
|
+
* • reset() ← session adoption: drop the buffer at a hard boundary (`/new`).
|
|
17
|
+
* Cost is O(parts-this-turn) with NO HTTP — unlike a fetch-the-whole-session-every-idle poll, which is
|
|
18
|
+
* O(N²) and grows unboundedly over a long session. flush() snapshots the turn's lines and clears before
|
|
19
|
+
* it publishes, so a duplicate/late session.idle can't republish the batch.
|
|
20
|
+
*
|
|
21
|
+
* Each turn's lines are published ONCE to the channel; DURABLE delivery (persistence, replay to offline
|
|
22
|
+
* readers) is the channel's job — a durable `tr-<name>` is JetStream-backed — not a hand-rolled retry
|
|
23
|
+
* buffer here. This mirror is therefore best-effort at the publish boundary: the plugin runs flush() on
|
|
24
|
+
* the turn-end path and keeps a transport error OFF the turn loop by logging it (so a failed publish can
|
|
25
|
+
* never wedge the agent) — which means a publish that fails outright drops THAT turn's lines. That is
|
|
26
|
+
* the deliberate tradeoff for an opt-in observability side-channel; it is logged, never silent.
|
|
27
|
+
*/
|
|
28
|
+
import type { MeshAgent } from "@cotal-ai/connector-core";
|
|
29
|
+
export interface TranscriptMirror {
|
|
30
|
+
/** message.updated → remember which messageIDs are assistant-authored (this turn). */
|
|
31
|
+
record(message: any): void;
|
|
32
|
+
/** message.part.updated → condense the part and buffer its lines (tool results truncated, assistant
|
|
33
|
+
* text kept full; keyed by `${messageID}:${partID}`, this turn). */
|
|
34
|
+
observe(part: any): void;
|
|
35
|
+
/** session.idle → publish the turn's settled assistant lines to the channel, then clear. */
|
|
36
|
+
flush(): Promise<void>;
|
|
37
|
+
/** session adoption (`/new`) → drop the buffer so a new session never inherits stale parts. */
|
|
38
|
+
reset(): void;
|
|
39
|
+
}
|
|
40
|
+
/** An event-driven mirror to `channel`, fed from the plugin's OpenCode event hook. */
|
|
41
|
+
export declare function createTranscriptMirror(agent: MeshAgent, channel: string): TranscriptMirror;
|
|
42
|
+
//# sourceMappingURL=transcript.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript.d.ts","sourceRoot":"","sources":["../src/transcript.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AA8D1D,MAAM,WAAW,gBAAgB;IAC/B,sFAAsF;IACtF,MAAM,CAAC,OAAO,EAAE,GAAG,GAAG,IAAI,CAAC;IAC3B;yEACqE;IACrE,OAAO,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,CAAC;IACzB,4FAA4F;IAC5F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,+FAA+F;IAC/F,KAAK,IAAI,IAAI,CAAC;CACf;AAED,sFAAsF;AACtF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAyC1F"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
const MAX_PREVIEW = 700; // tool results — enough to see what happened
|
|
2
|
+
const MAX_CHUNK = 6000; // chars per published message
|
|
3
|
+
function truncate(s, max) {
|
|
4
|
+
return s.length > max ? `${s.slice(0, max - 1)}…` : s;
|
|
5
|
+
}
|
|
6
|
+
/** A tool call's most salient input — mirrors the Claude connector's `salient`. */
|
|
7
|
+
function salient(input) {
|
|
8
|
+
const i = (input ?? {});
|
|
9
|
+
const v = i.command ?? i.filePath ?? i.file_path ?? i.path ?? i.url ?? i.pattern ?? i.description;
|
|
10
|
+
const s = typeof v === "string" ? v : Object.keys(i).length ? JSON.stringify(i) : "";
|
|
11
|
+
return s ? `: ${truncate(s, 300)}` : "";
|
|
12
|
+
}
|
|
13
|
+
/** One OpenCode message Part → the line(s) worth mirroring (empty for reasoning/file/step/etc). */
|
|
14
|
+
function condensePart(part) {
|
|
15
|
+
switch (part?.type) {
|
|
16
|
+
case "text":
|
|
17
|
+
if (part.synthetic || part.ignored || !part.text?.trim())
|
|
18
|
+
return [];
|
|
19
|
+
return [part.text.trim()]; // assistant text in FULL — chunkLines splits it across messages
|
|
20
|
+
case "tool": {
|
|
21
|
+
const head = `⚒ ${part.tool}${salient(part.state?.input)}`;
|
|
22
|
+
const status = part.state?.status;
|
|
23
|
+
if (status === "completed")
|
|
24
|
+
return [head, `→ ${truncate(String(part.state.output ?? ""), MAX_PREVIEW) || "(no output)"}`];
|
|
25
|
+
if (status === "error")
|
|
26
|
+
return [head, `→ ERROR: ${truncate(String(part.state.error ?? ""), MAX_PREVIEW)}`];
|
|
27
|
+
return [head];
|
|
28
|
+
}
|
|
29
|
+
default:
|
|
30
|
+
return []; // reasoning (thinking) omitted; file/step/snapshot/patch skipped
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Batch lines into wire-sized chunks, splitting on line boundaries — and hard-splitting any single
|
|
34
|
+
* line longer than `max` (e.g. a long assistant answer/code block) so full text is preserved across
|
|
35
|
+
* messages rather than truncated. */
|
|
36
|
+
function chunkLines(lines, max) {
|
|
37
|
+
const chunks = [];
|
|
38
|
+
let cur = "";
|
|
39
|
+
for (const ln of lines) {
|
|
40
|
+
if (ln.length > max) {
|
|
41
|
+
if (cur) {
|
|
42
|
+
chunks.push(cur);
|
|
43
|
+
cur = "";
|
|
44
|
+
}
|
|
45
|
+
for (let i = 0; i < ln.length; i += max)
|
|
46
|
+
chunks.push(ln.slice(i, i + max));
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (cur && cur.length + ln.length + 1 > max) {
|
|
50
|
+
chunks.push(cur);
|
|
51
|
+
cur = "";
|
|
52
|
+
}
|
|
53
|
+
cur = cur ? `${cur}\n${ln}` : ln;
|
|
54
|
+
}
|
|
55
|
+
if (cur)
|
|
56
|
+
chunks.push(cur);
|
|
57
|
+
return chunks;
|
|
58
|
+
}
|
|
59
|
+
/** An event-driven mirror to `channel`, fed from the plugin's OpenCode event hook. */
|
|
60
|
+
export function createTranscriptMirror(agent, channel) {
|
|
61
|
+
// One turn's worth, cleared every flush. `condensed` keeps each part's already-condensed lines (not
|
|
62
|
+
// the raw part) — tool results truncated to a preview, assistant text kept in full — so noisy tool
|
|
63
|
+
// output never sits in memory at full size. Map insertion order preserves emission order (a later
|
|
64
|
+
// .updated replaces the value, keeping its slot).
|
|
65
|
+
const assistantMsgs = new Set(); // messageIDs whose role is "assistant"
|
|
66
|
+
// Keyed by `${messageID}:${partID}` (NOT partID alone): OpenCode part ids are message-scoped, so a
|
|
67
|
+
// multi-message turn could otherwise collide two parts on the same id and drop output.
|
|
68
|
+
const condensed = new Map();
|
|
69
|
+
const clear = () => {
|
|
70
|
+
condensed.clear();
|
|
71
|
+
assistantMsgs.clear();
|
|
72
|
+
};
|
|
73
|
+
return {
|
|
74
|
+
record(message) {
|
|
75
|
+
const info = message?.info ?? message;
|
|
76
|
+
if (info?.role === "assistant" && typeof info.id === "string")
|
|
77
|
+
assistantMsgs.add(info.id);
|
|
78
|
+
},
|
|
79
|
+
observe(part) {
|
|
80
|
+
if (!part?.id || typeof part.messageID !== "string")
|
|
81
|
+
return;
|
|
82
|
+
const key = `${part.messageID}:${part.id}`;
|
|
83
|
+
const lines = condensePart(part); // condense NOW (tool results truncated) → assistant text kept full
|
|
84
|
+
if (lines.length)
|
|
85
|
+
condensed.set(key, { messageID: part.messageID, lines });
|
|
86
|
+
else
|
|
87
|
+
condensed.delete(key); // a part that condensed to nothing (e.g. reasoning) holds no slot
|
|
88
|
+
},
|
|
89
|
+
async flush() {
|
|
90
|
+
// Collect this turn's assistant lines and clear — each turn's output is published ONCE, then
|
|
91
|
+
// forgotten. Snapshot-then-clear before the await means a duplicate/late session.idle finds an
|
|
92
|
+
// empty buffer and can't republish. Delivery reliability is the CHANNEL's job (NATS/JetStream),
|
|
93
|
+
// not a hand-rolled retry buffer here.
|
|
94
|
+
const lines = [];
|
|
95
|
+
for (const { messageID, lines: ls } of condensed.values())
|
|
96
|
+
if (assistantMsgs.has(messageID))
|
|
97
|
+
lines.push(...ls); // the agent's OWN output, not injected turns
|
|
98
|
+
clear();
|
|
99
|
+
for (const chunk of chunkLines(lines, MAX_CHUNK))
|
|
100
|
+
await agent.send(chunk, channel);
|
|
101
|
+
},
|
|
102
|
+
reset() {
|
|
103
|
+
clear();
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=transcript.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript.js","sourceRoot":"","sources":["../src/transcript.ts"],"names":[],"mappings":"AA6BA,MAAM,WAAW,GAAG,GAAG,CAAC,CAAC,6CAA6C;AACtE,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,8BAA8B;AAEtD,SAAS,QAAQ,CAAC,CAAS,EAAE,GAAW;IACtC,OAAO,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,mFAAmF;AACnF,SAAS,OAAO,CAAC,KAAc;IAC7B,MAAM,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAA4B,CAAC;IACnD,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,WAAW,CAAC;IAClG,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACrF,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC1C,CAAC;AAED,mGAAmG;AACnG,SAAS,YAAY,CAAC,IAAS;IAC7B,QAAQ,IAAI,EAAE,IAAI,EAAE,CAAC;QACnB,KAAK,MAAM;YACT,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE;gBAAE,OAAO,EAAE,CAAC;YACpE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,gEAAgE;QAE7F,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,IAAI,GAAG,KAAK,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC;YAC3D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC;YAClC,IAAI,MAAM,KAAK,WAAW;gBACxB,OAAO,CAAC,IAAI,EAAE,KAAK,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,WAAW,CAAC,IAAI,aAAa,EAAE,CAAC,CAAC;YAChG,IAAI,MAAM,KAAK,OAAO;gBAAE,OAAO,CAAC,IAAI,EAAE,YAAY,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;YAC3G,OAAO,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC;QACD;YACE,OAAO,EAAE,CAAC,CAAC,iEAAiE;IAChF,CAAC;AACH,CAAC;AAED;;sCAEsC;AACtC,SAAS,UAAU,CAAC,KAAe,EAAE,GAAW;IAC9C,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;QACvB,IAAI,EAAE,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;YACpB,IAAI,GAAG,EAAE,CAAC;gBACR,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACjB,GAAG,GAAG,EAAE,CAAC;YACX,CAAC;YACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,GAAG;gBAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;YAC3E,SAAS;QACX,CAAC;QACD,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,MAAM,GAAG,CAAC,GAAG,GAAG,EAAE,CAAC;YAC5C,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACjB,GAAG,GAAG,EAAE,CAAC;QACX,CAAC;QACD,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACnC,CAAC;IACD,IAAI,GAAG;QAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1B,OAAO,MAAM,CAAC;AAChB,CAAC;AAcD,sFAAsF;AACtF,MAAM,UAAU,sBAAsB,CAAC,KAAgB,EAAE,OAAe;IACtE,oGAAoG;IACpG,mGAAmG;IACnG,kGAAkG;IAClG,kDAAkD;IAClD,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC,CAAC,uCAAuC;IAChF,mGAAmG;IACnG,uFAAuF;IACvF,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkD,CAAC;IAC5E,MAAM,KAAK,GAAG,GAAS,EAAE;QACvB,SAAS,CAAC,KAAK,EAAE,CAAC;QAClB,aAAa,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC,CAAC;IAEF,OAAO;QACL,MAAM,CAAC,OAAO;YACZ,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,IAAI,OAAO,CAAC;YACtC,IAAI,IAAI,EAAE,IAAI,KAAK,WAAW,IAAI,OAAO,IAAI,CAAC,EAAE,KAAK,QAAQ;gBAAE,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5F,CAAC;QACD,OAAO,CAAC,IAAI;YACV,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ;gBAAE,OAAO;YAC5D,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;YAC3C,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,mEAAmE;YACrG,IAAI,KAAK,CAAC,MAAM;gBAAE,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;;gBACtE,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,kEAAkE;QAChG,CAAC;QACD,KAAK,CAAC,KAAK;YACT,6FAA6F;YAC7F,+FAA+F;YAC/F,gGAAgG;YAChG,uCAAuC;YACvC,MAAM,KAAK,GAAa,EAAE,CAAC;YAC3B,KAAK,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,SAAS,CAAC,MAAM,EAAE;gBACvD,IAAI,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC;oBAAE,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,6CAA6C;YACpG,KAAK,EAAE,CAAC;YACR,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC;gBAAE,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACrF,CAAC;QACD,KAAK;YACH,KAAK,EAAE,CAAC;QACV,CAAC;KACF,CAAC;AACJ,CAAC"}
|
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.8.
|
|
4
|
+
"version": "0.8.3",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@cotal-ai/connector-core": "0.8.
|
|
21
|
+
"@cotal-ai/connector-core": "0.8.3"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
24
|
"@cotal-ai/core": ">=0.1.0",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"@opencode-ai/sdk": "^1.16.2",
|
|
31
31
|
"esbuild": "^0.28.0",
|
|
32
32
|
"tsx": "^4.22.4",
|
|
33
|
-
"@cotal-ai/core": "0.8.
|
|
33
|
+
"@cotal-ai/core": "0.8.3"
|
|
34
34
|
},
|
|
35
35
|
"files": [
|
|
36
36
|
"dist"
|