chat-recall 0.7.3 → 0.7.5

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/mcp.js CHANGED
@@ -6744,13 +6744,13 @@ async function parseCodexSubagents(sessionPath) {
6744
6744
  if (meta?.type !== "session_meta")
6745
6745
  continue;
6746
6746
  const payload = meta.payload || {};
6747
- const spawn4 = payload.source?.subagent?.thread_spawn;
6748
- if (!spawn4)
6747
+ const spawn5 = payload.source?.subagent?.thread_spawn;
6748
+ if (!spawn5)
6749
6749
  continue;
6750
- if (spawn4.parent_thread_id !== parentId)
6750
+ if (spawn5.parent_thread_id !== parentId)
6751
6751
  continue;
6752
6752
  const id = fileName.replace(/\.jsonl$/i, "");
6753
- const role = String(spawn4.agent_role || payload.agent_role || "").toLowerCase();
6753
+ const role = String(spawn5.agent_role || payload.agent_role || "").toLowerCase();
6754
6754
  const kind = role.includes("compact") ? "compact" : role.includes("aside") ? "aside" : role.includes("worker") || role.includes("explore") ? "explore" : "other";
6755
6755
  let messages = [];
6756
6756
  try {
@@ -6762,8 +6762,8 @@ async function parseCodexSubagents(sessionPath) {
6762
6762
  subagents.push({
6763
6763
  id,
6764
6764
  kind,
6765
- agentType: spawn4.agent_role || payload.agent_role,
6766
- description: spawn4.agent_nickname || payload.agent_nickname,
6765
+ agentType: spawn5.agent_role || payload.agent_role,
6766
+ description: spawn5.agent_nickname || payload.agent_nickname,
6767
6767
  filePath,
6768
6768
  messageCount: messages.length,
6769
6769
  toolUseCount,
@@ -7093,7 +7093,7 @@ async function archiveRawSession(store2, sessionId) {
7093
7093
  return await store2.putRawSession(sessionId, exp.tool, Math.floor(exp.mtime), gz, size);
7094
7094
  }
7095
7095
  function detectForkPredecessor(sessionPath) {
7096
- const { readFileSync: readFileSync44, readdirSync: readdirSync35 } = requireFs();
7096
+ const { readFileSync: readFileSync44, readdirSync: readdirSync34 } = requireFs();
7097
7097
  const { dirname: dirname21, join: join53, basename: basename24 } = requirePath();
7098
7098
  let text;
7099
7099
  try {
@@ -7123,7 +7123,7 @@ function detectForkPredecessor(sessionPath) {
7123
7123
  const self = basename24(sessionPath);
7124
7124
  let entries;
7125
7125
  try {
7126
- entries = readdirSync35(dir);
7126
+ entries = readdirSync34(dir);
7127
7127
  } catch {
7128
7128
  return null;
7129
7129
  }
@@ -9921,8 +9921,35 @@ var init_codex2 = __esm({
9921
9921
  configToml() {
9922
9922
  return join15(this.homeDir(), "config.toml");
9923
9923
  }
9924
- pluginsDir() {
9925
- return join15(this.homeDir(), ".tmp", "plugins", "plugins");
9924
+ /**
9925
+ * Plugins that are INSTALLED, one folder each:
9926
+ * `plugins/cache/<marketplace>/<plugin>/<version>/`, newest version.
9927
+ *
9928
+ * `.tmp/plugins/plugins/` is Codex's copy of the plugin catalog (62 plugins
9929
+ * and a README on one machine), not what is installed. Reading it reported
9930
+ * every catalog plugin's skills and MCP servers as the user's own, and a
9931
+ * pull then installed them into every tool: build-ios-apps' xcodebuildmcp
9932
+ * reached a Linux PC that never installed the plugin.
9933
+ */
9934
+ installedPluginDirs() {
9935
+ const cache4 = join15(this.homeDir(), "plugins", "cache");
9936
+ const out = [];
9937
+ const dirs = (p) => {
9938
+ try {
9939
+ return readdirSync10(p, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
9940
+ } catch {
9941
+ return [];
9942
+ }
9943
+ };
9944
+ for (const market of dirs(cache4)) {
9945
+ for (const plugin of dirs(join15(cache4, market))) {
9946
+ const versions = dirs(join15(cache4, market, plugin)).sort((a, b) => a.localeCompare(b, void 0, { numeric: true }));
9947
+ const newest = versions[versions.length - 1];
9948
+ if (newest)
9949
+ out.push({ name: plugin, dir: join15(cache4, market, plugin, newest) });
9950
+ }
9951
+ }
9952
+ return out;
9926
9953
  }
9927
9954
  /** OpenAI-bundled, read-only system skills (imagegen, skill-creator, …). */
9928
9955
  skillsSystemDir() {
@@ -10170,8 +10197,8 @@ var init_codex2 = __esm({
10170
10197
  } catch {
10171
10198
  continue;
10172
10199
  }
10173
- const spawn4 = meta?.payload?.source?.subagent?.thread_spawn;
10174
- if (!spawn4 || spawn4.parent_thread_id !== parentUuid)
10200
+ const spawn5 = meta?.payload?.source?.subagent?.thread_spawn;
10201
+ if (!spawn5 || spawn5.parent_thread_id !== parentUuid)
10175
10202
  continue;
10176
10203
  events.push(...this.parseRollout(path2));
10177
10204
  }
@@ -10364,10 +10391,10 @@ var init_codex2 = __esm({
10364
10391
  const meta = JSON.parse(first);
10365
10392
  const payload = meta?.payload || {};
10366
10393
  projectPath = payload.cwd || "";
10367
- const spawn4 = payload.source?.subagent?.thread_spawn;
10368
- if (spawn4 || payload.agent_role || payload.agent_nickname) {
10394
+ const spawn5 = payload.source?.subagent?.thread_spawn;
10395
+ if (spawn5 || payload.agent_role || payload.agent_nickname) {
10369
10396
  isSubagent = true;
10370
- parentId = spawn4?.parent_thread_id || null;
10397
+ parentId = spawn5?.parent_thread_id || null;
10371
10398
  }
10372
10399
  }
10373
10400
  } catch {
@@ -17947,14 +17974,11 @@ var init_skills_source = __esm({
17947
17974
  }
17948
17975
  if (!isSourceEnabled("codex", "skills"))
17949
17976
  return;
17950
- const codexPluginsDir = codexBackend.pluginsDir();
17951
- if (existsSync37(codexPluginsDir)) {
17952
- for (const pluginName of readdirSync26(codexPluginsDir)) {
17953
- const skillsDir = join40(codexPluginsDir, pluginName, "skills");
17954
- if (!existsSync37(skillsDir))
17955
- continue;
17956
- yield* this.fromSkillRoot({ path: skillsDir, tool: "codex" }, pluginName);
17957
- }
17977
+ for (const { name: pluginName, dir } of codexBackend.installedPluginDirs()) {
17978
+ const skillsDir = join40(dir, "skills");
17979
+ if (!existsSync37(skillsDir))
17980
+ continue;
17981
+ yield* this.fromSkillRoot({ path: skillsDir, tool: "codex" }, pluginName);
17958
17982
  }
17959
17983
  }
17960
17984
  async *fromSkillRoot(root, pluginName) {
@@ -18067,7 +18091,7 @@ var init_skills_source = __esm({
18067
18091
  });
18068
18092
 
18069
18093
  // ../engine/dist/parsers/mcps-source.js
18070
- import { existsSync as existsSync38, readdirSync as readdirSync27, readFileSync as readFileSync29, statSync as statSync35 } from "fs";
18094
+ import { existsSync as existsSync38, readFileSync as readFileSync29, statSync as statSync35 } from "fs";
18071
18095
  import { homedir as homedir15 } from "os";
18072
18096
  import { join as join41 } from "path";
18073
18097
  function readJson(path2) {
@@ -18179,16 +18203,13 @@ var init_mcps_source = __esm({
18179
18203
  scope: "user"
18180
18204
  });
18181
18205
  yield* this.fromCodexToml(codexBackend.configToml());
18182
- const pluginsDir = codexBackend.pluginsDir();
18183
- if (existsSync38(pluginsDir)) {
18184
- for (const plugin of readdirSync27(pluginsDir)) {
18185
- const mcpPath = join41(pluginsDir, plugin, ".mcp.json");
18186
- yield* this.fromObject(readJson(mcpPath)?.mcpServers, {
18187
- tool: "codex",
18188
- filePath: mcpPath,
18189
- scope: "plugin"
18190
- });
18191
- }
18206
+ for (const { dir } of codexBackend.installedPluginDirs()) {
18207
+ const mcpPath = join41(dir, ".mcp.json");
18208
+ yield* this.fromObject(readJson(mcpPath)?.mcpServers, {
18209
+ tool: "codex",
18210
+ filePath: mcpPath,
18211
+ scope: "plugin"
18212
+ });
18192
18213
  }
18193
18214
  }
18194
18215
  async *fromObject(obj, ctx) {
@@ -18226,7 +18247,12 @@ var init_mcps_source = __esm({
18226
18247
  // MCP's url into a shell string, and `alwaysAllow` is truncated to
18227
18248
  // 8 for the preview — rebuilding from either produces a broken or
18228
18249
  // half-permissioned server, silently.
18229
- spec
18250
+ spec,
18251
+ // Where this registration runs. A server that starts through npx
18252
+ // passes the command check on any machine, so this is what tells a
18253
+ // pull that it came from another platform (see toolkit-pull.ts).
18254
+ platform: process.platform,
18255
+ arch: process.arch
18230
18256
  }
18231
18257
  };
18232
18258
  }
@@ -18388,7 +18414,7 @@ var init_toolkit_format = __esm({
18388
18414
  });
18389
18415
 
18390
18416
  // ../engine/dist/parsers/slash-commands-source.js
18391
- import { existsSync as existsSync39, readdirSync as readdirSync28, readFileSync as readFileSync30, statSync as statSync36 } from "fs";
18417
+ import { existsSync as existsSync39, readdirSync as readdirSync27, readFileSync as readFileSync30, statSync as statSync36 } from "fs";
18392
18418
  import { join as join42, basename as basename20 } from "path";
18393
18419
  function rebuildBody2(content) {
18394
18420
  const { text, redacted } = redactInlineSecrets(content);
@@ -18443,7 +18469,7 @@ var init_slash_commands_source = __esm({
18443
18469
  const projectsRoot = claudeBackend.projectsDir();
18444
18470
  if (existsSync39(projectsRoot)) {
18445
18471
  try {
18446
- for (const entry of readdirSync28(projectsRoot, { withFileTypes: true })) {
18472
+ for (const entry of readdirSync27(projectsRoot, { withFileTypes: true })) {
18447
18473
  if (!entry.isDirectory() || entry.name.startsWith("."))
18448
18474
  continue;
18449
18475
  const projectPath = resolveProjectDirName(entry.name);
@@ -18475,7 +18501,7 @@ var init_slash_commands_source = __esm({
18475
18501
  const ext = root.format === "toml" ? ".toml" : ".md";
18476
18502
  let files;
18477
18503
  try {
18478
- files = readdirSync28(root.path).filter((f) => f.endsWith(ext));
18504
+ files = readdirSync27(root.path).filter((f) => f.endsWith(ext));
18479
18505
  } catch {
18480
18506
  continue;
18481
18507
  }
@@ -18535,7 +18561,7 @@ var init_slash_commands_source = __esm({
18535
18561
  });
18536
18562
 
18537
18563
  // ../engine/dist/parsers/subagents-source.js
18538
- import { existsSync as existsSync40, readdirSync as readdirSync29, readFileSync as readFileSync31, statSync as statSync37 } from "fs";
18564
+ import { existsSync as existsSync40, readdirSync as readdirSync28, readFileSync as readFileSync31, statSync as statSync37 } from "fs";
18539
18565
  import { join as join43, basename as basename21 } from "path";
18540
18566
  function rebuildBody3(content) {
18541
18567
  const { text, redacted } = redactInlineSecrets(content);
@@ -18592,7 +18618,7 @@ var init_subagents_source = __esm({
18592
18618
  const projectsRoot = claudeBackend.projectsDir();
18593
18619
  if (existsSync40(projectsRoot)) {
18594
18620
  try {
18595
- for (const entry of readdirSync29(projectsRoot, { withFileTypes: true })) {
18621
+ for (const entry of readdirSync28(projectsRoot, { withFileTypes: true })) {
18596
18622
  if (!entry.isDirectory() || entry.name.startsWith("."))
18597
18623
  continue;
18598
18624
  const projectPath = resolveProjectDirName(entry.name);
@@ -18624,7 +18650,7 @@ var init_subagents_source = __esm({
18624
18650
  const ext = root.format === "toml" ? ".toml" : ".md";
18625
18651
  let files;
18626
18652
  try {
18627
- files = readdirSync29(root.path).filter((f) => f.endsWith(ext));
18653
+ files = readdirSync28(root.path).filter((f) => f.endsWith(ext));
18628
18654
  } catch {
18629
18655
  continue;
18630
18656
  }
@@ -18685,7 +18711,7 @@ var init_subagents_source = __esm({
18685
18711
  });
18686
18712
 
18687
18713
  // ../engine/dist/parsers/hooks-source.js
18688
- import { existsSync as existsSync41, readFileSync as readFileSync32, readdirSync as readdirSync30, statSync as statSync38 } from "fs";
18714
+ import { existsSync as existsSync41, readFileSync as readFileSync32, readdirSync as readdirSync29, statSync as statSync38 } from "fs";
18689
18715
  import { join as join44 } from "path";
18690
18716
  function readJson2(path2) {
18691
18717
  if (!existsSync41(path2))
@@ -18712,7 +18738,7 @@ var init_hooks_source = __esm({
18712
18738
  const projectsRoot = claudeBackend.projectsDir();
18713
18739
  if (existsSync41(projectsRoot)) {
18714
18740
  try {
18715
- for (const entry of readdirSync30(projectsRoot, { withFileTypes: true })) {
18741
+ for (const entry of readdirSync29(projectsRoot, { withFileTypes: true })) {
18716
18742
  if (!entry.isDirectory() || entry.name.startsWith("."))
18717
18743
  continue;
18718
18744
  const projectPath = resolveProjectDirName(entry.name);
@@ -18808,7 +18834,7 @@ var init_hooks_source = __esm({
18808
18834
  });
18809
18835
 
18810
18836
  // ../engine/dist/parsers/plugins-source.js
18811
- import { existsSync as existsSync42, readFileSync as readFileSync33, readdirSync as readdirSync31, statSync as statSync39 } from "fs";
18837
+ import { existsSync as existsSync42, readFileSync as readFileSync33, readdirSync as readdirSync30, statSync as statSync39 } from "fs";
18812
18838
  import { homedir as homedir16 } from "os";
18813
18839
  import { join as join45 } from "path";
18814
18840
  function readJson3(path2) {
@@ -18870,7 +18896,7 @@ var init_plugins_source = __esm({
18870
18896
  if (isSourceEnabled("agy", "extensions") && existsSync42(gemRoot)) {
18871
18897
  let entries;
18872
18898
  try {
18873
- entries = readdirSync31(gemRoot);
18899
+ entries = readdirSync30(gemRoot);
18874
18900
  } catch {
18875
18901
  entries = [];
18876
18902
  }
@@ -18914,16 +18940,8 @@ var init_plugins_source = __esm({
18914
18940
  };
18915
18941
  }
18916
18942
  }
18917
- const codexPluginsRoot = codexBackend.pluginsDir();
18918
- if (isSourceEnabled("codex", "plugins") && existsSync42(codexPluginsRoot)) {
18919
- let entries;
18920
- try {
18921
- entries = readdirSync31(codexPluginsRoot);
18922
- } catch {
18923
- entries = [];
18924
- }
18925
- for (const name of entries) {
18926
- const pluginDir = join45(codexPluginsRoot, name);
18943
+ if (isSourceEnabled("codex", "plugins")) {
18944
+ for (const { name, dir: pluginDir } of codexBackend.installedPluginDirs()) {
18927
18945
  let st;
18928
18946
  try {
18929
18947
  st = statSync39(pluginDir);
@@ -19037,7 +19055,8 @@ var init_all_sources = __esm({
19037
19055
 
19038
19056
  // ../engine/dist/core/extractor-version.js
19039
19057
  function extractorVersionForItem(id, sourceType) {
19040
- return extractorVersionForTool(toolOfId(id)) + (ITEM_SOURCE_BUMP[sourceType] ?? 0);
19058
+ const tool = toolOfId(id);
19059
+ return extractorVersionForTool(tool) + (ITEM_SOURCE_BUMP[sourceType] ?? 0) + (ITEM_TOOL_SOURCE_BUMP[`${tool}:${sourceType}`] ?? 0);
19041
19060
  }
19042
19061
  function toolOfId(id) {
19043
19062
  if (id.startsWith("agy_"))
@@ -19058,17 +19077,21 @@ function extractorVersionForTool(tool) {
19058
19077
  function extractorVersionForId(id) {
19059
19078
  return extractorVersionForTool(toolOfId(id));
19060
19079
  }
19061
- var BASE_EXTRACTOR_VERSION, ITEM_SOURCE_BUMP, TOOL_EXTRACTOR_BUMP, EXTRACTOR_VERSION;
19080
+ var BASE_EXTRACTOR_VERSION, ITEM_SOURCE_BUMP, ITEM_TOOL_SOURCE_BUMP, TOOL_EXTRACTOR_BUMP, EXTRACTOR_VERSION;
19062
19081
  var init_extractor_version = __esm({
19063
19082
  "../engine/dist/core/extractor-version.js"() {
19064
19083
  "use strict";
19065
19084
  BASE_EXTRACTOR_VERSION = 2;
19066
19085
  ITEM_SOURCE_BUMP = {
19067
- mcp: 1,
19086
+ mcp: 2,
19068
19087
  skill: 1,
19069
19088
  agent: 1,
19070
19089
  command: 1
19071
19090
  };
19091
+ ITEM_TOOL_SOURCE_BUMP = {
19092
+ "codex:skill": 1,
19093
+ "codex:plugin": 1
19094
+ };
19072
19095
  TOOL_EXTRACTOR_BUMP = {
19073
19096
  agy: 3,
19074
19097
  claude: 1,
@@ -19177,6 +19200,28 @@ function saveItemVersions(server, versions) {
19177
19200
  writeFileSync7(tmp, JSON.stringify(all));
19178
19201
  renameSync5(tmp, path2);
19179
19202
  }
19203
+ function loadInventoryHashes(server) {
19204
+ try {
19205
+ const parsed = JSON.parse(readFileSync34(inventoryPath(), "utf-8"));
19206
+ return { ...(parsed && typeof parsed === "object" ? parsed[server] : null) || {} };
19207
+ } catch {
19208
+ return {};
19209
+ }
19210
+ }
19211
+ function saveInventoryHashes(server, hashes) {
19212
+ let all = {};
19213
+ try {
19214
+ all = JSON.parse(readFileSync34(inventoryPath(), "utf-8"));
19215
+ } catch {
19216
+ }
19217
+ if (!all || typeof all !== "object") all = {};
19218
+ all[server] = { ...all[server] || {}, ...hashes };
19219
+ const path2 = inventoryPath();
19220
+ mkdirSync10(dirname17(path2), { recursive: true });
19221
+ const tmp = `${path2}.tmp`;
19222
+ writeFileSync7(tmp, JSON.stringify(all));
19223
+ renameSync5(tmp, path2);
19224
+ }
19180
19225
  function getSyncedRows(server) {
19181
19226
  const data = load();
19182
19227
  const out = /* @__PURE__ */ new Map();
@@ -19288,7 +19333,7 @@ function syncMode(row, mtime, fileSize, extractorVersion, isAppendOnly) {
19288
19333
  if (row.m >= Math.floor(mtime)) return "skip";
19289
19334
  return "full";
19290
19335
  }
19291
- var ledgerPath, cache3, LEDGER_FLUSH_MS, pendingWrite, flushTimer, itemVersionsPath, itemVersionsCache, unackedCursorAttempts, reconcilePath, reconcileCache;
19336
+ var ledgerPath, cache3, LEDGER_FLUSH_MS, pendingWrite, flushTimer, itemVersionsPath, itemVersionsCache, inventoryPath, unackedCursorAttempts, reconcilePath, reconcileCache;
19292
19337
  var init_sync_ledger = __esm({
19293
19338
  "src/sync-ledger.ts"() {
19294
19339
  "use strict";
@@ -19301,6 +19346,7 @@ var init_sync_ledger = __esm({
19301
19346
  flushTimer = null;
19302
19347
  itemVersionsPath = () => join46(getDataDir(), "item-versions.json");
19303
19348
  itemVersionsCache = null;
19349
+ inventoryPath = () => join46(getDataDir(), "toolkit-inventory.json");
19304
19350
  unackedCursorAttempts = 0;
19305
19351
  reconcilePath = () => join46(getDataDir(), "sync-field-reconcile.json");
19306
19352
  reconcileCache = null;
@@ -19449,7 +19495,7 @@ var init_http = __esm({
19449
19495
  CLI_VERSION = JSON.parse(readFileSync36(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
19450
19496
  } catch {
19451
19497
  try {
19452
- if (true) CLI_VERSION = "0.7.3";
19498
+ if (true) CLI_VERSION = "0.7.5";
19453
19499
  } catch {
19454
19500
  }
19455
19501
  }
@@ -19948,7 +19994,7 @@ var init_telemetry = __esm({
19948
19994
  init_sync_client();
19949
19995
  cliVersion2 = () => {
19950
19996
  try {
19951
- return true ? "0.7.3" : "dev";
19997
+ return true ? "0.7.5" : "dev";
19952
19998
  } catch {
19953
19999
  return "dev";
19954
20000
  }
@@ -20117,7 +20163,7 @@ __export(auto_update_exports, {
20117
20163
  import { execSync as execSync4, execFileSync as execFileSync3 } from "node:child_process";
20118
20164
  import { readFileSync as readFileSync37, existsSync as existsSync45 } from "node:fs";
20119
20165
  import { fileURLToPath as fileURLToPath2 } from "node:url";
20120
- import { writeFileSync as writeFileSync9, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync32, statSync as statSync41, mkdirSync as mkdirSync12 } from "node:fs";
20166
+ import { writeFileSync as writeFileSync9, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync31, statSync as statSync41, mkdirSync as mkdirSync12 } from "node:fs";
20121
20167
  import { join as join48, dirname as dirname19, sep as sep3, posix as pathPosix, win32 as pathWin32 } from "node:path";
20122
20168
  import { tmpdir as tmpdir3, homedir as homedir17 } from "node:os";
20123
20169
  import { createHash as createHash10 } from "node:crypto";
@@ -20154,7 +20200,7 @@ function planAutoUpdate(base, caps, ownVersion2, flag) {
20154
20200
  if (!rel || !rel.version || !rel.sha256) return { update: false, reason: "server advertises no CLI release" };
20155
20201
  if (!isAutoUpdateEnabled(caps.edition, flag)) return { update: false, reason: `auto-update disabled (edition ${caps.edition ?? "?"})` };
20156
20202
  if (compareVersions(rel.version, ownVersion2) <= 0) return { update: false, reason: `already current (own ${ownVersion2} >= server ${rel.version})` };
20157
- return { update: true, reason: `server ${rel.version} > own ${ownVersion2}`, url: tarballUrl(base), sha256: rel.sha256, from: ownVersion2, to: rel.version };
20203
+ return { update: true, reason: `server ${rel.version} > own ${ownVersion2}`, url: tarballUrl(base, rel.version), fallbackUrl: tarballUrl(base), sha256: rel.sha256, from: ownVersion2, to: rel.version };
20158
20204
  }
20159
20205
  function sweepStaleStaging(root = tmpdir3(), now = Date.now()) {
20160
20206
  const MKDTEMP_SUFFIX = /^cr-update-.{6}$/s;
@@ -20162,7 +20208,7 @@ function sweepStaleStaging(root = tmpdir3(), now = Date.now()) {
20162
20208
  let removed = 0;
20163
20209
  let entries;
20164
20210
  try {
20165
- entries = readdirSync32(root);
20211
+ entries = readdirSync31(root);
20166
20212
  } catch {
20167
20213
  return 0;
20168
20214
  }
@@ -20172,7 +20218,7 @@ function sweepStaleStaging(root = tmpdir3(), now = Date.now()) {
20172
20218
  try {
20173
20219
  const st = statSync41(dir);
20174
20220
  if (!st.isDirectory() || now - st.mtimeMs < COLD_MS) continue;
20175
- const inside = readdirSync32(dir);
20221
+ const inside = readdirSync31(dir);
20176
20222
  if (inside.length > 1 || inside.length === 1 && inside[0] !== "chat-recall.tgz") continue;
20177
20223
  rmSync5(dir, { recursive: true, force: true });
20178
20224
  removed++;
@@ -20187,7 +20233,13 @@ async function executeAutoUpdate(plan, deps) {
20187
20233
  try {
20188
20234
  buf = await deps.download(plan.url);
20189
20235
  } catch (e) {
20190
- return { updated: false, reason: `download failed: ${e instanceof Error ? e.message : e}` };
20236
+ const notFound = e instanceof Error && e.message === "HTTP 404";
20237
+ if (!notFound || !plan.fallbackUrl) return { updated: false, reason: `download failed: ${e instanceof Error ? e.message : e}` };
20238
+ try {
20239
+ buf = await deps.download(plan.fallbackUrl);
20240
+ } catch (e2) {
20241
+ return { updated: false, reason: `download failed: ${e2 instanceof Error ? e2.message : e2}` };
20242
+ }
20191
20243
  }
20192
20244
  const got = createHash10("sha256").update(buf).digest("hex");
20193
20245
  if (got !== plan.sha256) return { updated: false, reason: `checksum mismatch (expected ${plan.sha256.slice(0, 12)}\u2026, got ${got.slice(0, 12)}\u2026) \u2014 NOT installing` };
@@ -20363,7 +20415,7 @@ var init_auto_update = __esm({
20363
20415
  "src/auto-update.ts"() {
20364
20416
  "use strict";
20365
20417
  init_http();
20366
- tarballUrl = (base) => `${base.replace(/\/+$/, "")}/install/chat-recall.tgz`;
20418
+ tarballUrl = (base, version2) => `${base.replace(/\/+$/, "")}/install/${version2 ? `chat-recall-${version2}.tgz` : "chat-recall.tgz"}`;
20367
20419
  GIVE_UP_AFTER = 3;
20368
20420
  reportedStandingSkip = /* @__PURE__ */ new Set();
20369
20421
  }
@@ -20376,6 +20428,7 @@ __export(sync_client_exports, {
20376
20428
  DERIVED_MAX_BYTES: () => DERIVED_MAX_BYTES,
20377
20429
  FULL_BUILD_MAX_BYTES: () => FULL_BUILD_MAX_BYTES,
20378
20430
  SYNC_CHUNK_BYTES: () => SYNC_CHUNK_BYTES,
20431
+ TOOLKIT_INVENTORY_TYPES: () => TOOLKIT_INVENTORY_TYPES,
20379
20432
  _acceptedUploads: () => _acceptedUploads,
20380
20433
  _fetchTenantSyncConfigForTests: () => _fetchTenantSyncConfigForTests,
20381
20434
  _resetCapsCacheForTests: () => _resetCapsCacheForTests,
@@ -20404,7 +20457,7 @@ __export(sync_client_exports, {
20404
20457
  syncSessions: () => syncSessions
20405
20458
  });
20406
20459
  import { join as join49, dirname as dirname20, basename as basename22 } from "node:path";
20407
- import { readFileSync as readFileSync38, writeFileSync as writeFileSync10, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync4, rmSync as rmSync6, chmodSync as chmodSync6, unlinkSync as unlinkSync3, readdirSync as readdirSync33, statSync as statSync42 } from "node:fs";
20460
+ import { readFileSync as readFileSync38, writeFileSync as writeFileSync10, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync4, rmSync as rmSync6, chmodSync as chmodSync6, unlinkSync as unlinkSync3, readdirSync as readdirSync32, statSync as statSync42 } from "node:fs";
20408
20461
  import { tmpdir as tmpdir4, hostname as hostname2 } from "node:os";
20409
20462
  import { createHash as createHash11 } from "node:crypto";
20410
20463
  import { fileURLToPath as fileURLToPath3 } from "node:url";
@@ -20807,7 +20860,7 @@ function reapStaleScanDirs(dir = tmpdir4(), maxAgeMs = 6 * 36e5, now = Date.now(
20807
20860
  let reaped = 0;
20808
20861
  let names;
20809
20862
  try {
20810
- names = readdirSync33(dir);
20863
+ names = readdirSync32(dir);
20811
20864
  } catch {
20812
20865
  return 0;
20813
20866
  }
@@ -21448,17 +21501,24 @@ async function syncToTargetInScope(cred, opts = {}) {
21448
21501
  const seenTools = /* @__PURE__ */ new Set();
21449
21502
  const seenItemKeys = /* @__PURE__ */ new Map();
21450
21503
  const registry = buildSourceRegistry();
21504
+ const inventory = /* @__PURE__ */ new Map();
21505
+ const inventoryFailed = /* @__PURE__ */ new Set();
21451
21506
  for (const sourceType of registry.getRegisteredTypes()) {
21452
21507
  if (sourceType === "session") continue;
21508
+ if (TOOLKIT_INVENTORY_TYPES.has(sourceType)) inventory.set(sourceType, /* @__PURE__ */ new Set());
21453
21509
  for (const source of registry.getAll(sourceType)) {
21454
21510
  let discovered;
21455
21511
  try {
21456
21512
  discovered = source.discover();
21457
21513
  } catch {
21514
+ inventoryFailed.add(sourceType);
21458
21515
  continue;
21459
21516
  }
21460
21517
  try {
21461
21518
  for await (const item of discovered) {
21519
+ if (inventory.has(item.sourceType) && !excluded(item.projectPath || "") && includedProject(item.projectPath || "")) {
21520
+ inventory.get(item.sourceType).add(item.id);
21521
+ }
21462
21522
  const itemTool = toolOfId(item.id);
21463
21523
  seenTools.add(itemTool);
21464
21524
  const itemKey = `${itemTool}:${item.sourceType}`;
@@ -21516,6 +21576,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21516
21576
  redactions += count.redactions;
21517
21577
  }
21518
21578
  } catch (err) {
21579
+ inventoryFailed.add(sourceType);
21519
21580
  console.error(`[sync] source '${sourceType}' failed mid-walk (its items may be incomplete this run): ${err instanceof Error ? err.message : err}`);
21520
21581
  }
21521
21582
  }
@@ -21542,6 +21603,27 @@ async function syncToTargetInScope(cred, opts = {}) {
21542
21603
  await kgTripleBatch.drain();
21543
21604
  await findingsBatch.drain();
21544
21605
  await drainInflight();
21606
+ {
21607
+ const sentHashes = loadInventoryHashes(base);
21608
+ const entries = [];
21609
+ const hashes = {};
21610
+ for (const [sourceType, ids] of inventory) {
21611
+ if (inventoryFailed.has(sourceType)) continue;
21612
+ const sorted = [...ids].sort();
21613
+ const hash = createHash11("sha256").update(sorted.join("\n")).digest("hex").slice(0, 32);
21614
+ if (sentHashes[sourceType] === hash) continue;
21615
+ entries.push({ source_type: sourceType, ids: sorted });
21616
+ hashes[sourceType] = hash;
21617
+ }
21618
+ if (entries.length > 0) {
21619
+ try {
21620
+ await post({ inventory: entries });
21621
+ saveInventoryHashes(base, hashes);
21622
+ } catch (err) {
21623
+ console.error(`[sync] toolkit inventory not sent (it is retried next sync): ${err instanceof Error ? err.message : err}`);
21624
+ }
21625
+ }
21626
+ }
21545
21627
  if (seenTools.size > 0) {
21546
21628
  try {
21547
21629
  const rec = {};
@@ -21987,7 +22069,7 @@ async function syncIncremental(opts = {}) {
21987
22069
  lock.release();
21988
22070
  }
21989
22071
  }
21990
- var ownVersion, ERROR_CLASS, STANDING_FAILURE, walkProgressForUpload, lastServerPackSpec, credPath, _securityConfigCache, SECURITY_CONFIG_TTL_MS, _syncConfigCache, EMPTY_SYNC_CONFIG, _fetchTenantSyncConfigForTests, SYNC_RULES_REFUSAL, hashPath, acceptedUploads, MAX_DERIVED_ROW_BYTES, isLocalHost, CAPS_TTL_MS, capsOk, advertisedIngestConc, advertisedGzipBody, FULL_BUILD_MAX_BYTES, DERIVED_MAX_BYTES, SYNC_CHUNK_BYTES, BATCH_SCAN_MAX_BYTES;
22072
+ var ownVersion, ERROR_CLASS, STANDING_FAILURE, walkProgressForUpload, lastServerPackSpec, credPath, _securityConfigCache, SECURITY_CONFIG_TTL_MS, _syncConfigCache, EMPTY_SYNC_CONFIG, _fetchTenantSyncConfigForTests, SYNC_RULES_REFUSAL, hashPath, acceptedUploads, MAX_DERIVED_ROW_BYTES, isLocalHost, CAPS_TTL_MS, capsOk, advertisedIngestConc, advertisedGzipBody, FULL_BUILD_MAX_BYTES, DERIVED_MAX_BYTES, SYNC_CHUNK_BYTES, TOOLKIT_INVENTORY_TYPES, BATCH_SCAN_MAX_BYTES;
21991
22073
  var init_sync_client = __esm({
21992
22074
  "src/sync-client.ts"() {
21993
22075
  "use strict";
@@ -22032,7 +22114,7 @@ var init_sync_client = __esm({
22032
22114
  ownVersion = JSON.parse(readFileSync38(join49(dir, "../package.json"), "utf-8")).version || ownVersion;
22033
22115
  } catch {
22034
22116
  try {
22035
- if (true) ownVersion = "0.7.3";
22117
+ if (true) ownVersion = "0.7.5";
22036
22118
  } catch {
22037
22119
  }
22038
22120
  }
@@ -22062,6 +22144,7 @@ var init_sync_client = __esm({
22062
22144
  FULL_BUILD_MAX_BYTES = Math.max(8, parseInt(process.env.CHAT_RECALL_FULL_BUILD_MAX_MB || "24", 10)) * 1024 * 1024;
22063
22145
  DERIVED_MAX_BYTES = Math.max(8, parseInt(process.env.CHAT_RECALL_DERIVED_MAX_MB || "64", 10)) * 1024 * 1024;
22064
22146
  SYNC_CHUNK_BYTES = 8 * 1024 * 1024;
22147
+ TOOLKIT_INVENTORY_TYPES = /* @__PURE__ */ new Set(["skill", "mcp", "command", "agent", "plugin"]);
22065
22148
  BATCH_SCAN_MAX_BYTES = Math.max(1, parseInt(process.env.CHAT_RECALL_BATCHSCAN_MAX_MB || "64", 10)) * 1024 * 1024;
22066
22149
  }
22067
22150
  });
@@ -22246,6 +22329,59 @@ var init_update_notice = __esm({
22246
22329
  }
22247
22330
  });
22248
22331
 
22332
+ // src/sync-delegate.ts
22333
+ var sync_delegate_exports = {};
22334
+ __export(sync_delegate_exports, {
22335
+ installedIsNewer: () => installedIsNewer,
22336
+ runInstalledSync: () => runInstalledSync
22337
+ });
22338
+ import { spawn as spawn3 } from "node:child_process";
22339
+ function installedIsNewer(running, installed) {
22340
+ return !!installed && compareVersions(installed, running) > 0;
22341
+ }
22342
+ function runInstalledSync(cliPath, timeoutMs = CHILD_TIMEOUT_MS) {
22343
+ return new Promise((resolve3) => {
22344
+ let settled = false;
22345
+ const done = (code) => {
22346
+ if (!settled) {
22347
+ settled = true;
22348
+ resolve3(code);
22349
+ }
22350
+ };
22351
+ let child;
22352
+ try {
22353
+ child = spawn3(process.execPath, [cliPath, "sync"], { env: process.env, stdio: ["ignore", "ignore", "inherit"] });
22354
+ } catch {
22355
+ done(null);
22356
+ return;
22357
+ }
22358
+ const timer = setTimeout(() => {
22359
+ try {
22360
+ child.kill("SIGKILL");
22361
+ } catch {
22362
+ }
22363
+ done(null);
22364
+ }, timeoutMs);
22365
+ timer.unref?.();
22366
+ child.on("error", () => {
22367
+ clearTimeout(timer);
22368
+ done(null);
22369
+ });
22370
+ child.on("exit", (code) => {
22371
+ clearTimeout(timer);
22372
+ done(code);
22373
+ });
22374
+ });
22375
+ }
22376
+ var CHILD_TIMEOUT_MS;
22377
+ var init_sync_delegate = __esm({
22378
+ "src/sync-delegate.ts"() {
22379
+ "use strict";
22380
+ init_auto_update();
22381
+ CHILD_TIMEOUT_MS = 20 * 6e4;
22382
+ }
22383
+ });
22384
+
22249
22385
  // src/service-installer.ts
22250
22386
  var service_installer_exports = {};
22251
22387
  __export(service_installer_exports, {
@@ -22528,7 +22664,7 @@ __export(install_skills_exports, {
22528
22664
  skillsSourceDir: () => skillsSourceDir,
22529
22665
  uninstallSkills: () => uninstallSkills
22530
22666
  });
22531
- import { readdirSync as readdirSync34, existsSync as existsSync47, mkdirSync as mkdirSync15, rmSync as rmSync8, cpSync, writeFileSync as writeFileSync13, readFileSync as readFileSync42, statSync as statSync43, realpathSync as realpathSync4 } from "fs";
22667
+ import { readdirSync as readdirSync33, existsSync as existsSync47, mkdirSync as mkdirSync15, rmSync as rmSync8, cpSync, writeFileSync as writeFileSync13, readFileSync as readFileSync42, statSync as statSync43, realpathSync as realpathSync4 } from "fs";
22532
22668
  import { createHash as createHash12 } from "crypto";
22533
22669
  import { join as join52, basename as basename23 } from "path";
22534
22670
  function skillsSourceDir() {
@@ -22554,7 +22690,7 @@ function skillsFingerprint() {
22554
22690
  }
22555
22691
  function filesUnder(dir, prefix = "") {
22556
22692
  const out = [];
22557
- for (const e of readdirSync34(dir, { withFileTypes: true })) {
22693
+ for (const e of readdirSync33(dir, { withFileTypes: true })) {
22558
22694
  if (e.name === MARKER) continue;
22559
22695
  const rel = prefix ? `${prefix}/${e.name}` : e.name;
22560
22696
  if (e.isDirectory()) out.push(...filesUnder(join52(dir, e.name), rel));
@@ -22568,7 +22704,7 @@ function skillStamp() {
22568
22704
  function bundledSkillNames() {
22569
22705
  const src = skillsSourceDir();
22570
22706
  if (!existsSync47(src)) return [];
22571
- return readdirSync34(src, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync47(join52(src, e.name, "SKILL.md"))).map((e) => e.name).sort();
22707
+ return readdirSync33(src, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync47(join52(src, e.name, "SKILL.md"))).map((e) => e.name).sort();
22572
22708
  }
22573
22709
  function realPathOrSelf(p) {
22574
22710
  try {
@@ -22658,7 +22794,7 @@ function uninstallSkills() {
22658
22794
  perTarget.push({ id: t.id, label: t.label, removed });
22659
22795
  continue;
22660
22796
  }
22661
- for (const entry of readdirSync34(t.dir, { withFileTypes: true })) {
22797
+ for (const entry of readdirSync33(t.dir, { withFileTypes: true })) {
22662
22798
  if (!entry.isDirectory()) continue;
22663
22799
  const dir = join52(t.dir, entry.name);
22664
22800
  if (isManaged(dir) && (names.has(entry.name) || entry.name.startsWith("chat-recall"))) {
@@ -22724,7 +22860,7 @@ var init_install_skills = __esm({
22724
22860
 
22725
22861
  // src/mcp.ts
22726
22862
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
22727
- import { spawn as spawn3 } from "child_process";
22863
+ import { spawn as spawn4 } from "child_process";
22728
22864
  import { createServer, connect } from "node:net";
22729
22865
  import { unlinkSync as unlinkSync4 } from "node:fs";
22730
22866
 
@@ -28576,7 +28712,7 @@ function startLoginChild(server) {
28576
28712
  reject(new Error("could not locate the CLI entry point"));
28577
28713
  return;
28578
28714
  }
28579
- const child = spawn3(process.execPath, [cliPath, "init", "--server", server, "--prompt-json", "--yes"], {
28715
+ const child = spawn4(process.execPath, [cliPath, "init", "--server", server, "--prompt-json", "--yes"], {
28580
28716
  env: process.env,
28581
28717
  stdio: ["ignore", "pipe", "pipe"],
28582
28718
  // Outlives this call by design: it polls until the user approves. Detached
@@ -28637,7 +28773,7 @@ function runIndexChild2(force) {
28637
28773
  resolve3(msg);
28638
28774
  }
28639
28775
  };
28640
- const child = spawn3(process.execPath, args, { env: process.env, stdio: ["ignore", "pipe", "pipe"] });
28776
+ const child = spawn4(process.execPath, args, { env: process.env, stdio: ["ignore", "pipe", "pipe"] });
28641
28777
  child.stdout?.on("data", (d) => {
28642
28778
  out += d;
28643
28779
  });
@@ -28669,6 +28805,14 @@ var SYNC_TICK_MS = 3 * 6e4;
28669
28805
  function startBackgroundSync() {
28670
28806
  const tick = async (scope) => {
28671
28807
  try {
28808
+ const { installedVersion: installedVersion2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
28809
+ const { installedIsNewer: installedIsNewer2, runInstalledSync: runInstalledSync2 } = await Promise.resolve().then(() => (init_sync_delegate(), sync_delegate_exports));
28810
+ const installed = installedVersion2();
28811
+ if (installedIsNewer2(RELAY_VERSION, installed)) {
28812
+ const code = await runInstalledSync2(fileURLToPath5(new URL("./cli.js", import.meta.url)));
28813
+ if (code !== 0) console.error(`[mcp] sync by the installed CLI ${installed} ended with ${code ?? "no exit code"}`);
28814
+ return;
28815
+ }
28672
28816
  const { syncIncremental: syncIncremental2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
28673
28817
  await syncIncremental2({ scope });
28674
28818
  } catch (err) {
@@ -28717,7 +28861,7 @@ function installCrashGuards() {
28717
28861
  reportClientEvent2("mcp_crash", { message: err.stack || err.message });
28718
28862
  });
28719
28863
  }
28720
- var RELAY_VERSION = true ? "0.7.3" : "0.0.0";
28864
+ var RELAY_VERSION = true ? "0.7.5" : "0.0.0";
28721
28865
  function daemonIdleSecs() {
28722
28866
  const raw = process.env.CHAT_RECALL_DAEMON_IDLE_SECS;
28723
28867
  if (raw === void 0) return 900;