@caplets/core 0.26.1 → 0.27.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,4 +1,5 @@
1
1
  import type { CapletsEngine } from "../engine";
2
+ import type { CapletShadowingPolicy } from "../config";
2
3
  export type AttachExportKind = "caplet" | "tool" | "resource" | "resourceTemplate" | "prompt" | "completion";
3
4
  export type AttachInvokeRequest = {
4
5
  revision: string;
@@ -26,7 +27,7 @@ export type AttachManifestExport = {
26
27
  annotations?: unknown;
27
28
  schemaHash: string | null;
28
29
  capletId: string;
29
- shadowing: "forbid" | "allow";
30
+ shadowing: CapletShadowingPolicy;
30
31
  };
31
32
  export type AttachProgressiveCapletExport = AttachManifestExport & {
32
33
  kind: "caplet";
@@ -454,6 +454,7 @@ export declare const capletFileSchema: z.ZodObject<{
454
454
  shadowing: z.ZodOptional<z.ZodEnum<{
455
455
  forbid: "forbid";
456
456
  allow: "allow";
457
+ namespace: "namespace";
457
458
  }>>;
458
459
  }, z.core.$strict>;
459
460
  export declare function capletJsonSchema(): unknown;
@@ -10031,6 +10031,7 @@ function superRefine(fn, params) {
10031
10031
  //#endregion
10032
10032
  //#region src/config/validation.ts
10033
10033
  const SERVER_ID_PATTERN = /^[a-zA-Z0-9_-]{1,64}$/;
10034
+ const NAMESPACE_ALIAS_LABEL_PATTERN = /^[a-z](?:[a-z0-9-]{0,30}[a-z0-9])?$/;
10034
10035
  const HEADER_NAME_PATTERN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
10035
10036
  const HTTP_BASE_URL_PATTERN = /^(?![a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^/?#]*@)[^?#]*$/;
10036
10037
  const FORBIDDEN_HEADERS = /* @__PURE__ */ new Set([
@@ -10169,7 +10170,11 @@ const capletExposureSchema = _enum([
10169
10170
  "direct_and_code_mode",
10170
10171
  "progressive_and_code_mode"
10171
10172
  ]).describe("How this Caplet is exposed to agents.");
10172
- const capletShadowingSchema = _enum(["forbid", "allow"]).describe("Whether attached local Caplets may shadow this remote Caplet ID.");
10173
+ const capletShadowingSchema = _enum([
10174
+ "forbid",
10175
+ "allow",
10176
+ "namespace"
10177
+ ]).describe("Whether attached local Caplets may shadow this remote Caplet ID.");
10173
10178
  const capletEndpointAuthSchema = discriminatedUnion("type", [
10174
10179
  object({ type: literal("none") }).strict(),
10175
10180
  object({
@@ -10849,7 +10854,11 @@ const exposureSchema = _enum([
10849
10854
  "direct_and_code_mode",
10850
10855
  "progressive_and_code_mode"
10851
10856
  ]);
10852
- const shadowingSchema = _enum(["forbid", "allow"]).default("forbid");
10857
+ const shadowingSchema = _enum([
10858
+ "forbid",
10859
+ "allow",
10860
+ "namespace"
10861
+ ]).default("forbid");
10853
10862
  const commonSchema = {
10854
10863
  name: string().trim().min(1).max(80),
10855
10864
  description: string().refine((value) => value.trim().length >= 10, "description must contain at least 10 non-whitespace characters").refine((value) => value.length <= 1500, "description must be at most 1500 characters"),
@@ -10988,6 +10997,28 @@ const capletSetSchema = object({
10988
10997
  maxSearchLimit: number().int().positive().max(50).default(50),
10989
10998
  toolCacheTtlMs: number().int().nonnegative().default(3e4)
10990
10999
  }).strict();
11000
+ const namespaceAliasLabelSchema = string().regex(NAMESPACE_ALIAS_LABEL_PATTERN, "namespace alias labels must be lowercase DNS-style labels using letters, numbers, or hyphens");
11001
+ const namespaceAliasesSchema = object({
11002
+ local: namespaceAliasLabelSchema.optional(),
11003
+ upstreams: record(string().trim().min(1), namespaceAliasLabelSchema).default({})
11004
+ }).strict().default({ upstreams: {} }).superRefine((aliases, ctx) => {
11005
+ const seen = /* @__PURE__ */ new Map();
11006
+ const addAlias = (value, path) => {
11007
+ if (!value) return;
11008
+ const existing = seen.get(value);
11009
+ if (existing) {
11010
+ ctx.addIssue({
11011
+ code: "custom",
11012
+ path,
11013
+ message: `namespace alias '${value}' is already used at ${existing.join(".")}`
11014
+ });
11015
+ return;
11016
+ }
11017
+ seen.set(value, path);
11018
+ };
11019
+ addAlias(aliases.local, ["local"]);
11020
+ for (const [selector, alias] of Object.entries(aliases.upstreams)) addAlias(alias, ["upstreams", selector]);
11021
+ });
10991
11022
  const configSchema = object({
10992
11023
  version: literal(1).default(1),
10993
11024
  defaultSearchLimit: number().int().positive().default(20),
@@ -11018,7 +11049,8 @@ const configSchema = object({
11018
11049
  graphqlEndpoints: record(string().regex(SERVER_ID_PATTERN), graphQlEndpointSchema).default({}),
11019
11050
  httpApis: record(string().regex(SERVER_ID_PATTERN), httpApiSchema).default({}),
11020
11051
  cliTools: record(string().regex(SERVER_ID_PATTERN), cliToolsSchema).default({}),
11021
- capletSets: record(string().regex(SERVER_ID_PATTERN), capletSetSchema).default({})
11052
+ capletSets: record(string().regex(SERVER_ID_PATTERN), capletSetSchema).default({}),
11053
+ namespaceAliases: namespaceAliasesSchema
11022
11054
  }).strict().superRefine((config, ctx) => {
11023
11055
  if (config.defaultSearchLimit > config.maxSearchLimit) ctx.addIssue({
11024
11056
  code: "custom",
@@ -11041,6 +11073,10 @@ function parseConfig(input) {
11041
11073
  exposureDiscoveryConcurrency: config.options.exposureDiscoveryConcurrency,
11042
11074
  completion: config.completion
11043
11075
  },
11076
+ namespaceAliases: stripUndefined({
11077
+ local: config.namespaceAliases.local,
11078
+ upstreams: config.namespaceAliases.upstreams
11079
+ }),
11044
11080
  mcpServers: mapBackend(config.mcpServers, "mcp", (id, raw) => {
11045
11081
  const server = raw;
11046
11082
  return {
@@ -1,4 +1,4 @@
1
- import { type CapletConfig, type ConfigSource, type ConfigWithSources } from "../config";
1
+ import { type CapletConfig, type CapletShadowingPolicy, type ConfigSource, type ConfigWithSources } from "../config";
2
2
  import type { ServerStatus } from "../registry";
3
3
  type CapletListRow = {
4
4
  server: string;
@@ -10,6 +10,7 @@ type CapletListRow = {
10
10
  source: ConfigSource["kind"] | "remote" | "unknown";
11
11
  path: string | null;
12
12
  shadows: ConfigSource[];
13
+ shadowing?: CapletShadowingPolicy | undefined;
13
14
  };
14
15
  type ConfigPaths = {
15
16
  userConfig: string;
@@ -6,7 +6,7 @@ export type CodeModeCallableCaplet = {
6
6
  id: string;
7
7
  name: string;
8
8
  description: string;
9
- shadowing?: "forbid" | "allow";
9
+ shadowing?: "forbid" | "allow" | "namespace";
10
10
  useWhen?: string;
11
11
  avoidWhen?: string;
12
12
  };
@@ -1,5 +1,5 @@
1
- import { Hn as __exportAll, It as DEFAULT_AUTH_DIR, Kt as resolveProjectConfigPath, Lt as DEFAULT_COMPLETION_CACHE_DIR, Ut as resolveCapletsRoot, Wt as resolveConfigPath, lt as loadConfigWithSources } from "./service-aBIn4nrw.js";
2
- import { u as CapletsError } from "./validation-GD2x5HW1.js";
1
+ import { Hn as __exportAll, It as DEFAULT_AUTH_DIR, Kt as resolveProjectConfigPath, Lt as DEFAULT_COMPLETION_CACHE_DIR, Ut as resolveCapletsRoot, Wt as resolveConfigPath, lt as loadConfigWithSources } from "./service-BGGiZLHa.js";
2
+ import { d as CapletsError } from "./validation-CWzd2gtn.js";
3
3
  import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
4
4
  import { dirname, join } from "node:path";
5
5
  import { createHash } from "node:crypto";
@@ -165,7 +165,8 @@ function listCaplets(configWithSources, options) {
165
165
  status: initialServerStatus(server),
166
166
  source: sources[server.server]?.kind ?? "unknown",
167
167
  path: sources[server.server]?.path ?? null,
168
- shadows: shadows[server.server] ?? []
168
+ shadows: shadows[server.server] ?? [],
169
+ shadowing: server.shadowing
169
170
  })).sort((left, right) => left.server.localeCompare(right.server));
170
171
  }
171
172
  function initialServerStatus(server) {
@@ -1,4 +1,5 @@
1
1
  export declare const SERVER_ID_PATTERN: RegExp;
2
+ export declare const NAMESPACE_ALIAS_LABEL_PATTERN: RegExp;
2
3
  export declare const HEADER_NAME_PATTERN: RegExp;
3
4
  export declare const HTTP_BASE_URL_PATTERN: RegExp;
4
5
  export declare const FORBIDDEN_HEADERS: Set<string>;
@@ -10,6 +11,7 @@ type ValidationIssueSink = {
10
11
  }): void;
11
12
  };
12
13
  export declare function validateHttpActionHeaders(headers: Record<string, unknown>, ctx: ValidationIssueSink, path: Array<string>): void;
14
+ export declare function isValidNamespaceAliasLabel(value: string): boolean;
13
15
  export declare function isAllowedRemoteUrl(value: string): boolean;
14
16
  export declare function isAllowedHttpBaseUrl(value: string): boolean;
15
17
  export declare function isUrl(value: string): boolean;
@@ -49,7 +49,7 @@ export type AgentSelectionHintsConfig = {
49
49
  avoidWhen?: string | undefined;
50
50
  };
51
51
  export type CapletExposure = "direct" | "progressive" | "code_mode" | "direct_and_code_mode" | "progressive_and_code_mode";
52
- export type CapletShadowingPolicy = "forbid" | "allow";
52
+ export type CapletShadowingPolicy = "forbid" | "allow" | "namespace";
53
53
  export type CapletServerConfig = CommonCapletConfig & {
54
54
  backend: "mcp";
55
55
  transport: "stdio" | "http" | "sse";
@@ -157,6 +157,10 @@ export type CapletSetConfig = CommonCapletConfig & {
157
157
  toolCacheTtlMs: number;
158
158
  };
159
159
  export type CapletConfig = CapletServerConfig | OpenApiEndpointConfig | GoogleDiscoveryApiConfig | GraphQlEndpointConfig | HttpApiConfig | CliToolsConfig | CapletSetConfig;
160
+ export type NamespaceAliasesConfig = {
161
+ local?: string | undefined;
162
+ upstreams: Record<string, string>;
163
+ };
160
164
  export type CapletsConfig = {
161
165
  version: 1;
162
166
  options: {
@@ -172,6 +176,7 @@ export type CapletsConfig = {
172
176
  negativeCacheTtlMs: number;
173
177
  };
174
178
  };
179
+ namespaceAliases: NamespaceAliasesConfig;
175
180
  mcpServers: Record<string, CapletServerConfig>;
176
181
  openapiEndpoints: Record<string, OpenApiEndpointConfig>;
177
182
  googleDiscoveryApis: Record<string, GoogleDiscoveryApiConfig>;
@@ -1,5 +1,5 @@
1
1
  import { _ as record, b as unknown, d as literal, l as discriminatedUnion, m as object, o as array, p as number, r as _enum, s as boolean, v as string, y as union } from "./schemas-BoqMu4MG.js";
2
- import { a as isAllowedHttpBaseUrl, c as validateHttpActionHeaders, i as SERVER_ID_PATTERN, n as HEADER_NAME_PATTERN, o as isAllowedRemoteUrl, r as HTTP_BASE_URL_PATTERN, s as isUrl, t as FORBIDDEN_HEADERS, u as CapletsError } from "./validation-GD2x5HW1.js";
2
+ import { a as SERVER_ID_PATTERN, c as isUrl, d as CapletsError, i as NAMESPACE_ALIAS_LABEL_PATTERN, l as validateHttpActionHeaders, n as HEADER_NAME_PATTERN, o as isAllowedHttpBaseUrl, r as HTTP_BASE_URL_PATTERN, s as isAllowedRemoteUrl, t as FORBIDDEN_HEADERS } from "./validation-CWzd2gtn.js";
3
3
  //#region src/config-runtime.ts
4
4
  const stringMapSchema = record(string(), string());
5
5
  const authSchema = discriminatedUnion("type", [
@@ -49,7 +49,11 @@ const exposureSchema = _enum([
49
49
  "direct_and_code_mode",
50
50
  "progressive_and_code_mode"
51
51
  ]);
52
- const shadowingSchema = _enum(["forbid", "allow"]).default("forbid");
52
+ const shadowingSchema = _enum([
53
+ "forbid",
54
+ "allow",
55
+ "namespace"
56
+ ]).default("forbid");
53
57
  const commonSchema = {
54
58
  name: string().trim().min(1).max(80),
55
59
  description: string().refine((value) => value.trim().length >= 10, "description must contain at least 10 non-whitespace characters").refine((value) => value.length <= 1500, "description must be at most 1500 characters"),
@@ -188,6 +192,28 @@ const capletSetSchema = object({
188
192
  maxSearchLimit: number().int().positive().max(50).default(50),
189
193
  toolCacheTtlMs: number().int().nonnegative().default(3e4)
190
194
  }).strict();
195
+ const namespaceAliasLabelSchema = string().regex(NAMESPACE_ALIAS_LABEL_PATTERN, "namespace alias labels must be lowercase DNS-style labels using letters, numbers, or hyphens");
196
+ const namespaceAliasesSchema = object({
197
+ local: namespaceAliasLabelSchema.optional(),
198
+ upstreams: record(string().trim().min(1), namespaceAliasLabelSchema).default({})
199
+ }).strict().default({ upstreams: {} }).superRefine((aliases, ctx) => {
200
+ const seen = /* @__PURE__ */ new Map();
201
+ const addAlias = (value, path) => {
202
+ if (!value) return;
203
+ const existing = seen.get(value);
204
+ if (existing) {
205
+ ctx.addIssue({
206
+ code: "custom",
207
+ path,
208
+ message: `namespace alias '${value}' is already used at ${existing.join(".")}`
209
+ });
210
+ return;
211
+ }
212
+ seen.set(value, path);
213
+ };
214
+ addAlias(aliases.local, ["local"]);
215
+ for (const [selector, alias] of Object.entries(aliases.upstreams)) addAlias(alias, ["upstreams", selector]);
216
+ });
191
217
  const configSchema = object({
192
218
  version: literal(1).default(1),
193
219
  defaultSearchLimit: number().int().positive().default(20),
@@ -218,7 +244,8 @@ const configSchema = object({
218
244
  graphqlEndpoints: record(string().regex(SERVER_ID_PATTERN), graphQlEndpointSchema).default({}),
219
245
  httpApis: record(string().regex(SERVER_ID_PATTERN), httpApiSchema).default({}),
220
246
  cliTools: record(string().regex(SERVER_ID_PATTERN), cliToolsSchema).default({}),
221
- capletSets: record(string().regex(SERVER_ID_PATTERN), capletSetSchema).default({})
247
+ capletSets: record(string().regex(SERVER_ID_PATTERN), capletSetSchema).default({}),
248
+ namespaceAliases: namespaceAliasesSchema
222
249
  }).strict().superRefine((config, ctx) => {
223
250
  if (config.defaultSearchLimit > config.maxSearchLimit) ctx.addIssue({
224
251
  code: "custom",
@@ -241,6 +268,10 @@ function parseConfig(input) {
241
268
  exposureDiscoveryConcurrency: config.options.exposureDiscoveryConcurrency,
242
269
  completion: config.completion
243
270
  },
271
+ namespaceAliases: stripUndefined({
272
+ local: config.namespaceAliases.local,
273
+ upstreams: config.namespaceAliases.upstreams
274
+ }),
244
275
  mcpServers: mapBackend(config.mcpServers, "mcp", (id, raw) => {
245
276
  const server = raw;
246
277
  return {
package/dist/config.d.ts CHANGED
@@ -64,7 +64,7 @@ export type AgentSelectionHintsConfig = {
64
64
  useWhen?: string | undefined;
65
65
  avoidWhen?: string | undefined;
66
66
  };
67
- export type CapletShadowingPolicy = "forbid" | "allow";
67
+ export type CapletShadowingPolicy = "forbid" | "allow" | "namespace";
68
68
  export type CapletExposure = "direct" | "progressive" | "code_mode" | "direct_and_code_mode" | "progressive_and_code_mode";
69
69
  export type CapletServerConfig = AgentSelectionHintsConfig & {
70
70
  server: string;
@@ -261,6 +261,10 @@ export type CapletSetConfig = AgentSelectionHintsConfig & {
261
261
  runtime?: RuntimeRequirementsConfig | undefined;
262
262
  };
263
263
  export type CapletConfig = CapletServerConfig | OpenApiEndpointConfig | GoogleDiscoveryApiConfig | GraphQlEndpointConfig | HttpApiConfig | CliToolsConfig | CapletSetConfig;
264
+ export type NamespaceAliasesConfig = {
265
+ local?: string | undefined;
266
+ upstreams: Record<string, string>;
267
+ };
264
268
  export type CapletsOptions = {
265
269
  defaultSearchLimit: number;
266
270
  maxSearchLimit: number;
@@ -278,6 +282,7 @@ export type CompletionConfig = {
278
282
  export type CapletsConfig = {
279
283
  version: 1;
280
284
  options: CapletsOptions;
285
+ namespaceAliases: NamespaceAliasesConfig;
281
286
  mcpServers: Record<string, CapletServerConfig>;
282
287
  openapiEndpoints: Record<string, OpenApiEndpointConfig>;
283
288
  googleDiscoveryApis: Record<string, GoogleDiscoveryApiConfig>;
@@ -350,6 +355,10 @@ export declare const configFileSchema: z.ZodObject<{
350
355
  exposureDiscoveryTimeoutMs: z.ZodDefault<z.ZodNumber>;
351
356
  exposureDiscoveryConcurrency: z.ZodDefault<z.ZodNumber>;
352
357
  }, z.core.$strict>>;
358
+ namespaceAliases: z.ZodDefault<z.ZodObject<{
359
+ local: z.ZodOptional<z.ZodString>;
360
+ upstreams: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
361
+ }, z.core.$strict>>;
353
362
  mcpServers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>>;
354
363
  openapiEndpoints: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>>;
355
364
  googleDiscoveryApis: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>>;
package/dist/errors.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export declare const CAPLETS_ERROR_CODES: readonly ["CONFIG_NOT_FOUND", "CONFIG_EXISTS", "CONFIG_INVALID", "REQUEST_INVALID", "SERVER_NOT_FOUND", "SERVER_UNAVAILABLE", "SERVER_START_TIMEOUT", "UNKNOWN_OPERATION", "TOOL_NOT_FOUND", "TOOL_CALL_TIMEOUT", "AUTH_REQUIRED", "AUTH_FAILED", "REMOTE_CREDENTIALS_REVOKED", "AUTH_REFRESH_FAILED", "DOWNSTREAM_PROTOCOL_ERROR", "DOWNSTREAM_TOOL_ERROR", "UNSUPPORTED_OPERATION", "UNSUPPORTED_CAPABILITY", "PROMPT_NOT_FOUND", "DOWNSTREAM_RESOURCE_ERROR", "DOWNSTREAM_PROMPT_ERROR", "DOWNSTREAM_COMPLETION_ERROR", "ATTACH_MANIFEST_STALE", "ATTACH_EXPORT_NOT_FOUND", "UNSUPPORTED_TRANSPORT", "INTERNAL_ERROR"];
1
+ export declare const CAPLETS_ERROR_CODES: readonly ["CONFIG_NOT_FOUND", "CONFIG_EXISTS", "CONFIG_INVALID", "REQUEST_INVALID", "SERVER_NOT_FOUND", "SERVER_UNAVAILABLE", "SERVER_START_TIMEOUT", "UNKNOWN_OPERATION", "CAPLET_NAMESPACE_COLLISION", "TOOL_NOT_FOUND", "TOOL_CALL_TIMEOUT", "AUTH_REQUIRED", "AUTH_FAILED", "REMOTE_CREDENTIALS_REVOKED", "AUTH_REFRESH_FAILED", "DOWNSTREAM_PROTOCOL_ERROR", "DOWNSTREAM_TOOL_ERROR", "UNSUPPORTED_OPERATION", "UNSUPPORTED_CAPABILITY", "PROMPT_NOT_FOUND", "DOWNSTREAM_RESOURCE_ERROR", "DOWNSTREAM_PROMPT_ERROR", "DOWNSTREAM_COMPLETION_ERROR", "ATTACH_MANIFEST_STALE", "ATTACH_EXPORT_NOT_FOUND", "UNSUPPORTED_TRANSPORT", "INTERNAL_ERROR"];
2
2
  export type CapletsErrorCode = (typeof CAPLETS_ERROR_CODES)[number];
3
3
  export type SafeErrorSummary = {
4
4
  code: CapletsErrorCode;
@@ -0,0 +1,39 @@
1
+ import type { CapletShadowingPolicy } from "../config";
2
+ export type NamespaceSourceKind = "local" | "upstream";
3
+ export type NamespaceDiagnosticReason = "namespace_collision" | "missing_durable_source_identity" | "namespace_alias_invalid" | "generated_id_collision" | "unsupported_protocol";
4
+ export type NamespaceSourceEntry<Route = unknown> = {
5
+ baseId: string;
6
+ sourceKind: NamespaceSourceKind;
7
+ sourceLabel?: string | undefined;
8
+ namespaceAlias?: string | undefined;
9
+ durableSourceIdentity?: string | undefined;
10
+ shadowing: CapletShadowingPolicy;
11
+ route: Route;
12
+ };
13
+ export type NamespaceVisibleRecord<Route = unknown> = NamespaceSourceEntry<Route> & {
14
+ id: string;
15
+ label: string;
16
+ namespaced: boolean;
17
+ };
18
+ export type NamespaceDiagnostic = {
19
+ requestedId: string;
20
+ reason: NamespaceDiagnosticReason;
21
+ alternatives: string[];
22
+ sources: Array<{
23
+ sourceKind: NamespaceSourceKind;
24
+ label: string;
25
+ durableSourceIdentity?: string | undefined;
26
+ }>;
27
+ hint: string;
28
+ };
29
+ export type NamespaceResolution<Route = unknown> = {
30
+ visibleRecords: NamespaceVisibleRecord<Route>[];
31
+ routes: Map<string, Route>;
32
+ suppressedBareIds: Map<string, NamespaceDiagnostic>;
33
+ unavailableDiagnostics: NamespaceDiagnostic[];
34
+ };
35
+ export type NamespaceResolutionOptions = {
36
+ hashLength?: number | undefined;
37
+ maxHashLength?: number | undefined;
38
+ };
39
+ export declare function resolveNamespaceExposure<Route>(entries: NamespaceSourceEntry<Route>[], options?: NamespaceResolutionOptions): NamespaceResolution<Route>;
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
- import { $ as resolveExposure, $t as mergeCapabilities, A as nativeCapletPromptGuidance, An as isJSONRPCRequest, At as runOAuthFlow, B as CodeModeSessionManager, Bn as safeParse, Bt as defaultConfigBaseDir, C as resolveHostedCloudRemote, Cn as ReadResourceRequestSchema, Ct as validateCapletFile, D as isLoopbackHost, Dn as assertCompleteRequestResourceTemplate, Dt as markdownStructuredContent, E as controlUrlForBase, En as assertCompleteRequestPrompt, Et as markdownCallToolResultContent, Fn as getSchemaDescription, Ft as readTokenBundle, G as CodeModeJournalStore, Gt as resolveProjectCapletsRoot, H as diagnoseCodeModeTypeScript, Ht as defaultStateBaseDir, I as codeModeRunInputSchema, In as isSchemaOptional, It as DEFAULT_AUTH_DIR, J as codeModeDeclarationHash, Jt as serializeMessage, K as CodeModeLogStore, Kt as resolveProjectConfigPath, L as codeModeRunParamsSchema, Ln as isZ4Schema, M as nativeCapletToolName, Mn as getLiteralValue, Mt as startOAuthFlow, Nn as getObjectShape, Nt as deleteTokenBundle, O as parseServerBaseUrl, On as isInitializeRequest, Ot as refreshOAuthTokenBundle, Pn as getParseErrorMessage, Pt as isTokenBundleExpired, Q as CapletsEngine, Qt as Protocol, R as emptyCodeModeRunMeta, Rn as normalizeObjectSchema, Rt as DEFAULT_OBSERVED_OUTPUT_SHAPE_CACHE_DIR, S as resolveCapletsRemote, Sn as McpError, St as discoverCapletFiles, T as appendBasePath, Tn as SetLevelRequestSchema, Tt as hasRenderableStructuredContent, U as createCodeModeCapletsApi, Ut as resolveCapletsRoot, V as QuickJsCodeModeSandbox, Vn as safeParseAsync, Vt as defaultConfigPath, W as listCodeModeCallableCaplets, Wt as resolveConfigPath, X as generateCodeModeRunToolDescription, Xt as assertToolsCallTaskCapability, Y as generateCodeModeDeclarations, Yt as assertClientRequestTaskCapability, Z as minifyCodeModeDeclarationText, Zt as AjvJsonSchemaValidator, _ as CapletsCloudClient, _n as ListResourceTemplatesRequestSchema, _t as FileVaultStore, a as CloudAuthStore, an as CreateMessageResultWithToolsSchema, at as ServerRegistry, b as isCapletsCloudUrl, bn as ListToolsRequestSchema, bt as decryptVaultValue, c as redactedCloudAuthStatus, cn as ElicitResultSchema, ct as loadConfig, d as projectBindingError, dn as GetPromptRequestSchema, dt as loadLocalOverlayConfigWithSources, en as toJsonSchemaCompat, et as decodeDirectResourceUri, f as projectBindingRecovery, fn as InitializeRequestSchema, ft as loadProjectConfig, g as buildProjectSyncManifest, gn as ListPromptsRequestSchema, gt as vaultStoreForAuthDir, hn as LATEST_PROTOCOL_VERSION, ht as vaultResolverForAuthDir, i as createRemoteProfileStore, in as CreateMessageResultSchema, it as handleServerTool, j as nativeCapletToolDescription, jn as isJSONRPCResultResponse, jt as startGenericOAuthFlow, k as resolveCapletsServer, kn as isJSONRPCErrorResponse, kt as runGenericOAuthFlow, l as PROJECT_BINDING_ERROR_CODES, ln as EmptyResultSchema, lt as loadConfigWithSources, mn as JSONRPCMessageSchema, mt as vaultBootstrapResolver, n as resolveRemoteSelection, nn as CallToolResultSchema, nt as findProjectRoot, o as cloudAuthPath, on as CreateTaskResultSchema, ot as capabilityDescription, p as CloudAuthClient, pn as InitializedNotificationSchema, pt as parseConfig, q as redactCodeModeLogText, qt as ReadBuffer, r as cloudCredentialsFromRemoteProfile, rn as CompleteRequestSchema, rt as fingerprintProjectRoot, s as migrateCredentials, st as GoogleDiscoveryManager, t as createNativeCapletsService, tn as CallToolRequestSchema, tt as directResourceUriMatchesTemplate, u as ProjectBindingError, un as ErrorCode, ut as loadGlobalConfig, vn as ListResourcesRequestSchema, vt as VAULT_MAX_VALUE_BYTES, w as resolveRemoteMode, wn as SUPPORTED_PROTOCOL_VERSIONS, wt as loadCapletFilesFromMap, x as normalizeRemoteProfileHostUrl, xn as LoggingLevelSchema, xt as encryptVaultValue, y as hostedCloudWorkspaceFromRemoteUrl, yn as ListRootsResultSchema, yt as validateVaultKeyName, z as runCodeMode, zn as objectFromShape, zt as defaultCacheBaseDir } from "./service-aBIn4nrw.js";
1
+ import { $ as resolveExposure, $t as mergeCapabilities, A as nativeCapletPromptGuidance, An as isJSONRPCRequest, At as runOAuthFlow, B as CodeModeSessionManager, Bn as safeParse, Bt as defaultConfigBaseDir, C as resolveHostedCloudRemote, Cn as ReadResourceRequestSchema, Ct as validateCapletFile, D as isLoopbackHost, Dn as assertCompleteRequestResourceTemplate, Dt as markdownStructuredContent, E as controlUrlForBase, En as assertCompleteRequestPrompt, Et as markdownCallToolResultContent, Fn as getSchemaDescription, Ft as readTokenBundle, G as CodeModeJournalStore, Gt as resolveProjectCapletsRoot, H as diagnoseCodeModeTypeScript, Ht as defaultStateBaseDir, I as codeModeRunInputSchema, In as isSchemaOptional, It as DEFAULT_AUTH_DIR, J as codeModeDeclarationHash, Jt as serializeMessage, K as CodeModeLogStore, Kt as resolveProjectConfigPath, L as codeModeRunParamsSchema, Ln as isZ4Schema, M as nativeCapletToolName, Mn as getLiteralValue, Mt as startOAuthFlow, Nn as getObjectShape, Nt as deleteTokenBundle, O as parseServerBaseUrl, On as isInitializeRequest, Ot as refreshOAuthTokenBundle, Pn as getParseErrorMessage, Pt as isTokenBundleExpired, Q as CapletsEngine, Qt as Protocol, R as emptyCodeModeRunMeta, Rn as normalizeObjectSchema, Rt as DEFAULT_OBSERVED_OUTPUT_SHAPE_CACHE_DIR, S as resolveCapletsRemote, Sn as McpError, St as discoverCapletFiles, T as appendBasePath, Tn as SetLevelRequestSchema, Tt as hasRenderableStructuredContent, U as createCodeModeCapletsApi, Ut as resolveCapletsRoot, V as QuickJsCodeModeSandbox, Vn as safeParseAsync, Vt as defaultConfigPath, W as listCodeModeCallableCaplets, Wt as resolveConfigPath, X as generateCodeModeRunToolDescription, Xt as assertToolsCallTaskCapability, Y as generateCodeModeDeclarations, Yt as assertClientRequestTaskCapability, Z as minifyCodeModeDeclarationText, Zt as AjvJsonSchemaValidator, _ as CapletsCloudClient, _n as ListResourceTemplatesRequestSchema, _t as FileVaultStore, a as CloudAuthStore, an as CreateMessageResultWithToolsSchema, at as ServerRegistry, b as isCapletsCloudUrl, bn as ListToolsRequestSchema, bt as decryptVaultValue, c as redactedCloudAuthStatus, cn as ElicitResultSchema, ct as loadConfig, d as projectBindingError, dn as GetPromptRequestSchema, dt as loadLocalOverlayConfigWithSources, en as toJsonSchemaCompat, et as decodeDirectResourceUri, f as projectBindingRecovery, fn as InitializeRequestSchema, ft as loadProjectConfig, g as buildProjectSyncManifest, gn as ListPromptsRequestSchema, gt as vaultStoreForAuthDir, hn as LATEST_PROTOCOL_VERSION, ht as vaultResolverForAuthDir, i as createRemoteProfileStore, in as CreateMessageResultSchema, it as handleServerTool, j as nativeCapletToolDescription, jn as isJSONRPCResultResponse, jt as startGenericOAuthFlow, k as resolveCapletsServer, kn as isJSONRPCErrorResponse, kt as runGenericOAuthFlow, l as PROJECT_BINDING_ERROR_CODES, ln as EmptyResultSchema, lt as loadConfigWithSources, mn as JSONRPCMessageSchema, mt as vaultBootstrapResolver, n as resolveRemoteSelection, nn as CallToolResultSchema, nt as findProjectRoot, o as cloudAuthPath, on as CreateTaskResultSchema, ot as capabilityDescription, p as CloudAuthClient, pn as InitializedNotificationSchema, pt as parseConfig, q as redactCodeModeLogText, qt as ReadBuffer, r as cloudCredentialsFromRemoteProfile, rn as CompleteRequestSchema, rt as fingerprintProjectRoot, s as migrateCredentials, st as GoogleDiscoveryManager, t as createNativeCapletsService, tn as CallToolRequestSchema, tt as directResourceUriMatchesTemplate, u as ProjectBindingError, un as ErrorCode, ut as loadGlobalConfig, vn as ListResourcesRequestSchema, vt as VAULT_MAX_VALUE_BYTES, w as resolveRemoteMode, wn as SUPPORTED_PROTOCOL_VERSIONS, wt as loadCapletFilesFromMap, x as normalizeRemoteProfileHostUrl, xn as LoggingLevelSchema, xt as encryptVaultValue, y as hostedCloudWorkspaceFromRemoteUrl, yn as ListRootsResultSchema, yt as validateVaultKeyName, z as runCodeMode, zn as objectFromShape, zt as defaultCacheBaseDir } from "./service-BGGiZLHa.js";
2
2
  import { _ as record, b as unknown, d as literal, m as object, n as ZodOptional, o as array, p as number, r as _enum, s as boolean, v as string, x as url } from "./schemas-BoqMu4MG.js";
3
- import { f as redactSecrets$1, i as SERVER_ID_PATTERN, l as CAPLETS_ERROR_CODES, p as toSafeError, u as CapletsError } from "./validation-GD2x5HW1.js";
3
+ import { a as SERVER_ID_PATTERN, d as CapletsError, m as toSafeError, p as redactSecrets$1, u as CAPLETS_ERROR_CODES } from "./validation-CWzd2gtn.js";
4
4
  import { generatedToolInputJsonSchemaForCaplet, generatedToolInputSchema, generatedToolInputSchemaForCaplet } from "./generated-tool-input-schema.js";
5
5
  import { f as observedOutputShapeKey, g as stableJsonStringify, h as schemaHash, i as observeOutputShape, u as FileObservedOutputShapeStore } from "./observed-output-shapes-DuP7mJQf.js";
6
- import { a as formatCapletList, c as resolveCliConfigPaths, l as cliCommands$1, n as completionScript, o as formatConfigPaths, s as listCaplets, t as completeCliWords, u as completionShells } from "./completion-CFOJucl5.js";
6
+ import { a as formatCapletList, c as resolveCliConfigPaths, l as cliCommands$1, n as completionScript, o as formatConfigPaths, s as listCaplets, t as completeCliWords, u as completionShells } from "./completion-1wDjwHkC.js";
7
7
  import { n as normalizeCapletSourcePath, t as FilesystemCapletSource } from "./filesystem-Kkg32TOJ.js";
8
8
  import { parseConfig as parseConfig$1 } from "./config-runtime.js";
9
9
  import fs, { accessSync, chmodSync, closeSync, constants, copyFileSync, cpSync, existsSync, fstatSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, watch, writeFileSync, writeSync } from "node:fs";
@@ -1553,7 +1553,7 @@ const EMPTY_COMPLETION_RESULT = { completion: {
1553
1553
  } };
1554
1554
  //#endregion
1555
1555
  //#region package.json
1556
- var version = "0.26.1";
1556
+ var version = "0.27.0";
1557
1557
  //#endregion
1558
1558
  //#region src/serve/session.ts
1559
1559
  var CapletsMcpSession = class {
@@ -17487,8 +17487,17 @@ function mergeRemoteAndLocalRows(remoteRows, localOverlay, options) {
17487
17487
  });
17488
17488
  if (!localOverlay) return [...rows.values()].filter((row) => options.includeDisabled || !row.disabled).sort((left, right) => left.server.localeCompare(right.server));
17489
17489
  for (const row of listCaplets(localOverlay, { includeDisabled: true })) {
17490
- if (rows.get(row.server)) {
17490
+ const remote = rows.get(row.server);
17491
+ if (remote) {
17491
17492
  if (row.disabled) continue;
17493
+ if (remote.shadowing === "namespace") {
17494
+ options.writeErr(`Local Caplet '${row.server}' is exposed under a qualified ID because the remote Caplet uses namespace shadowing for that Caplet ID.\n`);
17495
+ continue;
17496
+ }
17497
+ if (remote.shadowing !== "allow") {
17498
+ options.writeErr(`Local Caplet '${row.server}' is suppressed because the remote Caplet forbids shadowing that Caplet ID.\n`);
17499
+ continue;
17500
+ }
17492
17501
  options.writeErr(`Warning: ${formatOverlaySource(row.source)} Caplet ${row.server} shadows remote Caplet\n`);
17493
17502
  }
17494
17503
  rows.set(row.server, row);
@@ -1,11 +1,12 @@
1
1
  import type { AttachCodeModeCaplet } from "../attach/api";
2
+ import type { CapletShadowingPolicy } from "../config";
2
3
  import type { ResolvedNativeCapletsServiceOptions } from "./options";
3
4
  import type { NativeCapletsService, NativeCapletsToolsChangedListener, NativeCapletTool } from "./service";
4
5
  export type RemoteCapletsTool = {
5
6
  name: string;
6
7
  capletId?: string | undefined;
7
8
  sourceCapletId?: string | undefined;
8
- shadowing?: "forbid" | "allow" | undefined;
9
+ shadowing?: CapletShadowingPolicy | undefined;
9
10
  title?: string | undefined;
10
11
  description?: string | undefined;
11
12
  inputSchema?: unknown;
@@ -2,7 +2,7 @@ import type { NativeCapletsServiceResolutionInput } from "./options";
2
2
  import { resolveNativeCapletsServiceOptions } from "./options";
3
3
  import { type RemoteCapletsClient } from "./remote";
4
4
  import type { CodeModeCallableCaplet } from "../code-mode/types";
5
- import { type CapletsConfig } from "../config";
5
+ import { type CapletShadowingPolicy, type CapletsConfig } from "../config";
6
6
  import { generatedToolInputJsonSchemaForCaplet } from "../generated-tool-input-schema";
7
7
  export type NativeCapletsServiceOptions = NativeCapletsServiceResolutionInput & {
8
8
  configPath?: string;
@@ -18,7 +18,7 @@ export type NativeCapletsServiceOptions = NativeCapletsServiceResolutionInput &
18
18
  export type NativeCapletTool = {
19
19
  caplet: string;
20
20
  sourceCaplet?: string;
21
- shadowing?: "forbid" | "allow";
21
+ shadowing?: CapletShadowingPolicy;
22
22
  toolName: string;
23
23
  title: string;
24
24
  description: string;
package/dist/native.js CHANGED
@@ -1,4 +1,4 @@
1
- import { A as nativeCapletPromptGuidance, F as nativeCodeModeToolName, M as nativeCapletToolName, N as nativeCapletsSystemGuidance, P as nativeCodeModeToolId, h as createSdkRemoteCapletsClient, j as nativeCapletToolDescription, m as RemoteNativeCapletsService, t as createNativeCapletsService, v as resolveNativeCapletsServiceOptions } from "./service-aBIn4nrw.js";
1
+ import { A as nativeCapletPromptGuidance, F as nativeCodeModeToolName, M as nativeCapletToolName, N as nativeCapletsSystemGuidance, P as nativeCodeModeToolId, h as createSdkRemoteCapletsClient, j as nativeCapletToolDescription, m as RemoteNativeCapletsService, t as createNativeCapletsService, v as resolveNativeCapletsServiceOptions } from "./service-BGGiZLHa.js";
2
2
  import { generatedToolInputJsonSchema, generatedToolInputSchema } from "./generated-tool-input-schema.js";
3
3
  //#region src/native/process-cleanup.ts
4
4
  function registerNativeCapletsProcessCleanup(service, options = {}) {
@@ -1,5 +1,5 @@
1
1
  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
- import { a as isAllowedHttpBaseUrl, c as validateHttpActionHeaders, d as errorResult, f as redactSecrets, i as SERVER_ID_PATTERN, n as HEADER_NAME_PATTERN, o as isAllowedRemoteUrl, p as toSafeError, r as HTTP_BASE_URL_PATTERN, s as isUrl, t as FORBIDDEN_HEADERS, u as CapletsError } from "./validation-GD2x5HW1.js";
2
+ 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
3
  import { generatedToolInputJsonSchema, generatedToolInputJsonSchemaForCaplet, generatedToolInputSchemaForCaplet, mcpOperations, operations } from "./generated-tool-input-schema.js";
4
4
  import { f as observedOutputShapeKey, i as observeOutputShape, r as normalizedObservableValue, t as usefulOutputSchema, u as FileObservedOutputShapeStore } from "./observed-output-shapes-DuP7mJQf.js";
5
5
  import { createRequire } from "node:module";
@@ -26005,7 +26005,11 @@ const capletExposureSchema = _enum([
26005
26005
  "direct_and_code_mode",
26006
26006
  "progressive_and_code_mode"
26007
26007
  ]).describe("How this Caplet is exposed to agents.");
26008
- const capletShadowingSchema = _enum(["forbid", "allow"]).describe("Whether attached local Caplets may shadow this remote Caplet ID.");
26008
+ const capletShadowingSchema = _enum([
26009
+ "forbid",
26010
+ "allow",
26011
+ "namespace"
26012
+ ]).describe("Whether attached local Caplets may shadow this remote Caplet ID.");
26009
26013
  const capletEndpointAuthSchema = discriminatedUnion("type", [
26010
26014
  object$1({ type: literal("none") }).strict(),
26011
26015
  object$1({
@@ -27288,7 +27292,33 @@ const exposureSchema = _enum([
27288
27292
  "direct_and_code_mode",
27289
27293
  "progressive_and_code_mode"
27290
27294
  ]).describe("How this Caplet is exposed to agents.");
27291
- const shadowingSchema = _enum(["forbid", "allow"]).default("forbid").describe("Whether attached local Caplets may shadow this remote Caplet ID.");
27295
+ const shadowingSchema = _enum([
27296
+ "forbid",
27297
+ "allow",
27298
+ "namespace"
27299
+ ]).default("forbid").describe("Whether attached local Caplets may shadow this remote Caplet ID.");
27300
+ const namespaceAliasLabelSchema = string().regex(NAMESPACE_ALIAS_LABEL_PATTERN, "namespace alias labels must be lowercase DNS-style labels using letters, numbers, or hyphens").describe("Namespace label used when qualifying colliding Caplet IDs.");
27301
+ const namespaceAliasesSchema = object$1({
27302
+ local: namespaceAliasLabelSchema.optional(),
27303
+ upstreams: record(string().trim().min(1), namespaceAliasLabelSchema).default({}).describe("Namespace aliases keyed by durable upstream source identity.")
27304
+ }).strict().default({ upstreams: {} }).superRefine((aliases, ctx) => {
27305
+ const seen = /* @__PURE__ */ new Map();
27306
+ const addAlias = (value, path) => {
27307
+ if (!value) return;
27308
+ const existing = seen.get(value);
27309
+ if (existing) {
27310
+ ctx.addIssue({
27311
+ code: "custom",
27312
+ path,
27313
+ message: `namespace alias '${value}' is already used at ${existing.join(".")}`
27314
+ });
27315
+ return;
27316
+ }
27317
+ seen.set(value, path);
27318
+ };
27319
+ addAlias(aliases.local, ["local"]);
27320
+ for (const [selector, alias] of Object.entries(aliases.upstreams)) addAlias(alias, ["upstreams", selector]);
27321
+ }).describe("Source-level namespace aliases for hash-qualified Caplet IDs.");
27292
27322
  const publicServerSchema = object$1({
27293
27323
  name: string().trim().min(1).max(80).describe("Human-readable server display name."),
27294
27324
  description: string().describe("Capability description shown to agents before downstream tools are disclosed.").refine((value) => value.trim().length >= 10, "description must contain at least 10 non-whitespace characters").refine((value) => value.length <= 1500, "description must be at most 1500 characters"),
@@ -27545,6 +27575,7 @@ function configSchemaFor(serverValueSchema, openApiEndpointValueSchema, googleDi
27545
27575
  exposureDiscoveryTimeoutMs: 15e3,
27546
27576
  exposureDiscoveryConcurrency: 4
27547
27577
  }).describe("Global Caplets runtime options."),
27578
+ namespaceAliases: namespaceAliasesSchema,
27548
27579
  mcpServers: record(string().regex(SERVER_ID_PATTERN), serverValueSchema).default({}).describe("Downstream MCP servers keyed by stable server ID."),
27549
27580
  openapiEndpoints: record(string().regex(SERVER_ID_PATTERN), openApiEndpointValueSchema).default({}).describe("OpenAPI endpoints keyed by stable Caplet ID."),
27550
27581
  googleDiscoveryApis: record(string().regex(SERVER_ID_PATTERN), googleDiscoveryApiValueSchema).default({}).describe("Google Discovery APIs keyed by stable Caplet ID."),
@@ -28321,6 +28352,7 @@ function mergeConfigInputs(...inputs) {
28321
28352
  merged = {
28322
28353
  ...merged,
28323
28354
  ...input,
28355
+ namespaceAliases: mergeNamespaceAliases(merged?.namespaceAliases, input.namespaceAliases),
28324
28356
  mcpServers: {
28325
28357
  ...merged?.mcpServers,
28326
28358
  ...input.mcpServers
@@ -28353,6 +28385,23 @@ function mergeConfigInputs(...inputs) {
28353
28385
  }
28354
28386
  return merged;
28355
28387
  }
28388
+ function mergeNamespaceAliases(left, right) {
28389
+ if (right !== void 0 && !isPlainObject$5(right)) return right;
28390
+ if (left !== void 0 && !isPlainObject$5(left)) return left;
28391
+ if (!isPlainObject$5(left) && !isPlainObject$5(right)) return;
28392
+ const leftRecord = isPlainObject$5(left) ? left : void 0;
28393
+ const rightRecord = isPlainObject$5(right) ? right : void 0;
28394
+ const leftUpstreams = isPlainObject$5(leftRecord?.upstreams) ? leftRecord.upstreams : void 0;
28395
+ const rightUpstreams = isPlainObject$5(rightRecord?.upstreams) ? rightRecord.upstreams : void 0;
28396
+ return stripUndefined({
28397
+ ...leftRecord,
28398
+ ...rightRecord,
28399
+ upstreams: {
28400
+ ...leftUpstreams,
28401
+ ...rightUpstreams
28402
+ }
28403
+ });
28404
+ }
28356
28405
  function mergeConfigInputsWithSources(...inputs) {
28357
28406
  let merged = {};
28358
28407
  const sources = {};
@@ -28477,6 +28526,10 @@ function parseConfig(input, options = {}) {
28477
28526
  exposureDiscoveryConcurrency: parsed.data.options.exposureDiscoveryConcurrency,
28478
28527
  completion: parsed.data.completion
28479
28528
  },
28529
+ namespaceAliases: stripUndefined({
28530
+ local: parsed.data.namespaceAliases.local,
28531
+ upstreams: parsed.data.namespaceAliases.upstreams
28532
+ }),
28480
28533
  mcpServers: servers,
28481
28534
  openapiEndpoints,
28482
28535
  googleDiscoveryApis,
@@ -64028,7 +64081,7 @@ var CapletsEngine = class {
64028
64081
  }
64029
64082
  }
64030
64083
  async completeCliWords(words) {
64031
- const { completeCliWords } = await import("./completion-CFOJucl5.js").then((n) => n.r);
64084
+ const { completeCliWords } = await import("./completion-1wDjwHkC.js").then((n) => n.r);
64032
64085
  return await completeCliWords(words, {
64033
64086
  config: this.registry.config,
64034
64087
  managers: {
@@ -81409,7 +81462,7 @@ function primitiveInvokeInput(capletId, operation, input) {
81409
81462
  function toolsFromManifest(manifest) {
81410
81463
  const codeModeCaplets = manifest.codeModeCaplets ?? [];
81411
81464
  const codeModeMarker = attachCodeModeMarker(codeModeCaplets);
81412
- const codeModeShadowing = codeModeCaplets.some((entry) => entry.shadowing === "forbid") ? "forbid" : "allow";
81465
+ const codeModeShadowing = codeModeCaplets.some((entry) => entry.shadowing === "forbid") ? "forbid" : codeModeCaplets.some((entry) => entry.shadowing === "namespace") ? "namespace" : "allow";
81413
81466
  return [
81414
81467
  ...manifest.caplets.map((entry) => ({
81415
81468
  name: entry.capletId,
@@ -81711,6 +81764,119 @@ function isPermanentRemoteCredentialsCode(code) {
81711
81764
  return code === "remote_credentials_required" || code === "remote_credentials_revoked" || code === "remote_auth_failed";
81712
81765
  }
81713
81766
  //#endregion
81767
+ //#region src/exposure/namespace.ts
81768
+ function resolveNamespaceExposure(entries, options = {}) {
81769
+ const hashLength = options.hashLength ?? 4;
81770
+ const maxHashLength = options.maxHashLength ?? 8;
81771
+ const groups = groupByBaseId(entries);
81772
+ const visibleRecords = [];
81773
+ const suppressedBareIds = /* @__PURE__ */ new Map();
81774
+ const unavailableDiagnostics = [];
81775
+ const reservedBareIds = new Set(groups.keys());
81776
+ for (const [baseId, group] of groups) {
81777
+ if (group.length === 1) {
81778
+ visibleRecords.push(visibleRecord(group[0], baseId, false));
81779
+ continue;
81780
+ }
81781
+ if (!isNamespaceCollisionGroup(group)) {
81782
+ visibleRecords.push(visibleRecord(nonNamespaceWinner(group), baseId, false));
81783
+ continue;
81784
+ }
81785
+ const diagnostics = validateNamespaceGroup(baseId, group);
81786
+ if (diagnostics.length > 0) {
81787
+ unavailableDiagnostics.push(...diagnostics);
81788
+ continue;
81789
+ }
81790
+ const resolved = qualifyNamespaceGroup(group, {
81791
+ baseId,
81792
+ hashLength,
81793
+ maxHashLength,
81794
+ reservedBareIds
81795
+ });
81796
+ if ("diagnostic" in resolved) {
81797
+ unavailableDiagnostics.push(resolved.diagnostic);
81798
+ continue;
81799
+ }
81800
+ visibleRecords.push(...resolved.records);
81801
+ suppressedBareIds.set(baseId, diagnostic(baseId, "namespace_collision", group, resolved.records.map((record) => record.id)));
81802
+ }
81803
+ return {
81804
+ visibleRecords,
81805
+ routes: new Map(visibleRecords.map((record) => [record.id, record.route])),
81806
+ suppressedBareIds,
81807
+ unavailableDiagnostics
81808
+ };
81809
+ }
81810
+ function groupByBaseId(entries) {
81811
+ const groups = /* @__PURE__ */ new Map();
81812
+ for (const entry of entries) groups.set(entry.baseId, [...groups.get(entry.baseId) ?? [], entry]);
81813
+ return groups;
81814
+ }
81815
+ function isNamespaceCollisionGroup(group) {
81816
+ const upstreams = group.filter((entry) => entry.sourceKind === "upstream");
81817
+ return upstreams.length > 0 && upstreams.every((entry) => entry.shadowing === "namespace");
81818
+ }
81819
+ function nonNamespaceWinner(group) {
81820
+ const forbiddingUpstream = group.find((entry) => entry.sourceKind === "upstream" && entry.shadowing === "forbid");
81821
+ if (forbiddingUpstream) return forbiddingUpstream;
81822
+ const local = group.find((entry) => entry.sourceKind === "local");
81823
+ if (local) return local;
81824
+ return group.find((entry) => entry.sourceKind === "upstream") ?? group[0];
81825
+ }
81826
+ function validateNamespaceGroup(baseId, group) {
81827
+ const diagnostics = [];
81828
+ for (const entry of group) {
81829
+ if (!entry.durableSourceIdentity) {
81830
+ diagnostics.push(diagnostic(baseId, "missing_durable_source_identity", group, []));
81831
+ break;
81832
+ }
81833
+ if (entry.namespaceAlias && !NAMESPACE_ALIAS_LABEL_PATTERN.test(entry.namespaceAlias)) {
81834
+ diagnostics.push(diagnostic(baseId, "namespace_alias_invalid", group, []));
81835
+ break;
81836
+ }
81837
+ }
81838
+ return diagnostics;
81839
+ }
81840
+ function qualifyNamespaceGroup(group, options) {
81841
+ const ids = group.map((entry) => qualifiedId(entry, options.baseId, options.hashLength));
81842
+ if (!(new Set(ids).size !== ids.length || ids.some((id) => options.reservedBareIds.has(id) || !SERVER_ID_PATTERN.test(id)))) return { records: group.map((entry, index) => visibleRecord(entry, ids[index], true)) };
81843
+ return { diagnostic: diagnostic(options.baseId, "generated_id_collision", group, []) };
81844
+ }
81845
+ function qualifiedId(entry, baseId, hashLength) {
81846
+ return `${namespaceLabel(entry)}-${createHash("sha256").update(entry.durableSourceIdentity ?? "").digest("hex").slice(0, hashLength)}__${baseId}`;
81847
+ }
81848
+ function visibleRecord(entry, id, namespaced) {
81849
+ return {
81850
+ ...entry,
81851
+ id,
81852
+ label: namespaceLabel(entry),
81853
+ namespaced
81854
+ };
81855
+ }
81856
+ function namespaceLabel(entry) {
81857
+ return entry.namespaceAlias ?? entry.sourceLabel ?? entry.sourceKind;
81858
+ }
81859
+ function diagnostic(requestedId, reason, group, alternatives) {
81860
+ return {
81861
+ requestedId,
81862
+ reason,
81863
+ alternatives,
81864
+ sources: group.map((entry) => ({
81865
+ sourceKind: entry.sourceKind,
81866
+ label: namespaceLabel(entry),
81867
+ ...entry.durableSourceIdentity ? { durableSourceIdentity: entry.durableSourceIdentity } : {}
81868
+ })),
81869
+ hint: diagnosticHint(requestedId, reason, alternatives)
81870
+ };
81871
+ }
81872
+ function diagnosticHint(requestedId, reason, alternatives) {
81873
+ if (reason === "namespace_collision") return `Caplet '${requestedId}' is unavailable because namespace shadowing exposes qualified alternatives: ${alternatives.join(", ")}.`;
81874
+ if (reason === "missing_durable_source_identity") return `Caplet '${requestedId}' could not be namespaced because at least one source has no durable identity.`;
81875
+ if (reason === "namespace_alias_invalid") return `Caplet '${requestedId}' could not be namespaced because an alias label is invalid.`;
81876
+ if (reason === "unsupported_protocol") return `Caplet '${requestedId}' could not be namespaced because a source protocol cannot represent namespace metadata.`;
81877
+ return `Caplet '${requestedId}' could not be namespaced because generated qualified IDs were not unique.`;
81878
+ }
81879
+ //#endregion
81714
81880
  //#region src/cloud-auth/errors.ts
81715
81881
  const SECRET_PATTERN = /(cap_access_[a-z0-9._~+/=-]+|cap_refresh_[a-z0-9._~+/=-]+|one_time_code_[a-z0-9._~+/=-]+|Bearer\s+)[^\s"]*/giu;
81716
81882
  function redactCloudAuthSecrets(value) {
@@ -83197,7 +83363,7 @@ function createLocalOverlayService(options) {
83197
83363
  }
83198
83364
  function createCompositeRemoteService(remoteOptions, local, options, authKind) {
83199
83365
  const { remote, presence } = createCompositeRemoteParts(remoteOptions, local, options, authKind);
83200
- return new CompositeNativeCapletsService(remote, local, options, presence);
83366
+ return new CompositeNativeCapletsService(remote, local, options, remoteOptions.url.toString(), presence);
83201
83367
  }
83202
83368
  function createCompositeRemoteParts(remoteOptions, local, options, authKind, resolveRuntimeRemoteOptions) {
83203
83369
  const remote = new RemoteNativeCapletsService({
@@ -83289,7 +83455,7 @@ var ProfileBackedNativeCapletsService = class {
83289
83455
  const signature = remoteOptionsSignature(remoteOptions);
83290
83456
  if (!this.delegate) {
83291
83457
  const { remote, presence } = createCompositeRemoteParts(remoteOptions, this.local, this.options, this.authKind, () => this.resolveProfileRemoteOptions());
83292
- this.delegate = new CompositeNativeCapletsService(remote, this.local, this.options, presence);
83458
+ this.delegate = new CompositeNativeCapletsService(remote, this.local, this.options, remoteOptions.url.toString(), presence);
83293
83459
  this.unsubscribeDelegate = this.delegate.onToolsChanged((tools) => this.emit(tools));
83294
83460
  this.remoteSignature = signature;
83295
83461
  this.credentialExpiresAt = remoteOptions.credentialExpiresAt;
@@ -83301,7 +83467,7 @@ var ProfileBackedNativeCapletsService = class {
83301
83467
  await Promise.all([remote.close(), presence?.close()]);
83302
83468
  return;
83303
83469
  }
83304
- await this.delegate.replaceRemote(remote, presence);
83470
+ await this.delegate.replaceRemote(remote, remoteOptions.url.toString(), presence);
83305
83471
  this.remoteSignature = signature;
83306
83472
  this.credentialExpiresAt = remoteOptions.credentialExpiresAt;
83307
83473
  } catch (error) {
@@ -83386,23 +83552,30 @@ var CompositeNativeCapletsService = class {
83386
83552
  remote;
83387
83553
  local;
83388
83554
  options;
83555
+ remoteIdentity;
83389
83556
  presence;
83390
83557
  listeners = /* @__PURE__ */ new Set();
83391
83558
  unsubscribeRemote;
83392
83559
  unsubscribeLocal;
83393
83560
  warnedShadowedLocalCaplets = /* @__PURE__ */ new Set();
83394
83561
  tools = [];
83562
+ routes = /* @__PURE__ */ new Map();
83563
+ namespaceDiagnostics = /* @__PURE__ */ new Map();
83395
83564
  closed = false;
83396
83565
  batchingReload = false;
83397
83566
  codeModeSessions = new CodeModeSessionManager();
83398
- constructor(remote, local, options, presence) {
83567
+ constructor(remote, local, options, remoteIdentity, presence) {
83399
83568
  this.remote = remote;
83400
83569
  this.local = local;
83401
83570
  this.options = options;
83571
+ this.remoteIdentity = remoteIdentity;
83402
83572
  this.presence = presence;
83403
83573
  this.unsubscribeRemote = this.remote.onToolsChanged(() => this.updateMergedTools());
83404
83574
  this.unsubscribeLocal = this.local.onToolsChanged(() => this.updateMergedTools());
83405
- this.tools = this.mergeTools();
83575
+ const merged = this.mergeTools();
83576
+ this.tools = merged.tools;
83577
+ this.routes = merged.routes;
83578
+ this.namespaceDiagnostics = merged.namespaceDiagnostics;
83406
83579
  this.startPresence();
83407
83580
  }
83408
83581
  listTools() {
@@ -83410,7 +83583,11 @@ var CompositeNativeCapletsService = class {
83410
83583
  }
83411
83584
  async execute(capletId, request) {
83412
83585
  if (capletId === "code_mode") return await executeCodeModeRunNative(this, request, this.codeModeSessions);
83413
- if (this.localCanExecute(capletId)) return await this.local.execute(capletId, request);
83586
+ const route = this.routes.get(capletId);
83587
+ 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);
83589
+ const diagnostic = this.namespaceDiagnostics.get(capletId);
83590
+ if (diagnostic) throw new CapletsError("CAPLET_NAMESPACE_COLLISION", diagnostic.hint, diagnostic);
83414
83591
  return await this.remote.execute(capletId, request);
83415
83592
  }
83416
83593
  codeModeService() {
@@ -83451,16 +83628,19 @@ var CompositeNativeCapletsService = class {
83451
83628
  this.presence?.close()
83452
83629
  ]);
83453
83630
  }
83454
- async replaceRemote(remote, presence) {
83631
+ async replaceRemote(remote, remoteIdentityOrPresence, presence) {
83632
+ const remoteIdentity = typeof remoteIdentityOrPresence === "string" ? remoteIdentityOrPresence : this.remoteIdentity;
83633
+ const nextPresence = typeof remoteIdentityOrPresence === "string" ? presence : remoteIdentityOrPresence;
83455
83634
  if (this.closed) {
83456
- await Promise.all([remote.close(), presence?.close()]);
83635
+ await Promise.all([remote.close(), nextPresence?.close()]);
83457
83636
  return;
83458
83637
  }
83459
83638
  const previousRemote = this.remote;
83460
83639
  const previousPresence = this.presence;
83461
83640
  this.unsubscribeRemote();
83462
83641
  this.remote = remote;
83463
- this.presence = presence;
83642
+ this.remoteIdentity = remoteIdentity;
83643
+ this.presence = nextPresence;
83464
83644
  this.unsubscribeRemote = this.remote.onToolsChanged(() => this.updateMergedTools());
83465
83645
  await Promise.all([previousRemote.close(), previousPresence?.close()]);
83466
83646
  if (this.closed) return;
@@ -83469,9 +83649,15 @@ var CompositeNativeCapletsService = class {
83469
83649
  }
83470
83650
  updateMergedTools() {
83471
83651
  if (this.closed || this.batchingReload) return;
83472
- const tools = this.mergeTools();
83473
- if (JSON.stringify(tools) === JSON.stringify(this.tools)) return;
83474
- this.tools = tools;
83652
+ const merged = this.mergeTools();
83653
+ if (JSON.stringify(merged.tools) === JSON.stringify(this.tools)) {
83654
+ this.routes = merged.routes;
83655
+ this.namespaceDiagnostics = merged.namespaceDiagnostics;
83656
+ return;
83657
+ }
83658
+ this.tools = merged.tools;
83659
+ this.routes = merged.routes;
83660
+ this.namespaceDiagnostics = merged.namespaceDiagnostics;
83475
83661
  for (const listener of this.listeners) try {
83476
83662
  listener(this.listTools());
83477
83663
  } catch (error) {
@@ -83483,16 +83669,89 @@ var CompositeNativeCapletsService = class {
83483
83669
  const allRemoteTools = this.remote.listTools();
83484
83670
  const remoteCodeModeTools = remoteCodeModeCallableNativeTools(allRemoteTools);
83485
83671
  const remoteIds = remoteSuppressedCapletIds(allRemoteTools, remoteCodeModeTools);
83486
- const localTools = allLocalTools.filter((tool) => tool.codeModeRun !== true && !remoteIds.has(tool.sourceCaplet ?? tool.caplet));
83672
+ const unsuppressedLocalTools = allLocalTools.filter((tool) => tool.codeModeRun !== true && !remoteIds.has(tool.sourceCaplet ?? tool.caplet));
83487
83673
  this.warnShadowedLocalCaplets(allLocalTools, remoteIds);
83488
- const localCodeModeTools = codeModeCallableNativeTools(allLocalTools, { fallbackToVisible: false }).filter((tool) => !remoteIds.has(tool.caplet));
83489
- const mergedTools = [...allRemoteTools.filter((tool) => tool.codeModeRun !== true), ...localTools];
83490
- const codeModeTools = [...remoteCodeModeTools, ...localCodeModeTools];
83491
- return [...mergedTools, ...codeModeTools.length > 0 ? [codeModeRunNativeTool(codeModeTools)] : []];
83674
+ const unsuppressedLocalCodeModeTools = codeModeCallableNativeTools(allLocalTools, { fallbackToVisible: false }).filter((tool) => !remoteIds.has(tool.caplet));
83675
+ const remoteTools = allRemoteTools.filter((tool) => tool.codeModeRun !== true);
83676
+ const resolved = this.resolveVisibleToolIds(remoteTools, unsuppressedLocalTools, remoteCodeModeTools, unsuppressedLocalCodeModeTools);
83677
+ const codeModeTools = [...resolved.remoteCodeModeTools, ...resolved.localCodeModeTools];
83678
+ return {
83679
+ tools: [
83680
+ ...resolved.remoteTools,
83681
+ ...resolved.localTools,
83682
+ ...codeModeTools.length > 0 ? [codeModeRunNativeTool(codeModeTools)] : []
83683
+ ],
83684
+ routes: resolved.routes,
83685
+ namespaceDiagnostics: resolved.namespaceDiagnostics
83686
+ };
83492
83687
  }
83493
- localCanExecute(capletId) {
83494
- const remoteIds = remoteSuppressedCapletIds(this.remote.listTools());
83495
- return localExecutionKeys(this.local.listTools(), capletId).some((key) => !remoteIds.has(key));
83688
+ resolveVisibleToolIds(remoteTools, localTools, remoteCodeModeTools, localCodeModeTools) {
83689
+ const resolution = resolveNamespaceExposure(nativeNamespaceEntries([{
83690
+ service: "remote",
83691
+ tools: [...remoteTools, ...remoteCodeModeTools]
83692
+ }, {
83693
+ service: "local",
83694
+ tools: [...localTools, ...localCodeModeTools]
83695
+ }], {
83696
+ ...nativeNamespaceContext(this.options),
83697
+ remoteIdentity: this.remoteIdentity
83698
+ }));
83699
+ const namespacedRecords = resolution.visibleRecords.filter((record) => record.namespaced);
83700
+ const namespacedBaseIds = new Set(namespacedRecords.map((record) => record.baseId));
83701
+ const diagnosticBaseIds = new Set(resolution.unavailableDiagnostics.map((diagnostic) => diagnostic.requestedId));
83702
+ const routes = /* @__PURE__ */ new Map();
83703
+ const namespaceDiagnostics = new Map(resolution.suppressedBareIds);
83704
+ for (const diagnostic of resolution.unavailableDiagnostics) namespaceDiagnostics.set(diagnostic.requestedId, diagnostic);
83705
+ const alternativesByBaseId = /* @__PURE__ */ new Map();
83706
+ const staleIdsByBaseId = /* @__PURE__ */ new Map();
83707
+ const setRoute = (visibleCapletId, route, overwrite) => {
83708
+ if (overwrite || !routes.has(visibleCapletId)) routes.set(visibleCapletId, route);
83709
+ };
83710
+ const rewrite = (service, tools, overwrite) => {
83711
+ const rewritten = [];
83712
+ for (const tool of tools) {
83713
+ const baseId = sourceBaseId(tool);
83714
+ if (diagnosticBaseIds.has(baseId)) continue;
83715
+ if (!namespacedBaseIds.has(baseId)) {
83716
+ rewritten.push(tool);
83717
+ setRoute(tool.caplet, {
83718
+ service,
83719
+ capletId: tool.caplet
83720
+ }, overwrite);
83721
+ continue;
83722
+ }
83723
+ const record = namespacedRecords.find((candidate) => candidate.baseId === baseId && candidate.route.service === service);
83724
+ if (!record) continue;
83725
+ const visibleTool = renameNativeTool(tool, record.id);
83726
+ rewritten.push(visibleTool);
83727
+ addMapSetValue(alternativesByBaseId, baseId, visibleTool.caplet);
83728
+ if (tool.caplet !== visibleTool.caplet) addMapSetValue(staleIdsByBaseId, baseId, tool.caplet);
83729
+ setRoute(visibleTool.caplet, {
83730
+ service,
83731
+ capletId: tool.caplet
83732
+ }, overwrite);
83733
+ }
83734
+ return rewritten;
83735
+ };
83736
+ const remoteVisibleTools = rewrite("remote", remoteTools, false);
83737
+ const localVisibleTools = rewrite("local", localTools, true);
83738
+ const remoteVisibleCodeModeTools = rewrite("remote", remoteCodeModeTools, false);
83739
+ const localVisibleCodeModeTools = rewrite("local", localCodeModeTools, true);
83740
+ for (const [baseId, alternatives] of alternativesByBaseId) {
83741
+ const baseDiagnostic = resolution.suppressedBareIds.get(baseId);
83742
+ if (!baseDiagnostic) continue;
83743
+ const alternativeList = [...alternatives];
83744
+ namespaceDiagnostics.set(baseId, namespaceDiagnosticWithAlternatives(baseId, baseDiagnostic, alternativeList));
83745
+ for (const staleId of staleIdsByBaseId.get(baseId) ?? []) namespaceDiagnostics.set(staleId, namespaceDiagnosticWithAlternatives(staleId, baseDiagnostic, alternativeList));
83746
+ }
83747
+ return {
83748
+ remoteTools: remoteVisibleTools,
83749
+ localTools: localVisibleTools,
83750
+ remoteCodeModeTools: remoteVisibleCodeModeTools,
83751
+ localCodeModeTools: localVisibleCodeModeTools,
83752
+ routes,
83753
+ namespaceDiagnostics
83754
+ };
83496
83755
  }
83497
83756
  warnShadowedLocalCaplets(localTools, remoteIds) {
83498
83757
  const localIds = /* @__PURE__ */ new Set([...localTools.filter((tool) => tool.codeModeRun !== true).map((tool) => tool.sourceCaplet ?? tool.caplet), ...codeModeCallableNativeTools(localTools, { fallbackToVisible: false }).map((tool) => tool.caplet)]);
@@ -83517,22 +83776,85 @@ var CompositeNativeCapletsService = class {
83517
83776
  });
83518
83777
  }
83519
83778
  };
83520
- function remoteCodeModeCallableNativeTools(tools) {
83521
- return codeModeCallableNativeTools(tools, { fallbackToVisible: true });
83779
+ function addMapSetValue(map, key, value) {
83780
+ let values = map.get(key);
83781
+ if (!values) {
83782
+ values = /* @__PURE__ */ new Set();
83783
+ map.set(key, values);
83784
+ }
83785
+ values.add(value);
83522
83786
  }
83523
- function remoteSuppressedCapletIds(allRemoteTools, remoteCodeModeTools = remoteCodeModeCallableNativeTools(allRemoteTools)) {
83524
- return new Set([...allRemoteTools.filter((tool) => tool.codeModeRun !== true && tool.shadowing !== "allow").map((tool) => tool.sourceCaplet ?? tool.caplet), ...remoteCodeModeTools.filter((tool) => tool.shadowing !== "allow").map((tool) => tool.caplet)].filter((caplet) => caplet !== nativeCodeModeToolId));
83787
+ function namespaceDiagnosticWithAlternatives(requestedId, diagnostic, alternatives) {
83788
+ return {
83789
+ ...diagnostic,
83790
+ requestedId,
83791
+ alternatives,
83792
+ hint: `Caplet '${requestedId}' is unavailable because namespace shadowing exposes qualified alternatives: ${alternatives.join(", ")}.`
83793
+ };
83525
83794
  }
83526
- function localExecutionKeys(tools, capletId) {
83527
- const keys = [];
83528
- for (const tool of tools) {
83529
- if (tool.codeModeRun) {
83530
- for (const caplet of tool.codeModeCaplets ?? []) if (caplet.id === capletId) keys.push(caplet.id);
83531
- continue;
83795
+ function nativeNamespaceContext(options) {
83796
+ const configPath = options.configPath ?? resolveConfigPath();
83797
+ const projectConfigPath = options.projectConfigPath ?? resolveProjectConfigPath();
83798
+ let namespaceAliases = parseConfig({}).namespaceAliases;
83799
+ try {
83800
+ namespaceAliases = loadLocalOverlayConfigWithSources(configPath, projectConfigPath).config.namespaceAliases;
83801
+ } catch {}
83802
+ return {
83803
+ localIdentity: `local:${configPath}\0${projectConfigPath}`,
83804
+ namespaceAliases
83805
+ };
83806
+ }
83807
+ function nativeNamespaceEntries(groups, identities) {
83808
+ const entries = /* @__PURE__ */ new Map();
83809
+ for (const group of groups) {
83810
+ const byBaseId = /* @__PURE__ */ new Map();
83811
+ for (const tool of group.tools) {
83812
+ const baseId = sourceBaseId(tool);
83813
+ byBaseId.set(baseId, [...byBaseId.get(baseId) ?? [], tool]);
83814
+ }
83815
+ for (const [baseId, tools] of byBaseId) {
83816
+ const service = group.service;
83817
+ entries.set(`${service}:${baseId}`, {
83818
+ baseId,
83819
+ sourceKind: service === "local" ? "local" : "upstream",
83820
+ sourceLabel: service === "local" ? "local" : "remote",
83821
+ namespaceAlias: service === "local" ? identities.namespaceAliases.local : identities.namespaceAliases.upstreams[identities.remoteIdentity],
83822
+ durableSourceIdentity: service === "local" ? identities.localIdentity : identities.remoteIdentity,
83823
+ shadowing: aggregateShadowing(tools),
83824
+ route: {
83825
+ service,
83826
+ baseId
83827
+ }
83828
+ });
83532
83829
  }
83533
- if (tool.caplet === capletId || tool.sourceCaplet === capletId) keys.push(tool.sourceCaplet ?? tool.caplet);
83534
83830
  }
83535
- return keys;
83831
+ return [...entries.values()];
83832
+ }
83833
+ function aggregateShadowing(tools) {
83834
+ if (tools.some((tool) => (tool.shadowing ?? "forbid") === "forbid")) return "forbid";
83835
+ if (tools.some((tool) => tool.shadowing === "namespace")) return "namespace";
83836
+ return "allow";
83837
+ }
83838
+ function sourceBaseId(tool) {
83839
+ return tool.sourceCaplet ?? tool.caplet;
83840
+ }
83841
+ function renameNativeTool(tool, visibleBaseId) {
83842
+ const baseId = sourceBaseId(tool);
83843
+ const visibleCapletId = tool.sourceCaplet && tool.caplet.startsWith(`${baseId}__`) ? `${visibleBaseId}${tool.caplet.slice(baseId.length)}` : visibleBaseId;
83844
+ const operationName = tool.sourceCaplet && tool.caplet.startsWith(`${baseId}__`) ? tool.caplet.slice(baseId.length + 2) : void 0;
83845
+ const toolName = operationName ? nativeDirectToolName(visibleBaseId, operationName) : nativeCapletToolName(visibleCapletId);
83846
+ return {
83847
+ ...tool,
83848
+ caplet: visibleCapletId,
83849
+ ...tool.sourceCaplet ? { sourceCaplet: visibleBaseId } : {},
83850
+ toolName
83851
+ };
83852
+ }
83853
+ function remoteCodeModeCallableNativeTools(tools) {
83854
+ return codeModeCallableNativeTools(tools, { fallbackToVisible: true });
83855
+ }
83856
+ function remoteSuppressedCapletIds(allRemoteTools, remoteCodeModeTools = remoteCodeModeCallableNativeTools(allRemoteTools)) {
83857
+ 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));
83536
83858
  }
83537
83859
  function createProjectBindingSessionManager(cloud, local, options) {
83538
83860
  if (!cloud) return;
@@ -31,6 +31,7 @@ const CAPLETS_ERROR_CODES = [
31
31
  "SERVER_UNAVAILABLE",
32
32
  "SERVER_START_TIMEOUT",
33
33
  "UNKNOWN_OPERATION",
34
+ "CAPLET_NAMESPACE_COLLISION",
34
35
  "TOOL_NOT_FOUND",
35
36
  "TOOL_CALL_TIMEOUT",
36
37
  "AUTH_REQUIRED",
@@ -112,6 +113,7 @@ function errorResult(error, fallback) {
112
113
  //#endregion
113
114
  //#region src/config/validation.ts
114
115
  const SERVER_ID_PATTERN = /^[a-zA-Z0-9_-]{1,64}$/;
116
+ const NAMESPACE_ALIAS_LABEL_PATTERN = /^[a-z](?:[a-z0-9-]{0,30}[a-z0-9])?$/;
115
117
  const HEADER_NAME_PATTERN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
116
118
  const HTTP_BASE_URL_PATTERN = /^(?![a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^/?#]*@)[^?#]*$/;
117
119
  const FORBIDDEN_HEADERS = /* @__PURE__ */ new Set([
@@ -174,4 +176,4 @@ function isUrl(value) {
174
176
  }
175
177
  }
176
178
  //#endregion
177
- export { isAllowedHttpBaseUrl as a, validateHttpActionHeaders as c, errorResult as d, redactSecrets as f, SERVER_ID_PATTERN as i, CAPLETS_ERROR_CODES as l, HEADER_NAME_PATTERN as n, isAllowedRemoteUrl as o, toSafeError as p, HTTP_BASE_URL_PATTERN as r, isUrl as s, FORBIDDEN_HEADERS as t, CapletsError as u };
179
+ export { SERVER_ID_PATTERN as a, isUrl as c, CapletsError as d, errorResult as f, NAMESPACE_ALIAS_LABEL_PATTERN as i, validateHttpActionHeaders as l, toSafeError as m, HEADER_NAME_PATTERN as n, isAllowedHttpBaseUrl as o, redactSecrets as p, HTTP_BASE_URL_PATTERN as r, isAllowedRemoteUrl as s, FORBIDDEN_HEADERS as t, CAPLETS_ERROR_CODES as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caplets/core",
3
- "version": "0.26.1",
3
+ "version": "0.27.0",
4
4
  "description": "Core runtime library for Caplets Code Mode and progressive disclosure gateways.",
5
5
  "keywords": [
6
6
  "caplets",