@mastra/pg 1.31.0-alpha.2 → 1.31.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/dist/_types/@internal_core/dist/knowledge-compat/index.d.ts +594 -0
- package/dist/_types/@internal_core/package.json +1 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/reference-storage-retention.md +21 -18
- package/dist/index.cjs +1749 -310
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1752 -313
- package/dist/index.js.map +1 -1
- package/dist/shared/config.d.ts +6 -0
- package/dist/shared/config.d.ts.map +1 -1
- package/dist/storage/db/index.d.ts +15 -9
- package/dist/storage/db/index.d.ts.map +1 -1
- package/dist/storage/db/run-fencing.d.ts +23 -0
- package/dist/storage/db/run-fencing.d.ts.map +1 -0
- package/dist/storage/domains/agents/index.d.ts.map +1 -1
- package/dist/storage/domains/knowledge/index.d.ts +38 -9
- package/dist/storage/domains/knowledge/index.d.ts.map +1 -1
- package/dist/storage/domains/mcp-clients/index.d.ts.map +1 -1
- package/dist/storage/domains/mcp-servers/index.d.ts.map +1 -1
- package/dist/storage/domains/memory/index.d.ts +21 -7
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/prompt-blocks/index.d.ts.map +1 -1
- package/dist/storage/domains/scorer-definitions/index.d.ts.map +1 -1
- package/dist/storage/domains/skills/index.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts +21 -6
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/dist/storage/domains/workspaces/index.d.ts.map +1 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/retention.d.ts +3 -3
- package/dist/storage/retention.d.ts.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
|
|
2
2
|
import * as coreStorage from "@mastra/core/storage";
|
|
3
|
-
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,
|
|
3
|
+
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_SEMANTIC_OUTBOX_SCHEMA, KnowledgeConflictError, KnowledgeNotFoundError, KnowledgeStorage, MCPClientsStorage, MCPServersStorage, METRIC_DISTINCT_COLUMNS, MastraCompositeStore, MemoryStorage, NotificationsStorage, OBSERVATIONAL_MEMORY_TABLE_SCHEMA, ObservabilityStorage, PromptBlocksStorage, RUN_FENCING_TABLE_SCHEMAS, RunFenceConflictError, 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_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_MEMORY_RUN_FENCES, 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_RUN_OWNERS, TABLE_WORKFLOW_SNAPSHOT, TABLE_WORKSPACES, TABLE_WORKSPACE_VERSIONS, ThreadStateStorage, ToolProviderConnectionsStorage, TraceStatus, UniqueViolationError, WorkflowDefinitionsStorage, WorkflowsStorage, WorkspacesStorage, calculatePagination, canonicalizeKnowledgeScope, createKnowledgeUlid, createStorageErrorId, createVectorErrorId, ensureDate, executeRetentionPrune, getDefaultValue, getSqlType, hasErrorCode, isKnowledgeScopeVisible, isRunFenceConflictError, knowledgeScopeKey, knowledgeSemanticDocumentId, knowledgeSemanticIdempotencyKey, listBranchesArgsSchema, listFeedbackArgsSchema, listLogsArgsSchema, listMetricsArgsSchema, listScoresArgsSchema, listTracesArgsSchema, matchesExpectedWorkflowStatus, matchesRunFence, mergeWorkflowStepResult, normalizePerPage, normalizeScheduleTarget, parseDuration, parseKnowledgeNodeCursor, parseKnowledgeWikilinks, resolveListOrderBy, resolveRetentionTargets, resolveRunFence, retentionCutoffMs, runRetentionBatches, safelyParseJSON, storageMessageMatchesMetadataFilter, toTraceSpans, transformScoreRow, validateStorageMetadataFilter } from "@mastra/core/storage";
|
|
4
4
|
import { parseFieldKey, parseSqlIdentifier } from "@mastra/core/utils";
|
|
5
5
|
import { MastraVector, validateTopK, validateUpsertInput } from "@mastra/core/vector";
|
|
6
6
|
import { Mutex } from "async-mutex";
|
|
7
7
|
import * as pg$1 from "pg";
|
|
8
|
-
import pg, { Pool } from "pg";
|
|
8
|
+
import pg, { Pool, defaults } from "pg";
|
|
9
9
|
import xxhash from "xxhash-wasm";
|
|
10
10
|
import { parse } from "pg-connection-string";
|
|
11
11
|
import { BaseFilterTranslator } from "@mastra/core/vector/filter";
|
|
12
12
|
import { MastraBase } from "@mastra/core/base";
|
|
13
|
-
import { createHash } from "crypto";
|
|
14
|
-
import { MessageList } from "@mastra/core/agent";
|
|
13
|
+
import { createHash, randomUUID } from "crypto";
|
|
15
14
|
import { coreFeatures } from "@mastra/core/features";
|
|
15
|
+
import { MessageList } from "@mastra/core/agent";
|
|
16
16
|
import { saveScorePayloadSchema } from "@mastra/core/evals";
|
|
17
17
|
import { skillSnapshotFieldValuesEqual } from "@mastra/core/storage/domains/skills";
|
|
18
18
|
import { isDeepStrictEqual } from "util";
|
|
@@ -3189,9 +3189,10 @@ var PgDB = class extends MastraBase {
|
|
|
3189
3189
|
}
|
|
3190
3190
|
await flush();
|
|
3191
3191
|
}
|
|
3192
|
-
|
|
3192
|
+
/** `client` lets a caller run the insert inside its own transaction. */
|
|
3193
|
+
async insert({ tableName, record }, client = this.client) {
|
|
3193
3194
|
try {
|
|
3194
|
-
await this.executeInsert(
|
|
3195
|
+
await this.executeInsert(client, {
|
|
3195
3196
|
tableName,
|
|
3196
3197
|
record
|
|
3197
3198
|
});
|
|
@@ -4025,13 +4026,15 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
|
|
|
4025
4026
|
indexName: tableName,
|
|
4026
4027
|
schemaName: getSchemaName$6(this.schemaName)
|
|
4027
4028
|
});
|
|
4029
|
+
const parsedColumn = `"${parseSqlIdentifier(column, "column name")}"`;
|
|
4028
4030
|
const sql = `
|
|
4029
4031
|
DELETE FROM ${fullTableName}
|
|
4030
4032
|
WHERE ctid IN (
|
|
4031
4033
|
SELECT ctid FROM ${fullTableName}
|
|
4032
|
-
WHERE ${
|
|
4034
|
+
WHERE ${parsedColumn} < $1
|
|
4033
4035
|
LIMIT $2
|
|
4034
4036
|
)
|
|
4037
|
+
AND ${parsedColumn} < $1
|
|
4035
4038
|
`;
|
|
4036
4039
|
return (await this.client.query(sql, [cutoff, limit])).rowCount ?? 0;
|
|
4037
4040
|
}
|
|
@@ -4494,6 +4497,7 @@ var AgentsPG = class AgentsPG extends AgentsStorage {
|
|
|
4494
4497
|
}
|
|
4495
4498
|
async create(input) {
|
|
4496
4499
|
const { agent } = input;
|
|
4500
|
+
let draftInserted = false;
|
|
4497
4501
|
try {
|
|
4498
4502
|
const agentsTable = getTableName$5({
|
|
4499
4503
|
indexName: TABLE_AGENTS,
|
|
@@ -4520,6 +4524,7 @@ var AgentsPG = class AgentsPG extends AgentsStorage {
|
|
|
4520
4524
|
nowIso,
|
|
4521
4525
|
nowIso
|
|
4522
4526
|
]);
|
|
4527
|
+
draftInserted = true;
|
|
4523
4528
|
const { id: _id, authorId: _authorId, visibility: _visibility, metadata: _metadata, ...snapshotConfig } = agent;
|
|
4524
4529
|
const versionId = crypto.randomUUID();
|
|
4525
4530
|
await this.createVersion({
|
|
@@ -4542,14 +4547,14 @@ var AgentsPG = class AgentsPG extends AgentsStorage {
|
|
|
4542
4547
|
updatedAt: now
|
|
4543
4548
|
};
|
|
4544
4549
|
} catch (error) {
|
|
4545
|
-
if (
|
|
4546
|
-
try {
|
|
4550
|
+
if (draftInserted) try {
|
|
4547
4551
|
const agentsTable = getTableName$5({
|
|
4548
4552
|
indexName: TABLE_AGENTS,
|
|
4549
4553
|
schemaName: getSchemaName$5(this.#schema)
|
|
4550
4554
|
});
|
|
4551
4555
|
await this.#db.client.none(`DELETE FROM ${agentsTable} WHERE id = $1 AND status = 'draft' AND "activeVersionId" IS NULL`, [agent.id]);
|
|
4552
4556
|
} catch {}
|
|
4557
|
+
if (error instanceof MastraError) throw error;
|
|
4553
4558
|
throw new MastraError({
|
|
4554
4559
|
id: createStorageErrorId("PG", "CREATE_AGENT", "FAILED"),
|
|
4555
4560
|
domain: ErrorDomain.STORAGE,
|
|
@@ -8277,18 +8282,650 @@ var FavoritesPG = class FavoritesPG extends FavoritesStorage {
|
|
|
8277
8282
|
}
|
|
8278
8283
|
};
|
|
8279
8284
|
//#endregion
|
|
8285
|
+
//#region ../../packages/_internal-core/dist/knowledge-compat/index.js
|
|
8286
|
+
const KNOWLEDGE_V2_CORE_FEATURE = "knowledge-v2";
|
|
8287
|
+
/** Hard cap on scope nodes returned by one `listScopeNodes` read. */
|
|
8288
|
+
const MAX_KNOWLEDGE_SCOPE_NODES = 1e3;
|
|
8289
|
+
function knowledgeScopeNodeFilterKey(input) {
|
|
8290
|
+
return JSON.stringify([
|
|
8291
|
+
input.withinAddress ?? null,
|
|
8292
|
+
input.addresses ? [...input.addresses].sort() : null,
|
|
8293
|
+
input.ids ? [...input.ids].sort() : null
|
|
8294
|
+
]);
|
|
8295
|
+
}
|
|
8296
|
+
function createKnowledgeScopeNodeCursor(scope, input) {
|
|
8297
|
+
return encodeURIComponent(JSON.stringify({
|
|
8298
|
+
version: 1,
|
|
8299
|
+
type: "scope",
|
|
8300
|
+
name: scope.name,
|
|
8301
|
+
id: scope.id,
|
|
8302
|
+
filter: knowledgeScopeNodeFilterKey(input)
|
|
8303
|
+
}));
|
|
8304
|
+
}
|
|
8305
|
+
/** Validates the page size and cursor of a `listScopeNodes` query; throws on a cursor from a different query. */
|
|
8306
|
+
function parseListKnowledgeScopeNodesInput(input = {}) {
|
|
8307
|
+
const requested = Number.isFinite(input.limit) ? Math.trunc(input.limit) : MAX_KNOWLEDGE_SCOPE_NODES;
|
|
8308
|
+
const limit = Math.min(Math.max(requested, 1), MAX_KNOWLEDGE_SCOPE_NODES);
|
|
8309
|
+
if (!input.cursor) return {
|
|
8310
|
+
limit,
|
|
8311
|
+
after: null
|
|
8312
|
+
};
|
|
8313
|
+
let value;
|
|
8314
|
+
try {
|
|
8315
|
+
value = JSON.parse(decodeURIComponent(input.cursor));
|
|
8316
|
+
} catch {
|
|
8317
|
+
throw new Error("Invalid Knowledge scope node cursor.");
|
|
8318
|
+
}
|
|
8319
|
+
const parsed = value && typeof value === "object" ? value : {};
|
|
8320
|
+
if (parsed.version !== 1 || parsed.type !== "scope" || typeof parsed.name !== "string" || typeof parsed.id !== "string" || parsed.filter !== knowledgeScopeNodeFilterKey(input)) throw new Error("Knowledge scope node cursor does not match this query.");
|
|
8321
|
+
return {
|
|
8322
|
+
limit,
|
|
8323
|
+
after: {
|
|
8324
|
+
name: parsed.name,
|
|
8325
|
+
id: parsed.id
|
|
8326
|
+
}
|
|
8327
|
+
};
|
|
8328
|
+
}
|
|
8329
|
+
/** Builds a page from up to `limit + 1` name/id-ordered scope nodes. */
|
|
8330
|
+
function pageKnowledgeScopeNodes(rows, limit, input) {
|
|
8331
|
+
const scopes = rows.slice(0, limit);
|
|
8332
|
+
const last = scopes.at(-1);
|
|
8333
|
+
return {
|
|
8334
|
+
scopes,
|
|
8335
|
+
nextCursor: rows.length > limit && last ? createKnowledgeScopeNodeCursor(last, input) : null
|
|
8336
|
+
};
|
|
8337
|
+
}
|
|
8338
|
+
/** Clamps a requested member limit to 1–{@link MAX_KNOWLEDGE_SCOPE_MEMBERS_LIMIT}. */
|
|
8339
|
+
function knowledgeScopeMembersLimit(limit) {
|
|
8340
|
+
if (limit === void 0 || !Number.isFinite(limit)) return 500;
|
|
8341
|
+
return Math.min(Math.max(Math.trunc(limit), 1), 500);
|
|
8342
|
+
}
|
|
8343
|
+
/** Validates the page size and cursor of a `listScopeMembers` query; throws on a cursor from another scope. */
|
|
8344
|
+
function parseListKnowledgeScopeMembersInput(input) {
|
|
8345
|
+
const limit = knowledgeScopeMembersLimit(input.limit);
|
|
8346
|
+
if (!input.cursor) return {
|
|
8347
|
+
limit,
|
|
8348
|
+
after: null
|
|
8349
|
+
};
|
|
8350
|
+
let value;
|
|
8351
|
+
try {
|
|
8352
|
+
value = JSON.parse(decodeURIComponent(input.cursor));
|
|
8353
|
+
} catch {
|
|
8354
|
+
throw new Error("Invalid Knowledge scope member cursor.");
|
|
8355
|
+
}
|
|
8356
|
+
const parsed = value && typeof value === "object" ? value : {};
|
|
8357
|
+
const updatedAt = typeof parsed.updatedAt === "string" ? new Date(parsed.updatedAt) : /* @__PURE__ */ new Date(NaN);
|
|
8358
|
+
if (parsed.version !== 1 || parsed.type !== "scope-member" || parsed.scopeNodeId !== input.scopeNodeId || typeof parsed.name !== "string" || typeof parsed.id !== "string" || Number.isNaN(updatedAt.getTime())) throw new Error("Knowledge scope member cursor does not match this query.");
|
|
8359
|
+
return {
|
|
8360
|
+
limit,
|
|
8361
|
+
after: {
|
|
8362
|
+
updatedAt,
|
|
8363
|
+
name: parsed.name,
|
|
8364
|
+
id: parsed.id
|
|
8365
|
+
}
|
|
8366
|
+
};
|
|
8367
|
+
}
|
|
8368
|
+
/** Builds a page from up to `limit + 1` members in `listScopeMembers` order. */
|
|
8369
|
+
function pageKnowledgeScopeMembers(rows, limit, input) {
|
|
8370
|
+
const members = rows.slice(0, limit);
|
|
8371
|
+
const last = members.at(-1);
|
|
8372
|
+
const hasMore = rows.length > limit;
|
|
8373
|
+
return {
|
|
8374
|
+
members,
|
|
8375
|
+
hasMore,
|
|
8376
|
+
nextCursor: hasMore && last ? encodeURIComponent(JSON.stringify({
|
|
8377
|
+
version: 1,
|
|
8378
|
+
type: "scope-member",
|
|
8379
|
+
scopeNodeId: input.scopeNodeId,
|
|
8380
|
+
updatedAt: last.updatedAt.toISOString(),
|
|
8381
|
+
name: last.name,
|
|
8382
|
+
id: last.id
|
|
8383
|
+
})) : null
|
|
8384
|
+
};
|
|
8385
|
+
}
|
|
8386
|
+
const TABLE_KNOWLEDGE_NODES$1 = "mastra_knowledge_nodes";
|
|
8387
|
+
const TABLE_KNOWLEDGE_RECORDS$1 = "mastra_knowledge_records";
|
|
8388
|
+
const TABLE_KNOWLEDGE_MENTIONS$1 = "mastra_knowledge_mentions";
|
|
8389
|
+
const TABLE_KNOWLEDGE_ACTIVITY$1 = "mastra_knowledge_activity";
|
|
8390
|
+
const TABLE_KNOWLEDGE_SEMANTIC_OUTBOX$1 = "mastra_knowledge_semantic_outbox";
|
|
8391
|
+
const TABLE_KNOWLEDGE_NODE_SCOPES = "mastra_knowledge_node_scopes";
|
|
8392
|
+
const TABLE_KNOWLEDGE_RECORD_SCOPES = "mastra_knowledge_record_scopes";
|
|
8393
|
+
const TABLE_KNOWLEDGE_SCOPE_GRANTS = "mastra_knowledge_scope_grants";
|
|
8394
|
+
const TABLE_KNOWLEDGE_ACCESS_STATE = "mastra_knowledge_access_state";
|
|
8395
|
+
const TABLE_KNOWLEDGE_SCOPE_ADDRESSES = "mastra_knowledge_scope_addresses";
|
|
8396
|
+
const TABLE_KNOWLEDGE_NODE_ADDRESSES = "mastra_knowledge_node_addresses";
|
|
8397
|
+
const TABLE_KNOWLEDGE_IMPORT_STATE = "mastra_knowledge_import_state";
|
|
8398
|
+
const TABLE_KNOWLEDGE_IMPORT_RUNS = "mastra_knowledge_import_runs";
|
|
8399
|
+
const TABLE_KNOWLEDGE_PROPOSALS = "mastra_knowledge_proposals";
|
|
8400
|
+
const KNOWLEDGE_TABLE_NAMES = [
|
|
8401
|
+
TABLE_KNOWLEDGE_NODES$1,
|
|
8402
|
+
TABLE_KNOWLEDGE_RECORDS$1,
|
|
8403
|
+
TABLE_KNOWLEDGE_MENTIONS$1,
|
|
8404
|
+
TABLE_KNOWLEDGE_ACTIVITY$1,
|
|
8405
|
+
TABLE_KNOWLEDGE_SEMANTIC_OUTBOX$1,
|
|
8406
|
+
TABLE_KNOWLEDGE_NODE_SCOPES,
|
|
8407
|
+
TABLE_KNOWLEDGE_RECORD_SCOPES,
|
|
8408
|
+
TABLE_KNOWLEDGE_SCOPE_GRANTS,
|
|
8409
|
+
TABLE_KNOWLEDGE_ACCESS_STATE,
|
|
8410
|
+
TABLE_KNOWLEDGE_SCOPE_ADDRESSES,
|
|
8411
|
+
TABLE_KNOWLEDGE_NODE_ADDRESSES,
|
|
8412
|
+
TABLE_KNOWLEDGE_IMPORT_STATE,
|
|
8413
|
+
TABLE_KNOWLEDGE_IMPORT_RUNS,
|
|
8414
|
+
TABLE_KNOWLEDGE_PROPOSALS
|
|
8415
|
+
];
|
|
8416
|
+
/** Knowledge v1 tables with no v2 equivalent; an explicit schema reset drops them. */
|
|
8417
|
+
const RETIRED_KNOWLEDGE_TABLE_NAMES = ["mastra_knowledge_cursors"];
|
|
8418
|
+
/** Tables and indexes published v1 adapters created for every app, whether or not it used Knowledge. */
|
|
8419
|
+
const PUBLISHED_KNOWLEDGE_V1_TABLE_NAMES = /* @__PURE__ */ new Set([
|
|
8420
|
+
TABLE_KNOWLEDGE_NODES$1,
|
|
8421
|
+
TABLE_KNOWLEDGE_RECORDS$1,
|
|
8422
|
+
TABLE_KNOWLEDGE_MENTIONS$1,
|
|
8423
|
+
TABLE_KNOWLEDGE_ACTIVITY$1,
|
|
8424
|
+
TABLE_KNOWLEDGE_SEMANTIC_OUTBOX$1,
|
|
8425
|
+
...RETIRED_KNOWLEDGE_TABLE_NAMES
|
|
8426
|
+
]);
|
|
8427
|
+
const PUBLISHED_KNOWLEDGE_V1_INDEX_NAMES = /* @__PURE__ */ new Set([
|
|
8428
|
+
"idx_knowledge_nodes_identity",
|
|
8429
|
+
"idx_knowledge_nodes_scope",
|
|
8430
|
+
"idx_knowledge_records_node_latest",
|
|
8431
|
+
"idx_knowledge_records_thread_latest",
|
|
8432
|
+
"idx_knowledge_mentions_record",
|
|
8433
|
+
"idx_knowledge_activity_latest",
|
|
8434
|
+
"idx_knowledge_outbox_idempotency",
|
|
8435
|
+
"idx_knowledge_outbox_claim"
|
|
8436
|
+
]);
|
|
8437
|
+
const KNOWLEDGE_V2_NODES_SCHEMA = {
|
|
8438
|
+
id: {
|
|
8439
|
+
type: "text",
|
|
8440
|
+
nullable: false,
|
|
8441
|
+
primaryKey: true
|
|
8442
|
+
},
|
|
8443
|
+
name: {
|
|
8444
|
+
type: "text",
|
|
8445
|
+
nullable: false
|
|
8446
|
+
},
|
|
8447
|
+
kind: {
|
|
8448
|
+
type: "text",
|
|
8449
|
+
nullable: true
|
|
8450
|
+
},
|
|
8451
|
+
isScope: {
|
|
8452
|
+
type: "boolean",
|
|
8453
|
+
nullable: false
|
|
8454
|
+
},
|
|
8455
|
+
metadata: {
|
|
8456
|
+
type: "jsonb",
|
|
8457
|
+
nullable: true
|
|
8458
|
+
},
|
|
8459
|
+
version: {
|
|
8460
|
+
type: "integer",
|
|
8461
|
+
nullable: false
|
|
8462
|
+
},
|
|
8463
|
+
createdAt: {
|
|
8464
|
+
type: "timestamp",
|
|
8465
|
+
nullable: false
|
|
8466
|
+
},
|
|
8467
|
+
updatedAt: {
|
|
8468
|
+
type: "timestamp",
|
|
8469
|
+
nullable: false
|
|
8470
|
+
},
|
|
8471
|
+
deletedAt: {
|
|
8472
|
+
type: "timestamp",
|
|
8473
|
+
nullable: true
|
|
8474
|
+
},
|
|
8475
|
+
deletedBy: {
|
|
8476
|
+
type: "text",
|
|
8477
|
+
nullable: true
|
|
8478
|
+
},
|
|
8479
|
+
type: {
|
|
8480
|
+
type: "text",
|
|
8481
|
+
nullable: true
|
|
8482
|
+
},
|
|
8483
|
+
canonicalName: {
|
|
8484
|
+
type: "text",
|
|
8485
|
+
nullable: true
|
|
8486
|
+
},
|
|
8487
|
+
content: {
|
|
8488
|
+
type: "text",
|
|
8489
|
+
nullable: true
|
|
8490
|
+
},
|
|
8491
|
+
description: {
|
|
8492
|
+
type: "text",
|
|
8493
|
+
nullable: true
|
|
8494
|
+
},
|
|
8495
|
+
scope: {
|
|
8496
|
+
type: "jsonb",
|
|
8497
|
+
nullable: true
|
|
8498
|
+
},
|
|
8499
|
+
scopeKey: {
|
|
8500
|
+
type: "text",
|
|
8501
|
+
nullable: true
|
|
8502
|
+
},
|
|
8503
|
+
mergedInto: {
|
|
8504
|
+
type: "text",
|
|
8505
|
+
nullable: true
|
|
8506
|
+
}
|
|
8507
|
+
};
|
|
8508
|
+
const KNOWLEDGE_V2_RECORDS_SCHEMA = {
|
|
8509
|
+
id: {
|
|
8510
|
+
type: "text",
|
|
8511
|
+
nullable: false,
|
|
8512
|
+
primaryKey: true
|
|
8513
|
+
},
|
|
8514
|
+
node: {
|
|
8515
|
+
type: "text",
|
|
8516
|
+
nullable: false,
|
|
8517
|
+
references: {
|
|
8518
|
+
table: TABLE_KNOWLEDGE_NODES$1,
|
|
8519
|
+
column: "id"
|
|
8520
|
+
}
|
|
8521
|
+
},
|
|
8522
|
+
text: {
|
|
8523
|
+
type: "text",
|
|
8524
|
+
nullable: false
|
|
8525
|
+
},
|
|
8526
|
+
metadata: {
|
|
8527
|
+
type: "jsonb",
|
|
8528
|
+
nullable: true
|
|
8529
|
+
},
|
|
8530
|
+
version: {
|
|
8531
|
+
type: "integer",
|
|
8532
|
+
nullable: false
|
|
8533
|
+
},
|
|
8534
|
+
capturedAt: {
|
|
8535
|
+
type: "timestamp",
|
|
8536
|
+
nullable: false
|
|
8537
|
+
},
|
|
8538
|
+
updatedAt: {
|
|
8539
|
+
type: "timestamp",
|
|
8540
|
+
nullable: false
|
|
8541
|
+
},
|
|
8542
|
+
deletedAt: {
|
|
8543
|
+
type: "timestamp",
|
|
8544
|
+
nullable: true
|
|
8545
|
+
},
|
|
8546
|
+
deletedBy: {
|
|
8547
|
+
type: "text",
|
|
8548
|
+
nullable: true
|
|
8549
|
+
},
|
|
8550
|
+
scope: {
|
|
8551
|
+
type: "jsonb",
|
|
8552
|
+
nullable: true
|
|
8553
|
+
},
|
|
8554
|
+
scopeKey: {
|
|
8555
|
+
type: "text",
|
|
8556
|
+
nullable: true
|
|
8557
|
+
},
|
|
8558
|
+
sourceThreadId: {
|
|
8559
|
+
type: "text",
|
|
8560
|
+
nullable: true
|
|
8561
|
+
},
|
|
8562
|
+
when: {
|
|
8563
|
+
type: "timestamp",
|
|
8564
|
+
nullable: true
|
|
8565
|
+
}
|
|
8566
|
+
};
|
|
8567
|
+
const KNOWLEDGE_V2_MENTIONS_SCHEMA = {
|
|
8568
|
+
recordId: {
|
|
8569
|
+
type: "text",
|
|
8570
|
+
nullable: false,
|
|
8571
|
+
references: {
|
|
8572
|
+
table: TABLE_KNOWLEDGE_RECORDS$1,
|
|
8573
|
+
column: "id"
|
|
8574
|
+
}
|
|
8575
|
+
},
|
|
8576
|
+
targetNodeId: {
|
|
8577
|
+
type: "text",
|
|
8578
|
+
nullable: true,
|
|
8579
|
+
references: {
|
|
8580
|
+
table: TABLE_KNOWLEDGE_NODES$1,
|
|
8581
|
+
column: "id"
|
|
8582
|
+
}
|
|
8583
|
+
},
|
|
8584
|
+
sourceType: {
|
|
8585
|
+
type: "text",
|
|
8586
|
+
nullable: true
|
|
8587
|
+
},
|
|
8588
|
+
sourceId: {
|
|
8589
|
+
type: "text",
|
|
8590
|
+
nullable: true
|
|
8591
|
+
}
|
|
8592
|
+
};
|
|
8593
|
+
const KNOWLEDGE_NODE_SCOPES_SCHEMA = {
|
|
8594
|
+
nodeId: {
|
|
8595
|
+
type: "text",
|
|
8596
|
+
nullable: false,
|
|
8597
|
+
references: {
|
|
8598
|
+
table: TABLE_KNOWLEDGE_NODES$1,
|
|
8599
|
+
column: "id"
|
|
8600
|
+
}
|
|
8601
|
+
},
|
|
8602
|
+
scopeNodeId: {
|
|
8603
|
+
type: "text",
|
|
8604
|
+
nullable: false,
|
|
8605
|
+
references: {
|
|
8606
|
+
table: TABLE_KNOWLEDGE_NODES$1,
|
|
8607
|
+
column: "id"
|
|
8608
|
+
}
|
|
8609
|
+
},
|
|
8610
|
+
addedAt: {
|
|
8611
|
+
type: "timestamp",
|
|
8612
|
+
nullable: false
|
|
8613
|
+
}
|
|
8614
|
+
};
|
|
8615
|
+
const KNOWLEDGE_RECORD_SCOPES_SCHEMA = {
|
|
8616
|
+
recordId: {
|
|
8617
|
+
type: "text",
|
|
8618
|
+
nullable: false,
|
|
8619
|
+
references: {
|
|
8620
|
+
table: TABLE_KNOWLEDGE_RECORDS$1,
|
|
8621
|
+
column: "id"
|
|
8622
|
+
}
|
|
8623
|
+
},
|
|
8624
|
+
scopeNodeId: {
|
|
8625
|
+
type: "text",
|
|
8626
|
+
nullable: false,
|
|
8627
|
+
references: {
|
|
8628
|
+
table: TABLE_KNOWLEDGE_NODES$1,
|
|
8629
|
+
column: "id"
|
|
8630
|
+
}
|
|
8631
|
+
},
|
|
8632
|
+
addedAt: {
|
|
8633
|
+
type: "timestamp",
|
|
8634
|
+
nullable: false
|
|
8635
|
+
}
|
|
8636
|
+
};
|
|
8637
|
+
const KNOWLEDGE_SCOPE_GRANTS_SCHEMA = {
|
|
8638
|
+
scopeNodeId: {
|
|
8639
|
+
type: "text",
|
|
8640
|
+
nullable: false,
|
|
8641
|
+
references: {
|
|
8642
|
+
table: TABLE_KNOWLEDGE_NODES$1,
|
|
8643
|
+
column: "id"
|
|
8644
|
+
}
|
|
8645
|
+
},
|
|
8646
|
+
scopeRefId: {
|
|
8647
|
+
type: "text",
|
|
8648
|
+
nullable: false,
|
|
8649
|
+
references: {
|
|
8650
|
+
table: TABLE_KNOWLEDGE_NODES$1,
|
|
8651
|
+
column: "id"
|
|
8652
|
+
}
|
|
8653
|
+
},
|
|
8654
|
+
role: {
|
|
8655
|
+
type: "text",
|
|
8656
|
+
nullable: false
|
|
8657
|
+
},
|
|
8658
|
+
canSuggest: {
|
|
8659
|
+
type: "boolean",
|
|
8660
|
+
nullable: true
|
|
8661
|
+
}
|
|
8662
|
+
};
|
|
8663
|
+
const KNOWLEDGE_ACCESS_STATE_SCHEMA = {
|
|
8664
|
+
id: {
|
|
8665
|
+
type: "text",
|
|
8666
|
+
nullable: false,
|
|
8667
|
+
primaryKey: true
|
|
8668
|
+
},
|
|
8669
|
+
epoch: {
|
|
8670
|
+
type: "integer",
|
|
8671
|
+
nullable: false
|
|
8672
|
+
},
|
|
8673
|
+
schemaVersion: {
|
|
8674
|
+
type: "integer",
|
|
8675
|
+
nullable: false
|
|
8676
|
+
}
|
|
8677
|
+
};
|
|
8678
|
+
const KNOWLEDGE_SCOPE_ADDRESSES_SCHEMA = {
|
|
8679
|
+
address: {
|
|
8680
|
+
type: "text",
|
|
8681
|
+
nullable: false,
|
|
8682
|
+
primaryKey: true
|
|
8683
|
+
},
|
|
8684
|
+
scopeNodeId: {
|
|
8685
|
+
type: "text",
|
|
8686
|
+
nullable: false,
|
|
8687
|
+
references: {
|
|
8688
|
+
table: TABLE_KNOWLEDGE_NODES$1,
|
|
8689
|
+
column: "id"
|
|
8690
|
+
}
|
|
8691
|
+
}
|
|
8692
|
+
};
|
|
8693
|
+
const KNOWLEDGE_NODE_ADDRESSES_SCHEMA = {
|
|
8694
|
+
source: {
|
|
8695
|
+
type: "text",
|
|
8696
|
+
nullable: false
|
|
8697
|
+
},
|
|
8698
|
+
address: {
|
|
8699
|
+
type: "text",
|
|
8700
|
+
nullable: false
|
|
8701
|
+
},
|
|
8702
|
+
nodeId: {
|
|
8703
|
+
type: "text",
|
|
8704
|
+
nullable: false,
|
|
8705
|
+
references: {
|
|
8706
|
+
table: TABLE_KNOWLEDGE_NODES$1,
|
|
8707
|
+
column: "id"
|
|
8708
|
+
}
|
|
8709
|
+
}
|
|
8710
|
+
};
|
|
8711
|
+
const KNOWLEDGE_IMPORT_STATE_SCHEMA = {
|
|
8712
|
+
importerId: {
|
|
8713
|
+
type: "text",
|
|
8714
|
+
nullable: false
|
|
8715
|
+
},
|
|
8716
|
+
binding: {
|
|
8717
|
+
type: "text",
|
|
8718
|
+
nullable: false
|
|
8719
|
+
},
|
|
8720
|
+
key: {
|
|
8721
|
+
type: "text",
|
|
8722
|
+
nullable: false
|
|
8723
|
+
},
|
|
8724
|
+
value: {
|
|
8725
|
+
type: "text",
|
|
8726
|
+
nullable: false
|
|
8727
|
+
}
|
|
8728
|
+
};
|
|
8729
|
+
const KNOWLEDGE_IMPORT_RUNS_SCHEMA = {
|
|
8730
|
+
id: {
|
|
8731
|
+
type: "text",
|
|
8732
|
+
nullable: false,
|
|
8733
|
+
primaryKey: true
|
|
8734
|
+
},
|
|
8735
|
+
importerId: {
|
|
8736
|
+
type: "text",
|
|
8737
|
+
nullable: false
|
|
8738
|
+
},
|
|
8739
|
+
binding: {
|
|
8740
|
+
type: "text",
|
|
8741
|
+
nullable: false
|
|
8742
|
+
},
|
|
8743
|
+
importKind: {
|
|
8744
|
+
type: "text",
|
|
8745
|
+
nullable: false
|
|
8746
|
+
},
|
|
8747
|
+
triggerKind: {
|
|
8748
|
+
type: "text",
|
|
8749
|
+
nullable: false
|
|
8750
|
+
},
|
|
8751
|
+
status: {
|
|
8752
|
+
type: "text",
|
|
8753
|
+
nullable: false
|
|
8754
|
+
},
|
|
8755
|
+
error: {
|
|
8756
|
+
type: "text",
|
|
8757
|
+
nullable: true
|
|
8758
|
+
},
|
|
8759
|
+
transcriptThreadId: {
|
|
8760
|
+
type: "text",
|
|
8761
|
+
nullable: true
|
|
8762
|
+
},
|
|
8763
|
+
traceId: {
|
|
8764
|
+
type: "text",
|
|
8765
|
+
nullable: true
|
|
8766
|
+
},
|
|
8767
|
+
queuedAt: {
|
|
8768
|
+
type: "timestamp",
|
|
8769
|
+
nullable: false
|
|
8770
|
+
},
|
|
8771
|
+
startedAt: {
|
|
8772
|
+
type: "timestamp",
|
|
8773
|
+
nullable: true
|
|
8774
|
+
},
|
|
8775
|
+
completedAt: {
|
|
8776
|
+
type: "timestamp",
|
|
8777
|
+
nullable: true
|
|
8778
|
+
}
|
|
8779
|
+
};
|
|
8780
|
+
const KNOWLEDGE_V2_ACTIVITY_SCHEMA = {
|
|
8781
|
+
id: {
|
|
8782
|
+
type: "text",
|
|
8783
|
+
nullable: false,
|
|
8784
|
+
primaryKey: true
|
|
8785
|
+
},
|
|
8786
|
+
action: {
|
|
8787
|
+
type: "text",
|
|
8788
|
+
nullable: false
|
|
8789
|
+
},
|
|
8790
|
+
targetType: {
|
|
8791
|
+
type: "text",
|
|
8792
|
+
nullable: true
|
|
8793
|
+
},
|
|
8794
|
+
targetId: {
|
|
8795
|
+
type: "text",
|
|
8796
|
+
nullable: true
|
|
8797
|
+
},
|
|
8798
|
+
contextScopeId: {
|
|
8799
|
+
type: "text",
|
|
8800
|
+
nullable: true,
|
|
8801
|
+
references: {
|
|
8802
|
+
table: TABLE_KNOWLEDGE_NODES$1,
|
|
8803
|
+
column: "id"
|
|
8804
|
+
}
|
|
8805
|
+
},
|
|
8806
|
+
importRunId: {
|
|
8807
|
+
type: "text",
|
|
8808
|
+
nullable: true,
|
|
8809
|
+
references: {
|
|
8810
|
+
table: TABLE_KNOWLEDGE_IMPORT_RUNS,
|
|
8811
|
+
column: "id"
|
|
8812
|
+
}
|
|
8813
|
+
},
|
|
8814
|
+
details: {
|
|
8815
|
+
type: "jsonb",
|
|
8816
|
+
nullable: true
|
|
8817
|
+
},
|
|
8818
|
+
createdAt: {
|
|
8819
|
+
type: "timestamp",
|
|
8820
|
+
nullable: false
|
|
8821
|
+
},
|
|
8822
|
+
recordType: {
|
|
8823
|
+
type: "text",
|
|
8824
|
+
nullable: true
|
|
8825
|
+
},
|
|
8826
|
+
recordId: {
|
|
8827
|
+
type: "text",
|
|
8828
|
+
nullable: true
|
|
8829
|
+
},
|
|
8830
|
+
scope: {
|
|
8831
|
+
type: "jsonb",
|
|
8832
|
+
nullable: true
|
|
8833
|
+
},
|
|
8834
|
+
scopeKey: {
|
|
8835
|
+
type: "text",
|
|
8836
|
+
nullable: true
|
|
8837
|
+
},
|
|
8838
|
+
sourceThreadId: {
|
|
8839
|
+
type: "text",
|
|
8840
|
+
nullable: true
|
|
8841
|
+
}
|
|
8842
|
+
};
|
|
8843
|
+
const KNOWLEDGE_PROPOSALS_SCHEMA = {
|
|
8844
|
+
id: {
|
|
8845
|
+
type: "text",
|
|
8846
|
+
nullable: false,
|
|
8847
|
+
primaryKey: true
|
|
8848
|
+
},
|
|
8849
|
+
targetType: {
|
|
8850
|
+
type: "text",
|
|
8851
|
+
nullable: false
|
|
8852
|
+
},
|
|
8853
|
+
targetId: {
|
|
8854
|
+
type: "text",
|
|
8855
|
+
nullable: false
|
|
8856
|
+
},
|
|
8857
|
+
action: {
|
|
8858
|
+
type: "text",
|
|
8859
|
+
nullable: false
|
|
8860
|
+
},
|
|
8861
|
+
changes: {
|
|
8862
|
+
type: "jsonb",
|
|
8863
|
+
nullable: false
|
|
8864
|
+
},
|
|
8865
|
+
reason: {
|
|
8866
|
+
type: "text",
|
|
8867
|
+
nullable: true
|
|
8868
|
+
},
|
|
8869
|
+
proposerContextScopeId: {
|
|
8870
|
+
type: "text",
|
|
8871
|
+
nullable: false
|
|
8872
|
+
},
|
|
8873
|
+
expectedVersion: {
|
|
8874
|
+
type: "integer",
|
|
8875
|
+
nullable: false
|
|
8876
|
+
},
|
|
8877
|
+
status: {
|
|
8878
|
+
type: "text",
|
|
8879
|
+
nullable: false
|
|
8880
|
+
},
|
|
8881
|
+
reviewerContextScopeId: {
|
|
8882
|
+
type: "text",
|
|
8883
|
+
nullable: true
|
|
8884
|
+
},
|
|
8885
|
+
reviewedAt: {
|
|
8886
|
+
type: "timestamp",
|
|
8887
|
+
nullable: true
|
|
8888
|
+
},
|
|
8889
|
+
createdAt: {
|
|
8890
|
+
type: "timestamp",
|
|
8891
|
+
nullable: false
|
|
8892
|
+
}
|
|
8893
|
+
};
|
|
8894
|
+
function assertKnowledgeV2CoreSupport(features) {
|
|
8895
|
+
if (!features.has("knowledge-v2")) throw new Error(`Knowledge v2 requires a @mastra/core release with the "${KNOWLEDGE_V2_CORE_FEATURE}" feature. Upgrade @mastra/core to use Knowledge; other storage domains keep working on this version.`);
|
|
8896
|
+
}
|
|
8897
|
+
function resolveKnowledgeV2Core(storageModule) {
|
|
8898
|
+
if (typeof storageModule !== "object" || storageModule === null) throw new Error("@mastra/core advertises Knowledge v2 without the required storage API");
|
|
8899
|
+
const { assertKnowledgeDescriptionWithinBound: assertDescription, assertKnowledgeRecordTextWithinBound: assertRecordText, assertKnowledgeSchemaCompatible: assertCompatible, inspectKnowledgeSchema: inspectSchema } = storageModule;
|
|
8900
|
+
if (typeof assertDescription !== "function" || typeof assertRecordText !== "function" || typeof assertCompatible !== "function" || typeof inspectSchema !== "function") throw new Error("@mastra/core advertises Knowledge v2 without the required storage API");
|
|
8901
|
+
return {
|
|
8902
|
+
assertKnowledgeDescriptionWithinBound: (description) => assertDescription(description),
|
|
8903
|
+
assertKnowledgeRecordTextWithinBound: (text) => assertRecordText(text),
|
|
8904
|
+
assertKnowledgeSchemaCompatible: (inspection) => assertCompatible(inspection),
|
|
8905
|
+
inspectKnowledgeSchema: (snapshot) => inspectSchema(snapshot)
|
|
8906
|
+
};
|
|
8907
|
+
}
|
|
8908
|
+
function createKnowledgeV2CoreLoader(features, loadStorageModule) {
|
|
8909
|
+
let knowledgeV2Core;
|
|
8910
|
+
return async () => {
|
|
8911
|
+
assertKnowledgeV2CoreSupport(features);
|
|
8912
|
+
knowledgeV2Core ??= loadStorageModule().then(resolveKnowledgeV2Core).catch((error) => {
|
|
8913
|
+
knowledgeV2Core = void 0;
|
|
8914
|
+
throw error;
|
|
8915
|
+
});
|
|
8916
|
+
return knowledgeV2Core;
|
|
8917
|
+
};
|
|
8918
|
+
}
|
|
8919
|
+
//#endregion
|
|
8280
8920
|
//#region src/storage/domains/knowledge/index.ts
|
|
8281
|
-
|
|
8921
|
+
const loadKnowledgeV2Core = createKnowledgeV2CoreLoader(coreFeatures, () => import("@mastra/core/storage"));
|
|
8282
8922
|
async function assertKnowledgeDescriptionWithinBoundCompat(description) {
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
assertWithinBound = resolvedAssert;
|
|
8290
|
-
}
|
|
8291
|
-
assertWithinBound(description);
|
|
8923
|
+
const { assertKnowledgeDescriptionWithinBound } = await loadKnowledgeV2Core();
|
|
8924
|
+
assertKnowledgeDescriptionWithinBound(description);
|
|
8925
|
+
}
|
|
8926
|
+
async function assertKnowledgeRecordTextWithinBoundCompat(text) {
|
|
8927
|
+
const { assertKnowledgeRecordTextWithinBound } = await loadKnowledgeV2Core();
|
|
8928
|
+
assertKnowledgeRecordTextWithinBound(text);
|
|
8292
8929
|
}
|
|
8293
8930
|
const camelCaseColumns = [
|
|
8294
8931
|
"canonicalName",
|
|
@@ -8299,7 +8936,6 @@ const camelCaseColumns = [
|
|
|
8299
8936
|
"node",
|
|
8300
8937
|
"sourceThreadId",
|
|
8301
8938
|
"capturedAt",
|
|
8302
|
-
"maxScope",
|
|
8303
8939
|
"deletedAt",
|
|
8304
8940
|
"deletedBy",
|
|
8305
8941
|
"sourceType",
|
|
@@ -8313,7 +8949,30 @@ const camelCaseColumns = [
|
|
|
8313
8949
|
"availableAt",
|
|
8314
8950
|
"claimedAt",
|
|
8315
8951
|
"claimedBy",
|
|
8316
|
-
"completedAt"
|
|
8952
|
+
"completedAt",
|
|
8953
|
+
"isScope",
|
|
8954
|
+
"nodeId",
|
|
8955
|
+
"targetNodeId",
|
|
8956
|
+
"scopeNodeId",
|
|
8957
|
+
"scopeRefId",
|
|
8958
|
+
"canSuggest",
|
|
8959
|
+
"schemaVersion",
|
|
8960
|
+
"importerId",
|
|
8961
|
+
"importKind",
|
|
8962
|
+
"triggerKind",
|
|
8963
|
+
"transcriptThreadId",
|
|
8964
|
+
"traceId",
|
|
8965
|
+
"queuedAt",
|
|
8966
|
+
"startedAt",
|
|
8967
|
+
"targetType",
|
|
8968
|
+
"targetId",
|
|
8969
|
+
"contextScopeId",
|
|
8970
|
+
"importRunId",
|
|
8971
|
+
"proposerContextScopeId",
|
|
8972
|
+
"expectedVersion",
|
|
8973
|
+
"reviewerContextScopeId",
|
|
8974
|
+
"reviewedAt",
|
|
8975
|
+
"addedAt"
|
|
8317
8976
|
];
|
|
8318
8977
|
function transformSqlCode(sql, transform) {
|
|
8319
8978
|
return sql.split(/('(?:''|[^'])*')/g).map((part, index) => index % 2 === 0 ? transform(part) : part).join("");
|
|
@@ -8328,14 +8987,7 @@ function postgresSql(sql, schemaName) {
|
|
|
8328
8987
|
const quotedSchema = `"${parseSchemaName(schemaName)}"`;
|
|
8329
8988
|
normalized = transformSqlCode(normalized, (code) => {
|
|
8330
8989
|
let transformed = code;
|
|
8331
|
-
for (const table of [
|
|
8332
|
-
TABLE_KNOWLEDGE_NODES,
|
|
8333
|
-
TABLE_KNOWLEDGE_RECORDS,
|
|
8334
|
-
TABLE_KNOWLEDGE_MENTIONS,
|
|
8335
|
-
TABLE_KNOWLEDGE_CURSORS,
|
|
8336
|
-
TABLE_KNOWLEDGE_ACTIVITY,
|
|
8337
|
-
TABLE_KNOWLEDGE_SEMANTIC_OUTBOX
|
|
8338
|
-
]) transformed = transformed.replaceAll(`"${table}"`, `${quotedSchema}."${table}"`);
|
|
8990
|
+
for (const table of [...KNOWLEDGE_TABLE_NAMES, ...RETIRED_KNOWLEDGE_TABLE_NAMES]) transformed = transformed.replaceAll(`"${table}"`, `${quotedSchema}."${table}"`);
|
|
8339
8991
|
return transformed;
|
|
8340
8992
|
});
|
|
8341
8993
|
}
|
|
@@ -8343,17 +8995,35 @@ function postgresSql(sql, schemaName) {
|
|
|
8343
8995
|
return transformSqlCode(normalized, (code) => code.replace(/\?/g, () => `$${++index}`));
|
|
8344
8996
|
}
|
|
8345
8997
|
function createExecutor(client, schemaName) {
|
|
8346
|
-
|
|
8998
|
+
const execute = async (statement, qualifyWithSchema) => {
|
|
8347
8999
|
const sql = typeof statement === "string" ? statement : statement.sql;
|
|
8348
9000
|
const args = typeof statement === "string" ? [] : statement.args ?? [];
|
|
8349
|
-
const result = await client.query(postgresSql(sql, schemaName), args);
|
|
9001
|
+
const result = await client.query(postgresSql(sql, qualifyWithSchema ? schemaName : void 0), args);
|
|
8350
9002
|
return {
|
|
8351
9003
|
rows: result.rows,
|
|
8352
9004
|
rowsAffected: result.rowCount ?? 0
|
|
8353
9005
|
};
|
|
8354
|
-
}
|
|
9006
|
+
};
|
|
9007
|
+
return {
|
|
9008
|
+
execute: (statement) => execute(statement, true),
|
|
9009
|
+
executeRaw: (statement) => execute(statement, false)
|
|
9010
|
+
};
|
|
8355
9011
|
}
|
|
8356
|
-
|
|
9012
|
+
/**
|
|
9013
|
+
* Scope keys visible to a caller holding `scope`: a row is visible when its scope is a subset of the
|
|
9014
|
+
* caller's (companion entries such as `resource:x:uncurated` make this more than a prefix match).
|
|
9015
|
+
* Matching rows by `scopeKey` keeps visibility filters on an index; a caller scope has only a few
|
|
9016
|
+
* entries, so the list stays small.
|
|
9017
|
+
*/
|
|
9018
|
+
function visibleScopeKeys(scope) {
|
|
9019
|
+
const canonical = canonicalizeKnowledgeScope(scope);
|
|
9020
|
+
const keys = /* @__PURE__ */ new Set();
|
|
9021
|
+
for (let mask = 1; mask < 1 << canonical.length; mask++) try {
|
|
9022
|
+
keys.add(knowledgeScopeKey(canonical.filter((_, index) => mask & 1 << index)));
|
|
9023
|
+
} catch {}
|
|
9024
|
+
return [...keys];
|
|
9025
|
+
}
|
|
9026
|
+
const visibleSql = (keys, scopeKeyColumn = "scopeKey") => `${scopeKeyColumn} IN (${keys.map(() => "?").join(",")})`;
|
|
8357
9027
|
function parseJson$1(value) {
|
|
8358
9028
|
if (typeof value === "string") return JSON.parse(value);
|
|
8359
9029
|
if (value instanceof Uint8Array) return JSON.parse(new TextDecoder().decode(value));
|
|
@@ -8393,9 +9063,10 @@ function parseNode(row) {
|
|
|
8393
9063
|
id: String(row.id),
|
|
8394
9064
|
type: "node",
|
|
8395
9065
|
name: String(row.name),
|
|
8396
|
-
kind: String(row.kind),
|
|
9066
|
+
kind: row.kind == null ? "" : String(row.kind),
|
|
8397
9067
|
content: row.content == null ? void 0 : String(row.content),
|
|
8398
9068
|
description: row.description == null ? void 0 : String(row.description),
|
|
9069
|
+
isScope: row.isScope === true || row.isScope === 1 ? true : void 0,
|
|
8399
9070
|
scope: parseJson$1(row.scopeJson ?? row.scope),
|
|
8400
9071
|
version: Number(row.version),
|
|
8401
9072
|
mergedInto: row.mergedInto == null ? void 0 : String(row.mergedInto),
|
|
@@ -8412,7 +9083,6 @@ function parseKnowledge(row) {
|
|
|
8412
9083
|
sourceThreadId: String(row.sourceThreadId),
|
|
8413
9084
|
capturedAt: toDate$1(row.capturedAt),
|
|
8414
9085
|
when: optionalDate(row.when),
|
|
8415
|
-
maxScope: row.maxScope == null ? void 0 : String(row.maxScope),
|
|
8416
9086
|
metadata: row.metadata == null ? void 0 : parseJson$1(row.metadata),
|
|
8417
9087
|
deletedAt: optionalDate(row.deletedAt),
|
|
8418
9088
|
deletedBy: row.deletedBy == null ? void 0 : String(row.deletedBy)
|
|
@@ -8449,6 +9119,10 @@ function knowledgeIndexes(schemaName) {
|
|
|
8449
9119
|
name: "idx_knowledge_nodes_scope",
|
|
8450
9120
|
sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_nodes_scope ON ${table(TABLE_KNOWLEDGE_NODES)} ("scopeKey", "type");`
|
|
8451
9121
|
},
|
|
9122
|
+
{
|
|
9123
|
+
name: "idx_knowledge_nodes_name",
|
|
9124
|
+
sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_nodes_name ON ${table(TABLE_KNOWLEDGE_NODES)} ("type", "canonicalName");`
|
|
9125
|
+
},
|
|
8452
9126
|
{
|
|
8453
9127
|
name: "idx_knowledge_records_node_latest",
|
|
8454
9128
|
sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_records_node_latest ON ${table(TABLE_KNOWLEDGE_RECORDS)} ("node", "id" DESC);`
|
|
@@ -8465,6 +9139,14 @@ function knowledgeIndexes(schemaName) {
|
|
|
8465
9139
|
name: "idx_knowledge_activity_latest",
|
|
8466
9140
|
sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_activity_latest ON ${table(TABLE_KNOWLEDGE_ACTIVITY)} ("id" DESC);`
|
|
8467
9141
|
},
|
|
9142
|
+
{
|
|
9143
|
+
name: "idx_knowledge_records_scope",
|
|
9144
|
+
sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_records_scope ON ${table(TABLE_KNOWLEDGE_RECORDS)} ("scopeKey", "id");`
|
|
9145
|
+
},
|
|
9146
|
+
{
|
|
9147
|
+
name: "idx_knowledge_activity_scope",
|
|
9148
|
+
sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_activity_scope ON ${table(TABLE_KNOWLEDGE_ACTIVITY)} ("scopeKey", "id" DESC);`
|
|
9149
|
+
},
|
|
8468
9150
|
{
|
|
8469
9151
|
name: "idx_knowledge_outbox_idempotency",
|
|
8470
9152
|
sql: `CREATE UNIQUE INDEX IF NOT EXISTS idx_knowledge_outbox_idempotency ON ${table(TABLE_KNOWLEDGE_SEMANTIC_OUTBOX)} ("idempotencyKey");`
|
|
@@ -8472,67 +9154,152 @@ function knowledgeIndexes(schemaName) {
|
|
|
8472
9154
|
{
|
|
8473
9155
|
name: "idx_knowledge_outbox_claim",
|
|
8474
9156
|
sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_outbox_claim ON ${table(TABLE_KNOWLEDGE_SEMANTIC_OUTBOX)} ("status", "availableAt", "createdAt");`
|
|
9157
|
+
},
|
|
9158
|
+
{
|
|
9159
|
+
name: "idx_knowledge_node_scopes_scope",
|
|
9160
|
+
sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_node_scopes_scope ON ${table(TABLE_KNOWLEDGE_NODE_SCOPES)} ("scopeNodeId", "nodeId");`
|
|
9161
|
+
},
|
|
9162
|
+
{
|
|
9163
|
+
name: "idx_knowledge_record_scopes_scope",
|
|
9164
|
+
sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_record_scopes_scope ON ${table(TABLE_KNOWLEDGE_RECORD_SCOPES)} ("scopeNodeId", "recordId");`
|
|
9165
|
+
},
|
|
9166
|
+
{
|
|
9167
|
+
name: "idx_knowledge_scope_grants_ref",
|
|
9168
|
+
sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_scope_grants_ref ON ${table(TABLE_KNOWLEDGE_SCOPE_GRANTS)} ("scopeRefId", "scopeNodeId");`
|
|
9169
|
+
},
|
|
9170
|
+
{
|
|
9171
|
+
name: "idx_knowledge_node_addresses_node",
|
|
9172
|
+
sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_node_addresses_node ON ${table(TABLE_KNOWLEDGE_NODE_ADDRESSES)} ("nodeId");`
|
|
9173
|
+
},
|
|
9174
|
+
{
|
|
9175
|
+
name: "idx_knowledge_import_runs_lookup",
|
|
9176
|
+
sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_import_runs_lookup ON ${table(TABLE_KNOWLEDGE_IMPORT_RUNS)} ("importerId", "binding", "queuedAt" DESC);`
|
|
9177
|
+
},
|
|
9178
|
+
{
|
|
9179
|
+
name: "idx_knowledge_activity_import_run",
|
|
9180
|
+
sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_activity_import_run ON ${table(TABLE_KNOWLEDGE_ACTIVITY)} ("importRunId", "id" DESC);`
|
|
8475
9181
|
}
|
|
8476
9182
|
];
|
|
8477
9183
|
}
|
|
8478
9184
|
function knowledgeIndexDDL(schemaName) {
|
|
8479
9185
|
return knowledgeIndexes(schemaName).map((index) => index.sql);
|
|
8480
9186
|
}
|
|
9187
|
+
const KNOWLEDGE_CURATION_CURSOR_REMOVED_MESSAGE = "Knowledge curation cursors were removed: observation-time curate is the only Knowledge writer and needs no cursor.";
|
|
9188
|
+
const knowledgeTableDefinitions = [
|
|
9189
|
+
{
|
|
9190
|
+
tableName: TABLE_KNOWLEDGE_NODES,
|
|
9191
|
+
schema: KNOWLEDGE_V2_NODES_SCHEMA
|
|
9192
|
+
},
|
|
9193
|
+
{
|
|
9194
|
+
tableName: TABLE_KNOWLEDGE_RECORDS,
|
|
9195
|
+
schema: KNOWLEDGE_V2_RECORDS_SCHEMA
|
|
9196
|
+
},
|
|
9197
|
+
{
|
|
9198
|
+
tableName: TABLE_KNOWLEDGE_MENTIONS,
|
|
9199
|
+
schema: KNOWLEDGE_V2_MENTIONS_SCHEMA,
|
|
9200
|
+
compositePrimaryKey: ["recordId", "sourceId"]
|
|
9201
|
+
},
|
|
9202
|
+
{
|
|
9203
|
+
tableName: TABLE_KNOWLEDGE_NODE_SCOPES,
|
|
9204
|
+
schema: KNOWLEDGE_NODE_SCOPES_SCHEMA,
|
|
9205
|
+
compositePrimaryKey: ["nodeId", "scopeNodeId"]
|
|
9206
|
+
},
|
|
9207
|
+
{
|
|
9208
|
+
tableName: TABLE_KNOWLEDGE_RECORD_SCOPES,
|
|
9209
|
+
schema: KNOWLEDGE_RECORD_SCOPES_SCHEMA,
|
|
9210
|
+
compositePrimaryKey: ["recordId", "scopeNodeId"]
|
|
9211
|
+
},
|
|
9212
|
+
{
|
|
9213
|
+
tableName: TABLE_KNOWLEDGE_SCOPE_GRANTS,
|
|
9214
|
+
schema: KNOWLEDGE_SCOPE_GRANTS_SCHEMA,
|
|
9215
|
+
compositePrimaryKey: ["scopeNodeId", "scopeRefId"]
|
|
9216
|
+
},
|
|
9217
|
+
{
|
|
9218
|
+
tableName: TABLE_KNOWLEDGE_ACCESS_STATE,
|
|
9219
|
+
schema: KNOWLEDGE_ACCESS_STATE_SCHEMA
|
|
9220
|
+
},
|
|
9221
|
+
{
|
|
9222
|
+
tableName: TABLE_KNOWLEDGE_SCOPE_ADDRESSES,
|
|
9223
|
+
schema: KNOWLEDGE_SCOPE_ADDRESSES_SCHEMA
|
|
9224
|
+
},
|
|
9225
|
+
{
|
|
9226
|
+
tableName: TABLE_KNOWLEDGE_NODE_ADDRESSES,
|
|
9227
|
+
schema: KNOWLEDGE_NODE_ADDRESSES_SCHEMA,
|
|
9228
|
+
compositePrimaryKey: ["source", "address"]
|
|
9229
|
+
},
|
|
9230
|
+
{
|
|
9231
|
+
tableName: TABLE_KNOWLEDGE_IMPORT_STATE,
|
|
9232
|
+
schema: KNOWLEDGE_IMPORT_STATE_SCHEMA,
|
|
9233
|
+
compositePrimaryKey: [
|
|
9234
|
+
"importerId",
|
|
9235
|
+
"binding",
|
|
9236
|
+
"key"
|
|
9237
|
+
]
|
|
9238
|
+
},
|
|
9239
|
+
{
|
|
9240
|
+
tableName: TABLE_KNOWLEDGE_IMPORT_RUNS,
|
|
9241
|
+
schema: KNOWLEDGE_IMPORT_RUNS_SCHEMA
|
|
9242
|
+
},
|
|
9243
|
+
{
|
|
9244
|
+
tableName: TABLE_KNOWLEDGE_PROPOSALS,
|
|
9245
|
+
schema: KNOWLEDGE_PROPOSALS_SCHEMA
|
|
9246
|
+
},
|
|
9247
|
+
{
|
|
9248
|
+
tableName: TABLE_KNOWLEDGE_ACTIVITY,
|
|
9249
|
+
schema: KNOWLEDGE_V2_ACTIVITY_SCHEMA
|
|
9250
|
+
},
|
|
9251
|
+
{
|
|
9252
|
+
tableName: TABLE_KNOWLEDGE_SEMANTIC_OUTBOX,
|
|
9253
|
+
schema: KNOWLEDGE_SEMANTIC_OUTBOX_SCHEMA
|
|
9254
|
+
}
|
|
9255
|
+
];
|
|
9256
|
+
function pgSetting(config, key) {
|
|
9257
|
+
const value = config[key] || process.env[`PG${key.toUpperCase()}`] || defaults[key];
|
|
9258
|
+
return value === void 0 || value === null || value === "" ? void 0 : String(value);
|
|
9259
|
+
}
|
|
9260
|
+
function effectivePgTarget(config) {
|
|
9261
|
+
const host = pgSetting(config, "host");
|
|
9262
|
+
const database = pgSetting(config, "database") ?? pgSetting(config, "user");
|
|
9263
|
+
if (!host || !database) return void 0;
|
|
9264
|
+
return `${host.startsWith("/") ? host : host.toLocaleLowerCase()}:${pgSetting(config, "port") ?? "5432"}/${database}`;
|
|
9265
|
+
}
|
|
9266
|
+
function canonicalPgTarget(config) {
|
|
9267
|
+
if (config.connectionString) try {
|
|
9268
|
+
const parsed = new URL(config.connectionString);
|
|
9269
|
+
return effectivePgTarget({
|
|
9270
|
+
host: decodeURIComponent(parsed.hostname) || config.host,
|
|
9271
|
+
port: parsed.port || config.port,
|
|
9272
|
+
database: decodeURIComponent(parsed.pathname.slice(1)) || config.database,
|
|
9273
|
+
user: decodeURIComponent(parsed.username) || config.user
|
|
9274
|
+
});
|
|
9275
|
+
} catch {
|
|
9276
|
+
return config.connectionString;
|
|
9277
|
+
}
|
|
9278
|
+
return effectivePgTarget(config);
|
|
9279
|
+
}
|
|
9280
|
+
function pgSourceConfig(source) {
|
|
9281
|
+
if (!source || !("options" in source)) return void 0;
|
|
9282
|
+
return source.options;
|
|
9283
|
+
}
|
|
9284
|
+
function pgClientPool(client) {
|
|
9285
|
+
if (!client || !("$pool" in client)) return void 0;
|
|
9286
|
+
return client.$pool;
|
|
9287
|
+
}
|
|
9288
|
+
function getPgKnowledgeIsolationKey(config) {
|
|
9289
|
+
const schema = config.schemaName ?? "public";
|
|
9290
|
+
const poolConfig = pgSourceConfig(config.pool ?? pgClientPool(config.client));
|
|
9291
|
+
const target = Boolean(config.connectionString || config.host || config.database) ? canonicalPgTarget(config) : poolConfig ? canonicalPgTarget(poolConfig) : void 0;
|
|
9292
|
+
if (target) return `pg:${target}:schema:${schema}`;
|
|
9293
|
+
return `pg:unidentified:schema:${schema}`;
|
|
9294
|
+
}
|
|
8481
9295
|
var KnowledgePG = class extends KnowledgeStorage {
|
|
8482
|
-
static MANAGED_TABLES =
|
|
8483
|
-
TABLE_KNOWLEDGE_NODES,
|
|
8484
|
-
TABLE_KNOWLEDGE_RECORDS,
|
|
8485
|
-
TABLE_KNOWLEDGE_MENTIONS,
|
|
8486
|
-
TABLE_KNOWLEDGE_CURSORS,
|
|
8487
|
-
TABLE_KNOWLEDGE_ACTIVITY,
|
|
8488
|
-
TABLE_KNOWLEDGE_SEMANTIC_OUTBOX
|
|
8489
|
-
];
|
|
9296
|
+
static MANAGED_TABLES = KNOWLEDGE_TABLE_NAMES;
|
|
8490
9297
|
static getExportDDL(schemaName) {
|
|
8491
|
-
return [
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
includeAllConstraints: true
|
|
8497
|
-
}),
|
|
8498
|
-
generateTableSQL({
|
|
8499
|
-
tableName: TABLE_KNOWLEDGE_RECORDS,
|
|
8500
|
-
schema: KNOWLEDGE_RECORDS_SCHEMA,
|
|
8501
|
-
schemaName,
|
|
8502
|
-
includeAllConstraints: true
|
|
8503
|
-
}),
|
|
8504
|
-
generateTableSQL({
|
|
8505
|
-
tableName: TABLE_KNOWLEDGE_MENTIONS,
|
|
8506
|
-
schema: KNOWLEDGE_MENTIONS_SCHEMA,
|
|
8507
|
-
schemaName,
|
|
8508
|
-
compositePrimaryKey: [
|
|
8509
|
-
"sourceType",
|
|
8510
|
-
"sourceId",
|
|
8511
|
-
"recordId"
|
|
8512
|
-
],
|
|
8513
|
-
includeAllConstraints: true
|
|
8514
|
-
}),
|
|
8515
|
-
generateTableSQL({
|
|
8516
|
-
tableName: TABLE_KNOWLEDGE_CURSORS,
|
|
8517
|
-
schema: KNOWLEDGE_CURSORS_SCHEMA,
|
|
8518
|
-
schemaName,
|
|
8519
|
-
compositePrimaryKey: ["sourceThreadId", "agent"],
|
|
8520
|
-
includeAllConstraints: true
|
|
8521
|
-
}),
|
|
8522
|
-
generateTableSQL({
|
|
8523
|
-
tableName: TABLE_KNOWLEDGE_ACTIVITY,
|
|
8524
|
-
schema: KNOWLEDGE_ACTIVITY_SCHEMA,
|
|
8525
|
-
schemaName,
|
|
8526
|
-
includeAllConstraints: true
|
|
8527
|
-
}),
|
|
8528
|
-
generateTableSQL({
|
|
8529
|
-
tableName: TABLE_KNOWLEDGE_SEMANTIC_OUTBOX,
|
|
8530
|
-
schema: KNOWLEDGE_SEMANTIC_OUTBOX_SCHEMA,
|
|
8531
|
-
schemaName,
|
|
8532
|
-
includeAllConstraints: true
|
|
8533
|
-
}),
|
|
8534
|
-
...knowledgeIndexDDL(schemaName)
|
|
8535
|
-
];
|
|
9298
|
+
return [...knowledgeTableDefinitions.map((definition) => generateTableSQL({
|
|
9299
|
+
...definition,
|
|
9300
|
+
schemaName,
|
|
9301
|
+
includeAllConstraints: true
|
|
9302
|
+
})), ...knowledgeIndexDDL(schemaName)];
|
|
8536
9303
|
}
|
|
8537
9304
|
#client;
|
|
8538
9305
|
#executor;
|
|
@@ -8541,7 +9308,7 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
8541
9308
|
#db;
|
|
8542
9309
|
#schemaName;
|
|
8543
9310
|
constructor(config) {
|
|
8544
|
-
super();
|
|
9311
|
+
super({ storageIsolationKey: config.storageIsolationKey ?? getPgKnowledgeIsolationKey(config) });
|
|
8545
9312
|
const { client, readClient, schemaName, skipDefaultIndexes } = resolvePgConfig(config);
|
|
8546
9313
|
this.#client = client;
|
|
8547
9314
|
this.#schemaName = schemaName;
|
|
@@ -8554,55 +9321,358 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
8554
9321
|
skipDefaultIndexes
|
|
8555
9322
|
});
|
|
8556
9323
|
}
|
|
8557
|
-
|
|
8558
|
-
|
|
8559
|
-
|
|
8560
|
-
|
|
8561
|
-
|
|
8562
|
-
|
|
8563
|
-
|
|
8564
|
-
|
|
8565
|
-
|
|
9324
|
+
getCapabilities() {
|
|
9325
|
+
return {
|
|
9326
|
+
contractVersion: 2,
|
|
9327
|
+
schemaVersion: 2,
|
|
9328
|
+
supportsV2: true,
|
|
9329
|
+
supportsSchemaInspection: true,
|
|
9330
|
+
supportsExplicitReset: true
|
|
9331
|
+
};
|
|
9332
|
+
}
|
|
9333
|
+
async inspectSchema() {
|
|
9334
|
+
const { inspectKnowledgeSchema } = await loadKnowledgeV2Core();
|
|
9335
|
+
try {
|
|
9336
|
+
const snapshot = getSchemaSnapshot(this.#client, this.#schemaName);
|
|
9337
|
+
if (snapshot) {
|
|
9338
|
+
const tableNames = KNOWLEDGE_TABLE_NAMES.filter((table) => snapshot.tables.has(table));
|
|
9339
|
+
return this.#classifySchema(this.#executor, tableNames, snapshot.columns);
|
|
9340
|
+
}
|
|
9341
|
+
return this.#inspectSchemaWithExecutor(this.#executor);
|
|
9342
|
+
} catch (error) {
|
|
9343
|
+
return inspectKnowledgeSchema({
|
|
9344
|
+
available: false,
|
|
9345
|
+
tableNames: [],
|
|
9346
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
9347
|
+
});
|
|
9348
|
+
}
|
|
9349
|
+
}
|
|
9350
|
+
async #inspectSchemaWithExecutor(executor) {
|
|
9351
|
+
const tableNames = (await executor.execute({
|
|
9352
|
+
sql: `SELECT table_name FROM information_schema.tables WHERE table_schema = COALESCE(?, current_schema()) AND table_name = ANY(?::text[])`,
|
|
9353
|
+
args: [this.#schemaName ?? null, [...KNOWLEDGE_TABLE_NAMES]]
|
|
9354
|
+
})).rows.map((row) => String(row.table_name));
|
|
9355
|
+
const columnResult = await executor.execute({
|
|
9356
|
+
sql: `SELECT table_name, column_name FROM information_schema.columns WHERE table_schema = COALESCE(?, current_schema()) AND table_name = ANY(?::text[])`,
|
|
9357
|
+
args: [this.#schemaName ?? null, [...KNOWLEDGE_TABLE_NAMES]]
|
|
9358
|
+
});
|
|
9359
|
+
const columns = /* @__PURE__ */ new Map();
|
|
9360
|
+
for (const row of columnResult.rows) {
|
|
9361
|
+
const table = String(row.table_name);
|
|
9362
|
+
const names = columns.get(table) ?? /* @__PURE__ */ new Set();
|
|
9363
|
+
names.add(String(row.column_name));
|
|
9364
|
+
columns.set(table, names);
|
|
9365
|
+
}
|
|
9366
|
+
return this.#classifySchema(executor, tableNames, columns);
|
|
9367
|
+
}
|
|
9368
|
+
async #classifySchema(executor, tableNames, columns) {
|
|
9369
|
+
const { inspectKnowledgeSchema } = await loadKnowledgeV2Core();
|
|
9370
|
+
if (tableNames.length === 0) return inspectKnowledgeSchema({
|
|
9371
|
+
available: true,
|
|
9372
|
+
tableNames
|
|
9373
|
+
});
|
|
9374
|
+
const missingTables = KNOWLEDGE_TABLE_NAMES.filter((table) => !tableNames.includes(table));
|
|
9375
|
+
if (missingTables.length > 0) return inspectKnowledgeSchema({
|
|
9376
|
+
available: true,
|
|
9377
|
+
tableNames,
|
|
9378
|
+
reason: `Missing Knowledge v2 tables: ${missingTables.join(", ")}`
|
|
9379
|
+
});
|
|
9380
|
+
for (const { tableName: table, schema } of knowledgeTableDefinitions) {
|
|
9381
|
+
const actual = columns.get(table) ?? /* @__PURE__ */ new Set();
|
|
9382
|
+
const missing = Object.keys(schema).filter((column) => !actual.has(column));
|
|
9383
|
+
if (missing.length > 0) return inspectKnowledgeSchema({
|
|
9384
|
+
available: true,
|
|
9385
|
+
tableNames,
|
|
9386
|
+
reason: `Knowledge table ${table} is missing v2 columns: ${missing.join(", ")}`
|
|
9387
|
+
});
|
|
9388
|
+
}
|
|
9389
|
+
const accessState = await executor.execute(`SELECT schemaVersion FROM "${TABLE_KNOWLEDGE_ACCESS_STATE}" WHERE id='global'`);
|
|
9390
|
+
if (Number(accessState.rows[0]?.schemaVersion) !== 2) return inspectKnowledgeSchema({
|
|
9391
|
+
available: true,
|
|
9392
|
+
tableNames,
|
|
9393
|
+
reason: "Knowledge schema version marker is missing or incompatible"
|
|
9394
|
+
});
|
|
9395
|
+
return inspectKnowledgeSchema({
|
|
9396
|
+
available: true,
|
|
9397
|
+
tableNames,
|
|
9398
|
+
schemaVersion: 2
|
|
8566
9399
|
});
|
|
8567
|
-
|
|
8568
|
-
|
|
8569
|
-
|
|
9400
|
+
}
|
|
9401
|
+
async init() {
|
|
9402
|
+
await this.#transaction(async (tx) => {
|
|
9403
|
+
await tx.execute({
|
|
9404
|
+
sql: "SELECT pg_advisory_xact_lock(hashtext(?))",
|
|
9405
|
+
args: [`mastra-knowledge-v2:${this.#schemaName ?? "current_schema"}`]
|
|
9406
|
+
});
|
|
9407
|
+
const { assertKnowledgeSchemaCompatible } = await loadKnowledgeV2Core();
|
|
9408
|
+
let inspection = await this.#inspectSchemaWithExecutor(tx);
|
|
9409
|
+
if (inspection.status === "incompatible-reset-required" && await this.#isPublishedV1Layout(tx)) {
|
|
9410
|
+
const tables = [...RETIRED_KNOWLEDGE_TABLE_NAMES, ...[...KNOWLEDGE_TABLE_NAMES].reverse()].map((table) => `"${table}"`).join(", ");
|
|
9411
|
+
await tx.execute(`DROP TABLE IF EXISTS ${tables}`);
|
|
9412
|
+
inspection = await this.#inspectSchemaWithExecutor(tx);
|
|
9413
|
+
}
|
|
9414
|
+
assertKnowledgeSchemaCompatible(inspection);
|
|
9415
|
+
for (const definition of knowledgeTableDefinitions) await tx.executeRaw(generateTableSQL({
|
|
9416
|
+
...definition,
|
|
9417
|
+
schemaName: this.#schemaName,
|
|
9418
|
+
includeAllConstraints: true
|
|
9419
|
+
}));
|
|
9420
|
+
for (const index of knowledgeIndexes(this.#schemaName)) await tx.executeRaw(index.sql);
|
|
9421
|
+
await tx.execute({
|
|
9422
|
+
sql: `INSERT INTO "${TABLE_KNOWLEDGE_ACCESS_STATE}" (id, epoch, schemaVersion) VALUES ('global', 0, ?) ON CONFLICT (id) DO NOTHING`,
|
|
9423
|
+
args: [2]
|
|
9424
|
+
});
|
|
8570
9425
|
});
|
|
8571
|
-
|
|
8572
|
-
|
|
8573
|
-
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
9426
|
+
}
|
|
9427
|
+
/**
|
|
9428
|
+
* True when the only Knowledge objects are the tables and indexes published v1 adapters created,
|
|
9429
|
+
* with or without rows. Anything else (v2 tables, unknown tables, views, triggers, extra indexes)
|
|
9430
|
+
* needs an explicit reset.
|
|
9431
|
+
*/
|
|
9432
|
+
async #isPublishedV1Layout(executor) {
|
|
9433
|
+
const schema = this.#schemaName ?? null;
|
|
9434
|
+
const relations = await executor.execute({
|
|
9435
|
+
sql: `SELECT table_name, table_type FROM information_schema.tables WHERE table_schema = COALESCE(?, current_schema()) AND table_name LIKE 'mastra\\_knowledge\\_%'`,
|
|
9436
|
+
args: [schema]
|
|
9437
|
+
});
|
|
9438
|
+
const tables = [];
|
|
9439
|
+
for (const row of relations.rows) {
|
|
9440
|
+
const name = String(row.table_name);
|
|
9441
|
+
if (row.table_type !== "BASE TABLE" || !PUBLISHED_KNOWLEDGE_V1_TABLE_NAMES.has(name)) return false;
|
|
9442
|
+
tables.push(name);
|
|
9443
|
+
}
|
|
9444
|
+
const indexes = await executor.execute({
|
|
9445
|
+
sql: `SELECT indexname FROM pg_indexes WHERE schemaname = COALESCE(?, current_schema()) AND tablename = ANY(?::text[])`,
|
|
9446
|
+
args: [schema, tables]
|
|
9447
|
+
});
|
|
9448
|
+
for (const row of indexes.rows) {
|
|
9449
|
+
const name = String(row.indexname);
|
|
9450
|
+
if (!PUBLISHED_KNOWLEDGE_V1_INDEX_NAMES.has(name) && !name.endsWith("_pkey")) return false;
|
|
9451
|
+
}
|
|
9452
|
+
if ((await executor.execute({
|
|
9453
|
+
sql: `SELECT 1 FROM information_schema.view_table_usage WHERE table_schema = COALESCE(?, current_schema()) AND table_name = ANY(?::text[]) UNION ALL SELECT 1 FROM information_schema.triggers WHERE event_object_schema = COALESCE(?, current_schema()) AND event_object_table = ANY(?::text[]) LIMIT 1`,
|
|
9454
|
+
args: [
|
|
9455
|
+
schema,
|
|
9456
|
+
tables,
|
|
9457
|
+
schema,
|
|
9458
|
+
tables
|
|
8578
9459
|
]
|
|
9460
|
+
})).rows.length > 0) return false;
|
|
9461
|
+
return true;
|
|
9462
|
+
}
|
|
9463
|
+
async dangerouslyClearAll() {
|
|
9464
|
+
await this.#transaction(async (tx) => {
|
|
9465
|
+
for (const table of [
|
|
9466
|
+
TABLE_KNOWLEDGE_RECORD_SCOPES,
|
|
9467
|
+
TABLE_KNOWLEDGE_NODE_SCOPES,
|
|
9468
|
+
TABLE_KNOWLEDGE_SCOPE_GRANTS,
|
|
9469
|
+
TABLE_KNOWLEDGE_SCOPE_ADDRESSES,
|
|
9470
|
+
TABLE_KNOWLEDGE_NODE_ADDRESSES,
|
|
9471
|
+
TABLE_KNOWLEDGE_MENTIONS,
|
|
9472
|
+
TABLE_KNOWLEDGE_PROPOSALS,
|
|
9473
|
+
TABLE_KNOWLEDGE_ACTIVITY,
|
|
9474
|
+
TABLE_KNOWLEDGE_IMPORT_STATE,
|
|
9475
|
+
TABLE_KNOWLEDGE_IMPORT_RUNS,
|
|
9476
|
+
TABLE_KNOWLEDGE_SEMANTIC_OUTBOX,
|
|
9477
|
+
TABLE_KNOWLEDGE_RECORDS,
|
|
9478
|
+
TABLE_KNOWLEDGE_NODES
|
|
9479
|
+
]) await tx.execute(`DELETE FROM "${table}"`);
|
|
9480
|
+
await tx.execute(`UPDATE "${TABLE_KNOWLEDGE_ACCESS_STATE}" SET epoch = 0 WHERE id = 'global'`);
|
|
8579
9481
|
});
|
|
8580
|
-
|
|
8581
|
-
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
|
|
8591
|
-
|
|
9482
|
+
}
|
|
9483
|
+
async dangerouslyReset() {
|
|
9484
|
+
const tables = [...RETIRED_KNOWLEDGE_TABLE_NAMES, ...[...KNOWLEDGE_TABLE_NAMES].reverse()].map((table) => `"${table}"`).join(", ");
|
|
9485
|
+
await this.#executor.execute(`DROP TABLE IF EXISTS ${tables}`);
|
|
9486
|
+
await this.init();
|
|
9487
|
+
}
|
|
9488
|
+
async reconcileStructure(plan) {
|
|
9489
|
+
return this.#transaction(async (tx) => {
|
|
9490
|
+
await tx.execute({
|
|
9491
|
+
sql: `SELECT pg_advisory_xact_lock(hashtext(?))`,
|
|
9492
|
+
args: [`mastra-knowledge-reconcile:${this.#schemaName}`]
|
|
9493
|
+
});
|
|
9494
|
+
const scopes = {};
|
|
9495
|
+
const createdScopeIds = [];
|
|
9496
|
+
const createdAddresses = /* @__PURE__ */ new Set();
|
|
9497
|
+
const retrofit = plan.retrofit ?? true;
|
|
9498
|
+
let structureChanged = false;
|
|
9499
|
+
const deletedScopeAddresses = /* @__PURE__ */ new Set();
|
|
9500
|
+
const resolveAddress = async (address) => {
|
|
9501
|
+
if (scopes[address]) return scopes[address];
|
|
9502
|
+
const row = (await tx.execute({
|
|
9503
|
+
sql: `SELECT a."scopeNodeId",n."isScope",n."deletedAt" FROM "${TABLE_KNOWLEDGE_SCOPE_ADDRESSES}" a JOIN "${TABLE_KNOWLEDGE_NODES}" n ON n.id=a."scopeNodeId" WHERE a.address=?`,
|
|
9504
|
+
args: [address]
|
|
9505
|
+
})).rows[0];
|
|
9506
|
+
if (!row) return void 0;
|
|
9507
|
+
if (!row.isScope) throw new Error(`Knowledge address ${address} does not reference a scope`);
|
|
9508
|
+
if (row.deletedAt) deletedScopeAddresses.add(address);
|
|
9509
|
+
scopes[address] = String(row.scopeNodeId);
|
|
9510
|
+
return scopes[address];
|
|
9511
|
+
};
|
|
9512
|
+
for (const scope of plan.scopes) {
|
|
9513
|
+
if (await resolveAddress(scope.address)) continue;
|
|
9514
|
+
const id = randomUUID();
|
|
9515
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9516
|
+
await tx.execute({
|
|
9517
|
+
sql: `INSERT INTO "${TABLE_KNOWLEDGE_NODES}" (id,type,name,"canonicalName",kind,description,"isScope",metadata,version,"createdAt","updatedAt") VALUES (?,'node',?,?,?,?,TRUE,?,1,?,?)`,
|
|
9518
|
+
args: [
|
|
9519
|
+
id,
|
|
9520
|
+
scope.name,
|
|
9521
|
+
canonicalName(scope.name),
|
|
9522
|
+
scope.kind ?? null,
|
|
9523
|
+
scope.description ?? null,
|
|
9524
|
+
scope.metadata ?? null,
|
|
9525
|
+
now,
|
|
9526
|
+
now
|
|
9527
|
+
]
|
|
9528
|
+
});
|
|
9529
|
+
await tx.execute({
|
|
9530
|
+
sql: `INSERT INTO "${TABLE_KNOWLEDGE_SCOPE_ADDRESSES}" (address,"scopeNodeId") VALUES (?,?)`,
|
|
9531
|
+
args: [scope.address, id]
|
|
9532
|
+
});
|
|
9533
|
+
scopes[scope.address] = id;
|
|
9534
|
+
createdAddresses.add(scope.address);
|
|
9535
|
+
createdScopeIds.push(id);
|
|
9536
|
+
structureChanged = true;
|
|
9537
|
+
}
|
|
9538
|
+
for (const scope of plan.scopes) {
|
|
9539
|
+
if (!createdAddresses.has(scope.address) && (!retrofit || deletedScopeAddresses.has(scope.address))) continue;
|
|
9540
|
+
const scopeNodeId = scopes[scope.address];
|
|
9541
|
+
for (const parentAddress of scope.parentAddresses ?? []) {
|
|
9542
|
+
const parentId = await resolveAddress(parentAddress);
|
|
9543
|
+
if (!parentId || deletedScopeAddresses.has(parentAddress)) {
|
|
9544
|
+
const deletedParentId = scopes[parentAddress];
|
|
9545
|
+
if ((deletedParentId ? await tx.execute({
|
|
9546
|
+
sql: `SELECT 1 FROM "mastra_knowledge_node_scopes" WHERE "nodeId"=? AND "scopeNodeId"=?`,
|
|
9547
|
+
args: [scopeNodeId, deletedParentId]
|
|
9548
|
+
}) : void 0)?.rows.length) continue;
|
|
9549
|
+
throw new Error(`Knowledge parent scope does not exist: ${parentAddress}`);
|
|
9550
|
+
}
|
|
9551
|
+
if ((await tx.execute({
|
|
9552
|
+
sql: `SELECT 1 FROM "mastra_knowledge_node_scopes" WHERE "nodeId"=? AND "scopeNodeId"=?`,
|
|
9553
|
+
args: [scopeNodeId, parentId]
|
|
9554
|
+
})).rows.length) continue;
|
|
9555
|
+
if ((await tx.execute({
|
|
9556
|
+
sql: `SELECT n.id FROM "mastra_knowledge_node_scopes" ns JOIN "${TABLE_KNOWLEDGE_NODES}" n ON n.id=ns."nodeId" WHERE ns."scopeNodeId"=? AND n."isScope" AND n."canonicalName"=? AND n."deletedAt" IS NULL AND n.id<>? LIMIT 1`,
|
|
9557
|
+
args: [
|
|
9558
|
+
parentId,
|
|
9559
|
+
canonicalName(scope.name),
|
|
9560
|
+
scopeNodeId
|
|
9561
|
+
]
|
|
9562
|
+
})).rows.length) throw new Error(`Knowledge scope name ${scope.name} already exists under ${parentAddress}`);
|
|
9563
|
+
const inserted = await tx.execute({
|
|
9564
|
+
sql: `INSERT INTO "${TABLE_KNOWLEDGE_NODE_SCOPES}" ("nodeId","scopeNodeId","addedAt") VALUES (?,?,?) ON CONFLICT DO NOTHING`,
|
|
9565
|
+
args: [
|
|
9566
|
+
scopeNodeId,
|
|
9567
|
+
parentId,
|
|
9568
|
+
(/* @__PURE__ */ new Date()).toISOString()
|
|
9569
|
+
]
|
|
9570
|
+
});
|
|
9571
|
+
structureChanged ||= inserted.rowsAffected > 0;
|
|
9572
|
+
}
|
|
9573
|
+
for (const grant of scope.grants ?? []) {
|
|
9574
|
+
const scopeRefId = await resolveAddress(grant.scopeRefAddress);
|
|
9575
|
+
if (!scopeRefId || deletedScopeAddresses.has(grant.scopeRefAddress)) {
|
|
9576
|
+
const deletedScopeRefId = scopes[grant.scopeRefAddress];
|
|
9577
|
+
if ((deletedScopeRefId ? await tx.execute({
|
|
9578
|
+
sql: `SELECT 1 FROM "mastra_knowledge_scope_grants" WHERE "scopeNodeId"=? AND "scopeRefId"=?`,
|
|
9579
|
+
args: [scopeNodeId, deletedScopeRefId]
|
|
9580
|
+
}) : void 0)?.rows.length) continue;
|
|
9581
|
+
throw new Error(`Knowledge grant scope does not exist: ${grant.scopeRefAddress}`);
|
|
9582
|
+
}
|
|
9583
|
+
const inserted = await tx.execute({
|
|
9584
|
+
sql: `INSERT INTO "${TABLE_KNOWLEDGE_SCOPE_GRANTS}" ("scopeNodeId","scopeRefId",role,"canSuggest") VALUES (?,?,?,?) ON CONFLICT DO NOTHING`,
|
|
9585
|
+
args: [
|
|
9586
|
+
scopeNodeId,
|
|
9587
|
+
scopeRefId,
|
|
9588
|
+
grant.role,
|
|
9589
|
+
grant.canSuggest ?? null
|
|
9590
|
+
]
|
|
9591
|
+
});
|
|
9592
|
+
structureChanged ||= inserted.rowsAffected > 0;
|
|
9593
|
+
}
|
|
9594
|
+
}
|
|
9595
|
+
if (structureChanged) await tx.execute(`UPDATE "${TABLE_KNOWLEDGE_ACCESS_STATE}" SET epoch=epoch+1 WHERE id='global'`);
|
|
9596
|
+
const state = await tx.execute(`SELECT epoch FROM "${TABLE_KNOWLEDGE_ACCESS_STATE}" WHERE id='global'`);
|
|
9597
|
+
return {
|
|
9598
|
+
scopes,
|
|
9599
|
+
createdScopeIds,
|
|
9600
|
+
deletedScopeAddresses: [...deletedScopeAddresses],
|
|
9601
|
+
changed: structureChanged,
|
|
9602
|
+
accessEpoch: Number(state.rows[0]?.epoch ?? 0)
|
|
9603
|
+
};
|
|
8592
9604
|
});
|
|
8593
|
-
await Promise.all(knowledgeIndexes(this.#schemaName).map((index) => this.#db.createIndexFromStatement(index.name, index.sql)));
|
|
8594
9605
|
}
|
|
8595
|
-
async
|
|
8596
|
-
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
|
|
8601
|
-
|
|
8602
|
-
|
|
8603
|
-
|
|
8604
|
-
|
|
8605
|
-
|
|
9606
|
+
async listScopeNodes(input = {}) {
|
|
9607
|
+
const { limit, after } = parseListKnowledgeScopeNodesInput(input);
|
|
9608
|
+
if (input.addresses?.length === 0 || input.ids?.length === 0) return {
|
|
9609
|
+
scopes: [],
|
|
9610
|
+
nextCursor: null
|
|
9611
|
+
};
|
|
9612
|
+
const args = [];
|
|
9613
|
+
const where = [`n."isScope"`, `n."deletedAt" IS NULL`];
|
|
9614
|
+
let within = "";
|
|
9615
|
+
if (input.withinAddress !== void 0) {
|
|
9616
|
+
within = `WITH RECURSIVE within_scope(id) AS (SELECT r.id FROM "${TABLE_KNOWLEDGE_NODES}" r JOIN "${TABLE_KNOWLEDGE_SCOPE_ADDRESSES}" ra ON ra."scopeNodeId"=r.id WHERE ra.address=? AND r."isScope" AND r."deletedAt" IS NULL UNION SELECT ns."nodeId" FROM "${TABLE_KNOWLEDGE_NODE_SCOPES}" ns JOIN within_scope w ON ns."scopeNodeId"=w.id JOIN "${TABLE_KNOWLEDGE_NODES}" c ON c.id=ns."nodeId" WHERE c."isScope" AND c."deletedAt" IS NULL) `;
|
|
9617
|
+
args.push(input.withinAddress);
|
|
9618
|
+
where.push("n.id IN (SELECT id FROM within_scope)");
|
|
9619
|
+
}
|
|
9620
|
+
if (input.addresses) {
|
|
9621
|
+
where.push(`a.address IN (${input.addresses.map(() => "?").join(",")})`);
|
|
9622
|
+
args.push(...input.addresses);
|
|
9623
|
+
}
|
|
9624
|
+
if (input.ids) {
|
|
9625
|
+
where.push(`n.id IN (${input.ids.map(() => "?").join(",")})`);
|
|
9626
|
+
args.push(...input.ids);
|
|
9627
|
+
}
|
|
9628
|
+
if (after) {
|
|
9629
|
+
where.push("(n.name COLLATE \"C\" > ? OR (n.name = ? AND n.id > ?))");
|
|
9630
|
+
args.push(after.name, after.name, after.id);
|
|
9631
|
+
}
|
|
9632
|
+
args.push(limit + 1);
|
|
9633
|
+
const rows = (await this.#readExecutor.execute({
|
|
9634
|
+
sql: `${within}SELECT n.id,n.name,n.kind,n.description,a.address FROM "${TABLE_KNOWLEDGE_NODES}" n LEFT JOIN "${TABLE_KNOWLEDGE_SCOPE_ADDRESSES}" a ON a."scopeNodeId"=n.id WHERE ${where.join(" AND ")} ORDER BY n.name COLLATE "C", n.id LIMIT ?`,
|
|
9635
|
+
args
|
|
9636
|
+
})).rows;
|
|
9637
|
+
if (rows.length === 0) return {
|
|
9638
|
+
scopes: [],
|
|
9639
|
+
nextCursor: null
|
|
9640
|
+
};
|
|
9641
|
+
const parents = await this.#readExecutor.execute({
|
|
9642
|
+
sql: `SELECT ns."nodeId",ns."scopeNodeId" FROM "${TABLE_KNOWLEDGE_NODE_SCOPES}" ns WHERE ns."nodeId" IN (${rows.map(() => "?").join(",")}) ORDER BY ns."scopeNodeId" COLLATE "C"`,
|
|
9643
|
+
args: rows.map((row) => String(row.id))
|
|
9644
|
+
});
|
|
9645
|
+
const parentsByScopeId = /* @__PURE__ */ new Map();
|
|
9646
|
+
for (const row of parents.rows) {
|
|
9647
|
+
const scopeId = String(row.nodeId);
|
|
9648
|
+
const parentId = String(row.scopeNodeId);
|
|
9649
|
+
const existing = parentsByScopeId.get(scopeId);
|
|
9650
|
+
if (existing) existing.push(parentId);
|
|
9651
|
+
else parentsByScopeId.set(scopeId, [parentId]);
|
|
9652
|
+
}
|
|
9653
|
+
return pageKnowledgeScopeNodes(rows.map((row) => ({
|
|
9654
|
+
id: String(row.id),
|
|
9655
|
+
address: String(row.address),
|
|
9656
|
+
name: String(row.name),
|
|
9657
|
+
...row.kind == null ? {} : { kind: String(row.kind) },
|
|
9658
|
+
...row.description == null ? {} : { description: String(row.description) },
|
|
9659
|
+
parentIds: parentsByScopeId.get(String(row.id)) ?? []
|
|
9660
|
+
})), limit, input);
|
|
9661
|
+
}
|
|
9662
|
+
async listScopeMembers(input) {
|
|
9663
|
+
const { limit, after } = parseListKnowledgeScopeMembersInput(input);
|
|
9664
|
+
const args = [input.scopeNodeId];
|
|
9665
|
+
let keyset = "";
|
|
9666
|
+
if (after) {
|
|
9667
|
+
const updatedAt = postgresTimestamp(after.updatedAt);
|
|
9668
|
+
keyset = ` AND (n."updatedAt" < ? OR (n."updatedAt" = ? AND (n.name > ? OR (n.name = ? AND n.id > ?))))`;
|
|
9669
|
+
args.push(updatedAt, updatedAt, after.name, after.name, after.id);
|
|
9670
|
+
}
|
|
9671
|
+
args.push(limit + 1);
|
|
9672
|
+
return pageKnowledgeScopeMembers((await this.#readExecutor.execute({
|
|
9673
|
+
sql: `SELECT *, scope AS "scopeJson" FROM "${TABLE_KNOWLEDGE_NODES}" n WHERE EXISTS (SELECT 1 FROM "${TABLE_KNOWLEDGE_NODE_SCOPES}" ns WHERE ns."nodeId"=n.id AND ns."scopeNodeId"=?) AND n."deletedAt" IS NULL AND n."mergedInto" IS NULL${keyset} ORDER BY n."updatedAt" DESC, n.name ASC, n.id ASC LIMIT ?`,
|
|
9674
|
+
args
|
|
9675
|
+
})).rows.map(parseNode), limit, input);
|
|
8606
9676
|
}
|
|
8607
9677
|
async createNode(input) {
|
|
8608
9678
|
await assertKnowledgeDescriptionWithinBoundCompat(input.description);
|
|
@@ -8612,6 +9682,7 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
8612
9682
|
if (existing) {
|
|
8613
9683
|
const terminal = await this.#resolveTerminalNode(tx, existing.id);
|
|
8614
9684
|
if (!isKnowledgeScopeVisible(terminal.scope, scope)) throw new Error(`Merged knowledge node is not visible from scope: ${input.name}`);
|
|
9685
|
+
await this.#placeNodeInScopes(tx, terminal.id, input.scopeAddresses, /* @__PURE__ */ new Date());
|
|
8615
9686
|
return terminal;
|
|
8616
9687
|
}
|
|
8617
9688
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -8628,7 +9699,7 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
8628
9699
|
updatedAt: now
|
|
8629
9700
|
};
|
|
8630
9701
|
await tx.execute({
|
|
8631
|
-
sql: `INSERT INTO "${TABLE_KNOWLEDGE_NODES}" (id,type,name,canonicalName,kind,content,description,scope,scopeKey,version,mergedInto,createdAt,updatedAt) VALUES (?,?,?,?,?,?,?,jsonb(?),?,?,NULL,?,?)`,
|
|
9702
|
+
sql: `INSERT INTO "${TABLE_KNOWLEDGE_NODES}" (id,type,name,canonicalName,kind,content,description,isScope,scope,scopeKey,version,mergedInto,createdAt,updatedAt) VALUES (?,?,?,?,?,?,?,FALSE,jsonb(?),?,?,NULL,?,?)`,
|
|
8632
9703
|
args: [
|
|
8633
9704
|
node.id,
|
|
8634
9705
|
"node",
|
|
@@ -8644,6 +9715,8 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
8644
9715
|
now.toISOString()
|
|
8645
9716
|
]
|
|
8646
9717
|
});
|
|
9718
|
+
await this.#replaceNodeScopes(tx, node.id, scope, now);
|
|
9719
|
+
await this.#placeNodeInScopes(tx, node.id, input.scopeAddresses, now);
|
|
8647
9720
|
await this.#replaceMentions(tx, "node", node.id, node.content ?? "", input.resolutionScope ?? scope, scope);
|
|
8648
9721
|
await this.#activity(tx, "node-created", "node", node.id, scope);
|
|
8649
9722
|
await this.#outbox(tx, "node", node.id, "upsert", node.version, scope);
|
|
@@ -8660,13 +9733,13 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
8660
9733
|
return this.#resolveNode(this.#readExecutor, input.name, canonicalizeKnowledgeScope(input.scope));
|
|
8661
9734
|
}
|
|
8662
9735
|
async listNodes(input) {
|
|
8663
|
-
const
|
|
9736
|
+
const keys = visibleScopeKeys(canonicalizeKnowledgeScope(input.scope));
|
|
8664
9737
|
const clauses = [
|
|
8665
9738
|
`type = 'node'`,
|
|
8666
9739
|
"mergedInto IS NULL",
|
|
8667
|
-
visibleSql
|
|
9740
|
+
visibleSql(keys)
|
|
8668
9741
|
];
|
|
8669
|
-
const args = [
|
|
9742
|
+
const args = [...keys];
|
|
8670
9743
|
if (input.namePrefix) {
|
|
8671
9744
|
clauses.push("canonicalName LIKE ? ESCAPE '='");
|
|
8672
9745
|
args.push(`${escapeLikePattern(canonicalName(input.namePrefix))}%`);
|
|
@@ -8718,6 +9791,7 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
8718
9791
|
input.version
|
|
8719
9792
|
]
|
|
8720
9793
|
})).rowsAffected === 0) throw new KnowledgeConflictError(input.id);
|
|
9794
|
+
if (knowledgeScopeKey(existing.scope) !== knowledgeScopeKey(scope)) await this.#swapIdentityScopes(tx, input.id, existing.scope, scope, now);
|
|
8721
9795
|
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);
|
|
8722
9796
|
await this.#activity(tx, "node-updated", "node", input.id, scope);
|
|
8723
9797
|
if (knowledgeScopeKey(existing.scope) !== knowledgeScopeKey(scope)) {
|
|
@@ -8772,8 +9846,12 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
8772
9846
|
]
|
|
8773
9847
|
})).rowsAffected === 0) throw new KnowledgeConflictError(source.id);
|
|
8774
9848
|
await tx.execute({
|
|
8775
|
-
sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET node=? WHERE node=?`,
|
|
8776
|
-
args: [
|
|
9849
|
+
sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET node=?,version=version+1,updatedAt=? WHERE node=?`,
|
|
9850
|
+
args: [
|
|
9851
|
+
target.id,
|
|
9852
|
+
(/* @__PURE__ */ new Date()).toISOString(),
|
|
9853
|
+
source.id
|
|
9854
|
+
]
|
|
8777
9855
|
});
|
|
8778
9856
|
await tx.execute({
|
|
8779
9857
|
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=?)`,
|
|
@@ -8813,10 +9891,10 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
8813
9891
|
});
|
|
8814
9892
|
}
|
|
8815
9893
|
async appendKnowledge(input) {
|
|
9894
|
+
await assertKnowledgeRecordTextWithinBoundCompat(input.text);
|
|
8816
9895
|
const scope = canonicalizeKnowledgeScope(input.scope);
|
|
8817
9896
|
const resolutionScope = canonicalizeKnowledgeScope(input.resolutionScope);
|
|
8818
9897
|
const defaultScope = canonicalizeKnowledgeScope(input.defaultScope);
|
|
8819
|
-
assertKnowledgeScopeWithinCeiling(scope, input.maxScope);
|
|
8820
9898
|
return this.#transaction(async (tx) => {
|
|
8821
9899
|
const parent = await this.#resolveTerminalNode(tx, nodeReferenceId(input.node));
|
|
8822
9900
|
if (!parent) throw new KnowledgeNotFoundError("node", nodeReferenceId(input.node));
|
|
@@ -8829,11 +9907,10 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
8829
9907
|
sourceThreadId: input.sourceThreadId,
|
|
8830
9908
|
capturedAt: /* @__PURE__ */ new Date(),
|
|
8831
9909
|
when: input.when ? new Date(input.when) : void 0,
|
|
8832
|
-
maxScope: input.maxScope,
|
|
8833
9910
|
metadata: input.metadata
|
|
8834
9911
|
};
|
|
8835
9912
|
await tx.execute({
|
|
8836
|
-
sql: `INSERT INTO "${TABLE_KNOWLEDGE_RECORDS}" (id,node,text,scope,scopeKey,sourceThreadId,capturedAt,"when",
|
|
9913
|
+
sql: `INSERT INTO "${TABLE_KNOWLEDGE_RECORDS}" (id,node,text,scope,scopeKey,sourceThreadId,capturedAt,"when",metadata,version,updatedAt,deletedAt,deletedBy) VALUES (?,?,?,jsonb(?),?,?,?,?,jsonb(?),?,?,NULL,NULL)`,
|
|
8837
9914
|
args: [
|
|
8838
9915
|
record.id,
|
|
8839
9916
|
record.node,
|
|
@@ -8843,10 +9920,12 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
8843
9920
|
record.sourceThreadId,
|
|
8844
9921
|
record.capturedAt.toISOString(),
|
|
8845
9922
|
record.when?.toISOString() ?? null,
|
|
8846
|
-
|
|
8847
|
-
|
|
9923
|
+
metadataJson,
|
|
9924
|
+
1,
|
|
9925
|
+
record.capturedAt.toISOString()
|
|
8848
9926
|
]
|
|
8849
9927
|
});
|
|
9928
|
+
await this.#replaceRecordScopes(tx, record.id, scope, record.capturedAt);
|
|
8850
9929
|
await this.#replaceMentions(tx, "record", record.id, record.text, resolutionScope, defaultScope);
|
|
8851
9930
|
await this.#activity(tx, "record-created", "record", record.id, scope, record.sourceThreadId);
|
|
8852
9931
|
await this.#outbox(tx, "record", record.id, "upsert", record.id, scope);
|
|
@@ -8873,17 +9952,13 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
8873
9952
|
return this.#queryKnowledge(input, "related");
|
|
8874
9953
|
}
|
|
8875
9954
|
async knowledgeBySource(input) {
|
|
8876
|
-
const
|
|
8877
|
-
const args = [
|
|
8878
|
-
input.sourceThreadId,
|
|
8879
|
-
key,
|
|
8880
|
-
key
|
|
8881
|
-
];
|
|
9955
|
+
const keys = visibleScopeKeys(canonicalizeKnowledgeScope(input.scope));
|
|
9956
|
+
const args = [input.sourceThreadId, ...keys];
|
|
8882
9957
|
if (input.after) args.push(input.after);
|
|
8883
9958
|
const limit = input.limit ?? 100;
|
|
8884
9959
|
args.push(limit + 1);
|
|
8885
9960
|
const records = (await this.#readExecutor.execute({
|
|
8886
|
-
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 ?`,
|
|
9961
|
+
sql: `SELECT *,scope AS "scopeJson" FROM "${TABLE_KNOWLEDGE_RECORDS}" WHERE sourceThreadId=? AND ${visibleSql(keys)}${input.includeDeleted ? "" : " AND deletedAt IS NULL"}${input.after ? " AND id > ?" : ""} ORDER BY id ASC LIMIT ?`,
|
|
8887
9962
|
args
|
|
8888
9963
|
})).rows.map(parseKnowledge);
|
|
8889
9964
|
return {
|
|
@@ -8898,10 +9973,11 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
8898
9973
|
if (record.deletedAt) return record;
|
|
8899
9974
|
const deletedAt = /* @__PURE__ */ new Date();
|
|
8900
9975
|
await tx.execute({
|
|
8901
|
-
sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET deletedAt=?,deletedBy=? WHERE id=? AND deletedAt IS NULL`,
|
|
9976
|
+
sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET deletedAt=?,deletedBy=?,version=version+1,updatedAt=? WHERE id=? AND deletedAt IS NULL`,
|
|
8902
9977
|
args: [
|
|
8903
9978
|
deletedAt.toISOString(),
|
|
8904
9979
|
input.deletedBy,
|
|
9980
|
+
deletedAt.toISOString(),
|
|
8905
9981
|
input.id
|
|
8906
9982
|
]
|
|
8907
9983
|
});
|
|
@@ -8920,8 +9996,8 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
8920
9996
|
if (!record) throw new KnowledgeNotFoundError("record", input.id);
|
|
8921
9997
|
if (!record.deletedAt) return record;
|
|
8922
9998
|
await tx.execute({
|
|
8923
|
-
sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET deletedAt=NULL,deletedBy=NULL WHERE id=?`,
|
|
8924
|
-
args: [input.id]
|
|
9999
|
+
sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET deletedAt=NULL,deletedBy=NULL,version=version+1,updatedAt=? WHERE id=?`,
|
|
10000
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), input.id]
|
|
8925
10001
|
});
|
|
8926
10002
|
await this.#activity(tx, "record-restored", "record", input.id, record.scope, record.sourceThreadId);
|
|
8927
10003
|
await this.#outbox(tx, "record", input.id, "upsert", createKnowledgeUlid(), record.scope);
|
|
@@ -8937,15 +10013,17 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
8937
10013
|
return this.#transaction(async (tx) => {
|
|
8938
10014
|
const record = await this.#getKnowledge(tx, input.id, true);
|
|
8939
10015
|
if (!record) throw new KnowledgeNotFoundError("record", input.id);
|
|
8940
|
-
|
|
10016
|
+
const updatedAt = /* @__PURE__ */ new Date();
|
|
8941
10017
|
await tx.execute({
|
|
8942
|
-
sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET scope=jsonb(?),scopeKey=? WHERE id=?`,
|
|
10018
|
+
sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET scope=jsonb(?),scopeKey=?,version=version+1,updatedAt=? WHERE id=?`,
|
|
8943
10019
|
args: [
|
|
8944
10020
|
JSON.stringify(scope),
|
|
8945
10021
|
knowledgeScopeKey(scope),
|
|
10022
|
+
updatedAt.toISOString(),
|
|
8946
10023
|
input.id
|
|
8947
10024
|
]
|
|
8948
10025
|
});
|
|
10026
|
+
await this.#replaceRecordScopes(tx, input.id, scope, updatedAt);
|
|
8949
10027
|
await this.#activity(tx, "record-rescoped", "record", input.id, scope, record.sourceThreadId);
|
|
8950
10028
|
if (knowledgeScopeKey(record.scope) !== knowledgeScopeKey(scope)) await this.#outbox(tx, "record", input.id, "delete", createKnowledgeUlid(), record.scope);
|
|
8951
10029
|
if (!record.deletedAt) await this.#outbox(tx, "record", input.id, "upsert", createKnowledgeUlid(), scope);
|
|
@@ -8955,33 +10033,16 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
8955
10033
|
};
|
|
8956
10034
|
});
|
|
8957
10035
|
}
|
|
8958
|
-
async raiseKnowledgeCeiling(input) {
|
|
8959
|
-
return this.#transaction(async (tx) => {
|
|
8960
|
-
const record = await this.#getKnowledge(tx, input.id, true);
|
|
8961
|
-
if (!record) throw new KnowledgeNotFoundError("record", input.id);
|
|
8962
|
-
assertKnowledgeScopeWithinCeiling(record.scope, input.maxScope);
|
|
8963
|
-
assertKnowledgeCeilingRaised(record.maxScope, input.maxScope);
|
|
8964
|
-
await tx.execute({
|
|
8965
|
-
sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET maxScope=? WHERE id=?`,
|
|
8966
|
-
args: [input.maxScope ?? null, input.id]
|
|
8967
|
-
});
|
|
8968
|
-
return {
|
|
8969
|
-
...record,
|
|
8970
|
-
maxScope: input.maxScope
|
|
8971
|
-
};
|
|
8972
|
-
});
|
|
8973
|
-
}
|
|
8974
10036
|
async search(input) {
|
|
8975
10037
|
const scope = canonicalizeKnowledgeScope(input.scope);
|
|
8976
|
-
const
|
|
10038
|
+
const keys = visibleScopeKeys(scope);
|
|
8977
10039
|
const normalizedQuery = input.query.trim().toLocaleLowerCase();
|
|
8978
10040
|
if (!normalizedQuery) return [];
|
|
8979
10041
|
const query = `%${escapeLikePattern(normalizedQuery)}%`;
|
|
8980
10042
|
const results = (await this.#readExecutor.execute({
|
|
8981
|
-
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 '=' OR lower(COALESCE(description,'')) LIKE ? ESCAPE '=') ORDER BY updatedAt DESC LIMIT ?`,
|
|
10043
|
+
sql: `SELECT *,scope AS "scopeJson" FROM "${TABLE_KNOWLEDGE_NODES}" WHERE mergedInto IS NULL AND ${visibleSql(keys)} AND (canonicalName LIKE ? ESCAPE '=' OR lower(COALESCE(kind,'')) LIKE ? ESCAPE '=' OR lower(COALESCE(content,'')) LIKE ? ESCAPE '=' OR lower(COALESCE(description,'')) LIKE ? ESCAPE '=') ORDER BY updatedAt DESC LIMIT ?`,
|
|
8982
10044
|
args: [
|
|
8983
|
-
|
|
8984
|
-
key,
|
|
10045
|
+
...keys,
|
|
8985
10046
|
query,
|
|
8986
10047
|
query,
|
|
8987
10048
|
query,
|
|
@@ -9002,10 +10063,9 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
9002
10063
|
}));
|
|
9003
10064
|
if (results.length < (input.limit ?? 20)) {
|
|
9004
10065
|
const records = await this.#readExecutor.execute({
|
|
9005
|
-
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
|
|
10066
|
+
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(keys, "f.scopeKey")} AND lower(f.text) LIKE ? ESCAPE '=' ORDER BY f.id DESC LIMIT ?`,
|
|
9006
10067
|
args: [
|
|
9007
|
-
|
|
9008
|
-
key,
|
|
10068
|
+
...keys,
|
|
9009
10069
|
query,
|
|
9010
10070
|
(input.limit ?? 20) - results.length
|
|
9011
10071
|
]
|
|
@@ -9015,7 +10075,7 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
9015
10075
|
return {
|
|
9016
10076
|
type: "record",
|
|
9017
10077
|
id: String(row.id),
|
|
9018
|
-
recordId: String(row.node),
|
|
10078
|
+
recordId: parentVisible ? String(row.node) : String(row.id),
|
|
9019
10079
|
name: parentVisible ? String(row.name) : "(private node)",
|
|
9020
10080
|
text: String(row.text),
|
|
9021
10081
|
scope: parseJson$1(row.scopeJson)
|
|
@@ -9024,41 +10084,26 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
9024
10084
|
}
|
|
9025
10085
|
return results;
|
|
9026
10086
|
}
|
|
9027
|
-
|
|
9028
|
-
|
|
9029
|
-
|
|
9030
|
-
|
|
9031
|
-
|
|
9032
|
-
|
|
9033
|
-
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
|
|
9037
|
-
|
|
9038
|
-
|
|
9039
|
-
|
|
9040
|
-
const updatedAt = /* @__PURE__ */ new Date();
|
|
9041
|
-
if ((await this.#executor.execute({
|
|
9042
|
-
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`,
|
|
9043
|
-
args: [
|
|
9044
|
-
input.sourceThreadId,
|
|
9045
|
-
input.agent,
|
|
9046
|
-
input.lastKnowledgeId,
|
|
9047
|
-
updatedAt.toISOString()
|
|
9048
|
-
]
|
|
9049
|
-
})).rowsAffected === 0) throw new Error("Knowledge curation cursor cannot move backwards");
|
|
9050
|
-
return {
|
|
9051
|
-
...input,
|
|
9052
|
-
updatedAt
|
|
9053
|
-
};
|
|
10087
|
+
/**
|
|
10088
|
+
* @deprecated Curation cursors were removed. Observation-time curate is the only Knowledge writer and needs no
|
|
10089
|
+
* cursor. Always throws.
|
|
10090
|
+
*/
|
|
10091
|
+
async getCurationCursor(_input) {
|
|
10092
|
+
throw new Error(KNOWLEDGE_CURATION_CURSOR_REMOVED_MESSAGE);
|
|
10093
|
+
}
|
|
10094
|
+
/**
|
|
10095
|
+
* @deprecated Curation cursors were removed. Observation-time curate is the only Knowledge writer and needs no
|
|
10096
|
+
* cursor. Always throws.
|
|
10097
|
+
*/
|
|
10098
|
+
async advanceCurationCursor(_input) {
|
|
10099
|
+
throw new Error(KNOWLEDGE_CURATION_CURSOR_REMOVED_MESSAGE);
|
|
9054
10100
|
}
|
|
9055
10101
|
async listActivity(input) {
|
|
9056
|
-
const
|
|
10102
|
+
const keys = visibleScopeKeys(canonicalizeKnowledgeScope(input.scope));
|
|
9057
10103
|
return (await this.#readExecutor.execute({
|
|
9058
|
-
sql: `SELECT *,scope AS "scopeJson" FROM "${TABLE_KNOWLEDGE_ACTIVITY}" WHERE ${visibleSql}${input.after ? " AND id < ?" : ""} ORDER BY id DESC LIMIT ?`,
|
|
10104
|
+
sql: `SELECT *,scope AS "scopeJson" FROM "${TABLE_KNOWLEDGE_ACTIVITY}" WHERE ${visibleSql(keys)}${input.after ? " AND id < ?" : ""} ORDER BY id DESC LIMIT ?`,
|
|
9059
10105
|
args: [
|
|
9060
|
-
|
|
9061
|
-
key,
|
|
10106
|
+
...keys,
|
|
9062
10107
|
...input.after ? [input.after] : [],
|
|
9063
10108
|
input.limit ?? 100
|
|
9064
10109
|
]
|
|
@@ -9080,9 +10125,9 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
9080
10125
|
args.push(input.status);
|
|
9081
10126
|
}
|
|
9082
10127
|
if (input.scope) {
|
|
9083
|
-
const
|
|
9084
|
-
clauses.push(visibleSql);
|
|
9085
|
-
args.push(
|
|
10128
|
+
const keys = visibleScopeKeys(input.scope);
|
|
10129
|
+
clauses.push(visibleSql(keys));
|
|
10130
|
+
args.push(...keys);
|
|
9086
10131
|
}
|
|
9087
10132
|
args.push(input.limit ?? 100);
|
|
9088
10133
|
return (await this.#executor.execute({
|
|
@@ -9101,9 +10146,9 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
9101
10146
|
];
|
|
9102
10147
|
const args = [now.toISOString(), stale.toISOString()];
|
|
9103
10148
|
if (input.scope) {
|
|
9104
|
-
const
|
|
9105
|
-
clauses.push(visibleSql);
|
|
9106
|
-
args.push(
|
|
10149
|
+
const keys = visibleScopeKeys(input.scope);
|
|
10150
|
+
clauses.push(visibleSql(keys));
|
|
10151
|
+
args.push(...keys);
|
|
9107
10152
|
}
|
|
9108
10153
|
args.push(input.limit ?? 100);
|
|
9109
10154
|
const ids = (await tx.execute({
|
|
@@ -9170,12 +10215,14 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
9170
10215
|
return result.rows[0] ? parseNode(result.rows[0]) : null;
|
|
9171
10216
|
}
|
|
9172
10217
|
async #resolveNode(executor, name, scope) {
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
10218
|
+
const keys = visibleScopeKeys(scope);
|
|
10219
|
+
const candidates = (await executor.execute({
|
|
10220
|
+
sql: `SELECT *,scope AS "scopeJson" FROM "${TABLE_KNOWLEDGE_NODES}" WHERE type='node' AND canonicalName=? AND ("scopeKey" IN (${keys.map(() => "?").join(",")}) OR "mergedInto" IS NOT NULL)`,
|
|
10221
|
+
args: [canonicalName(name), ...keys]
|
|
10222
|
+
})).rows.map(parseNode).sort((left, right) => right.scope.length - left.scope.length);
|
|
10223
|
+
for (const candidate of candidates) {
|
|
10224
|
+
const terminal = await this.#resolveTerminalNode(executor, candidate.id);
|
|
10225
|
+
if (terminal && isKnowledgeScopeVisible(terminal.scope, scope)) return terminal;
|
|
9179
10226
|
}
|
|
9180
10227
|
return null;
|
|
9181
10228
|
}
|
|
@@ -9200,17 +10247,16 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
9200
10247
|
const scope = canonicalizeKnowledgeScope(input.scope);
|
|
9201
10248
|
const node = await this.#resolveTerminalNode(this.#readExecutor, nodeReferenceId(input.node));
|
|
9202
10249
|
if (!node) return { records: [] };
|
|
9203
|
-
const
|
|
10250
|
+
const keys = visibleScopeKeys(scope);
|
|
9204
10251
|
const args = [
|
|
9205
10252
|
node.id,
|
|
9206
10253
|
...relationship === "related" ? [node.id] : [],
|
|
9207
|
-
|
|
9208
|
-
key
|
|
10254
|
+
...keys
|
|
9209
10255
|
];
|
|
9210
10256
|
if (input.after) args.push(input.after);
|
|
9211
10257
|
args.push((input.limit ?? 100) + 1);
|
|
9212
10258
|
const records = (await this.#readExecutor.execute({
|
|
9213
|
-
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
|
|
10259
|
+
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(keys, "f.scopeKey")}${input.includeDeleted ? "" : " AND f.deletedAt IS NULL"}${input.after ? " AND f.id < ?" : ""} ORDER BY f.id DESC LIMIT ?`,
|
|
9214
10260
|
args
|
|
9215
10261
|
})).rows.map(parseKnowledge);
|
|
9216
10262
|
const limit = input.limit ?? 100;
|
|
@@ -9219,6 +10265,90 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
9219
10265
|
nextCursor: records.length > limit ? records[limit - 1]?.id : void 0
|
|
9220
10266
|
};
|
|
9221
10267
|
}
|
|
10268
|
+
async #resolveScopeNodeIds(executor, addresses) {
|
|
10269
|
+
const scopeNodeIds = /* @__PURE__ */ new Set();
|
|
10270
|
+
for (const address of addresses) {
|
|
10271
|
+
const result = await executor.execute({
|
|
10272
|
+
sql: `SELECT scopeNodeId FROM "${TABLE_KNOWLEDGE_SCOPE_ADDRESSES}" WHERE address=?`,
|
|
10273
|
+
args: [address]
|
|
10274
|
+
});
|
|
10275
|
+
if (result.rows[0]?.scopeNodeId != null) scopeNodeIds.add(String(result.rows[0].scopeNodeId));
|
|
10276
|
+
}
|
|
10277
|
+
return [...scopeNodeIds];
|
|
10278
|
+
}
|
|
10279
|
+
async #replaceNodeScopes(executor, nodeId, addresses, addedAt) {
|
|
10280
|
+
await executor.execute({
|
|
10281
|
+
sql: `DELETE FROM "${TABLE_KNOWLEDGE_NODE_SCOPES}" WHERE nodeId=?`,
|
|
10282
|
+
args: [nodeId]
|
|
10283
|
+
});
|
|
10284
|
+
for (const scopeNodeId of await this.#resolveScopeNodeIds(executor, addresses)) await executor.execute({
|
|
10285
|
+
sql: `INSERT INTO "${TABLE_KNOWLEDGE_NODE_SCOPES}" (nodeId,scopeNodeId,addedAt) VALUES (?,?,?) ON CONFLICT DO NOTHING`,
|
|
10286
|
+
args: [
|
|
10287
|
+
nodeId,
|
|
10288
|
+
scopeNodeId,
|
|
10289
|
+
addedAt.toISOString()
|
|
10290
|
+
]
|
|
10291
|
+
});
|
|
10292
|
+
}
|
|
10293
|
+
/**
|
|
10294
|
+
* Move a node's identity memberships from `from` to `to` without touching structural
|
|
10295
|
+
* placements added by `#placeNodeInScopes`, which a scope change must not erase.
|
|
10296
|
+
*/
|
|
10297
|
+
async #swapIdentityScopes(executor, nodeId, from, to, addedAt) {
|
|
10298
|
+
const next = await this.#resolveScopeNodeIds(executor, to);
|
|
10299
|
+
for (const scopeNodeId of await this.#resolveScopeNodeIds(executor, from)) {
|
|
10300
|
+
if (next.includes(scopeNodeId)) continue;
|
|
10301
|
+
await executor.execute({
|
|
10302
|
+
sql: `DELETE FROM "${TABLE_KNOWLEDGE_NODE_SCOPES}" WHERE nodeId=? AND scopeNodeId=?`,
|
|
10303
|
+
args: [nodeId, scopeNodeId]
|
|
10304
|
+
});
|
|
10305
|
+
}
|
|
10306
|
+
for (const scopeNodeId of next) await executor.execute({
|
|
10307
|
+
sql: `INSERT INTO "${TABLE_KNOWLEDGE_NODE_SCOPES}" (nodeId,scopeNodeId,addedAt) VALUES (?,?,?) ON CONFLICT DO NOTHING`,
|
|
10308
|
+
args: [
|
|
10309
|
+
nodeId,
|
|
10310
|
+
scopeNodeId,
|
|
10311
|
+
addedAt.toISOString()
|
|
10312
|
+
]
|
|
10313
|
+
});
|
|
10314
|
+
}
|
|
10315
|
+
/**
|
|
10316
|
+
* Additive structural placement: every address must resolve to a live scope node
|
|
10317
|
+
* (unlike identity membership, an unknown address is a caller error, not a lazy
|
|
10318
|
+
* materialization miss). Runs inside the caller's transaction, so a throw aborts
|
|
10319
|
+
* the whole mutation.
|
|
10320
|
+
*/
|
|
10321
|
+
async #placeNodeInScopes(executor, nodeId, addresses, addedAt) {
|
|
10322
|
+
for (const address of addresses ?? []) {
|
|
10323
|
+
const scopeNodeId = (await executor.execute({
|
|
10324
|
+
sql: `SELECT sa.scopeNodeId AS "scopeNodeId" FROM "${TABLE_KNOWLEDGE_SCOPE_ADDRESSES}" sa JOIN "${TABLE_KNOWLEDGE_NODES}" n ON n.id=sa."scopeNodeId" WHERE sa.address=? AND n."isScope" AND n."deletedAt" IS NULL`,
|
|
10325
|
+
args: [address]
|
|
10326
|
+
})).rows[0]?.scopeNodeId;
|
|
10327
|
+
if (scopeNodeId == null) throw new KnowledgeNotFoundError("scope", address);
|
|
10328
|
+
await executor.execute({
|
|
10329
|
+
sql: `INSERT INTO "${TABLE_KNOWLEDGE_NODE_SCOPES}" ("nodeId","scopeNodeId","addedAt") VALUES (?,?,?) ON CONFLICT DO NOTHING`,
|
|
10330
|
+
args: [
|
|
10331
|
+
nodeId,
|
|
10332
|
+
String(scopeNodeId),
|
|
10333
|
+
addedAt.toISOString()
|
|
10334
|
+
]
|
|
10335
|
+
});
|
|
10336
|
+
}
|
|
10337
|
+
}
|
|
10338
|
+
async #replaceRecordScopes(executor, recordId, addresses, addedAt) {
|
|
10339
|
+
await executor.execute({
|
|
10340
|
+
sql: `DELETE FROM "${TABLE_KNOWLEDGE_RECORD_SCOPES}" WHERE recordId=?`,
|
|
10341
|
+
args: [recordId]
|
|
10342
|
+
});
|
|
10343
|
+
for (const scopeNodeId of await this.#resolveScopeNodeIds(executor, addresses)) await executor.execute({
|
|
10344
|
+
sql: `INSERT INTO "${TABLE_KNOWLEDGE_RECORD_SCOPES}" (recordId,scopeNodeId,addedAt) VALUES (?,?,?) ON CONFLICT DO NOTHING`,
|
|
10345
|
+
args: [
|
|
10346
|
+
recordId,
|
|
10347
|
+
scopeNodeId,
|
|
10348
|
+
addedAt.toISOString()
|
|
10349
|
+
]
|
|
10350
|
+
});
|
|
10351
|
+
}
|
|
9222
10352
|
async #replaceMentions(tx, sourceType, sourceId, text, resolutionScope, defaultScope) {
|
|
9223
10353
|
await tx.execute({
|
|
9224
10354
|
sql: `DELETE FROM "${TABLE_KNOWLEDGE_MENTIONS}" WHERE sourceType=? AND sourceId=?`,
|
|
@@ -9242,7 +10372,7 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
9242
10372
|
updatedAt: now
|
|
9243
10373
|
};
|
|
9244
10374
|
await tx.execute({
|
|
9245
|
-
sql: `INSERT INTO "${TABLE_KNOWLEDGE_NODES}" (id,type,name,canonicalName,kind,content,scope,scopeKey,version,mergedInto,createdAt,updatedAt) VALUES (?,?,?,?,?,NULL,jsonb(?),?,?,NULL,?,?)`,
|
|
10375
|
+
sql: `INSERT INTO "${TABLE_KNOWLEDGE_NODES}" (id,type,name,canonicalName,kind,content,isScope,scope,scopeKey,version,mergedInto,createdAt,updatedAt) VALUES (?,?,?,?,?,NULL,FALSE,jsonb(?),?,?,NULL,?,?)`,
|
|
9246
10376
|
args: [
|
|
9247
10377
|
node.id,
|
|
9248
10378
|
"node",
|
|
@@ -9256,6 +10386,7 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
9256
10386
|
now.toISOString()
|
|
9257
10387
|
]
|
|
9258
10388
|
});
|
|
10389
|
+
await this.#replaceNodeScopes(tx, node.id, defaultScope, now);
|
|
9259
10390
|
await this.#activity(tx, "node-created", "node", node.id, defaultScope);
|
|
9260
10391
|
await this.#outbox(tx, "node", node.id, "upsert", 1, defaultScope);
|
|
9261
10392
|
}
|
|
@@ -9272,13 +10403,17 @@ var KnowledgePG = class extends KnowledgeStorage {
|
|
|
9272
10403
|
}
|
|
9273
10404
|
async #activity(executor, action, recordType, recordId, scope, sourceThreadId) {
|
|
9274
10405
|
const now = /* @__PURE__ */ new Date();
|
|
10406
|
+
const [contextScopeId] = await this.#resolveScopeNodeIds(executor, scope);
|
|
9275
10407
|
await executor.execute({
|
|
9276
|
-
sql: `INSERT INTO "${TABLE_KNOWLEDGE_ACTIVITY}" (id,action,recordType,recordId,scope,scopeKey,sourceThreadId,createdAt) VALUES (
|
|
10408
|
+
sql: `INSERT INTO "${TABLE_KNOWLEDGE_ACTIVITY}" (id,action,targetType,targetId,contextScopeId,recordType,recordId,scope,scopeKey,sourceThreadId,createdAt) VALUES (?,?,?,?,?,?,?,jsonb(?),?,?,?)`,
|
|
9277
10409
|
args: [
|
|
9278
10410
|
createKnowledgeUlid(),
|
|
9279
10411
|
action,
|
|
9280
10412
|
recordType,
|
|
9281
10413
|
recordId,
|
|
10414
|
+
contextScopeId ?? null,
|
|
10415
|
+
recordType,
|
|
10416
|
+
recordId,
|
|
9282
10417
|
JSON.stringify(scope),
|
|
9283
10418
|
knowledgeScopeKey(scope),
|
|
9284
10419
|
sourceThreadId ?? null,
|
|
@@ -9415,6 +10550,7 @@ var MCPClientsPG = class MCPClientsPG extends MCPClientsStorage {
|
|
|
9415
10550
|
}
|
|
9416
10551
|
async create(input) {
|
|
9417
10552
|
const { mcpClient } = input;
|
|
10553
|
+
let draftInserted = false;
|
|
9418
10554
|
try {
|
|
9419
10555
|
const tableName = getTableName$5({
|
|
9420
10556
|
indexName: TABLE_MCP_CLIENTS,
|
|
@@ -9437,6 +10573,7 @@ var MCPClientsPG = class MCPClientsPG extends MCPClientsStorage {
|
|
|
9437
10573
|
nowIso,
|
|
9438
10574
|
nowIso
|
|
9439
10575
|
]);
|
|
10576
|
+
draftInserted = true;
|
|
9440
10577
|
const { id: _id, authorId: _authorId, metadata: _metadata, ...snapshotConfig } = mcpClient;
|
|
9441
10578
|
const versionId = crypto.randomUUID();
|
|
9442
10579
|
await this.createVersion({
|
|
@@ -9457,7 +10594,7 @@ var MCPClientsPG = class MCPClientsPG extends MCPClientsStorage {
|
|
|
9457
10594
|
updatedAt: now
|
|
9458
10595
|
};
|
|
9459
10596
|
} catch (error) {
|
|
9460
|
-
try {
|
|
10597
|
+
if (draftInserted) try {
|
|
9461
10598
|
const tableName = getTableName$5({
|
|
9462
10599
|
indexName: TABLE_MCP_CLIENTS,
|
|
9463
10600
|
schemaName: getSchemaName$5(this.#schema)
|
|
@@ -9983,6 +11120,7 @@ var MCPServersPG = class MCPServersPG extends MCPServersStorage {
|
|
|
9983
11120
|
}
|
|
9984
11121
|
async create(input) {
|
|
9985
11122
|
const { mcpServer } = input;
|
|
11123
|
+
let draftInserted = false;
|
|
9986
11124
|
try {
|
|
9987
11125
|
const tableName = getTableName$5({
|
|
9988
11126
|
indexName: TABLE_MCP_SERVERS,
|
|
@@ -10004,6 +11142,7 @@ var MCPServersPG = class MCPServersPG extends MCPServersStorage {
|
|
|
10004
11142
|
nowIso,
|
|
10005
11143
|
nowIso
|
|
10006
11144
|
]);
|
|
11145
|
+
draftInserted = true;
|
|
10007
11146
|
const { id: _id, authorId: _authorId, metadata: _metadata, ...snapshotConfig } = mcpServer;
|
|
10008
11147
|
const versionId = crypto.randomUUID();
|
|
10009
11148
|
await this.createVersion({
|
|
@@ -10024,7 +11163,7 @@ var MCPServersPG = class MCPServersPG extends MCPServersStorage {
|
|
|
10024
11163
|
updatedAt: now
|
|
10025
11164
|
};
|
|
10026
11165
|
} catch (error) {
|
|
10027
|
-
try {
|
|
11166
|
+
if (draftInserted) try {
|
|
10028
11167
|
const tableName = getTableName$5({
|
|
10029
11168
|
indexName: TABLE_MCP_SERVERS,
|
|
10030
11169
|
schemaName: getSchemaName$5(this.#schema)
|
|
@@ -10448,6 +11587,38 @@ var MCPServersPG = class MCPServersPG extends MCPServersStorage {
|
|
|
10448
11587
|
}
|
|
10449
11588
|
};
|
|
10450
11589
|
//#endregion
|
|
11590
|
+
//#region src/storage/db/run-fencing.ts
|
|
11591
|
+
/**
|
|
11592
|
+
* Epoch milliseconds on the database clock. `clock_timestamp()` rather than
|
|
11593
|
+
* `now()`: a statement can wait on a row lock, and lease math must use the
|
|
11594
|
+
* time it actually runs, not when its transaction started.
|
|
11595
|
+
*/
|
|
11596
|
+
const DB_NOW_MS = `(EXTRACT(EPOCH FROM clock_timestamp()) * 1000)::bigint`;
|
|
11597
|
+
/**
|
|
11598
|
+
* The check half of a fenced write; call it first inside the write's
|
|
11599
|
+
* transaction. `FOR SHARE` holds the claim row until commit, so a claim that
|
|
11600
|
+
* updates the row waits for the write to finish, and every later write sees
|
|
11601
|
+
* the new claim.
|
|
11602
|
+
*/
|
|
11603
|
+
async function assertRunFence(t, claimsTable, fence, operation) {
|
|
11604
|
+
const row = await t.oneOrNone(`SELECT generation, "ownerId" FROM ${claimsTable} WHERE "runId" = $1 FOR SHARE`, [fence.runId]);
|
|
11605
|
+
if (!matchesRunFence(row && {
|
|
11606
|
+
generation: Number(row.generation),
|
|
11607
|
+
ownerId: row.ownerId
|
|
11608
|
+
}, fence)) throw new RunFenceConflictError(fence, operation);
|
|
11609
|
+
}
|
|
11610
|
+
/**
|
|
11611
|
+
* Runs `write` directly when it carries no fence, or in a transaction behind
|
|
11612
|
+
* the fence check when it does.
|
|
11613
|
+
*/
|
|
11614
|
+
function withRunFence(client, claimsTable, fence, operation, write) {
|
|
11615
|
+
if (!fence) return write(client);
|
|
11616
|
+
return client.tx(async (t) => {
|
|
11617
|
+
await assertRunFence(t, claimsTable, fence, operation);
|
|
11618
|
+
return write(t);
|
|
11619
|
+
});
|
|
11620
|
+
}
|
|
11621
|
+
//#endregion
|
|
10451
11622
|
//#region src/storage/domains/memory/index.ts
|
|
10452
11623
|
/**
|
|
10453
11624
|
* Local constant for the observational memory table name.
|
|
@@ -10548,6 +11719,10 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
10548
11719
|
* and are indexed for fast batched deletes. Cascade order is enforced in
|
|
10549
11720
|
* `prune()` (children before threads), not here. Observational memory has no
|
|
10550
11721
|
* timestamp anchor and is deliberately excluded.
|
|
11722
|
+
*
|
|
11723
|
+
* Run fences anchor on `retiredAt` (epoch ms), which stays NULL until the
|
|
11724
|
+
* execution holding the fence settles, so a running execution's fence is
|
|
11725
|
+
* never pruned.
|
|
10551
11726
|
*/
|
|
10552
11727
|
static retentionTables = {
|
|
10553
11728
|
messages: {
|
|
@@ -10564,6 +11739,12 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
10564
11739
|
table: TABLE_THREADS,
|
|
10565
11740
|
column: "createdAtZ",
|
|
10566
11741
|
indexed: true
|
|
11742
|
+
},
|
|
11743
|
+
runFences: {
|
|
11744
|
+
table: TABLE_MEMORY_RUN_FENCES,
|
|
11745
|
+
column: "retiredAt",
|
|
11746
|
+
indexed: true,
|
|
11747
|
+
anchorType: "epoch-ms"
|
|
10567
11748
|
}
|
|
10568
11749
|
};
|
|
10569
11750
|
#db;
|
|
@@ -10627,6 +11808,10 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
10627
11808
|
});
|
|
10628
11809
|
await this.#db.createIndexFromStatement("idx_om_lookup_key", `CREATE INDEX IF NOT EXISTS idx_om_lookup_key ON ${omTableName} ("lookupKey")`);
|
|
10629
11810
|
}
|
|
11811
|
+
await this.#db.createTable({
|
|
11812
|
+
tableName: TABLE_MEMORY_RUN_FENCES,
|
|
11813
|
+
schema: RUN_FENCING_TABLE_SCHEMAS[TABLE_MEMORY_RUN_FENCES]
|
|
11814
|
+
});
|
|
10630
11815
|
await this.createDefaultIndexes();
|
|
10631
11816
|
await this.createCustomIndexes();
|
|
10632
11817
|
}
|
|
@@ -10704,6 +11889,12 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
10704
11889
|
statements.push(`CREATE INDEX IF NOT EXISTS "${idxPrefix}idx_om_lookup_key" ON ${fullOmTableName} ("lookupKey");`);
|
|
10705
11890
|
}
|
|
10706
11891
|
for (const idx of MemoryPG.getDefaultIndexDefs(schemaPrefix)) statements.push(generateIndexSQL(idx, schemaName));
|
|
11892
|
+
statements.push(generateTableSQL({
|
|
11893
|
+
tableName: TABLE_MEMORY_RUN_FENCES,
|
|
11894
|
+
schema: RUN_FENCING_TABLE_SCHEMAS[TABLE_MEMORY_RUN_FENCES],
|
|
11895
|
+
schemaName,
|
|
11896
|
+
includeAllConstraints: true
|
|
11897
|
+
}));
|
|
10707
11898
|
return statements;
|
|
10708
11899
|
}
|
|
10709
11900
|
/**
|
|
@@ -10739,6 +11930,61 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
10739
11930
|
await this.#db.clearTable({ tableName: TABLE_MESSAGES });
|
|
10740
11931
|
await this.#db.clearTable({ tableName: TABLE_THREADS });
|
|
10741
11932
|
await this.#db.clearTable({ tableName: TABLE_RESOURCES });
|
|
11933
|
+
await this.#db.clearTable({ tableName: TABLE_MEMORY_RUN_FENCES });
|
|
11934
|
+
}
|
|
11935
|
+
supportsRunFencing() {
|
|
11936
|
+
return true;
|
|
11937
|
+
}
|
|
11938
|
+
#runFencesTable() {
|
|
11939
|
+
return getTableName$3({
|
|
11940
|
+
indexName: TABLE_MEMORY_RUN_FENCES,
|
|
11941
|
+
schemaName: getSchemaName$3(this.#schema)
|
|
11942
|
+
});
|
|
11943
|
+
}
|
|
11944
|
+
/** Runs `write` behind the run's fence check when it carries a fence. */
|
|
11945
|
+
#fenced(fence, operation, write) {
|
|
11946
|
+
return withRunFence(this.#db.client, this.#runFencesTable(), resolveRunFence(this, fence), operation, write);
|
|
11947
|
+
}
|
|
11948
|
+
async raiseRunFence(fence) {
|
|
11949
|
+
try {
|
|
11950
|
+
return await this.#db.client.oneOrNone(`INSERT INTO ${this.#runFencesTable()} AS f ("runId", generation, "ownerId")
|
|
11951
|
+
VALUES ($1, $2, $3)
|
|
11952
|
+
ON CONFLICT ("runId") DO UPDATE
|
|
11953
|
+
SET generation = EXCLUDED.generation, "ownerId" = EXCLUDED."ownerId", "retiredAt" = NULL
|
|
11954
|
+
WHERE f.generation < EXCLUDED.generation
|
|
11955
|
+
OR (f.generation = EXCLUDED.generation AND f."ownerId" = EXCLUDED."ownerId")
|
|
11956
|
+
RETURNING "runId"`, [
|
|
11957
|
+
fence.runId,
|
|
11958
|
+
fence.generation,
|
|
11959
|
+
fence.ownerId
|
|
11960
|
+
]) !== null;
|
|
11961
|
+
} catch (error) {
|
|
11962
|
+
throw new MastraError({
|
|
11963
|
+
id: createStorageErrorId("PG", "RAISE_RUN_FENCE", "FAILED"),
|
|
11964
|
+
domain: ErrorDomain.STORAGE,
|
|
11965
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
11966
|
+
details: { runId: fence.runId }
|
|
11967
|
+
}, error);
|
|
11968
|
+
}
|
|
11969
|
+
}
|
|
11970
|
+
async retireRunFence(fence) {
|
|
11971
|
+
try {
|
|
11972
|
+
return await this.#db.client.oneOrNone(`UPDATE ${this.#runFencesTable()}
|
|
11973
|
+
SET "retiredAt" = ${DB_NOW_MS}
|
|
11974
|
+
WHERE "runId" = $1 AND generation = $2 AND "ownerId" = $3
|
|
11975
|
+
RETURNING "runId"`, [
|
|
11976
|
+
fence.runId,
|
|
11977
|
+
fence.generation,
|
|
11978
|
+
fence.ownerId
|
|
11979
|
+
]) !== null;
|
|
11980
|
+
} catch (error) {
|
|
11981
|
+
throw new MastraError({
|
|
11982
|
+
id: createStorageErrorId("PG", "RETIRE_RUN_FENCE", "FAILED"),
|
|
11983
|
+
domain: ErrorDomain.STORAGE,
|
|
11984
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
11985
|
+
details: { runId: fence.runId }
|
|
11986
|
+
}, error);
|
|
11987
|
+
}
|
|
10742
11988
|
}
|
|
10743
11989
|
/**
|
|
10744
11990
|
* Deletes rows older than the configured `maxAge` per table, in bounded,
|
|
@@ -10759,7 +12005,8 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
10759
12005
|
order: [
|
|
10760
12006
|
"messages",
|
|
10761
12007
|
"resources",
|
|
10762
|
-
"threads"
|
|
12008
|
+
"threads",
|
|
12009
|
+
"runFences"
|
|
10763
12010
|
]
|
|
10764
12011
|
});
|
|
10765
12012
|
const results = await runPrune({
|
|
@@ -11017,7 +12264,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
11017
12264
|
throw mastraError;
|
|
11018
12265
|
}
|
|
11019
12266
|
}
|
|
11020
|
-
async saveThread({ thread }) {
|
|
12267
|
+
async saveThread({ thread, fence }) {
|
|
11021
12268
|
try {
|
|
11022
12269
|
const tableName = getTableName$3({
|
|
11023
12270
|
indexName: TABLE_THREADS,
|
|
@@ -11026,7 +12273,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
11026
12273
|
const createdAt = toUtcISOString(thread.createdAt);
|
|
11027
12274
|
const updatedAt = toUtcISOString(thread.updatedAt);
|
|
11028
12275
|
const metadataJson = thread.metadata ? toPgJson(thread.metadata) : null;
|
|
11029
|
-
await this.#
|
|
12276
|
+
await this.#fenced(fence, "saveThread", (q) => q.none(`INSERT INTO ${tableName} (
|
|
11030
12277
|
id,
|
|
11031
12278
|
"resourceId",
|
|
11032
12279
|
title,
|
|
@@ -11052,12 +12299,13 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
11052
12299
|
createdAt,
|
|
11053
12300
|
updatedAt,
|
|
11054
12301
|
updatedAt
|
|
11055
|
-
]);
|
|
12302
|
+
]));
|
|
11056
12303
|
return {
|
|
11057
12304
|
...thread,
|
|
11058
12305
|
metadata: metadataJson ? JSON.parse(metadataJson) : thread.metadata
|
|
11059
12306
|
};
|
|
11060
12307
|
} catch (error) {
|
|
12308
|
+
if (isRunFenceConflictError(error)) throw error;
|
|
11061
12309
|
throw new MastraError({
|
|
11062
12310
|
id: createStorageErrorId("PG", "SAVE_THREAD", "FAILED"),
|
|
11063
12311
|
domain: ErrorDomain.STORAGE,
|
|
@@ -11066,29 +12314,30 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
11066
12314
|
}, error);
|
|
11067
12315
|
}
|
|
11068
12316
|
}
|
|
11069
|
-
async updateThread({ id, title, metadata }) {
|
|
12317
|
+
async updateThread({ id, title, metadata, fence }) {
|
|
11070
12318
|
const threadTableName = getTableName$3({
|
|
11071
12319
|
indexName: TABLE_THREADS,
|
|
11072
12320
|
schemaName: getSchemaName$3(this.#schema)
|
|
11073
12321
|
});
|
|
11074
|
-
const existingThread = await this.#getThreadById(this.#db.client, { threadId: id });
|
|
11075
|
-
if (!existingThread) throw new MastraError({
|
|
11076
|
-
id: createStorageErrorId("PG", "UPDATE_THREAD", "FAILED"),
|
|
11077
|
-
domain: ErrorDomain.STORAGE,
|
|
11078
|
-
category: ErrorCategory.USER,
|
|
11079
|
-
text: `Thread ${id} not found`,
|
|
11080
|
-
details: {
|
|
11081
|
-
threadId: id,
|
|
11082
|
-
title: title ?? null
|
|
11083
|
-
}
|
|
11084
|
-
});
|
|
11085
|
-
const mergedMetadata = {
|
|
11086
|
-
...existingThread.metadata,
|
|
11087
|
-
...metadata
|
|
11088
|
-
};
|
|
11089
12322
|
try {
|
|
11090
|
-
|
|
11091
|
-
|
|
12323
|
+
return await this.#fenced(fence, "updateThread", async (q) => {
|
|
12324
|
+
const existingThread = await this.#getThreadById(q, { threadId: id });
|
|
12325
|
+
if (!existingThread) throw new MastraError({
|
|
12326
|
+
id: createStorageErrorId("PG", "UPDATE_THREAD", "FAILED"),
|
|
12327
|
+
domain: ErrorDomain.STORAGE,
|
|
12328
|
+
category: ErrorCategory.USER,
|
|
12329
|
+
text: `Thread ${id} not found`,
|
|
12330
|
+
details: {
|
|
12331
|
+
threadId: id,
|
|
12332
|
+
title: title ?? null
|
|
12333
|
+
}
|
|
12334
|
+
});
|
|
12335
|
+
const mergedMetadata = {
|
|
12336
|
+
...existingThread.metadata,
|
|
12337
|
+
...metadata
|
|
12338
|
+
};
|
|
12339
|
+
const nowStr = toUtcISOString(/* @__PURE__ */ new Date());
|
|
12340
|
+
const thread = await q.one(`UPDATE ${threadTableName}
|
|
11092
12341
|
SET
|
|
11093
12342
|
title = COALESCE($1, title),
|
|
11094
12343
|
metadata = $2,
|
|
@@ -11097,21 +12346,23 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
11097
12346
|
WHERE id = $5
|
|
11098
12347
|
RETURNING *
|
|
11099
12348
|
`, [
|
|
11100
|
-
|
|
11101
|
-
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
|
|
11109
|
-
|
|
11110
|
-
|
|
11111
|
-
|
|
11112
|
-
|
|
11113
|
-
|
|
12349
|
+
title ?? null,
|
|
12350
|
+
mergedMetadata,
|
|
12351
|
+
nowStr,
|
|
12352
|
+
nowStr,
|
|
12353
|
+
id
|
|
12354
|
+
]);
|
|
12355
|
+
return {
|
|
12356
|
+
id: thread.id,
|
|
12357
|
+
resourceId: thread.resourceId,
|
|
12358
|
+
title: thread.title,
|
|
12359
|
+
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
12360
|
+
createdAt: thread.createdAtZ || thread.createdAt,
|
|
12361
|
+
updatedAt: thread.updatedAtZ || thread.updatedAt
|
|
12362
|
+
};
|
|
12363
|
+
});
|
|
11114
12364
|
} catch (error) {
|
|
12365
|
+
if (error instanceof MastraError || isRunFenceConflictError(error)) throw error;
|
|
11115
12366
|
throw new MastraError({
|
|
11116
12367
|
id: createStorageErrorId("PG", "UPDATE_THREAD", "FAILED"),
|
|
11117
12368
|
domain: ErrorDomain.STORAGE,
|
|
@@ -11664,8 +12915,9 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
11664
12915
|
throw mastraError;
|
|
11665
12916
|
}
|
|
11666
12917
|
}
|
|
11667
|
-
async saveMessages({ messages }) {
|
|
12918
|
+
async saveMessages({ messages, fence: explicitFence }) {
|
|
11668
12919
|
if (messages.length === 0) return { messages: [] };
|
|
12920
|
+
const fence = resolveRunFence(this, explicitFence);
|
|
11669
12921
|
const threadId = messages[0]?.threadId;
|
|
11670
12922
|
if (!threadId) throw new MastraError({
|
|
11671
12923
|
id: createStorageErrorId("PG", "SAVE_MESSAGES", "FAILED"),
|
|
@@ -11693,6 +12945,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
11693
12945
|
});
|
|
11694
12946
|
const messagesToSave = dedupeMessagesForSave(messages);
|
|
11695
12947
|
await this.#db.client.tx(async (t) => {
|
|
12948
|
+
if (fence) await assertRunFence(t, this.#runFencesTable(), fence, "saveMessages");
|
|
11696
12949
|
for (let offset = 0; offset < messagesToSave.length; offset += MAX_MESSAGES_PER_INSERT) {
|
|
11697
12950
|
const batch = messagesToSave.slice(offset, offset + MAX_MESSAGES_PER_INSERT);
|
|
11698
12951
|
const values = [];
|
|
@@ -11739,7 +12992,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
11739
12992
|
});
|
|
11740
12993
|
return { messages: new MessageList().add(messagesWithParsedContent, "memory").get.all.db() };
|
|
11741
12994
|
} catch (error) {
|
|
11742
|
-
if (error instanceof MastraError) throw error;
|
|
12995
|
+
if (error instanceof MastraError || isRunFenceConflictError(error)) throw error;
|
|
11743
12996
|
throw new MastraError({
|
|
11744
12997
|
id: createStorageErrorId("PG", "SAVE_MESSAGES", "FAILED"),
|
|
11745
12998
|
domain: ErrorDomain.STORAGE,
|
|
@@ -11748,8 +13001,9 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
11748
13001
|
}, error);
|
|
11749
13002
|
}
|
|
11750
13003
|
}
|
|
11751
|
-
async updateMessages({ messages }) {
|
|
13004
|
+
async updateMessages({ messages, fence: explicitFence }) {
|
|
11752
13005
|
if (messages.length === 0) return [];
|
|
13006
|
+
const fence = resolveRunFence(this, explicitFence);
|
|
11753
13007
|
const messageIds = messages.map((m) => m.id);
|
|
11754
13008
|
const selectQuery = `SELECT id, content, role, type, "createdAt", "createdAtZ", thread_id AS "threadId", "resourceId" FROM ${getTableName$3({
|
|
11755
13009
|
indexName: TABLE_MESSAGES,
|
|
@@ -11765,6 +13019,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
11765
13019
|
});
|
|
11766
13020
|
const threadIdsToUpdate = /* @__PURE__ */ new Set();
|
|
11767
13021
|
await this.#db.client.tx(async (t) => {
|
|
13022
|
+
if (fence) await assertRunFence(t, this.#runFencesTable(), fence, "updateMessages");
|
|
11768
13023
|
const queries = [];
|
|
11769
13024
|
const columnMapping = { threadId: "thread_id" };
|
|
11770
13025
|
for (const existingMessage of existingMessages) {
|
|
@@ -11825,8 +13080,9 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
11825
13080
|
return message;
|
|
11826
13081
|
});
|
|
11827
13082
|
}
|
|
11828
|
-
async deleteMessages(messageIds) {
|
|
13083
|
+
async deleteMessages(messageIds, options) {
|
|
11829
13084
|
if (!messageIds || messageIds.length === 0) return;
|
|
13085
|
+
const fence = resolveRunFence(this, options?.fence);
|
|
11830
13086
|
try {
|
|
11831
13087
|
const messageTableName = getTableName$3({
|
|
11832
13088
|
indexName: TABLE_MESSAGES,
|
|
@@ -11837,12 +13093,14 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
11837
13093
|
schemaName: getSchemaName$3(this.#schema)
|
|
11838
13094
|
});
|
|
11839
13095
|
await this.#db.client.tx(async (t) => {
|
|
13096
|
+
if (fence) await assertRunFence(t, this.#runFencesTable(), fence, "deleteMessages");
|
|
11840
13097
|
const placeholders = messageIds.map((_, idx) => `$${idx + 1}`).join(",");
|
|
11841
13098
|
const threadIds = (await t.manyOrNone(`SELECT DISTINCT thread_id FROM ${messageTableName} WHERE id IN (${placeholders})`, messageIds))?.map((msg) => msg.thread_id).filter(Boolean) || [];
|
|
11842
13099
|
await t.none(`DELETE FROM ${messageTableName} WHERE id IN (${placeholders})`, messageIds);
|
|
11843
13100
|
if (threadIds.length > 0) await t.none(`UPDATE ${threadTableName} SET "updatedAt" = NOW(), "updatedAtZ" = NOW() WHERE id IN (${inPlaceholders(threadIds.length)})`, threadIds);
|
|
11844
13101
|
});
|
|
11845
13102
|
} catch (error) {
|
|
13103
|
+
if (isRunFenceConflictError(error)) throw error;
|
|
11846
13104
|
throw new MastraError({
|
|
11847
13105
|
id: createStorageErrorId("PG", "DELETE_MESSAGES", "FAILED"),
|
|
11848
13106
|
domain: ErrorDomain.STORAGE,
|
|
@@ -11870,6 +13128,9 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
11870
13128
|
};
|
|
11871
13129
|
}
|
|
11872
13130
|
async saveResource({ resource }) {
|
|
13131
|
+
return this.#insertResource(this.#db.client, resource);
|
|
13132
|
+
}
|
|
13133
|
+
async #insertResource(q, resource) {
|
|
11873
13134
|
const createdAt = toUtcISOString(resource.createdAt);
|
|
11874
13135
|
const updatedAt = toUtcISOString(resource.updatedAt);
|
|
11875
13136
|
const metadataJson = toPgJson(resource.metadata);
|
|
@@ -11881,7 +13142,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
11881
13142
|
createdAt,
|
|
11882
13143
|
updatedAt
|
|
11883
13144
|
}
|
|
11884
|
-
});
|
|
13145
|
+
}, q);
|
|
11885
13146
|
return {
|
|
11886
13147
|
...resource,
|
|
11887
13148
|
metadata: metadataJson ? JSON.parse(metadataJson) : resource.metadata
|
|
@@ -11892,9 +13153,11 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
11892
13153
|
indexName: TABLE_RESOURCES,
|
|
11893
13154
|
schemaName: getSchemaName$3(this.#schema)
|
|
11894
13155
|
});
|
|
13156
|
+
const fence = resolveRunFence(this, void 0);
|
|
11895
13157
|
const callbackError = {};
|
|
11896
13158
|
try {
|
|
11897
13159
|
return await this.#db.client.tx(async (t) => {
|
|
13160
|
+
if (fence) await assertRunFence(t, this.#runFencesTable(), fence, "mergeResourceWorkingMemory");
|
|
11898
13161
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
11899
13162
|
await t.none(`INSERT INTO ${tableName} (id, metadata, "createdAt", "createdAtZ", "updatedAt", "updatedAtZ")
|
|
11900
13163
|
VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (id) DO NOTHING`, [
|
|
@@ -11938,8 +13201,15 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
11938
13201
|
}, error);
|
|
11939
13202
|
}
|
|
11940
13203
|
}
|
|
11941
|
-
async updateResource({ resourceId, workingMemory, metadata }) {
|
|
11942
|
-
|
|
13204
|
+
async updateResource({ resourceId, workingMemory, metadata, fence }) {
|
|
13205
|
+
return this.#fenced(fence, "updateResource", (q) => this.#updateResource(q, {
|
|
13206
|
+
resourceId,
|
|
13207
|
+
workingMemory,
|
|
13208
|
+
metadata
|
|
13209
|
+
}));
|
|
13210
|
+
}
|
|
13211
|
+
async #updateResource(q, { resourceId, workingMemory, metadata }) {
|
|
13212
|
+
const existingResource = await this.#getResourceById(q, resourceId);
|
|
11943
13213
|
if (!existingResource) {
|
|
11944
13214
|
const newResource = {
|
|
11945
13215
|
id: resourceId,
|
|
@@ -11948,7 +13218,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
11948
13218
|
createdAt: /* @__PURE__ */ new Date(),
|
|
11949
13219
|
updatedAt: /* @__PURE__ */ new Date()
|
|
11950
13220
|
};
|
|
11951
|
-
return this
|
|
13221
|
+
return this.#insertResource(q, newResource);
|
|
11952
13222
|
}
|
|
11953
13223
|
const updatedResource = {
|
|
11954
13224
|
...existingResource,
|
|
@@ -11984,7 +13254,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
11984
13254
|
updates.push(`"updatedAtZ" = $${paramIndex++}`);
|
|
11985
13255
|
values.push(updatedAtStr);
|
|
11986
13256
|
values.push(resourceId);
|
|
11987
|
-
await
|
|
13257
|
+
await q.none(`UPDATE ${tableName} SET ${updates.join(", ")} WHERE id = $${paramIndex}`, values);
|
|
11988
13258
|
return metadataJson ? {
|
|
11989
13259
|
...updatedResource,
|
|
11990
13260
|
metadata: JSON.parse(metadataJson)
|
|
@@ -12123,9 +13393,11 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
12123
13393
|
* serializes generation creation across processes instead. It is
|
|
12124
13394
|
* transaction-scoped so it also works behind transaction-pooling proxies.
|
|
12125
13395
|
*/
|
|
12126
|
-
async #withOMLookupKeyLock(tableName, lookupKey, fn) {
|
|
13396
|
+
async #withOMLookupKeyLock(tableName, lookupKey, fn, fencedOperation) {
|
|
13397
|
+
const fence = fencedOperation ? resolveRunFence(this, void 0) : void 0;
|
|
12127
13398
|
return this.#db.client.tx(async (t) => {
|
|
12128
13399
|
await t.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${tableName}:${lookupKey}`]);
|
|
13400
|
+
if (fence && fencedOperation) await assertRunFence(t, this.#runFencesTable(), fence, fencedOperation);
|
|
12129
13401
|
return fn(t);
|
|
12130
13402
|
});
|
|
12131
13403
|
}
|
|
@@ -12422,7 +13694,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
12422
13694
|
const lastObservedAtStr = input.lastObservedAt.toISOString();
|
|
12423
13695
|
const nowStr = now.toISOString();
|
|
12424
13696
|
const observedMessageIdsJson = input.observedMessageIds ? toPgJson(input.observedMessageIds) : null;
|
|
12425
|
-
if ((await this.#
|
|
13697
|
+
if ((await this.#fenced(void 0, "updateActiveObservations", (q) => q.query(`UPDATE ${tableName} SET
|
|
12426
13698
|
"activeObservations" = $1,
|
|
12427
13699
|
"lastObservedAt" = $2,
|
|
12428
13700
|
"lastObservedAtZ" = $3,
|
|
@@ -12442,7 +13714,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
12442
13714
|
nowStr,
|
|
12443
13715
|
nowStr,
|
|
12444
13716
|
input.id
|
|
12445
|
-
])).rowCount === 0) throw new MastraError({
|
|
13717
|
+
]))).rowCount === 0) throw new MastraError({
|
|
12446
13718
|
id: createStorageErrorId("PG", "UPDATE_ACTIVE_OBSERVATIONS", "NOT_FOUND"),
|
|
12447
13719
|
text: `Observational memory record not found: ${input.id}`,
|
|
12448
13720
|
domain: ErrorDomain.STORAGE,
|
|
@@ -12450,7 +13722,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
12450
13722
|
details: { id: input.id }
|
|
12451
13723
|
});
|
|
12452
13724
|
} catch (error) {
|
|
12453
|
-
if (error instanceof MastraError) throw error;
|
|
13725
|
+
if (error instanceof MastraError || isRunFenceConflictError(error)) throw error;
|
|
12454
13726
|
throw new MastraError({
|
|
12455
13727
|
id: createStorageErrorId("PG", "UPDATE_ACTIVE_OBSERVATIONS", "FAILED"),
|
|
12456
13728
|
domain: ErrorDomain.STORAGE,
|
|
@@ -12470,8 +13742,9 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
12470
13742
|
const newer = await this.#getNewerOMGeneration(t, tableName, lookupKey, input.currentRecord.generationCount);
|
|
12471
13743
|
if (newer) return newer;
|
|
12472
13744
|
return this.#insertReflectionGeneration(t, tableName, lookupKey, input);
|
|
12473
|
-
});
|
|
13745
|
+
}, "createReflectionGeneration");
|
|
12474
13746
|
} catch (error) {
|
|
13747
|
+
if (isRunFenceConflictError(error)) throw error;
|
|
12475
13748
|
throw new MastraError({
|
|
12476
13749
|
id: createStorageErrorId("PG", "CREATE_REFLECTION_GENERATION", "FAILED"),
|
|
12477
13750
|
domain: ErrorDomain.STORAGE,
|
|
@@ -12831,7 +14104,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
12831
14104
|
extractionFailures: input.chunk.extractionFailures
|
|
12832
14105
|
};
|
|
12833
14106
|
const lastBufferedAtTime = input.lastBufferedAtTime ? input.lastBufferedAtTime.toISOString() : null;
|
|
12834
|
-
if ((await this.#
|
|
14107
|
+
if ((await this.#fenced(void 0, "updateBufferedObservations", (q) => q.query(`UPDATE ${tableName} SET
|
|
12835
14108
|
"bufferedObservationChunks" = CASE
|
|
12836
14109
|
WHEN EXISTS (
|
|
12837
14110
|
SELECT 1
|
|
@@ -12850,7 +14123,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
12850
14123
|
nowStr,
|
|
12851
14124
|
nowStr,
|
|
12852
14125
|
input.id
|
|
12853
|
-
])).rowCount === 0) throw new MastraError({
|
|
14126
|
+
]))).rowCount === 0) throw new MastraError({
|
|
12854
14127
|
id: createStorageErrorId("PG", "UPDATE_BUFFERED_OBSERVATIONS", "NOT_FOUND"),
|
|
12855
14128
|
text: `Observational memory record not found: ${input.id}`,
|
|
12856
14129
|
domain: ErrorDomain.STORAGE,
|
|
@@ -12858,7 +14131,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
12858
14131
|
details: { id: input.id }
|
|
12859
14132
|
});
|
|
12860
14133
|
} catch (error) {
|
|
12861
|
-
if (error instanceof MastraError) throw error;
|
|
14134
|
+
if (error instanceof MastraError || isRunFenceConflictError(error)) throw error;
|
|
12862
14135
|
throw new MastraError({
|
|
12863
14136
|
id: createStorageErrorId("PG", "UPDATE_BUFFERED_OBSERVATIONS", "FAILED"),
|
|
12864
14137
|
domain: ErrorDomain.STORAGE,
|
|
@@ -12940,7 +14213,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
12940
14213
|
const lastObservedAt = input.lastObservedAt ?? (latestChunk?.lastObservedAt ? new Date(latestChunk.lastObservedAt) : /* @__PURE__ */ new Date());
|
|
12941
14214
|
const lastObservedAtStr = lastObservedAt.toISOString();
|
|
12942
14215
|
const boundary = `\n\n--- message boundary (${lastObservedAt.toISOString()}) ---\n\n`;
|
|
12943
|
-
if ((await this.#
|
|
14216
|
+
if ((await this.#fenced(void 0, "swapBufferedToActive", (q) => q.query(`UPDATE ${tableName} SET
|
|
12944
14217
|
"activeObservations" = CASE
|
|
12945
14218
|
WHEN "activeObservations" IS NOT NULL AND "activeObservations" != ''
|
|
12946
14219
|
THEN "activeObservations" || $10 || $1
|
|
@@ -12966,7 +14239,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
12966
14239
|
nowStr,
|
|
12967
14240
|
input.id,
|
|
12968
14241
|
boundary
|
|
12969
|
-
])).rowCount === 0) return {
|
|
14242
|
+
]))).rowCount === 0) return {
|
|
12970
14243
|
chunksActivated: 0,
|
|
12971
14244
|
messageTokensActivated: 0,
|
|
12972
14245
|
observationTokensActivated: 0,
|
|
@@ -12994,7 +14267,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
12994
14267
|
currentTask: latestChunkHints?.currentTask ?? void 0
|
|
12995
14268
|
};
|
|
12996
14269
|
} catch (error) {
|
|
12997
|
-
if (error instanceof MastraError) throw error;
|
|
14270
|
+
if (error instanceof MastraError || isRunFenceConflictError(error)) throw error;
|
|
12998
14271
|
throw new MastraError({
|
|
12999
14272
|
id: createStorageErrorId("PG", "SWAP_BUFFERED_TO_ACTIVE", "FAILED"),
|
|
13000
14273
|
domain: ErrorDomain.STORAGE,
|
|
@@ -13010,7 +14283,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
13010
14283
|
schemaName: getSchemaName$3(this.#schema)
|
|
13011
14284
|
});
|
|
13012
14285
|
const nowStr = (/* @__PURE__ */ new Date()).toISOString();
|
|
13013
|
-
if ((await this.#
|
|
14286
|
+
if ((await this.#fenced(void 0, "updateBufferedReflection", (q) => q.query(`UPDATE ${tableName} SET
|
|
13014
14287
|
"bufferedReflection" = CASE
|
|
13015
14288
|
WHEN "bufferedReflection" IS NOT NULL AND "bufferedReflection" != ''
|
|
13016
14289
|
THEN "bufferedReflection" || E'\\n\\n' || $1
|
|
@@ -13029,7 +14302,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
13029
14302
|
nowStr,
|
|
13030
14303
|
nowStr,
|
|
13031
14304
|
input.id
|
|
13032
|
-
])).rowCount === 0) throw new MastraError({
|
|
14305
|
+
]))).rowCount === 0) throw new MastraError({
|
|
13033
14306
|
id: createStorageErrorId("PG", "UPDATE_BUFFERED_REFLECTION", "NOT_FOUND"),
|
|
13034
14307
|
text: `Observational memory record not found: ${input.id}`,
|
|
13035
14308
|
domain: ErrorDomain.STORAGE,
|
|
@@ -13037,7 +14310,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
13037
14310
|
details: { id: input.id }
|
|
13038
14311
|
});
|
|
13039
14312
|
} catch (error) {
|
|
13040
|
-
if (error instanceof MastraError) throw error;
|
|
14313
|
+
if (error instanceof MastraError || isRunFenceConflictError(error)) throw error;
|
|
13041
14314
|
throw new MastraError({
|
|
13042
14315
|
id: createStorageErrorId("PG", "UPDATE_BUFFERED_REFLECTION", "FAILED"),
|
|
13043
14316
|
domain: ErrorDomain.STORAGE,
|
|
@@ -13094,9 +14367,9 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
13094
14367
|
input.currentRecord.id
|
|
13095
14368
|
]);
|
|
13096
14369
|
return newRecord;
|
|
13097
|
-
});
|
|
14370
|
+
}, "swapBufferedReflectionToActive");
|
|
13098
14371
|
} catch (error) {
|
|
13099
|
-
if (error instanceof MastraError) throw error;
|
|
14372
|
+
if (error instanceof MastraError || isRunFenceConflictError(error)) throw error;
|
|
13100
14373
|
throw new MastraError({
|
|
13101
14374
|
id: createStorageErrorId("PG", "SWAP_BUFFERED_REFLECTION_TO_ACTIVE", "FAILED"),
|
|
13102
14375
|
domain: ErrorDomain.STORAGE,
|
|
@@ -20092,6 +21365,7 @@ var PromptBlocksPG = class PromptBlocksPG extends PromptBlocksStorage {
|
|
|
20092
21365
|
}
|
|
20093
21366
|
async create(input) {
|
|
20094
21367
|
const { promptBlock } = input;
|
|
21368
|
+
let draftInserted = false;
|
|
20095
21369
|
try {
|
|
20096
21370
|
const tableName = getTableName$5({
|
|
20097
21371
|
indexName: TABLE_PROMPT_BLOCKS,
|
|
@@ -20113,6 +21387,7 @@ var PromptBlocksPG = class PromptBlocksPG extends PromptBlocksStorage {
|
|
|
20113
21387
|
nowIso,
|
|
20114
21388
|
nowIso
|
|
20115
21389
|
]);
|
|
21390
|
+
draftInserted = true;
|
|
20116
21391
|
const { id: _id, authorId: _authorId, metadata: _metadata, ...snapshotConfig } = promptBlock;
|
|
20117
21392
|
const versionId = crypto.randomUUID();
|
|
20118
21393
|
await this.createVersion({
|
|
@@ -20133,14 +21408,14 @@ var PromptBlocksPG = class PromptBlocksPG extends PromptBlocksStorage {
|
|
|
20133
21408
|
updatedAt: now
|
|
20134
21409
|
};
|
|
20135
21410
|
} catch (error) {
|
|
20136
|
-
if (
|
|
20137
|
-
try {
|
|
21411
|
+
if (draftInserted) try {
|
|
20138
21412
|
const tableName = getTableName$5({
|
|
20139
21413
|
indexName: TABLE_PROMPT_BLOCKS,
|
|
20140
21414
|
schemaName: getSchemaName$5(this.#schema)
|
|
20141
21415
|
});
|
|
20142
21416
|
await this.#db.client.none(`DELETE FROM ${tableName} WHERE id = $1 AND status = 'draft' AND "activeVersionId" IS NULL`, [promptBlock.id]);
|
|
20143
21417
|
} catch {}
|
|
21418
|
+
if (error instanceof MastraError) throw error;
|
|
20144
21419
|
throw new MastraError({
|
|
20145
21420
|
id: createStorageErrorId("PG", "CREATE_PROMPT_BLOCK", "FAILED"),
|
|
20146
21421
|
domain: ErrorDomain.STORAGE,
|
|
@@ -21036,6 +22311,7 @@ var ScorerDefinitionsPG = class ScorerDefinitionsPG extends ScorerDefinitionsSto
|
|
|
21036
22311
|
}
|
|
21037
22312
|
async create(input) {
|
|
21038
22313
|
const { scorerDefinition } = input;
|
|
22314
|
+
let draftInserted = false;
|
|
21039
22315
|
try {
|
|
21040
22316
|
const tableName = getTableName$5({
|
|
21041
22317
|
indexName: TABLE_SCORER_DEFINITIONS,
|
|
@@ -21059,6 +22335,7 @@ var ScorerDefinitionsPG = class ScorerDefinitionsPG extends ScorerDefinitionsSto
|
|
|
21059
22335
|
nowIso,
|
|
21060
22336
|
nowIso
|
|
21061
22337
|
]);
|
|
22338
|
+
draftInserted = true;
|
|
21062
22339
|
const { id: _id, authorId: _authorId, organizationId: _organizationId, projectId: _projectId, metadata: _metadata, ...snapshotConfig } = scorerDefinition;
|
|
21063
22340
|
const versionId = crypto.randomUUID();
|
|
21064
22341
|
await this.createVersion({
|
|
@@ -21081,7 +22358,7 @@ var ScorerDefinitionsPG = class ScorerDefinitionsPG extends ScorerDefinitionsSto
|
|
|
21081
22358
|
updatedAt: now
|
|
21082
22359
|
};
|
|
21083
22360
|
} catch (error) {
|
|
21084
|
-
try {
|
|
22361
|
+
if (draftInserted) try {
|
|
21085
22362
|
const tableName = getTableName$5({
|
|
21086
22363
|
indexName: TABLE_SCORER_DEFINITIONS,
|
|
21087
22364
|
schemaName: getSchemaName$5(this.#schema)
|
|
@@ -22099,6 +23376,7 @@ var SkillsPG = class SkillsPG extends SkillsStorage {
|
|
|
22099
23376
|
}
|
|
22100
23377
|
async create(input) {
|
|
22101
23378
|
const { skill } = input;
|
|
23379
|
+
let draftInserted = false;
|
|
22102
23380
|
try {
|
|
22103
23381
|
const tableName = getTableName$5({
|
|
22104
23382
|
indexName: TABLE_SKILLS,
|
|
@@ -22122,6 +23400,7 @@ var SkillsPG = class SkillsPG extends SkillsStorage {
|
|
|
22122
23400
|
nowIso,
|
|
22123
23401
|
nowIso
|
|
22124
23402
|
]);
|
|
23403
|
+
draftInserted = true;
|
|
22125
23404
|
const { id: _id, authorId: _authorId, visibility: _visibility, ...snapshotConfig } = skill;
|
|
22126
23405
|
const versionId = crypto.randomUUID();
|
|
22127
23406
|
await this.createVersion({
|
|
@@ -22143,7 +23422,7 @@ var SkillsPG = class SkillsPG extends SkillsStorage {
|
|
|
22143
23422
|
updatedAt: now
|
|
22144
23423
|
};
|
|
22145
23424
|
} catch (error) {
|
|
22146
|
-
try {
|
|
23425
|
+
if (draftInserted) try {
|
|
22147
23426
|
const tableName = getTableName$5({
|
|
22148
23427
|
indexName: TABLE_SKILLS,
|
|
22149
23428
|
schemaName: getSchemaName$5(this.#schema)
|
|
@@ -23288,6 +24567,17 @@ function getTableName({ indexName, schemaName }) {
|
|
|
23288
24567
|
const quotedIndexName = `"${indexName}"`;
|
|
23289
24568
|
return schemaName ? `${schemaName}.${quotedIndexName}` : quotedIndexName;
|
|
23290
24569
|
}
|
|
24570
|
+
const RUN_OWNER_COLUMNS = `generation, "ownerId", "leaseExpiresAt", ${DB_NOW_MS} AS "nowMs"`;
|
|
24571
|
+
function toRunOwnershipRecord(runId, row) {
|
|
24572
|
+
const leaseExpiresAt = row.leaseExpiresAt === null ? null : Number(row.leaseExpiresAt);
|
|
24573
|
+
return {
|
|
24574
|
+
runId,
|
|
24575
|
+
generation: Number(row.generation),
|
|
24576
|
+
ownerId: row.ownerId,
|
|
24577
|
+
leaseExpiresAt: leaseExpiresAt === null ? null : new Date(leaseExpiresAt),
|
|
24578
|
+
live: leaseExpiresAt !== null && leaseExpiresAt > Number(row.nowMs)
|
|
24579
|
+
};
|
|
24580
|
+
}
|
|
23291
24581
|
/** Base name (before any schema prefix) of the expression index backing the status filter. */
|
|
23292
24582
|
const WORKFLOW_SNAPSHOT_STATUS_INDEX = "mastra_workflow_snapshot_name_status_createdat_idx";
|
|
23293
24583
|
/**
|
|
@@ -23372,12 +24662,24 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
|
|
|
23372
24662
|
* Workflow run snapshots accumulate as runs execute. Anchored on the
|
|
23373
24663
|
* timezone-aware `updatedAtZ` mirror column (last activity) so suspended or
|
|
23374
24664
|
* long-running runs are not pruned by start age.
|
|
24665
|
+
*
|
|
24666
|
+
* Run ownership rows are anchored on `updatedAt` (epoch ms), which every
|
|
24667
|
+
* claim, renewal and release refreshes, so a live execution's row is never
|
|
24668
|
+
* older than its renew interval.
|
|
23375
24669
|
*/
|
|
23376
|
-
static retentionTables = {
|
|
23377
|
-
|
|
23378
|
-
|
|
23379
|
-
|
|
23380
|
-
|
|
24670
|
+
static retentionTables = {
|
|
24671
|
+
workflowSnapshot: {
|
|
24672
|
+
table: TABLE_WORKFLOW_SNAPSHOT,
|
|
24673
|
+
column: "updatedAtZ",
|
|
24674
|
+
indexed: true
|
|
24675
|
+
},
|
|
24676
|
+
runOwnership: {
|
|
24677
|
+
table: TABLE_WORKFLOW_RUN_OWNERS,
|
|
24678
|
+
column: "updatedAt",
|
|
24679
|
+
indexed: true,
|
|
24680
|
+
anchorType: "epoch-ms"
|
|
24681
|
+
}
|
|
24682
|
+
};
|
|
23381
24683
|
constructor(config) {
|
|
23382
24684
|
super();
|
|
23383
24685
|
const { client, readClient, schemaName, skipDefaultIndexes, indexes } = resolvePgConfig(config);
|
|
@@ -23434,6 +24736,12 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
|
|
|
23434
24736
|
for (const idx of WorkflowsPG.getDefaultIndexDefs(schemaPrefix)) statements.push(generateIndexSQL(idx, schemaName));
|
|
23435
24737
|
statements.push(`${workflowSnapshotStatusIndexSQL(workflowSnapshotStatusIndexName(parsedSchema), schemaName)};`);
|
|
23436
24738
|
statements.push(`${workflowSnapshotThreadIdIndexSQL(workflowSnapshotThreadIdIndexName(parsedSchema), schemaName)};`);
|
|
24739
|
+
statements.push(generateTableSQL({
|
|
24740
|
+
tableName: TABLE_WORKFLOW_RUN_OWNERS,
|
|
24741
|
+
schema: RUN_FENCING_TABLE_SCHEMAS[TABLE_WORKFLOW_RUN_OWNERS],
|
|
24742
|
+
schemaName,
|
|
24743
|
+
includeAllConstraints: true
|
|
24744
|
+
}));
|
|
23437
24745
|
return statements;
|
|
23438
24746
|
}
|
|
23439
24747
|
/**
|
|
@@ -23477,6 +24785,10 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
|
|
|
23477
24785
|
schema: TABLE_SCHEMAS[TABLE_WORKFLOW_SNAPSHOT],
|
|
23478
24786
|
ifNotExists: ["resourceId"]
|
|
23479
24787
|
});
|
|
24788
|
+
await this.#db.createTable({
|
|
24789
|
+
tableName: TABLE_WORKFLOW_RUN_OWNERS,
|
|
24790
|
+
schema: RUN_FENCING_TABLE_SCHEMAS[TABLE_WORKFLOW_RUN_OWNERS]
|
|
24791
|
+
});
|
|
23480
24792
|
await this.createDefaultIndexes();
|
|
23481
24793
|
await this.createCustomIndexes();
|
|
23482
24794
|
}
|
|
@@ -23504,13 +24816,13 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
|
|
|
23504
24816
|
}
|
|
23505
24817
|
}
|
|
23506
24818
|
}
|
|
23507
|
-
/** Delete workflow run snapshots older than
|
|
24819
|
+
/** Delete workflow run snapshots and run ownership rows older than their policy's `maxAge`, batched. */
|
|
23508
24820
|
async prune(policies, options) {
|
|
23509
24821
|
await this.ensureRetentionIndexes(policies);
|
|
23510
24822
|
const targets = resolveTargets({
|
|
23511
24823
|
policies,
|
|
23512
24824
|
descriptor: WorkflowsPG.retentionTables,
|
|
23513
|
-
order: ["workflowSnapshot"]
|
|
24825
|
+
order: ["workflowSnapshot", "runOwnership"]
|
|
23514
24826
|
});
|
|
23515
24827
|
return runPrune({
|
|
23516
24828
|
db: this.#db,
|
|
@@ -23532,10 +24844,126 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
|
|
|
23532
24844
|
}
|
|
23533
24845
|
async dangerouslyClearAll() {
|
|
23534
24846
|
await this.#db.clearTable({ tableName: TABLE_WORKFLOW_SNAPSHOT });
|
|
24847
|
+
await this.#db.clearTable({ tableName: TABLE_WORKFLOW_RUN_OWNERS });
|
|
24848
|
+
}
|
|
24849
|
+
supportsRunFencing() {
|
|
24850
|
+
return true;
|
|
24851
|
+
}
|
|
24852
|
+
#runOwnersTable() {
|
|
24853
|
+
return getTableName({
|
|
24854
|
+
indexName: TABLE_WORKFLOW_RUN_OWNERS,
|
|
24855
|
+
schemaName: getSchemaName(this.#schema)
|
|
24856
|
+
});
|
|
24857
|
+
}
|
|
24858
|
+
async #readRunOwner(q, runId) {
|
|
24859
|
+
const row = await q.oneOrNone(`SELECT ${RUN_OWNER_COLUMNS} FROM ${this.#runOwnersTable()} WHERE "runId" = $1`, [runId]);
|
|
24860
|
+
return row ? toRunOwnershipRecord(runId, row) : null;
|
|
24861
|
+
}
|
|
24862
|
+
#ownershipError(operation, runId, error) {
|
|
24863
|
+
return new MastraError({
|
|
24864
|
+
id: createStorageErrorId("PG", operation, "FAILED"),
|
|
24865
|
+
domain: ErrorDomain.STORAGE,
|
|
24866
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
24867
|
+
details: { runId }
|
|
24868
|
+
}, error);
|
|
24869
|
+
}
|
|
24870
|
+
async claimRunOwnership({ runId, ownerId, leaseMs, force, expectedGeneration }) {
|
|
24871
|
+
const table = this.#runOwnersTable();
|
|
24872
|
+
try {
|
|
24873
|
+
return await this.#db.client.tx(async (t) => {
|
|
24874
|
+
if (!await t.oneOrNone(`SELECT 1 FROM ${table} WHERE "runId" = $1 FOR UPDATE`, [runId])) {
|
|
24875
|
+
if (expectedGeneration !== void 0 && expectedGeneration !== 0) return {
|
|
24876
|
+
acquired: false,
|
|
24877
|
+
record: null
|
|
24878
|
+
};
|
|
24879
|
+
const inserted = await t.oneOrNone(`INSERT INTO ${table} ("runId", generation, "ownerId", "leaseExpiresAt", "updatedAt")
|
|
24880
|
+
VALUES ($1, GREATEST(1, ${DB_NOW_MS}), $2, ${DB_NOW_MS} + $3, ${DB_NOW_MS})
|
|
24881
|
+
ON CONFLICT ("runId") DO NOTHING
|
|
24882
|
+
RETURNING ${RUN_OWNER_COLUMNS}`, [
|
|
24883
|
+
runId,
|
|
24884
|
+
ownerId,
|
|
24885
|
+
leaseMs
|
|
24886
|
+
]);
|
|
24887
|
+
if (inserted) return {
|
|
24888
|
+
acquired: true,
|
|
24889
|
+
record: toRunOwnershipRecord(runId, inserted)
|
|
24890
|
+
};
|
|
24891
|
+
}
|
|
24892
|
+
const claimed = await t.oneOrNone(`UPDATE ${table}
|
|
24893
|
+
SET generation = generation + 1, "ownerId" = $2, "leaseExpiresAt" = ${DB_NOW_MS} + $3,
|
|
24894
|
+
"updatedAt" = ${DB_NOW_MS}
|
|
24895
|
+
WHERE "runId" = $1
|
|
24896
|
+
AND ($4::bigint IS NULL OR generation = $4)
|
|
24897
|
+
AND ($5::boolean OR "leaseExpiresAt" IS NULL OR "leaseExpiresAt" <= ${DB_NOW_MS})
|
|
24898
|
+
RETURNING ${RUN_OWNER_COLUMNS}`, [
|
|
24899
|
+
runId,
|
|
24900
|
+
ownerId,
|
|
24901
|
+
leaseMs,
|
|
24902
|
+
expectedGeneration ?? null,
|
|
24903
|
+
force === true
|
|
24904
|
+
]);
|
|
24905
|
+
if (claimed) return {
|
|
24906
|
+
acquired: true,
|
|
24907
|
+
record: toRunOwnershipRecord(runId, claimed)
|
|
24908
|
+
};
|
|
24909
|
+
return {
|
|
24910
|
+
acquired: false,
|
|
24911
|
+
record: await this.#readRunOwner(t, runId)
|
|
24912
|
+
};
|
|
24913
|
+
});
|
|
24914
|
+
} catch (error) {
|
|
24915
|
+
throw this.#ownershipError("CLAIM_RUN_OWNERSHIP", runId, error);
|
|
24916
|
+
}
|
|
24917
|
+
}
|
|
24918
|
+
async renewRunOwnership({ leaseMs, ...fence }) {
|
|
24919
|
+
try {
|
|
24920
|
+
const renewed = await this.#db.client.oneOrNone(`UPDATE ${this.#runOwnersTable()}
|
|
24921
|
+
SET "leaseExpiresAt" = ${DB_NOW_MS} + $4, "updatedAt" = ${DB_NOW_MS}
|
|
24922
|
+
WHERE "runId" = $1 AND generation = $2 AND "ownerId" = $3 AND "leaseExpiresAt" IS NOT NULL
|
|
24923
|
+
RETURNING ${RUN_OWNER_COLUMNS}`, [
|
|
24924
|
+
fence.runId,
|
|
24925
|
+
fence.generation,
|
|
24926
|
+
fence.ownerId,
|
|
24927
|
+
leaseMs
|
|
24928
|
+
]);
|
|
24929
|
+
if (renewed) return {
|
|
24930
|
+
renewed: true,
|
|
24931
|
+
record: toRunOwnershipRecord(fence.runId, renewed)
|
|
24932
|
+
};
|
|
24933
|
+
return {
|
|
24934
|
+
renewed: false,
|
|
24935
|
+
record: await this.#readRunOwner(this.#db.client, fence.runId)
|
|
24936
|
+
};
|
|
24937
|
+
} catch (error) {
|
|
24938
|
+
throw this.#ownershipError("RENEW_RUN_OWNERSHIP", fence.runId, error);
|
|
24939
|
+
}
|
|
24940
|
+
}
|
|
24941
|
+
async releaseRunOwnership(fence) {
|
|
24942
|
+
try {
|
|
24943
|
+
return await this.#db.client.oneOrNone(`UPDATE ${this.#runOwnersTable()}
|
|
24944
|
+
SET "leaseExpiresAt" = NULL, "updatedAt" = ${DB_NOW_MS}
|
|
24945
|
+
WHERE "runId" = $1 AND generation = $2 AND "ownerId" = $3
|
|
24946
|
+
RETURNING "runId"`, [
|
|
24947
|
+
fence.runId,
|
|
24948
|
+
fence.generation,
|
|
24949
|
+
fence.ownerId
|
|
24950
|
+
]) !== null;
|
|
24951
|
+
} catch (error) {
|
|
24952
|
+
throw this.#ownershipError("RELEASE_RUN_OWNERSHIP", fence.runId, error);
|
|
24953
|
+
}
|
|
24954
|
+
}
|
|
24955
|
+
async getRunOwnership({ runId }) {
|
|
24956
|
+
try {
|
|
24957
|
+
return await this.#readRunOwner(this.#db.client, runId);
|
|
24958
|
+
} catch (error) {
|
|
24959
|
+
throw this.#ownershipError("GET_RUN_OWNERSHIP", runId, error);
|
|
24960
|
+
}
|
|
23535
24961
|
}
|
|
23536
|
-
async updateWorkflowResults({ workflowName, runId, stepId, result, requestContext, state }) {
|
|
24962
|
+
async updateWorkflowResults({ workflowName, runId, stepId, result, requestContext, state, fence: explicitFence }) {
|
|
24963
|
+
const fence = resolveRunFence(this, explicitFence, runId);
|
|
23537
24964
|
try {
|
|
23538
24965
|
return await this.#db.client.tx(async (t) => {
|
|
24966
|
+
if (fence) await assertRunFence(t, this.#runOwnersTable(), fence, "updateWorkflowResults");
|
|
23539
24967
|
const tableName = getTableName({
|
|
23540
24968
|
indexName: TABLE_WORKFLOW_SNAPSHOT,
|
|
23541
24969
|
schemaName: getSchemaName(this.#schema)
|
|
@@ -23585,6 +25013,7 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
|
|
|
23585
25013
|
return snapshot.context;
|
|
23586
25014
|
});
|
|
23587
25015
|
} catch (error) {
|
|
25016
|
+
if (isRunFenceConflictError(error)) throw error;
|
|
23588
25017
|
throw new MastraError({
|
|
23589
25018
|
id: createStorageErrorId("PG", "UPDATE_WORKFLOW_RESULTS", "FAILED"),
|
|
23590
25019
|
domain: ErrorDomain.STORAGE,
|
|
@@ -23597,9 +25026,11 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
|
|
|
23597
25026
|
}, error);
|
|
23598
25027
|
}
|
|
23599
25028
|
}
|
|
23600
|
-
async updateWorkflowState({ workflowName, runId, opts }) {
|
|
25029
|
+
async updateWorkflowState({ workflowName, runId, opts, fence: explicitFence }) {
|
|
25030
|
+
const fence = resolveRunFence(this, explicitFence, runId);
|
|
23601
25031
|
try {
|
|
23602
25032
|
return await this.#db.client.tx(async (t) => {
|
|
25033
|
+
if (fence) await assertRunFence(t, this.#runOwnersTable(), fence, "updateWorkflowState");
|
|
23603
25034
|
const tableName = getTableName({
|
|
23604
25035
|
indexName: TABLE_WORKFLOW_SNAPSHOT,
|
|
23605
25036
|
schemaName: getSchemaName(this.#schema)
|
|
@@ -23629,6 +25060,7 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
|
|
|
23629
25060
|
return updatedSnapshot;
|
|
23630
25061
|
});
|
|
23631
25062
|
} catch (error) {
|
|
25063
|
+
if (isRunFenceConflictError(error)) throw error;
|
|
23632
25064
|
throw new MastraError({
|
|
23633
25065
|
id: createStorageErrorId("PG", "UPDATE_WORKFLOW_STATE", "FAILED"),
|
|
23634
25066
|
domain: ErrorDomain.STORAGE,
|
|
@@ -23640,13 +25072,13 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
|
|
|
23640
25072
|
}, error);
|
|
23641
25073
|
}
|
|
23642
25074
|
}
|
|
23643
|
-
async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt }) {
|
|
25075
|
+
async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt, fence }) {
|
|
23644
25076
|
try {
|
|
23645
25077
|
const now = /* @__PURE__ */ new Date();
|
|
23646
25078
|
const createdAtValue = createdAt ? createdAt : now;
|
|
23647
25079
|
const updatedAtValue = updatedAt ? updatedAt : now;
|
|
23648
25080
|
const sanitizedSnapshot = toPgJson(snapshot);
|
|
23649
|
-
await this.#db.client.none(`INSERT INTO ${getTableName({
|
|
25081
|
+
await withRunFence(this.#db.client, this.#runOwnersTable(), resolveRunFence(this, fence, runId), "persistWorkflowSnapshot", (q) => q.none(`INSERT INTO ${getTableName({
|
|
23650
25082
|
indexName: TABLE_WORKFLOW_SNAPSHOT,
|
|
23651
25083
|
schemaName: getSchemaName(this.#schema)
|
|
23652
25084
|
})} AS t
|
|
@@ -23662,8 +25094,9 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
|
|
|
23662
25094
|
updatedAtValue,
|
|
23663
25095
|
createdAtValue,
|
|
23664
25096
|
updatedAtValue
|
|
23665
|
-
]);
|
|
25097
|
+
]));
|
|
23666
25098
|
} catch (error) {
|
|
25099
|
+
if (isRunFenceConflictError(error)) throw error;
|
|
23667
25100
|
throw new MastraError({
|
|
23668
25101
|
id: createStorageErrorId("PG", "PERSIST_WORKFLOW_SNAPSHOT", "FAILED"),
|
|
23669
25102
|
domain: ErrorDomain.STORAGE,
|
|
@@ -23729,13 +25162,14 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
|
|
|
23729
25162
|
}, error);
|
|
23730
25163
|
}
|
|
23731
25164
|
}
|
|
23732
|
-
async deleteWorkflowRunById({ runId, workflowName }) {
|
|
25165
|
+
async deleteWorkflowRunById({ runId, workflowName, fence }) {
|
|
23733
25166
|
try {
|
|
23734
|
-
await this.#db.client.none(`DELETE FROM ${getTableName({
|
|
25167
|
+
await withRunFence(this.#db.client, this.#runOwnersTable(), resolveRunFence(this, fence, runId), "deleteWorkflowRunById", (q) => q.none(`DELETE FROM ${getTableName({
|
|
23735
25168
|
indexName: TABLE_WORKFLOW_SNAPSHOT,
|
|
23736
25169
|
schemaName: getSchemaName(this.#schema)
|
|
23737
|
-
})} WHERE run_id = $1 AND workflow_name = $2`, [runId, workflowName]);
|
|
25170
|
+
})} WHERE run_id = $1 AND workflow_name = $2`, [runId, workflowName]));
|
|
23738
25171
|
} catch (error) {
|
|
25172
|
+
if (isRunFenceConflictError(error)) throw error;
|
|
23739
25173
|
throw new MastraError({
|
|
23740
25174
|
id: createStorageErrorId("PG", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
|
|
23741
25175
|
domain: ErrorDomain.STORAGE,
|
|
@@ -23947,6 +25381,7 @@ var WorkspacesPG = class WorkspacesPG extends WorkspacesStorage {
|
|
|
23947
25381
|
}
|
|
23948
25382
|
async create(input) {
|
|
23949
25383
|
const { workspace } = input;
|
|
25384
|
+
let draftInserted = false;
|
|
23950
25385
|
try {
|
|
23951
25386
|
const tableName = getTableName$5({
|
|
23952
25387
|
indexName: TABLE_WORKSPACES,
|
|
@@ -23968,6 +25403,7 @@ var WorkspacesPG = class WorkspacesPG extends WorkspacesStorage {
|
|
|
23968
25403
|
nowIso,
|
|
23969
25404
|
nowIso
|
|
23970
25405
|
]);
|
|
25406
|
+
draftInserted = true;
|
|
23971
25407
|
const { id: _id, authorId: _authorId, metadata: _metadata, ...snapshotConfig } = workspace;
|
|
23972
25408
|
const versionId = crypto.randomUUID();
|
|
23973
25409
|
await this.createVersion({
|
|
@@ -23988,7 +25424,7 @@ var WorkspacesPG = class WorkspacesPG extends WorkspacesStorage {
|
|
|
23988
25424
|
updatedAt: now
|
|
23989
25425
|
};
|
|
23990
25426
|
} catch (error) {
|
|
23991
|
-
try {
|
|
25427
|
+
if (draftInserted) try {
|
|
23992
25428
|
const tableName = getTableName$5({
|
|
23993
25429
|
indexName: TABLE_WORKSPACES,
|
|
23994
25430
|
schemaName: getSchemaName$5(this.#schema)
|
|
@@ -25038,7 +26474,10 @@ var PostgresStore = class extends MastraCompositeStore {
|
|
|
25038
26474
|
workflows: new WorkflowsPG(domainConfig),
|
|
25039
26475
|
workflowDefinitions: new WorkflowDefinitionsPG(domainConfig),
|
|
25040
26476
|
memory: new MemoryPG(domainConfig),
|
|
25041
|
-
knowledge: new KnowledgePG(
|
|
26477
|
+
knowledge: new KnowledgePG({
|
|
26478
|
+
...domainConfig,
|
|
26479
|
+
storageIsolationKey: config.storageIsolationKey ?? getPgKnowledgeIsolationKey(config)
|
|
26480
|
+
}),
|
|
25042
26481
|
notifications: new NotificationsPG(domainConfig),
|
|
25043
26482
|
observability: new ObservabilityPG(domainConfig),
|
|
25044
26483
|
agents: new AgentsPG(domainConfig),
|
|
@@ -25092,7 +26531,7 @@ var PostgresStore = class extends MastraCompositeStore {
|
|
|
25092
26531
|
const pinned = new PinnedClientAdapter(this.#writePool, pinnedClient);
|
|
25093
26532
|
this.#db.pin(pinned);
|
|
25094
26533
|
this.#db.setSchemaSnapshot(await loadSchemaSnapshot(pinned, this.schema));
|
|
25095
|
-
await
|
|
26534
|
+
await Promise.all(Object.entries(this.stores).filter(([name, store]) => name !== "knowledge" && store).map(([, store]) => store.init()));
|
|
25096
26535
|
this.isInitialized = true;
|
|
25097
26536
|
} catch (error) {
|
|
25098
26537
|
this.#initPromise = null;
|