@ggui-ai/mcp-server-handlers 0.6.3 → 0.8.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.
Files changed (57) hide show
  1. package/dist/blueprints/index.d.ts +2 -2
  2. package/dist/blueprints/index.d.ts.map +1 -1
  3. package/dist/blueprints/index.js +1 -1
  4. package/dist/blueprints/search-blueprints.d.ts +64 -12
  5. package/dist/blueprints/search-blueprints.d.ts.map +1 -1
  6. package/dist/blueprints/search-blueprints.js +166 -16
  7. package/dist/ops-apps/delete-app.d.ts +26 -9
  8. package/dist/ops-apps/delete-app.d.ts.map +1 -1
  9. package/dist/ops-apps/delete-app.js +34 -11
  10. package/dist/ops-apps/index.d.ts +1 -1
  11. package/dist/ops-apps/index.d.ts.map +1 -1
  12. package/dist/ops-apps/index.js +1 -1
  13. package/dist/ops-apps/types.d.ts +32 -1
  14. package/dist/ops-apps/types.d.ts.map +1 -1
  15. package/dist/ops-apps/types.js +16 -0
  16. package/dist/ops-blueprint/generate.d.ts.map +1 -1
  17. package/dist/ops-blueprint/generate.js +6 -2
  18. package/dist/ops-blueprint/register.d.ts.map +1 -1
  19. package/dist/ops-blueprint/register.js +7 -2
  20. package/dist/ops-connector-keys/types.d.ts +7 -0
  21. package/dist/ops-connector-keys/types.d.ts.map +1 -1
  22. package/dist/renders/blueprint-durability.d.ts +111 -0
  23. package/dist/renders/blueprint-durability.d.ts.map +1 -0
  24. package/dist/renders/blueprint-durability.js +85 -0
  25. package/dist/renders/blueprint-registry.d.ts +24 -0
  26. package/dist/renders/blueprint-registry.d.ts.map +1 -1
  27. package/dist/renders/blueprint-registry.js +9 -0
  28. package/dist/renders/code-delivery-events.d.ts +92 -0
  29. package/dist/renders/code-delivery-events.d.ts.map +1 -0
  30. package/dist/renders/code-delivery-events.js +77 -0
  31. package/dist/renders/fetch-gadget-types.d.ts +7 -3
  32. package/dist/renders/fetch-gadget-types.d.ts.map +1 -1
  33. package/dist/renders/fetch-gadget-types.js +7 -3
  34. package/dist/renders/generation-cache.d.ts +9 -0
  35. package/dist/renders/generation-cache.d.ts.map +1 -1
  36. package/dist/renders/handshake.d.ts +5 -3
  37. package/dist/renders/handshake.d.ts.map +1 -1
  38. package/dist/renders/handshake.js +9 -4
  39. package/dist/renders/index.d.ts +4 -1
  40. package/dist/renders/index.d.ts.map +1 -1
  41. package/dist/renders/index.js +14 -1
  42. package/dist/renders/render-identity.d.ts +178 -0
  43. package/dist/renders/render-identity.d.ts.map +1 -0
  44. package/dist/renders/render-identity.js +136 -0
  45. package/dist/renders/render.d.ts +39 -5
  46. package/dist/renders/render.d.ts.map +1 -1
  47. package/dist/renders/render.js +224 -85
  48. package/dist/renders/slice-meta-derivation.d.ts +89 -6
  49. package/dist/renders/slice-meta-derivation.d.ts.map +1 -1
  50. package/dist/renders/slice-meta-derivation.js +118 -7
  51. package/dist/renders/sync-context.d.ts +13 -1
  52. package/dist/renders/sync-context.d.ts.map +1 -1
  53. package/dist/renders/sync-context.js +6 -1
  54. package/dist/renders/update.d.ts +15 -1
  55. package/dist/renders/update.d.ts.map +1 -1
  56. package/dist/renders/update.js +14 -5
  57. package/package.json +7 -7
@@ -23,8 +23,8 @@
23
23
  * on top when wrapping these into its tool-handler shape (a wider
24
24
  * `SharedHandler<ToolContext>` re-export).
25
25
  */
26
- export { createSearchBlueprintsHandler, MIN_SIMILARITY_SCORE, MANIFEST_EXACT_NAME_SCORE, MANIFEST_SUBSTRING_SCORE, } from './search-blueprints.js';
27
- export type { SearchBlueprintsDeps } from './search-blueprints.js';
26
+ export { createSearchBlueprintsHandler, DEFAULT_REGISTRY_SEARCH_TIMEOUT_MS, MIN_SIMILARITY_SCORE, MANIFEST_EXACT_NAME_SCORE, MANIFEST_SUBSTRING_SCORE, } from './search-blueprints.js';
27
+ export type { SearchBlueprintsDeps, SearchBlueprintsRegistrySource, } from './search-blueprints.js';
28
28
  export { createListFeaturedBlueprintsHandler } from './list-featured-blueprints.js';
29
29
  export type { ListFeaturedBlueprintsDeps, ListFeaturedBlueprintsOutput, } from './list-featured-blueprints.js';
30
30
  export { createRenderBlueprintHandler } from './render-blueprint.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/blueprints/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EACL,6BAA6B,EAC7B,oBAAoB,EACpB,yBAAyB,EACzB,wBAAwB,GACzB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,mCAAmC,EAAE,MAAM,+BAA+B,CAAC;AACpF,YAAY,EACV,0BAA0B,EAC1B,4BAA4B,GAC7B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AACrE,YAAY,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,8BAA8B,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAAE,oCAAoC,EAAE,MAAM,gCAAgC,CAAC;AACtF,OAAO,EAAE,oCAAoC,EAAE,MAAM,gCAAgC,CAAC;AACtF,OAAO,EAAE,oCAAoC,EAAE,MAAM,gCAAgC,CAAC;AACtF,OAAO,EAAE,uCAAuC,EAAE,MAAM,oCAAoC,CAAC;AAC7F,OAAO,EAAE,iCAAiC,EAAE,MAAM,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/blueprints/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EACL,6BAA6B,EAC7B,kCAAkC,EAClC,oBAAoB,EACpB,yBAAyB,EACzB,wBAAwB,GACzB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,oBAAoB,EACpB,8BAA8B,GAC/B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,mCAAmC,EAAE,MAAM,+BAA+B,CAAC;AACpF,YAAY,EACV,0BAA0B,EAC1B,4BAA4B,GAC7B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AACrE,YAAY,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,8BAA8B,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAAE,oCAAoC,EAAE,MAAM,gCAAgC,CAAC;AACtF,OAAO,EAAE,oCAAoC,EAAE,MAAM,gCAAgC,CAAC;AACtF,OAAO,EAAE,oCAAoC,EAAE,MAAM,gCAAgC,CAAC;AACtF,OAAO,EAAE,uCAAuC,EAAE,MAAM,oCAAoC,CAAC;AAC7F,OAAO,EAAE,iCAAiC,EAAE,MAAM,6BAA6B,CAAC"}
@@ -23,7 +23,7 @@
23
23
  * on top when wrapping these into its tool-handler shape (a wider
24
24
  * `SharedHandler<ToolContext>` re-export).
25
25
  */
26
- export { createSearchBlueprintsHandler, MIN_SIMILARITY_SCORE, MANIFEST_EXACT_NAME_SCORE, MANIFEST_SUBSTRING_SCORE, } from './search-blueprints.js';
26
+ export { createSearchBlueprintsHandler, DEFAULT_REGISTRY_SEARCH_TIMEOUT_MS, MIN_SIMILARITY_SCORE, MANIFEST_EXACT_NAME_SCORE, MANIFEST_SUBSTRING_SCORE, } from './search-blueprints.js';
27
27
  export { createListFeaturedBlueprintsHandler } from './list-featured-blueprints.js';
28
28
  export { createRenderBlueprintHandler } from './render-blueprint.js';
29
29
  export { createValidateBlueprintHandler } from './validate-blueprint.js';
@@ -2,8 +2,8 @@
2
2
  * ggui_search_blueprints — search across every discoverable blueprint
3
3
  * on this server.
4
4
  *
5
- * Two sources are consulted in parallel, then merged + de-duplicated
6
- * by id:
5
+ * Up to three sources are consulted in parallel, then merged +
6
+ * de-duplicated by id:
7
7
  *
8
8
  * 1. **Manifest source** (optional `BlueprintProvider`) — authored
9
9
  * UIs declared in `ggui.json#blueprints.include`. Matched by
@@ -19,6 +19,11 @@
19
19
  * producer that has written into the scope. Continues to honor
20
20
  * `MIN_SIMILARITY_SCORE`.
21
21
  *
22
+ * 3. **Registry source** (optional, opt-in via deps.registry) —
23
+ * bounded public /search call for published blueprint
24
+ * candidates; appended after local hits, degrades typed on
25
+ * failure.
26
+ *
22
27
  * When both sources return the same id (a manifest blueprint that
23
28
  * also has a cached generation), the manifest entry wins — its
24
29
  * metadata is the source of truth and its name/description don't
@@ -26,8 +31,12 @@
26
31
  * max of the two so a lexical OR semantic hit keeps the entry in
27
32
  * the top band.
28
33
  *
29
- * The merge stays under the `limit` request by taking the top-N
30
- * after sort. `total` reflects pre-trim matches.
34
+ * The local merge (manifest + semantic) is trimmed to the top-N
35
+ * after sort, so it stays within `limit`. Registry candidates are
36
+ * bounded by the same `limit` on their own request and append AFTER
37
+ * that trim rather than sharing its budget, so the final `results`
38
+ * count can reach up to 2×`limit`. `total` reflects the pre-trim
39
+ * local match count plus the appended registry count.
31
40
  *
32
41
  * ## Why merge vs. a dedicated tool
33
42
  *
@@ -38,8 +47,8 @@
38
47
  * the split the server already knows how to make.
39
48
  *
40
49
  * Pure over `@ggui-ai/mcp-server-core`'s seams. No AWS imports. No
41
- * config loading. The hosted server's logger wrapper decorates this
42
- * when composing.
50
+ * config loading. A composing host may wrap this with its own
51
+ * logging when assembling the server.
43
52
  */
44
53
  import { z } from 'zod';
45
54
  import type { BlueprintProvider, EmbeddingProvider, VectorStore } from '@ggui-ai/mcp-server-core';
@@ -65,6 +74,30 @@ export declare const MANIFEST_EXACT_NAME_SCORE = 1;
65
74
  * stronger signal.
66
75
  */
67
76
  export declare const MANIFEST_SUBSTRING_SCORE = 0.7;
77
+ /**
78
+ * Default budget for the registry `/search` round-trip. The registry
79
+ * source is advisory — a slow registry must never stall the local
80
+ * sources past this bound. Operator-overridable per-deps.
81
+ */
82
+ export declare const DEFAULT_REGISTRY_SEARCH_TIMEOUT_MS = 3000;
83
+ /**
84
+ * Registry-backed discovery source configuration. Presence of this
85
+ * object on `SearchBlueprintsDeps` ACTIVATES the source (opt-in —
86
+ * a zero-config server never makes an outbound registry request).
87
+ */
88
+ export interface SearchBlueprintsRegistrySource {
89
+ /**
90
+ * Registry host (`host[:port]`). Absent = `DEFAULT_BUNDLE_HOST`.
91
+ * Scheme derives via `bundleHostScheme` — http for loopback,
92
+ * https otherwise (the same resolution the gadget bundleHost
93
+ * uses).
94
+ */
95
+ readonly host?: string;
96
+ /** Fetch budget in ms. Default `DEFAULT_REGISTRY_SEARCH_TIMEOUT_MS`. */
97
+ readonly timeoutMs?: number;
98
+ /** Injectable fetch for tests. Defaults to the global fetch. */
99
+ readonly fetch?: typeof fetch;
100
+ }
68
101
  export interface SearchBlueprintsDeps {
69
102
  readonly embedding: EmbeddingProvider;
70
103
  readonly vectors: VectorStore;
@@ -74,29 +107,48 @@ export interface SearchBlueprintsDeps {
74
107
  * included in the search results alongside the semantic
75
108
  * `VectorStore` matches.
76
109
  *
77
- * Omitted = semantic-only behavior (the pre-merge default). The
78
- * hosted server historically ran without a manifest provider on
79
- * this handler; OSS `createGguiServer` constructs a
80
- * `ManifestBlueprintProvider` at boot and threads it through.
110
+ * Omitted = semantic-only behavior (the pre-merge default) — a
111
+ * composing host can run without a manifest provider bound to this
112
+ * handler. This package's own `createGguiServer` constructs a
113
+ * `ManifestBlueprintProvider` at boot and threads it through by
114
+ * default.
81
115
  */
82
116
  readonly blueprints?: BlueprintProvider;
117
+ /**
118
+ * Optional registry discovery source. When bound, the handler
119
+ * queries the registry's public `/search` for published blueprint
120
+ * candidates and appends them after the local sources. Omitted =
121
+ * no outbound registry traffic (the zero-config default).
122
+ */
123
+ readonly registry?: SearchBlueprintsRegistrySource;
83
124
  }
84
125
  declare const inputSchema: {
85
126
  readonly query: z.ZodString;
86
127
  readonly limit: z.ZodOptional<z.ZodNumber>;
128
+ readonly tool: z.ZodOptional<z.ZodString>;
129
+ readonly server: z.ZodOptional<z.ZodString>;
87
130
  };
88
131
  declare const outputSchema: {
89
132
  results: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
90
133
  total: z.ZodNumber;
91
134
  query: z.ZodString;
135
+ degradedSources: z.ZodOptional<z.ZodArray<z.ZodObject<{
136
+ source: z.ZodLiteral<"registry">;
137
+ reason: z.ZodEnum<{
138
+ unreachable: "unreachable";
139
+ timeout: "timeout";
140
+ invalid_response: "invalid_response";
141
+ }>;
142
+ }, z.core.$strip>>>;
92
143
  };
93
144
  /**
94
145
  * Build a search-blueprints handler bound to concrete `embedding` +
95
146
  * `vectors` implementations (required) + an optional manifest
96
147
  * `BlueprintProvider`. Tests inject in-memory fakes from
97
148
  * `@ggui-ai/mcp-server-core/in-memory`; production hosts bind to
98
- * AWS Bedrock + S3 Vectors for the semantic source and a
99
- * `ManifestBlueprintProvider` for the manifest source.
149
+ * their own operator-configured embedding + vector-store providers
150
+ * for the semantic source and a `ManifestBlueprintProvider` for the
151
+ * manifest source.
100
152
  */
101
153
  export declare function createSearchBlueprintsHandler(deps: SearchBlueprintsDeps): SharedHandler<typeof inputSchema, typeof outputSchema, GguiSearchBlueprintsOutput>;
102
154
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"search-blueprints.d.ts","sourceRoot":"","sources":["../../src/blueprints/search-blueprints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAEV,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAGL,KAAK,0BAA0B,EAChC,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAkB,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,IAAM,CAAC;AAE7C;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,iBAAiB,CAAC;CACzC;AAID,QAAA,MAAM,WAAW;;;CAA6B,CAAC;AAE/C,QAAA,MAAM,YAAY;;;;CAUjB,CAAC;AAKF;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,oBAAoB,GACzB,aAAa,CAAC,OAAO,WAAW,EAAE,OAAO,YAAY,EAAE,0BAA0B,CAAC,CA4CpF"}
1
+ {"version":3,"file":"search-blueprints.d.ts","sourceRoot":"","sources":["../../src/blueprints/search-blueprints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAEV,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAKL,KAAK,0BAA0B,EAChC,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAkB,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,IAAM,CAAC;AAE7C;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C;;;;GAIG;AACH,eAAO,MAAM,kCAAkC,OAAO,CAAC;AAEvD;;;;GAIG;AACH,MAAM,WAAW,8BAA8B;IAC7C;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,wEAAwE;IACxE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,gEAAgE;IAChE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CAC/B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,iBAAiB,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,8BAA8B,CAAC;CACpD;AAID,QAAA,MAAM,WAAW;;;;;CAA6B,CAAC;AAE/C,QAAA,MAAM,YAAY;;;;;;;;;;;;CAkBjB,CAAC;AAKF;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,oBAAoB,GACzB,aAAa,CAAC,OAAO,WAAW,EAAE,OAAO,YAAY,EAAE,0BAA0B,CAAC,CAyDpF"}
@@ -2,8 +2,8 @@
2
2
  * ggui_search_blueprints — search across every discoverable blueprint
3
3
  * on this server.
4
4
  *
5
- * Two sources are consulted in parallel, then merged + de-duplicated
6
- * by id:
5
+ * Up to three sources are consulted in parallel, then merged +
6
+ * de-duplicated by id:
7
7
  *
8
8
  * 1. **Manifest source** (optional `BlueprintProvider`) — authored
9
9
  * UIs declared in `ggui.json#blueprints.include`. Matched by
@@ -19,6 +19,11 @@
19
19
  * producer that has written into the scope. Continues to honor
20
20
  * `MIN_SIMILARITY_SCORE`.
21
21
  *
22
+ * 3. **Registry source** (optional, opt-in via deps.registry) —
23
+ * bounded public /search call for published blueprint
24
+ * candidates; appended after local hits, degrades typed on
25
+ * failure.
26
+ *
22
27
  * When both sources return the same id (a manifest blueprint that
23
28
  * also has a cached generation), the manifest entry wins — its
24
29
  * metadata is the source of truth and its name/description don't
@@ -26,8 +31,12 @@
26
31
  * max of the two so a lexical OR semantic hit keeps the entry in
27
32
  * the top band.
28
33
  *
29
- * The merge stays under the `limit` request by taking the top-N
30
- * after sort. `total` reflects pre-trim matches.
34
+ * The local merge (manifest + semantic) is trimmed to the top-N
35
+ * after sort, so it stays within `limit`. Registry candidates are
36
+ * bounded by the same `limit` on their own request and append AFTER
37
+ * that trim rather than sharing its budget, so the final `results`
38
+ * count can reach up to 2×`limit`. `total` reflects the pre-trim
39
+ * local match count plus the appended registry count.
31
40
  *
32
41
  * ## Why merge vs. a dedicated tool
33
42
  *
@@ -38,11 +47,11 @@
38
47
  * the split the server already knows how to make.
39
48
  *
40
49
  * Pure over `@ggui-ai/mcp-server-core`'s seams. No AWS imports. No
41
- * config loading. The hosted server's logger wrapper decorates this
42
- * when composing.
50
+ * config loading. A composing host may wrap this with its own
51
+ * logging when assembling the server.
43
52
  */
44
53
  import { z } from 'zod';
45
- import { flatToBlueprintSource, searchBlueprintsInputShape, } from '@ggui-ai/protocol';
54
+ import { bundleHostScheme, DEFAULT_BUNDLE_HOST, flatToBlueprintSource, searchBlueprintsInputShape, } from '@ggui-ai/protocol';
46
55
  /**
47
56
  * Matches the hosted `MIN_SIMILARITY_SCORE`. Below this is noise —
48
57
  * callers that want stricter matching post-filter by `score`.
@@ -63,6 +72,12 @@ export const MANIFEST_EXACT_NAME_SCORE = 1.0;
63
72
  * stronger signal.
64
73
  */
65
74
  export const MANIFEST_SUBSTRING_SCORE = 0.7;
75
+ /**
76
+ * Default budget for the registry `/search` round-trip. The registry
77
+ * source is advisory — a slow registry must never stall the local
78
+ * sources past this bound. Operator-overridable per-deps.
79
+ */
80
+ export const DEFAULT_REGISTRY_SEARCH_TIMEOUT_MS = 3000;
66
81
  // Canonical SSoT shape — authored once in `@ggui-ai/protocol`
67
82
  // (`schemas/mcp.ts`).
68
83
  const inputSchema = searchBlueprintsInputShape;
@@ -76,32 +91,42 @@ const outputSchema = {
76
91
  results: z.array(z.record(z.string(), z.unknown())),
77
92
  total: z.number().int().nonnegative(),
78
93
  query: z.string(),
94
+ degradedSources: z
95
+ .array(z.object({
96
+ source: z.literal('registry'),
97
+ reason: z.enum(['unreachable', 'timeout', 'invalid_response']),
98
+ }))
99
+ .optional(),
79
100
  };
80
101
  /**
81
102
  * Build a search-blueprints handler bound to concrete `embedding` +
82
103
  * `vectors` implementations (required) + an optional manifest
83
104
  * `BlueprintProvider`. Tests inject in-memory fakes from
84
105
  * `@ggui-ai/mcp-server-core/in-memory`; production hosts bind to
85
- * AWS Bedrock + S3 Vectors for the semantic source and a
86
- * `ManifestBlueprintProvider` for the manifest source.
106
+ * their own operator-configured embedding + vector-store providers
107
+ * for the semantic source and a `ManifestBlueprintProvider` for the
108
+ * manifest source.
87
109
  */
88
110
  export function createSearchBlueprintsHandler(deps) {
89
111
  return {
90
112
  name: 'ggui_search_blueprints',
91
113
  title: 'Search blueprints',
92
114
  audience: ['agent'],
93
- description: "Search this app's blueprints — both manifest-declared UIs (ggui.json#blueprints.include) and any previously cached generations. Matches by name/description against the manifest source and by cosine similarity against the semantic vector index. Returns entries ordered by score (descending). The agent can decide to reuse a match or generate from scratch.",
115
+ description: "Search this app's blueprints — manifest-declared UIs (ggui.json#blueprints.include), previously cached generations, and, when a registry is configured, published blueprint candidates from the artifact registry. Local sources match by name/description and cosine similarity; registry candidates are advisory matches appended after local results, labeled origin 'registry'. Optional tool/server filters narrow registry candidates to artifacts declaring those MCP tool bindings. The agent can decide to reuse a match or generate from scratch; installing a registry candidate remains a human/operator act.",
94
116
  inputSchema,
95
117
  outputSchema,
96
118
  async handler(rawInput, ctx) {
97
- const { query, limit = 10 } = z.object(inputSchema).parse(rawInput);
98
- // Fan out both sources in parallel. The manifest source is a
119
+ const { query, limit = 10, tool, server } = z
120
+ .object(inputSchema)
121
+ .parse(rawInput);
122
+ // Fan out all sources in parallel. The manifest source is a
99
123
  // pure metadata read (cheap); the semantic source is one
100
- // `embed` + one `query` round-trip. Running them concurrently
101
- // keeps handler latency close to the slower of the two.
102
- const [semantic, manifest] = await Promise.all([
124
+ // `embed` + one `query` round-trip; the registry source is a
125
+ // bounded HTTP call that degrades typed instead of throwing.
126
+ const [semantic, manifest, registry] = await Promise.all([
103
127
  searchSemantic(deps, ctx.appId, query, limit),
104
128
  searchManifest(deps.blueprints, query, limit),
129
+ searchRegistry(deps.registry, { query, tool, server, limit }, ctx.signal),
105
130
  ]);
106
131
  // Merge + dedupe by id. Manifest entries win on collision —
107
132
  // their metadata is author-curated and cannot drift against a
@@ -117,7 +142,15 @@ export function createSearchBlueprintsHandler(deps) {
117
142
  }
118
143
  const merged = Array.from(byId.values()).sort((a, b) => b.score - a.score);
119
144
  const trimmed = merged.slice(0, limit);
120
- return { results: trimmed, total: merged.length, query };
145
+ // Registry candidates append AFTER the local sources (advisory
146
+ // per the discovery design §3) and never displace a local id.
147
+ const registryHits = registry.hits.filter((hit) => !byId.has(hit.id));
148
+ return {
149
+ results: [...trimmed, ...registryHits],
150
+ total: merged.length + registryHits.length,
151
+ query,
152
+ ...(registry.degraded ? { degradedSources: [registry.degraded] } : {}),
153
+ };
121
154
  },
122
155
  };
123
156
  }
@@ -222,3 +255,120 @@ function toManifestHit(entry, queryLower) {
222
255
  function asString(value) {
223
256
  return typeof value === 'string' ? value : '';
224
257
  }
258
+ /**
259
+ * Wire subset of the registry `GET /search` response this handler
260
+ * consumes. Local on purpose: this package does not depend on the
261
+ * registry server implementation, and zod's default strip semantics
262
+ * keep the guard forward-compatible with response additions.
263
+ */
264
+ const registrySearchEntrySchema = z.object({
265
+ artifactId: z.string().min(1),
266
+ latestVersion: z.string().min(1),
267
+ kind: z.string(),
268
+ description: z.string().optional(),
269
+ mcpTools: z.array(z.object({ server: z.string().optional(), tool: z.string() })).optional(),
270
+ scopeVerification: z.enum(['verified', 'unverified']).optional(),
271
+ });
272
+ const registrySearchResponseSchema = z.object({
273
+ results: z.array(registrySearchEntrySchema),
274
+ });
275
+ /**
276
+ * Registry-source branch: bounded public `/search` call. Inactive
277
+ * (empty hits, no degradation) when no registry source is
278
+ * configured. Never throws — every failure mode maps onto the typed
279
+ * `degraded` indication so the merged tool result always succeeds.
280
+ */
281
+ async function searchRegistry(registry, filters, signal) {
282
+ if (!registry)
283
+ return { hits: [] };
284
+ // Operator-override-then-default host resolution — the same
285
+ // precedence + scheme rule as gadget bundleHost (`DEFAULT_BUNDLE_HOST`
286
+ // + `bundleHostScheme` from `@ggui-ai/protocol`).
287
+ const host = typeof registry.host === 'string' && registry.host.length > 0
288
+ ? registry.host
289
+ : DEFAULT_BUNDLE_HOST;
290
+ const params = new URLSearchParams();
291
+ params.set('q', filters.query);
292
+ // This tool's kind scope is blueprints; gadget discovery is served
293
+ // by the registry HTTP surface directly.
294
+ params.set('kind', 'blueprint');
295
+ if (filters.tool !== undefined)
296
+ params.set('tool', filters.tool);
297
+ if (filters.server !== undefined)
298
+ params.set('server', filters.server);
299
+ params.set('limit', String(filters.limit));
300
+ const url = `${bundleHostScheme(host)}://${host}/search?${params.toString()}`;
301
+ const timeoutMs = registry.timeoutMs ?? DEFAULT_REGISTRY_SEARCH_TIMEOUT_MS;
302
+ const signals = [AbortSignal.timeout(timeoutMs)];
303
+ if (signal)
304
+ signals.push(signal);
305
+ const fetchImpl = registry.fetch ?? fetch;
306
+ let res;
307
+ try {
308
+ res = await fetchImpl(url, {
309
+ method: 'GET',
310
+ headers: { accept: 'application/json' },
311
+ signal: AbortSignal.any(signals),
312
+ });
313
+ }
314
+ catch (err) {
315
+ const reason = err instanceof Error && err.name === 'TimeoutError' ? 'timeout' : 'unreachable';
316
+ return { hits: [], degraded: { source: 'registry', reason } };
317
+ }
318
+ if (!res.ok) {
319
+ // Undici doesn't return the socket to the pool until the body is
320
+ // consumed or canceled. We never read this body (non-2xx), so
321
+ // cancel it explicitly — a rejecting cancel() must still surface
322
+ // the already-typed degradation, not throw past this branch.
323
+ try {
324
+ await res.body?.cancel();
325
+ }
326
+ catch {
327
+ // Deliberately swallowed — see comment above.
328
+ }
329
+ return {
330
+ hits: [],
331
+ degraded: { source: 'registry', reason: 'invalid_response' },
332
+ };
333
+ }
334
+ let body;
335
+ try {
336
+ body = await res.json();
337
+ }
338
+ catch {
339
+ return {
340
+ hits: [],
341
+ degraded: { source: 'registry', reason: 'invalid_response' },
342
+ };
343
+ }
344
+ const parsed = registrySearchResponseSchema.safeParse(body);
345
+ if (!parsed.success) {
346
+ return {
347
+ hits: [],
348
+ degraded: { source: 'registry', reason: 'invalid_response' },
349
+ };
350
+ }
351
+ return { hits: parsed.data.results.map(toRegistryHit) };
352
+ }
353
+ function toRegistryHit(entry) {
354
+ return {
355
+ id: `${entry.artifactId}@${entry.latestVersion}`,
356
+ name: entry.artifactId,
357
+ description: entry.description ?? '',
358
+ category: entry.kind,
359
+ // Registry search rows carry no contract details — honest empties.
360
+ props: [],
361
+ callbacks: [],
362
+ featured: false,
363
+ relevance: 'match',
364
+ // The registry source computes no similarity. Entries always
365
+ // append AFTER the score-sorted local sources, so this value
366
+ // carries no ranking weight; 0 is the honest "not measured".
367
+ score: 0,
368
+ origin: 'registry',
369
+ artifactId: entry.artifactId,
370
+ version: entry.latestVersion,
371
+ ...(entry.mcpTools ? { mcpTools: entry.mcpTools } : {}),
372
+ ...(entry.scopeVerification ? { scopeVerification: entry.scopeVerification } : {}),
373
+ };
374
+ }
@@ -1,23 +1,38 @@
1
1
  /**
2
- * `ggui_ops_delete_app` — hard-delete a `GguiApp` row owned by the
2
+ * `ggui_ops_delete_app` — hard-delete an app record owned by the
3
3
  * caller.
4
4
  *
5
5
  * Tenancy: cross-tenant probes return the success shape WITHOUT
6
6
  * touching the row (uniform shape; no existence leak). Idempotent —
7
7
  * a second delete of the same id resolves cleanly.
8
8
  *
9
- * What the cloud adapter additionally does on top of this seam (NOT
10
- * the responsibility of the handler):
11
- * - Cascade-revoke per-app `GguiUserApiKey` rows
12
- * - Cascade-clean per-app provider keys, blueprints, renders
13
- * The handler stays narrow; the adapter's `delete()` implementation
14
- * orchestrates the cascade.
9
+ * Default-app lock: REJECTED when the target is the caller's default
10
+ * app. `defaultAppId` is what the universal route resolves on every
11
+ * request, so deleting the app it names strands that route.
15
12
  *
16
- * Pure over the {@link AppsSource} seam.
13
+ * The check runs AFTER the ownership read. `getDefault(ownerSub)`
14
+ * only ever reads the caller's own row, so ordering is not what keeps
15
+ * another tenant's default secret — nothing here could read it under
16
+ * any ordering. What ordering buys is SHAPE UNIFORMITY in the one
17
+ * state where the two branches disagree: the caller's own default
18
+ * naming an app the caller does not own. Ownership first answers that
19
+ * with the same `{deleted: true}` every other foreign id gets;
20
+ * lock-first would answer with a distinguishable error, and the
21
+ * difference is itself the signal.
22
+ *
23
+ * Scope: this handler removes the APP RECORD, through
24
+ * {@link AppsSource.delete}, and claims nothing beyond it. Whether
25
+ * data other stores hold for the same app disappears with it is the
26
+ * bound implementation's business, spelled out in that method's
27
+ * contract. `{deleted: true}` means the app record is gone — read no
28
+ * cascade into it.
29
+ *
30
+ * Pure over the {@link AppsSource} + {@link UserDefaultAppSource}
31
+ * seams.
17
32
  */
18
33
  import { z } from 'zod';
19
34
  import type { SharedHandler } from '../types.js';
20
- import type { AppsSource } from './types.js';
35
+ import type { AppsSource, UserDefaultAppSource } from './types.js';
21
36
  declare const inputSchema: {
22
37
  readonly appId: z.ZodString;
23
38
  };
@@ -29,6 +44,8 @@ export interface DeleteAppOutput {
29
44
  }
30
45
  export interface DeleteAppDeps {
31
46
  readonly apps: AppsSource;
47
+ /** Read side only — the default-app lock reads `defaultAppId`, never writes it. */
48
+ readonly userDefaultApp: UserDefaultAppSource;
32
49
  }
33
50
  export declare function createDeleteAppHandler(deps: DeleteAppDeps): SharedHandler<typeof inputSchema, typeof outputSchema, DeleteAppOutput>;
34
51
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"delete-app.d.ts","sourceRoot":"","sources":["../../src/ops-apps/delete-app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAkB,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,QAAA,MAAM,WAAW;;CAOP,CAAC;AAEX,QAAA,MAAM,YAAY;;CAER,CAAC;AAEX,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;CAC3B;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,aAAa,GAClB,aAAa,CAAC,OAAO,WAAW,EAAE,OAAO,YAAY,EAAE,eAAe,CAAC,CA8BzE"}
1
+ {"version":3,"file":"delete-app.d.ts","sourceRoot":"","sources":["../../src/ops-apps/delete-app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAkB,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjE,OAAO,KAAK,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEnE,QAAA,MAAM,WAAW;;CAOP,CAAC;AAEX,QAAA,MAAM,YAAY;;CAER,CAAC;AAEX,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,mFAAmF;IACnF,QAAQ,CAAC,cAAc,EAAE,oBAAoB,CAAC;CAC/C;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,aAAa,GAClB,aAAa,CAAC,OAAO,WAAW,EAAE,OAAO,YAAY,EAAE,eAAe,CAAC,CAqCzE"}
@@ -1,22 +1,38 @@
1
1
  /**
2
- * `ggui_ops_delete_app` — hard-delete a `GguiApp` row owned by the
2
+ * `ggui_ops_delete_app` — hard-delete an app record owned by the
3
3
  * caller.
4
4
  *
5
5
  * Tenancy: cross-tenant probes return the success shape WITHOUT
6
6
  * touching the row (uniform shape; no existence leak). Idempotent —
7
7
  * a second delete of the same id resolves cleanly.
8
8
  *
9
- * What the cloud adapter additionally does on top of this seam (NOT
10
- * the responsibility of the handler):
11
- * - Cascade-revoke per-app `GguiUserApiKey` rows
12
- * - Cascade-clean per-app provider keys, blueprints, renders
13
- * The handler stays narrow; the adapter's `delete()` implementation
14
- * orchestrates the cascade.
9
+ * Default-app lock: REJECTED when the target is the caller's default
10
+ * app. `defaultAppId` is what the universal route resolves on every
11
+ * request, so deleting the app it names strands that route.
15
12
  *
16
- * Pure over the {@link AppsSource} seam.
13
+ * The check runs AFTER the ownership read. `getDefault(ownerSub)`
14
+ * only ever reads the caller's own row, so ordering is not what keeps
15
+ * another tenant's default secret — nothing here could read it under
16
+ * any ordering. What ordering buys is SHAPE UNIFORMITY in the one
17
+ * state where the two branches disagree: the caller's own default
18
+ * naming an app the caller does not own. Ownership first answers that
19
+ * with the same `{deleted: true}` every other foreign id gets;
20
+ * lock-first would answer with a distinguishable error, and the
21
+ * difference is itself the signal.
22
+ *
23
+ * Scope: this handler removes the APP RECORD, through
24
+ * {@link AppsSource.delete}, and claims nothing beyond it. Whether
25
+ * data other stores hold for the same app disappears with it is the
26
+ * bound implementation's business, spelled out in that method's
27
+ * contract. `{deleted: true}` means the app record is gone — read no
28
+ * cascade into it.
29
+ *
30
+ * Pure over the {@link AppsSource} + {@link UserDefaultAppSource}
31
+ * seams.
17
32
  */
18
33
  import { z } from 'zod';
19
34
  import { resolveOwnerSub } from './identity.js';
35
+ import { DefaultAppDeleteBlockedError } from './types.js';
20
36
  const inputSchema = {
21
37
  appId: z
22
38
  .string()
@@ -31,7 +47,7 @@ export function createDeleteAppHandler(deps) {
31
47
  name: 'ggui_ops_delete_app',
32
48
  title: 'Delete app',
33
49
  audience: ['ops'],
34
- description: "Hard-delete an app owned by the calling user. Idempotent — a second delete returns `{deleted: true}`. Cross-tenant probes return the same shape without touching foreign rows (no existence leak). Cascades per-app keys / blueprints / renders at the cloud adapter layer.",
50
+ description: "Hard-delete an app owned by the calling user. Removes the APP RECORD only — data other stores hold for that app (saved blueprints, per-app provider keys, marketplace installs, issued keys) is not removed by this call, and whether the deployment cleans it up separately is the deployment's own policy. Idempotent — a second delete returns `{deleted: true}`. Cross-tenant probes return the same shape without touching foreign rows (no existence leak). Throws `default_app_delete_blocked` when the target is the caller's default app — set a different default first.",
35
51
  inputSchema,
36
52
  outputSchema,
37
53
  async handler(rawInput, ctx) {
@@ -44,10 +60,17 @@ export function createDeleteAppHandler(deps) {
44
60
  if (!existing) {
45
61
  // Either the row doesn't exist or it lives under a different
46
62
  // owner. Either way: return the success shape without
47
- // touching DDB. Uniform across "missing" and "cross-tenant"
48
- // prevents id-existence leak.
63
+ // touching the store. Uniform across "missing" and
64
+ // "cross-tenant" prevents id-existence leak.
49
65
  return { deleted: true };
50
66
  }
67
+ // AFTER the ownership read, so a caller whose own default names
68
+ // a foreign app gets the same uniform shape as any other foreign
69
+ // id rather than a distinguishable lock error.
70
+ const currentDefault = await deps.userDefaultApp.getDefault(ownerSub);
71
+ if (currentDefault === parsed.appId) {
72
+ throw new DefaultAppDeleteBlockedError(parsed.appId);
73
+ }
51
74
  await deps.apps.delete({ appId: parsed.appId, ownerSub });
52
75
  return { deleted: true };
53
76
  },
@@ -14,7 +14,7 @@
14
14
  * - `createSetDefaultAppHandler` → `ggui_ops_set_default_app`
15
15
  */
16
16
  export type { AppRecord, AppsSource, AppUpdatePatch, UserDefaultAppSource, } from './types.js';
17
- export { AppNotFoundError, OpsAppsAccessDeniedError } from './types.js';
17
+ export { AppNotFoundError, DefaultAppDeleteBlockedError, OpsAppsAccessDeniedError, } from './types.js';
18
18
  export { createListAppsHandler } from './list-apps.js';
19
19
  export type { ListAppsDeps, ListAppsOutput } from './list-apps.js';
20
20
  export { createCreateAppHandler } from './create-app.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ops-apps/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,YAAY,EACV,SAAS,EACT,UAAU,EACV,cAAc,EACd,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAExE,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEtE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEtE,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,YAAY,EACV,eAAe,EACf,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEtE,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAClE,YAAY,EACV,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ops-apps/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,YAAY,EACV,SAAS,EACT,UAAU,EACV,cAAc,EACd,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,gBAAgB,EAChB,4BAA4B,EAC5B,wBAAwB,GACzB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEtE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEtE,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,YAAY,EACV,eAAe,EACf,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEtE,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAClE,YAAY,EACV,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC"}
@@ -13,7 +13,7 @@
13
13
  * - `createDeleteAppHandler` → `ggui_ops_delete_app`
14
14
  * - `createSetDefaultAppHandler` → `ggui_ops_set_default_app`
15
15
  */
16
- export { AppNotFoundError, OpsAppsAccessDeniedError } from './types.js';
16
+ export { AppNotFoundError, DefaultAppDeleteBlockedError, OpsAppsAccessDeniedError, } from './types.js';
17
17
  export { createListAppsHandler } from './list-apps.js';
18
18
  export { createCreateAppHandler } from './create-app.js';
19
19
  export { createUpdateAppHandler } from './update-app.js';
@@ -99,7 +99,25 @@ export interface AppsSource {
99
99
  ownerSub: string;
100
100
  patch: AppUpdatePatch;
101
101
  }): Promise<AppRecord>;
102
- /** Hard delete. No-throw idempotent — second delete of the same id resolves. */
102
+ /**
103
+ * Hard delete. No-throw idempotent — a second delete of the same id
104
+ * resolves. Rejects cross-tenant.
105
+ *
106
+ * Scope of the obligation, stated because a caller cannot see it:
107
+ * this seam owns the APP RECORD and nothing else. Whatever other
108
+ * stores hold keyed by the same `appId` — saved blueprints, per-app
109
+ * provider keys, marketplace installs, issued keys — is outside it.
110
+ *
111
+ * An implementation MAY cascade those away inside its own `delete`,
112
+ * and MAY complete that cascade asynchronously after resolving. One
113
+ * that leaves rows behind — permanently or until an asynchronous
114
+ * sweep lands — MUST make that observable: a named, structured event
115
+ * naming the row classes left in place, emitted on the delete that
116
+ * orphaned them, carrying an indication of whether asynchronous
117
+ * completion was arranged. Returning silently is the contract
118
+ * violation — not the orphaning itself. Orphaned rows an operator
119
+ * can enumerate are debt; orphaned rows nobody can find are loss.
120
+ */
103
121
  delete(args: {
104
122
  appId: string;
105
123
  ownerSub: string;
@@ -143,6 +161,19 @@ export declare class AppNotFoundError extends Error {
143
161
  readonly code: "app_not_found";
144
162
  constructor(appId: string);
145
163
  }
164
+ /**
165
+ * Thrown when the delete target IS the caller's default app.
166
+ *
167
+ * `defaultAppId` is what the universal route resolves on every
168
+ * request; deleting the app it names would leave that route pointing
169
+ * at a row that no longer exists. Same lock the console's Delete
170
+ * enforces — the operator picks a different default first, then
171
+ * deletes.
172
+ */
173
+ export declare class DefaultAppDeleteBlockedError extends Error {
174
+ readonly code: "default_app_delete_blocked";
175
+ constructor(appId: string);
176
+ }
146
177
  /**
147
178
  * Thrown by adapters that prefer surfacing access denial over the
148
179
  * "treat as not-found" privacy posture. Handlers translate to the