@hasna/knowledge 0.2.10 → 0.2.12

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.
package/README.md CHANGED
@@ -62,6 +62,9 @@ open-knowledge export --format jsonl
62
62
  # Show resolved workspace paths
63
63
  open-knowledge paths --scope project --json
64
64
 
65
+ # Inspect local/S3 artifact storage and source ownership
66
+ open-knowledge storage status --scope project --json
67
+
65
68
  # Initialize the project SQLite catalog
66
69
  open-knowledge db init --scope project
67
70
 
@@ -82,6 +85,10 @@ open-knowledge source resolve open-files://file/f_123/revision/rev_456 --scope p
82
85
 
83
86
  # Inspect local safety policy and approvals
84
87
  open-knowledge safety status --scope project --json
88
+
89
+ # Inspect AI SDK provider credentials and model aliases
90
+ open-knowledge providers status --scope project --json
91
+ open-knowledge providers models --scope project --json
85
92
  ```
86
93
 
87
94
  ## Commands
@@ -164,6 +171,18 @@ open-knowledge paths [--scope global|project|local] [--json]
164
171
  Show the resolved Hasna app workspace, JSON compatibility store, SQLite path,
165
172
  artifact directories, and config.
166
173
 
174
+ ### storage
175
+ ```bash
176
+ open-knowledge storage status [--scope project] [--json]
177
+ open-knowledge storage validate [--scope project] [--json]
178
+ ```
179
+ Show the storage contract for local or S3-backed generated artifacts. Local mode
180
+ uses `.hasna/apps/knowledge` for config, SQLite, indexes, wiki artifacts, logs,
181
+ runs, and exports. S3 mode stores generated artifacts under the configured
182
+ knowledge bucket/prefix while `open-files` remains the source of truth for raw
183
+ source bytes. The command also reports artifact classes, allowed source ref
184
+ schemes, and warnings for non-scalable or unsafe config.
185
+
167
186
  ### db
168
187
  ```bash
169
188
  open-knowledge db init [--scope project]
@@ -226,6 +245,19 @@ Inspect and operate the local safety model. Source reads are read-only by
226
245
  default, web search and S3 reads are opt-in, generated writes require approval
227
246
  by default, and known secret patterns are redacted before chunk storage.
228
247
 
248
+ ### providers
249
+ ```bash
250
+ open-knowledge providers status [--scope project] [--json]
251
+ open-knowledge providers models [--scope project] [--json]
252
+ open-knowledge providers check [provider|model-alias] [--scope project] [--json]
253
+ ```
254
+ Inspect AI SDK v6 provider readiness for OpenAI, Anthropic, and DeepSeek. The
255
+ provider layer resolves BYOK credentials from `OPENAI_API_KEY`,
256
+ `ANTHROPIC_API_KEY`, and `DEEPSEEK_API_KEY` by default, exposes model aliases
257
+ such as `default`, `fast`, `reasoning`, `sonnet`, and `deepseek`, and records
258
+ provider capability metadata for structured output, tool use, tool streaming,
259
+ reasoning, embeddings, and native web-search support.
260
+
229
261
  ### help
230
262
  ```bash
231
263
  open-knowledge help [command]
@@ -260,8 +292,9 @@ open-knowledge-mcp
260
292
  The MCP server exposes item tools (`ok_add`, `ok_list`, `ok_get`, `ok_update`,
261
293
  `ok_delete`, `ok_archive`, `ok_restore`, `ok_upsert`, `ok_untag`,
262
294
  `ok_bulk_delete`, `ok_prune`, `ok_dedupe`, `ok_stats`, `ok_export`,
263
- `ok_import`, `ok_batch`), workspace inspection (`ok_paths`), and source-ref
264
- parsing/resolution (`ok_parse_source_ref`, `ok_resolve_source`).
295
+ `ok_import`, `ok_batch`), workspace/storage inspection (`ok_paths`,
296
+ `ok_storage_status`), and source-ref parsing/resolution
297
+ (`ok_parse_source_ref`, `ok_resolve_source`).
265
298
 
266
299
  ## Source And Artifact Boundary
267
300
 
@@ -281,6 +314,11 @@ source ref. It does not copy raw files into the knowledge workspace; local file,
281
314
  S3, web, and open-files inputs are converted into redacted chunks with offsets,
282
315
  hashes, revision metadata, and FTS rows.
283
316
 
317
+ AI provider configuration is local/BYOK by default. `open-knowledge` declares
318
+ AI SDK v6 provider support through `ai`, `@ai-sdk/openai`,
319
+ `@ai-sdk/anthropic`, and `@ai-sdk/deepseek`, but does not call providers until a
320
+ future prompt/agent command explicitly requests a model.
321
+
284
322
  Generated knowledge artifacts can be stored locally under
285
323
  `.hasna/apps/knowledge/artifacts` or through the S3 artifact-store adapter.
286
324
 
@@ -13660,7 +13660,7 @@ import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync
13660
13660
  // package.json
13661
13661
  var package_default = {
13662
13662
  name: "@hasna/knowledge",
13663
- version: "0.2.10",
13663
+ version: "0.2.12",
13664
13664
  description: "Agent-friendly local knowledge CLI with JSON output, pagination, and safe destructive actions",
13665
13665
  type: "module",
13666
13666
  bin: {
@@ -13677,7 +13677,7 @@ var package_default = {
13677
13677
  scripts: {
13678
13678
  test: "bun test",
13679
13679
  "test:cli": "bun test tests/cli.test.ts",
13680
- build: "bun build --target=bun --outfile=bin/open-knowledge.js --minify --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers src/cli.ts && bun build --target=bun --outfile=bin/open-knowledge-mcp.js --external @modelcontextprotocol/sdk --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers src/mcp.js",
13680
+ build: "bun build --target=bun --outfile=bin/open-knowledge.js --minify --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/cli.ts && bun build --target=bun --outfile=bin/open-knowledge-mcp.js --external @modelcontextprotocol/sdk --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/mcp.js",
13681
13681
  prepublishOnly: "bun run build",
13682
13682
  postinstall: "bun run build"
13683
13683
  },
@@ -13710,7 +13710,11 @@ var package_default = {
13710
13710
  dependencies: {
13711
13711
  "@aws-sdk/client-s3": "^3.1063.0",
13712
13712
  "@aws-sdk/credential-providers": "^3.1063.0",
13713
+ "@ai-sdk/anthropic": "^3.0.81",
13714
+ "@ai-sdk/deepseek": "^2.0.35",
13715
+ "@ai-sdk/openai": "^3.0.68",
13713
13716
  "@modelcontextprotocol/sdk": "^1.29.0",
13717
+ ai: "^6.0.197",
13714
13718
  zod: "^4.3.6"
13715
13719
  },
13716
13720
  devDependencies: {
@@ -13764,6 +13768,28 @@ function defaultKnowledgeConfig() {
13764
13768
  preferred_ref: "open-files",
13765
13769
  allowed_schemes: ["open-files", "s3", "file", "https", "http"]
13766
13770
  },
13771
+ providers: {
13772
+ default_model: "openai:gpt-5.2",
13773
+ aliases: {
13774
+ fast: "openai:gpt-5-mini",
13775
+ reasoning: "anthropic:claude-opus-4-6",
13776
+ sonnet: "anthropic:claude-sonnet-4-6",
13777
+ deepseek: "deepseek:deepseek-chat",
13778
+ "deepseek-reasoning": "deepseek:deepseek-reasoner"
13779
+ },
13780
+ openai: {
13781
+ api_key_env: "OPENAI_API_KEY",
13782
+ default_model: "gpt-5.2"
13783
+ },
13784
+ anthropic: {
13785
+ api_key_env: "ANTHROPIC_API_KEY",
13786
+ default_model: "claude-sonnet-4-6"
13787
+ },
13788
+ deepseek: {
13789
+ api_key_env: "DEEPSEEK_API_KEY",
13790
+ default_model: "deepseek-chat"
13791
+ }
13792
+ },
13767
13793
  safety: {
13768
13794
  network: {
13769
13795
  web_search_enabled: false,
@@ -14365,7 +14391,8 @@ function getKnowledgeDbStats(path) {
14365
14391
  run_events: count(db, "run_events"),
14366
14392
  redaction_findings: count(db, "redaction_findings"),
14367
14393
  audit_events: count(db, "audit_events"),
14368
- approval_gates: count(db, "approval_gates")
14394
+ approval_gates: count(db, "approval_gates"),
14395
+ storage_objects: count(db, "storage_objects")
14369
14396
  };
14370
14397
  } finally {
14371
14398
  db.close();
@@ -15773,6 +15800,300 @@ async function ingestSourceRef(options) {
15773
15800
  };
15774
15801
  }
15775
15802
 
15803
+ // src/providers.ts
15804
+ var DEFAULT_PROVIDER_SETTINGS = {
15805
+ openai: {
15806
+ api_key_env: "OPENAI_API_KEY",
15807
+ default_model: "gpt-5.2"
15808
+ },
15809
+ anthropic: {
15810
+ api_key_env: "ANTHROPIC_API_KEY",
15811
+ default_model: "claude-sonnet-4-6"
15812
+ },
15813
+ deepseek: {
15814
+ api_key_env: "DEEPSEEK_API_KEY",
15815
+ default_model: "deepseek-chat"
15816
+ }
15817
+ };
15818
+ var PROVIDER_CAPABILITIES = {
15819
+ openai: {
15820
+ text_generation: true,
15821
+ structured_output: true,
15822
+ tool_usage: true,
15823
+ tool_streaming: true,
15824
+ image_input: true,
15825
+ native_web_search: true,
15826
+ reasoning: true,
15827
+ embeddings: true
15828
+ },
15829
+ anthropic: {
15830
+ text_generation: true,
15831
+ structured_output: true,
15832
+ tool_usage: true,
15833
+ tool_streaming: true,
15834
+ image_input: true,
15835
+ native_web_search: false,
15836
+ reasoning: true,
15837
+ embeddings: false
15838
+ },
15839
+ deepseek: {
15840
+ text_generation: true,
15841
+ structured_output: true,
15842
+ tool_usage: true,
15843
+ tool_streaming: true,
15844
+ image_input: false,
15845
+ native_web_search: false,
15846
+ reasoning: true,
15847
+ embeddings: false
15848
+ }
15849
+ };
15850
+ var BUILTIN_ALIASES = {
15851
+ default: "openai:gpt-5.2",
15852
+ fast: "openai:gpt-5-mini",
15853
+ reasoning: "anthropic:claude-opus-4-6",
15854
+ sonnet: "anthropic:claude-sonnet-4-6",
15855
+ deepseek: "deepseek:deepseek-chat",
15856
+ "deepseek-reasoning": "deepseek:deepseek-reasoner"
15857
+ };
15858
+ function providerConfig(config2) {
15859
+ return config2.providers ?? {};
15860
+ }
15861
+ function providerSettings(config2, provider) {
15862
+ const configured = providerConfig(config2)[provider] ?? {};
15863
+ return {
15864
+ ...DEFAULT_PROVIDER_SETTINGS[provider],
15865
+ ...configured
15866
+ };
15867
+ }
15868
+ function modelAliases(config2) {
15869
+ const configured = providerConfig(config2);
15870
+ return {
15871
+ ...BUILTIN_ALIASES,
15872
+ ...configured.default_model ? { default: configured.default_model } : {},
15873
+ ...configured.aliases ?? {}
15874
+ };
15875
+ }
15876
+ function parseModelRef(modelRef) {
15877
+ const [provider, ...rest] = modelRef.split(":");
15878
+ const model = rest.join(":");
15879
+ if (provider !== "openai" && provider !== "anthropic" && provider !== "deepseek") {
15880
+ throw new Error(`Unsupported AI provider: ${provider}`);
15881
+ }
15882
+ if (!model)
15883
+ throw new Error(`Invalid model ref: ${modelRef}. Expected provider:model.`);
15884
+ return { provider, model };
15885
+ }
15886
+ function resolveModelRef(aliasOrRef, config2) {
15887
+ const aliases = modelAliases(config2);
15888
+ return aliases[aliasOrRef] ?? aliasOrRef;
15889
+ }
15890
+ function listModelRegistry(config2) {
15891
+ const aliases = modelAliases(config2);
15892
+ return Object.entries(aliases).map(([alias, modelRef]) => {
15893
+ const parsed = parseModelRef(modelRef);
15894
+ return {
15895
+ alias,
15896
+ model_ref: modelRef,
15897
+ provider: parsed.provider,
15898
+ model: parsed.model,
15899
+ default: alias === "default",
15900
+ capabilities: PROVIDER_CAPABILITIES[parsed.provider]
15901
+ };
15902
+ });
15903
+ }
15904
+ function providerCredentialStatus(config2, env = process.env) {
15905
+ return Object.keys(DEFAULT_PROVIDER_SETTINGS).map((provider) => {
15906
+ const settings = providerSettings(config2, provider);
15907
+ const configured = Boolean(env[settings.api_key_env]);
15908
+ return {
15909
+ provider,
15910
+ api_key_env: settings.api_key_env,
15911
+ configured,
15912
+ source: configured ? "env" : "missing",
15913
+ base_url: settings.base_url ?? null,
15914
+ default_model: settings.default_model
15915
+ };
15916
+ });
15917
+ }
15918
+ function providerStatus(config2, env = process.env) {
15919
+ return {
15920
+ default_model: resolveModelRef("default", config2),
15921
+ providers: providerCredentialStatus(config2, env),
15922
+ models: listModelRegistry(config2)
15923
+ };
15924
+ }
15925
+
15926
+ // src/storage-contract.ts
15927
+ import { createHash as createHash5, randomUUID as randomUUID4 } from "crypto";
15928
+ var GENERATED_ARTIFACTS = [
15929
+ {
15930
+ kind: "schema",
15931
+ prefix: "schemas/",
15932
+ description: "Machine-readable agent schemas and source rules."
15933
+ },
15934
+ {
15935
+ kind: "index",
15936
+ prefix: "indexes/",
15937
+ description: "Small orientation indexes and future shard manifests."
15938
+ },
15939
+ {
15940
+ kind: "log",
15941
+ prefix: "logs/",
15942
+ description: "Append-only JSONL run and wiki-maintenance log partitions."
15943
+ },
15944
+ {
15945
+ kind: "run",
15946
+ prefix: "runs/",
15947
+ description: "Prompt/tool/cost ledgers and generated output records."
15948
+ },
15949
+ {
15950
+ kind: "wiki_page",
15951
+ prefix: "wiki/",
15952
+ description: "Generated cited Markdown pages, not raw source files."
15953
+ },
15954
+ {
15955
+ kind: "export",
15956
+ prefix: "exports/",
15957
+ description: "Portable exports and snapshots of derived knowledge state."
15958
+ }
15959
+ ];
15960
+ function hashArtifactBody(body) {
15961
+ const bytes = typeof body === "string" ? Buffer.from(body) : Buffer.from(body);
15962
+ return {
15963
+ hash: `sha256:${createHash5("sha256").update(bytes).digest("hex")}`,
15964
+ size_bytes: bytes.byteLength
15965
+ };
15966
+ }
15967
+ function artifactKindForKey(key) {
15968
+ const match = GENERATED_ARTIFACTS.find((entry) => key.startsWith(entry.prefix));
15969
+ return match?.kind ?? "artifact";
15970
+ }
15971
+ function resolveStorageContract(config2, workspace, scope = "global") {
15972
+ const validation = validateStorageConfig(config2, workspace);
15973
+ const s3 = config2.storage.s3 ?? null;
15974
+ const prefix = s3?.prefix?.replace(/^\/+|\/+$/g, "") ?? "";
15975
+ const s3UriPrefix = s3 ? `s3://${s3.bucket}/${prefix ? `${prefix}/` : ""}` : "";
15976
+ return {
15977
+ scope,
15978
+ mode: config2.mode,
15979
+ storage_type: config2.storage.type,
15980
+ workspace_home: workspace.home,
15981
+ local_layout: {
15982
+ app_path: HASNA_KNOWLEDGE_APP_PATH,
15983
+ config_path: workspace.configPath,
15984
+ json_store_path: workspace.jsonStorePath,
15985
+ knowledge_db_path: workspace.knowledgeDbPath,
15986
+ directories: {
15987
+ artifacts: workspace.artifactsDir,
15988
+ cache: workspace.cacheDir,
15989
+ exports: workspace.exportsDir,
15990
+ indexes: workspace.indexesDir,
15991
+ logs: workspace.logsDir,
15992
+ runs: workspace.runsDir,
15993
+ schemas: workspace.schemasDir,
15994
+ wiki: workspace.wikiDir
15995
+ }
15996
+ },
15997
+ artifact_store: {
15998
+ type: config2.storage.type,
15999
+ artifacts_root: config2.storage.artifacts_root,
16000
+ uri_prefix: config2.storage.type === "s3" ? s3UriPrefix : `file://${workspace.artifactsDir}/`,
16001
+ s3: s3 ? {
16002
+ bucket: s3.bucket,
16003
+ prefix,
16004
+ region: s3.region ?? null,
16005
+ profile: s3.profile ?? null,
16006
+ server_side_encryption: s3.server_side_encryption ?? null,
16007
+ kms_key_configured: Boolean(s3.kms_key_id)
16008
+ } : null
16009
+ },
16010
+ source_ownership: {
16011
+ owner: "open-files",
16012
+ preferred_ref: config2.sources.preferred_ref,
16013
+ allowed_schemes: config2.sources.allowed_schemes,
16014
+ raw_source_bytes_stored_in_open_knowledge: false,
16015
+ stores: [
16016
+ "source refs",
16017
+ "source revisions and hashes",
16018
+ "citation spans",
16019
+ "redacted extracted chunks",
16020
+ "embeddings",
16021
+ "generated wiki artifacts",
16022
+ "indexes",
16023
+ "run ledgers"
16024
+ ],
16025
+ does_not_store: [
16026
+ "raw open-files bytes",
16027
+ "S3 object credentials",
16028
+ "connector secrets",
16029
+ "hosted tenant ownership state"
16030
+ ]
16031
+ },
16032
+ generated_artifacts: GENERATED_ARTIFACTS,
16033
+ scalability: {
16034
+ catalog: "knowledge.db tracks sources, revisions, chunks, citations, indexes, runs, and storage_objects.",
16035
+ indexes: "Indexes are cataloged DB rows plus sharded artifacts, not one giant index.md.",
16036
+ logs: "Logs use dated JSONL partitions under logs/yyyy/mm/dd.jsonl.",
16037
+ markdown: "Markdown pages are the readable wiki layer over DB/object-store state."
16038
+ },
16039
+ warnings: validation.warnings
16040
+ };
16041
+ }
16042
+ function validateStorageConfig(config2, workspace) {
16043
+ const errors3 = [];
16044
+ const warnings = [];
16045
+ if (!workspace.home.endsWith(HASNA_KNOWLEDGE_APP_PATH)) {
16046
+ warnings.push(`Workspace home does not end with ${HASNA_KNOWLEDGE_APP_PATH}: ${workspace.home}`);
16047
+ }
16048
+ if (config2.storage.type === "s3") {
16049
+ if (!config2.storage.s3?.bucket)
16050
+ errors3.push("storage.s3.bucket is required when storage.type is s3.");
16051
+ if (!config2.storage.s3?.prefix)
16052
+ warnings.push("storage.s3.prefix is empty; generated knowledge artifacts will be written at the bucket root.");
16053
+ if (config2.mode === "local")
16054
+ warnings.push("storage.type is s3 while mode is local; this is valid for BYO S3, but hosted wrappers should set mode to hosted.");
16055
+ }
16056
+ if (config2.storage.type === "local" && config2.storage.s3) {
16057
+ warnings.push("storage.s3 is configured but ignored while storage.type is local.");
16058
+ }
16059
+ if (config2.sources.preferred_ref !== "open-files") {
16060
+ warnings.push("sources.preferred_ref should stay open-files for durable company knowledge.");
16061
+ }
16062
+ if (!config2.sources.allowed_schemes.includes("open-files")) {
16063
+ errors3.push("sources.allowed_schemes must include open-files.");
16064
+ }
16065
+ return {
16066
+ ok: errors3.length === 0,
16067
+ errors: errors3,
16068
+ warnings
16069
+ };
16070
+ }
16071
+ function recordStorageObjects(db, objects, now = new Date) {
16072
+ const timestamp = now.toISOString();
16073
+ const statement = db.prepare(`
16074
+ INSERT INTO storage_objects (
16075
+ id, artifact_uri, kind, content_type, hash, size_bytes, metadata_json, created_at, updated_at
16076
+ )
16077
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
16078
+ ON CONFLICT(artifact_uri) DO UPDATE SET
16079
+ kind = excluded.kind,
16080
+ content_type = excluded.content_type,
16081
+ hash = excluded.hash,
16082
+ size_bytes = excluded.size_bytes,
16083
+ metadata_json = excluded.metadata_json,
16084
+ updated_at = excluded.updated_at
16085
+ `);
16086
+ const insert = db.transaction((entries) => {
16087
+ for (const entry of entries) {
16088
+ statement.run(randomUUID4(), entry.uri, entry.kind, entry.content_type ?? null, entry.hash ?? null, entry.size_bytes ?? null, JSON.stringify({
16089
+ key: entry.key,
16090
+ ...entry.metadata ?? {}
16091
+ }), timestamp, timestamp);
16092
+ }
16093
+ });
16094
+ insert(objects);
16095
+ }
16096
+
15776
16097
  // src/wiki-layout.ts
15777
16098
  function todayParts(now) {
15778
16099
  const year = String(now.getUTCFullYear());
@@ -15847,19 +16168,29 @@ async function initializeWikiLayout(store, now = new Date) {
15847
16168
  root_index_key: rootIndexKey,
15848
16169
  wiki_readme_key: wikiReadmeKey
15849
16170
  };
15850
- const writes = [
15851
- store.put({ key: schemaKey, body: agentSchemaTemplate(), content_type: "text/markdown" }),
15852
- store.put({ key: rootIndexKey, body: rootIndexTemplate(), content_type: "text/markdown" }),
15853
- store.put({ key: wikiReadmeKey, body: wikiReadmeTemplate(), content_type: "text/markdown" }),
15854
- store.put({ key: logKey, body: `${JSON.stringify(event)}
15855
- `, content_type: "application/x-ndjson" })
16171
+ const entries = [
16172
+ { key: schemaKey, body: agentSchemaTemplate(), content_type: "text/markdown" },
16173
+ { key: rootIndexKey, body: rootIndexTemplate(), content_type: "text/markdown" },
16174
+ { key: wikiReadmeKey, body: wikiReadmeTemplate(), content_type: "text/markdown" },
16175
+ { key: logKey, body: `${JSON.stringify(event)}
16176
+ `, content_type: "application/x-ndjson" }
15856
16177
  ];
15857
- await Promise.all(writes);
16178
+ const artifacts = await Promise.all(entries.map(async (entry) => {
16179
+ const result = await store.put(entry);
16180
+ return {
16181
+ key: result.key,
16182
+ uri: result.uri,
16183
+ kind: artifactKindForKey(entry.key),
16184
+ content_type: entry.content_type,
16185
+ ...hashArtifactBody(entry.body)
16186
+ };
16187
+ }));
15858
16188
  return {
15859
16189
  schema_key: schemaKey,
15860
16190
  root_index_key: rootIndexKey,
15861
16191
  wiki_readme_key: wikiReadmeKey,
15862
16192
  log_key: logKey,
16193
+ artifacts,
15863
16194
  written: [schemaKey, rootIndexKey, wikiReadmeKey, logKey]
15864
16195
  };
15865
16196
  }
@@ -15899,6 +16230,12 @@ class KnowledgeService {
15899
16230
  artifactStore() {
15900
16231
  return createArtifactStore(this.config(), this.ensureWorkspace());
15901
16232
  }
16233
+ storageContract() {
16234
+ return resolveStorageContract(this.config(), this.ensureWorkspace(), this.scope);
16235
+ }
16236
+ validateStorage() {
16237
+ return validateStorageConfig(this.config(), this.ensureWorkspace());
16238
+ }
15902
16239
  paths() {
15903
16240
  const workspace = this.ensureWorkspace();
15904
16241
  return {
@@ -15927,7 +16264,16 @@ class KnowledgeService {
15927
16264
  return getKnowledgeDbStats(workspace.knowledgeDbPath);
15928
16265
  }
15929
16266
  async initWiki() {
15930
- return initializeWikiLayout(this.artifactStore());
16267
+ const workspace = this.ensureWorkspace();
16268
+ migrateKnowledgeDb(workspace.knowledgeDbPath);
16269
+ const result = await initializeWikiLayout(this.artifactStore());
16270
+ const db = openKnowledgeDb(workspace.knowledgeDbPath);
16271
+ try {
16272
+ recordStorageObjects(db, result.artifacts);
16273
+ } finally {
16274
+ db.close();
16275
+ }
16276
+ return result;
15931
16277
  }
15932
16278
  async ingestManifest(input) {
15933
16279
  const workspace = this.ensureWorkspace();
@@ -15967,6 +16313,12 @@ class KnowledgeService {
15967
16313
  safetyPolicy: this.safetyPolicy()
15968
16314
  });
15969
16315
  }
16316
+ providerStatus(env = process.env) {
16317
+ return providerStatus(this.config(), env);
16318
+ }
16319
+ modelRegistry() {
16320
+ return listModelRegistry(this.config());
16321
+ }
15970
16322
  }
15971
16323
  function createKnowledgeService(options = {}) {
15972
16324
  return new KnowledgeService(options);
@@ -16032,6 +16384,17 @@ function buildServer() {
16032
16384
  }, async ({ scope }) => {
16033
16385
  return jsonText(createKnowledgeService({ scope }).paths());
16034
16386
  });
16387
+ registerTool(server, "ok_storage_status", "Knowledge storage status", "Inspect local/S3 artifact storage, source ownership, and scalability contract", {
16388
+ scope: scopeField
16389
+ }, async ({ scope }) => {
16390
+ const service = createKnowledgeService({ scope });
16391
+ const validation = service.validateStorage();
16392
+ return jsonText({
16393
+ ok: validation.ok,
16394
+ ...service.storageContract(),
16395
+ validation
16396
+ });
16397
+ });
16035
16398
  registerTool(server, "ok_parse_source_ref", "Parse source reference", "Parse and validate an open-files, S3, file, or web source ref", {
16036
16399
  uri: exports_external.string().describe("Source reference URI")
16037
16400
  }, async ({ uri }) => {
@@ -16058,6 +16421,18 @@ function buildServer() {
16058
16421
  return errorText(error48 instanceof Error ? error48.message : String(error48));
16059
16422
  }
16060
16423
  });
16424
+ registerTool(server, "ok_provider_status", "AI provider status", "Inspect configured AI SDK providers, model aliases, and BYOK credential availability", {
16425
+ scope: scopeField
16426
+ }, async ({ scope }) => {
16427
+ const service = createKnowledgeService({ scope });
16428
+ return jsonText({ ok: true, ...service.providerStatus() });
16429
+ });
16430
+ registerTool(server, "ok_provider_models", "AI provider models", "List AI SDK model aliases and capability metadata", {
16431
+ scope: scopeField
16432
+ }, async ({ scope }) => {
16433
+ const service = createKnowledgeService({ scope });
16434
+ return jsonText({ ok: true, models: service.modelRegistry() });
16435
+ });
16061
16436
  registerTool(server, "ok_add", "Add a knowledge item", "Add a new item to the knowledge store", {
16062
16437
  title: exports_external.string().describe("Item title"),
16063
16438
  content: exports_external.string().describe("Item content/body"),