@oscharko-dev/keiko-server 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import { statSync } from "node:fs";
3
3
  import { basename, dirname } from "node:path";
4
- import { addSourceToCapsule, checkpointToProgress, composeCapsules, CompositionError, createSqliteAuditSink, createDefaultParserRegistry, createCapsule, deleteCapsule, getCapsule, listCapsuleSets, listCapsuleSources, listCapsules, listExtractionCheckpoints, listResumableDocuments, openKnowledgeStore, removeSourceFromCapsule, resolveKnowledgeStorePath, runIndexingJob, updateCapsuleDetails, updateCapsuleState, } from "@oscharko-dev/keiko-local-knowledge";
4
+ import { addSourceToCapsule, checkpointToProgress, composeCapsules, CompositionError, createSqliteAuditSink, createDefaultParserRegistry, createCapsule, deleteCapsule, getCapsule, listCapsuleSets, listCapsuleSources, listCapsules, listExtractionCheckpoints, listResumableDocuments, openKnowledgeStore, removeSourceFromCapsule, resolveKnowledgeStorePath, runIndexingJob, updateCapsuleEmbeddingModelIdentity, updateCapsuleDetails, updateCapsuleState, } from "@oscharko-dev/keiko-local-knowledge";
5
5
  import { KnowledgeNotFoundError, KnowledgeStoreError } from "@oscharko-dev/keiko-local-knowledge";
6
6
  import { localKnowledgeProtectionOptions } from "./localKnowledgeKeyProvider.js";
7
7
  import { CAPSULE_SET_MAX_MEMBERS, DEFAULT_EXTRACTION_CAPABILITY_AVAILABILITY, DEFAULT_LARGE_DOCUMENT_RESOURCE_POLICY, isSafeDisplaySummary, isSafeQualityWarning, validateCapsuleReindexRequest, validateKnowledgeSourceScope, } from "@oscharko-dev/keiko-contracts";
@@ -218,6 +218,52 @@ function configuredProviderForCapsule(deps, capsule) {
218
218
  ? provider
219
219
  : undefined;
220
220
  }
221
+ function embeddingIdentityMatchesCapsuleAlias(stored, current) {
222
+ if (stored.provider !== current.provider ||
223
+ stored.vectorDimensions !== current.vectorDimensions ||
224
+ stored.vectorMetric !== current.vectorMetric) {
225
+ return false;
226
+ }
227
+ if (stored.modelId === current.modelId) {
228
+ return true;
229
+ }
230
+ return current.modelRevision !== undefined && stored.modelId === current.modelRevision;
231
+ }
232
+ async function probeConfiguredProviderForCapsule(deps, provider, capsule) {
233
+ const adapter = createEmbeddingAdapter(provider, requestEmbeddingImpl(deps), requestEmbeddingBatchImpl(deps));
234
+ try {
235
+ const result = await verifyEmbeddingCapability(adapter, {
236
+ modelId: provider.modelId,
237
+ provider: embeddingProviderIdentity(provider),
238
+ vectorMetric: capsule.embeddingModelIdentity.vectorMetric,
239
+ expectedDimensions: capsule.embeddingModelIdentity.vectorDimensions,
240
+ timeoutMs: provider.timeoutMs,
241
+ });
242
+ return result.ok ? result.identity : undefined;
243
+ }
244
+ catch {
245
+ return undefined;
246
+ }
247
+ }
248
+ async function resolveIndexingProviderForCapsule(deps, store, capsule) {
249
+ const exact = configuredProviderForCapsule(deps, capsule);
250
+ if (exact !== undefined) {
251
+ return { capsule, provider: exact };
252
+ }
253
+ const providers = configuredEmbeddingProviders(currentGatewayConfig(deps));
254
+ for (const provider of providers) {
255
+ if (!storedProviderMatchesConfiguredProvider(capsule.embeddingModelIdentity.provider, provider)) {
256
+ continue;
257
+ }
258
+ const identity = await probeConfiguredProviderForCapsule(deps, provider, capsule);
259
+ if (identity !== undefined &&
260
+ embeddingIdentityMatchesCapsuleAlias(capsule.embeddingModelIdentity, identity)) {
261
+ const repaired = updateCapsuleEmbeddingModelIdentity(store, capsule.id, identity);
262
+ return { capsule: repaired, provider };
263
+ }
264
+ }
265
+ return undefined;
266
+ }
221
267
  function embeddingCompatibilityReason(config, capsule) {
222
268
  if (config === undefined)
223
269
  return undefined;
@@ -656,10 +702,19 @@ function storedProviderMatchesConfiguredProvider(storedProvider, provider) {
656
702
  // Issue #621 / #677: select the first provider whose resolved capability is embedding-capable.
657
703
  // Falling back to a chat model creates capsules that can never index successfully.
658
704
  export function selectEmbeddingModelId(config) {
705
+ return configuredEmbeddingModelIds(config)[0];
706
+ }
707
+ export function configuredEmbeddingModelIds(config) {
659
708
  if (config === undefined || config === null || config.providers.length === 0)
660
- return undefined;
661
- return config.providers.find((provider) => isConfiguredEmbeddingModel(config, provider.modelId))
662
- ?.modelId;
709
+ return [];
710
+ return config.providers
711
+ .filter((provider) => isConfiguredEmbeddingModel(config, provider.modelId))
712
+ .map((provider) => provider.modelId);
713
+ }
714
+ export function configuredEmbeddingProviders(config) {
715
+ if (config === undefined || config.providers.length === 0)
716
+ return [];
717
+ return config.providers.filter((provider) => isConfiguredEmbeddingModel(config, provider.modelId));
663
718
  }
664
719
  function createCapsuleStorageReference(capsuleId) {
665
720
  return `capsules/${capsuleId}`;
@@ -687,21 +742,28 @@ async function verifiedNewCapsuleEmbeddingIdentity(deps, provider) {
687
742
  }
688
743
  async function resolveNewCapsuleEmbeddingIdentity(deps) {
689
744
  const config = currentGatewayConfig(deps);
690
- const configuredModelId = selectEmbeddingModelId(config);
691
- if (configuredModelId === undefined) {
745
+ const providers = configuredEmbeddingProviders(config);
746
+ if (providers.length === 0) {
692
747
  return {
693
748
  ok: false,
694
749
  result: conflict("No configured embedding-capable model is available for new capsules. Configure the Model Gateway first."),
695
750
  };
696
751
  }
697
- const provider = configuredEmbeddingProvider(config, configuredModelId);
698
- if (provider === undefined) {
699
- return {
700
- ok: false,
701
- result: conflict("No configured embedding-capable model is available for new capsules. Configure the Model Gateway first."),
702
- };
752
+ let lastFailure;
753
+ for (const provider of providers) {
754
+ const verified = await verifiedNewCapsuleEmbeddingIdentity(deps, provider);
755
+ if (verified.ok) {
756
+ return verified;
757
+ }
758
+ lastFailure = verified.result;
703
759
  }
704
- return verifiedNewCapsuleEmbeddingIdentity(deps, provider);
760
+ if (lastFailure !== undefined) {
761
+ return { ok: false, result: lastFailure };
762
+ }
763
+ return {
764
+ ok: false,
765
+ result: conflict("No configured embedding-capable model is available for new capsules. Configure the Model Gateway first."),
766
+ };
705
767
  }
706
768
  function latestRunningJobId(store, capsuleId) {
707
769
  const row = store._internal.db
@@ -764,6 +826,9 @@ function indexingCompletionResponse(capsuleId, terminal, failedMessage) {
764
826
  }
765
827
  return actionResponse(capsuleId);
766
828
  }
829
+ function runningIndexingJobConflict(capsuleId, runningJobId) {
830
+ return indexingConflict("indexing-already-running", "An indexing job is already running for this capsule.", capsuleId, runningJobId);
831
+ }
767
832
  function buildIndexingOptions(store, capsule, adapter, options, sourceSelection, signal) {
768
833
  return {
769
834
  capsuleId: capsule.id,
@@ -1058,21 +1123,22 @@ export async function handleStartLocalKnowledgeCapsuleIndexing(ctx, deps) {
1058
1123
  if (capsule.sourceIds.length === 0) {
1059
1124
  return emptyCapsuleIndexingConflict();
1060
1125
  }
1061
- if (configuredProviderForCapsule(deps, capsule) === undefined) {
1126
+ const resolved = await resolveIndexingProviderForCapsule(deps, env.store, capsule);
1127
+ if (resolved === undefined) {
1062
1128
  return conflict("No configured embedding-capable model matches this capsule. Update the Model Gateway configuration before indexing it.");
1063
1129
  }
1064
1130
  // LK-003 (Epic #189): refuse to start a second concurrent indexer for the same
1065
1131
  // capsule — the orchestrator persists running jobs, so a duplicate POST would
1066
1132
  // race the in-flight one and corrupt vector counts.
1067
- const runningJobId = latestRunningJobId(env.store, capsule.id);
1133
+ const runningJobId = latestRunningJobId(env.store, resolved.capsule.id);
1068
1134
  if (runningJobId !== undefined) {
1069
- return indexingConflict("indexing-already-running", "An indexing job is already running for this capsule.", capsule.id, runningJobId);
1135
+ return runningIndexingJobConflict(resolved.capsule.id, runningJobId);
1070
1136
  }
1071
- const terminal = await runCapsuleIndexingJob(deps, env.store, capsule, {
1137
+ const terminal = await runCapsuleIndexingJob(deps, env.store, resolved.capsule, {
1072
1138
  mode: undefined,
1073
1139
  force: false,
1074
1140
  });
1075
- return indexingCompletionResponse(capsule.id, terminal, "Capsule indexing failed. Review the capsule health diagnostics and job history for details.");
1141
+ return indexingCompletionResponse(resolved.capsule.id, terminal, "Capsule indexing failed. Review the capsule health diagnostics and job history for details.");
1076
1142
  }
1077
1143
  finally {
1078
1144
  env.close();
@@ -1264,19 +1330,20 @@ export async function handleReindexLocalKnowledgeCapsule(ctx, deps) {
1264
1330
  if (capsule.sourceIds.length === 0) {
1265
1331
  return emptyCapsuleIndexingConflict();
1266
1332
  }
1267
- if (configuredProviderForCapsule(deps, capsule) === undefined) {
1333
+ const resolved = await resolveIndexingProviderForCapsule(deps, env.store, capsule);
1334
+ if (resolved === undefined) {
1268
1335
  return conflict("No configured embedding-capable model matches this capsule. Update the Model Gateway configuration before refreshing it.");
1269
1336
  }
1270
1337
  // LK-003 (Epic #189): same concurrent-run guard as the start handler.
1271
- const runningJobId = latestRunningJobId(env.store, capsule.id);
1338
+ const runningJobId = latestRunningJobId(env.store, resolved.capsule.id);
1272
1339
  if (runningJobId !== undefined) {
1273
- return indexingConflict("indexing-already-running", "An indexing job is already running for this capsule.", capsule.id, runningJobId);
1340
+ return runningIndexingJobConflict(resolved.capsule.id, runningJobId);
1274
1341
  }
1275
- const terminal = await runCapsuleIndexingJob(deps, env.store, capsule, {
1342
+ const terminal = await runCapsuleIndexingJob(deps, env.store, resolved.capsule, {
1276
1343
  mode,
1277
1344
  force,
1278
1345
  });
1279
- return indexingCompletionResponse(capsule.id, terminal, "Capsule refresh failed. Review the capsule health diagnostics and job history for details.");
1346
+ return indexingCompletionResponse(resolved.capsule.id, terminal, "Capsule refresh failed. Review the capsule health diagnostics and job history for details.");
1280
1347
  }
1281
1348
  finally {
1282
1349
  env.close();
@@ -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;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"}
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;AAOrE,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,aAAa,GAAG,SAAS,GAChC,MAAM,GAAG,SAAS,CAEpB;AAkDD,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,CAyBvB;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"}
@@ -16,14 +16,11 @@
16
16
  import { requestOpenAIEmbedding, } from "@oscharko-dev/keiko-model-gateway";
17
17
  import { randomUUID } from "node:crypto";
18
18
  import { currentGatewayConfig } from "./deps.js";
19
- import { selectEmbeddingModelId } from "./local-knowledge-handlers.js";
19
+ import { configuredEmbeddingProviders, selectEmbeddingModelId, } from "./local-knowledge-handlers.js";
20
20
  const MEMORY_VECTOR_METRIC = "cosine";
21
21
  export function selectMemoryEmbeddingModelId(config) {
22
22
  return selectEmbeddingModelId(config);
23
23
  }
24
- function providerForModel(config, modelId) {
25
- return config?.providers.find((provider) => provider.modelId === modelId);
26
- }
27
24
  function requestEmbeddingImpl(deps) {
28
25
  // Reuses the same gateway seam as Local Knowledge so a single injected adapter drives both.
29
26
  return deps.localKnowledgeEmbeddingRequest ?? requestOpenAIEmbedding;
@@ -62,32 +59,33 @@ function toEmbeddingInput(provider, outcome) {
62
59
  // configured (or its provider is absent). The CLI backfill and the conversation paths both compose
63
60
  // through this single factory so capability-aware model selection lives in one place.
64
61
  export function createMemoryEmbedder(config, requestImpl) {
65
- const modelId = selectMemoryEmbeddingModelId(config);
66
- if (modelId === undefined)
67
- return null;
68
- const provider = providerForModel(config, modelId);
69
- if (provider === undefined)
62
+ const providers = configuredEmbeddingProviders(config);
63
+ if (providers.length === 0)
70
64
  return null;
71
- const adapter = buildAdapter(provider, requestImpl);
65
+ const candidates = providers.map((provider) => ({
66
+ provider,
67
+ adapter: buildAdapter(provider, requestImpl),
68
+ }));
72
69
  return async (text) => {
73
70
  if (text.length === 0)
74
71
  return null;
75
- try {
76
- const outcome = await adapter.request({
77
- endpoint: provider.baseUrl,
78
- apiKey: provider.apiKey,
79
- modelId,
80
- input: text,
81
- ...(provider.egress !== undefined ? { egress: provider.egress } : {}),
82
- });
83
- if (!outcome.ok)
84
- return null;
85
- return toEmbeddingInput("openai", outcome);
86
- }
87
- catch {
88
- // Model/transport boundary: a thrown adapter must degrade to "no embedding", never crash.
89
- return null;
72
+ for (const { provider, adapter } of candidates) {
73
+ try {
74
+ const outcome = await adapter.request({
75
+ endpoint: provider.baseUrl,
76
+ apiKey: provider.apiKey,
77
+ modelId: provider.modelId,
78
+ input: text,
79
+ ...(provider.egress !== undefined ? { egress: provider.egress } : {}),
80
+ });
81
+ if (outcome.ok)
82
+ return toEmbeddingInput("openai", outcome);
83
+ }
84
+ catch {
85
+ // Model/transport boundary: a thrown adapter degrades to the next embedding provider.
86
+ }
90
87
  }
88
+ return null;
91
89
  };
92
90
  }
93
91
  // Embeds `text` against the configured embedding model. Returns null when no embedding-capable
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscharko-dev/keiko-server",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "description": "Internal server package: local loopback BFF runtime (HTTP/SSE/WebSocket router, CSP, host check, CSRF gate, terminal, browser, files, run engine, SQLite-backed UI store) that mediates the browser presentation tier and the Node-side domain packages (ADR-0019). Not published independently.",
@@ -29,24 +29,24 @@
29
29
  "node": ">=22"
30
30
  },
31
31
  "dependencies": {
32
- "@oscharko-dev/keiko-contracts": "0.2.0",
33
- "@oscharko-dev/keiko-security": "0.2.0",
34
- "@oscharko-dev/keiko-model-gateway": "0.2.0",
35
- "@oscharko-dev/keiko-quality-intelligence": "0.2.0",
36
- "@oscharko-dev/keiko-local-knowledge": "0.2.0",
37
- "@oscharko-dev/keiko-workspace": "0.2.0",
38
- "@oscharko-dev/keiko-sandbox": "0.2.0",
39
- "@oscharko-dev/keiko-tools": "0.2.0",
40
- "@oscharko-dev/keiko-evidence": "0.2.0",
41
- "@oscharko-dev/keiko-verification": "0.2.0",
42
- "@oscharko-dev/keiko-harness": "0.2.0",
43
- "@oscharko-dev/keiko-sdk": "0.2.0",
44
- "@oscharko-dev/keiko-workflows": "0.2.0",
45
- "@oscharko-dev/keiko-memory-vault": "0.2.0",
46
- "@oscharko-dev/keiko-memory-governance": "0.2.0",
47
- "@oscharko-dev/keiko-memory-retrieval": "0.2.0",
48
- "@oscharko-dev/keiko-memory-capture": "0.2.0",
49
- "@oscharko-dev/keiko-memory-consolidation": "0.2.0",
32
+ "@oscharko-dev/keiko-contracts": "0.2.2",
33
+ "@oscharko-dev/keiko-security": "0.2.2",
34
+ "@oscharko-dev/keiko-model-gateway": "0.2.2",
35
+ "@oscharko-dev/keiko-quality-intelligence": "0.2.2",
36
+ "@oscharko-dev/keiko-local-knowledge": "0.2.2",
37
+ "@oscharko-dev/keiko-workspace": "0.2.2",
38
+ "@oscharko-dev/keiko-sandbox": "0.2.2",
39
+ "@oscharko-dev/keiko-tools": "0.2.2",
40
+ "@oscharko-dev/keiko-evidence": "0.2.2",
41
+ "@oscharko-dev/keiko-verification": "0.2.2",
42
+ "@oscharko-dev/keiko-harness": "0.2.2",
43
+ "@oscharko-dev/keiko-sdk": "0.2.2",
44
+ "@oscharko-dev/keiko-workflows": "0.2.2",
45
+ "@oscharko-dev/keiko-memory-vault": "0.2.2",
46
+ "@oscharko-dev/keiko-memory-governance": "0.2.2",
47
+ "@oscharko-dev/keiko-memory-retrieval": "0.2.2",
48
+ "@oscharko-dev/keiko-memory-capture": "0.2.2",
49
+ "@oscharko-dev/keiko-memory-consolidation": "0.2.2",
50
50
  "typescript": "^6.0.3"
51
51
  }
52
52
  }