@mastra/pg 1.20.1-alpha.2 → 1.21.0-alpha.4
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/CHANGELOG.md +24 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-storage.md +1 -0
- package/dist/index.cjs +1014 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1015 -9
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/knowledge/index.d.ts +84 -0
- package/dist/storage/domains/knowledge/index.d.ts.map +1 -0
- package/dist/storage/factory-storage.d.ts +22 -0
- package/dist/storage/factory-storage.d.ts.map +1 -1
- package/dist/storage/index.d.ts +2 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
|
|
2
|
-
import { AgentsStorage, BRANCH_SPAN_TYPES, BackgroundTasksStorage, BlobStore, ChannelsStorage, DATASETS_SCHEMA, DATASET_ITEMS_SCHEMA, DATASET_VERSIONS_SCHEMA, DatasetsStorage, EXPERIMENTS_SCHEMA, EXPERIMENT_RESULTS_SCHEMA, EntityType, ExperimentsStorage, FactoryStorage, FavoritesStorage, MCPClientsStorage, MCPServersStorage, METRIC_DISTINCT_COLUMNS, MastraCompositeStore, MemoryStorage, NotificationsStorage, OBSERVATIONAL_MEMORY_TABLE_SCHEMA, ObservabilityStorage, PromptBlocksStorage, SchedulesStorage, ScorerDefinitionsStorage, ScoresStorage, SkillsStorage, TABLE_AGENTS, TABLE_AGENT_VERSIONS, TABLE_BACKGROUND_TASKS, TABLE_CHANNEL_CONFIG, TABLE_CHANNEL_INSTALLATIONS, TABLE_CONFIGS, TABLE_DATASETS, TABLE_DATASET_ITEMS, TABLE_DATASET_VERSIONS, TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS, TABLE_FAVORITES, TABLE_MCP_CLIENTS, TABLE_MCP_CLIENT_VERSIONS, TABLE_MCP_SERVERS, TABLE_MCP_SERVER_VERSIONS, TABLE_MESSAGES, TABLE_NOTIFICATIONS, TABLE_PROMPT_BLOCKS, TABLE_PROMPT_BLOCK_VERSIONS, TABLE_RESOURCES, TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS, TABLE_SCHEMAS, TABLE_SCORERS, TABLE_SCORER_DEFINITIONS, TABLE_SCORER_DEFINITION_VERSIONS, TABLE_SKILLS, TABLE_SKILL_BLOBS, TABLE_SKILL_VERSIONS, TABLE_SPANS, TABLE_THREADS, TABLE_THREAD_STATE, TABLE_TOOL_PROVIDER_CONNECTIONS, TABLE_WORKFLOW_DEFINITIONS, TABLE_WORKFLOW_SNAPSHOT, TABLE_WORKSPACES, TABLE_WORKSPACE_VERSIONS, ThreadStateStorage, ToolProviderConnectionsStorage, TraceStatus, UniqueViolationError, WorkflowDefinitionsStorage, WorkflowsStorage, WorkspacesStorage, calculatePagination, createStorageErrorId, createVectorErrorId, ensureDate, getDefaultValue, getSqlType, hasErrorCode, listBranchesArgsSchema, listFeedbackArgsSchema, listLogsArgsSchema, listMetricsArgsSchema, listScoresArgsSchema, listTracesArgsSchema, mergeWorkflowStepResult, normalizePerPage, normalizeScheduleTarget, parseDuration, safelyParseJSON, storageMessageMatchesMetadataFilter, toTraceSpans, transformScoreRow, validateStorageMetadataFilter } from "@mastra/core/storage";
|
|
2
|
+
import { AgentsStorage, BRANCH_SPAN_TYPES, BackgroundTasksStorage, BlobStore, ChannelsStorage, DATASETS_SCHEMA, DATASET_ITEMS_SCHEMA, DATASET_VERSIONS_SCHEMA, DatasetsStorage, EXPERIMENTS_SCHEMA, EXPERIMENT_RESULTS_SCHEMA, EntityType, ExperimentsStorage, FactoryStorage, FavoritesStorage, KNOWLEDGE_ACTIVITY_SCHEMA, KNOWLEDGE_CURSORS_SCHEMA, KNOWLEDGE_MENTIONS_SCHEMA, KNOWLEDGE_NODES_SCHEMA, KNOWLEDGE_RECORDS_SCHEMA, KNOWLEDGE_SEMANTIC_OUTBOX_SCHEMA, KnowledgeConflictError, KnowledgeNotFoundError, KnowledgeStorage, MCPClientsStorage, MCPServersStorage, METRIC_DISTINCT_COLUMNS, MastraCompositeStore, MemoryStorage, NotificationsStorage, OBSERVATIONAL_MEMORY_TABLE_SCHEMA, ObservabilityStorage, PromptBlocksStorage, SchedulesStorage, ScorerDefinitionsStorage, ScoresStorage, SkillsStorage, TABLE_AGENTS, TABLE_AGENT_VERSIONS, TABLE_BACKGROUND_TASKS, TABLE_CHANNEL_CONFIG, TABLE_CHANNEL_INSTALLATIONS, TABLE_CONFIGS, TABLE_DATASETS, TABLE_DATASET_ITEMS, TABLE_DATASET_VERSIONS, TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS, TABLE_FAVORITES, TABLE_KNOWLEDGE_ACTIVITY, TABLE_KNOWLEDGE_CURSORS, TABLE_KNOWLEDGE_MENTIONS, TABLE_KNOWLEDGE_NODES, TABLE_KNOWLEDGE_RECORDS, TABLE_KNOWLEDGE_SEMANTIC_OUTBOX, TABLE_MCP_CLIENTS, TABLE_MCP_CLIENT_VERSIONS, TABLE_MCP_SERVERS, TABLE_MCP_SERVER_VERSIONS, TABLE_MESSAGES, TABLE_NOTIFICATIONS, TABLE_PROMPT_BLOCKS, TABLE_PROMPT_BLOCK_VERSIONS, TABLE_RESOURCES, TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS, TABLE_SCHEMAS, TABLE_SCORERS, TABLE_SCORER_DEFINITIONS, TABLE_SCORER_DEFINITION_VERSIONS, TABLE_SKILLS, TABLE_SKILL_BLOBS, TABLE_SKILL_VERSIONS, TABLE_SPANS, TABLE_THREADS, TABLE_THREAD_STATE, TABLE_TOOL_PROVIDER_CONNECTIONS, TABLE_WORKFLOW_DEFINITIONS, TABLE_WORKFLOW_SNAPSHOT, TABLE_WORKSPACES, TABLE_WORKSPACE_VERSIONS, ThreadStateStorage, ToolProviderConnectionsStorage, TraceStatus, UniqueViolationError, WorkflowDefinitionsStorage, WorkflowsStorage, WorkspacesStorage, assertKnowledgeCeilingRaised, assertKnowledgeScopeWithinCeiling, calculatePagination, canonicalizeKnowledgeScope, createKnowledgeUlid, createStorageErrorId, createVectorErrorId, ensureDate, getDefaultValue, getSqlType, hasErrorCode, isKnowledgeScopeVisible, knowledgeScopeKey, knowledgeSemanticDocumentId, knowledgeSemanticIdempotencyKey, listBranchesArgsSchema, listFeedbackArgsSchema, listLogsArgsSchema, listMetricsArgsSchema, listScoresArgsSchema, listTracesArgsSchema, mergeWorkflowStepResult, normalizePerPage, normalizeScheduleTarget, parseDuration, parseKnowledgeNodeCursor, parseKnowledgeWikilinks, safelyParseJSON, storageMessageMatchesMetadataFilter, toTraceSpans, transformScoreRow, validateStorageMetadataFilter } from "@mastra/core/storage";
|
|
3
3
|
import { parseFieldKey, parseSqlIdentifier } from "@mastra/core/utils";
|
|
4
4
|
import { MastraVector, validateTopK, validateUpsertInput } from "@mastra/core/vector";
|
|
5
5
|
import { Mutex } from "async-mutex";
|
|
@@ -327,7 +327,7 @@ const FILTER_OPERATORS = {
|
|
|
327
327
|
return {
|
|
328
328
|
sql,
|
|
329
329
|
needsValue: true,
|
|
330
|
-
transformValue: () => Array.isArray(value) ? value.map(String) : typeof value === "string" ? escapeLikePattern(value) : value
|
|
330
|
+
transformValue: () => Array.isArray(value) ? value.map(String) : typeof value === "string" ? escapeLikePattern$1(value) : value
|
|
331
331
|
};
|
|
332
332
|
},
|
|
333
333
|
/**
|
|
@@ -351,7 +351,7 @@ const FILTER_OPERATORS = {
|
|
|
351
351
|
const parseJsonPathKey = (key) => {
|
|
352
352
|
return (key !== "" ? parseFieldKey(key) : "").replace(/\./g, ",");
|
|
353
353
|
};
|
|
354
|
-
function escapeLikePattern(str) {
|
|
354
|
+
function escapeLikePattern$1(str) {
|
|
355
355
|
return str.replace(/([%_\\])/g, "\\$1");
|
|
356
356
|
}
|
|
357
357
|
/**
|
|
@@ -4688,7 +4688,7 @@ function serializeJson(v) {
|
|
|
4688
4688
|
if (typeof v === "object" && v != null) return JSON.stringify(v);
|
|
4689
4689
|
return v ?? null;
|
|
4690
4690
|
}
|
|
4691
|
-
function parseJson$
|
|
4691
|
+
function parseJson$2(v) {
|
|
4692
4692
|
if (typeof v === "string") try {
|
|
4693
4693
|
return JSON.parse(v);
|
|
4694
4694
|
} catch {
|
|
@@ -4707,14 +4707,14 @@ function rowToTask(row) {
|
|
|
4707
4707
|
status: row.status,
|
|
4708
4708
|
toolName: row.tool_name,
|
|
4709
4709
|
toolCallId: row.tool_call_id,
|
|
4710
|
-
args: parseJson$
|
|
4710
|
+
args: parseJson$2(row.args),
|
|
4711
4711
|
agentId: row.agent_id,
|
|
4712
4712
|
threadId: row.thread_id ?? void 0,
|
|
4713
4713
|
resourceId: row.resource_id ?? void 0,
|
|
4714
4714
|
runId: row.run_id,
|
|
4715
|
-
result: parseJson$
|
|
4716
|
-
error: parseJson$
|
|
4717
|
-
suspendPayload: parseJson$
|
|
4715
|
+
result: parseJson$2(row.result),
|
|
4716
|
+
error: parseJson$2(row.error),
|
|
4717
|
+
suspendPayload: parseJson$2(row.suspend_payload),
|
|
4718
4718
|
retryCount: Number(row.retry_count),
|
|
4719
4719
|
maxRetries: Number(row.max_retries),
|
|
4720
4720
|
timeoutMs: Number(row.timeout_ms),
|
|
@@ -7584,6 +7584,977 @@ var FavoritesPG = class FavoritesPG extends FavoritesStorage {
|
|
|
7584
7584
|
}
|
|
7585
7585
|
};
|
|
7586
7586
|
//#endregion
|
|
7587
|
+
//#region src/storage/domains/knowledge/index.ts
|
|
7588
|
+
const camelCaseColumns = [
|
|
7589
|
+
"canonicalName",
|
|
7590
|
+
"scopeKey",
|
|
7591
|
+
"mergedInto",
|
|
7592
|
+
"createdAt",
|
|
7593
|
+
"updatedAt",
|
|
7594
|
+
"node",
|
|
7595
|
+
"sourceThreadId",
|
|
7596
|
+
"capturedAt",
|
|
7597
|
+
"maxScope",
|
|
7598
|
+
"deletedAt",
|
|
7599
|
+
"deletedBy",
|
|
7600
|
+
"sourceType",
|
|
7601
|
+
"sourceId",
|
|
7602
|
+
"recordId",
|
|
7603
|
+
"lastKnowledgeId",
|
|
7604
|
+
"recordType",
|
|
7605
|
+
"idempotencyKey",
|
|
7606
|
+
"documentId",
|
|
7607
|
+
"documentType",
|
|
7608
|
+
"availableAt",
|
|
7609
|
+
"claimedAt",
|
|
7610
|
+
"claimedBy",
|
|
7611
|
+
"completedAt"
|
|
7612
|
+
];
|
|
7613
|
+
function transformSqlCode(sql, transform) {
|
|
7614
|
+
return sql.split(/('(?:''|[^'])*')/g).map((part, index) => index % 2 === 0 ? transform(part) : part).join("");
|
|
7615
|
+
}
|
|
7616
|
+
function postgresSql(sql, schemaName) {
|
|
7617
|
+
let normalized = transformSqlCode(sql, (code) => {
|
|
7618
|
+
let transformed = code.replace(/jsonb\(\?\)/g, "?::jsonb");
|
|
7619
|
+
for (const column of camelCaseColumns) transformed = transformed.replace(new RegExp(`(?<!")\\b${column}\\b(?!")`, "g"), `"${column}"`);
|
|
7620
|
+
return transformed;
|
|
7621
|
+
});
|
|
7622
|
+
if (schemaName) {
|
|
7623
|
+
const quotedSchema = `"${parseSqlIdentifier(schemaName, "schema name")}"`;
|
|
7624
|
+
normalized = transformSqlCode(normalized, (code) => {
|
|
7625
|
+
let transformed = code;
|
|
7626
|
+
for (const table of [
|
|
7627
|
+
TABLE_KNOWLEDGE_NODES,
|
|
7628
|
+
TABLE_KNOWLEDGE_RECORDS,
|
|
7629
|
+
TABLE_KNOWLEDGE_MENTIONS,
|
|
7630
|
+
TABLE_KNOWLEDGE_CURSORS,
|
|
7631
|
+
TABLE_KNOWLEDGE_ACTIVITY,
|
|
7632
|
+
TABLE_KNOWLEDGE_SEMANTIC_OUTBOX
|
|
7633
|
+
]) transformed = transformed.replaceAll(`"${table}"`, `${quotedSchema}."${table}"`);
|
|
7634
|
+
return transformed;
|
|
7635
|
+
});
|
|
7636
|
+
}
|
|
7637
|
+
let index = 0;
|
|
7638
|
+
return transformSqlCode(normalized, (code) => code.replace(/\?/g, () => `$${++index}`));
|
|
7639
|
+
}
|
|
7640
|
+
function createExecutor(client, schemaName) {
|
|
7641
|
+
return { async execute(statement) {
|
|
7642
|
+
const sql = typeof statement === "string" ? statement : statement.sql;
|
|
7643
|
+
const args = typeof statement === "string" ? [] : statement.args ?? [];
|
|
7644
|
+
const result = await client.query(postgresSql(sql, schemaName), args);
|
|
7645
|
+
return {
|
|
7646
|
+
rows: result.rows,
|
|
7647
|
+
rowsAffected: result.rowCount ?? 0
|
|
7648
|
+
};
|
|
7649
|
+
} };
|
|
7650
|
+
}
|
|
7651
|
+
const visibleSql = `(scopeKey = ? OR LEFT(?, LENGTH(scopeKey) + 1) = scopeKey || chr(31))`;
|
|
7652
|
+
function parseJson$1(value) {
|
|
7653
|
+
if (typeof value === "string") return JSON.parse(value);
|
|
7654
|
+
if (value instanceof Uint8Array) return JSON.parse(new TextDecoder().decode(value));
|
|
7655
|
+
if (value instanceof ArrayBuffer) return JSON.parse(new TextDecoder().decode(value));
|
|
7656
|
+
return value;
|
|
7657
|
+
}
|
|
7658
|
+
/**
|
|
7659
|
+
* Knowledge timestamps are written as UTC digits into timezone-naive columns
|
|
7660
|
+
* ({@link postgresTimestamp} and the ISO strings passed to inserts), but the
|
|
7661
|
+
* driver parses naive columns in the process-local timezone. Reinterpret the
|
|
7662
|
+
* parsed components as UTC so reads return the digits that were stored.
|
|
7663
|
+
*/
|
|
7664
|
+
function toDate$1(value) {
|
|
7665
|
+
if (value instanceof Date) return new Date(Date.UTC(value.getFullYear(), value.getMonth(), value.getDate(), value.getHours(), value.getMinutes(), value.getSeconds(), value.getMilliseconds()));
|
|
7666
|
+
const text = String(value);
|
|
7667
|
+
if (/(Z|[+-]\d{2}(:?\d{2})?)$/.test(text)) return new Date(text);
|
|
7668
|
+
return /* @__PURE__ */ new Date(`${text.replace(" ", "T")}Z`);
|
|
7669
|
+
}
|
|
7670
|
+
function optionalDate(value) {
|
|
7671
|
+
return value == null ? void 0 : toDate$1(value);
|
|
7672
|
+
}
|
|
7673
|
+
function postgresTimestamp(value) {
|
|
7674
|
+
const pad = (part, width = 2) => String(part).padStart(width, "0");
|
|
7675
|
+
return `${value.getUTCFullYear()}-${pad(value.getUTCMonth() + 1)}-${pad(value.getUTCDate())} ${pad(value.getUTCHours())}:${pad(value.getUTCMinutes())}:${pad(value.getUTCSeconds())}.${pad(value.getUTCMilliseconds(), 3)}`;
|
|
7676
|
+
}
|
|
7677
|
+
function canonicalName(name) {
|
|
7678
|
+
return name.trim().toLocaleLowerCase();
|
|
7679
|
+
}
|
|
7680
|
+
function nodeReferenceId(node) {
|
|
7681
|
+
return typeof node === "string" ? node : node.id;
|
|
7682
|
+
}
|
|
7683
|
+
function escapeLikePattern(value) {
|
|
7684
|
+
return value.replaceAll("=", "==").replaceAll("%", "=%").replaceAll("_", "=_");
|
|
7685
|
+
}
|
|
7686
|
+
function parseNode(row) {
|
|
7687
|
+
return {
|
|
7688
|
+
id: String(row.id),
|
|
7689
|
+
type: "node",
|
|
7690
|
+
name: String(row.name),
|
|
7691
|
+
kind: String(row.kind),
|
|
7692
|
+
content: row.content == null ? void 0 : String(row.content),
|
|
7693
|
+
scope: parseJson$1(row.scopeJson ?? row.scope),
|
|
7694
|
+
version: Number(row.version),
|
|
7695
|
+
mergedInto: row.mergedInto == null ? void 0 : String(row.mergedInto),
|
|
7696
|
+
createdAt: toDate$1(row.createdAt),
|
|
7697
|
+
updatedAt: toDate$1(row.updatedAt)
|
|
7698
|
+
};
|
|
7699
|
+
}
|
|
7700
|
+
function parseKnowledge(row) {
|
|
7701
|
+
return {
|
|
7702
|
+
id: String(row.id),
|
|
7703
|
+
node: String(row.node),
|
|
7704
|
+
text: String(row.text),
|
|
7705
|
+
scope: parseJson$1(row.scopeJson ?? row.scope),
|
|
7706
|
+
sourceThreadId: String(row.sourceThreadId),
|
|
7707
|
+
capturedAt: toDate$1(row.capturedAt),
|
|
7708
|
+
when: optionalDate(row.when),
|
|
7709
|
+
maxScope: row.maxScope == null ? void 0 : String(row.maxScope),
|
|
7710
|
+
metadata: row.metadata == null ? void 0 : parseJson$1(row.metadata),
|
|
7711
|
+
deletedAt: optionalDate(row.deletedAt),
|
|
7712
|
+
deletedBy: row.deletedBy == null ? void 0 : String(row.deletedBy)
|
|
7713
|
+
};
|
|
7714
|
+
}
|
|
7715
|
+
function parseOutbox(row) {
|
|
7716
|
+
return {
|
|
7717
|
+
id: String(row.id),
|
|
7718
|
+
idempotencyKey: String(row.idempotencyKey),
|
|
7719
|
+
documentId: String(row.documentId),
|
|
7720
|
+
documentType: String(row.documentType),
|
|
7721
|
+
operation: String(row.operation),
|
|
7722
|
+
scope: parseJson$1(row.scopeJson ?? row.scope),
|
|
7723
|
+
status: String(row.status),
|
|
7724
|
+
attempts: Number(row.attempts),
|
|
7725
|
+
availableAt: toDate$1(row.availableAt),
|
|
7726
|
+
claimedAt: optionalDate(row.claimedAt),
|
|
7727
|
+
claimedBy: row.claimedBy == null ? void 0 : String(row.claimedBy),
|
|
7728
|
+
createdAt: toDate$1(row.createdAt),
|
|
7729
|
+
completedAt: optionalDate(row.completedAt)
|
|
7730
|
+
};
|
|
7731
|
+
}
|
|
7732
|
+
function knowledgeIndexes(schemaName) {
|
|
7733
|
+
const table = (name) => {
|
|
7734
|
+
const quotedName = `"${parseSqlIdentifier(name, "table name")}"`;
|
|
7735
|
+
return schemaName ? `"${parseSqlIdentifier(schemaName, "schema name")}".${quotedName}` : quotedName;
|
|
7736
|
+
};
|
|
7737
|
+
return [
|
|
7738
|
+
{
|
|
7739
|
+
name: "idx_knowledge_nodes_identity",
|
|
7740
|
+
sql: `CREATE UNIQUE INDEX IF NOT EXISTS idx_knowledge_nodes_identity ON ${table(TABLE_KNOWLEDGE_NODES)} ("type", "scopeKey", "canonicalName");`
|
|
7741
|
+
},
|
|
7742
|
+
{
|
|
7743
|
+
name: "idx_knowledge_nodes_scope",
|
|
7744
|
+
sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_nodes_scope ON ${table(TABLE_KNOWLEDGE_NODES)} ("scopeKey", "type");`
|
|
7745
|
+
},
|
|
7746
|
+
{
|
|
7747
|
+
name: "idx_knowledge_records_node_latest",
|
|
7748
|
+
sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_records_node_latest ON ${table(TABLE_KNOWLEDGE_RECORDS)} ("node", "id" DESC);`
|
|
7749
|
+
},
|
|
7750
|
+
{
|
|
7751
|
+
name: "idx_knowledge_records_thread_latest",
|
|
7752
|
+
sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_records_thread_latest ON ${table(TABLE_KNOWLEDGE_RECORDS)} ("sourceThreadId", "id" DESC);`
|
|
7753
|
+
},
|
|
7754
|
+
{
|
|
7755
|
+
name: "idx_knowledge_mentions_record",
|
|
7756
|
+
sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_mentions_record ON ${table(TABLE_KNOWLEDGE_MENTIONS)} ("recordId", "sourceType", "sourceId");`
|
|
7757
|
+
},
|
|
7758
|
+
{
|
|
7759
|
+
name: "idx_knowledge_activity_latest",
|
|
7760
|
+
sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_activity_latest ON ${table(TABLE_KNOWLEDGE_ACTIVITY)} ("id" DESC);`
|
|
7761
|
+
},
|
|
7762
|
+
{
|
|
7763
|
+
name: "idx_knowledge_outbox_idempotency",
|
|
7764
|
+
sql: `CREATE UNIQUE INDEX IF NOT EXISTS idx_knowledge_outbox_idempotency ON ${table(TABLE_KNOWLEDGE_SEMANTIC_OUTBOX)} ("idempotencyKey");`
|
|
7765
|
+
},
|
|
7766
|
+
{
|
|
7767
|
+
name: "idx_knowledge_outbox_claim",
|
|
7768
|
+
sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_outbox_claim ON ${table(TABLE_KNOWLEDGE_SEMANTIC_OUTBOX)} ("status", "availableAt", "createdAt");`
|
|
7769
|
+
}
|
|
7770
|
+
];
|
|
7771
|
+
}
|
|
7772
|
+
function knowledgeIndexDDL(schemaName) {
|
|
7773
|
+
return knowledgeIndexes(schemaName).map((index) => index.sql);
|
|
7774
|
+
}
|
|
7775
|
+
var KnowledgePG = class extends KnowledgeStorage {
|
|
7776
|
+
static MANAGED_TABLES = [
|
|
7777
|
+
TABLE_KNOWLEDGE_NODES,
|
|
7778
|
+
TABLE_KNOWLEDGE_RECORDS,
|
|
7779
|
+
TABLE_KNOWLEDGE_MENTIONS,
|
|
7780
|
+
TABLE_KNOWLEDGE_CURSORS,
|
|
7781
|
+
TABLE_KNOWLEDGE_ACTIVITY,
|
|
7782
|
+
TABLE_KNOWLEDGE_SEMANTIC_OUTBOX
|
|
7783
|
+
];
|
|
7784
|
+
static getExportDDL(schemaName) {
|
|
7785
|
+
return [
|
|
7786
|
+
generateTableSQL({
|
|
7787
|
+
tableName: TABLE_KNOWLEDGE_NODES,
|
|
7788
|
+
schema: KNOWLEDGE_NODES_SCHEMA,
|
|
7789
|
+
schemaName,
|
|
7790
|
+
includeAllConstraints: true
|
|
7791
|
+
}),
|
|
7792
|
+
generateTableSQL({
|
|
7793
|
+
tableName: TABLE_KNOWLEDGE_RECORDS,
|
|
7794
|
+
schema: KNOWLEDGE_RECORDS_SCHEMA,
|
|
7795
|
+
schemaName,
|
|
7796
|
+
includeAllConstraints: true
|
|
7797
|
+
}),
|
|
7798
|
+
generateTableSQL({
|
|
7799
|
+
tableName: TABLE_KNOWLEDGE_MENTIONS,
|
|
7800
|
+
schema: KNOWLEDGE_MENTIONS_SCHEMA,
|
|
7801
|
+
schemaName,
|
|
7802
|
+
compositePrimaryKey: [
|
|
7803
|
+
"sourceType",
|
|
7804
|
+
"sourceId",
|
|
7805
|
+
"recordId"
|
|
7806
|
+
],
|
|
7807
|
+
includeAllConstraints: true
|
|
7808
|
+
}),
|
|
7809
|
+
generateTableSQL({
|
|
7810
|
+
tableName: TABLE_KNOWLEDGE_CURSORS,
|
|
7811
|
+
schema: KNOWLEDGE_CURSORS_SCHEMA,
|
|
7812
|
+
schemaName,
|
|
7813
|
+
compositePrimaryKey: ["sourceThreadId", "agent"],
|
|
7814
|
+
includeAllConstraints: true
|
|
7815
|
+
}),
|
|
7816
|
+
generateTableSQL({
|
|
7817
|
+
tableName: TABLE_KNOWLEDGE_ACTIVITY,
|
|
7818
|
+
schema: KNOWLEDGE_ACTIVITY_SCHEMA,
|
|
7819
|
+
schemaName,
|
|
7820
|
+
includeAllConstraints: true
|
|
7821
|
+
}),
|
|
7822
|
+
generateTableSQL({
|
|
7823
|
+
tableName: TABLE_KNOWLEDGE_SEMANTIC_OUTBOX,
|
|
7824
|
+
schema: KNOWLEDGE_SEMANTIC_OUTBOX_SCHEMA,
|
|
7825
|
+
schemaName,
|
|
7826
|
+
includeAllConstraints: true
|
|
7827
|
+
}),
|
|
7828
|
+
...knowledgeIndexDDL(schemaName)
|
|
7829
|
+
];
|
|
7830
|
+
}
|
|
7831
|
+
#client;
|
|
7832
|
+
#executor;
|
|
7833
|
+
#db;
|
|
7834
|
+
#schemaName;
|
|
7835
|
+
constructor(config) {
|
|
7836
|
+
super();
|
|
7837
|
+
const { client, schemaName, skipDefaultIndexes } = resolvePgConfig(config);
|
|
7838
|
+
this.#client = client;
|
|
7839
|
+
this.#schemaName = schemaName;
|
|
7840
|
+
this.#executor = createExecutor(client, schemaName);
|
|
7841
|
+
this.#db = new PgDB({
|
|
7842
|
+
client,
|
|
7843
|
+
schemaName,
|
|
7844
|
+
skipDefaultIndexes
|
|
7845
|
+
});
|
|
7846
|
+
}
|
|
7847
|
+
async init() {
|
|
7848
|
+
await this.#db.createTable({
|
|
7849
|
+
tableName: TABLE_KNOWLEDGE_NODES,
|
|
7850
|
+
schema: KNOWLEDGE_NODES_SCHEMA
|
|
7851
|
+
});
|
|
7852
|
+
await this.#db.createTable({
|
|
7853
|
+
tableName: TABLE_KNOWLEDGE_RECORDS,
|
|
7854
|
+
schema: KNOWLEDGE_RECORDS_SCHEMA
|
|
7855
|
+
});
|
|
7856
|
+
await this.#db.createTable({
|
|
7857
|
+
tableName: TABLE_KNOWLEDGE_MENTIONS,
|
|
7858
|
+
schema: KNOWLEDGE_MENTIONS_SCHEMA,
|
|
7859
|
+
compositePrimaryKey: [
|
|
7860
|
+
"sourceType",
|
|
7861
|
+
"sourceId",
|
|
7862
|
+
"recordId"
|
|
7863
|
+
]
|
|
7864
|
+
});
|
|
7865
|
+
await this.#db.createTable({
|
|
7866
|
+
tableName: TABLE_KNOWLEDGE_CURSORS,
|
|
7867
|
+
schema: KNOWLEDGE_CURSORS_SCHEMA,
|
|
7868
|
+
compositePrimaryKey: ["sourceThreadId", "agent"]
|
|
7869
|
+
});
|
|
7870
|
+
await this.#db.createTable({
|
|
7871
|
+
tableName: TABLE_KNOWLEDGE_ACTIVITY,
|
|
7872
|
+
schema: KNOWLEDGE_ACTIVITY_SCHEMA
|
|
7873
|
+
});
|
|
7874
|
+
await this.#db.createTable({
|
|
7875
|
+
tableName: TABLE_KNOWLEDGE_SEMANTIC_OUTBOX,
|
|
7876
|
+
schema: KNOWLEDGE_SEMANTIC_OUTBOX_SCHEMA
|
|
7877
|
+
});
|
|
7878
|
+
await Promise.all(knowledgeIndexes(this.#schemaName).map((index) => this.#db.createIndexFromStatement(index.name, index.sql)));
|
|
7879
|
+
}
|
|
7880
|
+
async dangerouslyClearAll() {
|
|
7881
|
+
await this.#transaction(async (tx) => {
|
|
7882
|
+
for (const table of [
|
|
7883
|
+
TABLE_KNOWLEDGE_MENTIONS,
|
|
7884
|
+
TABLE_KNOWLEDGE_RECORDS,
|
|
7885
|
+
TABLE_KNOWLEDGE_NODES,
|
|
7886
|
+
TABLE_KNOWLEDGE_CURSORS,
|
|
7887
|
+
TABLE_KNOWLEDGE_ACTIVITY,
|
|
7888
|
+
TABLE_KNOWLEDGE_SEMANTIC_OUTBOX
|
|
7889
|
+
]) await tx.execute(`DELETE FROM "${table}"`);
|
|
7890
|
+
});
|
|
7891
|
+
}
|
|
7892
|
+
async createNode(input) {
|
|
7893
|
+
const scope = canonicalizeKnowledgeScope(input.scope);
|
|
7894
|
+
return this.#transaction(async (tx) => {
|
|
7895
|
+
const existing = await this.#getNodeByName(tx, input.name, scope);
|
|
7896
|
+
if (existing) {
|
|
7897
|
+
const terminal = await this.#resolveTerminalNode(tx, existing.id);
|
|
7898
|
+
if (!isKnowledgeScopeVisible(terminal.scope, scope)) throw new Error(`Merged knowledge node is not visible from scope: ${input.name}`);
|
|
7899
|
+
return terminal;
|
|
7900
|
+
}
|
|
7901
|
+
const now = /* @__PURE__ */ new Date();
|
|
7902
|
+
const node = {
|
|
7903
|
+
id: input.id ?? crypto.randomUUID(),
|
|
7904
|
+
type: "node",
|
|
7905
|
+
name: input.name.trim(),
|
|
7906
|
+
kind: input.kind,
|
|
7907
|
+
content: input.content,
|
|
7908
|
+
scope,
|
|
7909
|
+
version: 1,
|
|
7910
|
+
createdAt: now,
|
|
7911
|
+
updatedAt: now
|
|
7912
|
+
};
|
|
7913
|
+
await tx.execute({
|
|
7914
|
+
sql: `INSERT INTO "${TABLE_KNOWLEDGE_NODES}" (id,type,name,canonicalName,kind,content,scope,scopeKey,version,mergedInto,createdAt,updatedAt) VALUES (?,?,?,?,?,?,jsonb(?),?,?,NULL,?,?)`,
|
|
7915
|
+
args: [
|
|
7916
|
+
node.id,
|
|
7917
|
+
"node",
|
|
7918
|
+
node.name,
|
|
7919
|
+
canonicalName(node.name),
|
|
7920
|
+
node.kind,
|
|
7921
|
+
node.content ?? null,
|
|
7922
|
+
JSON.stringify(scope),
|
|
7923
|
+
knowledgeScopeKey(scope),
|
|
7924
|
+
node.version,
|
|
7925
|
+
now.toISOString(),
|
|
7926
|
+
now.toISOString()
|
|
7927
|
+
]
|
|
7928
|
+
});
|
|
7929
|
+
await this.#replaceMentions(tx, "node", node.id, node.content ?? "", input.resolutionScope ?? scope, scope);
|
|
7930
|
+
await this.#activity(tx, "node-created", "node", node.id, scope);
|
|
7931
|
+
await this.#outbox(tx, "node", node.id, "upsert", node.version, scope);
|
|
7932
|
+
return node;
|
|
7933
|
+
});
|
|
7934
|
+
}
|
|
7935
|
+
async getNode(id) {
|
|
7936
|
+
return this.#getNode(this.#executor, id);
|
|
7937
|
+
}
|
|
7938
|
+
async getNodeByName(input) {
|
|
7939
|
+
return this.#getNodeByName(this.#executor, input.name, canonicalizeKnowledgeScope(input.scope));
|
|
7940
|
+
}
|
|
7941
|
+
async resolveNode(input) {
|
|
7942
|
+
return this.#resolveNode(this.#executor, input.name, canonicalizeKnowledgeScope(input.scope));
|
|
7943
|
+
}
|
|
7944
|
+
async listNodes(input) {
|
|
7945
|
+
const key = knowledgeScopeKey(canonicalizeKnowledgeScope(input.scope));
|
|
7946
|
+
const clauses = [
|
|
7947
|
+
`type = 'node'`,
|
|
7948
|
+
"mergedInto IS NULL",
|
|
7949
|
+
visibleSql
|
|
7950
|
+
];
|
|
7951
|
+
const args = [key, key];
|
|
7952
|
+
if (input.namePrefix) {
|
|
7953
|
+
clauses.push("canonicalName LIKE ? ESCAPE '='");
|
|
7954
|
+
args.push(`${escapeLikePattern(canonicalName(input.namePrefix))}%`);
|
|
7955
|
+
}
|
|
7956
|
+
if (input.kind) {
|
|
7957
|
+
clauses.push("kind = ?");
|
|
7958
|
+
args.push(input.kind);
|
|
7959
|
+
}
|
|
7960
|
+
if (input.hasContent !== void 0) clauses.push(input.hasContent ? "content IS NOT NULL AND content <> ''" : "(content IS NULL OR content = '')");
|
|
7961
|
+
if (input.cursor) {
|
|
7962
|
+
const cursor = parseKnowledgeNodeCursor(input.cursor, {
|
|
7963
|
+
namePrefix: input.namePrefix,
|
|
7964
|
+
kind: input.kind,
|
|
7965
|
+
hasContent: input.hasContent
|
|
7966
|
+
});
|
|
7967
|
+
const updatedAt = postgresTimestamp(cursor.updatedAt);
|
|
7968
|
+
clauses.push("(updatedAt < ? OR (updatedAt = ? AND (name > ? OR (name = ? AND id > ?))))");
|
|
7969
|
+
args.push(updatedAt, updatedAt, cursor.name, cursor.name, cursor.id);
|
|
7970
|
+
}
|
|
7971
|
+
args.push(input.limit ?? 100);
|
|
7972
|
+
return (await this.#executor.execute({
|
|
7973
|
+
sql: `SELECT *, scope AS "scopeJson" FROM "${TABLE_KNOWLEDGE_NODES}" WHERE ${clauses.join(" AND ")} ORDER BY updatedAt DESC, name ASC, id ASC LIMIT ?`,
|
|
7974
|
+
args
|
|
7975
|
+
})).rows.map(parseNode);
|
|
7976
|
+
}
|
|
7977
|
+
async updateNode(input) {
|
|
7978
|
+
return this.#transaction(async (tx) => {
|
|
7979
|
+
const existing = await this.#getNode(tx, input.id);
|
|
7980
|
+
if (!existing) throw new KnowledgeNotFoundError("node", input.id);
|
|
7981
|
+
if (existing.mergedInto) throw new Error(`Cannot update merged knowledge node: ${input.id}`);
|
|
7982
|
+
const scope = canonicalizeKnowledgeScope(input.scope ?? existing.scope);
|
|
7983
|
+
const name = (input.name ?? existing.name).trim();
|
|
7984
|
+
const content = input.content ?? existing.content;
|
|
7985
|
+
const now = /* @__PURE__ */ new Date();
|
|
7986
|
+
if ((await tx.execute({
|
|
7987
|
+
sql: `UPDATE "${TABLE_KNOWLEDGE_NODES}" SET name=?,canonicalName=?,kind=?,content=?,scope=jsonb(?),scopeKey=?,version=version+1,updatedAt=? WHERE id=? AND type='node' AND version=?`,
|
|
7988
|
+
args: [
|
|
7989
|
+
name,
|
|
7990
|
+
canonicalName(name),
|
|
7991
|
+
input.kind ?? existing.kind,
|
|
7992
|
+
content ?? null,
|
|
7993
|
+
JSON.stringify(scope),
|
|
7994
|
+
knowledgeScopeKey(scope),
|
|
7995
|
+
now.toISOString(),
|
|
7996
|
+
input.id,
|
|
7997
|
+
input.version
|
|
7998
|
+
]
|
|
7999
|
+
})).rowsAffected === 0) throw new KnowledgeConflictError(input.id);
|
|
8000
|
+
if (input.content !== void 0 || input.name !== void 0 || input.scope !== void 0) await this.#replaceMentions(tx, "node", input.id, content ?? "", input.resolutionScope ?? scope, scope);
|
|
8001
|
+
await this.#activity(tx, "node-updated", "node", input.id, scope);
|
|
8002
|
+
if (knowledgeScopeKey(existing.scope) !== knowledgeScopeKey(scope)) {
|
|
8003
|
+
await this.#outbox(tx, "node", input.id, "delete", createKnowledgeUlid(), existing.scope);
|
|
8004
|
+
const records = await tx.execute({
|
|
8005
|
+
sql: `SELECT id,scope AS "scopeJson",deletedAt FROM "${TABLE_KNOWLEDGE_RECORDS}" WHERE node=?`,
|
|
8006
|
+
args: [input.id]
|
|
8007
|
+
});
|
|
8008
|
+
for (const row of records.rows) {
|
|
8009
|
+
const factScope = parseJson$1(row.scopeJson);
|
|
8010
|
+
await this.#outbox(tx, "record", String(row.id), "delete", createKnowledgeUlid(), factScope);
|
|
8011
|
+
if (row.deletedAt == null) await this.#outbox(tx, "record", String(row.id), "upsert", createKnowledgeUlid(), factScope);
|
|
8012
|
+
}
|
|
8013
|
+
}
|
|
8014
|
+
await this.#outbox(tx, "node", input.id, "upsert", input.version + 1, scope);
|
|
8015
|
+
return {
|
|
8016
|
+
...existing,
|
|
8017
|
+
name,
|
|
8018
|
+
kind: input.kind ?? existing.kind,
|
|
8019
|
+
content,
|
|
8020
|
+
scope,
|
|
8021
|
+
version: input.version + 1,
|
|
8022
|
+
updatedAt: now
|
|
8023
|
+
};
|
|
8024
|
+
});
|
|
8025
|
+
}
|
|
8026
|
+
async mergeNodes(input) {
|
|
8027
|
+
if (input.sourceId === input.targetId) throw new Error("Cannot merge a knowledge node into itself");
|
|
8028
|
+
return this.#transaction(async (tx) => {
|
|
8029
|
+
const source = await this.#getNode(tx, input.sourceId);
|
|
8030
|
+
if (!source) throw new KnowledgeNotFoundError("node", input.sourceId);
|
|
8031
|
+
const target = await this.#resolveTerminalNode(tx, input.targetId);
|
|
8032
|
+
if (!target) throw new KnowledgeNotFoundError("node", input.targetId);
|
|
8033
|
+
if (target.id === source.id) throw new Error("Cannot create a knowledge merge cycle");
|
|
8034
|
+
if (!isKnowledgeScopeVisible(target.scope, source.scope)) throw new Error("Cannot merge a knowledge node into a target that is narrower than its source scope");
|
|
8035
|
+
const affected = await tx.execute({
|
|
8036
|
+
sql: `SELECT DISTINCT m.sourceType,m.sourceId,COALESCE(f.scope,r.scope) AS "scopeJson",CASE WHEN f.deletedAt IS NULL THEN 0 ELSE 1 END AS deleted FROM "${TABLE_KNOWLEDGE_MENTIONS}" m LEFT JOIN "${TABLE_KNOWLEDGE_RECORDS}" f ON m.sourceType='record' AND f.id=m.sourceId LEFT JOIN "${TABLE_KNOWLEDGE_NODES}" r ON m.sourceType='node' AND r.id=m.sourceId WHERE m.recordId=?`,
|
|
8037
|
+
args: [source.id]
|
|
8038
|
+
});
|
|
8039
|
+
const movedFacts = await tx.execute({
|
|
8040
|
+
sql: `SELECT id,scope AS "scopeJson",deletedAt FROM "${TABLE_KNOWLEDGE_RECORDS}" WHERE node=?`,
|
|
8041
|
+
args: [source.id]
|
|
8042
|
+
});
|
|
8043
|
+
if ((await tx.execute({
|
|
8044
|
+
sql: `UPDATE "${TABLE_KNOWLEDGE_NODES}" SET mergedInto=?,version=version+1,updatedAt=? WHERE id=? AND type='node' AND version=? AND mergedInto IS NULL`,
|
|
8045
|
+
args: [
|
|
8046
|
+
target.id,
|
|
8047
|
+
(/* @__PURE__ */ new Date()).toISOString(),
|
|
8048
|
+
source.id,
|
|
8049
|
+
input.sourceVersion
|
|
8050
|
+
]
|
|
8051
|
+
})).rowsAffected === 0) throw new KnowledgeConflictError(source.id);
|
|
8052
|
+
await tx.execute({
|
|
8053
|
+
sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET node=? WHERE node=?`,
|
|
8054
|
+
args: [target.id, source.id]
|
|
8055
|
+
});
|
|
8056
|
+
await tx.execute({
|
|
8057
|
+
sql: `DELETE FROM "${TABLE_KNOWLEDGE_MENTIONS}" WHERE recordId=? AND EXISTS (SELECT 1 FROM "${TABLE_KNOWLEDGE_MENTIONS}" target WHERE target.sourceType="${TABLE_KNOWLEDGE_MENTIONS}".sourceType AND target.sourceId="${TABLE_KNOWLEDGE_MENTIONS}".sourceId AND target.recordId=?)`,
|
|
8058
|
+
args: [source.id, target.id]
|
|
8059
|
+
});
|
|
8060
|
+
await tx.execute({
|
|
8061
|
+
sql: `UPDATE "${TABLE_KNOWLEDGE_MENTIONS}" SET recordId=? WHERE recordId=?`,
|
|
8062
|
+
args: [target.id, source.id]
|
|
8063
|
+
});
|
|
8064
|
+
for (const row of movedFacts.rows) await this.#outbox(tx, "record", String(row.id), row.deletedAt == null ? "upsert" : "delete", createKnowledgeUlid(), parseJson$1(row.scopeJson));
|
|
8065
|
+
for (const row of affected.rows) await this.#outbox(tx, String(row.sourceType), String(row.sourceId), Number(row.deleted) ? "delete" : "upsert", createKnowledgeUlid(), parseJson$1(row.scopeJson));
|
|
8066
|
+
await this.#activity(tx, "node-merged", "node", source.id, source.scope);
|
|
8067
|
+
await this.#outbox(tx, "node", source.id, "delete", input.sourceVersion + 1, source.scope);
|
|
8068
|
+
await this.#outbox(tx, "node", target.id, "upsert", createKnowledgeUlid(), target.scope);
|
|
8069
|
+
return target;
|
|
8070
|
+
});
|
|
8071
|
+
}
|
|
8072
|
+
async appendKnowledge(input) {
|
|
8073
|
+
const scope = canonicalizeKnowledgeScope(input.scope);
|
|
8074
|
+
const resolutionScope = canonicalizeKnowledgeScope(input.resolutionScope);
|
|
8075
|
+
const defaultScope = canonicalizeKnowledgeScope(input.defaultScope);
|
|
8076
|
+
assertKnowledgeScopeWithinCeiling(scope, input.maxScope);
|
|
8077
|
+
return this.#transaction(async (tx) => {
|
|
8078
|
+
const parent = await this.#resolveTerminalNode(tx, nodeReferenceId(input.node));
|
|
8079
|
+
if (!parent) throw new KnowledgeNotFoundError("node", nodeReferenceId(input.node));
|
|
8080
|
+
const record = {
|
|
8081
|
+
id: input.id ?? createKnowledgeUlid(),
|
|
8082
|
+
node: parent.id,
|
|
8083
|
+
text: input.text,
|
|
8084
|
+
scope,
|
|
8085
|
+
sourceThreadId: input.sourceThreadId,
|
|
8086
|
+
capturedAt: /* @__PURE__ */ new Date(),
|
|
8087
|
+
when: input.when ? new Date(input.when) : void 0,
|
|
8088
|
+
maxScope: input.maxScope,
|
|
8089
|
+
metadata: input.metadata
|
|
8090
|
+
};
|
|
8091
|
+
await tx.execute({
|
|
8092
|
+
sql: `INSERT INTO "${TABLE_KNOWLEDGE_RECORDS}" (id,node,text,scope,scopeKey,sourceThreadId,capturedAt,"when",maxScope,metadata,deletedAt,deletedBy) VALUES (?,?,?,jsonb(?),?,?,?,?,?,jsonb(?),NULL,NULL)`,
|
|
8093
|
+
args: [
|
|
8094
|
+
record.id,
|
|
8095
|
+
record.node,
|
|
8096
|
+
record.text,
|
|
8097
|
+
JSON.stringify(scope),
|
|
8098
|
+
knowledgeScopeKey(scope),
|
|
8099
|
+
record.sourceThreadId,
|
|
8100
|
+
record.capturedAt.toISOString(),
|
|
8101
|
+
record.when?.toISOString() ?? null,
|
|
8102
|
+
record.maxScope ?? null,
|
|
8103
|
+
record.metadata ? JSON.stringify(record.metadata) : null
|
|
8104
|
+
]
|
|
8105
|
+
});
|
|
8106
|
+
await this.#replaceMentions(tx, "record", record.id, record.text, resolutionScope, defaultScope);
|
|
8107
|
+
await this.#activity(tx, "record-created", "record", record.id, scope, record.sourceThreadId);
|
|
8108
|
+
await this.#outbox(tx, "record", record.id, "upsert", record.id, scope);
|
|
8109
|
+
return record;
|
|
8110
|
+
});
|
|
8111
|
+
}
|
|
8112
|
+
async getKnowledge(input) {
|
|
8113
|
+
const result = await this.#executor.execute({
|
|
8114
|
+
sql: `SELECT *,scope AS "scopeJson" FROM "${TABLE_KNOWLEDGE_RECORDS}" WHERE id=?${input.includeDeleted ? "" : " AND deletedAt IS NULL"}`,
|
|
8115
|
+
args: [input.id]
|
|
8116
|
+
});
|
|
8117
|
+
return result.rows[0] ? parseKnowledge(result.rows[0]) : null;
|
|
8118
|
+
}
|
|
8119
|
+
async listKnowledgeAbout(input) {
|
|
8120
|
+
return this.#queryKnowledge(input, "about");
|
|
8121
|
+
}
|
|
8122
|
+
async listKnowledgeMentioning(input) {
|
|
8123
|
+
return this.#queryKnowledge(input, "mentioning");
|
|
8124
|
+
}
|
|
8125
|
+
async listKnowledgeRelatedTo(input) {
|
|
8126
|
+
return this.#queryKnowledge(input, "related");
|
|
8127
|
+
}
|
|
8128
|
+
async knowledgeBySource(input) {
|
|
8129
|
+
const key = knowledgeScopeKey(canonicalizeKnowledgeScope(input.scope));
|
|
8130
|
+
const args = [
|
|
8131
|
+
input.sourceThreadId,
|
|
8132
|
+
key,
|
|
8133
|
+
key
|
|
8134
|
+
];
|
|
8135
|
+
if (input.after) args.push(input.after);
|
|
8136
|
+
const limit = input.limit ?? 100;
|
|
8137
|
+
args.push(limit + 1);
|
|
8138
|
+
const records = (await this.#executor.execute({
|
|
8139
|
+
sql: `SELECT *,scope AS "scopeJson" FROM "${TABLE_KNOWLEDGE_RECORDS}" WHERE sourceThreadId=? AND ${visibleSql}${input.includeDeleted ? "" : " AND deletedAt IS NULL"}${input.after ? " AND id > ?" : ""} ORDER BY id ASC LIMIT ?`,
|
|
8140
|
+
args
|
|
8141
|
+
})).rows.map(parseKnowledge);
|
|
8142
|
+
return {
|
|
8143
|
+
records: records.slice(0, limit),
|
|
8144
|
+
nextCursor: records.length > limit ? records[limit - 1]?.id : void 0
|
|
8145
|
+
};
|
|
8146
|
+
}
|
|
8147
|
+
async removeKnowledge(input) {
|
|
8148
|
+
return this.#transaction(async (tx) => {
|
|
8149
|
+
const record = await this.#getKnowledge(tx, input.id, true);
|
|
8150
|
+
if (!record) throw new KnowledgeNotFoundError("record", input.id);
|
|
8151
|
+
if (record.deletedAt) return record;
|
|
8152
|
+
const deletedAt = /* @__PURE__ */ new Date();
|
|
8153
|
+
await tx.execute({
|
|
8154
|
+
sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET deletedAt=?,deletedBy=? WHERE id=? AND deletedAt IS NULL`,
|
|
8155
|
+
args: [
|
|
8156
|
+
deletedAt.toISOString(),
|
|
8157
|
+
input.deletedBy,
|
|
8158
|
+
input.id
|
|
8159
|
+
]
|
|
8160
|
+
});
|
|
8161
|
+
await this.#activity(tx, "record-deleted", "record", input.id, record.scope, record.sourceThreadId);
|
|
8162
|
+
await this.#outbox(tx, "record", input.id, "delete", deletedAt.toISOString(), record.scope);
|
|
8163
|
+
return {
|
|
8164
|
+
...record,
|
|
8165
|
+
deletedAt,
|
|
8166
|
+
deletedBy: input.deletedBy
|
|
8167
|
+
};
|
|
8168
|
+
});
|
|
8169
|
+
}
|
|
8170
|
+
async restoreKnowledge(input) {
|
|
8171
|
+
return this.#transaction(async (tx) => {
|
|
8172
|
+
const record = await this.#getKnowledge(tx, input.id, true);
|
|
8173
|
+
if (!record) throw new KnowledgeNotFoundError("record", input.id);
|
|
8174
|
+
if (!record.deletedAt) return record;
|
|
8175
|
+
await tx.execute({
|
|
8176
|
+
sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET deletedAt=NULL,deletedBy=NULL WHERE id=?`,
|
|
8177
|
+
args: [input.id]
|
|
8178
|
+
});
|
|
8179
|
+
await this.#activity(tx, "record-restored", "record", input.id, record.scope, record.sourceThreadId);
|
|
8180
|
+
await this.#outbox(tx, "record", input.id, "upsert", createKnowledgeUlid(), record.scope);
|
|
8181
|
+
return {
|
|
8182
|
+
...record,
|
|
8183
|
+
deletedAt: void 0,
|
|
8184
|
+
deletedBy: void 0
|
|
8185
|
+
};
|
|
8186
|
+
});
|
|
8187
|
+
}
|
|
8188
|
+
async rescopeKnowledge(input) {
|
|
8189
|
+
const scope = canonicalizeKnowledgeScope(input.scope);
|
|
8190
|
+
return this.#transaction(async (tx) => {
|
|
8191
|
+
const record = await this.#getKnowledge(tx, input.id, true);
|
|
8192
|
+
if (!record) throw new KnowledgeNotFoundError("record", input.id);
|
|
8193
|
+
assertKnowledgeScopeWithinCeiling(scope, record.maxScope);
|
|
8194
|
+
await tx.execute({
|
|
8195
|
+
sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET scope=jsonb(?),scopeKey=? WHERE id=?`,
|
|
8196
|
+
args: [
|
|
8197
|
+
JSON.stringify(scope),
|
|
8198
|
+
knowledgeScopeKey(scope),
|
|
8199
|
+
input.id
|
|
8200
|
+
]
|
|
8201
|
+
});
|
|
8202
|
+
await this.#activity(tx, "record-rescoped", "record", input.id, scope, record.sourceThreadId);
|
|
8203
|
+
if (knowledgeScopeKey(record.scope) !== knowledgeScopeKey(scope)) await this.#outbox(tx, "record", input.id, "delete", createKnowledgeUlid(), record.scope);
|
|
8204
|
+
if (!record.deletedAt) await this.#outbox(tx, "record", input.id, "upsert", createKnowledgeUlid(), scope);
|
|
8205
|
+
return {
|
|
8206
|
+
...record,
|
|
8207
|
+
scope
|
|
8208
|
+
};
|
|
8209
|
+
});
|
|
8210
|
+
}
|
|
8211
|
+
async raiseKnowledgeCeiling(input) {
|
|
8212
|
+
return this.#transaction(async (tx) => {
|
|
8213
|
+
const record = await this.#getKnowledge(tx, input.id, true);
|
|
8214
|
+
if (!record) throw new KnowledgeNotFoundError("record", input.id);
|
|
8215
|
+
assertKnowledgeScopeWithinCeiling(record.scope, input.maxScope);
|
|
8216
|
+
assertKnowledgeCeilingRaised(record.maxScope, input.maxScope);
|
|
8217
|
+
await tx.execute({
|
|
8218
|
+
sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET maxScope=? WHERE id=?`,
|
|
8219
|
+
args: [input.maxScope ?? null, input.id]
|
|
8220
|
+
});
|
|
8221
|
+
return {
|
|
8222
|
+
...record,
|
|
8223
|
+
maxScope: input.maxScope
|
|
8224
|
+
};
|
|
8225
|
+
});
|
|
8226
|
+
}
|
|
8227
|
+
async search(input) {
|
|
8228
|
+
const scope = canonicalizeKnowledgeScope(input.scope);
|
|
8229
|
+
const key = knowledgeScopeKey(scope);
|
|
8230
|
+
const normalizedQuery = input.query.trim().toLocaleLowerCase();
|
|
8231
|
+
if (!normalizedQuery) return [];
|
|
8232
|
+
const query = `%${escapeLikePattern(normalizedQuery)}%`;
|
|
8233
|
+
const results = (await this.#executor.execute({
|
|
8234
|
+
sql: `SELECT *,scope AS "scopeJson" FROM "${TABLE_KNOWLEDGE_NODES}" WHERE mergedInto IS NULL AND ${visibleSql} AND (canonicalName LIKE ? ESCAPE '=' OR lower(COALESCE(kind,'')) LIKE ? ESCAPE '=' OR lower(COALESCE(content,'')) LIKE ? ESCAPE '=') ORDER BY updatedAt DESC LIMIT ?`,
|
|
8235
|
+
args: [
|
|
8236
|
+
key,
|
|
8237
|
+
key,
|
|
8238
|
+
query,
|
|
8239
|
+
query,
|
|
8240
|
+
query,
|
|
8241
|
+
input.limit ?? 20
|
|
8242
|
+
]
|
|
8243
|
+
})).rows.map((row) => ({
|
|
8244
|
+
type: String(row.type),
|
|
8245
|
+
id: String(row.id),
|
|
8246
|
+
recordId: String(row.id),
|
|
8247
|
+
name: String(row.name),
|
|
8248
|
+
text: row.content ? `${String(row.name)}\n${String(row.content)}` : String(row.name),
|
|
8249
|
+
scope: parseJson$1(row.scopeJson)
|
|
8250
|
+
}));
|
|
8251
|
+
if (results.length < (input.limit ?? 20)) {
|
|
8252
|
+
const records = await this.#executor.execute({
|
|
8253
|
+
sql: `SELECT f.*,f.scope AS "scopeJson",r.name,r.scope AS "parentScopeJson" FROM "${TABLE_KNOWLEDGE_RECORDS}" f JOIN "${TABLE_KNOWLEDGE_NODES}" r ON r.id=f.node AND r.type='node' AND r.mergedInto IS NULL WHERE f.deletedAt IS NULL AND ${visibleSql.replaceAll("scopeKey", "f.scopeKey")} AND lower(f.text) LIKE ? ESCAPE '=' ORDER BY f.id DESC LIMIT ?`,
|
|
8254
|
+
args: [
|
|
8255
|
+
key,
|
|
8256
|
+
key,
|
|
8257
|
+
query,
|
|
8258
|
+
(input.limit ?? 20) - results.length
|
|
8259
|
+
]
|
|
8260
|
+
});
|
|
8261
|
+
results.push(...records.rows.map((row) => {
|
|
8262
|
+
const parentVisible = isKnowledgeScopeVisible(parseJson$1(row.parentScopeJson), scope);
|
|
8263
|
+
return {
|
|
8264
|
+
type: "record",
|
|
8265
|
+
id: String(row.id),
|
|
8266
|
+
recordId: String(row.node),
|
|
8267
|
+
name: parentVisible ? String(row.name) : "(private node)",
|
|
8268
|
+
text: String(row.text),
|
|
8269
|
+
scope: parseJson$1(row.scopeJson)
|
|
8270
|
+
};
|
|
8271
|
+
}));
|
|
8272
|
+
}
|
|
8273
|
+
return results;
|
|
8274
|
+
}
|
|
8275
|
+
async getCurationCursor(input) {
|
|
8276
|
+
const row = (await this.#executor.execute({
|
|
8277
|
+
sql: `SELECT * FROM "${TABLE_KNOWLEDGE_CURSORS}" WHERE sourceThreadId=? AND agent=?`,
|
|
8278
|
+
args: [input.sourceThreadId, input.agent]
|
|
8279
|
+
})).rows[0];
|
|
8280
|
+
return row ? {
|
|
8281
|
+
sourceThreadId: String(row.sourceThreadId),
|
|
8282
|
+
agent: String(row.agent),
|
|
8283
|
+
lastKnowledgeId: String(row.lastKnowledgeId),
|
|
8284
|
+
updatedAt: toDate$1(row.updatedAt)
|
|
8285
|
+
} : null;
|
|
8286
|
+
}
|
|
8287
|
+
async advanceCurationCursor(input) {
|
|
8288
|
+
const updatedAt = /* @__PURE__ */ new Date();
|
|
8289
|
+
if ((await this.#executor.execute({
|
|
8290
|
+
sql: `INSERT INTO "${TABLE_KNOWLEDGE_CURSORS}" (sourceThreadId,agent,lastKnowledgeId,updatedAt) VALUES (?,?,?,?) ON CONFLICT(sourceThreadId,agent) DO UPDATE SET lastKnowledgeId=excluded.lastKnowledgeId,updatedAt=excluded.updatedAt WHERE excluded.lastKnowledgeId >= "${TABLE_KNOWLEDGE_CURSORS}".lastKnowledgeId`,
|
|
8291
|
+
args: [
|
|
8292
|
+
input.sourceThreadId,
|
|
8293
|
+
input.agent,
|
|
8294
|
+
input.lastKnowledgeId,
|
|
8295
|
+
updatedAt.toISOString()
|
|
8296
|
+
]
|
|
8297
|
+
})).rowsAffected === 0) throw new Error("Knowledge curation cursor cannot move backwards");
|
|
8298
|
+
return {
|
|
8299
|
+
...input,
|
|
8300
|
+
updatedAt
|
|
8301
|
+
};
|
|
8302
|
+
}
|
|
8303
|
+
async listActivity(input) {
|
|
8304
|
+
const key = knowledgeScopeKey(canonicalizeKnowledgeScope(input.scope));
|
|
8305
|
+
return (await this.#executor.execute({
|
|
8306
|
+
sql: `SELECT *,scope AS "scopeJson" FROM "${TABLE_KNOWLEDGE_ACTIVITY}" WHERE ${visibleSql}${input.after ? " AND id < ?" : ""} ORDER BY id DESC LIMIT ?`,
|
|
8307
|
+
args: [
|
|
8308
|
+
key,
|
|
8309
|
+
key,
|
|
8310
|
+
...input.after ? [input.after] : [],
|
|
8311
|
+
input.limit ?? 100
|
|
8312
|
+
]
|
|
8313
|
+
})).rows.map((row) => ({
|
|
8314
|
+
id: String(row.id),
|
|
8315
|
+
action: String(row.action),
|
|
8316
|
+
recordType: String(row.recordType),
|
|
8317
|
+
recordId: String(row.recordId),
|
|
8318
|
+
scope: parseJson$1(row.scopeJson),
|
|
8319
|
+
sourceThreadId: row.sourceThreadId == null ? void 0 : String(row.sourceThreadId),
|
|
8320
|
+
createdAt: toDate$1(row.createdAt)
|
|
8321
|
+
}));
|
|
8322
|
+
}
|
|
8323
|
+
async listSemanticOutbox(input = {}) {
|
|
8324
|
+
const clauses = [];
|
|
8325
|
+
const args = [];
|
|
8326
|
+
if (input.status) {
|
|
8327
|
+
clauses.push("status=?");
|
|
8328
|
+
args.push(input.status);
|
|
8329
|
+
}
|
|
8330
|
+
if (input.scope) {
|
|
8331
|
+
const key = knowledgeScopeKey(canonicalizeKnowledgeScope(input.scope));
|
|
8332
|
+
clauses.push(visibleSql);
|
|
8333
|
+
args.push(key, key);
|
|
8334
|
+
}
|
|
8335
|
+
args.push(input.limit ?? 100);
|
|
8336
|
+
return (await this.#executor.execute({
|
|
8337
|
+
sql: `SELECT *,scope AS "scopeJson" FROM "${TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}"${clauses.length ? ` WHERE ${clauses.join(" AND ")}` : ""} ORDER BY createdAt ASC,id ASC LIMIT ?`,
|
|
8338
|
+
args
|
|
8339
|
+
})).rows.map(parseOutbox);
|
|
8340
|
+
}
|
|
8341
|
+
async claimSemanticOutbox(input) {
|
|
8342
|
+
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
8343
|
+
const stale = new Date(now.getTime() - (input.claimTimeoutMs ?? 6e4));
|
|
8344
|
+
return this.#transaction(async (tx) => {
|
|
8345
|
+
const clauses = [
|
|
8346
|
+
`availableAt <= ?`,
|
|
8347
|
+
`(status='pending' OR (status='processing' AND claimedAt <= ?))`,
|
|
8348
|
+
`NOT EXISTS (SELECT 1 FROM "${TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}" AS earlier WHERE earlier.documentId = "${TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}".documentId AND earlier.status != 'completed' AND (earlier.createdAt < "${TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}".createdAt OR (earlier.createdAt = "${TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}".createdAt AND earlier.id < "${TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}".id)))`
|
|
8349
|
+
];
|
|
8350
|
+
const args = [now.toISOString(), stale.toISOString()];
|
|
8351
|
+
if (input.scope) {
|
|
8352
|
+
const key = knowledgeScopeKey(canonicalizeKnowledgeScope(input.scope));
|
|
8353
|
+
clauses.push(visibleSql);
|
|
8354
|
+
args.push(key, key);
|
|
8355
|
+
}
|
|
8356
|
+
args.push(input.limit ?? 100);
|
|
8357
|
+
const ids = (await tx.execute({
|
|
8358
|
+
sql: `SELECT id FROM "${TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}" WHERE ${clauses.join(" AND ")} ORDER BY createdAt ASC,id ASC LIMIT ? FOR UPDATE SKIP LOCKED`,
|
|
8359
|
+
args
|
|
8360
|
+
})).rows.map((row) => String(row.id));
|
|
8361
|
+
for (const id of ids) await tx.execute({
|
|
8362
|
+
sql: `UPDATE "${TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}" SET status='processing',attempts=attempts+1,claimedAt=?,claimedBy=? WHERE id=?`,
|
|
8363
|
+
args: [
|
|
8364
|
+
now.toISOString(),
|
|
8365
|
+
input.workerId,
|
|
8366
|
+
id
|
|
8367
|
+
]
|
|
8368
|
+
});
|
|
8369
|
+
if (!ids.length) return [];
|
|
8370
|
+
return (await tx.execute({
|
|
8371
|
+
sql: `SELECT *,scope AS "scopeJson" FROM "${TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}" WHERE id IN (${ids.map(() => "?").join(",")}) ORDER BY createdAt ASC,id ASC`,
|
|
8372
|
+
args: ids
|
|
8373
|
+
})).rows.map(parseOutbox);
|
|
8374
|
+
});
|
|
8375
|
+
}
|
|
8376
|
+
async completeSemanticOutbox(input) {
|
|
8377
|
+
if (!input.ids.length) return;
|
|
8378
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8379
|
+
await this.#transaction(async (tx) => {
|
|
8380
|
+
for (const id of input.ids) await tx.execute({
|
|
8381
|
+
sql: `UPDATE "${TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}" SET status='completed',completedAt=? WHERE id=? AND status='processing' AND claimedBy=?`,
|
|
8382
|
+
args: [
|
|
8383
|
+
now,
|
|
8384
|
+
id,
|
|
8385
|
+
input.workerId
|
|
8386
|
+
]
|
|
8387
|
+
});
|
|
8388
|
+
});
|
|
8389
|
+
}
|
|
8390
|
+
async releaseSemanticOutbox(input) {
|
|
8391
|
+
if (!input.ids.length) return;
|
|
8392
|
+
await this.#transaction(async (tx) => {
|
|
8393
|
+
for (const id of input.ids) await tx.execute({
|
|
8394
|
+
sql: `UPDATE "${TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}" SET status='pending',availableAt=?,claimedAt=NULL,claimedBy=NULL WHERE id=? AND status='processing' AND claimedBy=?`,
|
|
8395
|
+
args: [
|
|
8396
|
+
(input.retryAt ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
8397
|
+
id,
|
|
8398
|
+
input.workerId
|
|
8399
|
+
]
|
|
8400
|
+
});
|
|
8401
|
+
});
|
|
8402
|
+
}
|
|
8403
|
+
async #transaction(operation) {
|
|
8404
|
+
return this.#client.tx((tx) => operation(createExecutor(tx, this.#schemaName)));
|
|
8405
|
+
}
|
|
8406
|
+
async #getNode(executor, id) {
|
|
8407
|
+
const result = await executor.execute({
|
|
8408
|
+
sql: `SELECT *,scope AS "scopeJson" FROM "${TABLE_KNOWLEDGE_NODES}" WHERE id=? AND type='node'`,
|
|
8409
|
+
args: [id]
|
|
8410
|
+
});
|
|
8411
|
+
return result.rows[0] ? parseNode(result.rows[0]) : null;
|
|
8412
|
+
}
|
|
8413
|
+
async #getNodeByName(executor, name, scope) {
|
|
8414
|
+
const result = await executor.execute({
|
|
8415
|
+
sql: `SELECT *,scope AS "scopeJson" FROM "${TABLE_KNOWLEDGE_NODES}" WHERE type='node' AND scopeKey=? AND canonicalName=?`,
|
|
8416
|
+
args: [knowledgeScopeKey(scope), canonicalName(name)]
|
|
8417
|
+
});
|
|
8418
|
+
return result.rows[0] ? parseNode(result.rows[0]) : null;
|
|
8419
|
+
}
|
|
8420
|
+
async #resolveNode(executor, name, scope) {
|
|
8421
|
+
for (let length = scope.length; length > 0; length--) {
|
|
8422
|
+
const node = await this.#getNodeByName(executor, name, scope.slice(0, length));
|
|
8423
|
+
if (node) {
|
|
8424
|
+
const terminal = await this.#resolveTerminalNode(executor, node.id);
|
|
8425
|
+
if (terminal && isKnowledgeScopeVisible(terminal.scope, scope)) return terminal;
|
|
8426
|
+
}
|
|
8427
|
+
}
|
|
8428
|
+
return null;
|
|
8429
|
+
}
|
|
8430
|
+
async #resolveTerminalNode(executor, id) {
|
|
8431
|
+
let node = await this.#getNode(executor, id);
|
|
8432
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8433
|
+
while (node?.mergedInto) {
|
|
8434
|
+
if (seen.has(node.id)) throw new Error(`Knowledge merge cycle detected at ${node.id}`);
|
|
8435
|
+
seen.add(node.id);
|
|
8436
|
+
node = await this.#getNode(executor, node.mergedInto);
|
|
8437
|
+
}
|
|
8438
|
+
return node;
|
|
8439
|
+
}
|
|
8440
|
+
async #getKnowledge(executor, id, includeDeleted) {
|
|
8441
|
+
const result = await executor.execute({
|
|
8442
|
+
sql: `SELECT *,scope AS "scopeJson" FROM "${TABLE_KNOWLEDGE_RECORDS}" WHERE id=?${includeDeleted ? "" : " AND deletedAt IS NULL"}`,
|
|
8443
|
+
args: [id]
|
|
8444
|
+
});
|
|
8445
|
+
return result.rows[0] ? parseKnowledge(result.rows[0]) : null;
|
|
8446
|
+
}
|
|
8447
|
+
async #queryKnowledge(input, relationship) {
|
|
8448
|
+
const scope = canonicalizeKnowledgeScope(input.scope);
|
|
8449
|
+
const node = await this.#resolveTerminalNode(this.#executor, nodeReferenceId(input.node));
|
|
8450
|
+
if (!node) return { records: [] };
|
|
8451
|
+
const key = knowledgeScopeKey(scope);
|
|
8452
|
+
const args = [
|
|
8453
|
+
node.id,
|
|
8454
|
+
...relationship === "related" ? [node.id] : [],
|
|
8455
|
+
key,
|
|
8456
|
+
key
|
|
8457
|
+
];
|
|
8458
|
+
if (input.after) args.push(input.after);
|
|
8459
|
+
args.push((input.limit ?? 100) + 1);
|
|
8460
|
+
const records = (await this.#executor.execute({
|
|
8461
|
+
sql: `SELECT DISTINCT f.*,f.scope AS "scopeJson" FROM "${TABLE_KNOWLEDGE_RECORDS}" f${relationship === "about" ? "" : ` LEFT JOIN "${TABLE_KNOWLEDGE_MENTIONS}" m ON m.sourceType='record' AND m.sourceId=f.id`} WHERE ${relationship === "about" ? "f.node=?" : relationship === "mentioning" ? "m.recordId=?" : "(f.node=? OR m.recordId=?)"} AND ${visibleSql.replaceAll("scopeKey", "f.scopeKey")}${input.includeDeleted ? "" : " AND f.deletedAt IS NULL"}${input.after ? " AND f.id < ?" : ""} ORDER BY f.id DESC LIMIT ?`,
|
|
8462
|
+
args
|
|
8463
|
+
})).rows.map(parseKnowledge);
|
|
8464
|
+
const limit = input.limit ?? 100;
|
|
8465
|
+
return {
|
|
8466
|
+
records: records.slice(0, limit),
|
|
8467
|
+
nextCursor: records.length > limit ? records[limit - 1]?.id : void 0
|
|
8468
|
+
};
|
|
8469
|
+
}
|
|
8470
|
+
async #replaceMentions(tx, sourceType, sourceId, text, resolutionScope, defaultScope) {
|
|
8471
|
+
await tx.execute({
|
|
8472
|
+
sql: `DELETE FROM "${TABLE_KNOWLEDGE_MENTIONS}" WHERE sourceType=? AND sourceId=?`,
|
|
8473
|
+
args: [sourceType, sourceId]
|
|
8474
|
+
});
|
|
8475
|
+
for (const name of parseKnowledgeWikilinks(text)) {
|
|
8476
|
+
let node = await this.#resolveNode(tx, name, resolutionScope);
|
|
8477
|
+
if (!node) {
|
|
8478
|
+
node = await this.#getNodeByName(tx, name, defaultScope);
|
|
8479
|
+
if (node) node = await this.#resolveTerminalNode(tx, node.id);
|
|
8480
|
+
if (!node) {
|
|
8481
|
+
const now = /* @__PURE__ */ new Date();
|
|
8482
|
+
node = {
|
|
8483
|
+
id: crypto.randomUUID(),
|
|
8484
|
+
type: "node",
|
|
8485
|
+
name,
|
|
8486
|
+
kind: "node",
|
|
8487
|
+
scope: defaultScope,
|
|
8488
|
+
version: 1,
|
|
8489
|
+
createdAt: now,
|
|
8490
|
+
updatedAt: now
|
|
8491
|
+
};
|
|
8492
|
+
await tx.execute({
|
|
8493
|
+
sql: `INSERT INTO "${TABLE_KNOWLEDGE_NODES}" (id,type,name,canonicalName,kind,content,scope,scopeKey,version,mergedInto,createdAt,updatedAt) VALUES (?,?,?,?,?,NULL,jsonb(?),?,?,NULL,?,?)`,
|
|
8494
|
+
args: [
|
|
8495
|
+
node.id,
|
|
8496
|
+
"node",
|
|
8497
|
+
node.name,
|
|
8498
|
+
canonicalName(node.name),
|
|
8499
|
+
node.kind,
|
|
8500
|
+
JSON.stringify(defaultScope),
|
|
8501
|
+
knowledgeScopeKey(defaultScope),
|
|
8502
|
+
1,
|
|
8503
|
+
now.toISOString(),
|
|
8504
|
+
now.toISOString()
|
|
8505
|
+
]
|
|
8506
|
+
});
|
|
8507
|
+
await this.#activity(tx, "node-created", "node", node.id, defaultScope);
|
|
8508
|
+
await this.#outbox(tx, "node", node.id, "upsert", 1, defaultScope);
|
|
8509
|
+
}
|
|
8510
|
+
}
|
|
8511
|
+
await tx.execute({
|
|
8512
|
+
sql: `INSERT INTO "${TABLE_KNOWLEDGE_MENTIONS}" (sourceType,sourceId,recordId) VALUES (?,?,?) ON CONFLICT DO NOTHING`,
|
|
8513
|
+
args: [
|
|
8514
|
+
sourceType,
|
|
8515
|
+
sourceId,
|
|
8516
|
+
node.id
|
|
8517
|
+
]
|
|
8518
|
+
});
|
|
8519
|
+
}
|
|
8520
|
+
}
|
|
8521
|
+
async #activity(executor, action, recordType, recordId, scope, sourceThreadId) {
|
|
8522
|
+
const now = /* @__PURE__ */ new Date();
|
|
8523
|
+
await executor.execute({
|
|
8524
|
+
sql: `INSERT INTO "${TABLE_KNOWLEDGE_ACTIVITY}" (id,action,recordType,recordId,scope,scopeKey,sourceThreadId,createdAt) VALUES (?,?,?,?,jsonb(?),?,?,?)`,
|
|
8525
|
+
args: [
|
|
8526
|
+
createKnowledgeUlid(),
|
|
8527
|
+
action,
|
|
8528
|
+
recordType,
|
|
8529
|
+
recordId,
|
|
8530
|
+
JSON.stringify(scope),
|
|
8531
|
+
knowledgeScopeKey(scope),
|
|
8532
|
+
sourceThreadId ?? null,
|
|
8533
|
+
now.toISOString()
|
|
8534
|
+
]
|
|
8535
|
+
});
|
|
8536
|
+
}
|
|
8537
|
+
async #outbox(executor, documentType, id, operation, version, scope) {
|
|
8538
|
+
const documentId = knowledgeSemanticDocumentId(documentType, id);
|
|
8539
|
+
const idempotencyKey = knowledgeSemanticIdempotencyKey(documentId, operation, version);
|
|
8540
|
+
const now = /* @__PURE__ */ new Date();
|
|
8541
|
+
await executor.execute({
|
|
8542
|
+
sql: `INSERT INTO "${TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}" (id,idempotencyKey,documentId,documentType,operation,scope,scopeKey,status,attempts,availableAt,claimedAt,claimedBy,createdAt,completedAt) VALUES (?,?,?,?,?,jsonb(?),?,'pending',0,?,NULL,NULL,?,NULL) ON CONFLICT DO NOTHING`,
|
|
8543
|
+
args: [
|
|
8544
|
+
createKnowledgeUlid(),
|
|
8545
|
+
idempotencyKey,
|
|
8546
|
+
documentId,
|
|
8547
|
+
documentType,
|
|
8548
|
+
operation,
|
|
8549
|
+
JSON.stringify(scope),
|
|
8550
|
+
knowledgeScopeKey(scope),
|
|
8551
|
+
now.toISOString(),
|
|
8552
|
+
now.toISOString()
|
|
8553
|
+
]
|
|
8554
|
+
});
|
|
8555
|
+
}
|
|
8556
|
+
};
|
|
8557
|
+
//#endregion
|
|
7587
8558
|
//#region src/storage/domains/mcp-clients/index.ts
|
|
7588
8559
|
const SNAPSHOT_FIELDS$5 = [
|
|
7589
8560
|
"name",
|
|
@@ -20750,6 +21721,38 @@ var PgFactoryStorageOps = class {
|
|
|
20750
21721
|
* Supports serializable app-table transactions for cross-replica invariant
|
|
20751
21722
|
* enforcement and `authDatabase()` exposing the shared pool.
|
|
20752
21723
|
*/
|
|
21724
|
+
/**
|
|
21725
|
+
* A pool-level `error` listener only covers *idle* clients: pg hands ownership
|
|
21726
|
+
* of a client to the borrower for the duration of a checkout and takes its own
|
|
21727
|
+
* listener off. So a backend restart or network blip that lands on a client
|
|
21728
|
+
* mid-transaction reaches an emitter with no listener, and Node escalates that
|
|
21729
|
+
* to an uncaughtException that kills the process — the pool handler logs the
|
|
21730
|
+
* idle siblings and the borrowed one still brings everything down.
|
|
21731
|
+
*
|
|
21732
|
+
* Attach a listener once per physical connection instead. `connect` fires when
|
|
21733
|
+
* the pool establishes a client, so the client stays covered for its whole life
|
|
21734
|
+
* and there is nothing to remove on release.
|
|
21735
|
+
*
|
|
21736
|
+
* That listener outlives the checkout, so it also sees the failures the pool is
|
|
21737
|
+
* already reporting through its own idle listener. Follow `acquire`/`release`
|
|
21738
|
+
* to tell the two apart and stay quiet while the client is idle — otherwise one
|
|
21739
|
+
* dropped connection is announced twice, the second time as the wrong thing.
|
|
21740
|
+
*
|
|
21741
|
+
* The pool still discards the failed connection; this only keeps the failure
|
|
21742
|
+
* reportable instead of fatal.
|
|
21743
|
+
*/
|
|
21744
|
+
function guardCheckedOutClientErrors(pool, warn = console.warn) {
|
|
21745
|
+
const borrowed = /* @__PURE__ */ new WeakSet();
|
|
21746
|
+
pool.on("connect", (client) => {
|
|
21747
|
+
borrowed.add(client);
|
|
21748
|
+
client.on("error", (err) => {
|
|
21749
|
+
if (!borrowed.has(client)) return;
|
|
21750
|
+
warn(`PgFactoryStorage: client error while checked out (the pool discards this connection): ${err instanceof Error ? err.message : String(err)}`);
|
|
21751
|
+
});
|
|
21752
|
+
});
|
|
21753
|
+
pool.on("acquire", (client) => borrowed.add(client));
|
|
21754
|
+
pool.on("release", (_err, client) => borrowed.delete(client));
|
|
21755
|
+
}
|
|
20753
21756
|
var PgFactoryStorage = class extends FactoryStorage {
|
|
20754
21757
|
ops;
|
|
20755
21758
|
#pool;
|
|
@@ -20770,6 +21773,7 @@ var PgFactoryStorage = class extends FactoryStorage {
|
|
|
20770
21773
|
this.#pool.on("error", (err) => {
|
|
20771
21774
|
console.warn(`PgFactoryStorage: idle pool client error (pool discards the client and reconnects on next checkout): ${err instanceof Error ? err.message : String(err)}`);
|
|
20772
21775
|
});
|
|
21776
|
+
guardCheckedOutClientErrors(this.#pool);
|
|
20773
21777
|
}
|
|
20774
21778
|
this.ops = new PgFactoryStorageOps(this.#pool, this.#schemas);
|
|
20775
21779
|
}
|
|
@@ -20899,6 +21903,7 @@ function createHostPool(config) {
|
|
|
20899
21903
|
*/
|
|
20900
21904
|
const ALL_DOMAINS = [
|
|
20901
21905
|
MemoryPG,
|
|
21906
|
+
KnowledgePG,
|
|
20902
21907
|
NotificationsPG,
|
|
20903
21908
|
ObservabilityPG,
|
|
20904
21909
|
ScoresPG,
|
|
@@ -20997,6 +22002,7 @@ var PostgresStore = class extends MastraCompositeStore {
|
|
|
20997
22002
|
workflows: new WorkflowsPG(domainConfig),
|
|
20998
22003
|
workflowDefinitions: new WorkflowDefinitionsPG(domainConfig),
|
|
20999
22004
|
memory: new MemoryPG(domainConfig),
|
|
22005
|
+
knowledge: new KnowledgePG(domainConfig),
|
|
21000
22006
|
notifications: new NotificationsPG(domainConfig),
|
|
21001
22007
|
observability: new ObservabilityPG(domainConfig),
|
|
21002
22008
|
agents: new AgentsPG(domainConfig),
|
|
@@ -21321,6 +22327,6 @@ Example Complex Query:
|
|
|
21321
22327
|
]
|
|
21322
22328
|
}`;
|
|
21323
22329
|
//#endregion
|
|
21324
|
-
export { AgentsPG, BackgroundTasksPG, BlobsPG, ChannelsPG, DatasetsPG, ExperimentsPG, FavoritesPG, MCPClientsPG, MCPServersPG, MemoryPG, NotificationsPG, ObservabilityPG, ObservabilityStoragePostgresVNext, PGVECTOR_PROMPT, PgFactoryStorage, PgVector, PoolAdapter, PostgresStore, PostgresStoreVNext, PromptBlocksPG, SchedulesPG, ScorerDefinitionsPG, ScoresPG, SkillsPG, ThreadStatePG, ToolProviderConnectionsPG, WorkflowDefinitionsPG, WorkflowsPG, WorkspacesPG, exportSchemas };
|
|
22330
|
+
export { AgentsPG, BackgroundTasksPG, BlobsPG, ChannelsPG, DatasetsPG, ExperimentsPG, FavoritesPG, KnowledgePG, MCPClientsPG, MCPServersPG, MemoryPG, NotificationsPG, ObservabilityPG, ObservabilityStoragePostgresVNext, PGVECTOR_PROMPT, PgFactoryStorage, PgVector, PoolAdapter, PostgresStore, PostgresStoreVNext, PromptBlocksPG, SchedulesPG, ScorerDefinitionsPG, ScoresPG, SkillsPG, ThreadStatePG, ToolProviderConnectionsPG, WorkflowDefinitionsPG, WorkflowsPG, WorkspacesPG, exportSchemas };
|
|
21325
22331
|
|
|
21326
22332
|
//# sourceMappingURL=index.js.map
|