@cotal-ai/connector-claude-code 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/hook.cjs CHANGED
@@ -16316,6 +16316,7 @@ var Algorithms;
16316
16316
 
16317
16317
  // ../../packages/core/dist/provision.js
16318
16318
  var import_nkeys2 = __toESM(require_mod(), 1);
16319
+ var FIVE_MINUTES = 5 * 60;
16319
16320
  var BASE_LIMITS = {
16320
16321
  subs: -1,
16321
16322
  conn: -1,
@@ -16332,18 +16333,20 @@ var SYS_LIMITS = { ...BASE_LIMITS, mem_storage: 0, disk_storage: 0 };
16332
16333
  // ../../packages/core/dist/streams.js
16333
16334
  var import_jetstream = __toESM(require_mod4(), 1);
16334
16335
  var import_transport_node = __toESM(require_transport_node(), 1);
16336
+ var import_kv2 = __toESM(require_mod6(), 1);
16337
+
16338
+ // ../../packages/core/dist/acls.js
16335
16339
  var import_kv = __toESM(require_mod6(), 1);
16340
+
16341
+ // ../../packages/core/dist/streams.js
16336
16342
  var PLANE3_DEDUP_WINDOW_MS = 2 * 60 * 60 * 1e3;
16337
16343
  var MEMBERSHIP_MAX_BYTES = 64 * 1024 * 1024;
16338
16344
 
16339
16345
  // ../../packages/core/dist/channels.js
16340
- var import_kv2 = __toESM(require_mod6(), 1);
16346
+ var import_kv3 = __toESM(require_mod6(), 1);
16341
16347
  var import_transport_node2 = __toESM(require_transport_node(), 1);
16342
16348
 
16343
16349
  // ../../packages/core/dist/members.js
16344
- var import_kv3 = __toESM(require_mod6(), 1);
16345
-
16346
- // ../../packages/core/dist/acls.js
16347
16350
  var import_kv4 = __toESM(require_mod6(), 1);
16348
16351
 
16349
16352
  // ../../packages/core/dist/membership-feed.js
package/dist/mcp.cjs CHANGED
@@ -47517,6 +47517,7 @@ var Algorithms;
47517
47517
 
47518
47518
  // ../../packages/core/dist/provision.js
47519
47519
  var import_nkeys2 = __toESM(require_mod(), 1);
47520
+ var FIVE_MINUTES = 5 * 60;
47520
47521
  var BASE_LIMITS = {
47521
47522
  subs: -1,
47522
47523
  conn: -1,
@@ -47533,7 +47534,56 @@ var SYS_LIMITS = { ...BASE_LIMITS, mem_storage: 0, disk_storage: 0 };
47533
47534
  // ../../packages/core/dist/streams.js
47534
47535
  var import_jetstream = __toESM(require_mod4(), 1);
47535
47536
  var import_transport_node = __toESM(require_transport_node(), 1);
47537
+ var import_kv2 = __toESM(require_mod6(), 1);
47538
+
47539
+ // ../../packages/core/dist/acls.js
47536
47540
  var import_kv = __toESM(require_mod6(), 1);
47541
+ async function openAclRegistry(nc, space, opts = {}) {
47542
+ const kvm = new import_kv.Kvm(nc);
47543
+ return opts.create ? kvm.create(aclBucket(space)) : kvm.open(aclBucket(space));
47544
+ }
47545
+ async function readAcl(kv, owner) {
47546
+ const e = await kv.get(aclKey(owner));
47547
+ if (!e || e.operation === "DEL" || e.operation === "PURGE")
47548
+ return void 0;
47549
+ try {
47550
+ const record2 = e.json();
47551
+ if (!Array.isArray(record2.allowSubscribe))
47552
+ return void 0;
47553
+ return { record: record2, revision: e.revision };
47554
+ } catch {
47555
+ return void 0;
47556
+ }
47557
+ }
47558
+ async function commitAcl(kv, owner, allowSubscribe) {
47559
+ const key = aclKey(owner);
47560
+ for (let attempt = 0; attempt < 5; attempt++) {
47561
+ const cur = await readAcl(kv, owner);
47562
+ const next = {
47563
+ allowSubscribe: [...allowSubscribe],
47564
+ revision: (cur?.record.revision ?? 0) + 1,
47565
+ updatedAt: Date.now()
47566
+ };
47567
+ const data = new TextEncoder().encode(JSON.stringify(next));
47568
+ if (!cur) {
47569
+ try {
47570
+ await kv.create(key, data);
47571
+ return next;
47572
+ } catch {
47573
+ continue;
47574
+ }
47575
+ }
47576
+ try {
47577
+ await kv.update(key, data, cur.revision);
47578
+ return next;
47579
+ } catch {
47580
+ continue;
47581
+ }
47582
+ }
47583
+ throw new Error(`acl CAS exhausted retries for ${owner}`);
47584
+ }
47585
+
47586
+ // ../../packages/core/dist/streams.js
47537
47587
  var MAX_MSGS_PER_SUBJECT = 1e3;
47538
47588
  var PLANE3_DEDUP_WINDOW_MS = 2 * 60 * 60 * 1e3;
47539
47589
  var DINBOX_MAX_ACK_PENDING = 1e3;
@@ -47639,7 +47689,7 @@ function fanoutDurableConfig(space, opts = {}) {
47639
47689
  }
47640
47690
 
47641
47691
  // ../../packages/core/dist/channels.js
47642
- var import_kv2 = __toESM(require_mod6(), 1);
47692
+ var import_kv3 = __toESM(require_mod6(), 1);
47643
47693
  var import_transport_node2 = __toESM(require_transport_node(), 1);
47644
47694
  function parseDuration(s) {
47645
47695
  const m = /^(\d+)(s|m|h|d)$/.exec(s.trim());
@@ -47660,7 +47710,7 @@ function effectiveDeliveryClass(cfg, defaults) {
47660
47710
  return cfg?.deliveryClass ?? defaults?.deliveryClass ?? "durable";
47661
47711
  }
47662
47712
  async function openChannelRegistry(nc, space, opts = {}) {
47663
- const kvm = new import_kv2.Kvm(nc);
47713
+ const kvm = new import_kv3.Kvm(nc);
47664
47714
  return opts.create ? kvm.create(channelBucket(space)) : kvm.open(channelBucket(space));
47665
47715
  }
47666
47716
  async function readChannelConfig(kv, channel) {
@@ -47681,7 +47731,7 @@ async function decode3(kv, key) {
47681
47731
  }
47682
47732
 
47683
47733
  // ../../packages/core/dist/members.js
47684
- var import_kv3 = __toESM(require_mod6(), 1);
47734
+ var import_kv4 = __toESM(require_mod6(), 1);
47685
47735
  var StaleMembershipWrite = class extends Error {
47686
47736
  constructor(channel, owner, attempted, current) {
47687
47737
  super(`stale membership write for ${channel}/${owner}: generation ${attempted} < current ${current}`);
@@ -47689,7 +47739,7 @@ var StaleMembershipWrite = class extends Error {
47689
47739
  }
47690
47740
  };
47691
47741
  async function openMembersRegistry(nc, space, opts = {}) {
47692
- const kvm = new import_kv3.Kvm(nc);
47742
+ const kvm = new import_kv4.Kvm(nc);
47693
47743
  return opts.create ? kvm.create(membersBucket(space)) : kvm.open(membersBucket(space));
47694
47744
  }
47695
47745
  async function readMember(kv, channel, owner) {
@@ -47788,53 +47838,6 @@ function durableEligible(rec, seq) {
47788
47838
  return true;
47789
47839
  }
47790
47840
 
47791
- // ../../packages/core/dist/acls.js
47792
- var import_kv4 = __toESM(require_mod6(), 1);
47793
- async function openAclRegistry(nc, space, opts = {}) {
47794
- const kvm = new import_kv4.Kvm(nc);
47795
- return opts.create ? kvm.create(aclBucket(space)) : kvm.open(aclBucket(space));
47796
- }
47797
- async function readAcl(kv, owner) {
47798
- const e = await kv.get(aclKey(owner));
47799
- if (!e || e.operation === "DEL" || e.operation === "PURGE")
47800
- return void 0;
47801
- try {
47802
- const record2 = e.json();
47803
- if (!Array.isArray(record2.allowSubscribe))
47804
- return void 0;
47805
- return { record: record2, revision: e.revision };
47806
- } catch {
47807
- return void 0;
47808
- }
47809
- }
47810
- async function commitAcl(kv, owner, allowSubscribe) {
47811
- const key = aclKey(owner);
47812
- for (let attempt = 0; attempt < 5; attempt++) {
47813
- const cur = await readAcl(kv, owner);
47814
- const next = {
47815
- allowSubscribe: [...allowSubscribe],
47816
- revision: (cur?.record.revision ?? 0) + 1,
47817
- updatedAt: Date.now()
47818
- };
47819
- const data = new TextEncoder().encode(JSON.stringify(next));
47820
- if (!cur) {
47821
- try {
47822
- await kv.create(key, data);
47823
- return next;
47824
- } catch {
47825
- continue;
47826
- }
47827
- }
47828
- try {
47829
- await kv.update(key, data, cur.revision);
47830
- return next;
47831
- } catch {
47832
- continue;
47833
- }
47834
- }
47835
- throw new Error(`acl CAS exhausted retries for ${owner}`);
47836
- }
47837
-
47838
47841
  // ../../packages/core/dist/membership-feed.js
47839
47842
  var import_transport_node3 = __toESM(require_transport_node(), 1);
47840
47843
  var import_kv5 = __toESM(require_mod6(), 1);
@@ -50294,6 +50297,7 @@ function feedbackLine(config2) {
50294
50297
 
50295
50298
  // ../connector-core/dist/agent.js
50296
50299
  var import_node_events2 = require("node:events");
50300
+ var SPAWN_TIMEOUT_MS = 4e4;
50297
50301
  function buildMeta(config2) {
50298
50302
  const meta3 = { ...config2.meta ?? {} };
50299
50303
  if (config2.model)
@@ -50655,11 +50659,14 @@ var MeshAgent = class extends import_node_events2.EventEmitter {
50655
50659
  }
50656
50660
  // ---- supervision ---------------------------------------------------------
50657
50661
  /** Ask the manager to spawn a new teammate into this space (its `start` op).
50662
+ * #159 B1: the manager replies to `start` only on a REAL outcome — presence join, process exit,
50663
+ * or its ~30s readiness backstop — so the request must outlive that window ({@link SPAWN_TIMEOUT_MS}),
50664
+ * not the 5s op default.
50658
50665
  * How it lands — a detached PTY, a tmux window, a cmux tab — is the manager's
50659
50666
  * runtime; from here it just joins the mesh as a lateral peer. `opts.agent` picks
50660
- * the harness (default the manager's `cotal`/Claude), `opts.model` overrides the
50667
+ * the harness (default the manager's `COTAL_DEFAULT_AGENT`, else `cotal`/Claude), `opts.model` overrides the
50661
50668
  * persona file's `model:`, and `opts.cwd` roots the new peer at a different folder/repo
50662
- * than the manager's workspace — the same knobs the operator's `cotal start` carries, so
50669
+ * than the manager's workspace — the same knobs the operator's `cotal spawn --detach` carries, so
50663
50670
  * the agent and operator spawn doors share one control-op contract. (Session `resume` is
50664
50671
  * intentionally NOT forwarded here: forking a host-local `~/.claude` transcript is an
50665
50672
  * operator-local intent, kept off the peer-facing spawn door — see #159.) */
@@ -50668,7 +50675,7 @@ var MeshAgent = class extends import_node_events2.EventEmitter {
50668
50675
  return this.ep.requestControl(CONTROL_PRIVILEGED, {
50669
50676
  op: "start",
50670
50677
  args: { name, role, agent: opts?.agent, model: opts?.model, cwd: opts?.cwd }
50671
- });
50678
+ }, SPAWN_TIMEOUT_MS);
50672
50679
  }
50673
50680
  /** Ask the manager to tear a teammate down (its `stop` op). Graceful by default —
50674
50681
  * the session is told to exit cleanly (so it leaves the mesh) before the
@@ -51246,14 +51253,14 @@ ${info}${caught}`);
51246
51253
  schema: {
51247
51254
  name: external_exports.string().describe("Which persona to spawn \u2014 the persona FILENAME in .cotal/agents (e.g. `review-critic`), without the .md. The new peer joins under the persona's own `name:` (auto-numbered, e.g. socrates-2, if that's taken). Fails if no such persona file exists \u2014 spawn an existing persona, don't invent a name."),
51248
51255
  role: external_exports.string().optional().describe("Optional role for the new peer (e.g. worker, reviewer); overrides the persona file's role."),
51249
- agent: external_exports.string().optional().describe("Optional harness the new peer runs on \u2014 the agent/connector type (claude, opencode, hermes), NOT the persona to spawn (that's `name`). Defaults to the manager's default (Claude)."),
51256
+ agent: external_exports.string().optional().describe("Optional harness the new peer runs on \u2014 the agent/connector type (claude, opencode, hermes), NOT the persona to spawn (that's `name`). Defaults to the manager's COTAL_DEFAULT_AGENT, else Claude."),
51250
51257
  model: external_exports.string().optional().describe("Optional model override (e.g. opus, sonnet) \u2014 wins over the persona file's model:."),
51251
51258
  cwd: external_exports.string().optional().describe("Optional working directory to root the new peer at (e.g. a different repo). A relative path resolves against the manager's workspace; omitted \u2192 it shares the manager's workspace.")
51252
51259
  // NOTE: session `resume` is deliberately NOT exposed here. Forking a host-local `~/.claude`
51253
51260
  // transcript is an operator-local intent; letting a spawn-capable mesh PEER name a host
51254
51261
  // session id would expand `spawn` into host-transcript disclosure with no broker-enforced
51255
- // boundary. Resume lives only on the operator CLI (`cotal spawn --resume` / `cotal start
51256
- // --resume`); a peer-facing, capability-gated resume is deferred (see #159).
51262
+ // boundary. Resume lives only on the operator CLI (`cotal spawn --resume`, foreground or
51263
+ // --detach); a peer-facing, capability-gated resume is deferred (see #159).
51257
51264
  },
51258
51265
  async run(agent, _config, { name, role, agent: agentType, model, cwd }) {
51259
51266
  try {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cotal-ai/connector-claude-code",
3
3
  "description": "Cotal connector for Claude Code: an installed plugin that joins a session to the mesh.",
4
- "version": "0.9.0",
4
+ "version": "0.10.0",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
@@ -19,7 +19,7 @@
19
19
  },
20
20
  "dependencies": {
21
21
  "@modelcontextprotocol/sdk": "^1.29.0",
22
- "@cotal-ai/connector-core": "0.9.0"
22
+ "@cotal-ai/connector-core": "0.10.0"
23
23
  },
24
24
  "peerDependencies": {
25
25
  "@cotal-ai/core": ">=0.1.0"
@@ -27,7 +27,7 @@
27
27
  "devDependencies": {
28
28
  "esbuild": "^0.28.0",
29
29
  "tsx": "^4.22.4",
30
- "@cotal-ai/core": "0.9.0"
30
+ "@cotal-ai/core": "0.10.0"
31
31
  },
32
32
  "files": [
33
33
  "dist",