cotal-ai 0.82.2 → 0.84.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cotal-ai",
3
- "version": "0.82.2",
3
+ "version": "0.84.0",
4
4
  "description": "Cotal — lateral agent coordination over NATS. Run `npx cotal-ai` for the CLI.",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -33,24 +33,24 @@
33
33
  "node": ">=22"
34
34
  },
35
35
  "dependencies": {
36
- "@cotal-ai/auth": "0.82.2",
37
- "@cotal-ai/cli": "0.82.2",
38
- "@cotal-ai/connector-core": "0.82.2",
39
- "@cotal-ai/core": "0.82.2",
40
- "@cotal-ai/delivery": "0.82.2",
41
- "@cotal-ai/manager": "0.82.2",
42
- "@cotal-ai/runtime": "0.82.2",
43
- "@cotal-ai/workspace": "0.82.2"
36
+ "@cotal-ai/auth": "0.84.0",
37
+ "@cotal-ai/cli": "0.84.0",
38
+ "@cotal-ai/connector-core": "0.84.0",
39
+ "@cotal-ai/core": "0.84.0",
40
+ "@cotal-ai/delivery": "0.84.0",
41
+ "@cotal-ai/manager": "0.84.0",
42
+ "@cotal-ai/runtime": "0.84.0",
43
+ "@cotal-ai/workspace": "0.84.0"
44
44
  },
45
45
  "devDependencies": {
46
- "@cotal-ai/connector-claude-code": "0.82.2",
47
- "@cotal-ai/connector-codex": "0.82.2",
48
- "@cotal-ai/connector-hermes": "0.82.2",
49
- "@cotal-ai/connector-jcode": "0.82.2",
50
- "@cotal-ai/connector-opencode": "0.82.2",
51
- "@cotal-ai/pi": "0.82.2",
46
+ "@cotal-ai/connector-claude-code": "0.84.0",
47
+ "@cotal-ai/connector-codex": "0.84.0",
48
+ "@cotal-ai/connector-hermes": "0.84.0",
49
+ "@cotal-ai/connector-jcode": "0.84.0",
50
+ "@cotal-ai/connector-opencode": "0.84.0",
51
+ "@cotal-ai/pi": "0.84.0",
52
52
  "@cotal-ai/smoke-kit": "0.0.0",
53
- "@cotal-ai/web": "0.82.2",
53
+ "@cotal-ai/web": "0.84.0",
54
54
  "@nats-io/jetstream": "^3.4.0",
55
55
  "@nats-io/transport-node": "^3.4.0",
56
56
  "typescript": "^5.9.3"
@@ -1 +1 @@
1
- {"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgD,KAAK,SAAS,EAAoC,MAAM,gBAAgB,CAAC;AAQhI;;;;;;;;;4FAS4F;AAC5F,eAAO,MAAM,oBAAoB,8/DA4EvB,CAAC;AAgDX;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,SA4M7B,CAAC"}
1
+ {"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgD,KAAK,SAAS,EAAoC,MAAM,gBAAgB,CAAC;AAShI;;;;;;;;;4FAS4F;AAC5F,eAAO,MAAM,oBAAoB,8/DA4EvB,CAAC;AAgDX;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,SAwM7B,CAAC"}
@@ -7,13 +7,13 @@ var __export = (target, all) => {
7
7
  // src/setup.ts
8
8
  import { spawn, spawnSync } from "node:child_process";
9
9
  import { randomUUID } from "node:crypto";
10
- import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
11
- import { homedir } from "node:os";
12
- import { dirname, join as join2 } from "node:path";
10
+ import { cpSync, existsSync as existsSync2, mkdirSync, readdirSync, readFileSync as readFileSync2, renameSync, rmSync, writeFileSync } from "node:fs";
11
+ import { homedir as homedir2 } from "node:os";
12
+ import { dirname, join as join3 } from "node:path";
13
13
  import { fileURLToPath } from "node:url";
14
14
  import {
15
15
  globalConfigPath,
16
- mcpServerProblem,
16
+ mcpServerProblem as mcpServerProblem2,
17
17
  registry as registry3
18
18
  } from "@cotal-ai/core";
19
19
 
@@ -14908,46 +14908,67 @@ var NO_TOOL_ARGS = external_exports.strictObject({});
14908
14908
  // ../connector-core/dist/control.js
14909
14909
  var MAX_FRAME_BYTES = 1 << 20;
14910
14910
 
14911
+ // src/user-mcp.ts
14912
+ import { existsSync, readFileSync } from "node:fs";
14913
+ import { homedir } from "node:os";
14914
+ import { join as join2 } from "node:path";
14915
+ import { mcpServerProblem } from "@cotal-ai/core";
14916
+ function claudeUserMcpServers() {
14917
+ const path = join2(process.env.CLAUDE_CONFIG_DIR?.trim() || homedir(), ".claude.json");
14918
+ if (!existsSync(path)) return {};
14919
+ let config2;
14920
+ try {
14921
+ config2 = JSON.parse(readFileSync(path, "utf8"));
14922
+ } catch (e) {
14923
+ throw new Error(`${path}: invalid JSON - ${e.message}`);
14924
+ }
14925
+ return config2?.mcpServers ?? {};
14926
+ }
14927
+ function claudeUserMcpEnvKeys() {
14928
+ const valid = Object.fromEntries(Object.entries(claudeUserMcpServers()).filter(([name, spec]) => mcpServerProblem(name, spec) === void 0));
14929
+ return mcpServerEnvKeys(valid);
14930
+ }
14931
+
14911
14932
  // src/setup.ts
14912
14933
  var PACKAGE_ROOT = fileURLToPath(new URL("..", import.meta.url));
14913
14934
  var DOCS_URL = "https://github.com/Cotal-AI/Cotal/blob/main/docs/connect-claude.md";
14914
14935
  var MARKETPLACE = "cotal-mesh";
14915
14936
  function cotalHome() {
14916
14937
  if (process.env.COTAL_HOME) return process.env.COTAL_HOME;
14917
- if (process.platform === "win32" && process.env.LOCALAPPDATA) return join2(process.env.LOCALAPPDATA, "Cotal");
14918
- return join2(homedir(), ".cotal");
14938
+ if (process.platform === "win32" && process.env.LOCALAPPDATA) return join3(process.env.LOCALAPPDATA, "Cotal");
14939
+ return join3(homedir2(), ".cotal");
14919
14940
  }
14920
14941
  function marketplaceDir() {
14921
- return join2(cotalHome(), "claude-plugin");
14942
+ return join3(cotalHome(), "claude-plugin");
14922
14943
  }
14923
14944
  function materialize(name, root, assets, version2) {
14924
14945
  const market = marketplaceDir();
14925
14946
  mkdirSync(market, { recursive: true });
14926
- const dest = join2(market, name);
14947
+ const dest = join3(market, name);
14927
14948
  const staging = `${dest}.staging.${process.pid}`;
14928
14949
  const old = `${dest}.old.${process.pid}`;
14929
14950
  const orphans = readdirSync(market, { withFileTypes: true }).filter((entry) => entry.isDirectory() && (entry.name.startsWith(`${name}.old.`) || entry.name.startsWith(`${name}.staging.`))).map((entry) => entry.name);
14930
- if (!existsSync(dest)) {
14951
+ if (!existsSync2(dest)) {
14931
14952
  const recoverable = orphans.find((entry) => entry.startsWith(`${name}.old.`));
14932
- if (recoverable) renameSync(join2(market, recoverable), dest);
14953
+ if (recoverable) renameSync(join3(market, recoverable), dest);
14933
14954
  }
14934
- for (const entry of orphans) rmSync(join2(market, entry), { recursive: true, force: true });
14955
+ for (const entry of orphans) rmSync(join3(market, entry), { recursive: true, force: true });
14935
14956
  let movedAside = false;
14936
14957
  let swapped = false;
14937
14958
  try {
14938
14959
  mkdirSync(staging, { recursive: true });
14939
14960
  for (const asset of assets) {
14940
- const destination = join2(staging, asset);
14941
- mkdirSync(join2(destination, ".."), { recursive: true });
14942
- cpSync(join2(root, asset), destination, { recursive: true, dereference: true });
14961
+ const destination = join3(staging, asset);
14962
+ mkdirSync(join3(destination, ".."), { recursive: true });
14963
+ cpSync(join3(root, asset), destination, { recursive: true, dereference: true });
14943
14964
  }
14944
14965
  if (version2) {
14945
- const manifestPath = join2(staging, ".claude-plugin", "plugin.json");
14946
- const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
14966
+ const manifestPath = join3(staging, ".claude-plugin", "plugin.json");
14967
+ const manifest = JSON.parse(readFileSync2(manifestPath, "utf8"));
14947
14968
  manifest.version = version2;
14948
14969
  writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
14949
14970
  }
14950
- if (existsSync(dest)) {
14971
+ if (existsSync2(dest)) {
14951
14972
  renameSync(dest, old);
14952
14973
  movedAside = true;
14953
14974
  }
@@ -14955,7 +14976,7 @@ function materialize(name, root, assets, version2) {
14955
14976
  swapped = true;
14956
14977
  if (movedAside) rmSync(old, { recursive: true, force: true });
14957
14978
  } catch (error51) {
14958
- if (movedAside && !swapped && !existsSync(dest) && existsSync(old)) renameSync(old, dest);
14979
+ if (movedAside && !swapped && !existsSync2(dest) && existsSync2(old)) renameSync(old, dest);
14959
14980
  throw error51;
14960
14981
  } finally {
14961
14982
  rmSync(staging, { recursive: true, force: true });
@@ -14967,16 +14988,16 @@ function command(...args) {
14967
14988
  return { status: result.status, output: `${result.stdout ?? ""}${result.stderr ?? ""}`.trim() };
14968
14989
  }
14969
14990
  function marketplaceEntry(market, name) {
14970
- const manifest = JSON.parse(readFileSync(join2(market, name, ".claude-plugin", "plugin.json"), "utf8"));
14991
+ const manifest = JSON.parse(readFileSync2(join3(market, name, ".claude-plugin", "plugin.json"), "utf8"));
14971
14992
  const description = typeof manifest.description === "string" ? manifest.description : void 0;
14972
14993
  return { name, source: `./${name}`, ...description ? { description } : {} };
14973
14994
  }
14974
14995
  function marketplaceManifest(market) {
14975
- const plugins = ["cotal", "cotal-skills"].filter((name) => existsSync(join2(market, name, ".claude-plugin", "plugin.json"))).map((name) => marketplaceEntry(market, name));
14996
+ const plugins = ["cotal", "cotal-skills"].filter((name) => existsSync2(join3(market, name, ".claude-plugin", "plugin.json"))).map((name) => marketplaceEntry(market, name));
14976
14997
  return { name: MARKETPLACE, description: "Cotal for Claude Code", owner: { name: "Cotal" }, plugins };
14977
14998
  }
14978
14999
  function writeMarketplaceManifest(market) {
14979
- const path = join2(market, ".claude-plugin", "marketplace.json");
15000
+ const path = join3(market, ".claude-plugin", "marketplace.json");
14980
15001
  mkdirSync(dirname(path), { recursive: true });
14981
15002
  writeFileSync(path, JSON.stringify(marketplaceManifest(market), null, 2) + "\n");
14982
15003
  return path;
@@ -15042,19 +15063,8 @@ function skillsRow(entries, { version: version2, skillsRemedy }) {
15042
15063
  if (match.version === version2) return row("ok", "current");
15043
15064
  return row("warn", `${typeof match.version === "string" ? `v${match.version} \u2260 v${version2} \xB7 ` : ""}stale \xB7 ${skillsRemedy}`);
15044
15065
  }
15045
- function userServers() {
15046
- const path = join2(process.env.CLAUDE_CONFIG_DIR?.trim() || homedir(), ".claude.json");
15047
- if (!existsSync(path)) return {};
15048
- let config2;
15049
- try {
15050
- config2 = JSON.parse(readFileSync(path, "utf8"));
15051
- } catch (e) {
15052
- throw new Error(`${path}: invalid JSON - ${e.message}`);
15053
- }
15054
- return config2?.mcpServers ?? {};
15055
- }
15056
15066
  function copyable(name, spec) {
15057
- if (mcpServerProblem(name, spec) !== void 0) return false;
15067
+ if (mcpServerProblem2(name, spec) !== void 0) return false;
15058
15068
  const { env, headers } = spec;
15059
15069
  return [env, headers].every((values) => values === void 0 || Object.values(values).every((v) => v.replace(ENV_REFERENCE, "") === ""));
15060
15070
  }
@@ -15070,7 +15080,7 @@ var claudeSetupProvider = {
15070
15080
  context: [DOCS_URL],
15071
15081
  run() {
15072
15082
  for (const asset of ["dist/mcp.cjs", "dist/hook.cjs", ".claude-plugin/plugin.json", ".mcp.json", "hooks/hooks.json"])
15073
- if (!existsSync(join2(PACKAGE_ROOT, asset))) throw new Error(`plugin asset missing: ${join2(PACKAGE_ROOT, asset)}`);
15083
+ if (!existsSync2(join3(PACKAGE_ROOT, asset))) throw new Error(`plugin asset missing: ${join3(PACKAGE_ROOT, asset)}`);
15074
15084
  materialize("cotal", PACKAGE_ROOT, [".claude-plugin", ".mcp.json", "hooks", "dist/mcp.cjs", "dist/hook.cjs"]);
15075
15085
  install("cotal", "local");
15076
15086
  return `cotal@${MARKETPLACE} (local scope)`;
@@ -15082,14 +15092,14 @@ var claudeSetupProvider = {
15082
15092
  explain: "Installs Cotal's authored skills as a Claude Code plugin.",
15083
15093
  context: [DOCS_URL],
15084
15094
  run({ skillsDir, version: version2, stateDir }) {
15085
- const manifestRoot = join2(PACKAGE_ROOT, "skills-plugin");
15086
- if (!existsSync(join2(manifestRoot, ".claude-plugin", "plugin.json"))) throw new Error("Claude skills plugin manifest is missing");
15087
- const payload = join2(stateDir, `.claude-skills-payload.${process.pid}`);
15095
+ const manifestRoot = join3(PACKAGE_ROOT, "skills-plugin");
15096
+ if (!existsSync2(join3(manifestRoot, ".claude-plugin", "plugin.json"))) throw new Error("Claude skills plugin manifest is missing");
15097
+ const payload = join3(stateDir, `.claude-skills-payload.${process.pid}`);
15088
15098
  rmSync(payload, { recursive: true, force: true });
15089
15099
  try {
15090
15100
  mkdirSync(payload, { recursive: true });
15091
- cpSync(join2(manifestRoot, ".claude-plugin"), join2(payload, ".claude-plugin"), { recursive: true, dereference: true });
15092
- cpSync(skillsDir, join2(payload, "skills"), { recursive: true, dereference: true });
15101
+ cpSync(join3(manifestRoot, ".claude-plugin"), join3(payload, ".claude-plugin"), { recursive: true, dereference: true });
15102
+ cpSync(skillsDir, join3(payload, "skills"), { recursive: true, dereference: true });
15093
15103
  materialize("cotal-skills", payload, [".claude-plugin", "skills"], version2);
15094
15104
  install("cotal-skills", "user", version2);
15095
15105
  return `cotal-skills@${MARKETPLACE} (user scope)`;
@@ -15104,7 +15114,7 @@ var claudeSetupProvider = {
15104
15114
  explain: "A spawned Claude Code session gets the cotal tools plus the MCP servers your own sessions load. Each spawn starts its own copy of every shared server, so on a small machine remove the heavy ones from the cotal config or spawn with --share-tools none.",
15105
15115
  context: [DOCS_URL],
15106
15116
  run({ seed }) {
15107
- const installed = Object.entries(userServers());
15117
+ const installed = Object.entries(claudeUserMcpServers());
15108
15118
  const shared = Object.fromEntries(installed.filter((entry) => copyable(...entry)));
15109
15119
  const left = installed.filter((entry) => !copyable(...entry)).map(([name]) => name);
15110
15120
  const path = globalConfigPath();
@@ -15145,30 +15155,30 @@ import { loadAgentFile as loadAgentFile2, registry as registry4, writeLaunchArti
15145
15155
  // src/carried.ts
15146
15156
  import { createHash as createHash2 } from "node:crypto";
15147
15157
  import { execFileSync } from "node:child_process";
15148
- import { copyFileSync, lstatSync, mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
15149
- import { homedir as homedir2, hostname as hostname3 } from "node:os";
15150
- import { join as join3 } from "node:path";
15158
+ import { copyFileSync, lstatSync, mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
15159
+ import { homedir as homedir3, hostname as hostname3 } from "node:os";
15160
+ import { join as join4 } from "node:path";
15151
15161
  var SEAT_PROJECT = "seat";
15152
15162
  var FORK_RECORD = "cotal-fork.json";
15153
15163
  var MIN_VERSION = [2, 1, 234];
15154
15164
  var MAX_TITLE = 1024;
15155
15165
  function configDir(env) {
15156
- return env.CLAUDE_CONFIG_DIR || join3(env.HOME || homedir2(), ".claude");
15166
+ return env.CLAUDE_CONFIG_DIR || join4(env.HOME || homedir3(), ".claude");
15157
15167
  }
15158
15168
  function transcripts(env) {
15159
- const projects = join3(configDir(env), "projects");
15169
+ const projects = join4(configDir(env), "projects");
15160
15170
  let dirs;
15161
15171
  try {
15162
- dirs = readdirSync2(projects, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => join3(projects, d.name));
15172
+ dirs = readdirSync2(projects, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => join4(projects, d.name));
15163
15173
  } catch (e) {
15164
15174
  if (e.code === "ENOENT") return [];
15165
15175
  throw e;
15166
15176
  }
15167
- return dirs.flatMap((dir) => readdirSync2(dir, { withFileTypes: true }).filter((f) => f.isFile() && f.name.endsWith(".jsonl")).map((f) => join3(dir, f.name)));
15177
+ return dirs.flatMap((dir) => readdirSync2(dir, { withFileTypes: true }).filter((f) => f.isFile() && f.name.endsWith(".jsonl")).map((f) => join4(dir, f.name)));
15168
15178
  }
15169
15179
  function titleOf(path) {
15170
15180
  let title;
15171
- for (const line of readFileSync2(path, "utf8").split("\n")) {
15181
+ for (const line of readFileSync3(path, "utf8").split("\n")) {
15172
15182
  try {
15173
15183
  const entry = JSON.parse(line);
15174
15184
  if (entry.type === "custom-title" && typeof entry.customTitle === "string") title = entry.customTitle;
@@ -15178,7 +15188,7 @@ function titleOf(path) {
15178
15188
  return title;
15179
15189
  }
15180
15190
  function sha256(path) {
15181
- return createHash2("sha256").update(readFileSync2(path)).digest("hex");
15191
+ return createHash2("sha256").update(readFileSync3(path)).digest("hex");
15182
15192
  }
15183
15193
  var claudeResumeTranscript = {
15184
15194
  find(id, env) {
@@ -15221,14 +15231,14 @@ function refuseCarriedLaunch(binary, seatEnv) {
15221
15231
  );
15222
15232
  }
15223
15233
  function carriedForkRecord(home) {
15224
- return join3(home, FORK_RECORD);
15234
+ return join4(home, FORK_RECORD);
15225
15235
  }
15226
15236
  function placeCarried(home, transcript, resume, cwd) {
15227
- writeFileSync2(join3(home, ".claude.json"), JSON.stringify({ hasCompletedOnboarding: true, projects: { [cwd]: { hasTrustDialogAccepted: true } } }), { mode: 384 });
15237
+ writeFileSync2(join4(home, ".claude.json"), JSON.stringify({ hasCompletedOnboarding: true, projects: { [cwd]: { hasTrustDialogAccepted: true } } }), { mode: 384 });
15228
15238
  if (resume !== void 0) {
15229
- const dir = join3(home, "projects", SEAT_PROJECT);
15239
+ const dir = join4(home, "projects", SEAT_PROJECT);
15230
15240
  mkdirSync2(dir, { recursive: true, mode: 448 });
15231
- copyFileSync(transcript, join3(dir, `${resume}.jsonl`));
15241
+ copyFileSync(transcript, join4(dir, `${resume}.jsonl`));
15232
15242
  }
15233
15243
  return { CLAUDE_CONFIG_DIR: home, CLAUDE_CODE_PROJECT_DIR_NAME: SEAT_PROJECT, ...resume !== void 0 ? { COTAL_CLAUDE_CARRIED: resume } : {} };
15234
15244
  }
@@ -15321,6 +15331,7 @@ var CLAUDE_PROVIDER_KEYS = [
15321
15331
  var CHANNEL_REF = `server:${MCP_SERVER_NAME}`;
15322
15332
  var PLUGIN_ROOT = fileURLToPath2(new URL("..", import.meta.url));
15323
15333
  var MCP_CJS = resolve(PLUGIN_ROOT, "dist", "mcp.cjs");
15334
+ var CLAUDE_MCP_KEYS = ["ENABLE_CLAUDEAI_MCP_SERVERS", "ENABLE_TOOL_SEARCH", "MCP_TIMEOUT", "MCP_CONNECTION_NONBLOCKING"];
15324
15335
  function assertServableModel(model) {
15325
15336
  if (!model.includes("/") || model.startsWith("arn:")) return;
15326
15337
  throw new Error(
@@ -15351,9 +15362,11 @@ var claudeConnector = {
15351
15362
  if (opts.continueSession) throw new Error("claude connector does not support exact-session continuation");
15352
15363
  if (opts.variant) throw new Error("claude connector: model variants are not supported");
15353
15364
  const shared = opts.mcpServers ?? {};
15365
+ const exclusive = opts.mcpServersExclusive === true || opts.carried !== void 0;
15366
+ const nativeEnvKeys = exclusive ? [] : claudeUserMcpEnvKeys();
15354
15367
  const control = controlEndpoint(opts.space, opts.name);
15355
15368
  const env = {
15356
- ...launchEnv({ providerKeys: CLAUDE_PROVIDER_KEYS, mcpKeys: mcpServerEnvKeys(shared), envAllow: opts.envAllow }),
15369
+ ...launchEnv({ providerKeys: CLAUDE_PROVIDER_KEYS, mcpKeys: [...mcpServerEnvKeys(shared), ...nativeEnvKeys, ...CLAUDE_MCP_KEYS], envAllow: opts.envAllow }),
15357
15370
  ...aclEnv(opts),
15358
15371
  // Creds, broker URL and the control token ride a 0600 file; only its path is exported, so the
15359
15372
  // shells, builds and third-party CLIs this session runs no longer inherit live authority.
@@ -15397,7 +15410,8 @@ var claudeConnector = {
15397
15410
  } else {
15398
15411
  mcpConfig = writeLaunchArtifact(artifacts, "cotal-mcp-", "mcp.json", JSON.stringify({ mcpServers }, null, 2));
15399
15412
  }
15400
- args.push("--strict-mcp-config", "--mcp-config", mcpConfig);
15413
+ if (exclusive) args.push("--strict-mcp-config");
15414
+ args.push("--mcp-config", mcpConfig);
15401
15415
  if (agentFile) env.COTAL_AGENT_FILE = agentFile;
15402
15416
  if (def?.persona) {
15403
15417
  args.push("--append-system-prompt-file", writeLaunchArtifact(artifacts, "cotal-claude-persona-", "persona.md", def.persona));