@schift-io/knowledge-scope 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -15,8 +15,8 @@ var __export = (target, all) => {
15
15
  };
16
16
 
17
17
  // src/main.ts
18
- import { constants as constants4 } from "node:fs";
19
- import { open as open5 } from "node:fs/promises";
18
+ import { constants as constants5 } from "node:fs";
19
+ import { open as open7 } from "node:fs/promises";
20
20
 
21
21
  // ../node_modules/zod/v3/external.js
22
22
  var exports_external = {};
@@ -4125,6 +4125,10 @@ var SearchProviderSchema = exports_external.object({
4125
4125
  kind: exports_external.literal("schift_search"),
4126
4126
  indexRef: KnowledgeScopeIdentifierSchema
4127
4127
  }).strict();
4128
+ var LocalDocumentsProviderSchema = exports_external.object({
4129
+ kind: exports_external.literal("local_documents"),
4130
+ indexRef: KnowledgeScopeIdentifierSchema
4131
+ }).strict();
4128
4132
  var WebProviderSchema = exports_external.object({
4129
4133
  kind: exports_external.literal("web_search"),
4130
4134
  provider: exports_external.enum(["customer", "schift"])
@@ -4133,6 +4137,7 @@ var QueryProviderSchema = exports_external.discriminatedUnion("kind", [
4133
4137
  RecordsProviderSchema,
4134
4138
  ConnectorProviderSchema,
4135
4139
  SearchProviderSchema,
4140
+ LocalDocumentsProviderSchema,
4136
4141
  WebProviderSchema
4137
4142
  ]).readonly();
4138
4143
  var QueryCapabilitySchema = exports_external.object({
@@ -4241,6 +4246,7 @@ var CandidateProviderEvidenceSchema = exports_external.discriminatedUnion("kind"
4241
4246
  auditPersisted: exports_external.boolean()
4242
4247
  }).strict(),
4243
4248
  SearchProviderSchema,
4249
+ LocalDocumentsProviderSchema,
4244
4250
  WebProviderSchema
4245
4251
  ]).readonly();
4246
4252
  var CandidateEnvelopeSchema = exports_external.object({
@@ -4442,6 +4448,10 @@ var ProviderEvidenceSchema = exports_external.discriminatedUnion("kind", [
4442
4448
  kind: exports_external.literal("schift_search"),
4443
4449
  indexRef: KnowledgeScopeIdentifierSchema
4444
4450
  }).strict(),
4451
+ exports_external.object({
4452
+ kind: exports_external.literal("local_documents"),
4453
+ indexRef: KnowledgeScopeIdentifierSchema
4454
+ }).strict(),
4445
4455
  exports_external.object({
4446
4456
  kind: exports_external.literal("web_search"),
4447
4457
  provider: exports_external.enum(["customer", "schift"])
@@ -4571,6 +4581,8 @@ var providerEvidenceDenial = (capability, binding, candidate) => {
4571
4581
  return evidence.auditPersisted ? undefined : "connector_audit_missing";
4572
4582
  case "schift_search":
4573
4583
  return evidence.kind === "schift_search" && evidence.indexRef === capability.provider.indexRef ? undefined : "provider_evidence_mismatch";
4584
+ case "local_documents":
4585
+ return evidence.kind === "local_documents" && evidence.indexRef === capability.provider.indexRef ? undefined : "provider_evidence_mismatch";
4574
4586
  case "web_search":
4575
4587
  return evidence.kind === "web_search" && evidence.provider === capability.provider.provider ? undefined : "provider_evidence_mismatch";
4576
4588
  }
@@ -5378,6 +5390,8 @@ var providerReference = (capability) => {
5378
5390
  return capability.provider.actionId;
5379
5391
  case "schift_search":
5380
5392
  return capability.provider.indexRef;
5393
+ case "local_documents":
5394
+ return capability.provider.indexRef;
5381
5395
  case "web_search":
5382
5396
  return capability.provider.provider;
5383
5397
  }
@@ -6214,6 +6228,7 @@ var createHttpProviderExecutionPort = (options) => ({
6214
6228
  return parsed.data;
6215
6229
  });
6216
6230
  }
6231
+ case "local_documents":
6217
6232
  case "web_search":
6218
6233
  throw new HttpProviderAdapterError("invalid_configuration");
6219
6234
  }
@@ -6224,6 +6239,154 @@ var createHttpProviderExecutionPort = (options) => ({
6224
6239
  import { timingSafeEqual as timingSafeEqual2 } from "node:crypto";
6225
6240
  import { createServer } from "node:http";
6226
6241
 
6242
+ // src/local-documents/files.ts
6243
+ import { constants as constants2 } from "node:fs";
6244
+ import { lstat as lstat2, open as open2, opendir, realpath } from "node:fs/promises";
6245
+ import { dirname, extname, join as join2, relative, resolve, sep } from "node:path";
6246
+ class LocalDocumentError extends Error {
6247
+ code;
6248
+ name = "LocalDocumentError";
6249
+ constructor(code) {
6250
+ super({
6251
+ local_source_invalid: "Select regular UTF-8 Markdown (.md) or text (.txt) files; symlinks, hardlinks and binary files are unsupported.",
6252
+ local_limit_exceeded: "Local documents exceed the supported file, byte, or chunk limits.",
6253
+ local_snapshot_invalid: "Local document snapshot is missing, unsafe, or corrupt; ingest the source again.",
6254
+ local_scope_invalid: "Local documents require the mounted tenant and document binding; filters and narrowed scopes are unsupported."
6255
+ }[code]);
6256
+ this.code = code;
6257
+ }
6258
+ }
6259
+ var LOCAL_LIMITS = { files: 100, fileBytes: 1048576, totalBytes: 8388608, snapshotBytes: 16777216, chunks: 4000, entries: 2000, depth: 16, chunkCharacters: 2000 };
6260
+ var identityMatches = (left, right) => left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs && left.ctimeMs === right.ctimeMs && right.nlink === 1;
6261
+ var checkDirectories = async (directories) => {
6262
+ for (const directory of directories) {
6263
+ const current = await lstat2(directory.path);
6264
+ if (!current.isDirectory() || current.isSymbolicLink() || current.dev !== directory.dev || current.ino !== directory.ino || await realpath(directory.path) !== directory.path)
6265
+ throw new LocalDocumentError("local_source_invalid");
6266
+ }
6267
+ };
6268
+ var mapLocalFileError = (error, ownerOnly) => {
6269
+ if (error instanceof Error && "code" in error && typeof error.code === "string" && ["ENOENT", "ELOOP", "EACCES", "EPERM", "ENOTDIR", "EISDIR"].includes(error.code)) {
6270
+ throw new LocalDocumentError(ownerOnly ? "local_snapshot_invalid" : "local_source_invalid");
6271
+ }
6272
+ throw error;
6273
+ };
6274
+ var readBounded = async (source, maxBytes, ownerOnly = false) => {
6275
+ try {
6276
+ return await readFileContents(source, maxBytes, ownerOnly);
6277
+ } catch (error) {
6278
+ return mapLocalFileError(error, ownerOnly);
6279
+ }
6280
+ };
6281
+ var readFileContents = async (source, maxBytes, ownerOnly) => {
6282
+ if (!constants2.O_NOFOLLOW)
6283
+ throw new LocalDocumentError("local_source_invalid");
6284
+ const path = typeof source === "string" ? source : source.path;
6285
+ if (typeof source !== "string") {
6286
+ await checkDirectories(source.ancestors);
6287
+ const currentPath = await realpath(path);
6288
+ const inside = relative(source.root, currentPath);
6289
+ if (currentPath !== path || inside === ".." || inside.startsWith(`..${sep}`))
6290
+ throw new LocalDocumentError("local_source_invalid");
6291
+ }
6292
+ const handle = await open2(path, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
6293
+ try {
6294
+ const before = await handle.stat();
6295
+ if (typeof source !== "string") {
6296
+ if (!identityMatches(source.identity, before))
6297
+ throw new LocalDocumentError("local_source_invalid");
6298
+ await checkDirectories(source.ancestors);
6299
+ }
6300
+ if (!before.isFile() || ownerOnly && ((before.mode & 63) !== 0 || process.getuid !== undefined && before.uid !== process.getuid()))
6301
+ throw new LocalDocumentError("local_snapshot_invalid");
6302
+ if (before.size > maxBytes)
6303
+ throw new LocalDocumentError("local_limit_exceeded");
6304
+ const buffer = Buffer.alloc(maxBytes + 1);
6305
+ let length = 0;
6306
+ while (length < buffer.length) {
6307
+ const { bytesRead } = await handle.read(buffer, length, buffer.length - length, length);
6308
+ if (bytesRead === 0)
6309
+ break;
6310
+ length += bytesRead;
6311
+ }
6312
+ const after = await handle.stat();
6313
+ if (length > maxBytes)
6314
+ throw new LocalDocumentError("local_limit_exceeded");
6315
+ if (before.size !== after.size || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs || typeof source !== "string" && !identityMatches(source.identity, after))
6316
+ throw new LocalDocumentError("local_source_invalid");
6317
+ if (typeof source !== "string")
6318
+ await checkDirectories(source.ancestors);
6319
+ const text = decodeUtf8Strict(buffer.subarray(0, length));
6320
+ if (/[\u0000-\u0008\u000b\u000c\u000e-\u001f]/u.test(text))
6321
+ throw new LocalDocumentError("local_source_invalid");
6322
+ return text;
6323
+ } finally {
6324
+ await handle.close();
6325
+ }
6326
+ };
6327
+ var collectFiles = async (source) => {
6328
+ try {
6329
+ return await collectSourceFiles(source);
6330
+ } catch (error) {
6331
+ return mapLocalFileError(error, false);
6332
+ }
6333
+ };
6334
+ var collectSourceFiles = async (source) => {
6335
+ const selected = resolve(source);
6336
+ const selectedMetadata = await lstat2(selected);
6337
+ if (selectedMetadata.isSymbolicLink())
6338
+ throw new LocalDocumentError("local_source_invalid");
6339
+ const root = await realpath(selected);
6340
+ const rootMetadata = await lstat2(root);
6341
+ if (selectedMetadata.dev !== rootMetadata.dev || selectedMetadata.ino !== rootMetadata.ino)
6342
+ throw new LocalDocumentError("local_source_invalid");
6343
+ const boundary = rootMetadata.isDirectory() ? root : dirname(root);
6344
+ const parentMetadata = await lstat2(dirname(boundary));
6345
+ const parents = [{ path: dirname(boundary), dev: parentMetadata.dev, ino: parentMetadata.ino }];
6346
+ const files = [];
6347
+ let entries = 1;
6348
+ const visit = async (path, depth, ancestors) => {
6349
+ await checkDirectories(ancestors);
6350
+ if (entries > LOCAL_LIMITS.entries || depth > LOCAL_LIMITS.depth)
6351
+ throw new LocalDocumentError("local_limit_exceeded");
6352
+ const metadata = await lstat2(path);
6353
+ if (metadata.isSymbolicLink())
6354
+ throw new LocalDocumentError("local_source_invalid");
6355
+ if (metadata.isDirectory()) {
6356
+ const directories = [...ancestors, { path, dev: metadata.dev, ino: metadata.ino }];
6357
+ await checkDirectories(directories);
6358
+ const directory = await opendir(path);
6359
+ for await (const child of directory) {
6360
+ entries += 1;
6361
+ if (entries > LOCAL_LIMITS.entries)
6362
+ throw new LocalDocumentError("local_limit_exceeded");
6363
+ if (child.name.startsWith(".") || child.name === "node_modules")
6364
+ continue;
6365
+ await visit(join2(path, child.name), depth + 1, directories);
6366
+ }
6367
+ await checkDirectories(directories);
6368
+ return;
6369
+ }
6370
+ if (!metadata.isFile())
6371
+ throw new LocalDocumentError("local_source_invalid");
6372
+ if (![".md", ".txt"].includes(extname(path).toLowerCase())) {
6373
+ if (depth === 0)
6374
+ throw new LocalDocumentError("local_source_invalid");
6375
+ return;
6376
+ }
6377
+ if (metadata.nlink !== 1 || await realpath(path) !== path)
6378
+ throw new LocalDocumentError("local_source_invalid");
6379
+ files.push({ path, root: boundary, identity: metadata, ancestors });
6380
+ if (files.length > LOCAL_LIMITS.files)
6381
+ throw new LocalDocumentError("local_limit_exceeded");
6382
+ };
6383
+ const boundaryMetadata = await lstat2(boundary);
6384
+ await visit(root, 0, rootMetadata.isDirectory() ? parents : [...parents, { path: boundary, dev: boundaryMetadata.dev, ino: boundaryMetadata.ino }]);
6385
+ if (files.length === 0)
6386
+ throw new LocalDocumentError("local_source_invalid");
6387
+ return files.sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
6388
+ };
6389
+
6227
6390
  // src/api-contract.ts
6228
6391
  var JsonValueSchema3 = exports_external.lazy(() => exports_external.union([
6229
6392
  exports_external.null(),
@@ -6388,6 +6551,8 @@ var createKnowledgeScopeApi = (options) => {
6388
6551
  return errorResponse(error.code, error.status);
6389
6552
  if (error instanceof HttpProviderAdapterError)
6390
6553
  return errorResponse(error.code, 502);
6554
+ if (error instanceof LocalDocumentError)
6555
+ return errorResponse(error.code, error.code === "local_snapshot_invalid" ? 503 : 400);
6391
6556
  if (error instanceof KnowledgeScopeProductError)
6392
6557
  return errorResponse(error.code, 400);
6393
6558
  if (error instanceof SyntaxError || error instanceof URIError)
@@ -6447,11 +6612,11 @@ var serveKnowledgeScopeApi = async (options) => {
6447
6612
  writeNodeResponse(outgoing, errorResponse(code, status));
6448
6613
  });
6449
6614
  });
6450
- await new Promise((resolve, reject) => {
6615
+ await new Promise((resolve2, reject) => {
6451
6616
  server.once("error", reject);
6452
6617
  server.listen(options.port ?? 8787, host, () => {
6453
6618
  server.off("error", reject);
6454
- resolve();
6619
+ resolve2();
6455
6620
  });
6456
6621
  });
6457
6622
  const address = server.address();
@@ -6529,8 +6694,8 @@ var createRemoteKnowledgeScopeApplication = (apiUrl, request = globalThis.fetch,
6529
6694
  var isJsonObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
6530
6695
 
6531
6696
  // src/cli.ts
6532
- import { mkdir as mkdir3, open as open2 } from "node:fs/promises";
6533
- import { basename, resolve as resolve2 } from "node:path";
6697
+ import { mkdir as mkdir5, open as open4 } from "node:fs/promises";
6698
+ import { basename as basename2, resolve as resolve4 } from "node:path";
6534
6699
 
6535
6700
  // src/cli-options.ts
6536
6701
  class CliUsageError extends Error {
@@ -6547,12 +6712,13 @@ var specifications = {
6547
6712
  lock: { count: 1, values: [] },
6548
6713
  mount: { count: 1, values: ["--bindings", "--api-url"] },
6549
6714
  inspect: { count: 1, values: ["--api-url"] },
6715
+ query: { count: 1, values: ["--query", "--api-url"] },
6550
6716
  run: { count: 2, values: ["--input", "--api-url"] },
6551
6717
  "run-batch": { count: 1, values: ["--input", "--api-url"] },
6552
6718
  admit: { count: 1, values: ["--candidate", "--api-url"] },
6553
6719
  unmount: { count: 1, values: ["--expected-revision", "--api-url"] },
6554
6720
  serve: { count: 0, values: ["--host", "--port"] },
6555
- quickstart: { count: 1, values: ["--index", "--tenant", "--query"] },
6721
+ quickstart: { count: 1, values: ["--source", "--index", "--tenant", "--query"] },
6556
6722
  doctor: { count: 1, values: ["--query"], flags: ["--probe"] }
6557
6723
  };
6558
6724
  var parseCliOptions = (command, args) => {
@@ -6587,12 +6753,14 @@ var parseCliOptions = (command, args) => {
6587
6753
  throw new CliUsageError("argument_invalid");
6588
6754
  if (command === "doctor" && seen.has("--probe") !== seen.has("--query"))
6589
6755
  throw new CliUsageError("argument_invalid");
6756
+ if (command === "quickstart" && seen.has("--source") && seen.has("--index"))
6757
+ throw new CliUsageError("argument_invalid");
6590
6758
  return positional;
6591
6759
  };
6592
6760
 
6593
6761
  // src/quickstart.ts
6594
6762
  import { mkdir as mkdir2, writeFile } from "node:fs/promises";
6595
- import { join as join2, resolve } from "node:path";
6763
+ import { join as join3, resolve as resolve2 } from "node:path";
6596
6764
 
6597
6765
  // src/onboarding-config.ts
6598
6766
  class OnboardingError extends Error {
@@ -6639,7 +6807,7 @@ var quickstart = async (request, dependencies, environment) => {
6639
6807
  });
6640
6808
  if (!definition.success)
6641
6809
  throw new OnboardingError("argument_invalid");
6642
- const directory = resolve(request.directory);
6810
+ const directory = resolve2(request.directory);
6643
6811
  try {
6644
6812
  await mkdir2(directory, { mode: 448 });
6645
6813
  } catch (error) {
@@ -6647,20 +6815,20 @@ var quickstart = async (request, dependencies, environment) => {
6647
6815
  throw new OnboardingError("directory_exists");
6648
6816
  throw error;
6649
6817
  }
6650
- const pack = join2(directory, "pack");
6651
- const bindingsPath = join2(directory, "bindings.json");
6652
- const inputPath = join2(directory, "input.json");
6818
+ const pack = join3(directory, "pack");
6819
+ const bindingsPath = join3(directory, "bindings.json");
6820
+ const inputPath = join3(directory, "input.json");
6653
6821
  let installationId;
6654
6822
  let artifactsComplete = false;
6655
6823
  const recovery = () => installationId === undefined ? ["schift-ks", "mount", pack, "--bindings", bindingsPath] : ["schift-ks", "run", installationId, "search", "--input", inputPath];
6656
6824
  try {
6657
- await mkdir2(join2(pack, "schemas"), { recursive: true, mode: 448 });
6825
+ await mkdir2(join3(pack, "schemas"), { recursive: true, mode: 448 });
6658
6826
  const bindings = { scopeAuthority: scopeAuthority.data, sourceBindings: [{ sourceId: "project-documents", sourceClass: "document", authority: "approved", permissionMode: "mirrored", providerRef: request.index, operationIds: ["search"] }] };
6659
6827
  const input = { effectiveScope: { tenant: request.tenant }, input: { query: request.query } };
6660
6828
  const files = {
6661
- [join2(pack, "scope.json")]: json(definition.data),
6662
- [join2(pack, "schemas/input.json")]: { type: "object", required: ["query"], properties: { query: { type: "string", minLength: 1, maxLength: 8192 } }, additionalProperties: false },
6663
- [join2(pack, "schemas/result.json")]: { type: "object", required: ["text"], properties: { text: { type: "string", minLength: 1 } }, additionalProperties: true },
6829
+ [join3(pack, "scope.json")]: json(definition.data),
6830
+ [join3(pack, "schemas/input.json")]: { type: "object", required: ["query"], properties: { query: { type: "string", minLength: 1, maxLength: 8192 } }, additionalProperties: false },
6831
+ [join3(pack, "schemas/result.json")]: { type: "object", required: ["text"], properties: { text: { type: "string", minLength: 1 } }, additionalProperties: true },
6664
6832
  [bindingsPath]: json(bindings),
6665
6833
  [inputPath]: input
6666
6834
  };
@@ -6671,7 +6839,7 @@ var quickstart = async (request, dependencies, environment) => {
6671
6839
  artifactsComplete = true;
6672
6840
  const mounted = KnowledgeScopeMountSchema.parse(await dependencies.embedded.mount(await dependencies.authoring.mountPayload(pack, bindingsPath)));
6673
6841
  installationId = mounted.installationId;
6674
- await writeFile(join2(directory, "installation.json"), `${JSON.stringify(mounted)}
6842
+ await writeFile(join3(directory, "installation.json"), `${JSON.stringify(mounted)}
6675
6843
  `, { flag: "wx", mode: 384 });
6676
6844
  const result = await dependencies.embedded.run({ installationId, operationId: "search", ...input });
6677
6845
  return { directory, installationId, operationId: "search", result, recovery: recovery(), status: "completed" };
@@ -6681,6 +6849,266 @@ var quickstart = async (request, dependencies, environment) => {
6681
6849
  }
6682
6850
  };
6683
6851
 
6852
+ // src/local-quickstart.ts
6853
+ import { mkdir as mkdir4, writeFile as writeFile2 } from "node:fs/promises";
6854
+ import { join as join5, resolve as resolve3 } from "node:path";
6855
+
6856
+ // src/local-documents/index.ts
6857
+ import { mkdir as mkdir3, lstat as lstat3, open as open3, link as link2, rm as rm2 } from "node:fs/promises";
6858
+ import { basename, join as join4 } from "node:path";
6859
+ import { fileURLToPath } from "node:url";
6860
+ import { homedir as homedir2 } from "node:os";
6861
+ import { randomUUID as randomUUID3 } from "node:crypto";
6862
+
6863
+ // src/local-documents/snapshot.ts
6864
+ import { createHash as createHash3 } from "node:crypto";
6865
+ import { pathToFileURL } from "node:url";
6866
+ var DocumentSchema = exports_external.object({ uri: exports_external.string().url(), text: exports_external.string().max(LOCAL_LIMITS.fileBytes), revision: exports_external.string().regex(/^sha256:[a-f0-9]{64}$/) }).strict().readonly();
6867
+ var SnapshotSchema = exports_external.object({ version: exports_external.literal(1), capturedAt: exports_external.string().datetime(), documents: exports_external.array(DocumentSchema).min(1).max(LOCAL_LIMITS.files).readonly() }).strict().readonly();
6868
+ var digest2 = (value) => createHash3("sha256").update(value).digest("hex");
6869
+ var snapshotId = (snapshot) => `local:${digest2(canonicalJson2(snapshot))}`;
6870
+ var buildSnapshot = async (source) => {
6871
+ const documents = [];
6872
+ let bytes = 0;
6873
+ for (const file of await collectFiles(source)) {
6874
+ const text = await readBounded(file, LOCAL_LIMITS.fileBytes);
6875
+ bytes += Buffer.byteLength(text);
6876
+ if (bytes > LOCAL_LIMITS.totalBytes)
6877
+ throw new LocalDocumentError("local_limit_exceeded");
6878
+ documents.push({ uri: pathToFileURL(file.path).href, text, revision: `sha256:${digest2(text)}` });
6879
+ }
6880
+ return { version: 1, capturedAt: new Date().toISOString(), documents };
6881
+ };
6882
+ var chunksFor = (snapshot) => {
6883
+ const chunks = [];
6884
+ let bytes = 0;
6885
+ for (const document of snapshot.documents) {
6886
+ const uri = new URL(document.uri);
6887
+ if (uri.protocol !== "file:" || uri.host !== "" || uri.search !== "" || uri.hash !== "" || document.revision !== `sha256:${digest2(document.text)}`)
6888
+ throw new LocalDocumentError("local_snapshot_invalid");
6889
+ bytes += Buffer.byteLength(document.text);
6890
+ if (bytes > LOCAL_LIMITS.totalBytes)
6891
+ throw new LocalDocumentError("local_limit_exceeded");
6892
+ const lines = document.text.split(/\r?\n/u);
6893
+ let text = "";
6894
+ let start = 1;
6895
+ let end = 1;
6896
+ const flush = () => {
6897
+ if (text.trim().length > 0)
6898
+ chunks.push({ document, text, lineStart: start, lineEnd: end, id: `chunk:${digest2(`${document.uri}:${start}:${end}:${text}`)}` });
6899
+ text = "";
6900
+ if (chunks.length > LOCAL_LIMITS.chunks)
6901
+ throw new LocalDocumentError("local_limit_exceeded");
6902
+ };
6903
+ lines.forEach((line, offset) => {
6904
+ if (line.length > LOCAL_LIMITS.chunkCharacters)
6905
+ throw new LocalDocumentError("local_limit_exceeded");
6906
+ if (text.length + line.length + 1 > LOCAL_LIMITS.chunkCharacters)
6907
+ flush();
6908
+ if (text.length === 0)
6909
+ start = offset + 1;
6910
+ text += `${text.length === 0 ? "" : `
6911
+ `}${line}`;
6912
+ end = offset + 1;
6913
+ });
6914
+ flush();
6915
+ }
6916
+ return chunks;
6917
+ };
6918
+ var lexicalTerms = (text) => {
6919
+ const words = text.normalize("NFKC").toLowerCase().match(/[\p{L}\p{N}]+/gu) ?? [];
6920
+ return [...new Set(words.flatMap((word) => /[가-힣]/u.test(word) && word.length > 2 ? [word, ...Array.from({ length: word.length - 1 }, (_, index) => word.slice(index, index + 2))] : [word]))];
6921
+ };
6922
+
6923
+ // src/local-documents/index.ts
6924
+ var QuerySchema = exports_external.object({ query: exports_external.string().min(1).max(8192) }).strict();
6925
+ var assertPrivateDirectory = async (path) => {
6926
+ await mkdir3(path, { recursive: true, mode: 448 });
6927
+ const info = await lstat3(path);
6928
+ if (!info.isDirectory() || info.isSymbolicLink() || (info.mode & 63) !== 0 || process.getuid !== undefined && info.uid !== process.getuid())
6929
+ throw new LocalDocumentError("local_snapshot_invalid");
6930
+ };
6931
+
6932
+ class LocalDocumentStore {
6933
+ home;
6934
+ constructor(options = {}) {
6935
+ this.home = options.home ?? (options.environment ?? process.env)["SCHIFT_KS_HOME"] ?? join4(homedir2(), ".schift", "knowledge-scope");
6936
+ }
6937
+ async directory() {
6938
+ try {
6939
+ await assertPrivateDirectory(this.home);
6940
+ const directory = join4(this.home, "local-documents");
6941
+ await assertPrivateDirectory(directory);
6942
+ return directory;
6943
+ } catch (error) {
6944
+ return mapLocalFileError(error, true);
6945
+ }
6946
+ }
6947
+ async ingest(sourcePath) {
6948
+ const snapshot = await buildSnapshot(sourcePath);
6949
+ const chunks = chunksFor(snapshot);
6950
+ if (chunks.length === 0)
6951
+ throw new LocalDocumentError("local_source_invalid");
6952
+ const indexRef = snapshotId(snapshot);
6953
+ const directory = await this.directory();
6954
+ const temporary = join4(directory, `.ingest-${randomUUID3()}`);
6955
+ const serialized = canonicalJson2(snapshot);
6956
+ if (Buffer.byteLength(serialized) > LOCAL_LIMITS.snapshotBytes)
6957
+ throw new LocalDocumentError("local_limit_exceeded");
6958
+ const handle = await open3(temporary, "wx", 384);
6959
+ try {
6960
+ await handle.writeFile(serialized, "utf8");
6961
+ await handle.sync();
6962
+ try {
6963
+ await link2(temporary, join4(directory, `${indexRef.slice(6)}.json`));
6964
+ } catch (error) {
6965
+ if (!(error instanceof Error && ("code" in error) && error.code === "EEXIST"))
6966
+ throw error;
6967
+ }
6968
+ } finally {
6969
+ await handle.close();
6970
+ await rm2(temporary, { force: true });
6971
+ }
6972
+ return { indexRef, documentCount: snapshot.documents.length, chunkCount: chunks.length };
6973
+ }
6974
+ async read(indexRef) {
6975
+ if (!/^local:[a-f0-9]{64}$/u.test(indexRef))
6976
+ throw new LocalDocumentError("local_snapshot_invalid");
6977
+ const directory = await this.directory();
6978
+ const text = await readBounded(join4(directory, `${indexRef.slice(6)}.json`), LOCAL_LIMITS.snapshotBytes, true);
6979
+ const parsed = SnapshotSchema.safeParse(parseJsonText(text, "local snapshot", { maxBytes: LOCAL_LIMITS.snapshotBytes }));
6980
+ if (!parsed.success || snapshotId(parsed.data) !== indexRef)
6981
+ throw new LocalDocumentError("local_snapshot_invalid");
6982
+ return parsed.data;
6983
+ }
6984
+ async execute(context) {
6985
+ const { capability, binding, request, mount } = context;
6986
+ if (mount.state !== "mounted" || !mount.sourceBindings.some((entry) => canonicalJson2(entry) === canonicalJson2(binding)) || capability.provider.kind !== "local_documents" || binding.sourceClass !== "document" || binding.providerRef !== capability.provider.indexRef || binding.connectorRef !== undefined || binding.permissionMode !== "static" || !binding.operationIds.includes(capability.operationId) || request.operationId !== capability.operationId || request.installationId !== mount.installationId || request.effectiveScope.tenant !== mount.scopeAuthority.tenant || request.effectiveScope.namespace !== undefined || request.effectiveScope.subject !== undefined || request.effectiveScope.session !== undefined || (capability.requiredProviderScopes?.length ?? 0) > 0 || Object.keys(request.filters ?? {}).length > 0)
6987
+ throw new LocalDocumentError("local_scope_invalid");
6988
+ const query = QuerySchema.safeParse(request.input);
6989
+ if (!query.success || !context.validateInput(request.input).valid)
6990
+ throw new LocalDocumentError("local_source_invalid");
6991
+ const snapshot = await this.read(capability.provider.indexRef);
6992
+ const terms = lexicalTerms(query.data.query);
6993
+ const ranked = chunksFor(snapshot).map((chunk) => {
6994
+ const tokens = new Set(lexicalTerms(chunk.text));
6995
+ return { chunk, score: terms.filter((term) => tokens.has(term)).length };
6996
+ }).filter((entry) => entry.score > 0).sort((a, b) => b.score - a.score || a.chunk.id.localeCompare(b.chunk.id));
6997
+ const results = [];
6998
+ let bytes = 0;
6999
+ for (const { chunk, score } of ranked.slice(0, Math.min(capability.limits?.maxRows ?? 8, 100))) {
7000
+ const sourcePath = fileURLToPath(chunk.document.uri);
7001
+ const documentId = `doc:${digest2(chunk.document.uri)}`;
7002
+ const payload = { text: chunk.text, sourceId: binding.sourceId, documentId, sourcePath, chunkId: chunk.id, lineStart: chunk.lineStart, lineEnd: chunk.lineEnd, score };
7003
+ if (!context.validateResult(payload).valid)
7004
+ throw new LocalDocumentError("local_source_invalid");
7005
+ const result = ProviderResultSchema.parse({
7006
+ resultId: chunk.id,
7007
+ srn: `srn:local:${chunk.id.slice(6)}`,
7008
+ revision: chunk.document.revision,
7009
+ freshness: snapshot.capturedAt,
7010
+ payload,
7011
+ citation: { uri: `schift://local-documents/${capability.provider.indexRef}/${documentId}#L${chunk.lineStart}-L${chunk.lineEnd}`, label: `${basename(sourcePath)}:L${chunk.lineStart}-L${chunk.lineEnd}` },
7012
+ providerScopes: [],
7013
+ providerEvidence: { kind: "local_documents", indexRef: capability.provider.indexRef }
7014
+ });
7015
+ bytes += Buffer.byteLength(canonicalJson2(result));
7016
+ if (bytes > Math.min(capability.limits?.maxResultBytes ?? 1048576, 8388608))
7017
+ throw new LocalDocumentError("local_limit_exceeded");
7018
+ results.push(result);
7019
+ }
7020
+ return results;
7021
+ }
7022
+ }
7023
+
7024
+ // src/local-quickstart.ts
7025
+ var localQuickstart = async (request, dependencies) => {
7026
+ const importer = dependencies.localDocuments;
7027
+ if (importer === undefined)
7028
+ throw new OnboardingError("local_import_unavailable");
7029
+ const scopeAuthority = ScopeAuthoritySchema.safeParse({ organizationId: "local-organization", tenant: request.tenant });
7030
+ if (!scopeAuthority.success || request.query.trim().length === 0 || request.query.length > 8192)
7031
+ throw new OnboardingError("argument_invalid");
7032
+ const directory = resolve3(request.directory);
7033
+ try {
7034
+ await mkdir4(directory, { mode: 448 });
7035
+ } catch (error) {
7036
+ if (error !== null && typeof error === "object" && "code" in error && error.code === "EEXIST")
7037
+ throw new OnboardingError("directory_exists", { nextAction: "Choose a new workspace directory; your existing files were not changed." });
7038
+ throw error;
7039
+ }
7040
+ const pack = join5(directory, "pack");
7041
+ const bindingsPath = join5(directory, "bindings.json");
7042
+ let installationId;
7043
+ let artifactsComplete = false;
7044
+ try {
7045
+ const imported = await importer.ingest(request.source);
7046
+ const definition = KnowledgeScopeDefinitionSchema.parse({
7047
+ packId: "local-project",
7048
+ version: "0.1.0",
7049
+ responsibility: "project-context",
7050
+ scope: { root: "tenant", descendants: ["namespace", "subject", "session"] },
7051
+ authority: { allowed: ["read", "draft"], forbidden: ["send", "approve", "mutate_source", "workflow"], precedence: ["primary", "operational", "approved", "observed", "derived"] },
7052
+ capabilities: [{ operationId: "search", provider: { kind: "local_documents", indexRef: imported.indexRef }, inputSchemaRef: "schemas/input.json", resultSchemaRef: "schemas/result.json", limits: { maxRows: 8, maxResultBytes: 131072 } }],
7053
+ contextPolicy: { mustConsider: [{ id: "project-evidence", minEvidence: 1, selector: { sourceClasses: ["document"] } }], mayConsider: [], mustNotUse: [{ id: "derived-context", selector: { authorities: ["derived"] } }] },
7054
+ evidence: { requireCitation: true, freshness: { defaultMaxAgeSeconds: 86400 }, coverageAssertions: ["project-evidence"] }
7055
+ });
7056
+ const bindings = { scopeAuthority: scopeAuthority.data, sourceBindings: [{ sourceId: "project-documents", sourceClass: "document", authority: "approved", permissionMode: "static", providerRef: imported.indexRef, operationIds: ["search"] }] };
7057
+ const input = { effectiveScope: { tenant: request.tenant }, input: { query: request.query } };
7058
+ await mkdir4(join5(pack, "schemas"), { recursive: true, mode: 448 });
7059
+ const files = {
7060
+ [join5(pack, "scope.json")]: parseJsonText(JSON.stringify(definition), "local definition"),
7061
+ [join5(pack, "schemas/input.json")]: { type: "object", required: ["query"], properties: { query: { type: "string", minLength: 1, maxLength: 8192 } }, additionalProperties: false },
7062
+ [join5(pack, "schemas/result.json")]: { type: "object", required: ["text"], properties: { text: { type: "string", minLength: 1 } }, additionalProperties: true },
7063
+ [bindingsPath]: bindings,
7064
+ [join5(directory, "input.json")]: input
7065
+ };
7066
+ for (const [path, value] of Object.entries(files))
7067
+ await writeFile2(path, `${canonicalJson2(value)}
7068
+ `, { flag: "wx", mode: 384 });
7069
+ await dependencies.authoring.lock(pack);
7070
+ artifactsComplete = true;
7071
+ const mounted = KnowledgeScopeMountSchema.parse(await dependencies.embedded.mount(await dependencies.authoring.mountPayload(pack, bindingsPath)));
7072
+ installationId = mounted.installationId;
7073
+ await writeFile2(join5(directory, "installation.json"), `${JSON.stringify(mounted)}
7074
+ `, { flag: "wx", mode: 384 });
7075
+ const result = await dependencies.embedded.run({ installationId, operationId: "search", expectedRevision: mounted.revision, ...input });
7076
+ return {
7077
+ directory,
7078
+ installationId,
7079
+ tenant: request.tenant,
7080
+ operationId: "search",
7081
+ documentCount: imported.documentCount,
7082
+ chunkCount: imported.chunkCount,
7083
+ result,
7084
+ nextQuery: ["schift-ks", "query", installationId, "--query", "Your next question"],
7085
+ status: "completed",
7086
+ storage: "local_snapshot",
7087
+ note: "Documents stay on this computer. Search uses keywords, not semantic embeddings. Re-import into a new workspace to refresh the snapshot."
7088
+ };
7089
+ } catch (error) {
7090
+ const code = error instanceof LocalDocumentError || error instanceof OnboardingError || error instanceof KnowledgeScopeProductError || error instanceof KnowledgeScopeApiError ? error.code : "local_quickstart_failed";
7091
+ const recovery = installationId !== undefined ? ["schift-ks", "query", installationId, "--query", request.query] : artifactsComplete ? ["schift-ks", "mount", pack, "--bindings", bindingsPath] : ["schift-ks", "quickstart", "<new-workspace>", "--source", "<file-or-directory>", "--query", "<question>"];
7092
+ throw new OnboardingError(code, { directory, artifactsComplete, recovery, nextAction: artifactsComplete ? "Use the recovery command to retry; your source files were not changed." : "Check that your source contains readable .md or .txt files, then retry with a new workspace. Your source files were not changed.", ...installationId === undefined ? {} : { installationId } });
7093
+ }
7094
+ };
7095
+
7096
+ // src/local-query.ts
7097
+ var queryProject = async (request, application) => {
7098
+ if (request.query.trim().length === 0 || request.query.length > 8192)
7099
+ throw new OnboardingError("argument_invalid");
7100
+ const { mount } = exports_external.object({ mount: KnowledgeScopeMountSchema }).parse(await application.inspect(request.installationId));
7101
+ if (mount.installationId !== request.installationId)
7102
+ throw new OnboardingError("installation_mismatch");
7103
+ return application.run({
7104
+ installationId: mount.installationId,
7105
+ operationId: "search",
7106
+ effectiveScope: { tenant: mount.scopeAuthority.tenant },
7107
+ expectedRevision: mount.revision,
7108
+ input: { query: request.query }
7109
+ });
7110
+ };
7111
+
6684
7112
  // src/doctor-config.ts
6685
7113
  var ActionsSchema = exports_external.array(exports_external.object({ connectorRef: exports_external.string().min(1), connectorAlias: exports_external.string().min(1), actionId: exports_external.string().min(1) }).strict()).min(1);
6686
7114
  var doctorConfiguration = (inspection, environment) => {
@@ -6689,6 +7117,10 @@ var doctorConfiguration = (inspection, environment) => {
6689
7117
  for (const capability of definition.capabilities.filter((entry) => mount.sourceBindings.some((binding) => binding.operationIds.includes(entry.operationId)))) {
6690
7118
  let scopeEnvironment;
6691
7119
  switch (capability.provider.kind) {
7120
+ case "local_documents":
7121
+ if ((capability.requiredProviderScopes?.length ?? 0) > 0)
7122
+ issues.push("local_documents_provider_scopes_unsupported");
7123
+ break;
6692
7124
  case "schift_search":
6693
7125
  issues.push(...searchConfiguration(environment));
6694
7126
  if (environment["SCHIFT_KS_SEARCH_ORGANIZATION_ID"] !== mount.scopeAuthority.organizationId)
@@ -6739,8 +7171,8 @@ var doctor = async (request, application, environment) => {
6739
7171
  throw new OnboardingError("argument_invalid");
6740
7172
  const inspection = InspectionSchema.parse(await application.inspect(request.installationId));
6741
7173
  const { definition, lock, mount } = inspection;
6742
- const digest2 = await digestKnowledgeScopeDefinition(definition);
6743
- const integrity = lock.definitionDigest === digest2 && mount.definitionDigest === digest2 && mount.installationId === request.installationId;
7174
+ const digest3 = await digestKnowledgeScopeDefinition(definition);
7175
+ const integrity = lock.definitionDigest === digest3 && mount.definitionDigest === digest3 && mount.installationId === request.installationId;
6744
7176
  const issues = doctorConfiguration(inspection, environment);
6745
7177
  const configured = integrity && mount.state === "mounted" && issues.length === 0;
6746
7178
  const report = { installationId: mount.installationId, integrity, state: mount.state, environment: [...new Set(issues)], configured };
@@ -6748,7 +7180,7 @@ var doctor = async (request, application, environment) => {
6748
7180
  return { ...report, status: configured ? "configured" : "attention_required", evidenceVerified: false };
6749
7181
  if (!configured)
6750
7182
  throw new OnboardingError("configuration_invalid", { ...report });
6751
- const capability = definition.capabilities.find((entry) => entry.provider.kind === "schift_search" && mount.sourceBindings.some((binding) => binding.operationIds.includes(entry.operationId)));
7183
+ const capability = definition.capabilities.find((entry) => (entry.provider.kind === "schift_search" || entry.provider.kind === "local_documents") && mount.sourceBindings.some((binding) => binding.operationIds.includes(entry.operationId)));
6752
7184
  if (capability === undefined)
6753
7185
  throw new OnboardingError("probe_operation_unavailable");
6754
7186
  const result = await application.run({ installationId: mount.installationId, operationId: capability.operationId, effectiveScope: { tenant: mount.scopeAuthority.tenant }, expectedRevision: mount.revision, input: { query: request.query } });
@@ -6756,10 +7188,12 @@ var doctor = async (request, application, environment) => {
6756
7188
  };
6757
7189
 
6758
7190
  // src/cli.ts
6759
- var COMMANDS = ["init", "validate", "lock", "mount", "inspect", "run", "run-batch", "admit", "unmount", "serve", "quickstart", "doctor"];
7191
+ var COMMANDS = ["init", "validate", "lock", "mount", "inspect", "run", "run-batch", "admit", "unmount", "serve", "quickstart", "query", "doctor"];
6760
7192
  var HELP = {
6761
7193
  bin: "schift-ks",
6762
7194
  commands: COMMANDS,
7195
+ start: { example: "schift-ks quickstart ./my-project --source ./notes.md --query 'What is the refund policy?'", supported: [".md", ".txt", "directory"], accountRequired: false, behavior: "Imports a local snapshot; no upload or model calls." },
7196
+ followUp: "schift-ks query <installation-id> --query 'Your next question'",
6763
7197
  serve: { apiTokenEnvironment: "SCHIFT_KS_API_TOKEN", loopbackOnly: true, tokenRequired: true }
6764
7198
  };
6765
7199
  var option = (args, name) => {
@@ -6800,7 +7234,7 @@ var candidateBatch = (value) => {
6800
7234
  throw new CliUsageError("input_invalid");
6801
7235
  };
6802
7236
  var initialDefinition = (directory) => {
6803
- const inferred = basename(resolve2(directory)).toLowerCase().replace(/[^a-z0-9._:-]+/g, "-").replace(/^-+|-+$/g, "");
7237
+ const inferred = basename2(resolve4(directory)).toLowerCase().replace(/[^a-z0-9._:-]+/g, "-").replace(/^-+|-+$/g, "");
6804
7238
  const packId = inferred.length >= 2 ? inferred.slice(0, 128) : "example-scope";
6805
7239
  return {
6806
7240
  authority: {
@@ -6826,8 +7260,8 @@ var initialDefinition = (directory) => {
6826
7260
  };
6827
7261
  };
6828
7262
  var initialize = async (directory) => {
6829
- await mkdir3(directory, { recursive: true, mode: 448 });
6830
- const file = await open2(resolve2(directory, "scope.json"), "wx", 384);
7263
+ await mkdir5(directory, { recursive: true, mode: 448 });
7264
+ const file = await open4(resolve4(directory, "scope.json"), "wx", 384);
6831
7265
  try {
6832
7266
  await file.writeFile(`${canonicalJson2(initialDefinition(directory))}
6833
7267
  `, "utf8");
@@ -6848,8 +7282,14 @@ var execute = async (argv, dependencies) => {
6848
7282
  const apiUrl = option(args, "--api-url");
6849
7283
  const application = apiUrl === undefined ? dependencies.embedded : dependencies.remote(apiUrl);
6850
7284
  switch (command) {
6851
- case "quickstart":
7285
+ case "quickstart": {
7286
+ const source = option(args, "--source");
7287
+ if (source !== undefined)
7288
+ return localQuickstart({ directory: positional[0] ?? "", source, tenant: option(args, "--tenant") ?? "local-tenant", query: requiredOption(args, "--query") }, dependencies);
6852
7289
  return quickstart({ directory: positional[0] ?? "", index: requiredOption(args, "--index"), tenant: requiredOption(args, "--tenant"), query: requiredOption(args, "--query") }, dependencies, dependencies.environment ?? {});
7290
+ }
7291
+ case "query":
7292
+ return queryProject({ installationId: positional[0] ?? "", query: requiredOption(args, "--query") }, application);
6853
7293
  case "doctor":
6854
7294
  return doctor({ installationId: positional[0] ?? "", ...args.includes("--probe") ? { query: requiredOption(args, "--query") } : {} }, application, dependencies.environment ?? {});
6855
7295
  case "init":
@@ -6930,7 +7370,7 @@ var execute = async (argv, dependencies) => {
6930
7370
  }
6931
7371
  };
6932
7372
  var errorCode = (error) => {
6933
- if (error instanceof OnboardingError || error instanceof CliUsageError || error instanceof KnowledgeScopeApiError || error instanceof KnowledgeScopeProductError || error instanceof HttpProviderAdapterError) {
7373
+ if (error instanceof LocalDocumentError || error instanceof OnboardingError || error instanceof CliUsageError || error instanceof KnowledgeScopeApiError || error instanceof KnowledgeScopeProductError || error instanceof HttpProviderAdapterError) {
6934
7374
  return error.code;
6935
7375
  }
6936
7376
  return "internal_error";
@@ -6946,12 +7386,12 @@ var runKnowledgeScopeCli = async (argv, dependencies, streams) => {
6946
7386
  };
6947
7387
 
6948
7388
  // src/portable.ts
6949
- import { constants as constants2 } from "node:fs";
6950
- import { lstat as lstat2, mkdir as mkdir4, open as open3, readdir, rename, rm as rm2 } from "node:fs/promises";
6951
- import { dirname, join as join3, relative, resolve as resolve3, sep } from "node:path";
6952
- import { randomUUID as randomUUID3 } from "node:crypto";
7389
+ import { constants as constants3 } from "node:fs";
7390
+ import { lstat as lstat4, mkdir as mkdir6, open as open5, readdir, rename, rm as rm3 } from "node:fs/promises";
7391
+ import { dirname as dirname2, join as join6, relative as relative2, resolve as resolve5, sep as sep2 } from "node:path";
7392
+ import { randomUUID as randomUUID4 } from "node:crypto";
6953
7393
  var normalizeRelativePath = (path) => {
6954
- const normalized = path.split(sep).join("/");
7394
+ const normalized = path.split(sep2).join("/");
6955
7395
  if (normalized.length === 0 || normalized.startsWith("/") || normalized.includes("\\") || normalized.split("/").some((part) => part === "" || part === "." || part === "..")) {
6956
7396
  throw productError("path_invalid");
6957
7397
  }
@@ -6959,12 +7399,12 @@ var normalizeRelativePath = (path) => {
6959
7399
  };
6960
7400
  var hasCode2 = (value, code) => value !== null && typeof value === "object" && ("code" in value) && value.code === code;
6961
7401
  var readNoFollowText = async (path, maxBytes = DEFAULT_MAX_JSON_BYTES) => {
6962
- if (typeof constants2.O_NOFOLLOW !== "number" || constants2.O_NOFOLLOW === 0) {
7402
+ if (typeof constants3.O_NOFOLLOW !== "number" || constants3.O_NOFOLLOW === 0) {
6963
7403
  throw productError("path_invalid");
6964
7404
  }
6965
7405
  let handle;
6966
7406
  try {
6967
- handle = await open3(path, constants2.O_RDONLY | constants2.O_NOFOLLOW);
7407
+ handle = await open5(path, constants3.O_RDONLY | constants3.O_NOFOLLOW);
6968
7408
  } catch (error) {
6969
7409
  if (hasCode2(error, "ELOOP"))
6970
7410
  throw productError("path_invalid");
@@ -6994,18 +7434,18 @@ var readNoFollowText = async (path, maxBytes = DEFAULT_MAX_JSON_BYTES) => {
6994
7434
  }
6995
7435
  };
6996
7436
  var walkJsonFiles = async (root, current = root) => {
6997
- const metadata = await lstat2(current);
7437
+ const metadata = await lstat4(current);
6998
7438
  if (metadata.isSymbolicLink())
6999
7439
  throw productError("path_invalid");
7000
7440
  if (!metadata.isDirectory())
7001
7441
  throw productError("path_invalid");
7002
7442
  const paths = [];
7003
7443
  for (const entry of await readdir(current, { withFileTypes: true })) {
7004
- const absolute = join3(current, entry.name);
7005
- const entryMetadata = await lstat2(absolute);
7444
+ const absolute = join6(current, entry.name);
7445
+ const entryMetadata = await lstat4(absolute);
7006
7446
  if (entryMetadata.isSymbolicLink())
7007
7447
  throw productError("path_invalid");
7008
- const path = normalizeRelativePath(relative(root, absolute));
7448
+ const path = normalizeRelativePath(relative2(root, absolute));
7009
7449
  if (entryMetadata.isDirectory()) {
7010
7450
  if (path !== ".schift")
7011
7451
  paths.push(...await walkJsonFiles(root, absolute));
@@ -7020,8 +7460,8 @@ var walkJsonFiles = async (root, current = root) => {
7020
7460
  };
7021
7461
  var readJsonFile = async (root, path) => {
7022
7462
  const normalized = normalizeRelativePath(path);
7023
- const absolute = resolve3(root, normalized);
7024
- const prefix = `${resolve3(root)}${sep}`;
7463
+ const absolute = resolve5(root, normalized);
7464
+ const prefix = `${resolve5(root)}${sep2}`;
7025
7465
  if (!absolute.startsWith(prefix))
7026
7466
  throw productError("path_invalid");
7027
7467
  const text = await readNoFollowText(absolute).catch((error) => {
@@ -7040,7 +7480,7 @@ var declaredPaths = (definition) => [
7040
7480
  ]))
7041
7481
  ].sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
7042
7482
  var loadPortableScope = async (directory, options = {}) => {
7043
- const root = resolve3(directory);
7483
+ const root = resolve5(directory);
7044
7484
  const discovered = await walkJsonFiles(root);
7045
7485
  if (!discovered.includes("scope.json"))
7046
7486
  throw productError("portable_file_missing");
@@ -7060,7 +7500,7 @@ var loadPortableScope = async (directory, options = {}) => {
7060
7500
  compileSchemaSubset(files[path]);
7061
7501
  if (options.readLock === false)
7062
7502
  return { directory: root, definition: parsedDefinition.data, files };
7063
- const lockPath = join3(root, "scope.lock.json");
7503
+ const lockPath = join6(root, "scope.lock.json");
7064
7504
  try {
7065
7505
  const rawLock = parseJsonText(await readNoFollowText(lockPath), "scope.lock.json");
7066
7506
  const parsedLock = KnowledgeScopeLockSchema.safeParse(rawLock);
@@ -7077,10 +7517,10 @@ var loadPortableScope = async (directory, options = {}) => {
7077
7517
  var createPortableLock = async (directory) => {
7078
7518
  const portable = await loadPortableScope(directory, { readLock: false });
7079
7519
  const lock = await buildKnowledgeScopeLock(portable.definition, portable.files);
7080
- const lockPath = join3(portable.directory, "scope.lock.json");
7081
- const temporaryPath = join3(dirname(lockPath), `.scope-lock-${randomUUID3()}.tmp`);
7082
- await mkdir4(dirname(lockPath), { recursive: true });
7083
- const handle = await open3(temporaryPath, "wx", 420);
7520
+ const lockPath = join6(portable.directory, "scope.lock.json");
7521
+ const temporaryPath = join6(dirname2(lockPath), `.scope-lock-${randomUUID4()}.tmp`);
7522
+ await mkdir6(dirname2(lockPath), { recursive: true });
7523
+ const handle = await open5(temporaryPath, "wx", 420);
7084
7524
  try {
7085
7525
  await handle.writeFile(`${canonicalJson2(lock)}
7086
7526
  `, "utf8");
@@ -7088,7 +7528,7 @@ var createPortableLock = async (directory) => {
7088
7528
  await rename(temporaryPath, lockPath);
7089
7529
  } finally {
7090
7530
  await handle.close();
7091
- await rm2(temporaryPath, { force: true });
7531
+ await rm3(temporaryPath, { force: true });
7092
7532
  }
7093
7533
  return lock;
7094
7534
  };
@@ -7100,11 +7540,11 @@ var verifyPortableLock = async (directory) => {
7100
7540
  };
7101
7541
 
7102
7542
  // src/state-store.ts
7103
- import { constants as constants3 } from "node:fs";
7104
- import { chmod as chmod2, link as link2, lstat as lstat3, mkdir as mkdir5, open as open4, rename as rename2, rm as rm3 } from "node:fs/promises";
7105
- import { homedir as homedir2 } from "node:os";
7106
- import { dirname as dirname2, join as join4 } from "node:path";
7107
- import { randomUUID as randomUUID4 } from "node:crypto";
7543
+ import { constants as constants4 } from "node:fs";
7544
+ import { chmod as chmod2, link as link3, lstat as lstat5, mkdir as mkdir7, open as open6, rename as rename2, rm as rm4 } from "node:fs/promises";
7545
+ import { homedir as homedir3 } from "node:os";
7546
+ import { dirname as dirname3, join as join7 } from "node:path";
7547
+ import { randomUUID as randomUUID5 } from "node:crypto";
7108
7548
 
7109
7549
  // src/state-contract.ts
7110
7550
  var StateJsonValueSchema = exports_external.lazy(() => exports_external.union([
@@ -7170,21 +7610,21 @@ var hasCode3 = (value, code) => {
7170
7610
  return value.code === code;
7171
7611
  };
7172
7612
  var assertOwnerOnly2 = async (path, expectedDirectory) => {
7173
- const metadata = await lstat3(path);
7613
+ const metadata = await lstat5(path);
7174
7614
  if (metadata.isSymbolicLink() || (metadata.mode & 63) !== 0 || (expectedDirectory ? !metadata.isDirectory() : !metadata.isFile())) {
7175
7615
  throw productError("state_permissions_invalid");
7176
7616
  }
7177
7617
  };
7178
7618
  var noFollowReadFlags = () => {
7179
- if (typeof constants3.O_NOFOLLOW !== "number" || constants3.O_NOFOLLOW === 0) {
7619
+ if (typeof constants4.O_NOFOLLOW !== "number" || constants4.O_NOFOLLOW === 0) {
7180
7620
  throw productError("state_permissions_invalid");
7181
7621
  }
7182
- return constants3.O_RDONLY | constants3.O_NOFOLLOW;
7622
+ return constants4.O_RDONLY | constants4.O_NOFOLLOW;
7183
7623
  };
7184
7624
  var readOwnerOnlyFile = async (path, maxBytes) => {
7185
7625
  let handle;
7186
7626
  try {
7187
- handle = await open4(path, noFollowReadFlags());
7627
+ handle = await open6(path, noFollowReadFlags());
7188
7628
  } catch (error) {
7189
7629
  if (hasCode3(error, "ELOOP"))
7190
7630
  throw productError("state_permissions_invalid");
@@ -7222,13 +7662,13 @@ class KnowledgeScopeStateStore {
7222
7662
  faults;
7223
7663
  constructor(options = {}) {
7224
7664
  const environment = options.environment ?? process.env;
7225
- this.home = options.home ?? environment["SCHIFT_KS_HOME"] ?? join4(homedir2(), ".schift", "knowledge-scope");
7226
- this.statePath = join4(this.home, "state.json");
7227
- this.lockPath = join4(this.home, "state.lock");
7665
+ this.home = options.home ?? environment["SCHIFT_KS_HOME"] ?? join7(homedir3(), ".schift", "knowledge-scope");
7666
+ this.statePath = join7(this.home, "state.json");
7667
+ this.lockPath = join7(this.home, "state.lock");
7228
7668
  this.faults = options.faults;
7229
7669
  }
7230
7670
  async initialize() {
7231
- const created = await mkdir5(this.home, { recursive: true, mode: 448 });
7671
+ const created = await mkdir7(this.home, { recursive: true, mode: 448 });
7232
7672
  if (created === undefined)
7233
7673
  await assertOwnerOnly2(this.home, true);
7234
7674
  else
@@ -7246,21 +7686,21 @@ class KnowledgeScopeStateStore {
7246
7686
  }
7247
7687
  }
7248
7688
  async createInitialState() {
7249
- const temporaryPath = join4(dirname2(this.statePath), `.state-init-${process.pid}-${randomUUID4()}.tmp`);
7250
- const handle = await open4(temporaryPath, "wx", 384);
7689
+ const temporaryPath = join7(dirname3(this.statePath), `.state-init-${process.pid}-${randomUUID5()}.tmp`);
7690
+ const handle = await open6(temporaryPath, "wx", 384);
7251
7691
  try {
7252
7692
  await handle.writeFile(serializeState(EMPTY_KNOWLEDGE_SCOPE_STATE), "utf8");
7253
7693
  await handle.sync();
7254
7694
  if (this.faults !== undefined)
7255
7695
  await this.faults.beforeRename();
7256
7696
  try {
7257
- await link2(temporaryPath, this.statePath);
7697
+ await link3(temporaryPath, this.statePath);
7258
7698
  } catch (error) {
7259
7699
  if (!hasCode3(error, "EEXIST"))
7260
7700
  throw error;
7261
7701
  return;
7262
7702
  }
7263
- const directory = await open4(this.home, "r");
7703
+ const directory = await open6(this.home, "r");
7264
7704
  try {
7265
7705
  await directory.sync();
7266
7706
  } finally {
@@ -7268,7 +7708,7 @@ class KnowledgeScopeStateStore {
7268
7708
  }
7269
7709
  } finally {
7270
7710
  await handle.close();
7271
- await rm3(temporaryPath, { force: true });
7711
+ await rm4(temporaryPath, { force: true });
7272
7712
  }
7273
7713
  }
7274
7714
  async read() {
@@ -7300,13 +7740,13 @@ class KnowledgeScopeStateStore {
7300
7740
  return mutation.value;
7301
7741
  } finally {
7302
7742
  await lockHandle.close();
7303
- await rm3(this.lockPath, { force: true });
7743
+ await rm4(this.lockPath, { force: true });
7304
7744
  }
7305
7745
  }
7306
7746
  async acquireLock() {
7307
7747
  let handle;
7308
7748
  try {
7309
- handle = await open4(this.lockPath, "wx", 384);
7749
+ handle = await open6(this.lockPath, "wx", 384);
7310
7750
  } catch (error) {
7311
7751
  if (!hasCode3(error, "EEXIST"))
7312
7752
  throw error;
@@ -7322,28 +7762,28 @@ class KnowledgeScopeStateStore {
7322
7762
  throw productError("lock_conflict");
7323
7763
  }
7324
7764
  try {
7325
- const owner = { pid: process.pid, createdAt: Date.now(), nonce: randomUUID4() };
7765
+ const owner = { pid: process.pid, createdAt: Date.now(), nonce: randomUUID5() };
7326
7766
  await handle.writeFile(`${canonicalJson2(owner)}
7327
7767
  `, "utf8");
7328
7768
  await handle.sync();
7329
7769
  return handle;
7330
7770
  } catch (error) {
7331
7771
  await handle.close();
7332
- await rm3(this.lockPath, { force: true });
7772
+ await rm4(this.lockPath, { force: true });
7333
7773
  throw error;
7334
7774
  }
7335
7775
  }
7336
7776
  async writeAtomically(state) {
7337
7777
  const serialized = serializeState(state);
7338
- const temporaryPath = join4(dirname2(this.statePath), `.state-${process.pid}-${randomUUID4()}.tmp`);
7339
- const handle = await open4(temporaryPath, "wx", 384);
7778
+ const temporaryPath = join7(dirname3(this.statePath), `.state-${process.pid}-${randomUUID5()}.tmp`);
7779
+ const handle = await open6(temporaryPath, "wx", 384);
7340
7780
  try {
7341
7781
  await handle.writeFile(serialized, "utf8");
7342
7782
  await handle.sync();
7343
7783
  if (this.faults !== undefined)
7344
7784
  await this.faults.beforeRename();
7345
7785
  await rename2(temporaryPath, this.statePath);
7346
- const directory = await open4(this.home, "r");
7786
+ const directory = await open6(this.home, "r");
7347
7787
  try {
7348
7788
  await directory.sync();
7349
7789
  } finally {
@@ -7352,7 +7792,7 @@ class KnowledgeScopeStateStore {
7352
7792
  await chmod2(this.statePath, 384);
7353
7793
  } finally {
7354
7794
  await handle.close();
7355
- await rm3(temporaryPath, { force: true });
7795
+ await rm4(temporaryPath, { force: true });
7356
7796
  }
7357
7797
  }
7358
7798
  }
@@ -7367,11 +7807,11 @@ var toJsonValue = (value) => {
7367
7807
  var isJsonObject3 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
7368
7808
  var hasCode4 = (value, code) => value !== null && typeof value === "object" && ("code" in value) && value.code === code;
7369
7809
  var readNoFollowText2 = async (path) => {
7370
- if (typeof constants4.O_NOFOLLOW !== "number" || constants4.O_NOFOLLOW === 0)
7810
+ if (typeof constants5.O_NOFOLLOW !== "number" || constants5.O_NOFOLLOW === 0)
7371
7811
  throw productError("path_invalid");
7372
7812
  let handle;
7373
7813
  try {
7374
- handle = await open5(path, constants4.O_RDONLY | constants4.O_NOFOLLOW);
7814
+ handle = await open7(path, constants5.O_RDONLY | constants5.O_NOFOLLOW);
7375
7815
  } catch (error) {
7376
7816
  if (hasCode4(error, "ELOOP"))
7377
7817
  throw productError("path_invalid");
@@ -7514,16 +7954,21 @@ var createCliDependencies = (options = {}) => {
7514
7954
  const apiToken = environment["SCHIFT_KS_API_TOKEN"];
7515
7955
  const storageOptions = { environment, ...options.home === undefined ? {} : { home: options.home } };
7516
7956
  const store = new KnowledgeScopeStateStore(storageOptions);
7957
+ const localDocuments = new LocalDocumentStore(storageOptions);
7958
+ const httpProvider = environmentProvider(environment);
7517
7959
  const authorization = new KnowledgeScopeAuthorization(storageOptions);
7518
7960
  const application = new KnowledgeScopeApplication({
7519
7961
  store,
7520
7962
  authorization,
7521
- provider: options.provider ?? environmentProvider(environment)
7963
+ provider: options.provider ?? {
7964
+ execute: (context) => context.capability.provider.kind === "local_documents" ? localDocuments.execute(context) : httpProvider.execute(context)
7965
+ }
7522
7966
  });
7523
7967
  const embedded = applicationPort(application);
7524
7968
  return {
7525
7969
  environment,
7526
7970
  authoring,
7971
+ localDocuments,
7527
7972
  embedded,
7528
7973
  remote: (apiUrl) => createRemoteKnowledgeScopeApplication(apiUrl, globalThis.fetch, apiToken),
7529
7974
  readJson,