@oscharko-dev/keiko 0.2.0-beta.6 → 0.2.0-beta.8

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 (210) hide show
  1. package/dist/ui/csp-hashes.json +14 -14
  2. package/dist/ui/static/404.html +1 -1
  3. package/dist/ui/static/__next.__PAGE__.txt +2 -2
  4. package/dist/ui/static/__next._full.txt +3 -3
  5. package/dist/ui/static/__next._head.txt +1 -1
  6. package/dist/ui/static/__next._index.txt +2 -2
  7. package/dist/ui/static/__next._tree.txt +2 -2
  8. package/dist/ui/static/_next/static/chunks/0i3jzgrj42so8.css +1 -0
  9. package/dist/ui/static/_next/static/chunks/1ru_021szp0u7.js +1 -0
  10. package/dist/ui/static/_next/static/chunks/1t7vb5d9ed2e7.js +1 -0
  11. package/dist/ui/static/_next/static/chunks/23o2c6pyjq92z.js +109 -0
  12. package/dist/ui/static/_not-found/__next._full.txt +2 -2
  13. package/dist/ui/static/_not-found/__next._head.txt +1 -1
  14. package/dist/ui/static/_not-found/__next._index.txt +2 -2
  15. package/dist/ui/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
  16. package/dist/ui/static/_not-found/__next._not-found.txt +1 -1
  17. package/dist/ui/static/_not-found/__next._tree.txt +2 -2
  18. package/dist/ui/static/_not-found.html +1 -1
  19. package/dist/ui/static/_not-found.txt +2 -2
  20. package/dist/ui/static/fonts/OFL.txt +93 -0
  21. package/dist/ui/static/fonts/jetbrains-mono-latin-wght-normal.woff2 +0 -0
  22. package/dist/ui/static/index.html +1 -1
  23. package/dist/ui/static/index.txt +3 -3
  24. package/dist/ui/static/launch/__next._full.txt +3 -3
  25. package/dist/ui/static/launch/__next._head.txt +1 -1
  26. package/dist/ui/static/launch/__next._index.txt +2 -2
  27. package/dist/ui/static/launch/__next._tree.txt +2 -2
  28. package/dist/ui/static/launch/__next.launch.__PAGE__.txt +2 -2
  29. package/dist/ui/static/launch/__next.launch.txt +1 -1
  30. package/dist/ui/static/launch.html +1 -1
  31. package/dist/ui/static/launch.txt +3 -3
  32. package/dist/ui/static/local-knowledge/__next._full.txt +3 -3
  33. package/dist/ui/static/local-knowledge/__next._head.txt +1 -1
  34. package/dist/ui/static/local-knowledge/__next._index.txt +2 -2
  35. package/dist/ui/static/local-knowledge/__next._tree.txt +2 -2
  36. package/dist/ui/static/local-knowledge/__next.local-knowledge.__PAGE__.txt +2 -2
  37. package/dist/ui/static/local-knowledge/__next.local-knowledge.txt +1 -1
  38. package/dist/ui/static/local-knowledge/capsule/__next._full.txt +3 -3
  39. package/dist/ui/static/local-knowledge/capsule/__next._head.txt +1 -1
  40. package/dist/ui/static/local-knowledge/capsule/__next._index.txt +2 -2
  41. package/dist/ui/static/local-knowledge/capsule/__next._tree.txt +2 -2
  42. package/dist/ui/static/local-knowledge/capsule/__next.local-knowledge.capsule.__PAGE__.txt +2 -2
  43. package/dist/ui/static/local-knowledge/capsule/__next.local-knowledge.capsule.txt +1 -1
  44. package/dist/ui/static/local-knowledge/capsule/__next.local-knowledge.txt +1 -1
  45. package/dist/ui/static/local-knowledge/capsule.html +1 -1
  46. package/dist/ui/static/local-knowledge/capsule.txt +3 -3
  47. package/dist/ui/static/local-knowledge.html +1 -1
  48. package/dist/ui/static/local-knowledge.txt +3 -3
  49. package/dist/ui/static/memoriaviva/__next._full.txt +2 -2
  50. package/dist/ui/static/memoriaviva/__next._head.txt +1 -1
  51. package/dist/ui/static/memoriaviva/__next._index.txt +2 -2
  52. package/dist/ui/static/memoriaviva/__next._tree.txt +2 -2
  53. package/dist/ui/static/memoriaviva/__next.memoriaviva.__PAGE__.txt +1 -1
  54. package/dist/ui/static/memoriaviva/__next.memoriaviva.txt +1 -1
  55. package/dist/ui/static/memoriaviva/consolidation/__next._full.txt +2 -2
  56. package/dist/ui/static/memoriaviva/consolidation/__next._head.txt +1 -1
  57. package/dist/ui/static/memoriaviva/consolidation/__next._index.txt +2 -2
  58. package/dist/ui/static/memoriaviva/consolidation/__next._tree.txt +2 -2
  59. package/dist/ui/static/memoriaviva/consolidation/__next.memoriaviva.consolidation.__PAGE__.txt +1 -1
  60. package/dist/ui/static/memoriaviva/consolidation/__next.memoriaviva.consolidation.txt +1 -1
  61. package/dist/ui/static/memoriaviva/consolidation/__next.memoriaviva.txt +1 -1
  62. package/dist/ui/static/memoriaviva/consolidation.html +1 -1
  63. package/dist/ui/static/memoriaviva/consolidation.txt +2 -2
  64. package/dist/ui/static/memoriaviva/detail/__next._full.txt +2 -2
  65. package/dist/ui/static/memoriaviva/detail/__next._head.txt +1 -1
  66. package/dist/ui/static/memoriaviva/detail/__next._index.txt +2 -2
  67. package/dist/ui/static/memoriaviva/detail/__next._tree.txt +2 -2
  68. package/dist/ui/static/memoriaviva/detail/__next.memoriaviva.detail.__PAGE__.txt +1 -1
  69. package/dist/ui/static/memoriaviva/detail/__next.memoriaviva.detail.txt +1 -1
  70. package/dist/ui/static/memoriaviva/detail/__next.memoriaviva.txt +1 -1
  71. package/dist/ui/static/memoriaviva/detail.html +1 -1
  72. package/dist/ui/static/memoriaviva/detail.txt +2 -2
  73. package/dist/ui/static/memoriaviva/review-queue/__next._full.txt +2 -2
  74. package/dist/ui/static/memoriaviva/review-queue/__next._head.txt +1 -1
  75. package/dist/ui/static/memoriaviva/review-queue/__next._index.txt +2 -2
  76. package/dist/ui/static/memoriaviva/review-queue/__next._tree.txt +2 -2
  77. package/dist/ui/static/memoriaviva/review-queue/__next.memoriaviva.review-queue.__PAGE__.txt +1 -1
  78. package/dist/ui/static/memoriaviva/review-queue/__next.memoriaviva.review-queue.txt +1 -1
  79. package/dist/ui/static/memoriaviva/review-queue/__next.memoriaviva.txt +1 -1
  80. package/dist/ui/static/memoriaviva/review-queue.html +1 -1
  81. package/dist/ui/static/memoriaviva/review-queue.txt +2 -2
  82. package/dist/ui/static/memoriaviva.html +1 -1
  83. package/dist/ui/static/memoriaviva.txt +2 -2
  84. package/dist/ui/static/sw.js +7 -3
  85. package/node_modules/@oscharko-dev/keiko-cli/dist/.tsbuildinfo +1 -1
  86. package/node_modules/@oscharko-dev/keiko-cli/dist/memory.d.ts.map +1 -1
  87. package/node_modules/@oscharko-dev/keiko-cli/dist/memory.js +4 -5
  88. package/node_modules/@oscharko-dev/keiko-cli/package.json +1 -1
  89. package/node_modules/@oscharko-dev/keiko-contracts/dist/.tsbuildinfo +1 -1
  90. package/node_modules/@oscharko-dev/keiko-contracts/dist/index.d.ts +1 -1
  91. package/node_modules/@oscharko-dev/keiko-contracts/dist/index.js +1 -1
  92. package/node_modules/@oscharko-dev/keiko-contracts/package.json +1 -1
  93. package/node_modules/@oscharko-dev/keiko-evaluations/dist/.tsbuildinfo +1 -1
  94. package/node_modules/@oscharko-dev/keiko-evaluations/package.json +1 -1
  95. package/node_modules/@oscharko-dev/keiko-evidence/dist/.tsbuildinfo +1 -1
  96. package/node_modules/@oscharko-dev/keiko-evidence/dist/qualityIntelligence/figmaSnapshot/store.d.ts +5 -0
  97. package/node_modules/@oscharko-dev/keiko-evidence/dist/qualityIntelligence/figmaSnapshot/store.d.ts.map +1 -1
  98. package/node_modules/@oscharko-dev/keiko-evidence/dist/qualityIntelligence/figmaSnapshot/store.js +16 -0
  99. package/node_modules/@oscharko-dev/keiko-evidence/package.json +1 -1
  100. package/node_modules/@oscharko-dev/keiko-harness/dist/.tsbuildinfo +1 -1
  101. package/node_modules/@oscharko-dev/keiko-harness/package.json +1 -1
  102. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/.tsbuildinfo +1 -1
  103. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/parsers/pdf-parser.d.ts.map +1 -1
  104. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/parsers/pdf-parser.js +0 -10
  105. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/parsers/types.d.ts +2 -2
  106. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/parsers/types.d.ts.map +1 -1
  107. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/parsers/types.js +3 -3
  108. package/node_modules/@oscharko-dev/keiko-local-knowledge/package.json +1 -1
  109. package/node_modules/@oscharko-dev/keiko-memory-capture/package.json +1 -1
  110. package/node_modules/@oscharko-dev/keiko-memory-consolidation/package.json +1 -1
  111. package/node_modules/@oscharko-dev/keiko-memory-governance/dist/.tsbuildinfo +1 -1
  112. package/node_modules/@oscharko-dev/keiko-memory-governance/dist/index.d.ts +1 -1
  113. package/node_modules/@oscharko-dev/keiko-memory-governance/dist/index.d.ts.map +1 -1
  114. package/node_modules/@oscharko-dev/keiko-memory-governance/dist/index.js +1 -1
  115. package/node_modules/@oscharko-dev/keiko-memory-governance/dist/maintenance.d.ts +2 -16
  116. package/node_modules/@oscharko-dev/keiko-memory-governance/dist/maintenance.d.ts.map +1 -1
  117. package/node_modules/@oscharko-dev/keiko-memory-governance/dist/maintenance.js +49 -48
  118. package/node_modules/@oscharko-dev/keiko-memory-governance/dist/retention.d.ts +2 -1
  119. package/node_modules/@oscharko-dev/keiko-memory-governance/dist/retention.d.ts.map +1 -1
  120. package/node_modules/@oscharko-dev/keiko-memory-governance/dist/retention.js +15 -0
  121. package/node_modules/@oscharko-dev/keiko-memory-governance/package.json +1 -1
  122. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/.tsbuildinfo +1 -1
  123. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/decay.d.ts +2 -0
  124. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/decay.d.ts.map +1 -0
  125. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/decay.js +22 -0
  126. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/diversity.d.ts +8 -0
  127. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/diversity.d.ts.map +1 -0
  128. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/diversity.js +87 -0
  129. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/index.d.ts +4 -2
  130. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/index.d.ts.map +1 -1
  131. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/index.js +4 -1
  132. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/ranking.d.ts +5 -1
  133. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/ranking.d.ts.map +1 -1
  134. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/ranking.js +140 -21
  135. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/recency.d.ts.map +1 -1
  136. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/recency.js +4 -10
  137. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/retrieve.d.ts.map +1 -1
  138. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/retrieve.js +11 -1
  139. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/strength.d.ts +9 -0
  140. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/strength.d.ts.map +1 -0
  141. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/strength.js +51 -0
  142. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/types.d.ts +11 -0
  143. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/types.d.ts.map +1 -1
  144. package/node_modules/@oscharko-dev/keiko-memory-retrieval/dist/types.js +7 -0
  145. package/node_modules/@oscharko-dev/keiko-memory-retrieval/package.json +1 -1
  146. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/.tsbuildinfo +1 -1
  147. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/access.d.ts +3 -0
  148. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/access.d.ts.map +1 -1
  149. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/access.js +31 -1
  150. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/schema.d.ts +1 -1
  151. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/schema.d.ts.map +1 -1
  152. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/schema.js +16 -1
  153. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/types.d.ts +1 -0
  154. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/types.d.ts.map +1 -1
  155. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/vault.d.ts.map +1 -1
  156. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/vault.js +4 -1
  157. package/node_modules/@oscharko-dev/keiko-memory-vault/package.json +1 -1
  158. package/node_modules/@oscharko-dev/keiko-model-gateway/dist/.tsbuildinfo +1 -1
  159. package/node_modules/@oscharko-dev/keiko-model-gateway/package.json +1 -1
  160. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/.tsbuildinfo +1 -1
  161. package/node_modules/@oscharko-dev/keiko-quality-intelligence/package.json +1 -1
  162. package/node_modules/@oscharko-dev/keiko-sdk/dist/.tsbuildinfo +1 -1
  163. package/node_modules/@oscharko-dev/keiko-sdk/package.json +1 -1
  164. package/node_modules/@oscharko-dev/keiko-security/dist/.tsbuildinfo +1 -1
  165. package/node_modules/@oscharko-dev/keiko-security/package.json +1 -1
  166. package/node_modules/@oscharko-dev/keiko-server/dist/.tsbuildinfo +1 -1
  167. package/node_modules/@oscharko-dev/keiko-server/dist/chat-handlers.d.ts.map +1 -1
  168. package/node_modules/@oscharko-dev/keiko-server/dist/chat-handlers.js +44 -65
  169. package/node_modules/@oscharko-dev/keiko-server/dist/deps.d.ts +2 -0
  170. package/node_modules/@oscharko-dev/keiko-server/dist/deps.d.ts.map +1 -1
  171. package/node_modules/@oscharko-dev/keiko-server/dist/deps.js +1 -0
  172. package/node_modules/@oscharko-dev/keiko-server/dist/gateway-setup.d.ts.map +1 -1
  173. package/node_modules/@oscharko-dev/keiko-server/dist/gateway-setup.js +348 -64
  174. package/node_modules/@oscharko-dev/keiko-server/dist/memory-conv-handlers.d.ts.map +1 -1
  175. package/node_modules/@oscharko-dev/keiko-server/dist/memory-conv-handlers.js +34 -5
  176. package/node_modules/@oscharko-dev/keiko-server/dist/memory-embedding.d.ts +12 -2
  177. package/node_modules/@oscharko-dev/keiko-server/dist/memory-embedding.d.ts.map +1 -1
  178. package/node_modules/@oscharko-dev/keiko-server/dist/memory-embedding.js +127 -0
  179. package/node_modules/@oscharko-dev/keiko-server/dist/memory-handlers.d.ts.map +1 -1
  180. package/node_modules/@oscharko-dev/keiko-server/dist/memory-handlers.js +40 -11
  181. package/node_modules/@oscharko-dev/keiko-server/dist/memory-maintenance-handlers.d.ts +16 -3
  182. package/node_modules/@oscharko-dev/keiko-server/dist/memory-maintenance-handlers.d.ts.map +1 -1
  183. package/node_modules/@oscharko-dev/keiko-server/dist/memory-maintenance-handlers.js +72 -50
  184. package/node_modules/@oscharko-dev/keiko-server/dist/memory-retrieval-signals.d.ts +12 -0
  185. package/node_modules/@oscharko-dev/keiko-server/dist/memory-retrieval-signals.d.ts.map +1 -0
  186. package/node_modules/@oscharko-dev/keiko-server/dist/memory-retrieval-signals.js +84 -0
  187. package/node_modules/@oscharko-dev/keiko-server/dist/memory-salience.d.ts.map +1 -1
  188. package/node_modules/@oscharko-dev/keiko-server/dist/memory-salience.js +11 -6
  189. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figmaSnapshotRoutes.d.ts +15 -0
  190. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figmaSnapshotRoutes.d.ts.map +1 -1
  191. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/figmaSnapshotRoutes.js +105 -0
  192. package/node_modules/@oscharko-dev/keiko-server/dist/routes.d.ts.map +1 -1
  193. package/node_modules/@oscharko-dev/keiko-server/dist/routes.js +2 -1
  194. package/node_modules/@oscharko-dev/keiko-server/package.json +1 -1
  195. package/node_modules/@oscharko-dev/keiko-tools/dist/.tsbuildinfo +1 -1
  196. package/node_modules/@oscharko-dev/keiko-tools/package.json +1 -1
  197. package/node_modules/@oscharko-dev/keiko-verification/dist/.tsbuildinfo +1 -1
  198. package/node_modules/@oscharko-dev/keiko-verification/package.json +1 -1
  199. package/node_modules/@oscharko-dev/keiko-workflows/dist/.tsbuildinfo +1 -1
  200. package/node_modules/@oscharko-dev/keiko-workflows/package.json +1 -1
  201. package/node_modules/@oscharko-dev/keiko-workspace/dist/.tsbuildinfo +1 -1
  202. package/node_modules/@oscharko-dev/keiko-workspace/package.json +1 -1
  203. package/package.json +2 -1
  204. package/dist/ui/static/_next/static/chunks/0-qhhdvxg2j_y.js +0 -1
  205. package/dist/ui/static/_next/static/chunks/0ke4ratkgvcxo.css +0 -1
  206. package/dist/ui/static/_next/static/chunks/3vf3oh2-sl2nc.js +0 -1
  207. package/dist/ui/static/_next/static/chunks/3wmd4-2vznp2g.js +0 -106
  208. /package/dist/ui/static/_next/static/{fQMXe8UmV01bh25WOoIt3 → Ppze_8n_i3yc1FS_Qdj0I}/_buildManifest.js +0 -0
  209. /package/dist/ui/static/_next/static/{fQMXe8UmV01bh25WOoIt3 → Ppze_8n_i3yc1FS_Qdj0I}/_clientMiddlewareManifest.js +0 -0
  210. /package/dist/ui/static/_next/static/{fQMXe8UmV01bh25WOoIt3 → Ppze_8n_i3yc1FS_Qdj0I}/_ssgManifest.js +0 -0
@@ -9,7 +9,9 @@ import { apiKeyHeaderValue, ConfigInvalidError, DEFAULT_API_KEY_HEADER_NAME, Gat
9
9
  import { gatewayFetch, readJsonCapped } from "@oscharko-dev/keiko-model-gateway/internal/http";
10
10
  import { redact } from "@oscharko-dev/keiko-security";
11
11
  import { errorBody } from "./routes.js";
12
- import { currentGatewayEgressConfig } from "./deps.js";
12
+ import { currentGatewayConfig, currentGatewayEgressConfig } from "./deps.js";
13
+ import { classifyFigmaTransportError, FigmaConnectorError, } from "./qualityIntelligence/figma/figmaConnectorErrors.js";
14
+ import { classifyTokenFailure } from "./qualityIntelligence/figma/figmaTokenSource.js";
13
15
  const MAX_BODY_BYTES = 64_000;
14
16
  // Issue #144: exported so discovery-normalization tests can pin the slice cap
15
17
  // without hardcoding the number. The discovery surface is a public seam.
@@ -18,8 +20,11 @@ const MAX_DEPLOYMENT_NAMES = 100;
18
20
  const MAX_MODEL_ID_LENGTH = 160;
19
21
  const DISCOVERED_MODEL_SMOKE_TIMEOUT_MS = 15_000;
20
22
  const DEPLOYMENT_SMOKE_TIMEOUT_MS = 30_000;
23
+ const FIGMA_CREDENTIAL_SMOKE_TIMEOUT_MS = 15_000;
24
+ const FIGMA_CREDENTIAL_SMOKE_RESPONSE_BYTES = 64_000;
21
25
  const SETUP_SMOKE_CONCURRENCY = 4;
22
26
  const CHAT_COMPATIBLE_MODES = new Set(["chat", "completion", "responses"]);
27
+ const EMBEDDING_ID_PATTERN = /(?:^|[-_/. ])(?:text-)?embed(?:ding)?s?(?:[-_/. ]|$)|ada-002(?:$|[-_/. ])/i;
23
28
  class BodyTooLargeError extends Error {
24
29
  constructor() {
25
30
  super("request body too large");
@@ -99,7 +104,9 @@ function isAzureFoundryBaseUrl(baseUrl) {
99
104
  }
100
105
  }
101
106
  function providerRaw(modelId, baseUrl, apiKey, options = {}) {
102
- const defaultCapability = createDefaultChatCapability(modelId);
107
+ const defaultCapability = options.embeddingModelIds?.includes(modelId) === true
108
+ ? createDefaultEmbeddingCapabilityForSetup(modelId)
109
+ : createDefaultChatCapability(modelId);
103
110
  return {
104
111
  modelId,
105
112
  baseUrl,
@@ -110,15 +117,80 @@ function providerRaw(modelId, baseUrl, apiKey, options = {}) {
110
117
  : defaultCapability,
111
118
  timeoutMs: options.timeoutMs ?? 30_000,
112
119
  maxRetries: options.maxRetries ?? 2,
113
- retryBaseDelayMs: 500,
120
+ retryBaseDelayMs: options.retryBaseDelayMs ?? 500,
114
121
  };
115
122
  }
123
+ function isLikelyEmbeddingModelId(modelId) {
124
+ return EMBEDDING_ID_PATTERN.test(modelId);
125
+ }
126
+ function createDefaultEmbeddingCapabilityForSetup(modelId) {
127
+ return {
128
+ id: modelId,
129
+ kind: "embedding",
130
+ contextWindow: 8_191,
131
+ maxOutputTokens: 0,
132
+ toolCalling: false,
133
+ structuredOutput: false,
134
+ streaming: false,
135
+ supportsImageInput: false,
136
+ supportsDocumentInput: false,
137
+ workflowEligible: false,
138
+ costClass: "low",
139
+ latencyClass: "fast",
140
+ throughputHint: "runtime-configured embedding endpoint",
141
+ preferredUseCases: ["Embeddings"],
142
+ knownLimitations: [
143
+ "Runtime-configured capability; validate against the target endpoint before production use",
144
+ ],
145
+ };
146
+ }
147
+ function mergeChatAndEmbeddingModelIds(chatModelIds, deploymentNames) {
148
+ const merged = [...chatModelIds];
149
+ const seen = new Set(merged);
150
+ for (const modelId of deploymentNames) {
151
+ if (!isLikelyEmbeddingModelId(modelId) || seen.has(modelId)) {
152
+ continue;
153
+ }
154
+ seen.add(modelId);
155
+ merged.push(modelId);
156
+ }
157
+ return merged;
158
+ }
159
+ function embeddingModelIdsFromDeployments(deploymentNames) {
160
+ return deploymentNames.filter(isLikelyEmbeddingModelId);
161
+ }
116
162
  function buildRawConfig(baseUrl, apiKey, modelIds, options = {}) {
117
163
  return {
118
164
  providers: modelIds.map((modelId) => providerRaw(modelId, baseUrl, apiKey, options)),
119
165
  circuitBreaker: { failureThreshold: 5, cooldownMs: 30_000, halfOpenProbes: 2 },
120
166
  };
121
167
  }
168
+ function currentImageInputModelIds(config) {
169
+ return (config?.capabilities
170
+ ?.filter((capability) => capability.kind === "chat" && capability.supportsImageInput)
171
+ .map((capability) => capability.id) ?? []);
172
+ }
173
+ function rawConfigFromCurrent(config, figmaAccessToken) {
174
+ return {
175
+ providers: config.providers.map((provider) => {
176
+ const capability = config.capabilities?.find((item) => item.id === provider.modelId);
177
+ return {
178
+ modelId: provider.modelId,
179
+ baseUrl: provider.baseUrl,
180
+ apiKey: provider.apiKey,
181
+ apiKeyHeaderName: provider.apiKeyHeaderName ?? DEFAULT_API_KEY_HEADER_NAME,
182
+ timeoutMs: provider.timeoutMs,
183
+ maxRetries: provider.maxRetries,
184
+ retryBaseDelayMs: provider.retryBaseDelayMs,
185
+ ...(capability === undefined ? {} : { capability }),
186
+ };
187
+ }),
188
+ circuitBreaker: config.circuitBreaker,
189
+ ...(config.capabilities === undefined ? {} : { capabilities: config.capabilities }),
190
+ ...(config.grounding === undefined ? {} : { grounding: config.grounding }),
191
+ ...(figmaAccessToken === undefined ? {} : { figma: { accessToken: figmaAccessToken } }),
192
+ };
193
+ }
122
194
  function withInheritedEgress(rawConfig, egress) {
123
195
  if (egress === undefined || Object.hasOwn(rawConfig, "egress")) {
124
196
  return rawConfig;
@@ -382,6 +454,46 @@ async function defaultGatewaySetupTester(config, candidateModelIds) {
382
454
  });
383
455
  }, SETUP_SMOKE_CONCURRENCY);
384
456
  }
457
+ const FIGMA_ME_ENDPOINT = "https://api.figma.com/v1/me";
458
+ function figmaReason(body) {
459
+ if (!isRecord(body))
460
+ return undefined;
461
+ const reason = body.err ?? body.message;
462
+ return typeof reason === "string" ? reason : undefined;
463
+ }
464
+ async function defaultFigmaCredentialTester(accessToken, egress) {
465
+ try {
466
+ const response = await gatewayFetch(FIGMA_ME_ENDPOINT, {
467
+ method: "GET",
468
+ headers: {
469
+ Accept: "application/json",
470
+ "X-Figma-Token": accessToken,
471
+ },
472
+ redirect: "manual",
473
+ signal: AbortSignal.timeout(FIGMA_CREDENTIAL_SMOKE_TIMEOUT_MS),
474
+ ...(egress !== undefined ? { egress } : {}),
475
+ });
476
+ let body;
477
+ try {
478
+ body = await readJsonCapped(response, FIGMA_CREDENTIAL_SMOKE_RESPONSE_BYTES);
479
+ }
480
+ catch {
481
+ throw new FigmaConnectorError("FIGMA_RESPONSE_TOO_LARGE");
482
+ }
483
+ if (!response.ok) {
484
+ throw classifyTokenFailure(response.status, figmaReason(body));
485
+ }
486
+ if (!isRecord(body)) {
487
+ throw new FigmaConnectorError("FIGMA_INTERNAL");
488
+ }
489
+ }
490
+ catch (error) {
491
+ if (error instanceof FigmaConnectorError) {
492
+ throw error;
493
+ }
494
+ throw new FigmaConnectorError(classifyFigmaTransportError(error));
495
+ }
496
+ }
385
497
  function savePrivateJson(path, raw) {
386
498
  const resolvedPath = resolve(path);
387
499
  const dir = dirname(resolvedPath);
@@ -456,39 +568,111 @@ function readSetupModelLists(raw) {
456
568
  }
457
569
  return { deploymentNames, imageInputModelIds };
458
570
  }
459
- function readSetupRequest(raw, env) {
460
- if (!isRecord(raw)) {
461
- return { status: 400, body: errorBody("BAD_REQUEST", "Request body must be a JSON object.") };
571
+ function optionalSetupSecret(value, path) {
572
+ if (value === undefined) {
573
+ return undefined;
574
+ }
575
+ if (typeof value !== "string") {
576
+ return { status: 400, body: errorBody("BAD_REQUEST", `${path} must be a string.`) };
577
+ }
578
+ const trimmed = value.trim();
579
+ return trimmed.length === 0 ? undefined : trimmed;
580
+ }
581
+ function hasNonBlankStringField(raw, key) {
582
+ const value = raw[key];
583
+ return typeof value === "string" && value.trim().length > 0;
584
+ }
585
+ function hasNonEmptyListField(raw, key) {
586
+ const value = raw[key];
587
+ if (typeof value === "string") {
588
+ return normalizeDeploymentNames(deploymentNameValues(value) ?? []).length > 0;
462
589
  }
463
- const baseUrl = typeof raw.baseUrl === "string" ? raw.baseUrl.trim() : "";
464
- const apiKey = typeof raw.apiKey === "string" ? raw.apiKey.trim() : "";
590
+ return Array.isArray(value) && value.some((item) => typeof item === "string" && item.trim());
591
+ }
592
+ function shouldPreserveExisting(raw, current) {
593
+ return raw.preserveExisting === true && current !== undefined;
594
+ }
595
+ function firstProvider(current) {
596
+ return current?.providers[0];
597
+ }
598
+ function trimmedSubmittedString(raw, key) {
599
+ const value = raw[key];
600
+ if (typeof value !== "string")
601
+ return undefined;
602
+ const trimmed = value.trim();
603
+ return trimmed.length === 0 ? undefined : trimmed;
604
+ }
605
+ function submittedOrInheritedString(raw, key, inherited, preserveExisting) {
606
+ return trimmedSubmittedString(raw, key) ?? (preserveExisting ? (inherited ?? "") : "");
607
+ }
608
+ function setupApiKeyHeaderSource(raw, provider, preserveExisting) {
609
+ if (raw.apiKeyHeaderName !== undefined || !preserveExisting) {
610
+ return raw.apiKeyHeaderName;
611
+ }
612
+ return provider?.apiKeyHeaderName ?? DEFAULT_API_KEY_HEADER_NAME;
613
+ }
614
+ function readSetupGatewayCredentials(raw, env, current, preserveExisting) {
615
+ const provider = firstProvider(current);
616
+ const baseUrl = submittedOrInheritedString(raw, "baseUrl", provider?.baseUrl, preserveExisting);
617
+ const apiKey = submittedOrInheritedString(raw, "apiKey", provider?.apiKey, preserveExisting);
465
618
  if (baseUrl.length === 0 || apiKey.length === 0) {
466
619
  return { status: 400, body: errorBody("BAD_REQUEST", "baseUrl and apiKey are required.") };
467
620
  }
468
- const apiKeyHeaderName = normalizeSetupApiKeyHeaderName(raw.apiKeyHeaderName);
621
+ const apiKeyHeaderSource = setupApiKeyHeaderSource(raw, provider, preserveExisting);
622
+ const apiKeyHeaderName = normalizeSetupApiKeyHeaderName(apiKeyHeaderSource);
469
623
  if (isRouteResult(apiKeyHeaderName)) {
470
624
  return apiKeyHeaderName;
471
625
  }
626
+ const invalidConnection = validateSetupConnection(baseUrl, apiKey, apiKeyHeaderName, env);
627
+ if (invalidConnection !== undefined) {
628
+ return invalidConnection;
629
+ }
630
+ return { baseUrl, apiKey, apiKeyHeaderName };
631
+ }
632
+ function resolveSetupModelLists(modelLists, current, preserveExisting) {
633
+ const existing = preserveExisting ? current : undefined;
634
+ return {
635
+ deploymentNames: existing !== undefined && modelLists.deploymentNames.length === 0
636
+ ? existing.providers.map((item) => item.modelId)
637
+ : modelLists.deploymentNames,
638
+ imageInputModelIds: existing !== undefined && modelLists.imageInputModelIds.length === 0
639
+ ? currentImageInputModelIds(existing)
640
+ : modelLists.imageInputModelIds,
641
+ };
642
+ }
643
+ function setupRequiresGatewayVerification(raw, preserveExisting) {
644
+ return (!preserveExisting ||
645
+ hasNonBlankStringField(raw, "baseUrl") ||
646
+ hasNonBlankStringField(raw, "apiKey") ||
647
+ hasNonBlankStringField(raw, "apiKeyHeaderName") ||
648
+ hasNonEmptyListField(raw, "deploymentNames") ||
649
+ hasNonEmptyListField(raw, "imageInputModelIds"));
650
+ }
651
+ function readSetupRequest(raw, env, current) {
652
+ if (!isRecord(raw)) {
653
+ return { status: 400, body: errorBody("BAD_REQUEST", "Request body must be a JSON object.") };
654
+ }
655
+ const preserveExisting = shouldPreserveExisting(raw, current);
656
+ const credentials = readSetupGatewayCredentials(raw, env, current, preserveExisting);
657
+ if (isRouteResult(credentials)) {
658
+ return credentials;
659
+ }
472
660
  const modelLists = readSetupModelLists(raw);
473
661
  if (isRouteResult(modelLists)) {
474
662
  return modelLists;
475
663
  }
476
- if (Object.hasOwn(raw, "figmaAccessToken")) {
477
- return {
478
- status: 400,
479
- body: errorBody("BAD_REQUEST", "Figma access tokens must be configured server-side, not through gateway setup."),
480
- };
481
- }
482
- const invalidConnection = validateSetupConnection(baseUrl, apiKey, apiKeyHeaderName, env);
483
- if (invalidConnection !== undefined) {
484
- return invalidConnection;
664
+ const figmaAccessToken = optionalSetupSecret(raw.figmaAccessToken, "figmaAccessToken");
665
+ if (isRouteResult(figmaAccessToken)) {
666
+ return figmaAccessToken;
485
667
  }
668
+ const resolvedModelLists = resolveSetupModelLists(modelLists, current, preserveExisting);
486
669
  return {
487
- baseUrl,
488
- apiKey,
489
- apiKeyHeaderName,
490
- deploymentNames: modelLists.deploymentNames,
491
- imageInputModelIds: modelLists.imageInputModelIds,
670
+ ...credentials,
671
+ deploymentNames: resolvedModelLists.deploymentNames,
672
+ imageInputModelIds: resolvedModelLists.imageInputModelIds,
673
+ figmaAccessToken: figmaAccessToken ?? current?.figma?.accessToken,
674
+ verifyGateway: setupRequiresGatewayVerification(raw, preserveExisting),
675
+ verifyFigmaCredential: figmaAccessToken !== undefined,
492
676
  };
493
677
  }
494
678
  function safeError(error, secrets) {
@@ -506,34 +690,55 @@ function assertImageInputModelsWereTested(imageInputModelIds, testedModelIds) {
506
690
  throw new Error("imageInputModelIds must match tested chat-callable model ids.");
507
691
  }
508
692
  }
509
- async function verifySetupCandidate(baseUrl, apiKey, apiKeyHeaderName, deploymentNames, imageInputModelIds, tester, discovery, env, egress) {
693
+ function validationConfigForSetup(input) {
694
+ const validationRawConfig = buildRawConfig(input.baseUrl, input.apiKey, ["setup-validation"], {
695
+ apiKeyHeaderName: input.apiKeyHeaderName,
696
+ imageInputModelIds: input.imageInputModelIds,
697
+ });
698
+ return parseGatewayConfig(withInheritedEgress(validationRawConfig, input.egress), input.env);
699
+ }
700
+ async function candidateModelIdsForSetup(input, validationConfig) {
701
+ return input.deploymentNames.length > 0
702
+ ? input.deploymentNames
703
+ : input.discovery(input.baseUrl, input.apiKey, input.apiKeyHeaderName, validationConfig.egress);
704
+ }
705
+ function finalRawConfigForSetup(input, testedModelIds) {
706
+ const embeddingModelIds = embeddingModelIdsFromDeployments(input.deploymentNames);
707
+ const configuredModelIds = mergeChatAndEmbeddingModelIds(testedModelIds, input.deploymentNames);
708
+ const rawConfig = buildRawConfig(input.baseUrl, input.apiKey, configuredModelIds, {
709
+ apiKeyHeaderName: input.apiKeyHeaderName,
710
+ imageInputModelIds: input.imageInputModelIds,
711
+ embeddingModelIds,
712
+ });
713
+ return {
714
+ ...rawConfig,
715
+ ...(input.current?.grounding === undefined ? {} : { grounding: input.current.grounding }),
716
+ ...(input.figmaAccessToken === undefined
717
+ ? {}
718
+ : { figma: { accessToken: input.figmaAccessToken } }),
719
+ };
720
+ }
721
+ async function verifySetupCandidate(input) {
510
722
  // Defence-in-depth: never send the credential to a candidate URL that has not passed the same
511
723
  // scheme/credential/loopback validation as the originally submitted base URL.
512
- validateBaseUrl(baseUrl, "candidate");
513
- const validationRawConfig = buildRawConfig(baseUrl, apiKey, ["setup-validation"], {
514
- apiKeyHeaderName,
515
- imageInputModelIds,
516
- });
517
- const validationConfig = parseGatewayConfig(withInheritedEgress(validationRawConfig, egress), env);
518
- const candidateModelIds = deploymentNames.length > 0
519
- ? deploymentNames
520
- : await discovery(baseUrl, apiKey, apiKeyHeaderName, validationConfig.egress);
521
- const smokeTimeoutMs = deploymentNames.length > 0 ? DEPLOYMENT_SMOKE_TIMEOUT_MS : DISCOVERED_MODEL_SMOKE_TIMEOUT_MS;
522
- const candidateRawConfig = buildRawConfig(baseUrl, apiKey, candidateModelIds, {
523
- apiKeyHeaderName,
724
+ validateBaseUrl(input.baseUrl, "candidate");
725
+ const validationConfig = validationConfigForSetup(input);
726
+ const candidateModelIds = await candidateModelIdsForSetup(input, validationConfig);
727
+ const smokeTimeoutMs = input.deploymentNames.length > 0
728
+ ? DEPLOYMENT_SMOKE_TIMEOUT_MS
729
+ : DISCOVERED_MODEL_SMOKE_TIMEOUT_MS;
730
+ const candidateRawConfig = buildRawConfig(input.baseUrl, input.apiKey, candidateModelIds, {
731
+ apiKeyHeaderName: input.apiKeyHeaderName,
524
732
  timeoutMs: smokeTimeoutMs,
525
733
  maxRetries: 0,
526
- imageInputModelIds,
527
- });
528
- const candidateConfig = parseGatewayConfig(withInheritedEgress(candidateRawConfig, egress), env);
529
- const testedModelIds = await tester(candidateConfig, candidateModelIds);
530
- assertImageInputModelsWereTested(imageInputModelIds, testedModelIds);
531
- const rawConfig = buildRawConfig(baseUrl, apiKey, testedModelIds, {
532
- apiKeyHeaderName,
533
- imageInputModelIds,
734
+ imageInputModelIds: input.imageInputModelIds,
534
735
  });
535
- const config = parseGatewayConfig(withInheritedEgress(rawConfig, egress), env);
536
- return { rawConfig, config, testedModelIds };
736
+ const candidateConfig = parseGatewayConfig(withInheritedEgress(candidateRawConfig, input.egress), input.env);
737
+ const testedModelIds = await input.tester(candidateConfig, candidateModelIds);
738
+ assertImageInputModelsWereTested(input.imageInputModelIds, testedModelIds);
739
+ const rawConfigWithOptionalBlocks = finalRawConfigForSetup(input, testedModelIds);
740
+ const config = parseGatewayConfig(withInheritedEgress(rawConfigWithOptionalBlocks, input.egress), input.env);
741
+ return { rawConfig: rawConfigWithOptionalBlocks, config, testedModelIds };
537
742
  }
538
743
  function setupSuccessResult(config, testedModelIds) {
539
744
  const testedModelId = testedModelIds[0] ?? "unknown";
@@ -555,6 +760,45 @@ function setupFailureResult(errors) {
555
760
  body: errorBody("GATEWAY_SETUP_FAILED", `Credentials could not be verified. ${errors.join(" ")}`),
556
761
  };
557
762
  }
763
+ function figmaFailureStatus(code) {
764
+ switch (code) {
765
+ case "FIGMA_TOKEN_INVALID":
766
+ case "FIGMA_TOKEN_EXPIRED":
767
+ case "FIGMA_TOKEN_REVOKED":
768
+ case "FIGMA_INSUFFICIENT_SCOPE":
769
+ case "FIGMA_CONSENT_REQUIRED":
770
+ return 400;
771
+ case "FIGMA_RATE_LIMITED":
772
+ return 429;
773
+ default:
774
+ return 502;
775
+ }
776
+ }
777
+ function figmaCredentialFailureResult(error, request) {
778
+ if (error instanceof FigmaConnectorError) {
779
+ return {
780
+ status: figmaFailureStatus(error.code),
781
+ body: errorBody(error.code, error.message),
782
+ };
783
+ }
784
+ return {
785
+ status: 502,
786
+ body: errorBody("FIGMA_EGRESS_FAILED", safeError(error, [request.figmaAccessToken, request.apiKey, request.baseUrl])),
787
+ };
788
+ }
789
+ async function verifySubmittedFigmaCredential(request, deps) {
790
+ if (!request.verifyFigmaCredential || request.figmaAccessToken === undefined) {
791
+ return undefined;
792
+ }
793
+ const tester = deps.figmaCredentialTester ?? defaultFigmaCredentialTester;
794
+ try {
795
+ await tester(request.figmaAccessToken, currentGatewayEgressConfig(deps));
796
+ return undefined;
797
+ }
798
+ catch (error) {
799
+ return figmaCredentialFailureResult(error, request);
800
+ }
801
+ }
558
802
  function deploymentNamesRequiredResult() {
559
803
  return {
560
804
  status: 400,
@@ -588,39 +832,60 @@ function gatewayUnavailableResult() {
588
832
  body: errorBody("GATEWAY_SETUP_UNAVAILABLE", "Gateway setup is unavailable."),
589
833
  };
590
834
  }
591
- async function trySetupCandidate(baseUrl, request, deps, gatewayConfig, tester, discovery) {
592
- const verified = await verifySetupCandidate(baseUrl, request.apiKey, request.apiKeyHeaderName, request.deploymentNames, request.imageInputModelIds, tester, discovery, deps.env, currentGatewayEgressConfig(deps));
835
+ async function trySetupCandidate(baseUrl, request, deps, gatewayConfig, tester, discovery, current) {
836
+ const verified = await verifySetupCandidate({
837
+ baseUrl,
838
+ apiKey: request.apiKey,
839
+ apiKeyHeaderName: request.apiKeyHeaderName,
840
+ deploymentNames: request.deploymentNames,
841
+ imageInputModelIds: request.imageInputModelIds,
842
+ tester,
843
+ discovery,
844
+ env: deps.env,
845
+ egress: currentGatewayEgressConfig(deps),
846
+ figmaAccessToken: request.figmaAccessToken,
847
+ current,
848
+ });
593
849
  savePrivateJson(gatewayConfig.storagePath, verified.rawConfig);
594
850
  gatewayConfig.set(verified.config, true);
595
851
  return setupSuccessResult(verified.config, verified.testedModelIds);
596
852
  }
597
853
  function setupCandidateError(error, request, baseUrl) {
598
- return safeError(error, [request.apiKey, request.baseUrl, baseUrl]);
854
+ return safeError(error, [request.apiKey, request.baseUrl, baseUrl, request.figmaAccessToken]);
599
855
  }
600
- export async function handleGatewaySetup(ctx, deps) {
601
- if (deps.gatewayConfig === undefined) {
602
- return gatewayUnavailableResult();
603
- }
604
- const { gatewayConfig } = deps;
605
- const bodyResult = await readJsonSetupBody(ctx);
606
- if ("status" in bodyResult) {
607
- return bodyResult;
608
- }
609
- const request = readSetupRequest(bodyResult.parsed, deps.env);
610
- if ("status" in request) {
611
- return request;
856
+ function saveExistingConfigUpdate(request, current, deps, gatewayConfig) {
857
+ const rawConfig = rawConfigFromCurrent(current, request.figmaAccessToken);
858
+ const config = parseGatewayConfig(withInheritedEgress(rawConfig, currentGatewayEgressConfig(deps)), deps.env);
859
+ savePrivateJson(gatewayConfig.storagePath, rawConfig);
860
+ gatewayConfig.set(config, true);
861
+ return setupSuccessResult(config, config.providers.map((provider) => provider.modelId));
862
+ }
863
+ async function verifyAndSaveExistingConfigUpdate(request, current, deps, gatewayConfig) {
864
+ const figmaFailure = await verifySubmittedFigmaCredential(request, deps);
865
+ if (figmaFailure !== undefined) {
866
+ return figmaFailure;
612
867
  }
868
+ return saveExistingConfigUpdate(request, current, deps, gatewayConfig);
869
+ }
870
+ function shouldRequireDeploymentNames(request, baseUrlCandidates) {
871
+ return (request.deploymentNames.length === 0 &&
872
+ baseUrlCandidates.some((baseUrl) => isAzureFoundryBaseUrl(baseUrl)));
873
+ }
874
+ async function verifyAndSaveGatewaySetup(request, current, deps, gatewayConfig) {
613
875
  const tester = deps.gatewaySetupTester ?? defaultGatewaySetupTester;
614
876
  const discovery = deps.gatewayModelDiscovery ?? defaultGatewayModelDiscovery;
877
+ const figmaFailure = await verifySubmittedFigmaCredential(request, deps);
878
+ if (figmaFailure !== undefined) {
879
+ return figmaFailure;
880
+ }
615
881
  const baseUrlCandidates = candidateBaseUrls(request.baseUrl);
616
- if (request.deploymentNames.length === 0 &&
617
- baseUrlCandidates.some((baseUrl) => isAzureFoundryBaseUrl(baseUrl))) {
882
+ if (shouldRequireDeploymentNames(request, baseUrlCandidates)) {
618
883
  return deploymentNamesRequiredResult();
619
884
  }
620
885
  const errors = [];
621
886
  for (const baseUrl of baseUrlCandidates) {
622
887
  try {
623
- return await trySetupCandidate(baseUrl, request, deps, gatewayConfig, tester, discovery);
888
+ return await trySetupCandidate(baseUrl, request, deps, gatewayConfig, tester, discovery, current);
624
889
  }
625
890
  catch (error) {
626
891
  errors.push(`candidate ${String(errors.length + 1)}: ${setupCandidateError(error, request, baseUrl)}`);
@@ -628,3 +893,22 @@ export async function handleGatewaySetup(ctx, deps) {
628
893
  }
629
894
  return setupFailureResult(errors);
630
895
  }
896
+ export async function handleGatewaySetup(ctx, deps) {
897
+ if (deps.gatewayConfig === undefined) {
898
+ return gatewayUnavailableResult();
899
+ }
900
+ const { gatewayConfig } = deps;
901
+ const current = currentGatewayConfig(deps);
902
+ const bodyResult = await readJsonSetupBody(ctx);
903
+ if ("status" in bodyResult) {
904
+ return bodyResult;
905
+ }
906
+ const request = readSetupRequest(bodyResult.parsed, deps.env, current);
907
+ if ("status" in request) {
908
+ return request;
909
+ }
910
+ if (!request.verifyGateway && current !== undefined) {
911
+ return verifyAndSaveExistingConfigUpdate(request, current, deps, gatewayConfig);
912
+ }
913
+ return verifyAndSaveGatewaySetup(request, current, deps, gatewayConfig);
914
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"memory-conv-handlers.d.ts","sourceRoot":"","sources":["../src/memory-conv-handlers.ts"],"names":[],"mappings":"AA0BA,OAAO,EAEL,KAAK,eAAe,EAErB,MAAM,sCAAsC,CAAC;AAe9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA6G7D,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,GAAG,eAAe,CAezE;AA8FD,wBAAsB,2BAA2B,CAC/C,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CAyCtB;AAkED,wBAAsB,mCAAmC,CACvD,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CA0BtB"}
1
+ {"version":3,"file":"memory-conv-handlers.d.ts","sourceRoot":"","sources":["../src/memory-conv-handlers.ts"],"names":[],"mappings":"AA0BA,OAAO,EAEL,KAAK,eAAe,EAErB,MAAM,sCAAsC,CAAC;AAgB9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAkH7D,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,GAAG,eAAe,CAezE;AAgJD,wBAAsB,2BAA2B,CAC/C,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CAwCtB;AAkED,wBAAsB,mCAAmC,CACvD,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CA0BtB"}
@@ -23,7 +23,7 @@
23
23
  // File budget: keep under 400 LOC per coordinator quality rules.
24
24
  import { randomUUID } from "node:crypto";
25
25
  import { retrieveMemoryContext, } from "@oscharko-dev/keiko-memory-retrieval";
26
- import { extractCandidatesFromUserText, } from "@oscharko-dev/keiko-memory-capture";
26
+ import { extractCandidatesFromUserText, memoryTextEgressRejectionReason, } from "@oscharko-dev/keiko-memory-capture";
27
27
  import { MEMORY_TYPES } from "@oscharko-dev/keiko-contracts";
28
28
  import { errorBody } from "./routes.js";
29
29
  import { createMemoryTargetResolver } from "./memory-target-resolver.js";
@@ -31,6 +31,7 @@ import { conversationMemoryScopes, resolveConversationMemoryContext, } from "./m
31
31
  import { recordMemoryAudit } from "./memory-audit-handler.js";
32
32
  import { buildMemoryRecordFromProposal } from "./memory-record-builders.js";
33
33
  import { enforcePersistableMemoryOutcome, isPersistableMemoryCandidate, memoryCapturePolicyForDeps, } from "./memory-capture-policy.js";
34
+ import { buildConversationRetrievalSignals, conversationFusionMode, } from "./memory-retrieval-signals.js";
34
35
  // ─── Constants ────────────────────────────────────────────────────────────────
35
36
  const MAX_BODY_BYTES = 64_000;
36
37
  // ─── Body reading (mirrors memory-handlers.ts pattern) ────────────────────────
@@ -197,11 +198,12 @@ function parseContextInput(raw) {
197
198
  budgetTokens: budgetTokens ?? undefined,
198
199
  };
199
200
  }
200
- function buildRetrievalRequest(scopes, input) {
201
+ function buildRetrievalRequest(scopes, input, nowMs, signals, fusion) {
201
202
  // exactOptionalPropertyTypes: omit undefined fields instead of assigning them.
202
203
  const req = {
203
204
  scopes,
204
- nowMs: Date.now(),
205
+ nowMs,
206
+ fusion,
205
207
  };
206
208
  if (input.queryText !== undefined)
207
209
  req.queryText = input.queryText;
@@ -209,8 +211,36 @@ function buildRetrievalRequest(scopes, input) {
209
211
  req.types = input.types;
210
212
  if (input.budgetTokens !== undefined)
211
213
  req.budgetTokens = input.budgetTokens;
214
+ // Embedding-cosine (#204, O-F4), reinforcement (O-P1), and MMR-diversity (O-F3) signals, same as
215
+ // the chat path. Passed only when present so a vault with no embeddings / no access history ranks
216
+ // byte-identically.
217
+ if (signals.semanticById !== undefined)
218
+ req.semanticById = signals.semanticById;
219
+ if (signals.strengthById.size > 0)
220
+ req.strengthById = signals.strengthById;
221
+ if (signals.embeddingById.size > 0)
222
+ req.embeddingById = signals.embeddingById;
212
223
  return req;
213
224
  }
225
+ // Builds the embedding + reinforcement signals, runs scoped retrieval, and records the reinforcement
226
+ // reflex — the same pipeline the chat path uses. Extracted so the route handler stays a thin
227
+ // parse/dispatch/audit shell.
228
+ async function retrieveConversationMemory(deps, vault, scopes, input) {
229
+ const port = vaultAsQueryPort(vault);
230
+ const nowMs = Date.now();
231
+ // Embedding egress gate (#204, O-F4): only send the query to the secondary embedding model when it
232
+ // is not secret-shaped — matching the chat path's safeForSecondaryModel guard.
233
+ const safeForSecondaryModel = input.queryText === undefined ||
234
+ memoryTextEgressRejectionReason(input.queryText, memoryCapturePolicyForDeps(deps)) === null;
235
+ const signals = await buildConversationRetrievalSignals(deps, vault, input.queryText, scopes, nowMs, safeForSecondaryModel);
236
+ const result = retrieveMemoryContext(buildRetrievalRequest(scopes, input, nowMs, signals, conversationFusionMode(deps)), port);
237
+ // Reinforcement reflex (#204, O-P1): every recall is an access, same as the chat path.
238
+ const accessedIds = result.included.map((item) => item.memoryId);
239
+ if (accessedIds.length > 0) {
240
+ vault.recordAccess(accessedIds, Date.now());
241
+ }
242
+ return result;
243
+ }
214
244
  export async function handleMemoryRetrieveContext(ctx, deps) {
215
245
  const vault = resolveVault(deps);
216
246
  if (isRouteResult(vault))
@@ -225,8 +255,7 @@ export async function handleMemoryRetrieveContext(ctx, deps) {
225
255
  if (isRouteResult(context))
226
256
  return context;
227
257
  const scopes = conversationMemoryScopes(context);
228
- const port = vaultAsQueryPort(vault);
229
- const result = retrieveMemoryContext(buildRetrievalRequest(scopes, input), port);
258
+ const result = await retrieveConversationMemory(deps, vault, scopes, input);
230
259
  if (result.included.length > 0) {
231
260
  const event = {
232
261
  schemaVersion: "1",
@@ -1,6 +1,6 @@
1
1
  import { type GatewayConfig, type OpenAIEmbeddingOutcome, type OpenAIEmbeddingRequest } from "@oscharko-dev/keiko-model-gateway";
2
- import type { MemoryId } from "@oscharko-dev/keiko-contracts/memory";
3
- import type { MemoryEmbeddingInput, MemoryVaultStore } from "@oscharko-dev/keiko-memory-vault";
2
+ import type { MemoryId, MemoryRecord } from "@oscharko-dev/keiko-contracts/memory";
3
+ import type { MemoryEmbeddingInput, MemoryEmbeddingRow, MemoryVaultStore } from "@oscharko-dev/keiko-memory-vault";
4
4
  import { type UiHandlerDeps } from "./deps.js";
5
5
  export declare function selectMemoryEmbeddingModelId(config: GatewayConfig | undefined): string | undefined;
6
6
  export type MemoryEmbedder = (text: string) => Promise<MemoryEmbeddingInput | null>;
@@ -8,4 +8,14 @@ export declare function createMemoryEmbedder(config: GatewayConfig | undefined,
8
8
  export declare function embedMemoryText(deps: UiHandlerDeps, text: string): Promise<MemoryEmbeddingInput | null>;
9
9
  export declare function embedAndStoreMemory(deps: UiHandlerDeps, vault: MemoryVaultStore, memoryId: MemoryId, text: string): Promise<void>;
10
10
  export declare function cosineSimilarity(a: Float32Array, b: Float32Array): number;
11
+ export declare const SEMANTIC_DEDUP_COSINE_THRESHOLD = 0.95;
12
+ export declare function findSemanticDuplicate(candidate: MemoryEmbeddingInput | null, neighbors: ReadonlyMap<MemoryId, MemoryEmbeddingRow>, threshold?: number): MemoryId | null;
13
+ export declare const RELATED_LINK_COSINE_THRESHOLD = 0.82;
14
+ export declare const MAX_AUTO_LINKS = 3;
15
+ export declare function findRelatedNeighbors(candidate: MemoryEmbeddingInput | null, neighbors: ReadonlyMap<MemoryId, MemoryEmbeddingRow>, lower?: number, upper?: number, maxLinks?: number): readonly MemoryId[];
16
+ export interface NoveltyInsertResult {
17
+ readonly inserted: MemoryRecord | null;
18
+ readonly mergedInto: MemoryId | null;
19
+ }
20
+ export declare function insertSalienceMemoryWithNoveltyGate(deps: UiHandlerDeps, vault: MemoryVaultStore, record: MemoryRecord): Promise<NoveltyInsertResult>;
11
21
  //# sourceMappingURL=memory-embedding.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"memory-embedding.d.ts","sourceRoot":"","sources":["../src/memory-embedding.ts"],"names":[],"mappings":"AAgBA,OAAO,EAEL,KAAK,aAAa,EAGlB,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC5B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AACrE,OAAO,KAAK,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAC/F,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAIrE,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,aAAa,GAAG,SAAS,GAChC,MAAM,GAAG,SAAS,CAEpB;AAyDD,MAAM,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;AAKpF,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,aAAa,GAAG,SAAS,EACjC,WAAW,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,OAAO,CAAC,sBAAsB,CAAC,GAChF,cAAc,GAAG,IAAI,CAuBvB;AAKD,wBAAsB,eAAe,CACnC,IAAI,EAAE,aAAa,EACnB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAItC;AAKD,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,aAAa,EACnB,KAAK,EAAE,gBAAgB,EACvB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAQf;AAKD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,GAAG,MAAM,CAgBzE"}
1
+ {"version":3,"file":"memory-embedding.d.ts","sourceRoot":"","sources":["../src/memory-embedding.ts"],"names":[],"mappings":"AAgBA,OAAO,EAEL,KAAK,aAAa,EAGlB,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC5B,MAAM,mCAAmC,CAAC;AAE3C,OAAO,KAAK,EAEV,QAAQ,EACR,YAAY,EAEb,MAAM,sCAAsC,CAAC;AAC9C,OAAO,KAAK,EACV,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAIrE,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,aAAa,GAAG,SAAS,GAChC,MAAM,GAAG,SAAS,CAEpB;AAyDD,MAAM,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;AAKpF,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,aAAa,GAAG,SAAS,EACjC,WAAW,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,OAAO,CAAC,sBAAsB,CAAC,GAChF,cAAc,GAAG,IAAI,CAuBvB;AAKD,wBAAsB,eAAe,CACnC,IAAI,EAAE,aAAa,EACnB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAItC;AAKD,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,aAAa,EACnB,KAAK,EAAE,gBAAgB,EACvB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAQf;AAKD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,GAAG,MAAM,CAgBzE;AAiBD,eAAO,MAAM,+BAA+B,OAAO,CAAC;AAKpD,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,oBAAoB,GAAG,IAAI,EACtC,SAAS,EAAE,WAAW,CAAC,QAAQ,EAAE,kBAAkB,CAAC,EACpD,SAAS,GAAE,MAAwC,GAClD,QAAQ,GAAG,IAAI,CAYjB;AAeD,eAAO,MAAM,6BAA6B,OAAO,CAAC;AAGlD,eAAO,MAAM,cAAc,IAAI,CAAC;AAMhC,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,oBAAoB,GAAG,IAAI,EACtC,SAAS,EAAE,WAAW,CAAC,QAAQ,EAAE,kBAAkB,CAAC,EACpD,KAAK,GAAE,MAAsC,EAC7C,KAAK,GAAE,MAAwC,EAC/C,QAAQ,GAAE,MAAuB,GAChC,SAAS,QAAQ,EAAE,CAWrB;AA+CD,MAAM,WAAW,mBAAmB;IAElC,QAAQ,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IAEvC,QAAQ,CAAC,UAAU,EAAE,QAAQ,GAAG,IAAI,CAAC;CACtC;AAOD,wBAAsB,mCAAmC,CACvD,IAAI,EAAE,aAAa,EACnB,KAAK,EAAE,gBAAgB,EACvB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,mBAAmB,CAAC,CAoB9B"}