@caplets/core 0.27.0 → 0.28.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,53 +1,20 @@
1
+ import { i as __require, o as __toESM, t as __commonJSMin } from "./rolldown-runtime-CE-6LUnI.js";
1
2
  import { A as safeParseAsync$1, C as toJSONSchema, D as parse$3, E as $ZodType, F as NEVER, M as defineLazy, N as normalizeParams, O as parseAsync, P as $constructor, S as datetime, T as $ZodObject, _ as record, a as any, b as unknown, c as custom, d as literal, f as looseObject, g as preprocess, h as optional, i as _null, j as clone, k as safeParse$1, l as discriminatedUnion, m as object$1, o as array, p as number$1, r as _enum, s as boolean, t as ZodNumber$1, u as intersection, v as string, w as _coercedNumber, x as url, y as union } from "./schemas-BoqMu4MG.js";
2
3
  import { a as SERVER_ID_PATTERN, c as isUrl, d as CapletsError, f as errorResult, i as NAMESPACE_ALIAS_LABEL_PATTERN, l as validateHttpActionHeaders, m as toSafeError, n as HEADER_NAME_PATTERN, o as isAllowedHttpBaseUrl, p as redactSecrets, r as HTTP_BASE_URL_PATTERN, s as isAllowedRemoteUrl, t as FORBIDDEN_HEADERS } from "./validation-CWzd2gtn.js";
3
4
  import { generatedToolInputJsonSchema, generatedToolInputJsonSchemaForCaplet, generatedToolInputSchemaForCaplet, mcpOperations, operations } from "./generated-tool-input-schema.js";
4
- import { f as observedOutputShapeKey, i as observeOutputShape, r as normalizedObservableValue, t as usefulOutputSchema, u as FileObservedOutputShapeStore } from "./observed-output-shapes-DuP7mJQf.js";
5
- import { createRequire } from "node:module";
6
- import { accessSync, chmodSync, constants, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, watch, writeFileSync } from "node:fs";
5
+ import { f as observedOutputShapeKey, g as stableJsonStringify, h as schemaHash, i as observeOutputShape, r as normalizedObservableValue, t as usefulOutputSchema, u as FileObservedOutputShapeStore } from "./observed-output-shapes-DuP7mJQf.js";
6
+ import { accessSync, chmodSync, constants, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, watch, writeFileSync } from "node:fs";
7
7
  import { basename, delimiter, dirname, extname, isAbsolute, join, parse, posix, relative, resolve, sep, win32 } from "node:path";
8
8
  import { spawn } from "node:child_process";
9
9
  import process$1 from "node:process";
10
10
  import { PassThrough } from "node:stream";
11
11
  import { createServer } from "node:http";
12
12
  import { createCipheriv, createDecipheriv, createHash, createHmac, randomBytes, randomUUID } from "node:crypto";
13
- import { homedir } from "node:os";
13
+ import { arch, homedir, platform } from "node:os";
14
14
  import { Buffer as Buffer$1 } from "node:buffer";
15
15
  import { readFile } from "node:fs/promises";
16
16
  import ts from "typescript";
17
17
  import { getQuickJS, shouldInterruptAfterDeadline } from "quickjs-emscripten";
18
- //#region \0rolldown/runtime.js
19
- var __create = Object.create;
20
- var __defProp = Object.defineProperty;
21
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
22
- var __getOwnPropNames = Object.getOwnPropertyNames;
23
- var __getProtoOf = Object.getPrototypeOf;
24
- var __hasOwnProp = Object.prototype.hasOwnProperty;
25
- var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
26
- var __exportAll = (all, no_symbols) => {
27
- let target = {};
28
- for (var name in all) __defProp(target, name, {
29
- get: all[name],
30
- enumerable: true
31
- });
32
- if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
33
- return target;
34
- };
35
- var __copyProps = (to, from, except, desc) => {
36
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
37
- key = keys[i];
38
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
39
- get: ((k) => from[k]).bind(null, key),
40
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
41
- });
42
- }
43
- return to;
44
- };
45
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
46
- value: mod,
47
- enumerable: true
48
- }) : target, mod));
49
- var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
50
- //#endregion
51
18
  //#region ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v3/helpers/util.js
52
19
  var util$1;
53
20
  (function(util) {
@@ -17242,6 +17209,18 @@ function defaultConfigPath(env = process.env, home = homedir(), platform = proce
17242
17209
  function defaultAuthDir(env = process.env, home = homedir(), platform = process.platform) {
17243
17210
  return (platform === "win32" ? win32.join : posix.join)(defaultStateBaseDir(env, home, platform), "caplets", "auth");
17244
17211
  }
17212
+ function defaultTelemetryStateDir(env = process.env, home = homedir(), platform = process.platform) {
17213
+ return (platform === "win32" ? win32.join : posix.join)(defaultStateBaseDir(env, home, platform), "caplets", "telemetry");
17214
+ }
17215
+ function defaultTelemetryIdentityPath(env = process.env, home = homedir(), platform = process.platform) {
17216
+ return (platform === "win32" ? win32.join : posix.join)(defaultTelemetryStateDir(env, home, platform), "identity.json");
17217
+ }
17218
+ function defaultTelemetryNoticePath(env = process.env, home = homedir(), platform = process.platform) {
17219
+ return (platform === "win32" ? win32.join : posix.join)(defaultTelemetryStateDir(env, home, platform), "notice.json");
17220
+ }
17221
+ function defaultTelemetryDeliveryHealthPath(env = process.env, home = homedir(), platform = process.platform) {
17222
+ return (platform === "win32" ? win32.join : posix.join)(defaultTelemetryStateDir(env, home, platform), "delivery-health.json");
17223
+ }
17245
17224
  function defaultArtifactDir(env = process.env, home = homedir(), platform = process.platform) {
17246
17225
  return (platform === "win32" ? win32.join : posix.join)(defaultStateBaseDir(env, home, platform), "caplets", "artifacts");
17247
17226
  }
@@ -17256,6 +17235,10 @@ function defaultObservedOutputShapeCacheDir(env = process.env, home = homedir(),
17256
17235
  const DEFAULT_CONFIG_PATH = defaultConfigPath();
17257
17236
  const DEFAULT_AUTH_DIR = defaultAuthDir();
17258
17237
  const DEFAULT_ARTIFACT_DIR = defaultArtifactDir();
17238
+ const DEFAULT_TELEMETRY_STATE_DIR = defaultTelemetryStateDir();
17239
+ defaultTelemetryIdentityPath();
17240
+ defaultTelemetryNoticePath();
17241
+ defaultTelemetryDeliveryHealthPath();
17259
17242
  const DEFAULT_COMPLETION_CACHE_DIR = defaultCompletionCacheDir();
17260
17243
  const DEFAULT_OBSERVED_OUTPUT_SHAPE_CACHE_DIR = defaultObservedOutputShapeCacheDir();
17261
17244
  const PROJECT_CONFIG_FILE = join(".caplets", "config.json");
@@ -18627,8 +18610,8 @@ function compactToolSafetyHints(tool) {
18627
18610
  };
18628
18611
  }
18629
18612
  function compactToolSchemaHints(tool) {
18630
- const schema = isRecord$7(tool.inputSchema) ? tool.inputSchema : void 0;
18631
- const properties = isRecord$7(schema?.properties) ? schema.properties : {};
18613
+ const schema = isRecord$9(tool.inputSchema) ? tool.inputSchema : void 0;
18614
+ const properties = isRecord$9(schema?.properties) ? schema.properties : {};
18632
18615
  const acceptedArgs = Object.keys(properties).sort();
18633
18616
  const requiredArgs = Array.isArray(schema?.required) ? schema.required.filter((value) => typeof value === "string").sort() : [];
18634
18617
  const argsTemplate = compactArgsTemplate(properties, requiredArgs, acceptedArgs);
@@ -18652,7 +18635,7 @@ function compactArgsTemplate(properties, requiredArgs, acceptedArgs) {
18652
18635
  if (templateArgs.length === 0 || templateArgs.length > 4) return void 0;
18653
18636
  if (requiredArgs.length === 0 && acceptedArgs.length > 3) return void 0;
18654
18637
  const entries = templateArgs.flatMap((name) => {
18655
- const value = placeholderForSchema(isRecord$7(properties[name]) ? properties[name] : void 0);
18638
+ const value = placeholderForSchema(isRecord$9(properties[name]) ? properties[name] : void 0);
18656
18639
  return value === void 0 ? [] : [[name, value]];
18657
18640
  });
18658
18641
  return entries.length === templateArgs.length ? Object.fromEntries(entries) : void 0;
@@ -18672,13 +18655,13 @@ function placeholderForSchema(schema) {
18672
18655
  }
18673
18656
  }
18674
18657
  function compactToolSelectionHints(tool) {
18675
- if (!isRecord$7(tool)) return {};
18658
+ if (!isRecord$9(tool)) return {};
18676
18659
  return {
18677
18660
  ...typeof tool.useWhen === "string" && tool.useWhen.trim() ? { useWhen: tool.useWhen.trim() } : {},
18678
18661
  ...typeof tool.avoidWhen === "string" && tool.avoidWhen.trim() ? { avoidWhen: tool.avoidWhen.trim() } : {}
18679
18662
  };
18680
18663
  }
18681
- function isRecord$7(value) {
18664
+ function isRecord$9(value) {
18682
18665
  return value !== null && typeof value === "object" && !Array.isArray(value);
18683
18666
  }
18684
18667
  function sameServerConfig(left, right) {
@@ -18753,7 +18736,7 @@ function markdownCallToolResultContent(result, context = {}) {
18753
18736
  return textContent(renderStructuredMarkdown(result, context));
18754
18737
  }
18755
18738
  function hasRenderableStructuredContent(value) {
18756
- if (!isRecord$6(value)) return false;
18739
+ if (!isRecord$8(value)) return false;
18757
18740
  return Object.keys(value).some((key) => key !== "caplets" && key !== "elapsedMs");
18758
18741
  }
18759
18742
  function renderStructuredMarkdown(value, context) {
@@ -18887,13 +18870,13 @@ function renderErrorMarkdown(value, title) {
18887
18870
  ].join("\n");
18888
18871
  }
18889
18872
  function isDiscoveryWrapper(value) {
18890
- return isRecord$6(value) && "result" in value;
18873
+ return isRecord$8(value) && "result" in value;
18891
18874
  }
18892
18875
  function isErrorStructuredContent(value) {
18893
- return isRecord$6(value) && "error" in value;
18876
+ return isRecord$8(value) && "error" in value;
18894
18877
  }
18895
18878
  function isHttpLikeResult(value) {
18896
- return isRecord$6(value) && ("status" in value || "statusText" in value || "body" in value);
18879
+ return isRecord$8(value) && ("status" in value || "statusText" in value || "body" in value);
18897
18880
  }
18898
18881
  function isGraphQlHttpResult(value) {
18899
18882
  if (!isHttpLikeResult(value)) return false;
@@ -18901,7 +18884,7 @@ function isGraphQlHttpResult(value) {
18901
18884
  return Boolean(body && ("data" in body || "errors" in body));
18902
18885
  }
18903
18886
  function isCliResult(value) {
18904
- return isRecord$6(value) && ("exitCode" in value || "stdout" in value || "stderr" in value);
18887
+ return isRecord$8(value) && ("exitCode" in value || "stdout" in value || "stderr" in value);
18905
18888
  }
18906
18889
  function renderBodyValue(value) {
18907
18890
  if (value === void 0) return "_No response body._";
@@ -18969,8 +18952,8 @@ function compactListHints(record) {
18969
18952
  const acceptedArgs = stringArrayValue(record.acceptedArgs);
18970
18953
  if (requiredArgs.length > 0) hints.push(`required args: ${requiredArgs.join(", ")}`);
18971
18954
  else if (acceptedArgs.length > 0) hints.push(`args: ${acceptedArgs.join(", ")}`);
18972
- if (isRecord$6(record.argsTemplate)) hints.push(`args template: ${compactJsonText(record.argsTemplate, 160)}`);
18973
- if (isRecord$6(record.callTemplate)) hints.push(`call: ${compactJsonText(record.callTemplate, 220)}`);
18955
+ if (isRecord$8(record.argsTemplate)) hints.push(`args template: ${compactJsonText(record.argsTemplate, 160)}`);
18956
+ if (isRecord$8(record.callTemplate)) hints.push(`call: ${compactJsonText(record.callTemplate, 220)}`);
18974
18957
  if (record.supportsFields === true) hints.push("supports fields");
18975
18958
  if (record.readOnlyHint === true) hints.push("read-only");
18976
18959
  if (record.destructiveHint === true) hints.push("destructive");
@@ -19040,9 +19023,9 @@ function humanizeKey(key) {
19040
19023
  return key.replace(/([A-Z])/gu, " $1").replace(/^./u, (char) => char.toUpperCase());
19041
19024
  }
19042
19025
  function asRecord$3(value) {
19043
- return isRecord$6(value) ? value : void 0;
19026
+ return isRecord$8(value) ? value : void 0;
19044
19027
  }
19045
- function isRecord$6(value) {
19028
+ function isRecord$8(value) {
19046
19029
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
19047
19030
  }
19048
19031
  //#endregion
@@ -27555,6 +27538,7 @@ function configSchemaFor(serverValueSchema, openApiEndpointValueSchema, googleDi
27555
27538
  version: literal(1).default(1).describe("Caplets config schema version."),
27556
27539
  defaultSearchLimit: number$1().int().positive().default(20).describe("Default maximum number of same-server search results."),
27557
27540
  maxSearchLimit: number$1().int().positive().max(50).default(50).describe("Maximum accepted search_tools limit."),
27541
+ telemetry: boolean().optional().describe("Set false to disable anonymous Caplets telemetry for this user config."),
27558
27542
  completion: object$1({
27559
27543
  discoveryTimeoutMs: number$1().int().positive().default(750),
27560
27544
  overallTimeoutMs: number$1().int().positive().default(1500),
@@ -28352,6 +28336,7 @@ function mergeConfigInputs(...inputs) {
28352
28336
  merged = {
28353
28337
  ...merged,
28354
28338
  ...input,
28339
+ telemetry: input.telemetry === void 0 ? merged?.telemetry : input.telemetry,
28355
28340
  namespaceAliases: mergeNamespaceAliases(merged?.namespaceAliases, input.namespaceAliases),
28356
28341
  mcpServers: {
28357
28342
  ...merged?.mcpServers,
@@ -28408,13 +28393,14 @@ function mergeConfigInputsWithSources(...inputs) {
28408
28393
  const shadows = {};
28409
28394
  for (const entry of inputs) {
28410
28395
  if (entry?.input === void 0) continue;
28411
- for (const id of capletIds(entry.input)) {
28396
+ const entryInput = entry.source.kind === "global-config" ? entry.input : stripUserOnlyConfig(entry.input);
28397
+ for (const id of capletIds(entryInput)) {
28412
28398
  const source = sourceForId(entry.source, id);
28413
28399
  if (sources[id]) shadows[id] = [...shadows[id] ?? [], sources[id]];
28414
28400
  sources[id] = source;
28415
28401
  merged = removeCapletId(merged, id);
28416
28402
  }
28417
- merged = mergeConfigInputs(merged, entry.input) ?? {};
28403
+ merged = mergeConfigInputs(merged, entryInput) ?? {};
28418
28404
  }
28419
28405
  return {
28420
28406
  input: merged,
@@ -28422,6 +28408,10 @@ function mergeConfigInputsWithSources(...inputs) {
28422
28408
  shadows
28423
28409
  };
28424
28410
  }
28411
+ function stripUserOnlyConfig(input) {
28412
+ const { telemetry: _telemetry, ...rest } = input;
28413
+ return rest;
28414
+ }
28425
28415
  function removeCapletBackendId(input, backend, id) {
28426
28416
  const caplets = input[backend];
28427
28417
  if (!isPlainObject$5(caplets)) return input;
@@ -28518,6 +28508,7 @@ function parseConfig(input, options = {}) {
28518
28508
  });
28519
28509
  return {
28520
28510
  version: parsed.data.version,
28511
+ telemetry: parsed.data.telemetry,
28521
28512
  options: {
28522
28513
  defaultSearchLimit: parsed.data.defaultSearchLimit,
28523
28514
  maxSearchLimit: parsed.data.maxSearchLimit,
@@ -28662,17 +28653,17 @@ function googleDiscoveryScopesForOperations(operations) {
28662
28653
  return [...new Set(operations.flatMap((operation) => operation.scopes))].sort();
28663
28654
  }
28664
28655
  function validateGoogleDiscoveryDocument(value) {
28665
- if (!isRecord$5(value)) throw new Error("Invalid Google Discovery document: expected an object");
28656
+ if (!isRecord$7(value)) throw new Error("Invalid Google Discovery document: expected an object");
28666
28657
  if (value.kind !== void 0 && value.kind !== "discovery#restDescription") throw new Error("Invalid Google Discovery document: expected kind discovery#restDescription");
28667
- if (value.resources !== void 0 && !isRecord$5(value.resources)) throw new Error("Invalid Google Discovery document: expected resources object");
28668
- if (value.methods !== void 0 && !isRecord$5(value.methods)) throw new Error("Invalid Google Discovery document: expected methods object");
28669
- if (!isRecord$5(value.resources) && !isRecord$5(value.methods)) throw new Error("Invalid Google Discovery document: expected resources or methods object");
28670
- if (value.schemas !== void 0 && !isRecord$5(value.schemas)) throw new Error("Invalid Google Discovery document: expected schemas object");
28671
- if (value.parameters !== void 0 && !isRecord$5(value.parameters)) throw new Error("Invalid Google Discovery document: expected parameters object");
28658
+ if (value.resources !== void 0 && !isRecord$7(value.resources)) throw new Error("Invalid Google Discovery document: expected resources object");
28659
+ if (value.methods !== void 0 && !isRecord$7(value.methods)) throw new Error("Invalid Google Discovery document: expected methods object");
28660
+ if (!isRecord$7(value.resources) && !isRecord$7(value.methods)) throw new Error("Invalid Google Discovery document: expected resources or methods object");
28661
+ if (value.schemas !== void 0 && !isRecord$7(value.schemas)) throw new Error("Invalid Google Discovery document: expected schemas object");
28662
+ if (value.parameters !== void 0 && !isRecord$7(value.parameters)) throw new Error("Invalid Google Discovery document: expected parameters object");
28672
28663
  return value;
28673
28664
  }
28674
28665
  function collectDocumentMethods(document) {
28675
- return [...Object.entries(document.methods ?? {}).filter((entry) => isRecord$5(entry[1])).map(([methodKey, method]) => ({
28666
+ return [...Object.entries(document.methods ?? {}).filter((entry) => isRecord$7(entry[1])).map(([methodKey, method]) => ({
28676
28667
  resourcePath: [],
28677
28668
  methodKey,
28678
28669
  method
@@ -28681,9 +28672,9 @@ function collectDocumentMethods(document) {
28681
28672
  function collectMethods(resources, resourcePath = []) {
28682
28673
  const entries = [];
28683
28674
  for (const [resourceName, resource] of Object.entries(resources)) {
28684
- if (!isRecord$5(resource)) continue;
28675
+ if (!isRecord$7(resource)) continue;
28685
28676
  const nextPath = [...resourcePath, resourceName];
28686
- for (const [methodKey, method] of Object.entries(resource.methods ?? {})) if (isRecord$5(method)) entries.push({
28677
+ for (const [methodKey, method] of Object.entries(resource.methods ?? {})) if (isRecord$7(method)) entries.push({
28687
28678
  resourcePath: nextPath,
28688
28679
  methodKey,
28689
28680
  method
@@ -28856,7 +28847,7 @@ function globMatches(pattern, name) {
28856
28847
  function isJsonSchemaObject(value) {
28857
28848
  return value.type === "object" || "properties" in value || "additionalProperties" in value;
28858
28849
  }
28859
- function isRecord$5(value) {
28850
+ function isRecord$7(value) {
28860
28851
  return typeof value === "object" && value !== null && !Array.isArray(value);
28861
28852
  }
28862
28853
  function collapseWhitespace(value) {
@@ -63945,6 +63936,746 @@ async function withTimeout(promise, timeoutMs) {
63945
63936
  }
63946
63937
  }
63947
63938
  //#endregion
63939
+ //#region src/telemetry/state.ts
63940
+ function telemetryStateDir(options = {}) {
63941
+ return options.stateDir ?? DEFAULT_TELEMETRY_STATE_DIR;
63942
+ }
63943
+ function telemetryIdentityPath(options = {}) {
63944
+ return join(telemetryStateDir(options), "identity.json");
63945
+ }
63946
+ function telemetryNoticePath(options = {}) {
63947
+ return join(telemetryStateDir(options), "notice.json");
63948
+ }
63949
+ function telemetryDeliveryHealthPath(options = {}) {
63950
+ return join(telemetryStateDir(options), "delivery-health.json");
63951
+ }
63952
+ function readTelemetryIdentity(options = {}) {
63953
+ const path = telemetryIdentityPath(options);
63954
+ const existing = readJson(path);
63955
+ if (typeof existing?.id === "string" && /^anon_[a-f0-9]{32}$/u.test(existing.id)) return {
63956
+ kind: "stable",
63957
+ id: existing.id
63958
+ };
63959
+ if (!options.create) return {
63960
+ kind: "ephemeral",
63961
+ id: ephemeralId(),
63962
+ reason: "state-unavailable"
63963
+ };
63964
+ const identity = {
63965
+ id: stableId(),
63966
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
63967
+ };
63968
+ if (!writePrivateJson(path, identity)) return {
63969
+ kind: "ephemeral",
63970
+ id: ephemeralId(),
63971
+ reason: "state-unavailable"
63972
+ };
63973
+ return {
63974
+ kind: "stable",
63975
+ id: identity.id
63976
+ };
63977
+ }
63978
+ function rotateTelemetryIdentity(options = {}) {
63979
+ const identity = {
63980
+ id: stableId(),
63981
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
63982
+ };
63983
+ if (!writePrivateJson(telemetryIdentityPath(options), identity)) return {
63984
+ kind: "ephemeral",
63985
+ id: ephemeralId(),
63986
+ reason: "state-unavailable"
63987
+ };
63988
+ return {
63989
+ kind: "stable",
63990
+ id: identity.id
63991
+ };
63992
+ }
63993
+ function deleteTelemetryIdentity(options = {}) {
63994
+ try {
63995
+ rmSync(telemetryIdentityPath(options), { force: true });
63996
+ } catch {}
63997
+ }
63998
+ function readTelemetryNotice(options = {}) {
63999
+ const existing = readJson(telemetryNoticePath(options));
64000
+ if (existing?.shown === true && typeof existing.shownAt === "string" && isTelemetrySurface(existing.surface)) return {
64001
+ shown: true,
64002
+ shownAt: existing.shownAt,
64003
+ surface: existing.surface
64004
+ };
64005
+ return { shown: false };
64006
+ }
64007
+ function recordTelemetryNoticeShown(options) {
64008
+ const notice = {
64009
+ shown: true,
64010
+ shownAt: (/* @__PURE__ */ new Date()).toISOString(),
64011
+ surface: options.surface
64012
+ };
64013
+ writePrivateJson(telemetryNoticePath(options), notice);
64014
+ return notice;
64015
+ }
64016
+ function readTelemetryDeliveryHealth(options = {}) {
64017
+ const existing = readJson(telemetryDeliveryHealthPath(options));
64018
+ if (!isDeliveryHealth(existing)) return {};
64019
+ return existing;
64020
+ }
64021
+ function recordTelemetryDrop(options) {
64022
+ const health = readTelemetryDeliveryHealth(options);
64023
+ health[options.provider] = health[options.provider] ?? {};
64024
+ health[options.provider][options.reason] = (health[options.provider][options.reason] ?? 0) + 1;
64025
+ writePrivateJson(telemetryDeliveryHealthPath(options), health);
64026
+ }
64027
+ function writePrivateJson(path, value) {
64028
+ try {
64029
+ mkdirSync(dirname(path), {
64030
+ recursive: true,
64031
+ mode: 448
64032
+ });
64033
+ const tmp = `${path}.${process.pid}.${randomUUID()}.tmp`;
64034
+ writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\n`, { mode: 384 });
64035
+ renameSync(tmp, path);
64036
+ return true;
64037
+ } catch {
64038
+ return false;
64039
+ }
64040
+ }
64041
+ function readJson(path) {
64042
+ if (!existsSync(path)) return void 0;
64043
+ try {
64044
+ return JSON.parse(readFileSync(path, "utf8"));
64045
+ } catch {
64046
+ return;
64047
+ }
64048
+ }
64049
+ function stableId() {
64050
+ return `anon_${randomBytes(16).toString("hex")}`;
64051
+ }
64052
+ function ephemeralId() {
64053
+ return `ephemeral_${randomBytes(16).toString("hex")}`;
64054
+ }
64055
+ function isTelemetrySurface(value) {
64056
+ return value === "cli" || value === "serve" || value === "attach" || value === "daemon" || value === "native" || value === "code_mode";
64057
+ }
64058
+ function isDeliveryHealth(value) {
64059
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
64060
+ for (const provider of Object.values(value)) {
64061
+ if (!provider || typeof provider !== "object" || Array.isArray(provider)) return false;
64062
+ for (const count of Object.values(provider)) if (typeof count !== "number" || !Number.isInteger(count) || count < 0) return false;
64063
+ }
64064
+ return true;
64065
+ }
64066
+ //#endregion
64067
+ //#region src/telemetry/context.ts
64068
+ function resolveTelemetryState(options) {
64069
+ const env = options.env ?? process.env;
64070
+ const executionContext = classifyExecutionContext(env);
64071
+ const notice = readTelemetryNotice(options);
64072
+ const base = {
64073
+ surface: options.surface,
64074
+ visibility: options.visibility,
64075
+ executionContext,
64076
+ notice,
64077
+ stateDir: options.stateDir
64078
+ };
64079
+ if (options.debug) return {
64080
+ ...base,
64081
+ status: "debug",
64082
+ decider: "debug"
64083
+ };
64084
+ if (env.CAPLETS_DISABLE_TELEMETRY === "1") return {
64085
+ ...base,
64086
+ status: "disabled",
64087
+ decider: "env"
64088
+ };
64089
+ if (options.config?.telemetry === false) return {
64090
+ ...base,
64091
+ status: "disabled",
64092
+ decider: "config"
64093
+ };
64094
+ if (isTestEnv(env)) return {
64095
+ ...base,
64096
+ status: "disabled",
64097
+ decider: "test"
64098
+ };
64099
+ if (executionContext !== "ci" && !notice.shown && !options.allowWithoutNotice) return {
64100
+ ...base,
64101
+ status: "suppressed",
64102
+ decider: "notice"
64103
+ };
64104
+ return {
64105
+ ...base,
64106
+ status: "enabled",
64107
+ decider: "default",
64108
+ identity: readTelemetryIdentity({
64109
+ ...options,
64110
+ create: options.createIdentity !== false
64111
+ })
64112
+ };
64113
+ }
64114
+ function classifyExecutionContext(env = process.env) {
64115
+ if (env.CI === "true" || env.GITHUB_ACTIONS === "true" || env.BUILDKITE === "true") return "ci";
64116
+ return process.stdout.isTTY || process.stderr.isTTY ? "interactive" : "noninteractive";
64117
+ }
64118
+ function isTestEnv(env) {
64119
+ return env.NODE_ENV === "test" || env.VITEST === "true" || env.VITEST_WORKER_ID !== void 0 || env.CAPLETS_TEST === "1";
64120
+ }
64121
+ //#endregion
64122
+ //#region src/telemetry/notice.ts
64123
+ const TELEMETRY_NOTICE = "Caplets collects anonymous telemetry for product usage and reliability. Disable it with CAPLETS_DISABLE_TELEMETRY=1 or `caplets telemetry disable`.\n";
64124
+ function maybePrintTelemetryNotice(options) {
64125
+ if (!options.stderrIsTTY) return false;
64126
+ if (readTelemetryNotice(options).shown) return false;
64127
+ options.writeErr(TELEMETRY_NOTICE);
64128
+ recordTelemetryNoticeShown({
64129
+ ...options,
64130
+ surface: options.surface
64131
+ });
64132
+ return true;
64133
+ }
64134
+ //#endregion
64135
+ //#region src/telemetry/privacy.ts
64136
+ const ALLOWED_PROPERTY_KEYS = /* @__PURE__ */ new Set([
64137
+ "$process_person_profile",
64138
+ "$geoip_disable",
64139
+ "package",
64140
+ "version",
64141
+ "surface",
64142
+ "runtime_mode",
64143
+ "execution_context",
64144
+ "command_family",
64145
+ "operation_family",
64146
+ "outcome",
64147
+ "duration_bucket",
64148
+ "timeout_bucket",
64149
+ "integration",
64150
+ "exposure_mode",
64151
+ "backend_mcp_count",
64152
+ "backend_openapi_count",
64153
+ "backend_google_discovery_count",
64154
+ "backend_graphql_count",
64155
+ "backend_http_count",
64156
+ "backend_cli_count",
64157
+ "backend_caplets_count",
64158
+ "direct_count",
64159
+ "progressive_count",
64160
+ "code_mode_count",
64161
+ "session_category",
64162
+ "any_caplet_invoked",
64163
+ "provider",
64164
+ "reason",
64165
+ "count_bucket",
64166
+ "error_code",
64167
+ "diagnostic_category",
64168
+ "os_family",
64169
+ "arch",
64170
+ "node_major"
64171
+ ]);
64172
+ const SAFE_STRING = /^[a-zA-Z0-9@._:-]{1,80}$/u;
64173
+ const SAFE_PACKAGE = /^@?[a-zA-Z0-9._-]+(?:\/[a-zA-Z0-9._-]+)?$/u;
64174
+ const SUSPICIOUS_VALUE = [
64175
+ /^\/|^[A-Za-z]:\\/u,
64176
+ /^https?:\/\//iu,
64177
+ /[a-z0-9-]+\.[a-z]{2,}/iu,
64178
+ /(?:^|[_.-])token(?:$|[_.=-])/iu,
64179
+ /(?:^|[_.-])secret(?:$|[_.=-])/iu,
64180
+ /(?:^|[_.-])key(?:$|[_.=-])/iu,
64181
+ /^sk-[a-z0-9]/iu,
64182
+ /^gh[pousr]_[a-z0-9]/iu,
64183
+ /^[A-Z_]{3,}=.+/u
64184
+ ];
64185
+ const COMMAND_FAMILIES = /* @__PURE__ */ new Set([
64186
+ "init",
64187
+ "setup",
64188
+ "add",
64189
+ "install",
64190
+ "auth",
64191
+ "remote",
64192
+ "doctor",
64193
+ "serve",
64194
+ "attach",
64195
+ "daemon",
64196
+ "inspect",
64197
+ "check",
64198
+ "tools",
64199
+ "resources",
64200
+ "prompts",
64201
+ "complete",
64202
+ "code_mode",
64203
+ "native",
64204
+ "telemetry",
64205
+ "unknown"
64206
+ ]);
64207
+ const STRING_VALUE_ALLOWLISTS = {
64208
+ surface: /* @__PURE__ */ new Set([
64209
+ "cli",
64210
+ "serve",
64211
+ "attach",
64212
+ "daemon",
64213
+ "native",
64214
+ "code_mode"
64215
+ ]),
64216
+ runtime_mode: /* @__PURE__ */ new Set([
64217
+ "local",
64218
+ "remote",
64219
+ "cloud",
64220
+ "unknown"
64221
+ ]),
64222
+ execution_context: /* @__PURE__ */ new Set([
64223
+ "interactive",
64224
+ "noninteractive",
64225
+ "ci"
64226
+ ]),
64227
+ command_family: COMMAND_FAMILIES,
64228
+ operation_family: COMMAND_FAMILIES,
64229
+ outcome: /* @__PURE__ */ new Set([
64230
+ "success",
64231
+ "failure",
64232
+ "cancelled",
64233
+ "timeout",
64234
+ "suppressed"
64235
+ ]),
64236
+ duration_bucket: /* @__PURE__ */ new Set([
64237
+ "lt_100ms",
64238
+ "lt_1s",
64239
+ "lt_5s",
64240
+ "lt_30s",
64241
+ "gte_30s"
64242
+ ]),
64243
+ timeout_bucket: /* @__PURE__ */ new Set([
64244
+ "none",
64245
+ "lt_1s",
64246
+ "lt_10s",
64247
+ "lt_60s",
64248
+ "gte_60s"
64249
+ ]),
64250
+ integration: /* @__PURE__ */ new Set([
64251
+ "opencode",
64252
+ "pi",
64253
+ "native",
64254
+ "unknown"
64255
+ ]),
64256
+ exposure_mode: /* @__PURE__ */ new Set([
64257
+ "direct",
64258
+ "progressive",
64259
+ "code_mode",
64260
+ "mixed",
64261
+ "unknown"
64262
+ ]),
64263
+ session_category: /* @__PURE__ */ new Set([
64264
+ "created",
64265
+ "reused",
64266
+ "none",
64267
+ "unknown"
64268
+ ]),
64269
+ provider: /* @__PURE__ */ new Set(["posthog", "sentry"]),
64270
+ reason: /* @__PURE__ */ new Set(["not_configured", "send_failed"]),
64271
+ diagnostic_category: /* @__PURE__ */ new Set([
64272
+ "config",
64273
+ "auth",
64274
+ "network",
64275
+ "runtime",
64276
+ "validation",
64277
+ "code_mode",
64278
+ "provider",
64279
+ "unknown"
64280
+ ])
64281
+ };
64282
+ function assertTelemetrySafeProperties(properties) {
64283
+ for (const [key, value] of Object.entries(properties)) {
64284
+ if (!ALLOWED_PROPERTY_KEYS.has(key)) throw new Error(`unknown telemetry property: ${key}`);
64285
+ if (typeof value === "string") {
64286
+ const valueAllowlist = STRING_VALUE_ALLOWLISTS[key];
64287
+ if (valueAllowlist && !valueAllowlist.has(value)) throw new Error(`unsafe telemetry property ${key}`);
64288
+ if (key === "error_code" && !/^[A-Z_]{2,80}$/u.test(value)) throw new Error(`unsafe telemetry property ${key}`);
64289
+ if (!(key === "package" ? SAFE_PACKAGE.test(value) : SAFE_STRING.test(value)) || key !== "package" && key !== "version" && SUSPICIOUS_VALUE.some((pattern) => pattern.test(value))) throw new Error(`unsafe telemetry property ${key}`);
64290
+ continue;
64291
+ }
64292
+ if (typeof value === "number") {
64293
+ if (!Number.isFinite(value) || value < 0 || !Number.isInteger(value)) throw new Error(`unsafe telemetry property ${key}`);
64294
+ continue;
64295
+ }
64296
+ if (typeof value === "boolean") continue;
64297
+ throw new Error(`unsafe telemetry property ${key}`);
64298
+ }
64299
+ }
64300
+ function stripSentryEvent(event) {
64301
+ const stripped = {};
64302
+ if (event.tags && typeof event.tags === "object" && !Array.isArray(event.tags)) stripped.tags = event.tags;
64303
+ if (Array.isArray(event.fingerprint)) stripped.fingerprint = event.fingerprint;
64304
+ if (event.level) stripped.level = event.level;
64305
+ return stripped;
64306
+ }
64307
+ //#endregion
64308
+ //#region src/telemetry/events.ts
64309
+ const PRODUCT_EVENTS = /* @__PURE__ */ new Set([
64310
+ "caplets_cli_command",
64311
+ "caplets_tool_activation",
64312
+ "caplets_code_mode_outcome"
64313
+ ]);
64314
+ const RELIABILITY_EVENTS = /* @__PURE__ */ new Set(["caplets_reliability_error"]);
64315
+ function buildProductTelemetryEvent(input) {
64316
+ if (!PRODUCT_EVENTS.has(input.name)) throw new Error(`unknown telemetry event: ${input.name}`);
64317
+ assertTelemetrySafeProperties(input.properties);
64318
+ return {
64319
+ provider: "posthog",
64320
+ name: input.name,
64321
+ distinctId: input.distinctId,
64322
+ properties: {
64323
+ ...input.properties,
64324
+ $process_person_profile: false
64325
+ }
64326
+ };
64327
+ }
64328
+ function buildReliabilityTelemetryEvent(input) {
64329
+ if (!RELIABILITY_EVENTS.has(input.name)) throw new Error(`unknown telemetry event: ${input.name}`);
64330
+ assertTelemetrySafeProperties(input.properties);
64331
+ const tags = tagsFor(input.properties);
64332
+ return {
64333
+ provider: "sentry",
64334
+ name: input.name,
64335
+ tags,
64336
+ fingerprint: [
64337
+ tags.package ?? "unknown",
64338
+ tags.surface ?? "unknown",
64339
+ tags.command_family ?? "unknown",
64340
+ tags.runtime_mode ?? "unknown",
64341
+ tags.error_code ?? "unknown",
64342
+ tags.diagnostic_category ?? "unknown"
64343
+ ]
64344
+ };
64345
+ }
64346
+ function durationBucket(ms) {
64347
+ if (ms < 100) return "lt_100ms";
64348
+ if (ms < 1e3) return "lt_1s";
64349
+ if (ms < 5e3) return "lt_5s";
64350
+ if (ms < 3e4) return "lt_30s";
64351
+ return "gte_30s";
64352
+ }
64353
+ function timeoutBucket(ms) {
64354
+ if (ms === void 0) return "none";
64355
+ if (ms < 1e3) return "lt_1s";
64356
+ if (ms < 1e4) return "lt_10s";
64357
+ if (ms < 6e4) return "lt_60s";
64358
+ return "gte_60s";
64359
+ }
64360
+ function tagsFor(properties) {
64361
+ return Object.fromEntries(Object.entries(properties).map(([key, value]) => [key, String(value)]));
64362
+ }
64363
+ //#endregion
64364
+ //#region src/telemetry/debug.ts
64365
+ var TelemetryDebugSink = class {
64366
+ records = [];
64367
+ capture(state, event) {
64368
+ this.records.push({
64369
+ state,
64370
+ event
64371
+ });
64372
+ }
64373
+ toJSON() {
64374
+ return this.records;
64375
+ }
64376
+ };
64377
+ //#endregion
64378
+ //#region src/telemetry/providers.ts
64379
+ function createTelemetryDispatcher(options = {}) {
64380
+ let posthog;
64381
+ let sentry;
64382
+ async function posthogClient() {
64383
+ const token = options.posthogToken ?? process.env.CAPLETS_POSTHOG_TOKEN ?? "phc_pUZ2ZCRtFkC5qCGDNYcoYEhxxjTaooH57Cj4PTRXovyB";
64384
+ if (!token) return void 0;
64385
+ posthog ??= Promise.resolve((options.factories?.createPostHog ?? defaultPostHogFactory)(token));
64386
+ return posthog;
64387
+ }
64388
+ async function sentryClient() {
64389
+ const dsn = options.sentryDsn ?? process.env.CAPLETS_SENTRY_DSN ?? "https://c66cfd76aad69209bcf44be4cd79f9b5@o4508042228006912.ingest.us.sentry.io/4511620382392320";
64390
+ if (!dsn) return void 0;
64391
+ sentry ??= Promise.resolve((options.factories?.createSentry ?? defaultSentryFactory)(dsn));
64392
+ return sentry;
64393
+ }
64394
+ return {
64395
+ async capture(state, event) {
64396
+ if (state.status !== "enabled") return;
64397
+ try {
64398
+ if (event.provider === "posthog") {
64399
+ await capturePostHog(await posthogClient(), event, state.stateDir ?? options.stateDir);
64400
+ return;
64401
+ }
64402
+ await captureSentry(await sentryClient(), event, state.stateDir ?? options.stateDir);
64403
+ } catch {
64404
+ recordTelemetryDrop({
64405
+ stateDir: state.stateDir ?? options.stateDir,
64406
+ provider: event.provider,
64407
+ reason: "send_failed"
64408
+ });
64409
+ }
64410
+ },
64411
+ async shutdown() {
64412
+ const clients = await Promise.allSettled([posthog, sentry]);
64413
+ for (const client of clients) {
64414
+ if (client.status !== "fulfilled" || !client.value) continue;
64415
+ if ("shutdown" in client.value) await Promise.resolve(client.value.shutdown()).catch(() => void 0);
64416
+ if ("flush" in client.value) await Promise.resolve(client.value.flush(2e3)).catch(() => void 0);
64417
+ }
64418
+ }
64419
+ };
64420
+ }
64421
+ async function capturePostHog(client, event, stateDir) {
64422
+ if (!client) {
64423
+ recordTelemetryDrop({
64424
+ stateDir,
64425
+ provider: "posthog",
64426
+ reason: "not_configured"
64427
+ });
64428
+ return;
64429
+ }
64430
+ client.capture({
64431
+ distinctId: event.distinctId,
64432
+ event: event.name,
64433
+ properties: {
64434
+ ...event.properties,
64435
+ $geoip_disable: true
64436
+ }
64437
+ });
64438
+ }
64439
+ async function captureSentry(client, event, stateDir) {
64440
+ if (!client) {
64441
+ recordTelemetryDrop({
64442
+ stateDir,
64443
+ provider: "sentry",
64444
+ reason: "not_configured"
64445
+ });
64446
+ return;
64447
+ }
64448
+ client.captureEvent({
64449
+ level: "error",
64450
+ tags: event.tags,
64451
+ fingerprint: event.fingerprint
64452
+ });
64453
+ }
64454
+ async function defaultPostHogFactory(token) {
64455
+ const { PostHog } = await import("./node-BgWIvSVP.js");
64456
+ return new PostHog(token, {
64457
+ flushAt: 20,
64458
+ flushInterval: 1e4,
64459
+ disableGeoip: true,
64460
+ historicalMigration: false
64461
+ });
64462
+ }
64463
+ async function defaultSentryFactory(dsn) {
64464
+ const sentry = await import("./esm-Db9dhnIG.js");
64465
+ const options = {
64466
+ dsn,
64467
+ sendDefaultPii: false,
64468
+ defaultIntegrations: false,
64469
+ integrations: [],
64470
+ tracesSampleRate: 0,
64471
+ transport: sentry.makeNodeTransport,
64472
+ stackParser: sentry.defaultStackParser,
64473
+ beforeSend(event) {
64474
+ return stripSentryEvent(event);
64475
+ }
64476
+ };
64477
+ return new sentry.NodeClient(options);
64478
+ }
64479
+ //#endregion
64480
+ //#region package.json
64481
+ var version = "0.28.0";
64482
+ //#endregion
64483
+ //#region src/telemetry/runtime.ts
64484
+ function createRuntimeTelemetryContext(options) {
64485
+ return {
64486
+ ...options,
64487
+ dispatcher: options.dispatcher ?? createTelemetryDispatcher({ stateDir: options.stateDir })
64488
+ };
64489
+ }
64490
+ async function captureRuntimeTelemetryEvent(context, name, properties) {
64491
+ const state = resolveTelemetryState({
64492
+ config: context.config,
64493
+ env: context.env,
64494
+ stateDir: context.stateDir,
64495
+ surface: context.surface,
64496
+ visibility: context.visibility,
64497
+ debug: context.debugSink !== void 0
64498
+ });
64499
+ if (state.status !== "enabled" && state.status !== "debug") return;
64500
+ const event = buildProductTelemetryEvent({
64501
+ name,
64502
+ distinctId: (state.identity ?? readTelemetryIdentity({
64503
+ stateDir: context.stateDir,
64504
+ create: false
64505
+ })).id,
64506
+ properties: {
64507
+ package: "@caplets/core",
64508
+ version,
64509
+ surface: context.surface,
64510
+ runtime_mode: context.runtimeMode ?? "unknown",
64511
+ execution_context: state.executionContext,
64512
+ ...context.integration ? { integration: context.integration } : {},
64513
+ ...properties
64514
+ }
64515
+ });
64516
+ if (state.status === "debug") {
64517
+ context.debugSink?.capture("debug", event);
64518
+ return;
64519
+ }
64520
+ await context.dispatcher.capture(state, event);
64521
+ }
64522
+ async function captureRuntimeReliabilityEvent(context, properties) {
64523
+ const state = resolveTelemetryState({
64524
+ config: context.config,
64525
+ env: context.env,
64526
+ stateDir: context.stateDir,
64527
+ surface: context.surface,
64528
+ visibility: context.visibility,
64529
+ debug: context.debugSink !== void 0
64530
+ });
64531
+ if (state.status !== "enabled" && state.status !== "debug") return;
64532
+ const event = buildReliabilityTelemetryEvent({
64533
+ name: "caplets_reliability_error",
64534
+ properties: {
64535
+ package: "@caplets/core",
64536
+ version,
64537
+ surface: context.surface,
64538
+ runtime_mode: context.runtimeMode ?? "unknown",
64539
+ execution_context: state.executionContext,
64540
+ ...context.integration ? { integration: context.integration } : {},
64541
+ os_family: platform(),
64542
+ arch: arch(),
64543
+ node_major: Number(process.versions.node.split(".")[0] ?? 0),
64544
+ ...properties
64545
+ }
64546
+ });
64547
+ if (state.status === "debug") {
64548
+ context.debugSink?.capture("debug", event);
64549
+ return;
64550
+ }
64551
+ await context.dispatcher.capture(state, event);
64552
+ }
64553
+ function backendFamilyCounts(config) {
64554
+ return {
64555
+ backend_mcp_count: enabledCount(config.mcpServers),
64556
+ backend_openapi_count: enabledCount(config.openapiEndpoints),
64557
+ backend_google_discovery_count: enabledCount(config.googleDiscoveryApis),
64558
+ backend_graphql_count: enabledCount(config.graphqlEndpoints),
64559
+ backend_http_count: enabledCount(config.httpApis),
64560
+ backend_cli_count: enabledCount(config.cliTools),
64561
+ backend_caplets_count: enabledCount(config.capletSets)
64562
+ };
64563
+ }
64564
+ function exposureModeCounts(config) {
64565
+ let direct = 0;
64566
+ let progressive = 0;
64567
+ let codeMode = 0;
64568
+ for (const caplet of allCaplets$1(config)) {
64569
+ if (caplet.disabled || caplet.setup || caplet.projectBinding?.required) continue;
64570
+ const exposure = resolveExposure(caplet.exposure, config.options.exposure);
64571
+ if (exposure.direct) direct += 1;
64572
+ if (exposure.progressive) progressive += 1;
64573
+ if (exposure.codeMode) codeMode += 1;
64574
+ }
64575
+ return {
64576
+ direct_count: direct,
64577
+ progressive_count: progressive,
64578
+ code_mode_count: codeMode
64579
+ };
64580
+ }
64581
+ function operationFamilyFromOperation(operation) {
64582
+ if (operation === "inspect") return "inspect";
64583
+ if (operation === "check") return "check";
64584
+ if (operation === "tools" || operation === "search_tools" || operation === "get_tool" || operation === "describe_tool" || operation === "call_tool") return "tools";
64585
+ if (operation === "resources" || operation === "resource_templates" || operation === "read_resource" || operation === "search_resources" || operation === "list_resources" || operation === "list_resource_templates" || operation === "search_resource_templates") return "resources";
64586
+ if (operation === "prompts" || operation === "get_prompt" || operation === "list_prompts" || operation === "search_prompts") return "prompts";
64587
+ if (operation === "complete") return "complete";
64588
+ if (operation === "code_mode") return "code_mode";
64589
+ return "unknown";
64590
+ }
64591
+ function outcomeFromResult(result) {
64592
+ if (isRecord$6(result) && result.isError === true) {
64593
+ if (errorCodeFromResult$1(result).toLowerCase().includes("timeout")) return "timeout";
64594
+ return "failure";
64595
+ }
64596
+ if (isRecord$6(result) && result.ok === false) {
64597
+ const code = errorCodeFromResult$1(result);
64598
+ if (typeof code === "string" && code.toLowerCase().includes("timeout")) return "timeout";
64599
+ return "failure";
64600
+ }
64601
+ return "success";
64602
+ }
64603
+ function codeModeTelemetryProperties(envelope, durationMs, timeoutMs) {
64604
+ const record = isRecord$6(envelope) ? envelope : {};
64605
+ const meta = isRecord$6(record.meta) ? record.meta : {};
64606
+ const sessionStatus = meta.sessionStatus;
64607
+ const effectiveTimeoutMs = timeoutMs ?? (typeof meta.timeoutMs === "number" ? meta.timeoutMs : void 0);
64608
+ return {
64609
+ command_family: "code_mode",
64610
+ outcome: outcomeFromResult(record),
64611
+ duration_bucket: durationBucket(durationMs),
64612
+ timeout_bucket: timeoutBucket(effectiveTimeoutMs),
64613
+ session_category: sessionStatus === "created" || sessionStatus === "reused" ? sessionStatus : sessionStatus === null ? "none" : "unknown",
64614
+ any_caplet_invoked: codeModeEnvelopeInvokedCaplet(record)
64615
+ };
64616
+ }
64617
+ function toolActivationProperties(input) {
64618
+ return {
64619
+ operation_family: operationFamilyFromOperation(input.operation),
64620
+ exposure_mode: input.exposureMode,
64621
+ outcome: outcomeFromResult(input.result),
64622
+ duration_bucket: durationBucket(input.durationMs),
64623
+ ...backendFamilyCounts(input.config),
64624
+ ...exposureModeCounts(input.config)
64625
+ };
64626
+ }
64627
+ function enabledCount(record) {
64628
+ return Object.values(record).filter((caplet) => !caplet.disabled).length;
64629
+ }
64630
+ function allCaplets$1(config) {
64631
+ return [
64632
+ ...Object.values(config.mcpServers),
64633
+ ...Object.values(config.openapiEndpoints),
64634
+ ...Object.values(config.googleDiscoveryApis),
64635
+ ...Object.values(config.graphqlEndpoints),
64636
+ ...Object.values(config.httpApis),
64637
+ ...Object.values(config.cliTools),
64638
+ ...Object.values(config.capletSets)
64639
+ ];
64640
+ }
64641
+ function isRecord$6(value) {
64642
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
64643
+ }
64644
+ function runtimeFailureTelemetryProperties(input) {
64645
+ const errorCode = errorCodeFromResult$1(input.result);
64646
+ return {
64647
+ operation_family: operationFamilyFromOperation(input.operation),
64648
+ exposure_mode: input.exposureMode,
64649
+ error_code: errorCode,
64650
+ diagnostic_category: diagnosticCategoryFromCode(errorCode)
64651
+ };
64652
+ }
64653
+ function errorCodeFromResult$1(result) {
64654
+ if (!isRecord$6(result)) return "UNKNOWN";
64655
+ const error = isRecord$6(result.error) ? result.error : isRecord$6(result.structuredContent) && isRecord$6(result.structuredContent.error) ? result.structuredContent.error : void 0;
64656
+ if (isRecord$6(error) && typeof error.code === "string") return error.code;
64657
+ return "UNKNOWN";
64658
+ }
64659
+ function diagnosticCategoryFromCode(code) {
64660
+ if (code.startsWith("CONFIG")) return "config";
64661
+ if (code.startsWith("AUTH")) return "auth";
64662
+ if (code.includes("NETWORK") || code.includes("UNAVAILABLE")) return "network";
64663
+ if (code.includes("VALID") || code.includes("REQUEST")) return "validation";
64664
+ if (code.includes("CODE_MODE") || code.includes("SANDBOX")) return "code_mode";
64665
+ return "runtime";
64666
+ }
64667
+ function codeModeEnvelopeInvokedCaplet(record) {
64668
+ const meta = isRecord$6(record.meta) ? record.meta : void 0;
64669
+ return hasBooleanTrue(meta, "anyCapletInvoked") || hasBooleanTrue(meta, "capletInvoked") || hasPositiveNumber(meta, "capletInvocationCount") || hasPositiveNumber(meta, "toolCallCount");
64670
+ }
64671
+ function hasBooleanTrue(record, key) {
64672
+ return record?.[key] === true;
64673
+ }
64674
+ function hasPositiveNumber(record, key) {
64675
+ const value = record?.[key];
64676
+ return typeof value === "number" && value > 0;
64677
+ }
64678
+ //#endregion
63948
64679
  //#region src/engine.ts
63949
64680
  var CapletsEngine = class {
63950
64681
  registry;
@@ -63963,6 +64694,8 @@ var CapletsEngine = class {
63963
64694
  observedOutputShapeStore;
63964
64695
  observedOutputShapeScope;
63965
64696
  projectFingerprint;
64697
+ telemetry;
64698
+ telemetryExecuteExposureMode;
63966
64699
  reloadListeners = /* @__PURE__ */ new Set();
63967
64700
  lastExposureSnapshot;
63968
64701
  watchers = [];
@@ -63980,6 +64713,18 @@ var CapletsEngine = class {
63980
64713
  this.configLoader = options.configLoader ?? runtimeConfigLoader(options.authDir, options.vaultRecoveryTarget);
63981
64714
  const config = this.loadConfigWithWarnings();
63982
64715
  this.registry = new ServerRegistry(config);
64716
+ this.telemetry = createRuntimeTelemetryContext({
64717
+ config: this.registry.config,
64718
+ env: options.telemetryEnv,
64719
+ stateDir: options.telemetryStateDir,
64720
+ surface: options.telemetrySurface ?? "serve",
64721
+ visibility: options.telemetryVisibility ?? "unknown",
64722
+ runtimeMode: options.telemetryRuntimeMode ?? runtimeModeFromEnv(options.telemetryEnv),
64723
+ integration: options.telemetryIntegration,
64724
+ debugSink: options.telemetryDebugSink,
64725
+ dispatcher: options.telemetryDispatcher
64726
+ });
64727
+ this.telemetryExecuteExposureMode = options.telemetrySurface === "code_mode" ? "code_mode" : "progressive";
63983
64728
  this.downstream = new DownstreamManager(this.registry, selectAuthOptions(options.authDir));
63984
64729
  this.openapi = new OpenApiManager(this.registry, selectHttpLikeOptions(options));
63985
64730
  this.googleDiscovery = new GoogleDiscoveryManager(this.registry, selectHttpLikeOptions(options));
@@ -64044,44 +64789,73 @@ var CapletsEngine = class {
64044
64789
  return await this.reloading;
64045
64790
  }
64046
64791
  async execute(serverId, request) {
64792
+ const started = Date.now();
64793
+ let caplet;
64047
64794
  try {
64048
- return await handleServerTool(this.registry.require(serverId), request, this.registry, this.downstream, this.openapi, this.graphql, this.http, this.cli, this.capletSets, {
64795
+ caplet = this.registry.require(serverId);
64796
+ const result = await handleServerTool(caplet, request, this.registry, this.downstream, this.openapi, this.graphql, this.http, this.cli, this.capletSets, {
64049
64797
  observedOutputShapeStore: this.observedOutputShapeStore,
64050
64798
  observedOutputShapeScope: this.observedOutputShapeScope,
64051
64799
  projectFingerprint: this.projectFingerprint
64052
64800
  }, this.googleDiscovery);
64801
+ this.captureToolActivation(caplet, operationFromRequest(request), this.telemetryExecuteExposureMode, result, started);
64802
+ return result;
64053
64803
  } catch (error) {
64054
- return errorResult(error);
64804
+ const result = errorResult(error);
64805
+ this.captureReliabilityError(operationFromRequest(request), this.telemetryExecuteExposureMode, result);
64806
+ this.captureToolActivation(caplet, operationFromRequest(request), this.telemetryExecuteExposureMode, result, started);
64807
+ return result;
64055
64808
  }
64056
64809
  }
64057
64810
  async executeDirectTool(serverId, toolName, args) {
64811
+ const started = Date.now();
64812
+ let caplet;
64058
64813
  try {
64059
- const caplet = this.registry.require(serverId);
64060
- return annotateDirectResult(await this.callTool(caplet, toolName, args), caplet, toolName);
64814
+ caplet = this.registry.require(serverId);
64815
+ const annotated = annotateDirectResult(await this.callTool(caplet, toolName, args), caplet, toolName);
64816
+ this.captureToolActivation(caplet, "call_tool", "direct", annotated, started);
64817
+ return annotated;
64061
64818
  } catch (error) {
64062
- return errorResult(error);
64819
+ const result = errorResult(error);
64820
+ this.captureReliabilityError("call_tool", "direct", result);
64821
+ this.captureToolActivation(caplet, "call_tool", "direct", result, started);
64822
+ return result;
64063
64823
  }
64064
64824
  }
64065
64825
  async readDirectResource(serverId, downstreamUri) {
64826
+ const started = Date.now();
64827
+ let caplet;
64066
64828
  try {
64067
- const caplet = this.registry.require(serverId);
64829
+ caplet = this.registry.require(serverId);
64068
64830
  if (caplet.backend !== "mcp") throw new Error(`Caplet ${serverId} has no MCP resources`);
64069
- return annotateDirectResult(await this.downstream.readResource(caplet, downstreamUri), caplet, "read_resource");
64831
+ const annotated = annotateDirectResult(await this.downstream.readResource(caplet, downstreamUri), caplet, "read_resource");
64832
+ this.captureToolActivation(caplet, "read_resource", "direct", annotated, started);
64833
+ return annotated;
64070
64834
  } catch (error) {
64071
- return errorResult(error);
64835
+ const result = errorResult(error);
64836
+ this.captureReliabilityError("read_resource", "direct", result);
64837
+ this.captureToolActivation(caplet, "read_resource", "direct", result, started);
64838
+ return result;
64072
64839
  }
64073
64840
  }
64074
64841
  async getDirectPrompt(serverId, promptName, args) {
64842
+ const started = Date.now();
64843
+ let caplet;
64075
64844
  try {
64076
- const caplet = this.registry.require(serverId);
64845
+ caplet = this.registry.require(serverId);
64077
64846
  if (caplet.backend !== "mcp") throw new Error(`Caplet ${serverId} has no MCP prompts`);
64078
- return annotateDirectResult(await this.downstream.getPrompt(caplet, promptName, args), caplet, promptName);
64847
+ const annotated = annotateDirectResult(await this.downstream.getPrompt(caplet, promptName, args), caplet, promptName);
64848
+ this.captureToolActivation(caplet, "get_prompt", "direct", annotated, started);
64849
+ return annotated;
64079
64850
  } catch (error) {
64080
- return errorResult(error);
64851
+ const result = errorResult(error);
64852
+ this.captureReliabilityError("get_prompt", "direct", result);
64853
+ this.captureToolActivation(caplet, "get_prompt", "direct", result, started);
64854
+ return result;
64081
64855
  }
64082
64856
  }
64083
64857
  async completeCliWords(words) {
64084
- const { completeCliWords } = await import("./completion-1wDjwHkC.js").then((n) => n.r);
64858
+ const { completeCliWords } = await import("./completion-CjE0EnbF.js").then((n) => n.r);
64085
64859
  return await completeCliWords(words, {
64086
64860
  config: this.registry.config,
64087
64861
  managers: {
@@ -64112,6 +64886,9 @@ var CapletsEngine = class {
64112
64886
  }
64113
64887
  });
64114
64888
  }
64889
+ async captureCodeModeOutcome(envelope, options) {
64890
+ await captureRuntimeTelemetryEvent(this.telemetry, "caplets_code_mode_outcome", codeModeTelemetryProperties(envelope, Date.now() - options.started, options.timeoutMs));
64891
+ }
64115
64892
  async close() {
64116
64893
  this.closed = true;
64117
64894
  try {
@@ -64128,6 +64905,7 @@ var CapletsEngine = class {
64128
64905
  this.closeWatchers();
64129
64906
  await this.downstream.close();
64130
64907
  await this.capletSets.close();
64908
+ await this.telemetry.dispatcher.shutdown();
64131
64909
  this.reloadListeners.clear();
64132
64910
  }
64133
64911
  }
@@ -64165,6 +64943,7 @@ var CapletsEngine = class {
64165
64943
  const previousConfig = this.registry.config;
64166
64944
  const nextRegistry = new ServerRegistry(nextConfig);
64167
64945
  this.registry = nextRegistry;
64946
+ this.telemetry.config = nextConfig;
64168
64947
  this.downstream.updateRegistry(nextRegistry);
64169
64948
  this.openapi.updateRegistry(nextRegistry);
64170
64949
  this.googleDiscovery.updateRegistry(nextRegistry);
@@ -64282,6 +65061,29 @@ var CapletsEngine = class {
64282
65061
  if (!this.closed) this.resetWatchers();
64283
65062
  }, this.watchDebounceMs);
64284
65063
  }
65064
+ captureReliabilityError(operation, exposureMode, result) {
65065
+ captureRuntimeReliabilityEvent(this.telemetry, {
65066
+ command_family: commandFamilyForTelemetrySurface(this.telemetry.surface),
65067
+ ...runtimeFailureTelemetryProperties({
65068
+ operation,
65069
+ exposureMode,
65070
+ result
65071
+ })
65072
+ }).catch(() => void 0);
65073
+ }
65074
+ captureToolActivation(caplet, operation, exposureMode, result, started) {
65075
+ captureRuntimeTelemetryEvent(this.telemetry, "caplets_tool_activation", {
65076
+ command_family: commandFamilyForTelemetrySurface(this.telemetry.surface),
65077
+ ...toolActivationProperties({
65078
+ config: this.registry.config,
65079
+ caplet,
65080
+ operation,
65081
+ exposureMode,
65082
+ result,
65083
+ durationMs: Date.now() - started
65084
+ })
65085
+ }).catch(() => void 0);
65086
+ }
64285
65087
  };
64286
65088
  function runtimeConfigLoader(authDir, vaultRecoveryTarget) {
64287
65089
  const vaultResolver = vaultResolverForAuthDir(authDir);
@@ -64384,7 +65186,7 @@ function annotateDirectResult(result, caplet, operation) {
64384
65186
  return {
64385
65187
  ...result,
64386
65188
  _meta: {
64387
- ...isRecord$4(existingMeta) ? existingMeta : {},
65189
+ ...isRecord$5(existingMeta) ? existingMeta : {},
64388
65190
  caplets: {
64389
65191
  capletId: caplet.server,
64390
65192
  backend: caplet.backend,
@@ -64397,9 +65199,25 @@ function annotateDirectResult(result, caplet, operation) {
64397
65199
  function isUnsupportedCapability(error) {
64398
65200
  return error instanceof CapletsError && error.code === "UNSUPPORTED_CAPABILITY";
64399
65201
  }
64400
- function isRecord$4(value) {
65202
+ function isRecord$5(value) {
64401
65203
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
64402
65204
  }
65205
+ function operationFromRequest(request) {
65206
+ return isRecord$5(request) ? request.operation : void 0;
65207
+ }
65208
+ function runtimeModeFromEnv(env) {
65209
+ const mode = env?.CAPLETS_MODE ?? process.env.CAPLETS_MODE;
65210
+ if (mode === "local" || mode === "remote" || mode === "cloud") return mode;
65211
+ return "unknown";
65212
+ }
65213
+ function commandFamilyForTelemetrySurface(surface) {
65214
+ if (surface === "serve") return "serve";
65215
+ if (surface === "attach") return "attach";
65216
+ if (surface === "daemon") return "daemon";
65217
+ if (surface === "code_mode") return "code_mode";
65218
+ if (surface === "native") return "native";
65219
+ return "tools";
65220
+ }
64403
65221
  //#endregion
64404
65222
  //#region src/code-mode/runtime-api.generated.ts
64405
65223
  const CODE_MODE_RUNTIME_API_DECLARATION = "type JsonPrimitive=string|number|boolean|null;type JsonValue=JsonPrimitive|JsonValue[]|{[key:string]:JsonValue};interface CapletHandle<Id extends string>{readonly id:Id;/** Show this Caplet card,without tool/resource/prompt schemas. */ inspect():Promise<CapletCard<Id>>;/** Check backend readiness/auth;expected unavailable states return ok:false. */ check():Promise<CapletsResult<BackendCheckResult>>;/** List tool summaries for the discovery pass;may be empty. */ tools(input?:PageInput):Promise<Page<ToolSummary>>;/** Search tool summaries for the discovery pass;may be empty. */ searchTools(query:string,input?:PageInput):Promise<Page<ToolSummary>>;/** Get schema,callSignature,types,examples;prefer outputSchema/outputTypeScript over observed hints. */ describeTool(name:string):Promise<CapletsResult<ToolDescriptor>>;/** Call one tool;expected failures return ok:false. Filter bulky data in script before returning. */ callTool(name:string,args?:unknown):Promise<CapletsResult<unknown>>;/** List readable resources for the discovery pass;many backends expose none. */ resources(input?:PageInput):Promise<Page<ResourceSummary>>;/** Search readable resources for the discovery pass;many backends expose none. */ searchResources(query:string,input?:PageInput):Promise<Page<ResourceSummary>>;/** List resource templates for the discovery pass;many backends expose none. */ resourceTemplates(input?:PageInput):Promise<Page<ResourceTemplateSummary>>;/** Read one resource by URI;unsupported/missing resources return ok:false. */ readResource(uri:string):Promise<CapletsResult<ResourceReadResult>>;/** List reusable prompts for the discovery pass;many backends expose none. */ prompts(input?:PageInput):Promise<Page<PromptSummary>>;/** Search reusable prompts for the discovery pass;many backends expose none. */ searchPrompts(query:string,input?:PageInput):Promise<Page<PromptSummary>>;/** Get one prompt by name and args;unsupported/missing prompts return ok:false. */ getPrompt(name:string,args?:unknown):Promise<CapletsResult<PromptResult>>;/** Complete a prompt or resource-template argument. */ complete(input:CompleteInput):Promise<CapletsResult<CompleteResult>>;}interface DebugApi{readLogs(input:ReadLogsInput):Promise<ReadLogsResult>;readRecovery(input:ReadCodeModeRecoveryInput):Promise<ReadCodeModeRecoveryResult>;}type CapletCard<Id extends string>={id:Id;name:string;description:string;useWhen?:string;avoidWhen?:string;tags?:string[];backend?:unknown;};type PageInput={limit?:number;cursor?:string};type Page<T>={items:T[];nextCursor?:string;truncated?:boolean};type CapletsResult<T>=|{ok:true;data:T;meta?:CapletsMeta}|{ok:false;error:CapletsError;meta?:CapletsMeta};type CapletsMeta={[key:string]:unknown};type CapletsError={code:string;message:string;details?:unknown};type BackendCheckResult=unknown;type ToolSummary={/** Exact downstream tool identifier for describeTool(name)and callTool(name,args). */ name:string;title?:string;description?:string;/** Optional author-supplied hint for when to prefer this tool. */ useWhen?:string;/** Optional author-supplied hint for when to avoid this tool. */ avoidWhen?:string;/** True when the tool declares that it only reads data. */ readOnlyHint?:boolean;/** True when the tool declares that it may perform destructive writes. */ destructiveHint?:boolean;};type ToolDescriptor={id?:string;tool?:unknown;inputSchema?:unknown;outputSchema?:unknown;callSignature?:string;inputTypeScript?:string;outputTypeScript?:string;observedOutputShape?:ObservedOutputShape;examples?:unknown[];};type ObservedOutputShape={version:1;source:\"observed\";observedAt:string;sampleCount:number;typeScript:string;jsonShape:JsonShape;truncated:boolean;};type JsonShape=|{kind:\"null\"}|{kind:\"boolean\"}|{kind:\"number\"}|{kind:\"string\"}|{kind:\"unknown\"}|{kind:\"array\";element?:JsonShape;truncated?:boolean}|{kind:\"object\";fields:Record<string,{optional:boolean;shape:JsonShape}>;truncated?:boolean;}|{kind:\"union\";variants:JsonShape[]};type ResourceSummary={uri?:string;name?:string;title?:string;description?:string};type ResourceTemplateSummary={uriTemplate?:string;name?:string;title?:string;description?:string;};type ResourceReadResult=unknown;type PromptSummary={name?:string;title?:string;description?:string};type PromptResult=unknown;type CompleteInput={ref:{type:\"prompt\";name:string}|{type:\"resourceTemplate\";uri:string};argument:{name:string;value:string};};type CompleteResult=unknown;type ReadLogsInput={logRef:string;cursor?:string;limit?:number};type ReadLogsResult={entries:CodeModeLogEntry[];nextCursor?:string};type ReadCodeModeRecoveryInput={recoveryRef:string;cursor?:string;limit?:number};type CodeModeDiagnostic={code:string;message:string;severity:\"error\"|\"warning\"|\"info\";line?:number;column?:number;};type CodeModeRecoveryClassification=\"setup_like\"|\"side_effecting\"|\"unknown\";type CodeModeRecoveryEntry={timestamp:string;code:string;declarationHash:string;outcome:{ok:true}|{ok:false;code:string;message:string};diagnostics:Array<Pick<CodeModeDiagnostic,\"code\"|\"severity\"|\"message\">>;recoveryClassification:CodeModeRecoveryClassification;logsStored?:boolean;summary?:string;};type ReadCodeModeRecoveryResult={entries:CodeModeRecoveryEntry[];nextCursor?:string};type CodeModeLogEntry={level:\"log\"|\"info\"|\"warn\"|\"error\"|\"debug\";message:string;timestamp:string;};type CodeModeSessionStatus=\"created\"|\"reused\";type CodeModeRunMeta={runId:string;traceId:string;declarationHash:string;durationMs:number;timeoutMs:number;maxTimeoutMs:number;sessionId?:string|null;sessionStatus?:CodeModeSessionStatus|null;recoveryRef?:string|null;};interface Console{log(...values:unknown[]):void;info(...values:unknown[]):void;warn(...values:unknown[]):void;error(...values:unknown[]):void;debug(...values:unknown[]):void;}declare const console:Console;";
@@ -79924,9 +80742,11 @@ async function runCodeMode(input) {
79924
80742
  const callable = listCodeModeCallableCaplets(input.service);
79925
80743
  const declaration = generateCodeModeDeclarations({ caplets: callable });
79926
80744
  const declarationHash = codeModeDeclarationHash(declaration);
80745
+ const platformRuntimeHash = codeModeDeclarationHash(CODE_MODE_PLATFORM_RUNTIME_SOURCE);
80746
+ let invokedCaplet = false;
79927
80747
  const sessionCompatibility = {
79928
80748
  declarationHash,
79929
- platformRuntimeHash: codeModeDeclarationHash(CODE_MODE_PLATFORM_RUNTIME_SOURCE),
80749
+ platformRuntimeHash,
79930
80750
  runtimeScope: input.runtimeScope ?? "",
79931
80751
  version: 1
79932
80752
  };
@@ -79943,7 +80763,8 @@ async function runCodeMode(input) {
79943
80763
  };
79944
80764
  const meta = () => ({
79945
80765
  ...metaBase,
79946
- durationMs: Date.now() - startedAt
80766
+ durationMs: Date.now() - startedAt,
80767
+ anyCapletInvoked: invokedCaplet
79947
80768
  });
79948
80769
  if (input.sessionId !== void 0 && !input.sessionManager) return {
79949
80770
  ok: false,
@@ -80030,7 +80851,6 @@ async function runCodeMode(input) {
80030
80851
  };
80031
80852
  }
80032
80853
  const capturedLogs = [];
80033
- let invokedCaplet = false;
80034
80854
  const api = createCodeModeCapletsApi({
80035
80855
  service: input.service,
80036
80856
  readLogs: async (readInput) => input.logStore?.read(readInput) ?? { entries: [] },
@@ -80336,6 +81156,7 @@ function emptyCodeModeRunMeta() {
80336
81156
  durationMs: 0,
80337
81157
  timeoutMs: 0,
80338
81158
  maxTimeoutMs: 0,
81159
+ anyCapletInvoked: false,
80339
81160
  sessionId: null,
80340
81161
  sessionStatus: null,
80341
81162
  recoveryRef: null
@@ -80595,18 +81416,28 @@ function resolveNativeCapletsServiceOptions(input = {}, env = process.env) {
80595
81416
  ...remoteFetch ? { fetch: remoteFetch } : {}
80596
81417
  }, env);
80597
81418
  const cloud = resolveNativeCloudPresence(input.remote?.cloud, env);
81419
+ const requestInit = mode.mode === "cloud" && cloud ? { headers: { Authorization: `Bearer ${cloud.accessToken}` } } : server.requestInit;
80598
81420
  return {
80599
81421
  mode: mode.mode,
80600
81422
  remote: {
80601
81423
  url: server.attachUrl,
80602
81424
  auth: nativeAuthFromRemoteAuth$1(server.auth),
80603
81425
  pollIntervalMs: parsePollInterval(input.remote?.pollIntervalMs),
80604
- requestInit: mode.mode === "cloud" && cloud ? { headers: { Authorization: `Bearer ${cloud.accessToken}` } } : server.requestInit,
81426
+ requestInit: withRequestHeaders(requestInit, input.remote?.requestHeaders),
80605
81427
  ...cloud ? { cloud } : {},
80606
81428
  ...server.fetch ? { fetch: server.fetch } : {}
80607
81429
  }
80608
81430
  };
80609
81431
  }
81432
+ function withRequestHeaders(requestInit, requestHeaders) {
81433
+ if (!requestHeaders) return requestInit;
81434
+ const headers = new Headers(requestInit.headers);
81435
+ for (const [name, value] of Object.entries(requestHeaders)) headers.set(name, value);
81436
+ return {
81437
+ ...requestInit,
81438
+ headers
81439
+ };
81440
+ }
80610
81441
  function resolveNativeHostedCloudRemote(url, workspace, fetch) {
80611
81442
  return resolveHostedCloudRemote({
80612
81443
  url,
@@ -81021,7 +81852,382 @@ function projectSyncFiles(projectRoot) {
81021
81852
  }));
81022
81853
  }
81023
81854
  //#endregion
81855
+ //#region src/attach/api.ts
81856
+ const CAPLETS_ATTACH_SESSION_HEADER = "caplets-attach-session-id";
81857
+ async function buildAttachProjection(engine) {
81858
+ const snapshot = await engine.exposureSnapshot();
81859
+ const partial = sortAttachProjectionInput({
81860
+ caplets: snapshot.progressiveCaplets.map(progressiveCapletExport),
81861
+ tools: snapshot.directTools.map(toolExport),
81862
+ resources: snapshot.directResources.map(resourceExport),
81863
+ resourceTemplates: snapshot.directResourceTemplates.map(resourceTemplateExport),
81864
+ prompts: snapshot.directPrompts.map(promptExport),
81865
+ completions: completionExports(snapshot),
81866
+ codeModeCaplets: snapshot.codeModeCaplets.map(codeModeCapletExport),
81867
+ diagnostics: snapshot.hiddenCaplets.map((hidden) => ({
81868
+ code: `ATTACH_CAPLET_${hidden.reason.toUpperCase()}`,
81869
+ message: `Caplet ${hidden.capletId} is not exported: ${hidden.reason}.`,
81870
+ capletId: hidden.capletId,
81871
+ ...hidden.error ? { details: hidden.error } : {}
81872
+ }))
81873
+ });
81874
+ const revision = revisionFor(partial);
81875
+ const manifest = {
81876
+ version: 1,
81877
+ revision,
81878
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
81879
+ ...withRevisionExportIds(revision, partial)
81880
+ };
81881
+ return {
81882
+ manifest,
81883
+ routes: routesFor(manifest)
81884
+ };
81885
+ }
81886
+ async function buildNativeAttachProjection(service) {
81887
+ const tools = service.listTools();
81888
+ const partial = sortAttachProjectionInput({
81889
+ caplets: nativeProgressiveCaplets(tools),
81890
+ tools: nativeDirectTools(tools),
81891
+ resources: [],
81892
+ resourceTemplates: [],
81893
+ prompts: [],
81894
+ completions: [],
81895
+ codeModeCaplets: nativeCodeModeCaplets(tools),
81896
+ diagnostics: []
81897
+ });
81898
+ const revision = revisionFor(partial);
81899
+ const manifest = {
81900
+ version: 1,
81901
+ revision,
81902
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
81903
+ ...withRevisionExportIds(revision, partial)
81904
+ };
81905
+ return {
81906
+ manifest,
81907
+ routes: routesFor(manifest)
81908
+ };
81909
+ }
81910
+ function nativeProgressiveCaplets(tools) {
81911
+ return tools.filter((tool) => tool.codeModeRun !== true && !nativeDirectToolOperation(tool)).map((tool) => ({
81912
+ stableId: `native:${tool.caplet}`,
81913
+ kind: "caplet",
81914
+ name: tool.caplet,
81915
+ title: tool.title,
81916
+ description: tool.description,
81917
+ inputSchema: tool.inputSchema,
81918
+ outputSchema: tool.outputSchema,
81919
+ annotations: tool.annotations,
81920
+ schemaHash: schemaHash(tool.inputSchema ?? null),
81921
+ capletId: tool.caplet,
81922
+ shadowing: tool.shadowing ?? "forbid"
81923
+ }));
81924
+ }
81925
+ function nativeDirectTools(tools) {
81926
+ return tools.flatMap((tool) => {
81927
+ const operation = nativeDirectToolOperation(tool);
81928
+ if (!operation || tool.codeModeRun === true || !tool.sourceCaplet) return [];
81929
+ return [{
81930
+ stableId: `native-tool:${tool.caplet}`,
81931
+ kind: "tool",
81932
+ name: tool.caplet,
81933
+ downstreamName: operation,
81934
+ title: tool.title,
81935
+ description: tool.description,
81936
+ inputSchema: tool.inputSchema,
81937
+ outputSchema: tool.outputSchema,
81938
+ annotations: tool.annotations,
81939
+ schemaHash: schemaHash({
81940
+ input: tool.inputSchema,
81941
+ output: tool.outputSchema
81942
+ }),
81943
+ capletId: tool.sourceCaplet,
81944
+ shadowing: tool.shadowing ?? "forbid"
81945
+ }];
81946
+ });
81947
+ }
81948
+ function nativeDirectToolOperation(tool) {
81949
+ if (!tool.sourceCaplet || !tool.caplet.startsWith(`${tool.sourceCaplet}__`)) return;
81950
+ return tool.caplet.slice(tool.sourceCaplet.length + 2);
81951
+ }
81952
+ function nativeCodeModeCaplets(tools) {
81953
+ return tools.flatMap((tool) => (tool.codeModeCaplets ?? []).map((caplet) => ({
81954
+ stableId: `native-code-mode:${caplet.id}`,
81955
+ kind: "caplet",
81956
+ name: caplet.name,
81957
+ title: caplet.name,
81958
+ description: caplet.description,
81959
+ schemaHash: null,
81960
+ capletId: caplet.id,
81961
+ shadowing: caplet.shadowing ?? "forbid"
81962
+ })));
81963
+ }
81964
+ async function invokeNativeAttachExport(service, projection, request) {
81965
+ if (request.revision !== projection.manifest.revision) throw new CapletsError("ATTACH_MANIFEST_STALE", "Attach manifest revision is stale.");
81966
+ const route = projection.routes.get(request.exportId);
81967
+ if (!route || route.kind !== request.kind) throw new CapletsError("ATTACH_EXPORT_NOT_FOUND", "Attach export was not found.");
81968
+ if (route.kind !== "caplet") {
81969
+ if (route.kind === "tool") return await service.execute(`${route.capletId}__${route.downstreamName}`, request.input);
81970
+ throw new CapletsError("REQUEST_INVALID", "Native attach sessions only support Caplet and tool exports.");
81971
+ }
81972
+ return await service.execute(route.capletId, request.input);
81973
+ }
81974
+ async function invokeAttachExport$1(engine, projection, request) {
81975
+ if (request.revision !== projection.manifest.revision) throw new CapletsError("ATTACH_MANIFEST_STALE", "Attach manifest revision is stale.");
81976
+ const route = projection.routes.get(request.exportId);
81977
+ if (!route || route.kind !== request.kind) throw new CapletsError("ATTACH_EXPORT_NOT_FOUND", "Attach export was not found.");
81978
+ if (route.kind === "caplet") return await engine.execute(route.capletId, request.input);
81979
+ if (route.kind === "tool") return await engine.executeDirectTool(route.capletId, route.downstreamName, isRecord$4(request.input) ? request.input : {});
81980
+ if (route.kind === "resource") return await engine.readDirectResource(route.capletId, route.downstreamUri);
81981
+ if (route.kind === "resourceTemplate") {
81982
+ const uri = isRecord$4(request.input) && typeof request.input.uri === "string" ? request.input.uri : void 0;
81983
+ if (!uri) throw new CapletsError("REQUEST_INVALID", "Attach resource template invoke requires input.uri.");
81984
+ const downstreamUri = downstreamResourceUri$1(route.capletId, uri);
81985
+ if (!directResourceUriMatchesTemplate(downstreamUri, route.downstreamUriTemplate)) throw new CapletsError("ATTACH_EXPORT_NOT_FOUND", "Attach resource URI does not match the exported resource template.");
81986
+ return await engine.readDirectResource(route.capletId, downstreamUri);
81987
+ }
81988
+ if (route.kind === "prompt") return await engine.getDirectPrompt(route.capletId, route.downstreamName, isRecord$4(request.input) ? stringifyRecord(request.input) : {});
81989
+ if (route.kind === "completion") return await engine.execute(route.capletId, {
81990
+ ...normalizeCompletionInput(projection.manifest, route.capletId, request.input),
81991
+ operation: "complete"
81992
+ });
81993
+ throw new CapletsError("REQUEST_INVALID", "Attach export kind is not invokable via /v1/attach/invoke.");
81994
+ }
81995
+ function attachErrorResponse(error) {
81996
+ const safe = toSafeError(error, "INTERNAL_ERROR");
81997
+ return {
81998
+ status: safe.code === "ATTACH_MANIFEST_STALE" ? 409 : safe.code === "ATTACH_EXPORT_NOT_FOUND" ? 404 : safe.code === "REQUEST_INVALID" ? 400 : 500,
81999
+ body: {
82000
+ ok: false,
82001
+ error: safe
82002
+ }
82003
+ };
82004
+ }
82005
+ function progressiveCapletExport(entry) {
82006
+ const inputSchema = generatedToolInputJsonSchemaForCaplet(entry.caplet);
82007
+ return {
82008
+ stableId: `progressive:${entry.caplet.server}`,
82009
+ kind: "caplet",
82010
+ name: entry.caplet.server,
82011
+ title: entry.caplet.name,
82012
+ description: entry.caplet.description,
82013
+ inputSchema,
82014
+ schemaHash: schemaHash(inputSchema),
82015
+ capletId: entry.caplet.server,
82016
+ shadowing: shadowingPolicy(entry.caplet)
82017
+ };
82018
+ }
82019
+ function codeModeCapletExport(entry) {
82020
+ return {
82021
+ stableId: `code_mode:${entry.caplet.server}`,
82022
+ kind: "caplet",
82023
+ name: entry.caplet.name,
82024
+ title: entry.caplet.name,
82025
+ description: entry.caplet.description,
82026
+ schemaHash: null,
82027
+ capletId: entry.caplet.server,
82028
+ shadowing: shadowingPolicy(entry.caplet)
82029
+ };
82030
+ }
82031
+ function toolExport(entry) {
82032
+ return {
82033
+ stableId: `tool:${entry.caplet.server}:${entry.downstreamName}`,
82034
+ kind: "tool",
82035
+ name: entry.name,
82036
+ downstreamName: entry.downstreamName,
82037
+ title: entry.tool.name,
82038
+ description: entry.tool.description,
82039
+ inputSchema: entry.tool.inputSchema,
82040
+ outputSchema: entry.tool.outputSchema,
82041
+ annotations: entry.tool.annotations,
82042
+ schemaHash: schemaHash({
82043
+ input: entry.tool.inputSchema,
82044
+ output: entry.tool.outputSchema
82045
+ }),
82046
+ capletId: entry.caplet.server,
82047
+ shadowing: shadowingPolicy(entry.caplet)
82048
+ };
82049
+ }
82050
+ function resourceExport(entry) {
82051
+ return {
82052
+ stableId: `resource:${entry.caplet.server}:${entry.downstreamUri}`,
82053
+ kind: "resource",
82054
+ uri: entry.uri,
82055
+ downstreamUri: entry.downstreamUri,
82056
+ title: entry.resource.name,
82057
+ description: entry.resource.description,
82058
+ ...entry.resource.mimeType ? { mimeType: entry.resource.mimeType } : {},
82059
+ ...typeof entry.resource.size === "number" ? { size: entry.resource.size } : {},
82060
+ schemaHash: null,
82061
+ capletId: entry.caplet.server,
82062
+ shadowing: shadowingPolicy(entry.caplet)
82063
+ };
82064
+ }
82065
+ function resourceTemplateExport(entry) {
82066
+ return {
82067
+ stableId: `resourceTemplate:${entry.caplet.server}:${entry.downstreamUriTemplate}`,
82068
+ kind: "resourceTemplate",
82069
+ uriTemplate: entry.uriTemplate,
82070
+ downstreamUriTemplate: entry.downstreamUriTemplate,
82071
+ title: entry.resourceTemplate.name,
82072
+ description: entry.resourceTemplate.description,
82073
+ ...entry.resourceTemplate.mimeType ? { mimeType: entry.resourceTemplate.mimeType } : {},
82074
+ schemaHash: null,
82075
+ capletId: entry.caplet.server,
82076
+ shadowing: shadowingPolicy(entry.caplet)
82077
+ };
82078
+ }
82079
+ function promptExport(entry) {
82080
+ const inputSchema = { arguments: entry.prompt.arguments ?? [] };
82081
+ return {
82082
+ stableId: `prompt:${entry.caplet.server}:${entry.downstreamName}`,
82083
+ kind: "prompt",
82084
+ name: entry.name,
82085
+ downstreamName: entry.downstreamName,
82086
+ title: entry.prompt.name,
82087
+ description: entry.prompt.description,
82088
+ inputSchema,
82089
+ schemaHash: schemaHash(inputSchema),
82090
+ capletId: entry.caplet.server,
82091
+ shadowing: shadowingPolicy(entry.caplet)
82092
+ };
82093
+ }
82094
+ function completionExports(snapshot) {
82095
+ return [...new Map([...snapshot.directPrompts, ...snapshot.directResourceTemplates].map((entry) => [entry.caplet.server, entry.caplet])).entries()].sort(([left], [right]) => left.localeCompare(right)).map(([capletId, caplet]) => ({
82096
+ stableId: `completion:${capletId}`,
82097
+ kind: "completion",
82098
+ name: `${capletId}:complete`,
82099
+ title: "Complete",
82100
+ description: `MCP completion for ${capletId}.`,
82101
+ schemaHash: null,
82102
+ capletId,
82103
+ shadowing: shadowingPolicy(caplet)
82104
+ }));
82105
+ }
82106
+ function shadowingPolicy(caplet) {
82107
+ return caplet.shadowing ?? "forbid";
82108
+ }
82109
+ function sortAttachProjectionInput(partial) {
82110
+ return {
82111
+ caplets: sortByStableId(partial.caplets),
82112
+ tools: sortByStableId(partial.tools),
82113
+ resources: sortByStableId(partial.resources),
82114
+ resourceTemplates: sortByStableId(partial.resourceTemplates),
82115
+ prompts: sortByStableId(partial.prompts),
82116
+ completions: sortByStableId(partial.completions),
82117
+ codeModeCaplets: sortByStableId(partial.codeModeCaplets),
82118
+ diagnostics: [...partial.diagnostics].sort((left, right) => diagnosticSortKey(left).localeCompare(diagnosticSortKey(right)))
82119
+ };
82120
+ }
82121
+ function sortByStableId(entries) {
82122
+ return [...entries].sort((left, right) => left.stableId.localeCompare(right.stableId));
82123
+ }
82124
+ function diagnosticSortKey(diagnostic) {
82125
+ return stableJsonStringify({
82126
+ code: diagnostic.code,
82127
+ capletId: diagnostic.capletId ?? "",
82128
+ message: diagnostic.message
82129
+ });
82130
+ }
82131
+ function revisionFor(value) {
82132
+ return `sha256:${createHash("sha256").update(stableJsonStringify(value)).digest("hex")}`;
82133
+ }
82134
+ function withRevisionExportIds(revision, partial) {
82135
+ return {
82136
+ ...partial,
82137
+ caplets: partial.caplets.map((entry) => withExportId(revision, entry)),
82138
+ tools: partial.tools.map((entry) => withExportId(revision, entry)),
82139
+ resources: partial.resources.map((entry) => withExportId(revision, entry)),
82140
+ resourceTemplates: partial.resourceTemplates.map((entry) => withExportId(revision, entry)),
82141
+ prompts: partial.prompts.map((entry) => withExportId(revision, entry)),
82142
+ completions: partial.completions.map((entry) => withExportId(revision, entry)),
82143
+ codeModeCaplets: partial.codeModeCaplets.map((entry) => withExportId(revision, entry))
82144
+ };
82145
+ }
82146
+ function withExportId(revision, entry) {
82147
+ return {
82148
+ ...entry,
82149
+ exportId: `${revision}:${entry.stableId}`
82150
+ };
82151
+ }
82152
+ function routesFor(manifest) {
82153
+ const routes = /* @__PURE__ */ new Map();
82154
+ for (const entry of manifest.caplets) routes.set(entry.exportId, {
82155
+ kind: "caplet",
82156
+ capletId: entry.capletId
82157
+ });
82158
+ for (const entry of manifest.tools) routes.set(entry.exportId, {
82159
+ kind: "tool",
82160
+ capletId: entry.capletId,
82161
+ downstreamName: entry.downstreamName
82162
+ });
82163
+ for (const entry of manifest.resources) routes.set(entry.exportId, {
82164
+ kind: "resource",
82165
+ capletId: entry.capletId,
82166
+ downstreamUri: entry.downstreamUri
82167
+ });
82168
+ for (const entry of manifest.resourceTemplates) routes.set(entry.exportId, {
82169
+ kind: "resourceTemplate",
82170
+ capletId: entry.capletId,
82171
+ downstreamUriTemplate: entry.downstreamUriTemplate
82172
+ });
82173
+ for (const entry of manifest.prompts) routes.set(entry.exportId, {
82174
+ kind: "prompt",
82175
+ capletId: entry.capletId,
82176
+ downstreamName: entry.downstreamName
82177
+ });
82178
+ for (const entry of manifest.completions) routes.set(entry.exportId, {
82179
+ kind: "completion",
82180
+ capletId: entry.capletId
82181
+ });
82182
+ for (const entry of manifest.codeModeCaplets) routes.set(entry.exportId, {
82183
+ kind: "caplet",
82184
+ capletId: entry.capletId
82185
+ });
82186
+ return routes;
82187
+ }
82188
+ function normalizeCompletionInput(manifest, capletId, input) {
82189
+ if (!isRecord$4(input)) return {};
82190
+ const ref = input.ref;
82191
+ if (!isRecord$4(ref)) return input;
82192
+ if (ref.type === "prompt" && typeof ref.name === "string") {
82193
+ const prompt = manifest.prompts.find((entry) => entry.capletId === capletId && (entry.name === ref.name || entry.downstreamName === ref.name));
82194
+ if (!prompt) return input;
82195
+ return {
82196
+ ...input,
82197
+ ref: {
82198
+ ...ref,
82199
+ name: prompt.downstreamName
82200
+ }
82201
+ };
82202
+ }
82203
+ if (ref.type === "resourceTemplate" && typeof ref.uri === "string") {
82204
+ const resourceTemplate = manifest.resourceTemplates.find((entry) => entry.capletId === capletId && (entry.uriTemplate === ref.uri || entry.downstreamUriTemplate === ref.uri));
82205
+ if (!resourceTemplate) return input;
82206
+ return {
82207
+ ...input,
82208
+ ref: {
82209
+ ...ref,
82210
+ uri: resourceTemplate.downstreamUriTemplate
82211
+ }
82212
+ };
82213
+ }
82214
+ return input;
82215
+ }
82216
+ function isRecord$4(value) {
82217
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
82218
+ }
82219
+ function stringifyRecord(record) {
82220
+ return Object.fromEntries(Object.entries(record).map(([key, value]) => [key, typeof value === "string" ? value : JSON.stringify(value)]));
82221
+ }
82222
+ function downstreamResourceUri$1(capletId, uri) {
82223
+ if (!uri.startsWith("caplets://")) return uri;
82224
+ const decoded = decodeDirectResourceUri(uri);
82225
+ if (decoded.capletId !== capletId) throw new CapletsError("ATTACH_EXPORT_NOT_FOUND", "Attach resource template URI belongs to a different Caplet.");
82226
+ return decoded.downstreamUri;
82227
+ }
82228
+ //#endregion
81024
82229
  //#region src/native/remote.ts
82230
+ const ATTACH_SESSION_UNSUPPORTED_RETRY_MS = 6e4;
81025
82231
  function createSdkRemoteCapletsClient(options) {
81026
82232
  const listeners = /* @__PURE__ */ new Set();
81027
82233
  let manifest;
@@ -81029,18 +82235,50 @@ function createSdkRemoteCapletsClient(options) {
81029
82235
  let eventsAbort;
81030
82236
  let eventsStartInFlight;
81031
82237
  let eventsReconnectTimer;
82238
+ let attachSessionId;
82239
+ let attachSessionInFlight;
82240
+ let attachSessionsUnsupportedUntil = 0;
81032
82241
  let closed = false;
81033
82242
  const resolveRuntimeOptions = async () => {
81034
82243
  return options.resolveRuntimeOptions ? await options.resolveRuntimeOptions() : options;
81035
82244
  };
81036
82245
  const fetchFor = (runtimeOptions) => runtimeOptions.fetch ?? fetch;
81037
82246
  const fetchCurrentManifest = async () => {
81038
- const runtimeOptions = await resolveRuntimeOptions();
81039
- return await fetchAttachManifest(runtimeOptions.url, runtimeOptions.requestInit, fetchFor(runtimeOptions));
82247
+ return await withAttachSessionRetry(async (runtimeOptions, sessionId) => {
82248
+ return await fetchAttachManifest(runtimeOptions.url, runtimeOptions.requestInit, sessionId, fetchFor(runtimeOptions));
82249
+ });
81040
82250
  };
81041
82251
  const invokeCurrentExport = async (body) => {
82252
+ return await withAttachSessionRetry(async (runtimeOptions, sessionId) => {
82253
+ return await invokeAttachExport(runtimeOptions.url, runtimeOptions.requestInit, sessionId, fetchFor(runtimeOptions), body);
82254
+ });
82255
+ };
82256
+ const ensureAttachSession = async (runtimeOptions) => {
82257
+ if (!options.attachSessionMetadata) return void 0;
82258
+ if (attachSessionsUnsupportedUntil > Date.now()) return void 0;
82259
+ attachSessionsUnsupportedUntil = 0;
82260
+ if (attachSessionId) return attachSessionId;
82261
+ if (attachSessionInFlight) return await attachSessionInFlight;
82262
+ attachSessionInFlight = createAttachSession(runtimeOptions.url, runtimeOptions.requestInit, fetchFor(runtimeOptions), options.attachSessionMetadata);
82263
+ try {
82264
+ attachSessionId = await attachSessionInFlight;
82265
+ if (!attachSessionId) attachSessionsUnsupportedUntil = Date.now() + ATTACH_SESSION_UNSUPPORTED_RETRY_MS;
82266
+ return attachSessionId;
82267
+ } finally {
82268
+ attachSessionInFlight = void 0;
82269
+ }
82270
+ };
82271
+ const withAttachSessionRetry = async (operation) => {
81042
82272
  const runtimeOptions = await resolveRuntimeOptions();
81043
- return await invokeAttachExport(runtimeOptions.url, runtimeOptions.requestInit, fetchFor(runtimeOptions), body);
82273
+ const sessionId = await ensureAttachSession(runtimeOptions);
82274
+ if (closed) throw new CapletsError("SERVER_UNAVAILABLE", "Remote Caplets client is closed.");
82275
+ try {
82276
+ return await operation(runtimeOptions, sessionId);
82277
+ } catch (error) {
82278
+ if (!options.attachSessionMetadata || attachSessionsUnsupportedUntil > Date.now() || !sessionId || !isAttachSessionNotFound(error)) throw error;
82279
+ if (attachSessionId === sessionId) attachSessionId = void 0;
82280
+ return await operation(runtimeOptions, await ensureAttachSession(runtimeOptions));
82281
+ }
81044
82282
  };
81045
82283
  const clearEventsReconnectTimer = () => {
81046
82284
  if (eventsReconnectTimer) {
@@ -81060,7 +82298,9 @@ function createSdkRemoteCapletsClient(options) {
81060
82298
  try {
81061
82299
  const runtimeOptions = await resolveRuntimeOptions();
81062
82300
  if (closed || eventsAbort || listeners.size === 0) return;
81063
- eventsAbort = startAttachEvents(runtimeOptions.url, runtimeOptions.requestInit, fetchFor(runtimeOptions), listeners, (closedAbort, retry) => {
82301
+ const sessionId = await ensureAttachSession(runtimeOptions);
82302
+ if (closed || eventsAbort || listeners.size === 0) return;
82303
+ eventsAbort = startAttachEvents(runtimeOptions.url, runtimeOptions.requestInit, sessionId, fetchFor(runtimeOptions), listeners, (closedAbort, retry) => {
81064
82304
  if (eventsAbort !== closedAbort) return;
81065
82305
  eventsAbort = void 0;
81066
82306
  if (!retry || closedAbort.signal.aborted || listeners.size === 0) return;
@@ -81142,6 +82382,13 @@ function createSdkRemoteCapletsClient(options) {
81142
82382
  eventsAbort?.abort();
81143
82383
  eventsAbort = void 0;
81144
82384
  listeners.clear();
82385
+ const pendingSessionId = await attachSessionInFlight?.catch(() => void 0);
82386
+ const sessionId = attachSessionId ?? pendingSessionId;
82387
+ attachSessionId = void 0;
82388
+ if (sessionId) await (async () => {
82389
+ const runtimeOptions = await resolveRuntimeOptions();
82390
+ await closeAttachSession(runtimeOptions.url, runtimeOptions.requestInit, fetchFor(runtimeOptions), sessionId);
82391
+ })().catch(() => void 0);
81145
82392
  }
81146
82393
  };
81147
82394
  }
@@ -81338,17 +82585,63 @@ function remoteCodeModeCallableNativeTools$1(tools) {
81338
82585
  function nativeToolRouteId(tool) {
81339
82586
  return tool.codeModeRun ? nativeCodeModeToolId : tool.name;
81340
82587
  }
81341
- async function fetchAttachManifest(attachUrl, requestInit, fetchImpl) {
82588
+ async function fetchAttachManifest(attachUrl, requestInit, attachSessionId, fetchImpl) {
82589
+ const headers = attachHeaders(requestInit, attachSessionId);
81342
82590
  const response = await fetchImpl(new URL("manifest", slashUrl(attachUrl)), {
81343
82591
  ...requestInit,
81344
- method: "GET"
82592
+ method: "GET",
82593
+ headers
81345
82594
  });
81346
- if (!response.ok) throw new CapletsError("SERVER_UNAVAILABLE", `Caplets attach manifest returned HTTP ${response.status}.`);
82595
+ if (!response.ok) {
82596
+ let payload;
82597
+ try {
82598
+ payload = await response.json();
82599
+ } catch {
82600
+ payload = { error: { message: `Caplets attach manifest returned HTTP ${response.status}.` } };
82601
+ }
82602
+ throw attachPayloadError(payload, response.status, "manifest");
82603
+ }
81347
82604
  return await response.json();
81348
82605
  }
81349
- async function invokeAttachExport(attachUrl, requestInit, fetchImpl, body) {
82606
+ async function createAttachSession(attachUrl, requestInit, fetchImpl, metadata) {
81350
82607
  const headers = new Headers(requestInit?.headers);
81351
82608
  headers.set("content-type", "application/json");
82609
+ const response = await fetchImpl(new URL("sessions", slashUrl(attachUrl)), {
82610
+ ...requestInit,
82611
+ method: "POST",
82612
+ headers,
82613
+ body: JSON.stringify(metadata)
82614
+ });
82615
+ if (!response.ok) {
82616
+ if (response.status === 404) return void 0;
82617
+ let payload;
82618
+ try {
82619
+ payload = await response.json();
82620
+ } catch {
82621
+ payload = void 0;
82622
+ }
82623
+ if (response.status === 400 && isAttachSessionProjectContextRejected(payload)) return;
82624
+ throw new CapletsError("SERVER_UNAVAILABLE", `Caplets attach session returned HTTP ${response.status}.`);
82625
+ }
82626
+ const payload = await response.json();
82627
+ if (!isPlainObject(payload) || typeof payload.sessionId !== "string") throw new CapletsError("SERVER_UNAVAILABLE", "Caplets attach session response was invalid.");
82628
+ return payload.sessionId;
82629
+ }
82630
+ async function closeAttachSession(attachUrl, requestInit, fetchImpl, attachSessionId) {
82631
+ await fetchImpl(new URL(`sessions/${encodeURIComponent(attachSessionId)}`, slashUrl(attachUrl)), {
82632
+ ...requestInit,
82633
+ method: "DELETE",
82634
+ headers: attachHeaders(requestInit, attachSessionId)
82635
+ });
82636
+ }
82637
+ function attachHeaders(requestInit, attachSessionId) {
82638
+ const headers = new Headers(requestInit?.headers);
82639
+ if (attachSessionId) headers.set(CAPLETS_ATTACH_SESSION_HEADER, attachSessionId);
82640
+ return headers;
82641
+ }
82642
+ async function invokeAttachExport(attachUrl, requestInit, attachSessionId, fetchImpl, body) {
82643
+ const headers = attachHeaders(requestInit, attachSessionId);
82644
+ headers.set("content-type", "application/json");
81352
82645
  const response = await fetchImpl(new URL("invoke", slashUrl(attachUrl)), {
81353
82646
  ...requestInit,
81354
82647
  method: "POST",
@@ -81359,10 +82652,10 @@ async function invokeAttachExport(attachUrl, requestInit, fetchImpl, body) {
81359
82652
  try {
81360
82653
  payload = await response.json();
81361
82654
  } catch (error) {
81362
- if (!response.ok) throw attachPayloadError({ error: { message: `Caplets attach invoke returned HTTP ${response.status}.` } }, response.status);
82655
+ if (!response.ok) throw attachPayloadError({ error: { message: `Caplets attach invoke returned HTTP ${response.status}.` } }, response.status, "invoke");
81363
82656
  throw error;
81364
82657
  }
81365
- if (!response.ok) throw attachPayloadError(payload, response.status);
82658
+ if (!response.ok) throw attachPayloadError(payload, response.status, "invoke");
81366
82659
  if (isPlainObject(payload) && payload.ok === true && "data" in payload) return payload.data;
81367
82660
  return payload;
81368
82661
  }
@@ -81619,9 +82912,9 @@ function slashUrl(url) {
81619
82912
  next.pathname = next.pathname.endsWith("/") ? next.pathname : `${next.pathname}/`;
81620
82913
  return next;
81621
82914
  }
81622
- function attachPayloadError(payload, status) {
82915
+ function attachPayloadError(payload, status, endpoint) {
81623
82916
  const error = isPlainObject(payload) && isPlainObject(payload.error) ? payload.error : void 0;
81624
- const message = typeof error?.message === "string" ? error.message : `Caplets attach invoke returned HTTP ${status}.`;
82917
+ const message = typeof error?.message === "string" ? error.message : `Caplets attach ${endpoint} returned HTTP ${status}.`;
81625
82918
  const thrown = new Error(message);
81626
82919
  thrown.status = status;
81627
82920
  if (error && "code" in error) thrown.code = error.code;
@@ -81630,14 +82923,23 @@ function attachPayloadError(payload, status) {
81630
82923
  function isAttachManifestStale(error) {
81631
82924
  return isPlainObject(error) && error.code === "ATTACH_MANIFEST_STALE";
81632
82925
  }
81633
- function startAttachEvents(attachUrl, requestInit, fetchImpl, listeners, onClose) {
82926
+ function isAttachSessionNotFound(error) {
82927
+ return error.code === "REQUEST_INVALID" && /\battach session was not found\b/iu.test(errorMessage$1(error));
82928
+ }
82929
+ function isAttachSessionProjectContextRejected(payload) {
82930
+ const error = isPlainObject(payload) && isPlainObject(payload.error) ? payload.error : void 0;
82931
+ return error?.code === "REQUEST_INVALID" && typeof error.message === "string" && /\bproject context is only accepted by loopback runtimes\b/iu.test(error.message);
82932
+ }
82933
+ function startAttachEvents(attachUrl, requestInit, attachSessionId, fetchImpl, listeners, onClose) {
81634
82934
  const abort = new AbortController();
81635
82935
  let retry = true;
81636
82936
  (async () => {
81637
82937
  try {
82938
+ const headers = attachHeaders(requestInit, attachSessionId);
81638
82939
  const response = await fetchImpl(new URL("events", slashUrl(attachUrl)), {
81639
82940
  ...requestInit,
81640
82941
  method: "GET",
82942
+ headers,
81641
82943
  signal: abort.signal
81642
82944
  });
81643
82945
  if (!response.ok) {
@@ -83018,7 +84320,11 @@ var DefaultNativeCapletsService = class {
83018
84320
  this.writeErr = options.writeErr ?? (() => void 0);
83019
84321
  this.engine = new CapletsEngine({
83020
84322
  ...options,
83021
- writeErr: this.writeErr
84323
+ writeErr: this.writeErr,
84324
+ telemetrySurface: options.telemetrySurface ?? "native",
84325
+ telemetryVisibility: options.telemetryVisibility ?? "hidden",
84326
+ telemetryRuntimeMode: options.telemetryRuntimeMode ?? runtimeModeFromNativeOptions(options),
84327
+ telemetryIntegration: options.telemetryIntegration ?? "native"
83022
84328
  });
83023
84329
  this.postReloadRefresh = this.refreshExposureSnapshot({ emitToolsChanged: this.hasSnapshotBackedDirectExposure() });
83024
84330
  this.unsubscribeEngineReload = this.engine.onReload(() => {
@@ -83049,7 +84355,16 @@ var DefaultNativeCapletsService = class {
83049
84355
  ];
83050
84356
  }
83051
84357
  async execute(capletId, request) {
83052
- if (capletId === "code_mode") return await executeCodeModeRunNative(this.codeModeDelegate(), request, this.codeModeSessions);
84358
+ if (capletId === "code_mode") {
84359
+ const started = Date.now();
84360
+ const envelope = await executeCodeModeRunNative(this.codeModeDelegate(), request, this.codeModeSessions);
84361
+ const parsed = codeModeRunInputSchema.safeParse(request);
84362
+ this.engine.captureCodeModeOutcome(envelope, {
84363
+ started,
84364
+ ...parsed.success && parsed.data.timeoutMs !== void 0 ? { timeoutMs: parsed.data.timeoutMs } : {}
84365
+ }).catch(() => void 0);
84366
+ return envelope;
84367
+ }
83053
84368
  const route = this.directToolRoutes.get(capletId);
83054
84369
  if (route) {
83055
84370
  if (isMcpPrimitiveRoute(route.operationName)) return await this.engine.execute(route.capletId, nativeMcpPrimitiveRequest(route.operationName, request));
@@ -83057,6 +84372,9 @@ var DefaultNativeCapletsService = class {
83057
84372
  }
83058
84373
  return await this.engine.execute(capletId, request);
83059
84374
  }
84375
+ async captureCodeModeOutcome(envelope, options) {
84376
+ await this.engine.captureCodeModeOutcome(envelope, options);
84377
+ }
83060
84378
  codeModeService() {
83061
84379
  return this.codeModeDelegate();
83062
84380
  }
@@ -83277,9 +84595,39 @@ function nativeMcpPrimitiveRequest(operationName, request) {
83277
84595
  };
83278
84596
  return { operation: operationName };
83279
84597
  }
84598
+ function operationFromNativeRequest(request) {
84599
+ return isRecord(request) ? request.operation : void 0;
84600
+ }
83280
84601
  function isRecord(value) {
83281
84602
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
83282
84603
  }
84604
+ function runtimeModeFromNativeOptions(options) {
84605
+ if (options.mode === "local") return "local";
84606
+ if (options.mode === "remote") return "remote";
84607
+ if (options.mode === "cloud") return "cloud";
84608
+ if (options.remote?.url) return "remote";
84609
+ const envMode = options.telemetryEnv?.CAPLETS_MODE ?? process.env.CAPLETS_MODE;
84610
+ if (envMode === "remote" || envMode === "cloud" || envMode === "local") return envMode;
84611
+ return "local";
84612
+ }
84613
+ function telemetryConfigFromNativeOptions(options) {
84614
+ const configPath = resolveConfigPath(options.configPath);
84615
+ const config = createLocalOverlayConfigLoader(options)(configPath, options.projectConfigPath ?? resolveProjectConfigPath());
84616
+ const explicitTelemetry = readTelemetryOnlyConfig(configPath);
84617
+ return explicitTelemetry === void 0 ? config : {
84618
+ ...config,
84619
+ telemetry: explicitTelemetry
84620
+ };
84621
+ }
84622
+ function readTelemetryOnlyConfig(path) {
84623
+ if (!existsSync(path)) return void 0;
84624
+ try {
84625
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
84626
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? typeof parsed.telemetry === "boolean" ? parsed.telemetry : void 0 : void 0;
84627
+ } catch {
84628
+ return;
84629
+ }
84630
+ }
83283
84631
  function codeModeRunNativeTool(capletTools) {
83284
84632
  const codeModeCaplets = capletTools.map((tool) => ({
83285
84633
  id: tool.caplet,
@@ -83373,7 +84721,7 @@ function createCompositeRemoteParts(remoteOptions, local, options, authKind, res
83373
84721
  authKind,
83374
84722
  ...options.writeErr ? { writeErr: options.writeErr } : {}
83375
84723
  });
83376
- const presence = createProjectBindingSessionManager(remoteOptions.cloud, local, options);
84724
+ const presence = createProjectBindingSessionManager(remoteOptions.cloud, remoteOptions, local, options);
83377
84725
  return {
83378
84726
  remote,
83379
84727
  ...presence ? { presence } : {}
@@ -83381,13 +84729,33 @@ function createCompositeRemoteParts(remoteOptions, local, options, authKind, res
83381
84729
  }
83382
84730
  function createRemoteClient(remoteOptions, options, authKind, resolveRuntimeRemoteOptions) {
83383
84731
  if (options.remoteClientFactory) return options.remoteClientFactory(remoteOptions);
84732
+ const attachSessionMetadata = isLoopbackRemote(remoteOptions) ? attachSessionMetadataForOptions(options) : void 0;
83384
84733
  return createSdkRemoteCapletsClient({
83385
84734
  ...remoteOptions,
83386
84735
  authKind,
83387
84736
  ...options.writeErr ? { writeErr: options.writeErr } : {},
84737
+ ...attachSessionMetadata ? { attachSessionMetadata } : {},
83388
84738
  ...resolveRuntimeRemoteOptions ? { resolveRuntimeOptions: resolveRuntimeRemoteOptions } : {}
83389
84739
  });
83390
84740
  }
84741
+ function attachSessionMetadataForOptions(options) {
84742
+ if (!options.projectRoot) return void 0;
84743
+ const projectRoot = canonicalProjectRootForMetadata(options.projectRoot);
84744
+ return {
84745
+ projectRoot,
84746
+ projectConfigPath: resolve(projectRoot, ".caplets", "config.json")
84747
+ };
84748
+ }
84749
+ function canonicalProjectRootForMetadata(projectRoot) {
84750
+ try {
84751
+ return realpathSync(projectRoot);
84752
+ } catch {
84753
+ return projectRoot;
84754
+ }
84755
+ }
84756
+ function isLoopbackRemote(remoteOptions) {
84757
+ return remoteOptions.url.protocol === "http:" && isLoopbackHost(remoteOptions.url.hostname);
84758
+ }
83391
84759
  var ProfileBackedNativeCapletsService = class {
83392
84760
  options;
83393
84761
  baseRemote;
@@ -83564,6 +84932,8 @@ var CompositeNativeCapletsService = class {
83564
84932
  closed = false;
83565
84933
  batchingReload = false;
83566
84934
  codeModeSessions = new CodeModeSessionManager();
84935
+ telemetry;
84936
+ ownsTelemetryDispatcher;
83567
84937
  constructor(remote, local, options, remoteIdentity, presence) {
83568
84938
  this.remote = remote;
83569
84939
  this.local = local;
@@ -83572,6 +84942,18 @@ var CompositeNativeCapletsService = class {
83572
84942
  this.presence = presence;
83573
84943
  this.unsubscribeRemote = this.remote.onToolsChanged(() => this.updateMergedTools());
83574
84944
  this.unsubscribeLocal = this.local.onToolsChanged(() => this.updateMergedTools());
84945
+ this.ownsTelemetryDispatcher = options.telemetryDispatcher === void 0;
84946
+ this.telemetry = createRuntimeTelemetryContext({
84947
+ config: telemetryConfigFromNativeOptions(options),
84948
+ env: options.telemetryEnv,
84949
+ stateDir: options.telemetryStateDir,
84950
+ surface: options.telemetrySurface ?? "native",
84951
+ visibility: options.telemetryVisibility ?? "hidden",
84952
+ runtimeMode: options.telemetryRuntimeMode ?? runtimeModeFromNativeOptions(options),
84953
+ integration: options.telemetryIntegration ?? "native",
84954
+ debugSink: options.telemetryDebugSink,
84955
+ dispatcher: options.telemetryDispatcher
84956
+ });
83575
84957
  const merged = this.mergeTools();
83576
84958
  this.tools = merged.tools;
83577
84959
  this.routes = merged.routes;
@@ -83585,10 +84967,10 @@ var CompositeNativeCapletsService = class {
83585
84967
  if (capletId === "code_mode") return await executeCodeModeRunNative(this, request, this.codeModeSessions);
83586
84968
  const route = this.routes.get(capletId);
83587
84969
  if (route?.service === "local") return await this.local.execute(route.capletId, request);
83588
- if (route?.service === "remote") return await this.remote.execute(route.capletId, request);
84970
+ if (route?.service === "remote") return await this.executeRemote(route.capletId, request);
83589
84971
  const diagnostic = this.namespaceDiagnostics.get(capletId);
83590
84972
  if (diagnostic) throw new CapletsError("CAPLET_NAMESPACE_COLLISION", diagnostic.hint, diagnostic);
83591
- return await this.remote.execute(capletId, request);
84973
+ return await this.executeRemote(capletId, request);
83592
84974
  }
83593
84975
  codeModeService() {
83594
84976
  return {
@@ -83608,6 +84990,8 @@ var CompositeNativeCapletsService = class {
83608
84990
  this.batchingReload = false;
83609
84991
  if (remoteReloaded === void 0 || localReloaded === void 0) return false;
83610
84992
  if (localReloaded) await this.presence?.updateAllowedCapletIds(this.local.listTools().map((tool) => tool.caplet));
84993
+ this.telemetry.config = telemetryConfigFromNativeOptions(this.options);
84994
+ this.startPresence();
83611
84995
  this.updateMergedTools();
83612
84996
  return remoteReloaded || localReloaded;
83613
84997
  }
@@ -83625,7 +85009,8 @@ var CompositeNativeCapletsService = class {
83625
85009
  await Promise.all([
83626
85010
  this.remote.close(),
83627
85011
  this.local.close(),
83628
- this.presence?.close()
85012
+ this.presence?.close(),
85013
+ this.ownsTelemetryDispatcher ? this.telemetry.dispatcher.shutdown() : void 0
83629
85014
  ]);
83630
85015
  }
83631
85016
  async replaceRemote(remote, remoteIdentityOrPresence, presence) {
@@ -83664,6 +85049,42 @@ var CompositeNativeCapletsService = class {
83664
85049
  writeErr(this.options, `Caplets tools-changed listener failed: ${errorMessage(error)}\n`);
83665
85050
  }
83666
85051
  }
85052
+ async executeRemote(capletId, request) {
85053
+ const started = Date.now();
85054
+ try {
85055
+ const result = await this.remote.execute(capletId, request);
85056
+ this.captureRemoteToolActivation(request, result, started);
85057
+ return result;
85058
+ } catch (error) {
85059
+ const result = errorResult(error);
85060
+ this.captureRemoteReliabilityError(request, result);
85061
+ this.captureRemoteToolActivation(request, result, started);
85062
+ throw error;
85063
+ }
85064
+ }
85065
+ captureRemoteToolActivation(request, result, started) {
85066
+ captureRuntimeTelemetryEvent(this.telemetry, "caplets_tool_activation", {
85067
+ command_family: "native",
85068
+ ...toolActivationProperties({
85069
+ config: this.telemetry.config,
85070
+ caplet: void 0,
85071
+ operation: operationFromNativeRequest(request),
85072
+ exposureMode: "direct",
85073
+ result,
85074
+ durationMs: Date.now() - started
85075
+ })
85076
+ }).catch(() => void 0);
85077
+ }
85078
+ captureRemoteReliabilityError(request, result) {
85079
+ captureRuntimeReliabilityEvent(this.telemetry, {
85080
+ command_family: "native",
85081
+ ...runtimeFailureTelemetryProperties({
85082
+ operation: operationFromNativeRequest(request),
85083
+ exposureMode: "direct",
85084
+ result
85085
+ })
85086
+ }).catch(() => void 0);
85087
+ }
83667
85088
  mergeTools() {
83668
85089
  const allLocalTools = this.local.listTools();
83669
85090
  const allRemoteTools = this.remote.listTools();
@@ -83772,7 +85193,7 @@ var CompositeNativeCapletsService = class {
83772
85193
  }
83773
85194
  startPresence() {
83774
85195
  this.presence?.start().catch((error) => {
83775
- writeErr(this.options, `Could not register Caplets Cloud Project Binding: ${errorMessage(error)}\n`);
85196
+ writeErr(this.options, `Could not start upstream Project Binding: ${errorMessage(error)}\n`);
83776
85197
  });
83777
85198
  }
83778
85199
  };
@@ -83856,27 +85277,173 @@ function remoteCodeModeCallableNativeTools(tools) {
83856
85277
  function remoteSuppressedCapletIds(allRemoteTools, remoteCodeModeTools = remoteCodeModeCallableNativeTools(allRemoteTools)) {
83857
85278
  return new Set([...allRemoteTools.filter((tool) => tool.codeModeRun !== true && (tool.shadowing ?? "forbid") === "forbid").map((tool) => tool.sourceCaplet ?? tool.caplet), ...remoteCodeModeTools.filter((tool) => (tool.shadowing ?? "forbid") === "forbid").map((tool) => tool.caplet)].filter((caplet) => caplet !== nativeCodeModeToolId));
83858
85279
  }
83859
- function createProjectBindingSessionManager(cloud, local, options) {
83860
- if (!cloud) return;
83861
- const projectRoot = cloud.projectRoot ?? findProjectRoot();
83862
- const cloudFetch = options.remote?.fetch;
83863
- return new ProjectBindingSessionManager({
83864
- client: new CapletsCloudClient({
83865
- baseUrl: cloud.url,
83866
- accessToken: cloud.accessToken,
83867
- ...cloudFetch ? { fetch: cloudFetch } : {}
83868
- }),
83869
- workspaceId: cloud.workspaceId,
83870
- projectRoot,
83871
- projectFingerprint: fingerprintProjectRoot(projectRoot),
83872
- projectFiles: projectSyncFiles(projectRoot),
83873
- allowedCapletIds: local.listTools().map((tool) => tool.caplet),
83874
- heartbeatIntervalMs: cloud.heartbeatIntervalMs,
83875
- onError: (error) => {
83876
- writeErr(options, `Caplets Cloud Project Binding heartbeat failed: ${errorMessage(error)}\n`);
83877
- }
85280
+ function createProjectBindingSessionManager(cloud, remoteOptions, local, options) {
85281
+ const allowedCapletIds = local.listTools().map((tool) => tool.caplet);
85282
+ if (cloud) {
85283
+ const projectRoot = cloud.projectRoot ?? findProjectRoot();
85284
+ const cloudFetch = options.remote?.fetch;
85285
+ return new ProjectBindingSessionManager({
85286
+ client: new CapletsCloudClient({
85287
+ baseUrl: cloud.url,
85288
+ accessToken: cloud.accessToken,
85289
+ ...cloudFetch ? { fetch: cloudFetch } : {}
85290
+ }),
85291
+ workspaceId: cloud.workspaceId,
85292
+ projectRoot,
85293
+ projectFingerprint: fingerprintProjectRoot(projectRoot),
85294
+ projectFiles: projectSyncFiles(projectRoot),
85295
+ allowedCapletIds,
85296
+ heartbeatIntervalMs: cloud.heartbeatIntervalMs,
85297
+ onError: (error) => {
85298
+ writeErr(options, `Caplets Cloud Project Binding heartbeat failed: ${errorMessage(error)}\n`);
85299
+ }
85300
+ });
85301
+ }
85302
+ if (!options.projectRoot || !isLoopbackRemote(remoteOptions)) return;
85303
+ return new RemoteProjectBindingSessionManager({
85304
+ attachUrl: remoteOptions.url,
85305
+ requestInit: remoteOptions.requestInit,
85306
+ fetch: remoteOptions.fetch,
85307
+ projectRoot: options.projectRoot,
85308
+ allowedCapletIds,
85309
+ heartbeatIntervalMs: 3e4,
85310
+ writeErr: options.writeErr
83878
85311
  });
83879
85312
  }
85313
+ var RemoteProjectBindingSessionManager = class {
85314
+ options;
85315
+ bindingId;
85316
+ sessionId;
85317
+ allowedCapletIds;
85318
+ heartbeatTimer;
85319
+ startPromise;
85320
+ unsupported = false;
85321
+ constructor(options) {
85322
+ this.options = options;
85323
+ this.allowedCapletIds = [...options.allowedCapletIds];
85324
+ }
85325
+ async start() {
85326
+ if (this.unsupported) return;
85327
+ if (!this.startPromise) {
85328
+ const start = this.register();
85329
+ this.startPromise = start;
85330
+ start.catch((error) => {
85331
+ if (isUnsupportedProjectBinding(error)) this.unsupported = true;
85332
+ if (this.startPromise === start) this.startPromise = void 0;
85333
+ });
85334
+ }
85335
+ return await this.startPromise;
85336
+ }
85337
+ async close() {
85338
+ await this.startPromise?.catch(() => void 0);
85339
+ this.stopHeartbeat();
85340
+ const bindingId = this.bindingId;
85341
+ this.bindingId = void 0;
85342
+ const sessionId = this.sessionId;
85343
+ this.sessionId = void 0;
85344
+ if (!bindingId || !sessionId) return;
85345
+ await this.fetchJson(projectBindingUrl(this.options.attachUrl, bindingId, "session"), {
85346
+ method: "DELETE",
85347
+ body: {
85348
+ sessionId,
85349
+ terminalReason: {
85350
+ code: "completed",
85351
+ message: "Binding Session completed."
85352
+ }
85353
+ }
85354
+ }).catch(() => void 0);
85355
+ }
85356
+ async updateAllowedCapletIds(allowedCapletIds) {
85357
+ this.allowedCapletIds = [...allowedCapletIds];
85358
+ await this.startPromise?.catch(() => void 0);
85359
+ if (!this.bindingId || !this.sessionId) return;
85360
+ await this.heartbeat().catch(() => void 0);
85361
+ }
85362
+ async register() {
85363
+ const projectFingerprint = fingerprintProjectRoot(this.options.projectRoot);
85364
+ const response = await this.fetchJson(projectBindingUrl(this.options.attachUrl, "sessions"), {
85365
+ method: "POST",
85366
+ body: {
85367
+ projectRoot: this.options.projectRoot,
85368
+ projectFingerprint,
85369
+ allowedCapletIds: this.allowedCapletIds
85370
+ }
85371
+ });
85372
+ if (!response.binding?.bindingId || !response.sessionId) throw new CapletsError("SERVER_UNAVAILABLE", "Project Binding session response was invalid.");
85373
+ this.bindingId = response.binding.bindingId;
85374
+ this.sessionId = response.sessionId;
85375
+ this.startHeartbeat();
85376
+ }
85377
+ startHeartbeat() {
85378
+ this.stopHeartbeat();
85379
+ this.heartbeatTimer = setInterval(() => {
85380
+ this.heartbeat().catch((error) => {
85381
+ this.disconnect();
85382
+ this.options.writeErr?.(`Remote Project Binding heartbeat failed: ${errorMessage(error)}\n`);
85383
+ });
85384
+ }, this.options.heartbeatIntervalMs);
85385
+ this.heartbeatTimer.unref?.();
85386
+ }
85387
+ stopHeartbeat() {
85388
+ if (!this.heartbeatTimer) return;
85389
+ clearInterval(this.heartbeatTimer);
85390
+ this.heartbeatTimer = void 0;
85391
+ }
85392
+ disconnect() {
85393
+ this.stopHeartbeat();
85394
+ this.bindingId = void 0;
85395
+ this.sessionId = void 0;
85396
+ this.startPromise = void 0;
85397
+ }
85398
+ async heartbeat() {
85399
+ if (!this.bindingId || !this.sessionId) return;
85400
+ await this.fetchJson(projectBindingUrl(this.options.attachUrl, this.bindingId, "heartbeat"), {
85401
+ method: "POST",
85402
+ body: {
85403
+ sessionId: this.sessionId,
85404
+ state: "ready",
85405
+ syncState: "idle",
85406
+ allowedCapletIds: this.allowedCapletIds
85407
+ }
85408
+ });
85409
+ }
85410
+ async fetchJson(url, input) {
85411
+ const headers = new Headers(this.options.requestInit.headers);
85412
+ headers.set("content-type", "application/json");
85413
+ const response = await (this.options.fetch ?? fetch)(url, {
85414
+ ...this.options.requestInit,
85415
+ method: input.method,
85416
+ headers,
85417
+ body: JSON.stringify(input.body)
85418
+ });
85419
+ if (!response.ok) {
85420
+ let payload;
85421
+ try {
85422
+ payload = await response.json();
85423
+ } catch {
85424
+ payload = void 0;
85425
+ }
85426
+ const error = isRecord(payload) && isRecord(payload.error) ? payload.error : void 0;
85427
+ if (error?.code === "UNSUPPORTED_CAPABILITY" && typeof error.message === "string") throw new CapletsError("UNSUPPORTED_CAPABILITY", error.message);
85428
+ throw new CapletsError("SERVER_UNAVAILABLE", `Project Binding request failed (${response.status}).`);
85429
+ }
85430
+ return await response.json().catch(() => ({}));
85431
+ }
85432
+ };
85433
+ function isUnsupportedProjectBinding(error) {
85434
+ return isRecord(error) && error.code === "UNSUPPORTED_CAPABILITY";
85435
+ }
85436
+ function projectBindingUrl(attachUrl, ...segments) {
85437
+ const url = new URL(attachUrl);
85438
+ url.pathname = [
85439
+ url.pathname.replace(/\/+$/u, ""),
85440
+ "project-bindings",
85441
+ ...segments.map(encodeURIComponent)
85442
+ ].join("/");
85443
+ url.search = "";
85444
+ url.hash = "";
85445
+ return url;
85446
+ }
83880
85447
  function createLocalOverlayConfigLoader(options) {
83881
85448
  let hasLoaded = false;
83882
85449
  let previousWarnings = /* @__PURE__ */ new Set();
@@ -83913,4 +85480,4 @@ function errorMessage(error) {
83913
85480
  return error instanceof Error ? error.message : String(error);
83914
85481
  }
83915
85482
  //#endregion
83916
- export { resolveExposure as $, mergeCapabilities as $t, nativeCapletPromptGuidance as A, isJSONRPCRequest as An, runOAuthFlow as At, CodeModeSessionManager as B, safeParse as Bn, defaultConfigBaseDir as Bt, resolveHostedCloudRemote as C, ReadResourceRequestSchema as Cn, validateCapletFile as Ct, isLoopbackHost as D, assertCompleteRequestResourceTemplate as Dn, markdownStructuredContent as Dt, controlUrlForBase as E, assertCompleteRequestPrompt as En, markdownCallToolResultContent as Et, nativeCodeModeToolName as F, getSchemaDescription as Fn, readTokenBundle as Ft, CodeModeJournalStore as G, resolveProjectCapletsRoot as Gt, diagnoseCodeModeTypeScript as H, __exportAll as Hn, defaultStateBaseDir as Ht, codeModeRunInputSchema as I, isSchemaOptional as In, DEFAULT_AUTH_DIR as It, codeModeDeclarationHash as J, serializeMessage as Jt, CodeModeLogStore as K, resolveProjectConfigPath as Kt, codeModeRunParamsSchema as L, isZ4Schema as Ln, DEFAULT_COMPLETION_CACHE_DIR as Lt, nativeCapletToolName as M, getLiteralValue as Mn, startOAuthFlow as Mt, nativeCapletsSystemGuidance as N, getObjectShape as Nn, deleteTokenBundle as Nt, parseServerBaseUrl as O, isInitializeRequest as On, refreshOAuthTokenBundle as Ot, nativeCodeModeToolId as P, getParseErrorMessage as Pn, isTokenBundleExpired as Pt, CapletsEngine as Q, Protocol as Qt, emptyCodeModeRunMeta as R, normalizeObjectSchema as Rn, DEFAULT_OBSERVED_OUTPUT_SHAPE_CACHE_DIR as Rt, resolveCapletsRemote as S, McpError as Sn, discoverCapletFiles as St, appendBasePath as T, SetLevelRequestSchema as Tn, hasRenderableStructuredContent as Tt, createCodeModeCapletsApi as U, resolveCapletsRoot as Ut, QuickJsCodeModeSandbox as V, safeParseAsync as Vn, defaultConfigPath as Vt, listCodeModeCallableCaplets as W, resolveConfigPath as Wt, generateCodeModeRunToolDescription as X, assertToolsCallTaskCapability as Xt, generateCodeModeDeclarations as Y, assertClientRequestTaskCapability as Yt, minifyCodeModeDeclarationText as Z, AjvJsonSchemaValidator as Zt, CapletsCloudClient as _, ListResourceTemplatesRequestSchema as _n, FileVaultStore as _t, CloudAuthStore as a, CreateMessageResultWithToolsSchema as an, ServerRegistry as at, isCapletsCloudUrl as b, ListToolsRequestSchema as bn, decryptVaultValue as bt, redactedCloudAuthStatus as c, ElicitResultSchema as cn, loadConfig as ct, projectBindingError as d, GetPromptRequestSchema as dn, loadLocalOverlayConfigWithSources as dt, toJsonSchemaCompat as en, decodeDirectResourceUri as et, projectBindingRecovery as f, InitializeRequestSchema as fn, loadProjectConfig as ft, buildProjectSyncManifest as g, ListPromptsRequestSchema as gn, vaultStoreForAuthDir as gt, createSdkRemoteCapletsClient as h, LATEST_PROTOCOL_VERSION as hn, vaultResolverForAuthDir as ht, createRemoteProfileStore as i, CreateMessageResultSchema as in, handleServerTool as it, nativeCapletToolDescription as j, isJSONRPCResultResponse as jn, startGenericOAuthFlow as jt, resolveCapletsServer as k, isJSONRPCErrorResponse as kn, runGenericOAuthFlow as kt, PROJECT_BINDING_ERROR_CODES as l, EmptyResultSchema as ln, loadConfigWithSources as lt, RemoteNativeCapletsService as m, JSONRPCMessageSchema as mn, vaultBootstrapResolver as mt, resolveRemoteSelection as n, CallToolResultSchema as nn, findProjectRoot as nt, cloudAuthPath as o, CreateTaskResultSchema as on, capabilityDescription as ot, CloudAuthClient as p, InitializedNotificationSchema as pn, parseConfig as pt, redactCodeModeLogText as q, ReadBuffer as qt, cloudCredentialsFromRemoteProfile as r, CompleteRequestSchema as rn, fingerprintProjectRoot as rt, migrateCredentials as s, DEFAULT_NEGOTIATED_PROTOCOL_VERSION as sn, GoogleDiscoveryManager as st, createNativeCapletsService as t, CallToolRequestSchema as tn, directResourceUriMatchesTemplate as tt, ProjectBindingError as u, ErrorCode as un, loadGlobalConfig as ut, resolveNativeCapletsServiceOptions as v, ListResourcesRequestSchema as vn, VAULT_MAX_VALUE_BYTES as vt, resolveRemoteMode as w, SUPPORTED_PROTOCOL_VERSIONS as wn, loadCapletFilesFromMap as wt, normalizeRemoteProfileHostUrl as x, LoggingLevelSchema as xn, encryptVaultValue as xt, hostedCloudWorkspaceFromRemoteUrl as y, ListRootsResultSchema as yn, validateVaultKeyName as yt, runCodeMode as z, objectFromShape as zn, defaultCacheBaseDir as zt };
85483
+ export { redactCodeModeLogText as $, getParseErrorMessage as $n, readTokenBundle as $t, resolveRemoteMode as A, GetPromptRequestSchema as An, loadProjectConfig as At, nativeCodeModeToolId as B, LoggingLevelSchema as Bn, discoverCapletFiles as Bt, CapletsCloudClient as C, CreateMessageResultSchema as Cn, ServerRegistry as Ct, normalizeRemoteProfileHostUrl as D, ElicitResultSchema as Dn, loadConfigWithSources as Dt, isCapletsCloudUrl as E, DEFAULT_NEGOTIATED_PROTOCOL_VERSION as En, loadConfig as Et, resolveCapletsServer as F, ListPromptsRequestSchema as Fn, FileVaultStore as Ft, runCodeMode as G, assertCompleteRequestPrompt as Gn, markdownStructuredContent as Gt, codeModeRunInputSchema as H, ReadResourceRequestSchema as Hn, loadCapletFilesFromMap as Ht, nativeCapletPromptGuidance as I, ListResourceTemplatesRequestSchema as In, VAULT_MAX_VALUE_BYTES as It, diagnoseCodeModeTypeScript as J, isJSONRPCErrorResponse as Jn, runOAuthFlow as Jt, CodeModeSessionManager as K, assertCompleteRequestResourceTemplate as Kn, refreshOAuthTokenBundle as Kt, nativeCapletToolDescription as L, ListResourcesRequestSchema as Ln, validateVaultKeyName as Lt, controlUrlForBase as M, InitializedNotificationSchema as Mn, vaultBootstrapResolver as Mt, isLoopbackHost as N, JSONRPCMessageSchema as Nn, vaultResolverForAuthDir as Nt, resolveCapletsRemote as O, EmptyResultSchema as On, loadGlobalConfig as Ot, parseServerBaseUrl as P, LATEST_PROTOCOL_VERSION as Pn, vaultStoreForAuthDir as Pt, CodeModeLogStore as Q, getObjectShape as Qn, isTokenBundleExpired as Qt, nativeCapletToolName as R, ListRootsResultSchema as Rn, decryptVaultValue as Rt, buildProjectSyncManifest as S, CompleteRequestSchema as Sn, handleServerTool as St, hostedCloudWorkspaceFromRemoteUrl as T, CreateTaskResultSchema as Tn, GoogleDiscoveryManager as Tt, codeModeRunParamsSchema as U, SUPPORTED_PROTOCOL_VERSIONS as Un, hasRenderableStructuredContent as Ut, nativeCodeModeToolName as V, McpError as Vn, validateCapletFile as Vt, emptyCodeModeRunMeta as W, SetLevelRequestSchema as Wn, markdownCallToolResultContent as Wt, listCodeModeCallableCaplets as X, isJSONRPCResultResponse as Xn, startOAuthFlow as Xt, createCodeModeCapletsApi as Y, isJSONRPCRequest as Yn, startGenericOAuthFlow as Yt, CodeModeJournalStore as Z, getLiteralValue as Zn, deleteTokenBundle as Zt, attachErrorResponse as _, Protocol as _n, rotateTelemetryIdentity as _t, CloudAuthStore as a, defaultConfigPath as an, safeParse as ar, version as at, invokeAttachExport$1 as b, CallToolRequestSchema as bn, findProjectRoot as bt, redactedCloudAuthStatus as c, resolveCapletsRoot as cn, buildProductTelemetryEvent as ct, projectBindingError as d, resolveProjectConfigPath as dn, maybePrintTelemetryNotice as dt, DEFAULT_AUTH_DIR as en, getSchemaDescription as er, codeModeDeclarationHash as et, projectBindingRecovery as f, ReadBuffer as fn, resolveTelemetryState as ft, CAPLETS_ATTACH_SESSION_HEADER as g, AjvJsonSchemaValidator as gn, readTelemetryNotice as gt, createSdkRemoteCapletsClient as h, assertToolsCallTaskCapability as hn, readTelemetryIdentity as ht, createRemoteProfileStore as i, defaultConfigBaseDir as in, objectFromShape as ir, CapletsEngine as it, appendBasePath as j, InitializeRequestSchema as jn, parseConfig as jt, resolveHostedCloudRemote as k, ErrorCode as kn, loadLocalOverlayConfigWithSources as kt, PROJECT_BINDING_ERROR_CODES as l, resolveConfigPath as ln, buildReliabilityTelemetryEvent as lt, RemoteNativeCapletsService as m, assertClientRequestTaskCapability as mn, readTelemetryDeliveryHealth as mt, resolveRemoteSelection as n, DEFAULT_OBSERVED_OUTPUT_SHAPE_CACHE_DIR as nn, isZ4Schema as nr, generateCodeModeRunToolDescription as nt, cloudAuthPath as o, defaultStateBaseDir as on, safeParseAsync as or, createTelemetryDispatcher as ot, CloudAuthClient as p, serializeMessage as pn, deleteTelemetryIdentity as pt, QuickJsCodeModeSandbox as q, isInitializeRequest as qn, runGenericOAuthFlow as qt, cloudCredentialsFromRemoteProfile as r, defaultCacheBaseDir as rn, normalizeObjectSchema as rr, minifyCodeModeDeclarationText as rt, migrateCredentials as s, defaultTelemetryStateDir as sn, TelemetryDebugSink as st, createNativeCapletsService as t, DEFAULT_COMPLETION_CACHE_DIR as tn, isSchemaOptional as tr, generateCodeModeDeclarations as tt, ProjectBindingError as u, resolveProjectCapletsRoot as un, durationBucket as ut, buildAttachProjection as v, mergeCapabilities as vn, resolveExposure as vt, resolveNativeCapletsServiceOptions as w, CreateMessageResultWithToolsSchema as wn, capabilityDescription as wt, invokeNativeAttachExport as x, CallToolResultSchema as xn, fingerprintProjectRoot as xt, buildNativeAttachProjection as y, toJsonSchemaCompat as yn, decodeDirectResourceUri as yt, nativeCapletsSystemGuidance as z, ListToolsRequestSchema as zn, encryptVaultValue as zt };