@cotal-ai/connector-claude-code 0.1.3 → 0.2.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.
@@ -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":"AACA,OAAO,EAA2B,KAAK,SAAS,EAAoC,MAAM,gBAAgB,CAAC;AAS3G;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,SAwC7B,CAAC"}
package/dist/extension.js CHANGED
@@ -22,6 +22,9 @@ export const claudeConnector = {
22
22
  // Force the connector to emit channel wake-nudges: Claude doesn't advertise the
23
23
  // `claude/channel` capability back over MCP, so auto-detection would see it "off".
24
24
  COTAL_CHANNEL: "1",
25
+ // Managed sessions mirror their own transcript to `tr-<name>` so peers can read
26
+ // what the agent actually did. Personal sessions (no buildLaunch) never mirror.
27
+ COTAL_TRANSCRIPT: "1",
25
28
  };
26
29
  if (opts.role)
27
30
  env.COTAL_ROLE = opts.role;
@@ -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":"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;YAClB,gFAAgF;YAChF,gFAAgF;YAChF,gBAAgB,EAAE,GAAG;SACtB,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"}
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();
@@ -16502,7 +16513,8 @@ function configFromEnv(env = process.env) {
16502
16513
  user: link?.user,
16503
16514
  pass: link?.pass,
16504
16515
  tls: env.COTAL_TLS?.trim() === "1" || link?.tls || false,
16505
- feedbackKey: env.COTAL_FEEDBACK_KEY?.trim() || void 0
16516
+ feedbackKey: env.COTAL_FEEDBACK_KEY?.trim() || void 0,
16517
+ feedbackUrl: env.COTAL_FEEDBACK_URL?.trim() || void 0
16506
16518
  };
16507
16519
  }
16508
16520
 
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
  }
@@ -48509,6 +48515,11 @@ function describeStatusError(err2) {
48509
48515
  return err2;
48510
48516
  }
48511
48517
 
48518
+ // ../../packages/core/dist/spaces.js
48519
+ var import_transport_node4 = __toESM(require_transport_node(), 1);
48520
+ var import_jetstream3 = __toESM(require_mod4(), 1);
48521
+ var import_kv4 = __toESM(require_mod6(), 1);
48522
+
48512
48523
  // ../../packages/core/dist/registry.js
48513
48524
  var Registry = class {
48514
48525
  #byKey = /* @__PURE__ */ new Map();
@@ -48536,6 +48547,8 @@ var Registry = class {
48536
48547
  var registry2 = new Registry();
48537
48548
 
48538
48549
  // ../connector-core/dist/config.js
48550
+ var FEEDBACK_URL = "https://broker.cotal.ai/v1/feedback";
48551
+ var PUBLIC_FEEDBACK_URL = "https://cotal.ai/v1/feedback";
48539
48552
  function splitList(v) {
48540
48553
  if (!v)
48541
48554
  return [];
@@ -48570,7 +48583,8 @@ function configFromEnv(env = process.env) {
48570
48583
  user: link?.user,
48571
48584
  pass: link?.pass,
48572
48585
  tls: env.COTAL_TLS?.trim() === "1" || link?.tls || false,
48573
- feedbackKey: env.COTAL_FEEDBACK_KEY?.trim() || void 0
48586
+ feedbackKey: env.COTAL_FEEDBACK_KEY?.trim() || void 0,
48587
+ feedbackUrl: env.COTAL_FEEDBACK_URL?.trim() || void 0
48574
48588
  };
48575
48589
  }
48576
48590
  function laneLine(config2) {
@@ -48581,9 +48595,8 @@ function laneLine(config2) {
48581
48595
  return same ? `You read and may post to ${fmt(subs)}. ` : `You read ${fmt(subs)}; you may post only to ${fmt(pubs)} (posts to other channels are rejected). `;
48582
48596
  }
48583
48597
  function feedbackLine(config2) {
48584
- if (!config2.feedbackKey)
48585
- return "";
48586
- return `Beta feedback is enabled: use cotal_feedback with origin="human" when the user asks you to send feedback or gives you feedback to pass along. If you independently hit a major Cotal issue \u2014 for example repeated Cotal tool failures, inability to connect, lost/incorrect mesh messages, or a workflow-blocking bug \u2014 send cotal_feedback yourself with origin="agent". Do not send minor noise or secrets; include diagnostics only when they help debug the Cotal issue. `;
48598
+ const dest = config2.feedbackKey ? "" : `Without a feedback key it goes to the public cotal.ai intake and needs a contact email \u2014 the tool will tell you to ask the user for one if it can't find it. `;
48599
+ return `Use cotal_feedback with origin="human" when the user asks you to send feedback or gives you feedback to pass along. If you independently hit a major Cotal issue \u2014 for example repeated Cotal tool failures, inability to connect, lost/incorrect mesh messages, or a workflow-blocking bug \u2014 send cotal_feedback yourself with origin="agent". Do not send minor noise or secrets; include diagnostics only when they help debug the Cotal issue. ` + dest;
48587
48600
  }
48588
48601
 
48589
48602
  // ../connector-core/dist/agent.js
@@ -48825,6 +48838,29 @@ var MeshAgent = class extends import_node_events2.EventEmitter {
48825
48838
  this.assertConnected();
48826
48839
  return this.ep.requestControl("manager", { op: "start", args: { name, role } });
48827
48840
  }
48841
+ /** Ask the manager to tear a teammate down (its `stop` op). Graceful by default —
48842
+ * the session is told to exit cleanly (so it leaves the mesh) before the
48843
+ * process/tab is closed; `graceful:false` is a hard, immediate kill. */
48844
+ async despawn(name, opts) {
48845
+ this.assertConnected();
48846
+ return this.ep.requestControl("manager", {
48847
+ op: "stop",
48848
+ args: { name, graceful: opts?.graceful ?? true }
48849
+ });
48850
+ }
48851
+ /** Define a persona and persist it as config (the manager's `definePersona` op writes
48852
+ * .cotal/agents/<name>.md). On success, announce it on the channel — the "send it out"
48853
+ * half — so peers see the new persona; `spawn(name)` then launches an agent wearing it. */
48854
+ async definePersona(def) {
48855
+ this.assertConnected();
48856
+ const reply = await this.ep.requestControl("manager", {
48857
+ op: "definePersona",
48858
+ args: { name: def.name, role: def.role, model: def.model, persona: def.prompt }
48859
+ });
48860
+ if (reply.ok)
48861
+ await this.send(`persona \`${def.name}\` is now available \u2014 spawn it to bring it online`);
48862
+ return reply;
48863
+ }
48828
48864
  // ---- presence ------------------------------------------------------------
48829
48865
  /** The full roster, including ourselves. */
48830
48866
  roster() {
@@ -48919,11 +48955,14 @@ function controlSocketPath(space, name) {
48919
48955
  }
48920
48956
 
48921
48957
  // ../connector-core/dist/tool-specs.js
48958
+ var import_node_child_process = require("node:child_process");
48922
48959
  var ok = (text) => ({ text });
48923
48960
  var err = (text) => ({ text, isError: true });
48924
48961
  function statusGlyph(s) {
48925
48962
  return s === "working" ? "\u25CF" : s === "waiting" ? "\u25D0" : s === "idle" ? "\u25CB" : "\xB7";
48926
48963
  }
48964
+ var FACE_TAG_RE = /\[\[\s*face\s*:\s*[\w-]+\s*\]\]\s?/gi;
48965
+ var stripFaceTags = (text) => text.replace(FACE_TAG_RE, "");
48927
48966
  var ATTENTION_DESC = {
48928
48967
  open: "open \u2014 you receive everything; untagged channel chatter wakes you when idle",
48929
48968
  dnd: "dnd \u2014 channel chatter no longer wakes you (it still arrives in your next turn); DMs, anycast, and @mentions still wake you",
@@ -48953,6 +48992,18 @@ function renderChannelInfo(channel, info) {
48953
48992
  lines.push(` \u2022 replay-on-join: ${info.replay ? "on \u2014 new joiners see recent history" : "off \u2014 new joiners start from now (no backfill)"}`);
48954
48993
  return lines.join("\n");
48955
48994
  }
48995
+ function resolveFeedbackEmail(explicit) {
48996
+ if (explicit?.trim())
48997
+ return explicit.trim();
48998
+ if (process.env.COTAL_FEEDBACK_EMAIL?.trim())
48999
+ return process.env.COTAL_FEEDBACK_EMAIL.trim();
49000
+ try {
49001
+ const email3 = (0, import_node_child_process.execFileSync)("git", ["config", "user.email"], { encoding: "utf8" }).trim();
49002
+ return email3 || void 0;
49003
+ } catch {
49004
+ return void 0;
49005
+ }
49006
+ }
48956
49007
  function channelMeta(i) {
48957
49008
  const m = { kind: i.kind, from: i.fromName, from_id: i.fromId };
48958
49009
  if (i.fromRole)
@@ -48967,7 +49018,7 @@ function channelMeta(i) {
48967
49018
  m.mentioned = "true";
48968
49019
  return m;
48969
49020
  }
48970
- function cotalToolSpecs(config2) {
49021
+ function cotalToolSpecs(config2, source = "connector") {
48971
49022
  return [
48972
49023
  {
48973
49024
  name: "cotal_roster",
@@ -49030,7 +49081,7 @@ ${all.map(fmtItem).join("\n")}`);
49030
49081
  },
49031
49082
  async run(agent, _config, { text: msg, channel, mentions }) {
49032
49083
  try {
49033
- const m = await agent.send(msg, channel, mentions);
49084
+ const m = await agent.send(stripFaceTags(msg), channel, mentions);
49034
49085
  return ok(`Sent to #${m.channel}${m.mentions?.length ? ` (mentioned @${m.mentions.join(", @")})` : ""}.`);
49035
49086
  } catch (e) {
49036
49087
  return err(`Couldn't send: ${e.message}`);
@@ -49047,7 +49098,7 @@ ${all.map(fmtItem).join("\n")}`);
49047
49098
  },
49048
49099
  async run(agent, _config, { to, text: msg }) {
49049
49100
  try {
49050
- const { peer } = await agent.dm(to, msg);
49101
+ const { peer } = await agent.dm(to, stripFaceTags(msg));
49051
49102
  return ok(`DM sent to ${peer.card.name}.`);
49052
49103
  } catch (e) {
49053
49104
  return err(`Couldn't DM: ${e.message}`);
@@ -49064,7 +49115,7 @@ ${all.map(fmtItem).join("\n")}`);
49064
49115
  },
49065
49116
  async run(agent, _config, { role, text: msg }) {
49066
49117
  try {
49067
- await agent.anycast(role, msg);
49118
+ await agent.anycast(role, stripFaceTags(msg));
49068
49119
  return ok(`Sent to one @${role}.`);
49069
49120
  } catch (e) {
49070
49121
  return err(`Couldn't send: ${e.message}`);
@@ -49189,6 +49240,95 @@ ${info}${caught}`);
49189
49240
  return err(`Couldn't spawn ${name}: no manager reachable (${e.message}). Is the manager running?`);
49190
49241
  }
49191
49242
  }
49243
+ },
49244
+ {
49245
+ name: "cotal_feedback",
49246
+ title: "Cotal: send beta feedback",
49247
+ description: "Send feedback about Cotal to its developers. With a configured feedback key it goes to the keyed beta intake; without one it goes to the public cotal.ai intake, which requires a contact email.",
49248
+ schema: {
49249
+ origin: external_exports.enum(["human", "agent"]).describe(`"human" when relaying the user's feedback, "agent" when reporting an issue you hit yourself.`),
49250
+ type: external_exports.enum(["bug", "idea", "friction", "praise", "other"]).describe("What kind of feedback this is."),
49251
+ summary: external_exports.string().max(300).describe("Required one-line summary, max 300 characters."),
49252
+ details: external_exports.string().max(1e4).optional().describe("Longer free-form details. Do not include secrets."),
49253
+ severity: external_exports.enum(["low", "medium", "high"]).optional().describe("How badly this hurts (bugs/friction)."),
49254
+ area: external_exports.string().max(120).optional().describe("The part of Cotal this concerns (e.g. presence, channels, CLI)."),
49255
+ repro: external_exports.string().max(1e4).optional().describe("Steps to reproduce."),
49256
+ expected: external_exports.string().max(5e3).optional().describe("What you expected to happen."),
49257
+ actual: external_exports.string().max(5e3).optional().describe("What actually happened."),
49258
+ diagnostics: external_exports.string().max(1e4).optional().describe("Relevant diagnostics as text (logs, errors). Never include secrets."),
49259
+ email: external_exports.string().optional().describe("Contact email \u2014 required on the keyless public path when none is configured in the environment.")
49260
+ },
49261
+ async run(_agent, _config, args) {
49262
+ const { email: email3, ...payload } = args;
49263
+ const url2 = config2.feedbackUrl ?? (config2.feedbackKey ? FEEDBACK_URL : PUBLIC_FEEDBACK_URL);
49264
+ const headers = { "content-type": "application/json" };
49265
+ const body = { ...payload, source };
49266
+ if (config2.feedbackKey) {
49267
+ headers.authorization = `Bearer ${config2.feedbackKey}`;
49268
+ } else {
49269
+ const contact = resolveFeedbackEmail(email3);
49270
+ if (!contact)
49271
+ return err("Keyless feedback goes to the public cotal.ai intake, which requires a traceable contact email \u2014 ask the user for one and retry with the email argument (or set COTAL_FEEDBACK_EMAIL).");
49272
+ body.email = contact;
49273
+ }
49274
+ try {
49275
+ const res = await fetch(url2, { method: "POST", headers, body: JSON.stringify(body) });
49276
+ const raw = await res.text();
49277
+ let reply = {};
49278
+ if (raw)
49279
+ try {
49280
+ reply = JSON.parse(raw);
49281
+ } catch {
49282
+ reply = { error: raw };
49283
+ }
49284
+ if (!res.ok)
49285
+ return err(`Feedback rejected (${res.status}${reply.error ? `: ${reply.error}` : ""}).`);
49286
+ const note = reply.published === false ? " (stored, but the internal feedback channel publish failed)" : "";
49287
+ return ok(`Feedback sent${reply.id ? ` (id ${reply.id})` : ""}${note}. Thanks!`);
49288
+ } catch (e) {
49289
+ return err(`Couldn't reach the feedback intake at ${url2}: ${e.message}`);
49290
+ }
49291
+ }
49292
+ },
49293
+ {
49294
+ name: "cotal_despawn",
49295
+ title: "Cotal: stop a teammate",
49296
+ 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.",
49297
+ schema: {
49298
+ name: external_exports.string().describe("Name of the peer to stop."),
49299
+ graceful: external_exports.boolean().optional().describe("Default true: let the session exit cleanly. false = hard kill.")
49300
+ },
49301
+ async run(agent, _config, { name, graceful }) {
49302
+ try {
49303
+ const reply = await agent.despawn(name, { graceful });
49304
+ if (!reply.ok)
49305
+ return err(`Couldn't despawn ${name}: ${reply.error ?? "manager refused"}`);
49306
+ return ok(`Stopping ${name}${graceful === false ? " (hard)" : ""} \u2014 it will leave the roster shortly.`);
49307
+ } catch (e) {
49308
+ return err(`Couldn't despawn ${name}: no manager reachable (${e.message}). Is the manager running?`);
49309
+ }
49310
+ }
49311
+ },
49312
+ {
49313
+ name: "cotal_persona",
49314
+ title: "Cotal: define a persona",
49315
+ 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.",
49316
+ schema: {
49317
+ 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 -."),
49318
+ prompt: external_exports.string().max(1e4).describe("The persona \u2014 an appended system prompt describing who this agent is."),
49319
+ role: external_exports.string().max(120).optional().describe("Optional role label (e.g. reviewer, scout)."),
49320
+ model: external_exports.string().max(120).optional().describe("Optional model override (e.g. opus, sonnet).")
49321
+ },
49322
+ async run(agent, _config, { name, prompt, role, model }) {
49323
+ try {
49324
+ const reply = await agent.definePersona({ name, prompt, role, model });
49325
+ if (!reply.ok)
49326
+ return err(`Couldn't define ${name}: ${reply.error ?? "manager refused"}`);
49327
+ return ok(`Persona \`${name}\` saved \u2014 spawn it with cotal_spawn(name="${name}") to bring it online.`);
49328
+ } catch (e) {
49329
+ return err(`Couldn't define ${name}: no manager reachable (${e.message}). Is the manager running?`);
49330
+ }
49331
+ }
49192
49332
  }
49193
49333
  ];
49194
49334
  }
@@ -49198,8 +49338,8 @@ function toContent(r) {
49198
49338
  const content = [{ type: "text", text: r.text }];
49199
49339
  return r.isError ? { content, isError: true } : { content };
49200
49340
  }
49201
- function registerCotalTools(server, agent, config2) {
49202
- for (const spec of cotalToolSpecs(config2)) {
49341
+ function registerCotalTools(server, agent, config2, source) {
49342
+ for (const spec of cotalToolSpecs(config2, source)) {
49203
49343
  if (spec.schema) {
49204
49344
  server.registerTool(spec.name, { title: spec.title, description: spec.description, inputSchema: spec.schema }, async (args) => toContent(await spec.run(agent, config2, args)));
49205
49345
  } else {
@@ -49267,13 +49407,176 @@ function startControlServer(agent, socketPath, handle) {
49267
49407
  return server;
49268
49408
  }
49269
49409
 
49410
+ // src/transcript.ts
49411
+ var import_node_fs4 = require("node:fs");
49412
+ var MAX_PREVIEW = 700;
49413
+ var MAX_CHUNK = 6e3;
49414
+ function transcriptChannel(name) {
49415
+ return `tr-${name.toLowerCase().replace(/[^a-z0-9_-]+/g, "-")}`;
49416
+ }
49417
+ function truncate(s, max) {
49418
+ return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
49419
+ }
49420
+ function salient(input) {
49421
+ const i = input ?? {};
49422
+ const v = i.command ?? i.file_path ?? i.path ?? i.url ?? i.pattern ?? i.description;
49423
+ const s = typeof v === "string" ? v : Object.keys(i).length ? JSON.stringify(i) : "";
49424
+ return s ? `: ${truncate(s, 300)}` : "";
49425
+ }
49426
+ function resultText(content) {
49427
+ if (typeof content === "string") return content;
49428
+ if (Array.isArray(content))
49429
+ return content.map((b) => typeof b?.text === "string" ? b.text : "").filter(Boolean).join("\n");
49430
+ return "";
49431
+ }
49432
+ function condense(line) {
49433
+ let e;
49434
+ try {
49435
+ e = JSON.parse(line);
49436
+ } catch {
49437
+ return [];
49438
+ }
49439
+ if (!e || e.isMeta) return [];
49440
+ const content = e.message?.content;
49441
+ if (e.type === "assistant" && Array.isArray(content)) {
49442
+ const out = [];
49443
+ for (const b of content) {
49444
+ if (b.type === "text" && b.text?.trim()) out.push(b.text.trim());
49445
+ else if (b.type === "tool_use" && b.name) out.push(`\u2692 ${b.name}${salient(b.input)}`);
49446
+ }
49447
+ return out;
49448
+ }
49449
+ if (e.type === "user") {
49450
+ if (typeof content === "string")
49451
+ return content.trim() ? [`\xBB ${truncate(content.trim(), MAX_PREVIEW)}`] : [];
49452
+ if (Array.isArray(content)) {
49453
+ const out = [];
49454
+ for (const b of content) {
49455
+ if (b.type === "tool_result") {
49456
+ const t = resultText(b.content).trim();
49457
+ out.push(`\u2192 ${b.is_error ? "ERROR: " : ""}${t ? truncate(t, MAX_PREVIEW) : "(no output)"}`);
49458
+ } else if (b.type === "text" && b.text?.trim()) {
49459
+ out.push(`\xBB ${truncate(b.text.trim(), MAX_PREVIEW)}`);
49460
+ }
49461
+ }
49462
+ return out;
49463
+ }
49464
+ }
49465
+ return [];
49466
+ }
49467
+ var TranscriptMirror = class {
49468
+ constructor(agent, channel) {
49469
+ this.agent = agent;
49470
+ this.channel = channel;
49471
+ }
49472
+ agent;
49473
+ channel;
49474
+ path;
49475
+ offset = 0;
49476
+ /** A batch that failed mid-publish: chunks + how many already landed. Retried (from the
49477
+ * first unsent chunk — never re-sending a delivered one) before any new read. */
49478
+ pending;
49479
+ /** ALL path/offset mutation and publishing runs on this serialized chain — hook events
49480
+ * land concurrently on the control socket. */
49481
+ chain = Promise.resolve();
49482
+ /** Adopt the transcript at its CURRENT end — mirror only what happens from now on, so a
49483
+ * resumed session (or a mirror that first sees the path mid-session) never rebroadcasts. */
49484
+ adopt(path) {
49485
+ this.enqueue(() => {
49486
+ this.adoptNow(path);
49487
+ return Promise.resolve();
49488
+ });
49489
+ }
49490
+ /** Queue a flush of new transcript entries to the channel. Never throws, never blocks the
49491
+ * hook reply — the read+publish runs on the serialized chain. */
49492
+ flush(path) {
49493
+ this.enqueue(() => {
49494
+ if (!this.path) this.adoptNow(path);
49495
+ return this.doFlush();
49496
+ });
49497
+ }
49498
+ enqueue(step) {
49499
+ this.chain = this.chain.then(step).catch((e) => {
49500
+ process.stderr.write(`[cotal-connector] transcript mirror: ${e.message}
49501
+ `);
49502
+ });
49503
+ }
49504
+ adoptNow(path) {
49505
+ if (typeof path !== "string" || !path || this.path === path) return;
49506
+ this.path = path;
49507
+ this.pending = void 0;
49508
+ try {
49509
+ this.offset = (0, import_node_fs4.statSync)(path).size;
49510
+ } catch {
49511
+ this.offset = 0;
49512
+ }
49513
+ }
49514
+ async doFlush() {
49515
+ if (!this.path || !this.agent.connected) return;
49516
+ if (!this.pending) {
49517
+ const { lines, nextOffset } = this.readComplete();
49518
+ if (nextOffset === this.offset) return;
49519
+ this.pending = { chunks: chunkLines(lines.flatMap(condense), MAX_CHUNK), sent: 0, nextOffset };
49520
+ }
49521
+ const p = this.pending;
49522
+ while (p.sent < p.chunks.length) {
49523
+ await this.agent.send(p.chunks[p.sent], this.channel);
49524
+ p.sent++;
49525
+ }
49526
+ this.offset = p.nextOffset;
49527
+ this.pending = void 0;
49528
+ }
49529
+ /** New complete lines since the offset (a trailing partial line stays for the next flush). */
49530
+ readComplete() {
49531
+ const none = () => ({ lines: [], nextOffset: this.offset });
49532
+ let fd;
49533
+ try {
49534
+ fd = (0, import_node_fs4.openSync)(this.path, "r");
49535
+ } catch {
49536
+ return none();
49537
+ }
49538
+ try {
49539
+ const size = (0, import_node_fs4.fstatSync)(fd).size;
49540
+ if (size < this.offset) this.offset = 0;
49541
+ if (size === this.offset) return none();
49542
+ const buf = Buffer.alloc(size - this.offset);
49543
+ (0, import_node_fs4.readSync)(fd, buf, 0, buf.length, this.offset);
49544
+ const text = buf.toString("utf8");
49545
+ const lastNl = text.lastIndexOf("\n");
49546
+ if (lastNl < 0) return none();
49547
+ return {
49548
+ lines: text.slice(0, lastNl).split("\n").filter(Boolean),
49549
+ nextOffset: this.offset + Buffer.byteLength(text.slice(0, lastNl + 1), "utf8")
49550
+ };
49551
+ } finally {
49552
+ (0, import_node_fs4.closeSync)(fd);
49553
+ }
49554
+ }
49555
+ };
49556
+ function chunkLines(lines, max) {
49557
+ const chunks = [];
49558
+ let cur = "";
49559
+ for (const line of lines) {
49560
+ if (cur && cur.length + 1 + line.length > max) {
49561
+ chunks.push(cur);
49562
+ cur = line;
49563
+ } else {
49564
+ cur = cur ? `${cur}
49565
+ ${line}` : line;
49566
+ }
49567
+ }
49568
+ if (cur) chunks.push(cur);
49569
+ return chunks;
49570
+ }
49571
+
49270
49572
  // src/mcp.ts
49573
+ var mirror;
49271
49574
  var pendingTool;
49272
49575
  function toolDetail(name, input) {
49273
49576
  if (typeof name !== "string" || !name) return void 0;
49274
49577
  const i = input ?? {};
49275
- const salient = i.command ?? i.file_path ?? i.path ?? i.url ?? i.pattern ?? i.description;
49276
- let detail = typeof salient === "string" ? salient : Object.keys(i).length ? JSON.stringify(i) : "";
49578
+ const salient2 = i.command ?? i.file_path ?? i.path ?? i.url ?? i.pattern ?? i.description;
49579
+ let detail = typeof salient2 === "string" ? salient2 : Object.keys(i).length ? JSON.stringify(i) : "";
49277
49580
  if (detail.length > 300) detail = `${detail.slice(0, 299)}\u2026`;
49278
49581
  return { name, detail };
49279
49582
  }
@@ -49283,6 +49586,7 @@ var claudeHandle = async (agent, ev) => {
49283
49586
  try {
49284
49587
  switch (event) {
49285
49588
  case "SessionStart": {
49589
+ mirror?.adopt(ev.transcript_path);
49286
49590
  await agent.setStatus("idle");
49287
49591
  await agent.setAttention("open");
49288
49592
  const parts = [agent.channelBriefing(), formatInjection(agent.drainInbox())].filter(Boolean);
@@ -49290,10 +49594,12 @@ var claudeHandle = async (agent, ev) => {
49290
49594
  }
49291
49595
  case "UserPromptSubmit":
49292
49596
  pendingTool = void 0;
49597
+ mirror?.flush(ev.transcript_path);
49293
49598
  await agent.setStatus("working");
49294
49599
  return withContext(formatInjection(agent.drainInbox()));
49295
49600
  case "PreToolUse":
49296
49601
  pendingTool = toolDetail(ev.tool_name, ev.tool_input);
49602
+ mirror?.flush(ev.transcript_path);
49297
49603
  return {};
49298
49604
  case "Notification": {
49299
49605
  const msg = typeof ev.message === "string" ? ev.message : void 0;
@@ -49304,11 +49610,13 @@ var claudeHandle = async (agent, ev) => {
49304
49610
  case "Stop":
49305
49611
  case "StopFailure":
49306
49612
  pendingTool = void 0;
49613
+ mirror?.flush(ev.transcript_path);
49307
49614
  await agent.setStatus("idle");
49308
49615
  const pending = agent.attention === "open" ? agent.inboxCount() : agent.directedPendingCount();
49309
49616
  if (pending > 0) agent.requestWake();
49310
49617
  return {};
49311
49618
  case "SessionEnd":
49619
+ mirror?.flush(ev.transcript_path);
49312
49620
  await agent.setStatus("offline");
49313
49621
  return {};
49314
49622
  default:
@@ -49326,6 +49634,8 @@ async function main() {
49326
49634
  const config2 = configFromEnv();
49327
49635
  const agent = new MeshAgent(config2);
49328
49636
  agent.start();
49637
+ if (/^(1|true|yes|on)$/i.test(process.env.COTAL_TRANSCRIPT ?? ""))
49638
+ mirror = new TranscriptMirror(agent, transcriptChannel(config2.name));
49329
49639
  const socketPath = controlSocketPath(config2.space, config2.name);
49330
49640
  const controlServer = startControlServer(agent, socketPath, claudeHandle);
49331
49641
  const server = new McpServer(
@@ -49337,7 +49647,7 @@ async function main() {
49337
49647
  instructions: `You are connected to the Cotal mesh as "${config2.name}"${config2.role ? ` (role: ${config2.role})` : ""} in space "${config2.space}". ` + laneLine(config2) + feedbackLine(config2) + `Other agents coordinate with you here as lateral peers. Peer messages may arrive as <channel source="cotal" from="<name>" role="<role>" kind="dm|channel|anycast" channel="<name>">\u2026</channel> \u2014 read them and, when a reply is warranted, respond with cotal_dm (back to that peer), cotal_send (to a channel), or cotal_anycast (to a role). Use cotal_roster to see who is present, cotal_inbox to pull anything you may have missed, and cotal_status to report what you are doing. If you need to concentrate, cotal_status also sets your attention \u2014 dnd (channel chatter stops waking you; it still arrives on your next turn) or focus (only DMs and @mentions reach your context \u2014 pull the held chatter with cotal_inbox). Reply only when a reply is actually needed \u2014 a silent acknowledgement is correct; "agreed/thanks/good point" messages are noise. And @-mention a peer only when you need THAT specific peer to act: a mention wakes them, so mentioning in acknowledgements or sign-offs makes peers ping-pong wake-ups in an endless loop.`
49338
49648
  }
49339
49649
  );
49340
- registerCotalTools(server, agent, config2);
49650
+ registerCotalTools(server, agent, config2, "claude-code");
49341
49651
  let channelActive = false;
49342
49652
  const nudge = (item, pullHint) => {
49343
49653
  if (!channelActive) return;
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);
@@ -126,7 +137,7 @@ async function main() {
126
137
  `THAT specific peer to act: a mention wakes them, so mentioning in acknowledgements or ` +
127
138
  `sign-offs makes peers ping-pong wake-ups in an endless loop.`,
128
139
  });
129
- registerCotalTools(server, agent, config);
140
+ registerCotalTools(server, agent, config, "claude-code");
130
141
  // One wake-nudge path, shared by incoming messages and the Stop→idle flush. It stays a stable
131
142
  // function gated on a *mutable* `channelActive` flag (flipped true only after the MCP
132
143
  // handshake confirms the client speaks claude/channel — see below). If it fires before then it
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":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cotal-ai/connector-claude-code",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -18,15 +18,15 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@modelcontextprotocol/sdk": "^1.29.0",
21
- "@cotal-ai/connector-core": "0.1.3"
21
+ "@cotal-ai/connector-core": "0.2.0"
22
22
  },
23
23
  "peerDependencies": {
24
- "@cotal-ai/core": "0.1.2"
24
+ "@cotal-ai/core": "0.1.3"
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.1.3"
30
30
  },
31
31
  "files": [
32
32
  "dist"