@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.
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "cotal",
3
+ "version": "0.0.0",
4
+ "description": "Join the Cotal mesh — coordinate with other AI agents as lateral peers (presence, messaging, role requests) over NATS.",
5
+ "author": { "name": "Cotal" }
6
+ }
package/.mcp.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "cotal": {
4
+ "command": "node",
5
+ "args": ["${CLAUDE_PLUGIN_ROOT}/dist/mcp.cjs"]
6
+ }
7
+ }
8
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AACA,OAAO,EAA2B,KAAK,SAAS,EAAoC,MAAM,gBAAgB,CAAC;AAS3G;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,SAqC7B,CAAC"}
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
- /** Channel ref for the locally-installed `cotal` plugin (marketplace `cotal-mesh`).
4
- * `--dangerously-load-development-channels <ref>` turns on the plugin's
5
- * `claude/channel` capability so an idle session wakes the instant a peer message
6
- * arrives. The plugin must be *installed* (not `--plugin-dir`) for the channel to
7
- * bind; the connector's identity guard keeps it inert in non-managed sessions. */
8
- const CHANNEL_REF = "plugin:cotal@cotal-mesh";
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
- const args = ["--dangerously-load-development-channels", CHANNEL_REF];
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) {
@@ -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;;;;mFAImF;AACnF,MAAM,WAAW,GAAG,yBAAyB,CAAC;AAE9C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAc;IACxC,IAAI,EAAE,WAAW;IACjB,IAAI,EAAE,QAAQ;IACd,WAAW,CAAC,IAAgB;QAC1B,MAAM,GAAG,GAA2B;YAClC,WAAW,EAAE,IAAI,CAAC,KAAK;YACvB,UAAU,EAAE,IAAI,CAAC,IAAI;YACrB,gFAAgF;YAChF,mFAAmF;YACnF,aAAa,EAAE,GAAG;SACnB,CAAC;QACF,IAAI,IAAI,CAAC,IAAI;YAAE,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC;QAC1C,IAAI,IAAI,CAAC,EAAE;YAAE,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC;QACpC,IAAI,IAAI,CAAC,KAAK;YAAE,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC;QAC7C,IAAI,IAAI,CAAC,OAAO;YAAE,GAAG,CAAC,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC;QAEnD,MAAM,IAAI,GAAG,CAAC,yCAAyC,EAAE,WAAW,CAAC,CAAC;QAEtE,6EAA6E;QAC7E,8EAA8E;QAC9E,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACtC,GAAG,CAAC,gBAAgB,GAAG,IAAI,CAAC;YAC5B,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;YAChC,IAAI,GAAG,CAAC,OAAO;gBAAE,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;YAClE,IAAI,GAAG,CAAC,KAAK;gBAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;QACjD,CAAC;QAED,OAAO;YACL,OAAO,EAAE,QAAQ;YACjB,IAAI;YACJ,GAAG;YACH,wEAAwE;YACxE,yEAAyE;YACzE,OAAO,EAAE,kBAAkB;SAC5B,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,QAAQ,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC"}
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 = credsAuthenticator4;
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 credsAuthenticator4(creds) {
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 = jetstreamManager3;
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 jetstreamManager3(nc, opts = {}) {
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 jetstreamManager3(this.nc, opts);
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 = connect5;
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 connect5(opts = {}) {
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 Kvm4 = class {
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 = Kvm4;
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
@@ -1,2 +1,3 @@
1
1
  export * from "./extension.js";
2
+ export { transcriptChannel } from "./transcript.js";
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -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
@@ -1,2 +1,3 @@
1
1
  export * from "./extension.js"; // self-registers the `claude` connector on import
2
+ export { transcriptChannel } from "./transcript.js"; // launchers provision pub rights for `tr-<name>` in auth mode
2
3
  //# sourceMappingURL=index.js.map
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 = credsAuthenticator4;
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 credsAuthenticator4(creds) {
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 = jetstreamManager3;
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 jetstreamManager3(nc, opts = {}) {
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 jetstreamManager3(this.nc, opts);
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 = connect4;
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 connect4(opts = {}) {
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 Kvm4 = class {
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 = Kvm4;
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 mirror = Object.assign({}, opts.mirror);
20800
- if (!mirror.name.startsWith(types_1.kvPrefix)) {
20801
- mirror.name = `${types_1.kvPrefix}${mirror.name}`;
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 = 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 (mirror) {
20868
- let n = mirror.name;
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 (mirror.external && mirror.external.api !== "") {
20873
- const mb = mirror.name.substring(types_1.kvPrefix.length);
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 = `${mirror.external.api}.$KV.${n}`;
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 m = await this.nc.request(controlServiceSubject(this.space, service, this.card.id), JSON.stringify({ ...req, from: req.from ?? this.ref() }), { timeout: timeoutMs });
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(2e3).describe("One-line summary of the feedback."),
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 reply = await res.json().catch(() => ({}));
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
- return ok(`Feedback sent${reply.id ? ` (id ${reply.id})` : ""}. Thanks!`);
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 salient = i.command ?? i.file_path ?? i.path ?? i.url ?? i.pattern ?? i.description;
49331
- let detail = typeof salient === "string" ? salient : Object.keys(i).length ? JSON.stringify(i) : "";
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":"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"}
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":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAG7E,MAAM,WAAW,GAAG,GAAG,CAAC,CAAC,4DAA4D;AACrF,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,iEAAiE;AAEzF,4EAA4E;AAC5E,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,EAAE,CAAC;AAClE,CAAC;AAED,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,kFAAkF;AAClF,SAAS,OAAO,CAAC,KAAc;IAC7B,MAAM,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAA4B,CAAC;IACnD,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,WAAW,CAAC;IACpF,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,kFAAkF;AAClF,SAAS,UAAU,CAAC,OAAgB;IAClC,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAChD,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QACxB,OAAO,OAAO;aACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAQ,CAAwB,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAE,CAAsB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;aACrG,MAAM,CAAC,OAAO,CAAC;aACf,IAAI,CAAC,IAAI,CAAC,CAAC;IAChB,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,iGAAiG;AACjG,SAAS,QAAQ,CAAC,IAAY;IAC5B,IAAI,CAAuE,CAAC;IAC5E,IAAI,CAAC;QACH,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAa,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAC9B,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC;IACnC,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACrD,MAAM,GAAG,GAAa,EAAE,CAAC;QACzB,KAAK,MAAM,CAAC,IAAI,OAA6E,EAAE,CAAC;YAC9F,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE;gBAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;iBAC5D,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,CAAC,IAAI;gBAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACvF,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACtB,IAAI,OAAO,OAAO,KAAK,QAAQ;YAC7B,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9E,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAa,EAAE,CAAC;YACzB,KAAK,MAAM,CAAC,IAAI,OAAoF,EAAE,CAAC;gBACrG,IAAI,CAAC,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;oBAC7B,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;oBACvC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC;gBAC9F,CAAC;qBAAM,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;oBAC/C,GAAG,CAAC,IAAI,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;gBACxD,CAAC;YACH,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC;IACH,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,OAAO,gBAAgB;IAWR;IACA;IAXX,IAAI,CAAU;IACd,MAAM,GAAG,CAAC,CAAC;IACnB;sFACkF;IAC1E,OAAO,CAA0D;IACzE;mDAC+C;IACvC,KAAK,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;IAEjD,YACmB,KAAgB,EAChB,OAAe;QADf,UAAK,GAAL,KAAK,CAAW;QAChB,YAAO,GAAP,OAAO,CAAQ;IAC/B,CAAC;IAEJ;iGAC6F;IAC7F,KAAK,CAAC,IAAa;QACjB,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE;YAChB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YACpB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC,CAAC,CAAC;IACL,CAAC;IAED;sEACkE;IAClE,KAAK,CAAC,IAAa;QACjB,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE;YAChB,IAAI,CAAC,IAAI,CAAC,IAAI;gBAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YACpC,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;QACxB,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,OAAO,CAAC,IAAyB;QACvC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;YAC7C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wCAAyC,CAAW,CAAC,OAAO,IAAI,CAAC,CAAC;QACzF,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,QAAQ,CAAC,IAAa;QAC5B,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI;YAAE,OAAO;QACpE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC,2DAA2D;QACrF,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,6DAA6D;QAChF,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,OAAO;QACnB,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS;YAAE,OAAO,CAAC,yCAAyC;QAC1F,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;YAClD,IAAI,UAAU,KAAK,IAAI,CAAC,MAAM;gBAAE,OAAO,CAAC,cAAc;YACtD,IAAI,CAAC,OAAO,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC;QACjG,CAAC;QACD,wFAAwF;QACxF,uFAAuF;QACvF,mDAAmD;QACnD,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC;QACvB,OAAO,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YAChC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YACtD,CAAC,CAAC,IAAI,EAAE,CAAC;QACX,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,UAAU,CAAC;QAC3B,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;IAC3B,CAAC;IAED,8FAA8F;IACtF,YAAY;QAClB,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,8CAA8C;QAC3G,IAAI,EAAU,CAAC;QACf,IAAI,CAAC;YACH,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAK,EAAE,GAAG,CAAC,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;QACD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC;YAChC,IAAI,IAAI,GAAG,IAAI,CAAC,MAAM;gBAAE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,iCAAiC;YAC1E,IAAI,IAAI,KAAK,IAAI,CAAC,MAAM;gBAAE,OAAO,IAAI,EAAE,CAAC;YACxC,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;YAC7C,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;YAC9C,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YACtC,IAAI,MAAM,GAAG,CAAC;gBAAE,OAAO,IAAI,EAAE,CAAC;YAC9B,OAAO;gBACL,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;gBACxD,UAAU,EAAE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC;aAC/E,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,SAAS,CAAC,EAAE,CAAC,CAAC;QAChB,CAAC;IACH,CAAC;CACF;AAED;wEACwE;AACxE,SAAS,UAAU,CAAC,KAAe,EAAE,GAAW;IAC9C,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;YAC9C,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACjB,GAAG,GAAG,IAAI,CAAC;QACb,CAAC;aAAM,CAAC;YACN,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACvC,CAAC;IACH,CAAC;IACD,IAAI,GAAG;QAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1B,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -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.1.4-next-20260612020133",
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.2.0-next-20260612020133"
21
+ "@cotal-ai/connector-core": "0.3.0"
22
22
  },
23
23
  "peerDependencies": {
24
- "@cotal-ai/core": "0.1.2"
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.1.2"
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
  }