@cotal-ai/connector-claude-code 0.1.4-next-20260612020133 → 0.3.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/.claude-plugin/plugin.json +6 -0
- package/.mcp.json +8 -0
- package/dist/extension.d.ts.map +1 -1
- package/dist/extension.js +36 -7
- package/dist/extension.js.map +1 -1
- package/dist/hook.cjs +20 -9
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp.cjs +319 -33
- package/dist/mcp.js +11 -0
- package/dist/mcp.js.map +1 -1
- package/dist/transcript.d.ts +28 -0
- package/dist/transcript.d.ts.map +1 -0
- package/dist/transcript.js +204 -0
- package/dist/transcript.js.map +1 -0
- package/hooks/hooks.json +82 -0
- package/package.json +9 -6
package/.mcp.json
ADDED
package/dist/extension.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AAEA,OAAO,EAA2B,KAAK,SAAS,EAAoC,MAAM,gBAAgB,CAAC;AAmB3G;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,SA8D7B,CAAC"}
|
package/dist/extension.js
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
2
3
|
import { loadAgentFile, registry } from "@cotal-ai/core";
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
|
|
4
|
+
/** Name the cotal MCP server is registered under via --mcp-config (see buildLaunch). */
|
|
5
|
+
const MCP_SERVER_NAME = "cotal";
|
|
6
|
+
/** Channel ref for `--dangerously-load-development-channels`, which turns on the cotal MCP server's
|
|
7
|
+
* `claude/channel` capability so an idle session wakes the instant a peer message arrives. Because
|
|
8
|
+
* we isolate the session with --strict-mcp-config the plugin's own MCP server is suppressed and
|
|
9
|
+
* cotal is re-supplied via --mcp-config, so the ref is the manually-configured server tagged
|
|
10
|
+
* `server:<name>` (the CLI rejects a plugin ref or a bare name here). The plugin stays installed
|
|
11
|
+
* for its hooks, which do message delivery independent of this wake nudge. */
|
|
12
|
+
const CHANNEL_REF = `server:${MCP_SERVER_NAME}`;
|
|
13
|
+
/** Package root (parent of dist/), which doubles as the installable plugin dir: it carries
|
|
14
|
+
* .claude-plugin/, .mcp.json, hooks/ and the dist/*.cjs bundles. */
|
|
15
|
+
const PLUGIN_ROOT = fileURLToPath(new URL("..", import.meta.url));
|
|
16
|
+
/** The cotal MCP server bundle, supplied explicitly so a spawned session can run with ONLY this
|
|
17
|
+
* MCP server (see buildLaunch's --strict-mcp-config). */
|
|
18
|
+
const MCP_CJS = resolve(PLUGIN_ROOT, "dist", "mcp.cjs");
|
|
9
19
|
/**
|
|
10
20
|
* The Claude Code connector: launches the real `claude` with the Cotal identity in
|
|
11
21
|
* the environment and the mesh channel enabled, so the session joins the mesh and
|
|
@@ -15,6 +25,7 @@ const CHANNEL_REF = "plugin:cotal@cotal-mesh";
|
|
|
15
25
|
export const claudeConnector = {
|
|
16
26
|
kind: "connector",
|
|
17
27
|
name: "claude",
|
|
28
|
+
pluginRoot: PLUGIN_ROOT,
|
|
18
29
|
buildLaunch(opts) {
|
|
19
30
|
const env = {
|
|
20
31
|
COTAL_SPACE: opts.space,
|
|
@@ -22,6 +33,9 @@ export const claudeConnector = {
|
|
|
22
33
|
// Force the connector to emit channel wake-nudges: Claude doesn't advertise the
|
|
23
34
|
// `claude/channel` capability back over MCP, so auto-detection would see it "off".
|
|
24
35
|
COTAL_CHANNEL: "1",
|
|
36
|
+
// Managed sessions mirror their own transcript to `tr-<name>` so peers can read
|
|
37
|
+
// what the agent actually did. Personal sessions (no buildLaunch) never mirror.
|
|
38
|
+
COTAL_TRANSCRIPT: "1",
|
|
25
39
|
};
|
|
26
40
|
if (opts.role)
|
|
27
41
|
env.COTAL_ROLE = opts.role;
|
|
@@ -31,7 +45,22 @@ export const claudeConnector = {
|
|
|
31
45
|
env.COTAL_CREDS = opts.creds;
|
|
32
46
|
if (opts.servers)
|
|
33
47
|
env.COTAL_SERVERS = opts.servers;
|
|
34
|
-
|
|
48
|
+
// A leading positional is claude's first message, auto-submitted on start —
|
|
49
|
+
// so a driving session can greet the operator the moment it joins.
|
|
50
|
+
const args = opts.prompt
|
|
51
|
+
? [opts.prompt, "--dangerously-load-development-channels", CHANNEL_REF]
|
|
52
|
+
: ["--dangerously-load-development-channels", CHANNEL_REF];
|
|
53
|
+
// Pre-allow fetching the public Cotal docs so a doc-grounded persona (e.g. david)
|
|
54
|
+
// can look something up under `npx` (no repo on disk) without prompting the operator
|
|
55
|
+
// mid-demo. Additive under the default permission mode — leaves other tools as-is.
|
|
56
|
+
args.push("--allowedTools", "WebFetch(domain:github.com),WebFetch(domain:raw.githubusercontent.com)");
|
|
57
|
+
// Isolate the spawned session's MCP to ONLY the cotal server. --strict-mcp-config drops every
|
|
58
|
+
// other MCP source — including the operator's personal ~/.claude.json servers (e.g. a headless
|
|
59
|
+
// Chromium, a DB server) that a meshed teammate never needs and that, multiplied across several
|
|
60
|
+
// spawns on a busy machine, starve memory and kill the session before it registers presence —
|
|
61
|
+
// and --mcp-config re-supplies cotal so its tools + presence still load. The plugin itself stays
|
|
62
|
+
// enabled (its hooks + the dev-channels wake path are unaffected; only MCP config is scoped).
|
|
63
|
+
args.push("--strict-mcp-config", "--mcp-config", JSON.stringify({ mcpServers: { [MCP_SERVER_NAME]: { command: "node", args: [MCP_CJS] } } }));
|
|
35
64
|
// An agent file carries identity (read in-session via COTAL_AGENT_FILE) plus
|
|
36
65
|
// persona + model, which can only be applied to a `claude` session at launch.
|
|
37
66
|
if (opts.configPath) {
|
package/dist/extension.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAoD,MAAM,gBAAgB,CAAC;AAE3G
|
|
1
|
+
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"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"}
|
package/dist/hook.cjs
CHANGED
|
@@ -6421,7 +6421,7 @@ var require_authenticator = __commonJS({
|
|
|
6421
6421
|
exports2.tokenAuthenticator = tokenAuthenticator;
|
|
6422
6422
|
exports2.nkeyAuthenticator = nkeyAuthenticator;
|
|
6423
6423
|
exports2.jwtAuthenticator = jwtAuthenticator;
|
|
6424
|
-
exports2.credsAuthenticator =
|
|
6424
|
+
exports2.credsAuthenticator = credsAuthenticator5;
|
|
6425
6425
|
var nkeys_1 = require_nkeys2();
|
|
6426
6426
|
var encoders_1 = require_encoders();
|
|
6427
6427
|
function multiAuthenticator(authenticators) {
|
|
@@ -6471,7 +6471,7 @@ var require_authenticator = __commonJS({
|
|
|
6471
6471
|
return { jwt, nkey, sig };
|
|
6472
6472
|
};
|
|
6473
6473
|
}
|
|
6474
|
-
function
|
|
6474
|
+
function credsAuthenticator5(creds) {
|
|
6475
6475
|
const fn = typeof creds !== "function" ? () => creds : creds;
|
|
6476
6476
|
const parse = () => {
|
|
6477
6477
|
const CREDS = /\s*(?:(?:[-]{3,}[^\n]*[-]{3,}\n)(.+)(?:\n\s*[-]{3,}[^\n]*[-]{3,}\n))/ig;
|
|
@@ -12404,7 +12404,7 @@ var require_jsclient = __commonJS({
|
|
|
12404
12404
|
exports2.startFastIngest = startFastIngest;
|
|
12405
12405
|
exports2.toJetStreamClient = toJetStreamClient;
|
|
12406
12406
|
exports2.jetstream = jetstream2;
|
|
12407
|
-
exports2.jetstreamManager =
|
|
12407
|
+
exports2.jetstreamManager = jetstreamManager4;
|
|
12408
12408
|
exports2.scheduleSpecToHeader = scheduleSpecToHeader;
|
|
12409
12409
|
var jsbaseclient_api_1 = require_jsbaseclient_api();
|
|
12410
12410
|
var jsmconsumer_api_1 = require_jsmconsumer_api();
|
|
@@ -12491,7 +12491,7 @@ var require_jsclient = __commonJS({
|
|
|
12491
12491
|
function jetstream2(nc, opts = {}) {
|
|
12492
12492
|
return new JetStreamClientImpl(nc, opts);
|
|
12493
12493
|
}
|
|
12494
|
-
async function
|
|
12494
|
+
async function jetstreamManager4(nc, opts = {}) {
|
|
12495
12495
|
const adm = new JetStreamManagerImpl(nc, opts);
|
|
12496
12496
|
if (opts.checkAPI !== false) {
|
|
12497
12497
|
try {
|
|
@@ -12606,7 +12606,7 @@ var require_jsclient = __commonJS({
|
|
|
12606
12606
|
} catch (err) {
|
|
12607
12607
|
return Promise.reject(err);
|
|
12608
12608
|
}
|
|
12609
|
-
return
|
|
12609
|
+
return jetstreamManager4(this.nc, opts);
|
|
12610
12610
|
}
|
|
12611
12611
|
get apiPrefix() {
|
|
12612
12612
|
return this.prefix;
|
|
@@ -13601,11 +13601,11 @@ var require_connect = __commonJS({
|
|
|
13601
13601
|
"../../node_modules/.pnpm/@nats-io+transport-node@3.4.0/node_modules/@nats-io/transport-node/lib/connect.js"(exports2) {
|
|
13602
13602
|
"use strict";
|
|
13603
13603
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
13604
|
-
exports2.connect =
|
|
13604
|
+
exports2.connect = connect6;
|
|
13605
13605
|
var node_transport_1 = require_node_transport();
|
|
13606
13606
|
var nats_base_client_1 = require_nats_base_client();
|
|
13607
13607
|
var nats_base_client_2 = require_nats_base_client();
|
|
13608
|
-
function
|
|
13608
|
+
function connect6(opts = {}) {
|
|
13609
13609
|
if ((0, nats_base_client_2.hasWsProtocol)(opts)) {
|
|
13610
13610
|
return Promise.reject(nats_base_client_2.errors.InvalidArgumentError.format(`servers`, `node client doesn't support websockets, use the 'wsconnect' function instead`));
|
|
13611
13611
|
}
|
|
@@ -13797,7 +13797,7 @@ var require_kv = __commonJS({
|
|
|
13797
13797
|
throw new Error(`invalid bucket name: ${name}`);
|
|
13798
13798
|
}
|
|
13799
13799
|
}
|
|
13800
|
-
var
|
|
13800
|
+
var Kvm5 = class {
|
|
13801
13801
|
js;
|
|
13802
13802
|
/**
|
|
13803
13803
|
* Creates an instance of the Kv that allows you to create and access KV stores.
|
|
@@ -13863,7 +13863,7 @@ var require_kv = __commonJS({
|
|
|
13863
13863
|
return new internal_2.ListerImpl(subj, filter, this.js);
|
|
13864
13864
|
}
|
|
13865
13865
|
};
|
|
13866
|
-
exports2.Kvm =
|
|
13866
|
+
exports2.Kvm = Kvm5;
|
|
13867
13867
|
var Bucket = class _Bucket {
|
|
13868
13868
|
js;
|
|
13869
13869
|
jsm;
|
|
@@ -16422,6 +16422,11 @@ function loadAgentFile(path) {
|
|
|
16422
16422
|
const kind = str("kind");
|
|
16423
16423
|
if (kind && kind !== "agent" && kind !== "endpoint")
|
|
16424
16424
|
throw new Error(`agent file ${path}: "kind" must be "agent" or "endpoint"`);
|
|
16425
|
+
const known = /* @__PURE__ */ new Set(["name", "role", "kind", "description", "tags", "channels", "publish", "model"]);
|
|
16426
|
+
const meta = {};
|
|
16427
|
+
for (const [k, v] of Object.entries(fm))
|
|
16428
|
+
if (!known.has(k) && typeof v === "string")
|
|
16429
|
+
meta[k] = v;
|
|
16425
16430
|
return {
|
|
16426
16431
|
name,
|
|
16427
16432
|
role: str("role"),
|
|
@@ -16431,6 +16436,7 @@ function loadAgentFile(path) {
|
|
|
16431
16436
|
channels: list("channels"),
|
|
16432
16437
|
publish: list("publish"),
|
|
16433
16438
|
model: str("model"),
|
|
16439
|
+
meta: Object.keys(meta).length ? meta : void 0,
|
|
16434
16440
|
persona: persona || void 0
|
|
16435
16441
|
};
|
|
16436
16442
|
}
|
|
@@ -16441,6 +16447,11 @@ var import_jetstream2 = __toESM(require_mod4(), 1);
|
|
|
16441
16447
|
var import_kv3 = __toESM(require_mod6(), 1);
|
|
16442
16448
|
var DEFAULT_SERVER = "nats://127.0.0.1:4222";
|
|
16443
16449
|
|
|
16450
|
+
// ../../packages/core/dist/spaces.js
|
|
16451
|
+
var import_transport_node4 = __toESM(require_transport_node(), 1);
|
|
16452
|
+
var import_jetstream3 = __toESM(require_mod4(), 1);
|
|
16453
|
+
var import_kv4 = __toESM(require_mod6(), 1);
|
|
16454
|
+
|
|
16444
16455
|
// ../../packages/core/dist/registry.js
|
|
16445
16456
|
var Registry = class {
|
|
16446
16457
|
#byKey = /* @__PURE__ */ new Map();
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC,CAAC,kDAAkD"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC,CAAC,kDAAkD;AAClF,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC,CAAC,8DAA8D"}
|
package/dist/mcp.cjs
CHANGED
|
@@ -13281,7 +13281,7 @@ var require_authenticator = __commonJS({
|
|
|
13281
13281
|
exports2.tokenAuthenticator = tokenAuthenticator;
|
|
13282
13282
|
exports2.nkeyAuthenticator = nkeyAuthenticator;
|
|
13283
13283
|
exports2.jwtAuthenticator = jwtAuthenticator;
|
|
13284
|
-
exports2.credsAuthenticator =
|
|
13284
|
+
exports2.credsAuthenticator = credsAuthenticator5;
|
|
13285
13285
|
var nkeys_1 = require_nkeys2();
|
|
13286
13286
|
var encoders_1 = require_encoders();
|
|
13287
13287
|
function multiAuthenticator(authenticators) {
|
|
@@ -13331,7 +13331,7 @@ var require_authenticator = __commonJS({
|
|
|
13331
13331
|
return { jwt: jwt2, nkey, sig };
|
|
13332
13332
|
};
|
|
13333
13333
|
}
|
|
13334
|
-
function
|
|
13334
|
+
function credsAuthenticator5(creds) {
|
|
13335
13335
|
const fn = typeof creds !== "function" ? () => creds : creds;
|
|
13336
13336
|
const parse3 = () => {
|
|
13337
13337
|
const CREDS = /\s*(?:(?:[-]{3,}[^\n]*[-]{3,}\n)(.+)(?:\n\s*[-]{3,}[^\n]*[-]{3,}\n))/ig;
|
|
@@ -19264,7 +19264,7 @@ var require_jsclient = __commonJS({
|
|
|
19264
19264
|
exports2.startFastIngest = startFastIngest;
|
|
19265
19265
|
exports2.toJetStreamClient = toJetStreamClient;
|
|
19266
19266
|
exports2.jetstream = jetstream2;
|
|
19267
|
-
exports2.jetstreamManager =
|
|
19267
|
+
exports2.jetstreamManager = jetstreamManager4;
|
|
19268
19268
|
exports2.scheduleSpecToHeader = scheduleSpecToHeader;
|
|
19269
19269
|
var jsbaseclient_api_1 = require_jsbaseclient_api();
|
|
19270
19270
|
var jsmconsumer_api_1 = require_jsmconsumer_api();
|
|
@@ -19351,7 +19351,7 @@ var require_jsclient = __commonJS({
|
|
|
19351
19351
|
function jetstream2(nc, opts = {}) {
|
|
19352
19352
|
return new JetStreamClientImpl(nc, opts);
|
|
19353
19353
|
}
|
|
19354
|
-
async function
|
|
19354
|
+
async function jetstreamManager4(nc, opts = {}) {
|
|
19355
19355
|
const adm = new JetStreamManagerImpl(nc, opts);
|
|
19356
19356
|
if (opts.checkAPI !== false) {
|
|
19357
19357
|
try {
|
|
@@ -19466,7 +19466,7 @@ var require_jsclient = __commonJS({
|
|
|
19466
19466
|
} catch (err2) {
|
|
19467
19467
|
return Promise.reject(err2);
|
|
19468
19468
|
}
|
|
19469
|
-
return
|
|
19469
|
+
return jetstreamManager4(this.nc, opts);
|
|
19470
19470
|
}
|
|
19471
19471
|
get apiPrefix() {
|
|
19472
19472
|
return this.prefix;
|
|
@@ -20461,11 +20461,11 @@ var require_connect = __commonJS({
|
|
|
20461
20461
|
"../../node_modules/.pnpm/@nats-io+transport-node@3.4.0/node_modules/@nats-io/transport-node/lib/connect.js"(exports2) {
|
|
20462
20462
|
"use strict";
|
|
20463
20463
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
20464
|
-
exports2.connect =
|
|
20464
|
+
exports2.connect = connect5;
|
|
20465
20465
|
var node_transport_1 = require_node_transport();
|
|
20466
20466
|
var nats_base_client_1 = require_nats_base_client();
|
|
20467
20467
|
var nats_base_client_2 = require_nats_base_client();
|
|
20468
|
-
function
|
|
20468
|
+
function connect5(opts = {}) {
|
|
20469
20469
|
if ((0, nats_base_client_2.hasWsProtocol)(opts)) {
|
|
20470
20470
|
return Promise.reject(nats_base_client_2.errors.InvalidArgumentError.format(`servers`, `node client doesn't support websockets, use the 'wsconnect' function instead`));
|
|
20471
20471
|
}
|
|
@@ -20657,7 +20657,7 @@ var require_kv = __commonJS({
|
|
|
20657
20657
|
throw new Error(`invalid bucket name: ${name}`);
|
|
20658
20658
|
}
|
|
20659
20659
|
}
|
|
20660
|
-
var
|
|
20660
|
+
var Kvm5 = class {
|
|
20661
20661
|
js;
|
|
20662
20662
|
/**
|
|
20663
20663
|
* Creates an instance of the Kv that allows you to create and access KV stores.
|
|
@@ -20723,7 +20723,7 @@ var require_kv = __commonJS({
|
|
|
20723
20723
|
return new internal_2.ListerImpl(subj, filter, this.js);
|
|
20724
20724
|
}
|
|
20725
20725
|
};
|
|
20726
|
-
exports2.Kvm =
|
|
20726
|
+
exports2.Kvm = Kvm5;
|
|
20727
20727
|
var Bucket = class _Bucket {
|
|
20728
20728
|
js;
|
|
20729
20729
|
jsm;
|
|
@@ -20796,11 +20796,11 @@ var require_kv = __commonJS({
|
|
|
20796
20796
|
sc.subject_delete_marker_ttl = (0, internal_1.nanos)(bo.markerTTL);
|
|
20797
20797
|
}
|
|
20798
20798
|
if (opts.mirror) {
|
|
20799
|
-
const
|
|
20800
|
-
if (!
|
|
20801
|
-
|
|
20799
|
+
const mirror2 = Object.assign({}, opts.mirror);
|
|
20800
|
+
if (!mirror2.name.startsWith(types_1.kvPrefix)) {
|
|
20801
|
+
mirror2.name = `${types_1.kvPrefix}${mirror2.name}`;
|
|
20802
20802
|
}
|
|
20803
|
-
sc.mirror =
|
|
20803
|
+
sc.mirror = mirror2;
|
|
20804
20804
|
sc.mirror_direct = true;
|
|
20805
20805
|
} else if (opts.sources) {
|
|
20806
20806
|
const sources = opts.sources.map((s) => {
|
|
@@ -20863,17 +20863,17 @@ var require_kv = __commonJS({
|
|
|
20863
20863
|
this._prefixLen = 0;
|
|
20864
20864
|
this.prefix = `$KV.${this.bucket}`;
|
|
20865
20865
|
this.useJsPrefix = this.js.apiPrefix !== "$JS.API";
|
|
20866
|
-
const { mirror } = info.config;
|
|
20867
|
-
if (
|
|
20868
|
-
let n =
|
|
20866
|
+
const { mirror: mirror2 } = info.config;
|
|
20867
|
+
if (mirror2) {
|
|
20868
|
+
let n = mirror2.name;
|
|
20869
20869
|
if (n.startsWith(types_1.kvPrefix)) {
|
|
20870
20870
|
n = n.substring(types_1.kvPrefix.length);
|
|
20871
20871
|
}
|
|
20872
|
-
if (
|
|
20873
|
-
const mb =
|
|
20872
|
+
if (mirror2.external && mirror2.external.api !== "") {
|
|
20873
|
+
const mb = mirror2.name.substring(types_1.kvPrefix.length);
|
|
20874
20874
|
this.useJsPrefix = false;
|
|
20875
20875
|
this.prefix = `$KV.${mb}`;
|
|
20876
|
-
this.editPrefix = `${
|
|
20876
|
+
this.editPrefix = `${mirror2.external.api}.$KV.${n}`;
|
|
20877
20877
|
} else {
|
|
20878
20878
|
this.editPrefix = this.prefix;
|
|
20879
20879
|
}
|
|
@@ -47553,6 +47553,11 @@ function loadAgentFile(path) {
|
|
|
47553
47553
|
const kind = str("kind");
|
|
47554
47554
|
if (kind && kind !== "agent" && kind !== "endpoint")
|
|
47555
47555
|
throw new Error(`agent file ${path}: "kind" must be "agent" or "endpoint"`);
|
|
47556
|
+
const known = /* @__PURE__ */ new Set(["name", "role", "kind", "description", "tags", "channels", "publish", "model"]);
|
|
47557
|
+
const meta3 = {};
|
|
47558
|
+
for (const [k, v] of Object.entries(fm))
|
|
47559
|
+
if (!known.has(k) && typeof v === "string")
|
|
47560
|
+
meta3[k] = v;
|
|
47556
47561
|
return {
|
|
47557
47562
|
name,
|
|
47558
47563
|
role: str("role"),
|
|
@@ -47562,6 +47567,7 @@ function loadAgentFile(path) {
|
|
|
47562
47567
|
channels: list("channels"),
|
|
47563
47568
|
publish: list("publish"),
|
|
47564
47569
|
model: str("model"),
|
|
47570
|
+
meta: Object.keys(meta3).length ? meta3 : void 0,
|
|
47565
47571
|
persona: persona || void 0
|
|
47566
47572
|
};
|
|
47567
47573
|
}
|
|
@@ -47810,7 +47816,8 @@ var CotalEndpoint = class extends import_node_events.EventEmitter {
|
|
|
47810
47816
|
async requestControl(service, req, timeoutMs = 5e3) {
|
|
47811
47817
|
if (!this.nc)
|
|
47812
47818
|
throw new Error("endpoint not started");
|
|
47813
|
-
const
|
|
47819
|
+
const body = { ...req, from: req.from ?? this.ref() };
|
|
47820
|
+
const m = await this.nc.request(controlServiceSubject(this.space, service, this.card.id), JSON.stringify(body), { timeout: timeoutMs });
|
|
47814
47821
|
return m.json();
|
|
47815
47822
|
}
|
|
47816
47823
|
// ---- presence ------------------------------------------------------------
|
|
@@ -48509,6 +48516,11 @@ function describeStatusError(err2) {
|
|
|
48509
48516
|
return err2;
|
|
48510
48517
|
}
|
|
48511
48518
|
|
|
48519
|
+
// ../../packages/core/dist/spaces.js
|
|
48520
|
+
var import_transport_node4 = __toESM(require_transport_node(), 1);
|
|
48521
|
+
var import_jetstream3 = __toESM(require_mod4(), 1);
|
|
48522
|
+
var import_kv4 = __toESM(require_mod6(), 1);
|
|
48523
|
+
|
|
48512
48524
|
// ../../packages/core/dist/registry.js
|
|
48513
48525
|
var Registry = class {
|
|
48514
48526
|
#byKey = /* @__PURE__ */ new Map();
|
|
@@ -48827,6 +48839,38 @@ var MeshAgent = class extends import_node_events2.EventEmitter {
|
|
|
48827
48839
|
this.assertConnected();
|
|
48828
48840
|
return this.ep.requestControl("manager", { op: "start", args: { name, role } });
|
|
48829
48841
|
}
|
|
48842
|
+
/** Ask the manager to tear a teammate down (its `stop` op). Graceful by default —
|
|
48843
|
+
* the session is told to exit cleanly (so it leaves the mesh) before the
|
|
48844
|
+
* process/tab is closed; `graceful:false` is a hard, immediate kill. */
|
|
48845
|
+
async despawn(name, opts) {
|
|
48846
|
+
this.assertConnected();
|
|
48847
|
+
return this.ep.requestControl("manager", {
|
|
48848
|
+
op: "stop",
|
|
48849
|
+
args: { name, graceful: opts?.graceful ?? true }
|
|
48850
|
+
});
|
|
48851
|
+
}
|
|
48852
|
+
/** Ask the manager to purge the space's retained chat backlog (its `purge` op). Cleanup only —
|
|
48853
|
+
* it doesn't touch live agents or the anycast work queue. `includeDms` also clears DM history. */
|
|
48854
|
+
async purgeHistory(opts) {
|
|
48855
|
+
this.assertConnected();
|
|
48856
|
+
return this.ep.requestControl("manager", {
|
|
48857
|
+
op: "purge",
|
|
48858
|
+
args: { includeDms: opts?.includeDms ?? false }
|
|
48859
|
+
});
|
|
48860
|
+
}
|
|
48861
|
+
/** Define a persona and persist it as config (the manager's `definePersona` op writes
|
|
48862
|
+
* .cotal/agents/<name>.md). On success, announce it on the channel — the "send it out"
|
|
48863
|
+
* half — so peers see the new persona; `spawn(name)` then launches an agent wearing it. */
|
|
48864
|
+
async definePersona(def) {
|
|
48865
|
+
this.assertConnected();
|
|
48866
|
+
const reply = await this.ep.requestControl("manager", {
|
|
48867
|
+
op: "definePersona",
|
|
48868
|
+
args: { name: def.name, role: def.role, model: def.model, persona: def.prompt }
|
|
48869
|
+
});
|
|
48870
|
+
if (reply.ok)
|
|
48871
|
+
await this.send(`persona \`${def.name}\` is now available \u2014 spawn it to bring it online`);
|
|
48872
|
+
return reply;
|
|
48873
|
+
}
|
|
48830
48874
|
// ---- presence ------------------------------------------------------------
|
|
48831
48875
|
/** The full roster, including ourselves. */
|
|
48832
48876
|
roster() {
|
|
@@ -48927,6 +48971,8 @@ var err = (text) => ({ text, isError: true });
|
|
|
48927
48971
|
function statusGlyph(s) {
|
|
48928
48972
|
return s === "working" ? "\u25CF" : s === "waiting" ? "\u25D0" : s === "idle" ? "\u25CB" : "\xB7";
|
|
48929
48973
|
}
|
|
48974
|
+
var FACE_TAG_RE = /\[\[\s*face\s*:\s*[\w-]+\s*\]\]\s?/gi;
|
|
48975
|
+
var stripFaceTags = (text) => text.replace(FACE_TAG_RE, "");
|
|
48930
48976
|
var ATTENTION_DESC = {
|
|
48931
48977
|
open: "open \u2014 you receive everything; untagged channel chatter wakes you when idle",
|
|
48932
48978
|
dnd: "dnd \u2014 channel chatter no longer wakes you (it still arrives in your next turn); DMs, anycast, and @mentions still wake you",
|
|
@@ -49045,7 +49091,7 @@ ${all.map(fmtItem).join("\n")}`);
|
|
|
49045
49091
|
},
|
|
49046
49092
|
async run(agent, _config, { text: msg, channel, mentions }) {
|
|
49047
49093
|
try {
|
|
49048
|
-
const m = await agent.send(msg, channel, mentions);
|
|
49094
|
+
const m = await agent.send(stripFaceTags(msg), channel, mentions);
|
|
49049
49095
|
return ok(`Sent to #${m.channel}${m.mentions?.length ? ` (mentioned @${m.mentions.join(", @")})` : ""}.`);
|
|
49050
49096
|
} catch (e) {
|
|
49051
49097
|
return err(`Couldn't send: ${e.message}`);
|
|
@@ -49062,7 +49108,7 @@ ${all.map(fmtItem).join("\n")}`);
|
|
|
49062
49108
|
},
|
|
49063
49109
|
async run(agent, _config, { to, text: msg }) {
|
|
49064
49110
|
try {
|
|
49065
|
-
const { peer } = await agent.dm(to, msg);
|
|
49111
|
+
const { peer } = await agent.dm(to, stripFaceTags(msg));
|
|
49066
49112
|
return ok(`DM sent to ${peer.card.name}.`);
|
|
49067
49113
|
} catch (e) {
|
|
49068
49114
|
return err(`Couldn't DM: ${e.message}`);
|
|
@@ -49079,7 +49125,7 @@ ${all.map(fmtItem).join("\n")}`);
|
|
|
49079
49125
|
},
|
|
49080
49126
|
async run(agent, _config, { role, text: msg }) {
|
|
49081
49127
|
try {
|
|
49082
|
-
await agent.anycast(role, msg);
|
|
49128
|
+
await agent.anycast(role, stripFaceTags(msg));
|
|
49083
49129
|
return ok(`Sent to one @${role}.`);
|
|
49084
49130
|
} catch (e) {
|
|
49085
49131
|
return err(`Couldn't send: ${e.message}`);
|
|
@@ -49212,13 +49258,14 @@ ${info}${caught}`);
|
|
|
49212
49258
|
schema: {
|
|
49213
49259
|
origin: external_exports.enum(["human", "agent"]).describe(`"human" when relaying the user's feedback, "agent" when reporting an issue you hit yourself.`),
|
|
49214
49260
|
type: external_exports.enum(["bug", "idea", "friction", "praise", "other"]).describe("What kind of feedback this is."),
|
|
49215
|
-
summary: external_exports.string().max(
|
|
49216
|
-
details: external_exports.string().max(1e4).optional().describe("Longer free-form details."),
|
|
49261
|
+
summary: external_exports.string().max(300).describe("Required one-line summary, max 300 characters."),
|
|
49262
|
+
details: external_exports.string().max(1e4).optional().describe("Longer free-form details. Do not include secrets."),
|
|
49217
49263
|
severity: external_exports.enum(["low", "medium", "high"]).optional().describe("How badly this hurts (bugs/friction)."),
|
|
49218
|
-
area: external_exports.string().optional().describe("The part of Cotal this concerns (e.g. presence, channels, CLI)."),
|
|
49219
|
-
repro: external_exports.string().optional().describe("Steps to reproduce."),
|
|
49220
|
-
expected: external_exports.string().optional().describe("What you expected to happen."),
|
|
49221
|
-
actual: external_exports.string().optional().describe("What actually happened."),
|
|
49264
|
+
area: external_exports.string().max(120).optional().describe("The part of Cotal this concerns (e.g. presence, channels, CLI)."),
|
|
49265
|
+
repro: external_exports.string().max(1e4).optional().describe("Steps to reproduce."),
|
|
49266
|
+
expected: external_exports.string().max(5e3).optional().describe("What you expected to happen."),
|
|
49267
|
+
actual: external_exports.string().max(5e3).optional().describe("What actually happened."),
|
|
49268
|
+
diagnostics: external_exports.string().max(1e4).optional().describe("Relevant diagnostics as text (logs, errors). Never include secrets."),
|
|
49222
49269
|
email: external_exports.string().optional().describe("Contact email \u2014 required on the keyless public path when none is configured in the environment.")
|
|
49223
49270
|
},
|
|
49224
49271
|
async run(_agent, _config, args) {
|
|
@@ -49236,14 +49283,83 @@ ${info}${caught}`);
|
|
|
49236
49283
|
}
|
|
49237
49284
|
try {
|
|
49238
49285
|
const res = await fetch(url2, { method: "POST", headers, body: JSON.stringify(body) });
|
|
49239
|
-
const
|
|
49286
|
+
const raw = await res.text();
|
|
49287
|
+
let reply = {};
|
|
49288
|
+
if (raw)
|
|
49289
|
+
try {
|
|
49290
|
+
reply = JSON.parse(raw);
|
|
49291
|
+
} catch {
|
|
49292
|
+
reply = { error: raw };
|
|
49293
|
+
}
|
|
49240
49294
|
if (!res.ok)
|
|
49241
49295
|
return err(`Feedback rejected (${res.status}${reply.error ? `: ${reply.error}` : ""}).`);
|
|
49242
|
-
|
|
49296
|
+
const note = reply.published === false ? " (stored, but the internal feedback channel publish failed)" : "";
|
|
49297
|
+
return ok(`Feedback sent${reply.id ? ` (id ${reply.id})` : ""}${note}. Thanks!`);
|
|
49243
49298
|
} catch (e) {
|
|
49244
49299
|
return err(`Couldn't reach the feedback intake at ${url2}: ${e.message}`);
|
|
49245
49300
|
}
|
|
49246
49301
|
}
|
|
49302
|
+
},
|
|
49303
|
+
{
|
|
49304
|
+
name: "cotal_despawn",
|
|
49305
|
+
title: "Cotal: stop a teammate",
|
|
49306
|
+
description: "Ask the manager to tear a teammate down \u2014 it leaves the mesh and its process/tab is closed. Graceful by default (the session exits cleanly first); pass graceful:false for a hard, immediate kill. The inverse of cotal_spawn.",
|
|
49307
|
+
schema: {
|
|
49308
|
+
name: external_exports.string().describe("Name of the peer to stop."),
|
|
49309
|
+
graceful: external_exports.boolean().optional().describe("Default true: let the session exit cleanly. false = hard kill.")
|
|
49310
|
+
},
|
|
49311
|
+
async run(agent, _config, { name, graceful }) {
|
|
49312
|
+
try {
|
|
49313
|
+
const reply = await agent.despawn(name, { graceful });
|
|
49314
|
+
if (!reply.ok)
|
|
49315
|
+
return err(`Couldn't despawn ${name}: ${reply.error ?? "manager refused"}`);
|
|
49316
|
+
return ok(`Stopping ${name}${graceful === false ? " (hard)" : ""} \u2014 it will leave the roster shortly.`);
|
|
49317
|
+
} catch (e) {
|
|
49318
|
+
return err(`Couldn't despawn ${name}: no manager reachable (${e.message}). Is the manager running?`);
|
|
49319
|
+
}
|
|
49320
|
+
}
|
|
49321
|
+
},
|
|
49322
|
+
{
|
|
49323
|
+
name: "cotal_purge",
|
|
49324
|
+
title: "Cotal: clear chat history",
|
|
49325
|
+
description: "Ask the manager to purge this space's retained chat backlog (channel history). Set includeDms to also clear direct-message history. Cleanup only \u2014 it does not affect live agents or the anycast work queue. Irreversible.",
|
|
49326
|
+
schema: {
|
|
49327
|
+
includeDms: external_exports.boolean().optional().describe("Default false: channel history only. true = also purge DM history.")
|
|
49328
|
+
},
|
|
49329
|
+
async run(agent, _config, { includeDms }) {
|
|
49330
|
+
try {
|
|
49331
|
+
const reply = await agent.purgeHistory({ includeDms });
|
|
49332
|
+
if (!reply.ok)
|
|
49333
|
+
return err(`Couldn't purge history: ${reply.error ?? "manager refused"}`);
|
|
49334
|
+
const d = reply.data;
|
|
49335
|
+
const chat = d?.chat ?? 0;
|
|
49336
|
+
const dm = d?.dm;
|
|
49337
|
+
return ok(`Cleared ${chat} channel message${chat === 1 ? "" : "s"}${dm === void 0 ? "" : ` and ${dm} DM${dm === 1 ? "" : "s"}`} from "${_config.space}".`);
|
|
49338
|
+
} catch (e) {
|
|
49339
|
+
return err(`Couldn't purge history: no manager reachable (${e.message}). Is the manager running?`);
|
|
49340
|
+
}
|
|
49341
|
+
}
|
|
49342
|
+
},
|
|
49343
|
+
{
|
|
49344
|
+
name: "cotal_persona",
|
|
49345
|
+
title: "Cotal: define a persona",
|
|
49346
|
+
description: "Define a new persona and save it as config (the manager writes .cotal/agents/<name>.md), then announce it on the mesh. Afterwards cotal_spawn(name) launches a real agent wearing this persona/model. Use to grow the team with a custom role you describe on the fly.",
|
|
49347
|
+
schema: {
|
|
49348
|
+
name: external_exports.string().regex(/^[A-Za-z0-9_-]+$/, "letters, digits, _ or - only").describe("Unique name for the persona (also the spawn name): letters, digits, _ or -."),
|
|
49349
|
+
prompt: external_exports.string().max(1e4).describe("The persona \u2014 an appended system prompt describing who this agent is."),
|
|
49350
|
+
role: external_exports.string().max(120).optional().describe("Optional role label (e.g. reviewer, scout)."),
|
|
49351
|
+
model: external_exports.string().max(120).optional().describe("Optional model override (e.g. opus, sonnet).")
|
|
49352
|
+
},
|
|
49353
|
+
async run(agent, _config, { name, prompt, role, model }) {
|
|
49354
|
+
try {
|
|
49355
|
+
const reply = await agent.definePersona({ name, prompt, role, model });
|
|
49356
|
+
if (!reply.ok)
|
|
49357
|
+
return err(`Couldn't define ${name}: ${reply.error ?? "manager refused"}`);
|
|
49358
|
+
return ok(`Persona \`${name}\` saved \u2014 spawn it with cotal_spawn(name="${name}") to bring it online.`);
|
|
49359
|
+
} catch (e) {
|
|
49360
|
+
return err(`Couldn't define ${name}: no manager reachable (${e.message}). Is the manager running?`);
|
|
49361
|
+
}
|
|
49362
|
+
}
|
|
49247
49363
|
}
|
|
49248
49364
|
];
|
|
49249
49365
|
}
|
|
@@ -49322,13 +49438,176 @@ function startControlServer(agent, socketPath, handle) {
|
|
|
49322
49438
|
return server;
|
|
49323
49439
|
}
|
|
49324
49440
|
|
|
49441
|
+
// src/transcript.ts
|
|
49442
|
+
var import_node_fs4 = require("node:fs");
|
|
49443
|
+
var MAX_PREVIEW = 700;
|
|
49444
|
+
var MAX_CHUNK = 6e3;
|
|
49445
|
+
function transcriptChannel(name) {
|
|
49446
|
+
return `tr-${name.toLowerCase().replace(/[^a-z0-9_-]+/g, "-")}`;
|
|
49447
|
+
}
|
|
49448
|
+
function truncate(s, max) {
|
|
49449
|
+
return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
|
|
49450
|
+
}
|
|
49451
|
+
function salient(input) {
|
|
49452
|
+
const i = input ?? {};
|
|
49453
|
+
const v = i.command ?? i.file_path ?? i.path ?? i.url ?? i.pattern ?? i.description;
|
|
49454
|
+
const s = typeof v === "string" ? v : Object.keys(i).length ? JSON.stringify(i) : "";
|
|
49455
|
+
return s ? `: ${truncate(s, 300)}` : "";
|
|
49456
|
+
}
|
|
49457
|
+
function resultText(content) {
|
|
49458
|
+
if (typeof content === "string") return content;
|
|
49459
|
+
if (Array.isArray(content))
|
|
49460
|
+
return content.map((b) => typeof b?.text === "string" ? b.text : "").filter(Boolean).join("\n");
|
|
49461
|
+
return "";
|
|
49462
|
+
}
|
|
49463
|
+
function condense(line) {
|
|
49464
|
+
let e;
|
|
49465
|
+
try {
|
|
49466
|
+
e = JSON.parse(line);
|
|
49467
|
+
} catch {
|
|
49468
|
+
return [];
|
|
49469
|
+
}
|
|
49470
|
+
if (!e || e.isMeta) return [];
|
|
49471
|
+
const content = e.message?.content;
|
|
49472
|
+
if (e.type === "assistant" && Array.isArray(content)) {
|
|
49473
|
+
const out = [];
|
|
49474
|
+
for (const b of content) {
|
|
49475
|
+
if (b.type === "text" && b.text?.trim()) out.push(b.text.trim());
|
|
49476
|
+
else if (b.type === "tool_use" && b.name) out.push(`\u2692 ${b.name}${salient(b.input)}`);
|
|
49477
|
+
}
|
|
49478
|
+
return out;
|
|
49479
|
+
}
|
|
49480
|
+
if (e.type === "user") {
|
|
49481
|
+
if (typeof content === "string")
|
|
49482
|
+
return content.trim() ? [`\xBB ${truncate(content.trim(), MAX_PREVIEW)}`] : [];
|
|
49483
|
+
if (Array.isArray(content)) {
|
|
49484
|
+
const out = [];
|
|
49485
|
+
for (const b of content) {
|
|
49486
|
+
if (b.type === "tool_result") {
|
|
49487
|
+
const t = resultText(b.content).trim();
|
|
49488
|
+
out.push(`\u2192 ${b.is_error ? "ERROR: " : ""}${t ? truncate(t, MAX_PREVIEW) : "(no output)"}`);
|
|
49489
|
+
} else if (b.type === "text" && b.text?.trim()) {
|
|
49490
|
+
out.push(`\xBB ${truncate(b.text.trim(), MAX_PREVIEW)}`);
|
|
49491
|
+
}
|
|
49492
|
+
}
|
|
49493
|
+
return out;
|
|
49494
|
+
}
|
|
49495
|
+
}
|
|
49496
|
+
return [];
|
|
49497
|
+
}
|
|
49498
|
+
var TranscriptMirror = class {
|
|
49499
|
+
constructor(agent, channel) {
|
|
49500
|
+
this.agent = agent;
|
|
49501
|
+
this.channel = channel;
|
|
49502
|
+
}
|
|
49503
|
+
agent;
|
|
49504
|
+
channel;
|
|
49505
|
+
path;
|
|
49506
|
+
offset = 0;
|
|
49507
|
+
/** A batch that failed mid-publish: chunks + how many already landed. Retried (from the
|
|
49508
|
+
* first unsent chunk — never re-sending a delivered one) before any new read. */
|
|
49509
|
+
pending;
|
|
49510
|
+
/** ALL path/offset mutation and publishing runs on this serialized chain — hook events
|
|
49511
|
+
* land concurrently on the control socket. */
|
|
49512
|
+
chain = Promise.resolve();
|
|
49513
|
+
/** Adopt the transcript at its CURRENT end — mirror only what happens from now on, so a
|
|
49514
|
+
* resumed session (or a mirror that first sees the path mid-session) never rebroadcasts. */
|
|
49515
|
+
adopt(path) {
|
|
49516
|
+
this.enqueue(() => {
|
|
49517
|
+
this.adoptNow(path);
|
|
49518
|
+
return Promise.resolve();
|
|
49519
|
+
});
|
|
49520
|
+
}
|
|
49521
|
+
/** Queue a flush of new transcript entries to the channel. Never throws, never blocks the
|
|
49522
|
+
* hook reply — the read+publish runs on the serialized chain. */
|
|
49523
|
+
flush(path) {
|
|
49524
|
+
this.enqueue(() => {
|
|
49525
|
+
if (!this.path) this.adoptNow(path);
|
|
49526
|
+
return this.doFlush();
|
|
49527
|
+
});
|
|
49528
|
+
}
|
|
49529
|
+
enqueue(step) {
|
|
49530
|
+
this.chain = this.chain.then(step).catch((e) => {
|
|
49531
|
+
process.stderr.write(`[cotal-connector] transcript mirror: ${e.message}
|
|
49532
|
+
`);
|
|
49533
|
+
});
|
|
49534
|
+
}
|
|
49535
|
+
adoptNow(path) {
|
|
49536
|
+
if (typeof path !== "string" || !path || this.path === path) return;
|
|
49537
|
+
this.path = path;
|
|
49538
|
+
this.pending = void 0;
|
|
49539
|
+
try {
|
|
49540
|
+
this.offset = (0, import_node_fs4.statSync)(path).size;
|
|
49541
|
+
} catch {
|
|
49542
|
+
this.offset = 0;
|
|
49543
|
+
}
|
|
49544
|
+
}
|
|
49545
|
+
async doFlush() {
|
|
49546
|
+
if (!this.path || !this.agent.connected) return;
|
|
49547
|
+
if (!this.pending) {
|
|
49548
|
+
const { lines, nextOffset } = this.readComplete();
|
|
49549
|
+
if (nextOffset === this.offset) return;
|
|
49550
|
+
this.pending = { chunks: chunkLines(lines.flatMap(condense), MAX_CHUNK), sent: 0, nextOffset };
|
|
49551
|
+
}
|
|
49552
|
+
const p = this.pending;
|
|
49553
|
+
while (p.sent < p.chunks.length) {
|
|
49554
|
+
await this.agent.send(p.chunks[p.sent], this.channel);
|
|
49555
|
+
p.sent++;
|
|
49556
|
+
}
|
|
49557
|
+
this.offset = p.nextOffset;
|
|
49558
|
+
this.pending = void 0;
|
|
49559
|
+
}
|
|
49560
|
+
/** New complete lines since the offset (a trailing partial line stays for the next flush). */
|
|
49561
|
+
readComplete() {
|
|
49562
|
+
const none = () => ({ lines: [], nextOffset: this.offset });
|
|
49563
|
+
let fd;
|
|
49564
|
+
try {
|
|
49565
|
+
fd = (0, import_node_fs4.openSync)(this.path, "r");
|
|
49566
|
+
} catch {
|
|
49567
|
+
return none();
|
|
49568
|
+
}
|
|
49569
|
+
try {
|
|
49570
|
+
const size = (0, import_node_fs4.fstatSync)(fd).size;
|
|
49571
|
+
if (size < this.offset) this.offset = 0;
|
|
49572
|
+
if (size === this.offset) return none();
|
|
49573
|
+
const buf = Buffer.alloc(size - this.offset);
|
|
49574
|
+
(0, import_node_fs4.readSync)(fd, buf, 0, buf.length, this.offset);
|
|
49575
|
+
const text = buf.toString("utf8");
|
|
49576
|
+
const lastNl = text.lastIndexOf("\n");
|
|
49577
|
+
if (lastNl < 0) return none();
|
|
49578
|
+
return {
|
|
49579
|
+
lines: text.slice(0, lastNl).split("\n").filter(Boolean),
|
|
49580
|
+
nextOffset: this.offset + Buffer.byteLength(text.slice(0, lastNl + 1), "utf8")
|
|
49581
|
+
};
|
|
49582
|
+
} finally {
|
|
49583
|
+
(0, import_node_fs4.closeSync)(fd);
|
|
49584
|
+
}
|
|
49585
|
+
}
|
|
49586
|
+
};
|
|
49587
|
+
function chunkLines(lines, max) {
|
|
49588
|
+
const chunks = [];
|
|
49589
|
+
let cur = "";
|
|
49590
|
+
for (const line of lines) {
|
|
49591
|
+
if (cur && cur.length + 1 + line.length > max) {
|
|
49592
|
+
chunks.push(cur);
|
|
49593
|
+
cur = line;
|
|
49594
|
+
} else {
|
|
49595
|
+
cur = cur ? `${cur}
|
|
49596
|
+
${line}` : line;
|
|
49597
|
+
}
|
|
49598
|
+
}
|
|
49599
|
+
if (cur) chunks.push(cur);
|
|
49600
|
+
return chunks;
|
|
49601
|
+
}
|
|
49602
|
+
|
|
49325
49603
|
// src/mcp.ts
|
|
49604
|
+
var mirror;
|
|
49326
49605
|
var pendingTool;
|
|
49327
49606
|
function toolDetail(name, input) {
|
|
49328
49607
|
if (typeof name !== "string" || !name) return void 0;
|
|
49329
49608
|
const i = input ?? {};
|
|
49330
|
-
const
|
|
49331
|
-
let detail = typeof
|
|
49609
|
+
const salient2 = i.command ?? i.file_path ?? i.path ?? i.url ?? i.pattern ?? i.description;
|
|
49610
|
+
let detail = typeof salient2 === "string" ? salient2 : Object.keys(i).length ? JSON.stringify(i) : "";
|
|
49332
49611
|
if (detail.length > 300) detail = `${detail.slice(0, 299)}\u2026`;
|
|
49333
49612
|
return { name, detail };
|
|
49334
49613
|
}
|
|
@@ -49338,6 +49617,7 @@ var claudeHandle = async (agent, ev) => {
|
|
|
49338
49617
|
try {
|
|
49339
49618
|
switch (event) {
|
|
49340
49619
|
case "SessionStart": {
|
|
49620
|
+
mirror?.adopt(ev.transcript_path);
|
|
49341
49621
|
await agent.setStatus("idle");
|
|
49342
49622
|
await agent.setAttention("open");
|
|
49343
49623
|
const parts = [agent.channelBriefing(), formatInjection(agent.drainInbox())].filter(Boolean);
|
|
@@ -49345,10 +49625,12 @@ var claudeHandle = async (agent, ev) => {
|
|
|
49345
49625
|
}
|
|
49346
49626
|
case "UserPromptSubmit":
|
|
49347
49627
|
pendingTool = void 0;
|
|
49628
|
+
mirror?.flush(ev.transcript_path);
|
|
49348
49629
|
await agent.setStatus("working");
|
|
49349
49630
|
return withContext(formatInjection(agent.drainInbox()));
|
|
49350
49631
|
case "PreToolUse":
|
|
49351
49632
|
pendingTool = toolDetail(ev.tool_name, ev.tool_input);
|
|
49633
|
+
mirror?.flush(ev.transcript_path);
|
|
49352
49634
|
return {};
|
|
49353
49635
|
case "Notification": {
|
|
49354
49636
|
const msg = typeof ev.message === "string" ? ev.message : void 0;
|
|
@@ -49359,11 +49641,13 @@ var claudeHandle = async (agent, ev) => {
|
|
|
49359
49641
|
case "Stop":
|
|
49360
49642
|
case "StopFailure":
|
|
49361
49643
|
pendingTool = void 0;
|
|
49644
|
+
mirror?.flush(ev.transcript_path);
|
|
49362
49645
|
await agent.setStatus("idle");
|
|
49363
49646
|
const pending = agent.attention === "open" ? agent.inboxCount() : agent.directedPendingCount();
|
|
49364
49647
|
if (pending > 0) agent.requestWake();
|
|
49365
49648
|
return {};
|
|
49366
49649
|
case "SessionEnd":
|
|
49650
|
+
mirror?.flush(ev.transcript_path);
|
|
49367
49651
|
await agent.setStatus("offline");
|
|
49368
49652
|
return {};
|
|
49369
49653
|
default:
|
|
@@ -49381,6 +49665,8 @@ async function main() {
|
|
|
49381
49665
|
const config2 = configFromEnv();
|
|
49382
49666
|
const agent = new MeshAgent(config2);
|
|
49383
49667
|
agent.start();
|
|
49668
|
+
if (/^(1|true|yes|on)$/i.test(process.env.COTAL_TRANSCRIPT ?? ""))
|
|
49669
|
+
mirror = new TranscriptMirror(agent, transcriptChannel(config2.name));
|
|
49384
49670
|
const socketPath = controlSocketPath(config2.space, config2.name);
|
|
49385
49671
|
const controlServer = startControlServer(agent, socketPath, claudeHandle);
|
|
49386
49672
|
const server = new McpServer(
|
package/dist/mcp.js
CHANGED
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
12
12
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13
13
|
import { configFromEnv, hasIdentity, MeshAgent, controlSocketPath, startControlServer, registerCotalTools, laneLine, feedbackLine, formatInjection, fmtFrom, channelMeta, } from "@cotal-ai/connector-core";
|
|
14
|
+
import { TranscriptMirror, transcriptChannel } from "./transcript.js";
|
|
15
|
+
/** Mirrors this session's transcript to `tr-<name>` — set in main() iff COTAL_TRANSCRIPT
|
|
16
|
+
* is on (buildLaunch sets it for managed sessions; personal sessions never mirror). */
|
|
17
|
+
let mirror;
|
|
14
18
|
/**
|
|
15
19
|
* Last tool Claude tried to use, captured on PreToolUse. When a permission Notification
|
|
16
20
|
* fires moments later, this is *what* it's blocked on — so the dashboard shows the actual
|
|
@@ -35,6 +39,7 @@ const claudeHandle = async (agent, ev) => {
|
|
|
35
39
|
try {
|
|
36
40
|
switch (event) {
|
|
37
41
|
case "SessionStart": {
|
|
42
|
+
mirror?.adopt(ev.transcript_path); // mirror from HERE — a resumed session never rebroadcasts
|
|
38
43
|
await agent.setStatus("idle");
|
|
39
44
|
await agent.setAttention("open"); // F3: reset to fail-open on every (re)start — a crashed/restarted agent must not stay silently deaf
|
|
40
45
|
// Boot push: a one-line note per subscribed channel (if the registry has loaded),
|
|
@@ -44,12 +49,14 @@ const claudeHandle = async (agent, ev) => {
|
|
|
44
49
|
}
|
|
45
50
|
case "UserPromptSubmit":
|
|
46
51
|
pendingTool = undefined; // new turn — the previous block (if any) is resolved
|
|
52
|
+
mirror?.flush(ev.transcript_path);
|
|
47
53
|
await agent.setStatus("working");
|
|
48
54
|
return withContext(formatInjection(agent.drainInbox()));
|
|
49
55
|
case "PreToolUse":
|
|
50
56
|
// Remember what Claude is about to do; if it needs permission, the Notification
|
|
51
57
|
// below turns this into the "blocked on" detail. Auto-approved tools just overwrite it.
|
|
52
58
|
pendingTool = toolDetail(ev.tool_name, ev.tool_input);
|
|
59
|
+
mirror?.flush(ev.transcript_path); // near-live mirror: each tool boundary ships the turn so far
|
|
53
60
|
return {};
|
|
54
61
|
case "Notification": {
|
|
55
62
|
// Claude Code's Notification carries the human-readable reason the session is
|
|
@@ -67,6 +74,7 @@ const claudeHandle = async (agent, ev) => {
|
|
|
67
74
|
case "Stop":
|
|
68
75
|
case "StopFailure": // turn died on an API error — Stop won't fire, so reset here too
|
|
69
76
|
pendingTool = undefined; // turn ended — don't let a stale tool attach to an idle-wait notification
|
|
77
|
+
mirror?.flush(ev.transcript_path);
|
|
70
78
|
await agent.setStatus("idle");
|
|
71
79
|
// Now idle: if ambient channel chatter was held while we were busy, ask the channel to
|
|
72
80
|
// wake one turn so its UserPromptSubmit drains+acks the batch. (Ack sites are two:
|
|
@@ -80,6 +88,7 @@ const claudeHandle = async (agent, ev) => {
|
|
|
80
88
|
agent.requestWake();
|
|
81
89
|
return {};
|
|
82
90
|
case "SessionEnd":
|
|
91
|
+
mirror?.flush(ev.transcript_path); // best-effort — the process may exit before it lands
|
|
83
92
|
await agent.setStatus("offline");
|
|
84
93
|
return {};
|
|
85
94
|
default:
|
|
@@ -101,6 +110,8 @@ async function main() {
|
|
|
101
110
|
const config = configFromEnv();
|
|
102
111
|
const agent = new MeshAgent(config);
|
|
103
112
|
agent.start(); // background connect with retry — never blocks tool serving
|
|
113
|
+
if (/^(1|true|yes|on)$/i.test(process.env.COTAL_TRANSCRIPT ?? ""))
|
|
114
|
+
mirror = new TranscriptMirror(agent, transcriptChannel(config.name));
|
|
104
115
|
// Local control plane for the lifecycle hooks (presence + message injection).
|
|
105
116
|
const socketPath = controlSocketPath(config.space, config.name);
|
|
106
117
|
const controlServer = startControlServer(agent, socketPath, claudeHandle);
|
package/dist/mcp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,aAAa,EACb,WAAW,EACX,SAAS,EACT,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,QAAQ,EACR,YAAY,EACZ,eAAe,EACf,OAAO,EACP,WAAW,GAGZ,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { MeshAgent } from "@cotal-ai/connector-core";
|
|
2
|
+
/** `tr-<name>` with the name reduced to subject-safe channel characters. */
|
|
3
|
+
export declare function transcriptChannel(name: string): string;
|
|
4
|
+
export declare class TranscriptMirror {
|
|
5
|
+
private readonly agent;
|
|
6
|
+
private readonly channel;
|
|
7
|
+
private path?;
|
|
8
|
+
private offset;
|
|
9
|
+
/** A batch that failed mid-publish: chunks + how many already landed. Retried (from the
|
|
10
|
+
* first unsent chunk — never re-sending a delivered one) before any new read. */
|
|
11
|
+
private pending?;
|
|
12
|
+
/** ALL path/offset mutation and publishing runs on this serialized chain — hook events
|
|
13
|
+
* land concurrently on the control socket. */
|
|
14
|
+
private chain;
|
|
15
|
+
constructor(agent: MeshAgent, channel: string);
|
|
16
|
+
/** Adopt the transcript at its CURRENT end — mirror only what happens from now on, so a
|
|
17
|
+
* resumed session (or a mirror that first sees the path mid-session) never rebroadcasts. */
|
|
18
|
+
adopt(path: unknown): void;
|
|
19
|
+
/** Queue a flush of new transcript entries to the channel. Never throws, never blocks the
|
|
20
|
+
* hook reply — the read+publish runs on the serialized chain. */
|
|
21
|
+
flush(path: unknown): void;
|
|
22
|
+
private enqueue;
|
|
23
|
+
private adoptNow;
|
|
24
|
+
private doFlush;
|
|
25
|
+
/** New complete lines since the offset (a trailing partial line stays for the next flush). */
|
|
26
|
+
private readComplete;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=transcript.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript.d.ts","sourceRoot":"","sources":["../src/transcript.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAK1D,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtD;AA8DD,qBAAa,gBAAgB;IAWzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAX1B,OAAO,CAAC,IAAI,CAAC,CAAS;IACtB,OAAO,CAAC,MAAM,CAAK;IACnB;sFACkF;IAClF,OAAO,CAAC,OAAO,CAAC,CAAyD;IACzE;mDAC+C;IAC/C,OAAO,CAAC,KAAK,CAAoC;gBAG9B,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,MAAM;IAGlC;iGAC6F;IAC7F,KAAK,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAO1B;sEACkE;IAClE,KAAK,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAO1B,OAAO,CAAC,OAAO;IAMf,OAAO,CAAC,QAAQ;YAWF,OAAO;IAmBrB,8FAA8F;IAC9F,OAAO,CAAC,YAAY;CAyBrB"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transcript mirror — publishes this session's own Claude Code transcript (the JSONL
|
|
3
|
+
* behind the hooks' `transcript_path`) onto a per-agent mesh channel, `tr-<name>`, so
|
|
4
|
+
* peers and observer agents can read what the agent is ACTUALLY doing, not only what
|
|
5
|
+
* it chooses to narrate.
|
|
6
|
+
*
|
|
7
|
+
* Off unless the launch sets COTAL_TRANSCRIPT (the connector's buildLaunch sets it for
|
|
8
|
+
* managed sessions; a personal session never mirrors). Flushes ride the lifecycle hooks:
|
|
9
|
+
* read the JSONL from the last offset, condense each entry to its observable surface
|
|
10
|
+
* (assistant text in full, tool calls as one-liners, tool results truncated, thinking
|
|
11
|
+
* omitted), and multicast the batch. The offset starts at end-of-file on adopt, so a
|
|
12
|
+
* resumed session never rebroadcasts history. Publishing is at-least-once: the offset
|
|
13
|
+
* commits only after a successful publish, so a mesh outage replays the batch rather
|
|
14
|
+
* than losing it.
|
|
15
|
+
*/
|
|
16
|
+
import { closeSync, fstatSync, openSync, readSync, statSync } from "node:fs";
|
|
17
|
+
const MAX_PREVIEW = 700; // tool results / user prompts — enough to see what happened
|
|
18
|
+
const MAX_CHUNK = 6000; // chars per published message; batches split on entry boundaries
|
|
19
|
+
/** `tr-<name>` with the name reduced to subject-safe channel characters. */
|
|
20
|
+
export function transcriptChannel(name) {
|
|
21
|
+
return `tr-${name.toLowerCase().replace(/[^a-z0-9_-]+/g, "-")}`;
|
|
22
|
+
}
|
|
23
|
+
function truncate(s, max) {
|
|
24
|
+
return s.length > max ? `${s.slice(0, max - 1)}…` : s;
|
|
25
|
+
}
|
|
26
|
+
/** A tool call's most salient input, mirroring the presence preview in mcp.ts. */
|
|
27
|
+
function salient(input) {
|
|
28
|
+
const i = (input ?? {});
|
|
29
|
+
const v = i.command ?? i.file_path ?? i.path ?? i.url ?? i.pattern ?? i.description;
|
|
30
|
+
const s = typeof v === "string" ? v : Object.keys(i).length ? JSON.stringify(i) : "";
|
|
31
|
+
return s ? `: ${truncate(s, 300)}` : "";
|
|
32
|
+
}
|
|
33
|
+
/** Flatten a tool_result's content (string or text-block array) to plain text. */
|
|
34
|
+
function resultText(content) {
|
|
35
|
+
if (typeof content === "string")
|
|
36
|
+
return content;
|
|
37
|
+
if (Array.isArray(content))
|
|
38
|
+
return content
|
|
39
|
+
.map((b) => (typeof b?.text === "string" ? b.text : ""))
|
|
40
|
+
.filter(Boolean)
|
|
41
|
+
.join("\n");
|
|
42
|
+
return "";
|
|
43
|
+
}
|
|
44
|
+
/** One JSONL entry → the lines worth mirroring (empty for meta/system/thinking-only entries). */
|
|
45
|
+
function condense(line) {
|
|
46
|
+
let e;
|
|
47
|
+
try {
|
|
48
|
+
e = JSON.parse(line);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
if (!e || e.isMeta)
|
|
54
|
+
return [];
|
|
55
|
+
const content = e.message?.content;
|
|
56
|
+
if (e.type === "assistant" && Array.isArray(content)) {
|
|
57
|
+
const out = [];
|
|
58
|
+
for (const b of content) {
|
|
59
|
+
if (b.type === "text" && b.text?.trim())
|
|
60
|
+
out.push(b.text.trim());
|
|
61
|
+
else if (b.type === "tool_use" && b.name)
|
|
62
|
+
out.push(`⚒ ${b.name}${salient(b.input)}`);
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
if (e.type === "user") {
|
|
67
|
+
if (typeof content === "string")
|
|
68
|
+
return content.trim() ? [`» ${truncate(content.trim(), MAX_PREVIEW)}`] : [];
|
|
69
|
+
if (Array.isArray(content)) {
|
|
70
|
+
const out = [];
|
|
71
|
+
for (const b of content) {
|
|
72
|
+
if (b.type === "tool_result") {
|
|
73
|
+
const t = resultText(b.content).trim();
|
|
74
|
+
out.push(`→ ${b.is_error ? "ERROR: " : ""}${t ? truncate(t, MAX_PREVIEW) : "(no output)"}`);
|
|
75
|
+
}
|
|
76
|
+
else if (b.type === "text" && b.text?.trim()) {
|
|
77
|
+
out.push(`» ${truncate(b.text.trim(), MAX_PREVIEW)}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
export class TranscriptMirror {
|
|
86
|
+
agent;
|
|
87
|
+
channel;
|
|
88
|
+
path;
|
|
89
|
+
offset = 0;
|
|
90
|
+
/** A batch that failed mid-publish: chunks + how many already landed. Retried (from the
|
|
91
|
+
* first unsent chunk — never re-sending a delivered one) before any new read. */
|
|
92
|
+
pending;
|
|
93
|
+
/** ALL path/offset mutation and publishing runs on this serialized chain — hook events
|
|
94
|
+
* land concurrently on the control socket. */
|
|
95
|
+
chain = Promise.resolve();
|
|
96
|
+
constructor(agent, channel) {
|
|
97
|
+
this.agent = agent;
|
|
98
|
+
this.channel = channel;
|
|
99
|
+
}
|
|
100
|
+
/** Adopt the transcript at its CURRENT end — mirror only what happens from now on, so a
|
|
101
|
+
* resumed session (or a mirror that first sees the path mid-session) never rebroadcasts. */
|
|
102
|
+
adopt(path) {
|
|
103
|
+
this.enqueue(() => {
|
|
104
|
+
this.adoptNow(path);
|
|
105
|
+
return Promise.resolve();
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
/** Queue a flush of new transcript entries to the channel. Never throws, never blocks the
|
|
109
|
+
* hook reply — the read+publish runs on the serialized chain. */
|
|
110
|
+
flush(path) {
|
|
111
|
+
this.enqueue(() => {
|
|
112
|
+
if (!this.path)
|
|
113
|
+
this.adoptNow(path);
|
|
114
|
+
return this.doFlush();
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
enqueue(step) {
|
|
118
|
+
this.chain = this.chain.then(step).catch((e) => {
|
|
119
|
+
process.stderr.write(`[cotal-connector] transcript mirror: ${e.message}\n`);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
adoptNow(path) {
|
|
123
|
+
if (typeof path !== "string" || !path || this.path === path)
|
|
124
|
+
return;
|
|
125
|
+
this.path = path;
|
|
126
|
+
this.pending = undefined; // a batch from the old path must not commit the new offset
|
|
127
|
+
try {
|
|
128
|
+
this.offset = statSync(path).size;
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
this.offset = 0; // not written yet — everything from byte 0 is this session's
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async doFlush() {
|
|
135
|
+
if (!this.path || !this.agent.connected)
|
|
136
|
+
return; // offset untouched — catch up next flush
|
|
137
|
+
if (!this.pending) {
|
|
138
|
+
const { lines, nextOffset } = this.readComplete();
|
|
139
|
+
if (nextOffset === this.offset)
|
|
140
|
+
return; // nothing new
|
|
141
|
+
this.pending = { chunks: chunkLines(lines.flatMap(condense), MAX_CHUNK), sent: 0, nextOffset };
|
|
142
|
+
}
|
|
143
|
+
// Publish the pinned batch, tracking per-chunk progress: a mid-batch failure resumes at
|
|
144
|
+
// the first UNSENT chunk (no duplicates), and only a fully delivered batch commits the
|
|
145
|
+
// offset — the read range stays pinned until then.
|
|
146
|
+
const p = this.pending;
|
|
147
|
+
while (p.sent < p.chunks.length) {
|
|
148
|
+
await this.agent.send(p.chunks[p.sent], this.channel);
|
|
149
|
+
p.sent++;
|
|
150
|
+
}
|
|
151
|
+
this.offset = p.nextOffset;
|
|
152
|
+
this.pending = undefined;
|
|
153
|
+
}
|
|
154
|
+
/** New complete lines since the offset (a trailing partial line stays for the next flush). */
|
|
155
|
+
readComplete() {
|
|
156
|
+
const none = () => ({ lines: [], nextOffset: this.offset }); // evaluated late — after any truncation reset
|
|
157
|
+
let fd;
|
|
158
|
+
try {
|
|
159
|
+
fd = openSync(this.path, "r");
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return none();
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
const size = fstatSync(fd).size;
|
|
166
|
+
if (size < this.offset)
|
|
167
|
+
this.offset = 0; // truncated/rotated — start over
|
|
168
|
+
if (size === this.offset)
|
|
169
|
+
return none();
|
|
170
|
+
const buf = Buffer.alloc(size - this.offset);
|
|
171
|
+
readSync(fd, buf, 0, buf.length, this.offset);
|
|
172
|
+
const text = buf.toString("utf8");
|
|
173
|
+
const lastNl = text.lastIndexOf("\n");
|
|
174
|
+
if (lastNl < 0)
|
|
175
|
+
return none();
|
|
176
|
+
return {
|
|
177
|
+
lines: text.slice(0, lastNl).split("\n").filter(Boolean),
|
|
178
|
+
nextOffset: this.offset + Buffer.byteLength(text.slice(0, lastNl + 1), "utf8"),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
finally {
|
|
182
|
+
closeSync(fd);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/** Pack lines into chunks of at most `max` chars, splitting only on line boundaries
|
|
187
|
+
* (an oversized single line becomes its own chunk — never dropped). */
|
|
188
|
+
function chunkLines(lines, max) {
|
|
189
|
+
const chunks = [];
|
|
190
|
+
let cur = "";
|
|
191
|
+
for (const line of lines) {
|
|
192
|
+
if (cur && cur.length + 1 + line.length > max) {
|
|
193
|
+
chunks.push(cur);
|
|
194
|
+
cur = line;
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
cur = cur ? `${cur}\n${line}` : line;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (cur)
|
|
201
|
+
chunks.push(cur);
|
|
202
|
+
return chunks;
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=transcript.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript.js","sourceRoot":"","sources":["../src/transcript.ts"],"names":[],"mappings":"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"}
|
package/hooks/hooks.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"hooks": [
|
|
6
|
+
{
|
|
7
|
+
"type": "command",
|
|
8
|
+
"command": "node",
|
|
9
|
+
"args": ["${CLAUDE_PLUGIN_ROOT}/dist/hook.cjs"]
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"UserPromptSubmit": [
|
|
15
|
+
{
|
|
16
|
+
"hooks": [
|
|
17
|
+
{
|
|
18
|
+
"type": "command",
|
|
19
|
+
"command": "node",
|
|
20
|
+
"args": ["${CLAUDE_PLUGIN_ROOT}/dist/hook.cjs"]
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"PreToolUse": [
|
|
26
|
+
{
|
|
27
|
+
"hooks": [
|
|
28
|
+
{
|
|
29
|
+
"type": "command",
|
|
30
|
+
"command": "node",
|
|
31
|
+
"args": ["${CLAUDE_PLUGIN_ROOT}/dist/hook.cjs"]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"Notification": [
|
|
37
|
+
{
|
|
38
|
+
"matcher": "permission_prompt|elicitation_dialog",
|
|
39
|
+
"hooks": [
|
|
40
|
+
{
|
|
41
|
+
"type": "command",
|
|
42
|
+
"command": "node",
|
|
43
|
+
"args": ["${CLAUDE_PLUGIN_ROOT}/dist/hook.cjs"]
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
"Stop": [
|
|
49
|
+
{
|
|
50
|
+
"hooks": [
|
|
51
|
+
{
|
|
52
|
+
"type": "command",
|
|
53
|
+
"command": "node",
|
|
54
|
+
"args": ["${CLAUDE_PLUGIN_ROOT}/dist/hook.cjs"]
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
"StopFailure": [
|
|
60
|
+
{
|
|
61
|
+
"hooks": [
|
|
62
|
+
{
|
|
63
|
+
"type": "command",
|
|
64
|
+
"command": "node",
|
|
65
|
+
"args": ["${CLAUDE_PLUGIN_ROOT}/dist/hook.cjs"]
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
"SessionEnd": [
|
|
71
|
+
{
|
|
72
|
+
"hooks": [
|
|
73
|
+
{
|
|
74
|
+
"type": "command",
|
|
75
|
+
"command": "node",
|
|
76
|
+
"args": ["${CLAUDE_PLUGIN_ROOT}/dist/hook.cjs"]
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cotal-ai/connector-claude-code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -18,25 +18,28 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
21
|
-
"@cotal-ai/connector-core": "0.
|
|
21
|
+
"@cotal-ai/connector-core": "0.3.0"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
|
-
"@cotal-ai/core": "0.1.
|
|
24
|
+
"@cotal-ai/core": ">=0.1.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"esbuild": "^0.28.0",
|
|
28
28
|
"tsx": "^4.22.4",
|
|
29
|
-
"@cotal-ai/core": "0.
|
|
29
|
+
"@cotal-ai/core": "0.3.0"
|
|
30
30
|
},
|
|
31
31
|
"files": [
|
|
32
|
-
"dist"
|
|
32
|
+
"dist",
|
|
33
|
+
".mcp.json",
|
|
34
|
+
"hooks",
|
|
35
|
+
".claude-plugin"
|
|
33
36
|
],
|
|
34
37
|
"publishConfig": {
|
|
35
38
|
"access": "public"
|
|
36
39
|
},
|
|
37
40
|
"scripts": {
|
|
38
41
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
39
|
-
"build": "tsc -p tsconfig.json",
|
|
42
|
+
"build": "tsc -p tsconfig.json && pnpm run bundle",
|
|
40
43
|
"bundle": "esbuild src/mcp.ts src/hook.ts --bundle --platform=node --format=cjs --target=node20 --outdir=dist --out-extension:.js=.cjs"
|
|
41
44
|
}
|
|
42
45
|
}
|