@deployxai/dxc 0.3.2 → 0.3.3

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.
@@ -7,23 +7,29 @@ import {
7
7
  CONTENT_KNOWLEDGE_EMBEDDING_REVISION,
8
8
  CONTENT_KNOWLEDGE_SCHEMA_VERSION,
9
9
  LocalStateError,
10
+ ProjectCli,
11
+ contentKnowledgeAdoptResultSchema,
10
12
  contentKnowledgeImportResultSchema,
11
13
  contentKnowledgeModelSchema,
12
14
  contentKnowledgeQuoteListResultSchema,
13
15
  contentKnowledgeQuoteMutationResultSchema,
14
16
  contentKnowledgeQuoteStatusResultSchema,
17
+ contentKnowledgeRefSchema,
15
18
  contentKnowledgeRemoveResultSchema,
16
19
  contentKnowledgeSearchModeSchema,
17
20
  contentKnowledgeSearchResultSchema,
21
+ contentKnowledgeSourceFilterSchema,
18
22
  contentKnowledgeStatusResultSchema,
23
+ contentWorkflowAdoptedKnowledgeSchema,
19
24
  ensurePrivateDirectory,
20
25
  ensurePrivateRegularFile,
21
26
  readPrivateBuffer,
22
27
  readPrivateFile,
23
28
  resolveUserPath,
29
+ withPrivateFileLocks,
24
30
  writePrivateBuffer,
25
31
  writePrivateJson
26
- } from "./chunk-6JNQZHAQ.js";
32
+ } from "./chunk-P5VZG7OG.js";
27
33
 
28
34
  // apps/cli/src/knowledge.ts
29
35
  import { createHash as createHash2, randomUUID } from "node:crypto";
@@ -330,8 +336,6 @@ var LocalBgeKnowledgeEmbedder = class {
330
336
  };
331
337
 
332
338
  // apps/cli/src/knowledge.ts
333
- var DATABASE_DISPLAY_PATH = "~/.dxc/content-memory.sqlite";
334
- var QUOTE_SOURCE_DISPLAY_PATH = "~/Documents/DxC/\u91D1\u53E5\u5E93.md";
335
339
  var QUOTE_SOURCE_FILE_NAME = "\u91D1\u53E5\u5E93.md";
336
340
  var LEGACY_QUOTE_SOURCE_FILE_NAME = "quotes.json";
337
341
  var MAXIMUM_ARTICLE_BYTES = 5 * 1024 * 1024;
@@ -348,15 +352,20 @@ var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".markdown", ".txt"])
348
352
  var MAXIMUM_DIRECTORY_ARTICLES = 1e3;
349
353
  var MAXIMUM_DIRECTORY_DEPTH = 32;
350
354
  var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
355
+ var KNOWLEDGE_REF_PATTERN = /^kr1_([A-Za-z0-9_-]+)\.([a-f0-9]{32})$/u;
356
+ var PROJECT_CONTEXT_FILE = ".dxc/context.json";
357
+ var PROJECT_CONTEXT_MAXIMUM_BYTES = 64 * 1024;
351
358
  var KnowledgeCliError = class extends Error {
352
- constructor(code, message, details) {
359
+ constructor(code, message, details, fields = { recovery: "\u8BF7\u6309 Skill \u8FD4\u56DE\u7684\u6062\u590D\u52A8\u4F5C\u91CD\u8BD5\u3002" }) {
353
360
  super(message);
354
361
  this.code = code;
355
362
  this.details = details;
363
+ this.fields = fields;
356
364
  this.name = "KnowledgeCliError";
357
365
  }
358
366
  code;
359
367
  details;
368
+ fields;
360
369
  };
361
370
  var quoteMetadataValueSchema = z.string().trim().min(1).max(300).refine((value) => !/[\r\n]/u.test(value) && value !== "\uFF08\u65E0\uFF09", {
362
371
  message: "quote metadata must stay on one line"
@@ -426,6 +435,75 @@ function skippedArticle(error, inputPath) {
426
435
  function sha2562(value) {
427
436
  return createHash2("sha256").update(value).digest("hex");
428
437
  }
438
+ var projectContextStoreSchema = z.object({
439
+ adoptedKnowledge: z.array(
440
+ z.union([
441
+ contentWorkflowAdoptedKnowledgeSchema,
442
+ z.object({
443
+ attribution: z.string().trim().min(1).max(300).nullable(),
444
+ knowledgeRef: contentKnowledgeRefSchema,
445
+ label: z.string().trim().min(1).max(300),
446
+ snippet: z.string().trim().min(1).max(420),
447
+ sourceType: z.literal("quote"),
448
+ verbatimUse: z.literal("do-not-use-verbatim")
449
+ }).strict()
450
+ ])
451
+ ).max(20),
452
+ quoteVerbatimGuards: z.array(
453
+ z.object({
454
+ knowledgeRef: contentKnowledgeRefSchema,
455
+ text: z.string().trim().min(1).max(2e3)
456
+ }).strict()
457
+ ).max(20).default([]),
458
+ schemaVersion: z.literal("1")
459
+ }).strict();
460
+ function encodeKnowledgeRef(payload) {
461
+ const encoded = Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
462
+ return `kr1_${encoded}.${sha2562(encoded).slice(0, 32)}`;
463
+ }
464
+ function decodeKnowledgeRef(value) {
465
+ const match = KNOWLEDGE_REF_PATTERN.exec(value);
466
+ if (match?.[1] === void 0 || match[2] === void 0 || sha2562(match[1]).slice(0, 32) !== match[2]) {
467
+ throw new KnowledgeCliError(
468
+ "DXC_KNOWLEDGE_REFERENCE_INVALID",
469
+ "\u77E5\u8BC6\u5F15\u7528\u683C\u5F0F\u6216\u5B8C\u6574\u6027\u65E0\u6548\u3002",
470
+ void 0,
471
+ {
472
+ actual: value,
473
+ expected: "knowledge search \u8FD4\u56DE\u7684 knowledgeRef",
474
+ field: "item",
475
+ recovery: "\u8BF7\u91CD\u65B0\u68C0\u7D22\u5E76\u4F7F\u7528\u5F53\u524D\u7ED3\u679C\u8FD4\u56DE\u7684 knowledgeRef\u3002"
476
+ }
477
+ );
478
+ }
479
+ try {
480
+ const payload = JSON.parse(Buffer.from(match[1], "base64url").toString("utf8"));
481
+ if (typeof payload !== "object" || payload === null || !("version" in payload) || payload.version !== 1 || !("sourceType" in payload) || payload.sourceType !== "history-article" && payload.sourceType !== "quote" || !("contentSha256" in payload) || typeof payload.contentSha256 !== "string") {
482
+ throw new Error("invalid");
483
+ }
484
+ return payload;
485
+ } catch {
486
+ throw new KnowledgeCliError(
487
+ "DXC_KNOWLEDGE_REFERENCE_INVALID",
488
+ "\u77E5\u8BC6\u5F15\u7528\u5185\u5BB9\u65E0\u6548\u3002",
489
+ void 0,
490
+ {
491
+ recovery: "\u8BF7\u91CD\u65B0\u68C0\u7D22\u5E76\u4F7F\u7528\u5F53\u524D\u7ED3\u679C\u8FD4\u56DE\u7684 knowledgeRef\u3002"
492
+ }
493
+ );
494
+ }
495
+ }
496
+ function sameKnowledgeEntity(value, expected) {
497
+ try {
498
+ const actual = decodeKnowledgeRef(value);
499
+ if (actual.sourceType !== expected.sourceType) {
500
+ return false;
501
+ }
502
+ return actual.sourceType === "quote" ? actual.quoteId === (expected.sourceType === "quote" ? expected.quoteId : void 0) : actual.articleId === (expected.sourceType === "history-article" ? expected.articleId : void 0) && actual.chunkId === (expected.sourceType === "history-article" ? expected.chunkId : void 0);
503
+ } catch {
504
+ return false;
505
+ }
506
+ }
429
507
  function knowledgeModelChoices(operation, inputFingerprint) {
430
508
  const choiceId = (mode) => {
431
509
  const payload = Buffer.from(
@@ -772,7 +850,6 @@ function articleSummary(row) {
772
850
  return {
773
851
  articleId: row.id,
774
852
  chunkCount: row.chunkCount,
775
- contentSha256: row.contentSha256,
776
853
  importedAt: row.importedAt,
777
854
  sourceLabel: row.sourceLabel,
778
855
  title: row.title,
@@ -1151,10 +1228,26 @@ async function readQuoteSource(sourcePath, allowLegacyPermissions = false) {
1151
1228
  quotes: preparedQuotes(document.data)
1152
1229
  };
1153
1230
  }
1154
- async function writeQuoteSource(sourcePath, quotes) {
1231
+ async function writeQuoteSource(sourcePath, quotes, expectedSha256) {
1155
1232
  try {
1156
- await writePrivateBuffer(sourcePath, Buffer.from(quoteSourceTemplate(quotes), "utf8"));
1157
- } catch {
1233
+ if (expectedSha256 !== void 0) {
1234
+ const current = await readQuoteSource(sourcePath);
1235
+ if (current.contentSha256 !== expectedSha256) {
1236
+ throw new KnowledgeCliError(
1237
+ "DXC_KNOWLEDGE_REFERENCE_STALE",
1238
+ "\u91D1\u53E5\u5E93\u5DF2\u88AB\u5176\u4ED6\u52A8\u4F5C\u6216\u7528\u6237\u4FEE\u6539\u3002",
1239
+ void 0,
1240
+ { recovery: "\u8BF7\u91CD\u65B0\u67E5\u770B\u91D1\u53E5\u5E93\uFF0C\u518D\u91CD\u505A\u672C\u6B21\u4FEE\u6539\u3002" }
1241
+ );
1242
+ }
1243
+ }
1244
+ const contents = quoteSourceTemplate(quotes);
1245
+ await writePrivateBuffer(sourcePath, Buffer.from(contents, "utf8"));
1246
+ return sha2562(contents);
1247
+ } catch (error) {
1248
+ if (error instanceof KnowledgeCliError) {
1249
+ throw error;
1250
+ }
1158
1251
  throw new KnowledgeCliError("DXC_KNOWLEDGE_IO_FAILED", "Unable to write quote source file");
1159
1252
  }
1160
1253
  }
@@ -1179,13 +1272,10 @@ function quoteSourceSummary(database, sourceInitialized) {
1179
1272
  FROM quotes`
1180
1273
  ).get()
1181
1274
  );
1182
- const source = database.prepare("SELECT content_sha256 FROM quote_sources WHERE source_key = 'default' LIMIT 1").get();
1183
1275
  return {
1184
1276
  activeCount: numberColumn(counts, "active_count"),
1185
1277
  archivedCount: numberColumn(counts, "archived_count"),
1186
- sourceInitialized,
1187
- sourcePath: QUOTE_SOURCE_DISPLAY_PATH,
1188
- sourceSha256: source === void 0 ? null : stringColumn(sqlRow(source), "content_sha256")
1278
+ sourceInitialized
1189
1279
  };
1190
1280
  }
1191
1281
  function quoteEmbeddingText(quote) {
@@ -1397,9 +1487,11 @@ function semanticIndexComplete(database, articleId) {
1397
1487
  );
1398
1488
  return numberColumn(row, "total") > 0 && numberColumn(row, "total") === numberColumn(row, "indexed");
1399
1489
  }
1400
- function saveArticle(database, article, existing, vectors, timestamp, createId) {
1490
+ function saveArticle(database, article, existing, vectors, timestamp, createId, manageTransaction = true) {
1401
1491
  const articleId = existing?.id ?? createId();
1402
- database.exec("BEGIN IMMEDIATE");
1492
+ if (manageTransaction) {
1493
+ database.exec("BEGIN IMMEDIATE");
1494
+ }
1403
1495
  try {
1404
1496
  if (existing === void 0) {
1405
1497
  database.prepare(
@@ -1476,11 +1568,15 @@ function saveArticle(database, article, existing, vectors, timestamp, createId)
1476
1568
  );
1477
1569
  insertSearch.run(chunkId, articleId, article.title, chunk.heading ?? "", chunk.body);
1478
1570
  }
1479
- database.exec("COMMIT");
1571
+ if (manageTransaction) {
1572
+ database.exec("COMMIT");
1573
+ }
1480
1574
  } catch (error) {
1481
- try {
1482
- database.exec("ROLLBACK");
1483
- } catch {
1575
+ if (manageTransaction) {
1576
+ try {
1577
+ database.exec("ROLLBACK");
1578
+ } catch {
1579
+ }
1484
1580
  }
1485
1581
  if (error instanceof KnowledgeCliError) {
1486
1582
  throw error;
@@ -1651,12 +1747,15 @@ function fusedResults(chunks, mode, lexical, semantic, limit) {
1651
1747
  }
1652
1748
  articleCounts.set(chunk.articleId, (articleCounts.get(chunk.articleId) ?? 0) + 1);
1653
1749
  results.push({
1654
- articleId: chunk.articleId,
1655
- chunkId: chunk.id,
1656
- chunkSha256: chunk.chunkSha256,
1657
1750
  heading: chunk.heading,
1751
+ knowledgeRef: encodeKnowledgeRef({
1752
+ articleId: chunk.articleId,
1753
+ chunkId: chunk.id,
1754
+ contentSha256: chunk.chunkSha256,
1755
+ sourceType: "history-article",
1756
+ version: 1
1757
+ }),
1658
1758
  lanes: [...score.lanes].sort(),
1659
- ordinal: chunk.ordinal,
1660
1759
  rank: results.length + 1,
1661
1760
  snippet: snippet(chunk.body),
1662
1761
  sourceLabel: chunk.sourceLabel,
@@ -1692,15 +1791,18 @@ function fusedQuoteResults(quotes, mode, lexical, semantic, limit) {
1692
1791
  }
1693
1792
  results.push({
1694
1793
  attribution: quote.attribution,
1695
- contentSha256: quote.contentSha256,
1794
+ knowledgeRef: encodeKnowledgeRef({
1795
+ contentSha256: quote.contentSha256,
1796
+ quoteId: quote.id,
1797
+ sourceType: "quote",
1798
+ version: 1
1799
+ }),
1696
1800
  lanes: [...score.lanes].sort(),
1697
1801
  origin: quote.origin,
1698
- quoteId: quote.id,
1699
1802
  rank: results.length + 1,
1700
1803
  snippet: snippet(quote.text),
1701
1804
  sourceLabel: quote.sourceLabel,
1702
1805
  sourceType: "quote",
1703
- status: quote.status,
1704
1806
  tags: [...quote.tags],
1705
1807
  verbatimUse: quote.verbatimUse
1706
1808
  });
@@ -1756,6 +1858,7 @@ var KnowledgeCli = class {
1756
1858
  try {
1757
1859
  const imported = [];
1758
1860
  const skipped = [];
1861
+ const pending = [];
1759
1862
  let semanticPreparationAnnounced = false;
1760
1863
  for (const file of options.files) {
1761
1864
  let article;
@@ -1772,40 +1875,59 @@ var KnowledgeCli = class {
1772
1875
  const existing = findExistingArticle(database, article);
1773
1876
  const unchanged = existing?.contentSha256 === article.contentSha256 && existing.chunkerVersion === CONTENT_KNOWLEDGE_CHUNKER_VERSION;
1774
1877
  const semanticComplete = existing === void 0 ? false : semanticIndexComplete(database, existing.id);
1775
- if (unchanged && (options.lexicalOnly || semanticComplete)) {
1776
- imported.push({
1777
- article: articleSummary(existing),
1778
- semanticIndexed: semanticComplete,
1779
- status: "unchanged"
1780
- });
1781
- continue;
1782
- }
1783
- if (!options.lexicalOnly && !semanticPreparationAnnounced) {
1878
+ const fullyUnchanged = unchanged && (options.lexicalOnly || semanticComplete);
1879
+ if (!fullyUnchanged && !options.lexicalOnly && !semanticPreparationAnnounced) {
1784
1880
  this.#writeStderr(
1785
1881
  "Preparing the local semantic index; first use downloads a quantized model of about 24 MB.\n"
1786
1882
  );
1787
1883
  semanticPreparationAnnounced = true;
1788
1884
  }
1789
- const vectors = options.lexicalOnly ? void 0 : await embedArticle(this.#embedder, article);
1790
- const saved = saveArticle(
1791
- database,
1885
+ pending.push({
1792
1886
  article,
1793
1887
  existing,
1794
- vectors,
1795
- this.#now().toISOString(),
1796
- this.#randomUUID
1797
- );
1798
- imported.push({
1799
- article: articleSummary(saved),
1800
- semanticIndexed: vectors !== void 0,
1801
- status: existing === void 0 ? "created" : "updated"
1888
+ semanticComplete,
1889
+ unchanged: fullyUnchanged,
1890
+ vectors: fullyUnchanged || options.lexicalOnly ? void 0 : await embedArticle(this.#embedder, article)
1802
1891
  });
1803
1892
  }
1893
+ database.exec("BEGIN IMMEDIATE");
1894
+ try {
1895
+ for (const item of pending) {
1896
+ if (item.unchanged && item.existing !== void 0) {
1897
+ imported.push({
1898
+ article: articleSummary(item.existing),
1899
+ semanticIndexed: item.semanticComplete,
1900
+ status: "unchanged"
1901
+ });
1902
+ continue;
1903
+ }
1904
+ const saved = saveArticle(
1905
+ database,
1906
+ item.article,
1907
+ item.existing,
1908
+ item.vectors,
1909
+ this.#now().toISOString(),
1910
+ this.#randomUUID,
1911
+ false
1912
+ );
1913
+ imported.push({
1914
+ article: articleSummary(saved),
1915
+ semanticIndexed: item.vectors !== void 0,
1916
+ status: item.existing === void 0 ? "created" : "updated"
1917
+ });
1918
+ }
1919
+ database.exec("COMMIT");
1920
+ } catch (error) {
1921
+ try {
1922
+ database.exec("ROLLBACK");
1923
+ } catch {
1924
+ }
1925
+ throw error;
1926
+ }
1804
1927
  return contentKnowledgeImportResultSchema.parse({
1805
1928
  command: "knowledge.import",
1806
1929
  data: {
1807
1930
  imported,
1808
- model: options.lexicalOnly ? null : this.#embedder.model,
1809
1931
  skipped
1810
1932
  },
1811
1933
  ok: true
@@ -1873,144 +1995,219 @@ var KnowledgeCli = class {
1873
1995
  }
1874
1996
  await this.#syncQuotes(true);
1875
1997
  }
1876
- async listQuotes() {
1998
+ async #withQuoteLock(action) {
1877
1999
  const sourcePath = quoteSourcePath(this.#homeDirectory);
1878
- if (!await quoteSourceExists(sourcePath)) {
1879
- await this.#initQuotes();
1880
- } else {
1881
- await this.#syncQuotes(true);
1882
- }
1883
- const source = await readQuoteSource(sourcePath);
1884
- return contentKnowledgeQuoteListResultSchema.parse({
1885
- command: "knowledge.quotes.list",
1886
- data: { quotes: source.quotes.map(quoteRecord) },
1887
- ok: true
2000
+ return withPrivateFileLocks([sourcePath], action).catch((error) => {
2001
+ if (error instanceof LocalStateError && error.failure === "busy") {
2002
+ throw new KnowledgeCliError(
2003
+ "DXC_KNOWLEDGE_BUSY",
2004
+ "\u91D1\u53E5\u5E93\u6B63\u5728\u88AB\u53E6\u4E00\u4E2A\u52A8\u4F5C\u66F4\u65B0\u3002",
2005
+ void 0,
2006
+ {
2007
+ recovery: "\u8BF7\u7B49\u5F85\u5F53\u524D\u91D1\u53E5\u52A8\u4F5C\u5B8C\u6210\u540E\u91CD\u8BD5\u540C\u4E00\u64CD\u4F5C\u3002",
2008
+ retryable: true
2009
+ }
2010
+ );
2011
+ }
2012
+ throw error;
2013
+ });
2014
+ }
2015
+ async listQuotes() {
2016
+ return this.#withQuoteLock(async () => {
2017
+ const sourcePath = quoteSourcePath(this.#homeDirectory);
2018
+ if (!await quoteSourceExists(sourcePath)) {
2019
+ await this.#initQuotes();
2020
+ } else {
2021
+ await this.#syncQuotes(true);
2022
+ }
2023
+ const source = await readQuoteSource(sourcePath);
2024
+ return contentKnowledgeQuoteListResultSchema.parse({
2025
+ command: "knowledge.quotes.list",
2026
+ data: { quotes: source.quotes.map(quoteRecord) },
2027
+ ok: true
2028
+ });
1888
2029
  });
1889
2030
  }
1890
2031
  async addQuote(options) {
1891
- const sourcePath = quoteSourcePath(this.#homeDirectory);
1892
- if (!await quoteSourceExists(sourcePath)) {
1893
- await this.#initQuotes();
2032
+ if (options.origin !== "original" && options.origin !== "third-party") {
2033
+ throw new KnowledgeCliError("DXC_KNOWLEDGE_QUOTE_INVALID", "\u91D1\u53E5\u6765\u6E90\u5F52\u5C5E\u65E0\u6548\u3002", void 0, {
2034
+ candidates: ["original", "third-party"],
2035
+ expected: "\u672C\u4EBA\u539F\u521B\u6216\u7B2C\u4E09\u65B9\u6765\u6E90",
2036
+ field: "origin",
2037
+ recovery: "\u8BF7\u5148\u786E\u8BA4\u91D1\u53E5\u662F\u5426\u4E3A\u7528\u6237\u672C\u4EBA\u539F\u521B\uFF0C\u518D\u6309\u91D1\u53E5 Skill \u63D0\u4EA4\u6765\u6E90\u5F52\u5C5E\u3002"
2038
+ });
1894
2039
  }
1895
- const source = await readQuoteSource(sourcePath);
1896
- const duplicate = source.quotes.find(
1897
- (item) => item.status === "active" && item.text.trim() === options.text.trim()
1898
- );
1899
- if (duplicate !== void 0) {
1900
- await this.#syncQuotes(false);
2040
+ const origin = options.origin;
2041
+ return this.#withQuoteLock(async () => {
2042
+ const sourcePath = quoteSourcePath(this.#homeDirectory);
2043
+ if (!await quoteSourceExists(sourcePath)) {
2044
+ await this.#initQuotes();
2045
+ }
2046
+ const source = await readQuoteSource(sourcePath);
2047
+ const duplicate = source.quotes.find(
2048
+ (item) => item.status === "active" && item.text.trim() === options.text.trim()
2049
+ );
2050
+ if (duplicate !== void 0) {
2051
+ await this.#syncQuotes(false);
2052
+ return contentKnowledgeQuoteMutationResultSchema.parse({
2053
+ command: "knowledge.quotes.add",
2054
+ data: { quote: quoteRecord(duplicate), synced: true },
2055
+ ok: true
2056
+ });
2057
+ }
2058
+ const quote = quoteSourceItemSchema.safeParse({
2059
+ attribution: options.attribution ?? null,
2060
+ origin,
2061
+ quoteId: this.#randomUUID(),
2062
+ source: options.source ?? null,
2063
+ status: "active",
2064
+ tags: options.tags ?? [],
2065
+ text: options.text,
2066
+ verbatimUse: options.verbatimUse ?? (origin === "third-party" ? "needs-verification" : "original")
2067
+ });
2068
+ if (!quote.success) {
2069
+ throw new KnowledgeCliError("DXC_KNOWLEDGE_QUOTE_INVALID", "\u91D1\u53E5\u8F93\u5165\u65E0\u6548\u3002", void 0, {
2070
+ candidates: ["original", "third-party"],
2071
+ expected: "\u660E\u786E\u6765\u6E90\u5F52\u5C5E\uFF1B\u7B2C\u4E09\u65B9\u91D1\u53E5\u5FC5\u987B\u6709\u7F72\u540D",
2072
+ field: quote.error.issues[0]?.path.join(".") || "quote",
2073
+ recovery: "\u8BF7\u6309\u91D1\u53E5 Skill \u8865\u5145\u6765\u6E90\u5F52\u5C5E\u3001\u7F72\u540D\u548C\u4F7F\u7528\u8FB9\u754C\u3002"
2074
+ });
2075
+ }
2076
+ const nextQuotes = [...source.quotes, quote.data];
2077
+ const writtenSha256 = await writeQuoteSource(sourcePath, nextQuotes, source.contentSha256);
2078
+ try {
2079
+ await this.#syncQuotes(false);
2080
+ if ((await readQuoteSource(sourcePath)).contentSha256 !== writtenSha256) {
2081
+ throw new KnowledgeCliError(
2082
+ "DXC_KNOWLEDGE_REFERENCE_STALE",
2083
+ "\u91D1\u53E5\u5E93\u5728\u7D22\u5F15\u671F\u95F4\u88AB\u4FEE\u6539\u3002",
2084
+ void 0,
2085
+ { recovery: "\u8BF7\u91CD\u65B0\u67E5\u770B\u91D1\u53E5\u5E93\u5E76\u786E\u8BA4\u672C\u6B21\u6536\u5F55\u7ED3\u679C\u3002" }
2086
+ );
2087
+ }
2088
+ } catch (error) {
2089
+ if (error instanceof KnowledgeCliError && error.code === "DXC_KNOWLEDGE_REFERENCE_STALE") {
2090
+ throw error;
2091
+ }
2092
+ await writeQuoteSource(sourcePath, source.quotes, writtenSha256);
2093
+ throw error;
2094
+ }
1901
2095
  return contentKnowledgeQuoteMutationResultSchema.parse({
1902
2096
  command: "knowledge.quotes.add",
1903
- data: { quote: quoteRecord(duplicate), synced: true },
2097
+ data: { quote: quoteRecord(quote.data), synced: true },
1904
2098
  ok: true
1905
2099
  });
1906
- }
1907
- const quote = quoteSourceItemSchema.safeParse({
1908
- attribution: options.attribution ?? null,
1909
- origin: options.origin ?? "original",
1910
- quoteId: this.#randomUUID(),
1911
- source: options.source ?? null,
1912
- status: "active",
1913
- tags: options.tags ?? [],
1914
- text: options.text,
1915
- verbatimUse: options.verbatimUse ?? (options.origin === "third-party" ? "needs-verification" : "original")
1916
- });
1917
- if (!quote.success) {
1918
- throw new KnowledgeCliError("DXC_KNOWLEDGE_QUOTE_INVALID", "Quote input is invalid");
1919
- }
1920
- const nextQuotes = [...source.quotes, quote.data];
1921
- await writeQuoteSource(sourcePath, nextQuotes);
1922
- try {
1923
- await this.#syncQuotes(false);
1924
- } catch (error) {
1925
- await writeQuoteSource(sourcePath, source.quotes);
1926
- throw error;
1927
- }
1928
- return contentKnowledgeQuoteMutationResultSchema.parse({
1929
- command: "knowledge.quotes.add",
1930
- data: { quote: quoteRecord(quote.data), synced: true },
1931
- ok: true
1932
2100
  });
1933
2101
  }
1934
2102
  async updateQuote(quoteId, options) {
1935
2103
  if (!UUID_PATTERN.test(quoteId) || Object.keys(options).length === 0) {
1936
- throw new KnowledgeCliError("DXC_KNOWLEDGE_QUOTE_INVALID", "Quote update is invalid");
1937
- }
1938
- const sourcePath = quoteSourcePath(this.#homeDirectory);
1939
- if (!await quoteSourceExists(sourcePath)) {
1940
- await this.#initQuotes();
1941
- }
1942
- const source = await readQuoteSource(sourcePath);
1943
- const index = source.quotes.findIndex((quote) => quote.quoteId === quoteId);
1944
- if (index < 0) {
1945
- throw new KnowledgeCliError("DXC_KNOWLEDGE_QUOTE_INVALID", "Quote ID was not found");
1946
- }
1947
- const existing = source.quotes[index];
1948
- if (existing === void 0) {
1949
- throw new KnowledgeCliError(
1950
- "DXC_KNOWLEDGE_STATE_INVALID",
1951
- "Knowledge quote state is invalid"
1952
- );
1953
- }
1954
- const updated = quoteSourceItemSchema.safeParse({
1955
- ...sourceQuote(existing),
1956
- ...options,
1957
- attribution: options.attribution === void 0 ? existing.attribution ?? null : options.attribution,
1958
- source: options.source === void 0 ? existing.source ?? null : options.source,
1959
- tags: options.tags === void 0 ? existing.tags : options.tags,
1960
- verbatimUse: options.verbatimUse ?? (options.origin === "third-party" && existing.origin !== "third-party" ? "needs-verification" : existing.verbatimUse)
1961
- });
1962
- if (!updated.success) {
1963
- throw new KnowledgeCliError("DXC_KNOWLEDGE_QUOTE_INVALID", "Quote update is invalid");
1964
- }
1965
- const quotes = source.quotes.map(sourceQuote);
1966
- quotes[index] = updated.data;
1967
- await writeQuoteSource(sourcePath, quotes);
1968
- try {
1969
- await this.#syncQuotes(false);
1970
- } catch (error) {
1971
- await writeQuoteSource(sourcePath, source.quotes);
1972
- throw error;
2104
+ throw new KnowledgeCliError("DXC_KNOWLEDGE_QUOTE_INVALID", "\u91D1\u53E5\u66F4\u65B0\u8F93\u5165\u65E0\u6548\u3002", void 0, {
2105
+ expected: "\u5F53\u524D\u91D1\u53E5 ID \u548C\u81F3\u5C11\u4E00\u4E2A\u53D8\u5316\u5B57\u6BB5",
2106
+ field: "quote",
2107
+ recovery: "\u8BF7\u91CD\u65B0\u5217\u51FA\u91D1\u53E5\u5E76\u53EA\u63D0\u4EA4\u7528\u6237\u660E\u786E\u4FEE\u6539\u7684\u5B57\u6BB5\u3002"
2108
+ });
1973
2109
  }
1974
- return contentKnowledgeQuoteMutationResultSchema.parse({
1975
- command: "knowledge.quotes.update",
1976
- data: { quote: quoteRecord(updated.data), synced: true },
1977
- ok: true
2110
+ return this.#withQuoteLock(async () => {
2111
+ const sourcePath = quoteSourcePath(this.#homeDirectory);
2112
+ if (!await quoteSourceExists(sourcePath)) {
2113
+ throw new KnowledgeCliError(
2114
+ "DXC_KNOWLEDGE_QUOTE_SOURCE_NOT_INITIALIZED",
2115
+ "\u91D1\u53E5\u5E93\u5C1A\u672A\u521B\u5EFA\u3002",
2116
+ void 0,
2117
+ { recovery: "\u8BF7\u5148\u6536\u5F55\u7B2C\u4E00\u6761\u91D1\u53E5\uFF0C\u518D\u6267\u884C\u66F4\u65B0\u3002" }
2118
+ );
2119
+ }
2120
+ const source = await readQuoteSource(sourcePath);
2121
+ const index = source.quotes.findIndex((quote) => quote.quoteId === quoteId);
2122
+ if (index < 0) {
2123
+ throw new KnowledgeCliError(
2124
+ "DXC_KNOWLEDGE_QUOTE_INVALID",
2125
+ "\u6CA1\u6709\u627E\u5230\u8BE5\u91D1\u53E5 ID\u3002",
2126
+ void 0,
2127
+ {
2128
+ actual: quoteId,
2129
+ expected: "quotes list \u8FD4\u56DE\u7684 quoteId",
2130
+ field: "quoteId",
2131
+ recovery: "\u8BF7\u91CD\u65B0\u5217\u51FA\u91D1\u53E5\u5E76\u4F7F\u7528\u5F53\u524D ID\u3002"
2132
+ }
2133
+ );
2134
+ }
2135
+ const existing = source.quotes[index];
2136
+ const updated = quoteSourceItemSchema.safeParse({
2137
+ ...sourceQuote(existing),
2138
+ ...options,
2139
+ attribution: options.attribution === void 0 ? existing.attribution ?? null : options.attribution,
2140
+ source: options.source === void 0 ? existing.source ?? null : options.source,
2141
+ tags: options.tags === void 0 ? existing.tags : options.tags,
2142
+ verbatimUse: options.verbatimUse ?? (options.origin === "third-party" && existing.origin !== "third-party" ? "needs-verification" : existing.verbatimUse)
2143
+ });
2144
+ if (!updated.success) {
2145
+ throw new KnowledgeCliError("DXC_KNOWLEDGE_QUOTE_INVALID", "\u91D1\u53E5\u66F4\u65B0\u65E0\u6548\u3002", void 0, {
2146
+ field: updated.error.issues[0]?.path.join(".") || "quote",
2147
+ recovery: "\u8BF7\u8865\u9F50\u6765\u6E90\u3001\u7F72\u540D\u548C\u9010\u5B57\u4F7F\u7528\u8FB9\u754C\u540E\u91CD\u8BD5\u3002"
2148
+ });
2149
+ }
2150
+ const quotes = source.quotes.map(sourceQuote);
2151
+ quotes[index] = updated.data;
2152
+ const writtenSha256 = await writeQuoteSource(sourcePath, quotes, source.contentSha256);
2153
+ try {
2154
+ await this.#syncQuotes(false);
2155
+ } catch (error) {
2156
+ await writeQuoteSource(sourcePath, source.quotes, writtenSha256);
2157
+ throw error;
2158
+ }
2159
+ return contentKnowledgeQuoteMutationResultSchema.parse({
2160
+ command: "knowledge.quotes.update",
2161
+ data: { quote: quoteRecord(updated.data), synced: true },
2162
+ ok: true
2163
+ });
1978
2164
  });
1979
2165
  }
1980
2166
  async archiveQuote(quoteId) {
1981
2167
  if (!UUID_PATTERN.test(quoteId)) {
1982
2168
  throw new KnowledgeCliError("DXC_KNOWLEDGE_QUOTE_INVALID", "Quote ID is invalid");
1983
2169
  }
1984
- const sourcePath = quoteSourcePath(this.#homeDirectory);
1985
- if (!await quoteSourceExists(sourcePath)) {
1986
- await this.#initQuotes();
1987
- }
1988
- const source = await readQuoteSource(sourcePath);
1989
- const index = source.quotes.findIndex((quote) => quote.quoteId === quoteId);
1990
- if (index < 0) {
1991
- throw new KnowledgeCliError("DXC_KNOWLEDGE_QUOTE_INVALID", "Quote ID was not found");
1992
- }
1993
- const existing = source.quotes[index];
1994
- if (existing === void 0) {
1995
- throw new KnowledgeCliError(
1996
- "DXC_KNOWLEDGE_STATE_INVALID",
1997
- "Knowledge quote state is invalid"
1998
- );
1999
- }
2000
- const archived = { ...sourceQuote(existing), status: "archived" };
2001
- const quotes = source.quotes.map(sourceQuote);
2002
- quotes[index] = archived;
2003
- await writeQuoteSource(sourcePath, quotes);
2004
- try {
2005
- await this.#syncQuotes(false);
2006
- } catch (error) {
2007
- await writeQuoteSource(sourcePath, source.quotes);
2008
- throw error;
2009
- }
2010
- return contentKnowledgeQuoteMutationResultSchema.parse({
2011
- command: "knowledge.quotes.archive",
2012
- data: { quote: quoteRecord(archived), synced: true },
2013
- ok: true
2170
+ return this.#withQuoteLock(async () => {
2171
+ const sourcePath = quoteSourcePath(this.#homeDirectory);
2172
+ if (!await quoteSourceExists(sourcePath)) {
2173
+ throw new KnowledgeCliError(
2174
+ "DXC_KNOWLEDGE_QUOTE_SOURCE_NOT_INITIALIZED",
2175
+ "\u91D1\u53E5\u5E93\u5C1A\u672A\u521B\u5EFA\u3002",
2176
+ void 0,
2177
+ { recovery: "\u65E0\u9700\u5F52\u6863\uFF1B\u5F53\u524D\u6CA1\u6709\u91D1\u53E5\u5E93\u3002" }
2178
+ );
2179
+ }
2180
+ const source = await readQuoteSource(sourcePath);
2181
+ const index = source.quotes.findIndex((quote) => quote.quoteId === quoteId);
2182
+ if (index < 0) {
2183
+ throw new KnowledgeCliError(
2184
+ "DXC_KNOWLEDGE_QUOTE_INVALID",
2185
+ "\u6CA1\u6709\u627E\u5230\u8BE5\u91D1\u53E5 ID\u3002",
2186
+ void 0,
2187
+ {
2188
+ actual: quoteId,
2189
+ expected: "quotes list \u8FD4\u56DE\u7684 quoteId",
2190
+ field: "quoteId",
2191
+ recovery: "\u8BF7\u91CD\u65B0\u5217\u51FA\u91D1\u53E5\u5E76\u4F7F\u7528\u5F53\u524D ID\u3002"
2192
+ }
2193
+ );
2194
+ }
2195
+ const existing = source.quotes[index];
2196
+ const archived = { ...sourceQuote(existing), status: "archived" };
2197
+ const quotes = source.quotes.map(sourceQuote);
2198
+ quotes[index] = archived;
2199
+ const writtenSha256 = await writeQuoteSource(sourcePath, quotes, source.contentSha256);
2200
+ try {
2201
+ await this.#syncQuotes(false);
2202
+ } catch (error) {
2203
+ await writeQuoteSource(sourcePath, source.quotes, writtenSha256);
2204
+ throw error;
2205
+ }
2206
+ return contentKnowledgeQuoteMutationResultSchema.parse({
2207
+ command: "knowledge.quotes.archive",
2208
+ data: { quote: quoteRecord(archived), synced: true },
2209
+ ok: true
2210
+ });
2014
2211
  });
2015
2212
  }
2016
2213
  async quoteStatus() {
@@ -2021,9 +2218,7 @@ var KnowledgeCli = class {
2021
2218
  data: {
2022
2219
  activeCount: 0,
2023
2220
  archivedCount: 0,
2024
- sourceInitialized,
2025
- sourcePath: QUOTE_SOURCE_DISPLAY_PATH,
2026
- sourceSha256: null
2221
+ sourceInitialized
2027
2222
  },
2028
2223
  ok: true
2029
2224
  });
@@ -2083,58 +2278,89 @@ var KnowledgeCli = class {
2083
2278
  database.close();
2084
2279
  }
2085
2280
  }
2086
- async remove(articleId) {
2087
- if (!UUID_PATTERN.test(articleId)) {
2281
+ async remove(knowledgeRef) {
2282
+ const reference = decodeKnowledgeRef(knowledgeRef);
2283
+ if (reference.sourceType !== "history-article") {
2088
2284
  throw new KnowledgeCliError(
2089
2285
  "DXC_KNOWLEDGE_ARTICLE_INVALID",
2090
- "Knowledge article ID is invalid"
2286
+ "\u53EA\u6709\u5386\u53F2\u6587\u7AE0\u68C0\u7D22\u7ED3\u679C\u53EF\u4EE5\u7528\u4E8E\u5220\u9664\u6587\u7AE0\u3002",
2287
+ void 0,
2288
+ { recovery: "\u8BF7\u91CD\u65B0\u68C0\u7D22\u5386\u53F2\u6587\u7AE0\uFF0C\u5E76\u539F\u6837\u4F7F\u7528\u76EE\u6807\u7ED3\u679C\u7684 knowledgeRef\u3002" }
2091
2289
  );
2092
2290
  }
2291
+ const articleId = reference.articleId;
2093
2292
  if (!await databaseExists(this.#databasePath)) {
2094
2293
  return contentKnowledgeRemoveResultSchema.parse({
2095
2294
  command: "knowledge.remove",
2096
- data: { articleId, removed: false },
2295
+ data: { knowledgeRef, removed: false },
2097
2296
  ok: true
2098
2297
  });
2099
2298
  }
2100
2299
  const database = await openDatabase(this.#databasePath);
2101
2300
  try {
2301
+ let removed = false;
2102
2302
  database.exec("BEGIN IMMEDIATE");
2103
2303
  try {
2304
+ const current = loadChunks(database).find(
2305
+ (chunk) => chunk.id === reference.chunkId && chunk.articleId === reference.articleId
2306
+ );
2307
+ if (current === void 0 || current.chunkSha256 !== reference.contentSha256) {
2308
+ throw new KnowledgeCliError(
2309
+ "DXC_KNOWLEDGE_REFERENCE_STALE",
2310
+ "\u5386\u53F2\u6587\u7AE0\u68C0\u7D22\u7ED3\u679C\u5DF2\u53D8\u5316\u3002",
2311
+ void 0,
2312
+ { recovery: "\u8BF7\u91CD\u65B0\u68C0\u7D22\u5386\u53F2\u6587\u7AE0\uFF0C\u5E76\u8BA9\u7528\u6237\u91CD\u65B0\u9009\u62E9\u8981\u5220\u9664\u7684\u5F53\u524D\u7ED3\u679C\u3002" }
2313
+ );
2314
+ }
2104
2315
  database.prepare("DELETE FROM chunk_search WHERE article_id = ?").run(articleId);
2105
2316
  const result = database.prepare("DELETE FROM articles WHERE id = ?").run(articleId);
2317
+ removed = result.changes > 0;
2106
2318
  database.exec("COMMIT");
2107
- return contentKnowledgeRemoveResultSchema.parse({
2108
- command: "knowledge.remove",
2109
- data: {
2110
- articleId,
2111
- removed: result.changes > 0
2112
- },
2113
- ok: true
2114
- });
2115
- } catch {
2319
+ } catch (error) {
2116
2320
  database.exec("ROLLBACK");
2321
+ if (error instanceof KnowledgeCliError) {
2322
+ throw error;
2323
+ }
2117
2324
  throw new KnowledgeCliError(
2118
2325
  "DXC_KNOWLEDGE_IO_FAILED",
2119
2326
  "Unable to remove knowledge article"
2120
2327
  );
2121
2328
  }
2329
+ return contentKnowledgeRemoveResultSchema.parse({
2330
+ command: "knowledge.remove",
2331
+ data: { knowledgeRef, removed },
2332
+ ok: true
2333
+ });
2122
2334
  } finally {
2123
2335
  database.close();
2124
2336
  }
2125
2337
  }
2126
- async search(queryInput, modeInput, limitInput, choiceId) {
2338
+ async search(queryInput, modeInput, limitInput, choiceId, sourceFilterInput = "all") {
2127
2339
  const query = queryInput.trim();
2128
2340
  const modelAction = knowledgeModelChoices(
2129
2341
  "search",
2130
- sha2562(JSON.stringify({ limit: limitInput, mode: modeInput, query }))
2342
+ sha2562(
2343
+ JSON.stringify({
2344
+ limit: limitInput,
2345
+ mode: modeInput,
2346
+ query,
2347
+ sourceFilter: sourceFilterInput
2348
+ })
2349
+ )
2131
2350
  );
2132
2351
  const selectedMode = selectedKnowledgeMode(modelAction, choiceId);
2133
2352
  const mode = contentKnowledgeSearchModeSchema.safeParse(
2134
2353
  selectedMode === "lexical" ? "lexical" : modeInput
2135
2354
  );
2136
- if (query.length === 0 || query.length > 500 || !mode.success || !Number.isSafeInteger(limitInput) || limitInput < 1 || limitInput > MAXIMUM_RESULTS) {
2137
- throw new KnowledgeCliError("DXC_KNOWLEDGE_QUERY_INVALID", "Knowledge query is invalid");
2355
+ const sourceFilter = contentKnowledgeSourceFilterSchema.safeParse(sourceFilterInput);
2356
+ if (query.length === 0 || query.length > 500 || !mode.success || !sourceFilter.success || !Number.isSafeInteger(limitInput) || limitInput < 1 || limitInput > MAXIMUM_RESULTS) {
2357
+ throw new KnowledgeCliError("DXC_KNOWLEDGE_QUERY_INVALID", "\u77E5\u8BC6\u68C0\u7D22\u8F93\u5165\u65E0\u6548\u3002", void 0, {
2358
+ actual: !mode.success ? modeInput : !sourceFilter.success ? sourceFilterInput : queryInput,
2359
+ candidates: !mode.success ? contentKnowledgeSearchModeSchema.options : contentKnowledgeSourceFilterSchema.options,
2360
+ expected: "\u6709\u6548\u67E5\u8BE2\u3001\u68C0\u7D22\u6A21\u5F0F\u3001\u6765\u6E90\u8303\u56F4\u548C 1\u201320 \u6761\u7ED3\u679C\u6570",
2361
+ field: !mode.success ? "mode" : !sourceFilter.success ? "source" : "query",
2362
+ recovery: "\u8BF7\u6309\u5BF9\u5E94 Skill \u58F0\u660E\u7684\u68C0\u7D22\u52A8\u4F5C\u4FEE\u6B63\u5931\u8D25\u5B57\u6BB5\u3002"
2363
+ });
2138
2364
  }
2139
2365
  if (!await databaseExists(this.#databasePath)) {
2140
2366
  return contentKnowledgeSearchResultSchema.parse({
@@ -2143,15 +2369,16 @@ var KnowledgeCli = class {
2143
2369
  mode: mode.data,
2144
2370
  model: mode.data === "lexical" ? null : this.#embedder.model,
2145
2371
  query,
2146
- results: []
2372
+ results: [],
2373
+ sourceFilter: sourceFilter.data
2147
2374
  },
2148
2375
  ok: true
2149
2376
  });
2150
2377
  }
2151
2378
  const database = await openDatabase(this.#databasePath);
2152
2379
  try {
2153
- const chunks = loadChunks(database);
2154
- const quotes = loadActiveQuotes(database);
2380
+ const chunks = sourceFilter.data === "quotes" ? [] : loadChunks(database);
2381
+ const quotes = sourceFilter.data === "history" ? [] : loadActiveQuotes(database);
2155
2382
  const usesSemantic = mode.data !== "lexical";
2156
2383
  if (usesSemantic && (chunks.some((chunk) => chunk.embedding === null) || quotes.some((quote) => quote.embedding === null))) {
2157
2384
  throw new KnowledgeCliError(
@@ -2182,13 +2409,13 @@ var KnowledgeCli = class {
2182
2409
  command: "knowledge.search",
2183
2410
  data: {
2184
2411
  mode: mode.data,
2185
- model: usesSemantic ? this.#embedder.model : null,
2186
2412
  query,
2187
2413
  results: mergeSearchResults(
2188
2414
  fusedResults(chunks, mode.data, lexical, semantic, limitInput),
2189
2415
  fusedQuoteResults(quotes, mode.data, quoteLexical, quoteSemantic, limitInput),
2190
2416
  limitInput
2191
- )
2417
+ ),
2418
+ sourceFilter: sourceFilter.data
2192
2419
  },
2193
2420
  ok: true
2194
2421
  });
@@ -2196,6 +2423,127 @@ var KnowledgeCli = class {
2196
2423
  database.close();
2197
2424
  }
2198
2425
  }
2426
+ async adopt(projectId, knowledgeRef) {
2427
+ const payload = decodeKnowledgeRef(knowledgeRef);
2428
+ if (!await databaseExists(this.#databasePath)) {
2429
+ throw new KnowledgeCliError(
2430
+ "DXC_KNOWLEDGE_REFERENCE_STALE",
2431
+ "\u77E5\u8BC6\u5F15\u7528\u5BF9\u5E94\u7684\u672C\u5730\u7D22\u5F15\u4E0D\u5B58\u5728\u3002",
2432
+ void 0,
2433
+ { recovery: "\u8BF7\u91CD\u65B0\u5BFC\u5165\u6216\u68C0\u7D22\u5185\u5BB9\uFF0C\u518D\u91C7\u7528\u65B0\u7684\u7ED3\u679C\u3002" }
2434
+ );
2435
+ }
2436
+ const database = await openDatabase(this.#databasePath);
2437
+ let adopted;
2438
+ let quoteVerbatimGuard;
2439
+ try {
2440
+ if (payload.sourceType === "history-article") {
2441
+ const chunk = loadChunks(database).find(
2442
+ (candidate) => candidate.id === payload.chunkId && candidate.articleId === payload.articleId
2443
+ );
2444
+ if (chunk === void 0 || chunk.chunkSha256 !== payload.contentSha256) {
2445
+ throw new KnowledgeCliError(
2446
+ "DXC_KNOWLEDGE_REFERENCE_STALE",
2447
+ "\u5386\u53F2\u6587\u7AE0\u7247\u6BB5\u5DF2\u53D8\u5316\u3002",
2448
+ void 0,
2449
+ { recovery: "\u8BF7\u91CD\u65B0\u68C0\u7D22\u5E76\u8BA9\u7528\u6237\u91CD\u65B0\u9009\u62E9\u5F53\u524D\u7247\u6BB5\u3002" }
2450
+ );
2451
+ }
2452
+ adopted = contentWorkflowAdoptedKnowledgeSchema.parse({
2453
+ heading: chunk.heading,
2454
+ knowledgeRef,
2455
+ label: chunk.title,
2456
+ snippet: snippet(chunk.body),
2457
+ sourceType: "history-article"
2458
+ });
2459
+ } else {
2460
+ const quote = loadActiveQuotes(database).find(
2461
+ (candidate) => candidate.id === payload.quoteId
2462
+ );
2463
+ if (quote === void 0 || quote.contentSha256 !== payload.contentSha256) {
2464
+ throw new KnowledgeCliError(
2465
+ "DXC_KNOWLEDGE_REFERENCE_STALE",
2466
+ "\u91D1\u53E5\u5DF2\u53D8\u5316\u6216\u5DF2\u5F52\u6863\u3002",
2467
+ void 0,
2468
+ { recovery: "\u8BF7\u91CD\u65B0\u68C0\u7D22\u5E76\u8BA9\u7528\u6237\u91CD\u65B0\u9009\u62E9\u5F53\u524D\u91D1\u53E5\u3002" }
2469
+ );
2470
+ }
2471
+ if (quote.verbatimUse === "do-not-use-verbatim") {
2472
+ throw new KnowledgeCliError(
2473
+ "DXC_KNOWLEDGE_REFERENCE_INVALID",
2474
+ "\u8BE5\u91D1\u53E5\u7981\u6B62\u91C7\u7528\u5230\u6587\u7AE0\u9879\u76EE\u3002",
2475
+ void 0,
2476
+ { recovery: "\u8BF7\u4FDD\u7559\u5B83\u4EC5\u4F9B\u4EBA\u5DE5\u67E5\u770B\uFF0C\u6216\u91CD\u65B0\u68C0\u7D22\u5E76\u9009\u62E9\u5141\u8BB8\u91C7\u7528\u7684\u5176\u4ED6\u91D1\u53E5\u3002" }
2477
+ );
2478
+ }
2479
+ adopted = contentWorkflowAdoptedKnowledgeSchema.parse({
2480
+ attribution: quote.attribution,
2481
+ knowledgeRef,
2482
+ label: quote.attribution ?? quote.sourceLabel ?? "\u5DF2\u91C7\u7528\u91D1\u53E5",
2483
+ snippet: snippet(quote.text),
2484
+ sourceType: "quote",
2485
+ verbatimUse: quote.verbatimUse
2486
+ });
2487
+ if (quote.verbatimUse === "needs-verification") {
2488
+ quoteVerbatimGuard = quote.text;
2489
+ }
2490
+ }
2491
+ } finally {
2492
+ database.close();
2493
+ }
2494
+ const project = await new ProjectCli({
2495
+ currentDirectory: this.#currentDirectory,
2496
+ homeDirectory: this.#homeDirectory,
2497
+ now: this.#now
2498
+ }).locateByProjectId(projectId);
2499
+ const target = path2.join(project.directory, PROJECT_CONTEXT_FILE);
2500
+ return withPrivateFileLocks([target], async () => {
2501
+ let store = {
2502
+ adoptedKnowledge: [],
2503
+ quoteVerbatimGuards: [],
2504
+ schemaVersion: "1"
2505
+ };
2506
+ try {
2507
+ store = projectContextStoreSchema.parse(
2508
+ JSON.parse(await readPrivateFile(target, PROJECT_CONTEXT_MAXIMUM_BYTES))
2509
+ );
2510
+ } catch (error) {
2511
+ if (!(error instanceof LocalStateError && error.failure === "missing")) {
2512
+ throw error;
2513
+ }
2514
+ }
2515
+ const adoptedKnowledge = [
2516
+ ...store.adoptedKnowledge.filter(
2517
+ (item) => !sameKnowledgeEntity(item.knowledgeRef, payload)
2518
+ ),
2519
+ adopted
2520
+ ].slice(-20);
2521
+ const quoteVerbatimGuards = [
2522
+ ...store.quoteVerbatimGuards.filter(
2523
+ (item) => !sameKnowledgeEntity(item.knowledgeRef, payload)
2524
+ ),
2525
+ ...quoteVerbatimGuard === void 0 ? [] : [{ knowledgeRef, text: quoteVerbatimGuard }]
2526
+ ].slice(-20);
2527
+ await writePrivateJson(target, {
2528
+ adoptedKnowledge,
2529
+ quoteVerbatimGuards,
2530
+ schemaVersion: "1"
2531
+ });
2532
+ return contentKnowledgeAdoptResultSchema.parse({
2533
+ command: "knowledge.adopt",
2534
+ data: { adopted, projectId: project.manifest.projectId },
2535
+ ok: true
2536
+ });
2537
+ }).catch((error) => {
2538
+ if (error instanceof LocalStateError && error.failure === "busy") {
2539
+ throw new KnowledgeCliError("DXC_KNOWLEDGE_BUSY", "\u9879\u76EE\u77E5\u8BC6\u4E0A\u4E0B\u6587\u6B63\u5728\u66F4\u65B0\u3002", void 0, {
2540
+ recovery: "\u8BF7\u7B49\u5F85\u5F53\u524D\u91C7\u7528\u52A8\u4F5C\u5B8C\u6210\u540E\u91CD\u8BD5\u540C\u4E00 knowledgeRef\u3002",
2541
+ retryable: true
2542
+ });
2543
+ }
2544
+ throw error;
2545
+ });
2546
+ }
2199
2547
  async status() {
2200
2548
  let articleCount = 0;
2201
2549
  let chunkCount = 0;
@@ -2203,9 +2551,7 @@ var KnowledgeCli = class {
2203
2551
  let quoteSummary = {
2204
2552
  activeCount: 0,
2205
2553
  archivedCount: 0,
2206
- sourceInitialized: await quoteSourceExists(quoteSourcePath(this.#homeDirectory)),
2207
- sourcePath: QUOTE_SOURCE_DISPLAY_PATH,
2208
- sourceSha256: null
2554
+ sourceInitialized: await quoteSourceExists(quoteSourcePath(this.#homeDirectory))
2209
2555
  };
2210
2556
  if (await databaseExists(this.#databasePath)) {
2211
2557
  const database = await openDatabase(this.#databasePath);
@@ -2251,13 +2597,6 @@ var KnowledgeCli = class {
2251
2597
  data: {
2252
2598
  articleCount,
2253
2599
  chunkCount,
2254
- databasePath: DATABASE_DISPLAY_PATH,
2255
- model: contentKnowledgeModelSchema.parse({
2256
- dimensions: CONTENT_KNOWLEDGE_EMBEDDING_DIMENSIONS,
2257
- id: CONTENT_KNOWLEDGE_EMBEDDING_MODEL,
2258
- quantization: CONTENT_KNOWLEDGE_EMBEDDING_QUANTIZATION,
2259
- revision: CONTENT_KNOWLEDGE_EMBEDDING_REVISION
2260
- }),
2261
2600
  modelCached: await this.#isModelCached(),
2262
2601
  semanticIndexedChunkCount,
2263
2602
  quotes: quoteSummary