feat(ql3): establish 3.0 incubation baseline

This commit is contained in:
whyour
2026-08-12 00:25:26 +08:00
parent 4bf92dcfeb
commit c699c32461
2817 changed files with 779642 additions and 653 deletions
@@ -0,0 +1,727 @@
import type { PostgresClient, PostgresPool } from '@qinglong/runtime-core';
import {
InvalidProjectToolDefinitionSnapshotError,
MAX_PROJECT_TOOL_DEFINITION_SNAPSHOT_JSON_BYTES,
MAX_PROJECT_TOOL_SNAPSHOT_ACTIVE_PACKAGES,
ProjectToolDefinitionSnapshotConflictError,
ProjectToolDefinitionSnapshotUnavailableError,
assertProjectToolDefinitionSnapshotRecoveryPageSize,
assertProjectToolDefinitionSnapshotSourcePageSize,
normalizeProjectToolDefinitionSnapshot,
normalizeProjectToolDefinitionSnapshotPendingProjectCursor,
normalizeProjectToolDefinitionSnapshotRecord,
normalizeProjectToolDefinitionSnapshotSourceCursor,
projectToolDefinitionActiveVectorDigest,
type ProjectToolDefinitionSnapshot,
type ProjectToolDefinitionSnapshotPendingProjectPage,
type ProjectToolDefinitionSnapshotRecord,
type ProjectToolDefinitionSnapshotRepository,
type ProjectToolDefinitionSnapshotSource,
type ProjectToolDefinitionSnapshotSourcePage,
type ProjectToolDefinitionSnapshotSourceRepository,
} from '@qinglong/runtime-core/project-tool-definition-snapshot';
import { assertProjectPolicyProjectId } from '@qinglong/runtime-core/project-policy';
import {
POSTGRES_DEFINITION_RETRYABLE_SQL_STATES,
POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS,
configurePostgresDefinitionTransaction,
postgresRequiredInteger,
postgresRequiredJsonObject,
postgresRequiredString,
postgresSqlState,
rollbackPostgresDefinitionTransaction,
} from '../repository/definitionRepositorySupport';
type Row = Record<string, unknown>;
type Queryable = Pick<PostgresPool, 'query'> | Pick<PostgresClient, 'query'>;
function invalid(message: string): never {
throw new InvalidProjectToolDefinitionSnapshotError(message);
}
function unavailable(): ProjectToolDefinitionSnapshotUnavailableError {
return new ProjectToolDefinitionSnapshotUnavailableError();
}
function normalizeProjectId(value: unknown): string {
try {
assertProjectPolicyProjectId(value as string);
} catch {
return invalid('projectId is invalid');
}
return value as string;
}
function serialize(snapshot: Readonly<ProjectToolDefinitionSnapshot>): string {
const value = JSON.stringify(snapshot);
if (
Buffer.byteLength(value, 'utf8') >
MAX_PROJECT_TOOL_DEFINITION_SNAPSHOT_JSON_BYTES
) {
return invalid('snapshot exceeds the durable JSON budget');
}
return value;
}
function same(
left: readonly Readonly<ProjectToolDefinitionSnapshotSource>[],
right: readonly Readonly<ProjectToolDefinitionSnapshotSource>[],
): boolean {
return JSON.stringify(left) === JSON.stringify(right);
}
function exactOptions(
value: unknown,
required: readonly string[],
optional: readonly string[],
label: string,
): asserts value is Record<string, unknown> {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
return invalid(`${label} is invalid`);
}
const keys = Reflect.ownKeys(value);
const allowed = new Set([...required, ...optional]);
if (
required.some((key) => !keys.includes(key)) ||
keys.some((key) => typeof key !== 'string' || !allowed.has(key))
) {
invalid(`${label} shape is invalid`);
}
}
function sourceFromRow(
row: Row,
): Readonly<ProjectToolDefinitionSnapshotSource> {
if (postgresRequiredString(row.activeState, unavailable) !== 'active') {
throw unavailable();
}
return Object.freeze({
installationId: postgresRequiredString(row.installationId, unavailable),
packageName: postgresRequiredString(row.packageName, unavailable),
generation: postgresRequiredInteger(row.generation, unavailable),
generationDigest: postgresRequiredString(row.generationDigest, unavailable),
lockDigest: postgresRequiredString(row.lockDigest, unavailable),
revisionDigest: postgresRequiredString(row.revisionDigest, unavailable),
});
}
function mappedError(error: unknown): Error {
if (
error instanceof InvalidProjectToolDefinitionSnapshotError ||
error instanceof ProjectToolDefinitionSnapshotConflictError ||
error instanceof ProjectToolDefinitionSnapshotUnavailableError
) {
return error;
}
const state = postgresSqlState(error);
if (state === '23503' || state === '23505' || state === '23514') {
return new ProjectToolDefinitionSnapshotConflictError(
'snapshot identity is already bound',
);
}
return new ProjectToolDefinitionSnapshotUnavailableError({
cause: error instanceof Error ? error : undefined,
});
}
export class PostgresProjectToolDefinitionSnapshotRepository
implements
ProjectToolDefinitionSnapshotRepository,
ProjectToolDefinitionSnapshotSourceRepository
{
constructor(private readonly pool: PostgresPool) {
if (
!pool ||
typeof pool.query !== 'function' ||
typeof pool.connect !== 'function'
) {
throw new TypeError(
'PostgreSQL Project Tool Definition snapshot repository options are invalid',
);
}
}
private async currentSources(
queryable: Queryable,
projectId: string,
lock: boolean,
): Promise<readonly Readonly<ProjectToolDefinitionSnapshotSource>[]> {
const result = await queryable.query<Row>(
`SELECT
head.package_name AS "packageName",
active_install.installation_id AS "installationId",
active_install.target_generation AS "generation",
revision.generation_digest AS "generationDigest",
active_install.lock_digest AS "lockDigest",
revision.revision_digest AS "revisionDigest",
active_install.state AS "activeState"
FROM "ql3"."plugin_package_install_heads" AS head
JOIN "ql3"."plugin_package_installs" AS head_install
ON head_install.installation_id = head.installation_id
LEFT JOIN "ql3"."plugin_package_installs" AS active_install
ON active_install.project_id = head.project_id
AND active_install.package_name = head.package_name
AND active_install.lock_digest = head_install.active_lock_digest
LEFT JOIN "ql3"."plugin_package_materialized_revisions" AS revision
ON revision.project_id = active_install.project_id
AND revision.package_name = active_install.package_name
AND revision.generation = active_install.target_generation
AND revision.lock_digest = active_install.lock_digest
LEFT JOIN "ql3"."plugin_package_quarantine_events" AS quarantine
ON quarantine.project_id = active_install.project_id
AND quarantine.package_name = active_install.package_name
AND quarantine.installation_id = active_install.installation_id
AND quarantine.lock_digest = active_install.lock_digest
LEFT JOIN "ql3"."plugin_package_lifecycle_heads" AS lifecycle
ON lifecycle.project_id = active_install.project_id
AND lifecycle.package_name = active_install.package_name
AND lifecycle.installation_id = active_install.installation_id
AND lifecycle.lock_digest = active_install.lock_digest
AND lifecycle.install_record_digest = active_install.record_digest
WHERE head.project_id = $1
AND head_install.active_lock_digest IS NOT NULL
AND quarantine.event_digest IS NULL
AND (
lifecycle.event_digest IS NULL OR lifecycle.disposition = 'active'
)
AND NOT EXISTS (
SELECT 1
FROM "ql3"."plugin_package_publisher_provenance" AS provenance
JOIN "ql3"."plugin_package_publisher_revocation_receipts" AS revoked
ON revoked.publisher = provenance.publisher
AND revoked.key_id = provenance.key_id
WHERE provenance.installation_id = active_install.installation_id
AND provenance.lock_digest = active_install.lock_digest
)
ORDER BY head.package_name, active_install.installation_id
${lock ? 'FOR SHARE OF head, head_install' : ''}`,
[projectId],
);
if (result.rows.length > MAX_PROJECT_TOOL_SNAPSHOT_ACTIVE_PACKAGES) {
throw unavailable();
}
const sources = result.rows.map(sourceFromRow);
if (
sources.some(
(source, index) =>
index > 0 && sources[index - 1]!.packageName >= source.packageName,
)
) {
throw unavailable();
}
return Object.freeze(sources);
}
private activeVectorDigest(
projectId: string,
sources: readonly Readonly<ProjectToolDefinitionSnapshotSource>[],
): string {
try {
return projectToolDefinitionActiveVectorDigest(projectId, sources);
} catch {
throw unavailable();
}
}
private async sourceRows(
queryable: Queryable,
projectId: string,
activeVectorDigest: string,
): Promise<readonly Readonly<ProjectToolDefinitionSnapshotSource>[]> {
const result = await queryable.query<Row>(
`SELECT
installation_id AS "installationId",
package_name AS "packageName",
generation,
generation_digest AS "generationDigest",
lock_digest AS "lockDigest",
revision_digest AS "revisionDigest"
FROM "ql3"."project_tool_definition_snapshot_sources"
WHERE project_id = $1 AND active_vector_digest = $2
ORDER BY package_name`,
[projectId, activeVectorDigest],
);
return Object.freeze(
result.rows.map((row) =>
Object.freeze({
installationId: postgresRequiredString(
row.installationId,
unavailable,
),
packageName: postgresRequiredString(row.packageName, unavailable),
generation: postgresRequiredInteger(row.generation, unavailable),
generationDigest: postgresRequiredString(
row.generationDigest,
unavailable,
),
lockDigest: postgresRequiredString(row.lockDigest, unavailable),
revisionDigest: postgresRequiredString(
row.revisionDigest,
unavailable,
),
}),
),
);
}
private async findStored(
queryable: Queryable,
projectId: string,
activeVectorDigest: string,
): Promise<Readonly<ProjectToolDefinitionSnapshotRecord> | null> {
const result = await queryable.query<Row>(
`SELECT
project_id AS "projectId",
active_vector_digest AS "activeVectorDigest",
definitions_digest AS "definitionsDigest",
snapshot_digest AS "snapshotDigest",
snapshot_json AS "snapshotJson",
committed_at_ms AS "committedAtMs"
FROM "ql3"."project_tool_definition_snapshots"
WHERE project_id = $1 AND active_vector_digest = $2
LIMIT 2`,
[projectId, activeVectorDigest],
);
if (result.rows.length === 0) return null;
if (result.rows.length !== 1) throw unavailable();
const row = result.rows[0]!;
try {
const snapshot = normalizeProjectToolDefinitionSnapshot(
postgresRequiredJsonObject(
row.snapshotJson,
unavailable,
) as unknown as ProjectToolDefinitionSnapshot,
);
const record = normalizeProjectToolDefinitionSnapshotRecord({
snapshot,
committedAtMs: postgresRequiredInteger(row.committedAtMs, unavailable),
});
if (
snapshot.projectId !==
postgresRequiredString(row.projectId, unavailable) ||
snapshot.activeVectorDigest !==
postgresRequiredString(row.activeVectorDigest, unavailable) ||
snapshot.definitionsDigest !==
postgresRequiredString(row.definitionsDigest, unavailable) ||
snapshot.snapshotDigest !==
postgresRequiredString(row.snapshotDigest, unavailable) ||
!same(
snapshot.sources,
await this.sourceRows(
queryable,
snapshot.projectId,
snapshot.activeVectorDigest,
),
)
) {
throw unavailable();
}
return record;
} catch (error) {
if (error instanceof ProjectToolDefinitionSnapshotUnavailableError) {
throw error;
}
throw unavailable();
}
}
async findCurrent(
projectIdValue: string,
): Promise<Readonly<ProjectToolDefinitionSnapshotRecord> | null> {
const projectId = normalizeProjectId(projectIdValue);
let client: PostgresClient;
try {
client = await this.pool.connect();
} catch {
throw unavailable();
}
let began = false;
try {
await client.query('BEGIN ISOLATION LEVEL REPEATABLE READ READ ONLY');
began = true;
await client.query(`SELECT set_config('statement_timeout', $1, true)`, [
'5000ms',
]);
const sources = await this.currentSources(client, projectId, false);
const record = await this.findStored(
client,
projectId,
this.activeVectorDigest(projectId, sources),
);
if (record && !same(record.snapshot.sources, sources)) {
throw unavailable();
}
await client.query('COMMIT');
began = false;
return record;
} catch (error) {
if (began) await rollbackPostgresDefinitionTransaction(client);
throw mappedError(error);
} finally {
client.release();
}
}
async listActiveSourcePage(options: {
readonly projectId: string;
readonly limit: number;
readonly after?: Readonly<{ readonly packageName: string }>;
}): Promise<Readonly<ProjectToolDefinitionSnapshotSourcePage>> {
exactOptions(
options,
['limit', 'projectId'],
['after'],
'snapshot source page options',
);
const projectId = normalizeProjectId(options.projectId);
assertProjectToolDefinitionSnapshotSourcePageSize(options.limit);
const after =
options.after === undefined
? undefined
: normalizeProjectToolDefinitionSnapshotSourceCursor(options.after);
try {
const result = await this.pool.query<Row>(
`SELECT
head.package_name AS "packageName",
active_install.installation_id AS "installationId",
active_install.target_generation AS "generation",
revision.generation_digest AS "generationDigest",
active_install.lock_digest AS "lockDigest",
revision.revision_digest AS "revisionDigest",
active_install.state AS "activeState"
FROM "ql3"."plugin_package_install_heads" AS head
JOIN "ql3"."plugin_package_installs" AS head_install
ON head_install.installation_id = head.installation_id
LEFT JOIN "ql3"."plugin_package_installs" AS active_install
ON active_install.project_id = head.project_id
AND active_install.package_name = head.package_name
AND active_install.lock_digest = head_install.active_lock_digest
LEFT JOIN "ql3"."plugin_package_materialized_revisions" AS revision
ON revision.project_id = active_install.project_id
AND revision.package_name = active_install.package_name
AND revision.generation = active_install.target_generation
AND revision.lock_digest = active_install.lock_digest
LEFT JOIN "ql3"."plugin_package_quarantine_events" AS quarantine
ON quarantine.project_id = active_install.project_id
AND quarantine.package_name = active_install.package_name
AND quarantine.installation_id = active_install.installation_id
AND quarantine.lock_digest = active_install.lock_digest
WHERE head.project_id = $1
AND head_install.active_lock_digest IS NOT NULL
AND quarantine.event_digest IS NULL
AND NOT EXISTS (
SELECT 1
FROM "ql3"."plugin_package_publisher_provenance" AS provenance
JOIN "ql3"."plugin_package_publisher_revocation_receipts" AS revoked
ON revoked.publisher = provenance.publisher
AND revoked.key_id = provenance.key_id
WHERE provenance.installation_id =
active_install.installation_id
AND provenance.lock_digest = active_install.lock_digest
)
AND head.package_name > $2
ORDER BY head.package_name, active_install.installation_id
LIMIT $3`,
[projectId, after?.packageName ?? '', options.limit + 1],
);
const truncated = result.rows.length > options.limit;
const sources = Object.freeze(
result.rows.slice(0, options.limit).map(sourceFromRow),
);
if (
sources.some(
(source, index) =>
index > 0 && sources[index - 1]!.packageName >= source.packageName,
)
) {
throw unavailable();
}
const last = sources.at(-1);
return Object.freeze({
sources,
truncated,
...(truncated && last
? { next: Object.freeze({ packageName: last.packageName }) }
: {}),
});
} catch (error) {
throw mappedError(error);
}
}
async listPendingProjectPage(options: {
readonly limit: number;
readonly after?: Readonly<{ readonly projectId: string }>;
}): Promise<Readonly<ProjectToolDefinitionSnapshotPendingProjectPage>> {
exactOptions(
options,
['limit'],
['after'],
'snapshot pending Project page options',
);
assertProjectToolDefinitionSnapshotRecoveryPageSize(options.limit);
const after =
options.after === undefined
? undefined
: normalizeProjectToolDefinitionSnapshotPendingProjectCursor(
options.after,
);
try {
const result = await this.pool.query<Row>(
`WITH active_sources AS (
SELECT
head.project_id,
head.package_name,
active_install.installation_id,
active_install.target_generation AS generation,
revision.generation_digest,
active_install.lock_digest,
revision.revision_digest,
CASE
WHEN active_install.state = 'active'
AND active_install.installation_id IS NOT NULL
AND revision.generation_digest IS NOT NULL
AND revision.revision_digest IS NOT NULL
THEN true ELSE false
END AS valid
FROM "ql3"."plugin_package_install_heads" AS head
JOIN "ql3"."plugin_package_installs" AS head_install
ON head_install.installation_id = head.installation_id
LEFT JOIN "ql3"."plugin_package_installs" AS active_install
ON active_install.project_id = head.project_id
AND active_install.package_name = head.package_name
AND active_install.lock_digest = head_install.active_lock_digest
LEFT JOIN "ql3"."plugin_package_materialized_revisions" AS revision
ON revision.project_id = active_install.project_id
AND revision.package_name = active_install.package_name
AND revision.generation = active_install.target_generation
AND revision.lock_digest = active_install.lock_digest
LEFT JOIN "ql3"."plugin_package_quarantine_events" AS quarantine
ON quarantine.project_id = active_install.project_id
AND quarantine.package_name = active_install.package_name
AND quarantine.installation_id = active_install.installation_id
AND quarantine.lock_digest = active_install.lock_digest
WHERE head_install.active_lock_digest IS NOT NULL
AND quarantine.event_digest IS NULL
AND NOT EXISTS (
SELECT 1
FROM "ql3"."plugin_package_publisher_provenance" AS provenance
JOIN "ql3"."plugin_package_publisher_revocation_receipts" AS revoked
ON revoked.publisher = provenance.publisher
AND revoked.key_id = provenance.key_id
WHERE provenance.installation_id =
active_install.installation_id
AND provenance.lock_digest = active_install.lock_digest
)
)
SELECT project.id AS "projectId"
FROM "ql3"."projects" AS project
WHERE project.status = 'active'
AND project.id COLLATE "C" > $1 COLLATE "C"
AND (
EXISTS (
SELECT 1
FROM active_sources AS active
WHERE active.project_id = project.id
AND active.valid = false
)
OR NOT EXISTS (
SELECT 1
FROM "ql3"."project_tool_definition_snapshots" AS snapshot
WHERE snapshot.project_id = project.id
AND NOT EXISTS (
SELECT 1
FROM active_sources AS active
WHERE active.project_id = project.id
AND active.valid = true
AND NOT EXISTS (
SELECT 1
FROM "ql3"."project_tool_definition_snapshot_sources" AS stored
WHERE stored.project_id = snapshot.project_id
AND stored.active_vector_digest =
snapshot.active_vector_digest
AND stored.package_name = active.package_name
AND stored.installation_id = active.installation_id
AND stored.generation = active.generation
AND stored.generation_digest =
active.generation_digest
AND stored.lock_digest = active.lock_digest
AND stored.revision_digest =
active.revision_digest
)
)
AND NOT EXISTS (
SELECT 1
FROM "ql3"."project_tool_definition_snapshot_sources" AS stored
WHERE stored.project_id = snapshot.project_id
AND stored.active_vector_digest =
snapshot.active_vector_digest
AND NOT EXISTS (
SELECT 1
FROM active_sources AS active
WHERE active.project_id = project.id
AND active.valid = true
AND active.package_name = stored.package_name
AND active.installation_id =
stored.installation_id
AND active.generation = stored.generation
AND active.generation_digest =
stored.generation_digest
AND active.lock_digest = stored.lock_digest
AND active.revision_digest =
stored.revision_digest
)
)
)
)
ORDER BY project.id COLLATE "C"
LIMIT $2`,
[after?.projectId ?? '', options.limit + 1],
);
const truncated = result.rows.length > options.limit;
const projectIds = Object.freeze(
result.rows
.slice(0, options.limit)
.map((row) =>
normalizeProjectId(
postgresRequiredString(row.projectId, unavailable),
),
),
);
const last = projectIds.at(-1);
return Object.freeze({
projectIds,
truncated,
...(truncated && last
? { next: Object.freeze({ projectId: last }) }
: {}),
});
} catch (error) {
throw mappedError(error);
}
}
async publish(value: Readonly<ProjectToolDefinitionSnapshot>): Promise<
Readonly<{
status: 'created' | 'existing';
record: Readonly<ProjectToolDefinitionSnapshotRecord>;
}>
> {
const snapshot = normalizeProjectToolDefinitionSnapshot(value);
const snapshotJson = serialize(snapshot);
for (
let attempt = 0;
attempt < POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS;
attempt += 1
) {
let client: PostgresClient;
try {
client = await this.pool.connect();
} catch {
throw unavailable();
}
let began = false;
try {
await configurePostgresDefinitionTransaction(client);
began = true;
const sources = await this.currentSources(
client,
snapshot.projectId,
true,
);
if (
!same(snapshot.sources, sources) ||
this.activeVectorDigest(snapshot.projectId, sources) !==
snapshot.activeVectorDigest
) {
throw new ProjectToolDefinitionSnapshotConflictError(
'snapshot source vector is not the current active Package vector',
);
}
const inserted = await client.query(
`INSERT INTO "ql3"."project_tool_definition_snapshots" (
project_id, active_vector_digest, definitions_digest,
snapshot_digest, snapshot_json, committed_at_ms
)
VALUES ($1, $2, $3, $4, $5::jsonb,
floor(extract(epoch FROM clock_timestamp()) * 1000)::bigint)
ON CONFLICT (project_id, active_vector_digest) DO NOTHING
RETURNING active_vector_digest`,
[
snapshot.projectId,
snapshot.activeVectorDigest,
snapshot.definitionsDigest,
snapshot.snapshotDigest,
snapshotJson,
],
);
if (inserted.rows.length === 1 && snapshot.sources.length > 0) {
await client.query(
`INSERT INTO "ql3"."project_tool_definition_snapshot_sources" (
project_id, active_vector_digest, package_name,
installation_id, generation, generation_digest,
lock_digest, revision_digest
)
SELECT $1, $2, source.package_name, source.installation_id,
source.generation, source.generation_digest,
source.lock_digest, source.revision_digest
FROM jsonb_to_recordset($3::jsonb) AS source(
package_name varchar(63),
installation_id varchar(128),
generation integer,
generation_digest char(64),
lock_digest char(64),
revision_digest char(64)
)`,
[
snapshot.projectId,
snapshot.activeVectorDigest,
JSON.stringify(
snapshot.sources.map((source) => ({
package_name: source.packageName,
installation_id: source.installationId,
generation: source.generation,
generation_digest: source.generationDigest,
lock_digest: source.lockDigest,
revision_digest: source.revisionDigest,
})),
),
],
);
}
const stored = await this.findStored(
client,
snapshot.projectId,
snapshot.activeVectorDigest,
);
if (!stored) throw unavailable();
if (JSON.stringify(stored.snapshot) !== snapshotJson) {
throw new ProjectToolDefinitionSnapshotConflictError(
'active vector is bound to another semantic snapshot',
);
}
await client.query('COMMIT');
began = false;
return Object.freeze({
status: inserted.rows.length === 1 ? 'created' : 'existing',
record: stored,
});
} catch (error) {
if (began) await rollbackPostgresDefinitionTransaction(client);
const state = postgresSqlState(error);
if (
state &&
POSTGRES_DEFINITION_RETRYABLE_SQL_STATES.has(state) &&
attempt + 1 < POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS
) {
continue;
}
throw mappedError(error);
} finally {
client.release();
}
}
throw unavailable();
}
}
@@ -0,0 +1,705 @@
import type {
PostgresClient,
PostgresPool,
PostgresQueryable,
} from '@qinglong/runtime-core';
import {
InvalidToolExecutionCompletionError,
MAX_TOOL_EXECUTION_COMPLETION_JSON_BYTES,
MAX_TOOL_EXECUTION_RESULT_ARTIFACT_JSON_BYTES,
ToolExecutionCompletionConflictError,
ToolExecutionCompletionUnavailableError,
normalizeToolExecutionCompletionCommand,
normalizeToolExecutionCompletionRecord,
normalizeToolExecutionResultKeyBinding,
normalizeToolExecutionResultArtifact,
toolExecutionCompletionRecord,
toolExecutionResultKeyBinding,
type CommitToolExecutionCompletionResult,
type ToolExecutionCompletionCommand,
type ToolExecutionCompletionRecord,
type ToolExecutionCompletionRepository,
type ToolExecutionResultArtifact,
type ToolExecutionResultKeyBinding,
} from '@qinglong/runtime-core/tool-execution-completion';
import {
normalizeToolResultKeyCatalogRecord,
requireActiveToolResultKey,
toolResultKeyCatalogFence,
type ToolResultKeyCatalogRecord,
} from '@qinglong/runtime-core/tool-result-key-catalog';
import type { StepRunMutation } from '@qinglong/runtime-core/step-run';
import { normalizeToolExecutionStartBarrierRecord } from '@qinglong/runtime-core/tool-execution-start-barrier';
import {
POSTGRES_DEFINITION_RETRYABLE_SQL_STATES,
POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS,
configurePostgresDefinitionTransaction,
postgresRequiredInteger,
postgresRequiredJsonObject,
postgresRequiredString,
postgresSqlState,
rollbackPostgresDefinitionTransaction,
} from '../repository/definitionRepositorySupport';
type Row = Record<string, unknown>;
type Queryable = Pick<PostgresQueryable, 'query'>;
const IDENTITY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
const TERMINAL_RUN_STATUSES = new Set([
'succeeded',
'failed',
'cancelled',
'timed_out',
]);
const CATALOG_TRANSACTION_LOCK = 'SELECT pg_advisory_xact_lock(190397473, 3)';
const COMPLETION_SELECT = `
completion.completion_json AS "completionJson",
completion.artifact_json AS "artifactJson",
completion.start_id AS "storedStartId",
completion.artifact_id AS "storedArtifactId",
completion.project_id AS "storedProjectId",
completion.run_id AS "storedRunId",
completion.step_run_id AS "storedStepRunId",
completion.started_step_run_version AS "storedStartedStepRunVersion",
completion.completed_step_run_version AS "storedCompletedStepRunVersion",
completion.barrier_digest AS "storedBarrierDigest",
completion.adapter_digest AS "storedAdapterDigest",
completion.output_digest AS "storedOutputDigest",
completion.execution_result_digest AS "storedExecutionResultDigest",
completion.artifact_digest AS "storedArtifactDigest",
completion.key_id AS "storedKeyId",
completion.algorithm AS "storedAlgorithm",
completion.plaintext_bytes AS "storedPlaintextBytes",
completion.step_run_mutation_id AS "storedMutationId",
completion.step_run_mutation_digest AS "storedMutationDigest",
completion.completed_step_run_digest AS "storedCompletedStepRunDigest",
completion.run_event_id AS "storedRunEventId",
completion.completed_at_ms AS "storedCompletedAtMs",
completion.completion_digest AS "storedCompletionDigest",
binding.artifact_digest AS "bindingArtifactDigest",
binding.catalog_generation AS "bindingCatalogGeneration",
binding.catalog_digest AS "bindingCatalogDigest",
binding.key_id AS "bindingKeyId",
binding.material_proof AS "bindingMaterialProof",
binding.binding_digest AS "bindingDigest",
catalog.catalog_json AS "catalogJson",
barrier.barrier_digest AS "joinedBarrierDigest",
mutation.mutation_digest AS "joinedMutationDigest",
mutation.step_run_digest AS "joinedCompletedStepRunDigest",
event.id AS "joinedRunEventId"
`;
function unavailable(cause?: unknown): ToolExecutionCompletionUnavailableError {
return new ToolExecutionCompletionUnavailableError({
cause: cause instanceof Error ? cause : undefined,
});
}
function requiredText(row: Row, key: string): string {
return postgresRequiredString(row[key], unavailable);
}
function requiredInteger(row: Row, key: string): number {
return postgresRequiredInteger(row[key], unavailable);
}
function requiredJson(row: Row, key: string): Record<string, unknown> {
return postgresRequiredJsonObject(row[key], unavailable);
}
function identity(value: unknown, label: string): string {
if (typeof value !== 'string' || !IDENTITY_PATTERN.test(value)) {
throw new InvalidToolExecutionCompletionError(`${label} is invalid`);
}
return value;
}
function constraintError(error: unknown): boolean {
const state = postgresSqlState(error);
return state === '23503' || state === '23505' || state === '23514';
}
function mapStorageError(error: unknown): Error {
if (
error instanceof InvalidToolExecutionCompletionError ||
error instanceof ToolExecutionCompletionConflictError ||
error instanceof ToolExecutionCompletionUnavailableError
) {
return error;
}
return constraintError(error)
? new ToolExecutionCompletionConflictError()
: unavailable(error);
}
function valuesFromRow(row: Row): Readonly<{
artifact: Readonly<ToolExecutionResultArtifact>;
binding: Readonly<ToolExecutionResultKeyBinding>;
completion: Readonly<ToolExecutionCompletionRecord>;
}> {
let completion: Readonly<ToolExecutionCompletionRecord>;
let artifact: Readonly<ToolExecutionResultArtifact>;
let binding: Readonly<ToolExecutionResultKeyBinding>;
let catalog: Readonly<ToolResultKeyCatalogRecord>;
try {
completion = normalizeToolExecutionCompletionRecord(
requiredJson(
row,
'completionJson',
) as unknown as ToolExecutionCompletionRecord,
);
artifact = normalizeToolExecutionResultArtifact(
requiredJson(
row,
'artifactJson',
) as unknown as ToolExecutionResultArtifact,
);
binding = normalizeToolExecutionResultKeyBinding({
schema: 'qinglong/tool-execution-result-key-binding@v1',
startId: completion.startId,
artifactId: artifact.artifactId,
artifactDigest: requiredText(row, 'bindingArtifactDigest'),
catalogGeneration: requiredInteger(row, 'bindingCatalogGeneration'),
catalogDigest: requiredText(row, 'bindingCatalogDigest'),
keyId: requiredText(row, 'bindingKeyId'),
materialProof: requiredText(row, 'bindingMaterialProof'),
bindingDigest: requiredText(row, 'bindingDigest'),
});
catalog = normalizeToolResultKeyCatalogRecord(
requiredJson(row, 'catalogJson') as unknown as ToolResultKeyCatalogRecord,
);
} catch {
throw unavailable();
}
if (
Buffer.byteLength(JSON.stringify(completion), 'utf8') >
MAX_TOOL_EXECUTION_COMPLETION_JSON_BYTES ||
Buffer.byteLength(JSON.stringify(artifact), 'utf8') >
MAX_TOOL_EXECUTION_RESULT_ARTIFACT_JSON_BYTES ||
completion.startId !== requiredText(row, 'storedStartId') ||
completion.resultArtifact.artifactId !==
requiredText(row, 'storedArtifactId') ||
completion.projectId !== requiredText(row, 'storedProjectId') ||
completion.runId !== requiredText(row, 'storedRunId') ||
completion.stepRunId !== requiredText(row, 'storedStepRunId') ||
completion.startedStepRunVersion !==
requiredInteger(row, 'storedStartedStepRunVersion') ||
completion.completedStepRunVersion !==
requiredInteger(row, 'storedCompletedStepRunVersion') ||
completion.barrierDigest !== requiredText(row, 'storedBarrierDigest') ||
completion.adapterDigest !== requiredText(row, 'storedAdapterDigest') ||
completion.resultArtifact.outputDigest !==
requiredText(row, 'storedOutputDigest') ||
completion.resultArtifact.executionResultDigest !==
requiredText(row, 'storedExecutionResultDigest') ||
completion.resultArtifact.artifactDigest !==
requiredText(row, 'storedArtifactDigest') ||
completion.stepRunMutationId !== requiredText(row, 'storedMutationId') ||
completion.stepRunMutationDigest !==
requiredText(row, 'storedMutationDigest') ||
completion.completedStepRunDigest !==
requiredText(row, 'storedCompletedStepRunDigest') ||
completion.runEventId !== requiredText(row, 'storedRunEventId') ||
completion.completedAtMs !== requiredInteger(row, 'storedCompletedAtMs') ||
completion.completionDigest !==
requiredText(row, 'storedCompletionDigest') ||
artifact.artifactId !== completion.resultArtifact.artifactId ||
artifact.artifactDigest !== completion.resultArtifact.artifactDigest ||
artifact.projectId !== completion.projectId ||
artifact.startId !== completion.startId ||
artifact.runId !== completion.runId ||
artifact.stepRunId !== completion.stepRunId ||
artifact.barrierDigest !== completion.barrierDigest ||
artifact.adapterDigest !== completion.adapterDigest ||
artifact.outputDigest !== completion.resultArtifact.outputDigest ||
artifact.executionResultDigest !==
completion.resultArtifact.executionResultDigest ||
artifact.keyId !== requiredText(row, 'storedKeyId') ||
artifact.algorithm !== requiredText(row, 'storedAlgorithm') ||
artifact.plaintextBytes !== requiredInteger(row, 'storedPlaintextBytes') ||
artifact.sealedAtMs !== completion.completedAtMs ||
binding.startId !== completion.startId ||
binding.artifactId !== artifact.artifactId ||
binding.artifactDigest !== artifact.artifactDigest ||
binding.catalogGeneration !== catalog.generation ||
binding.catalogDigest !== catalog.catalogDigest ||
binding.keyId !== artifact.keyId ||
catalog.activeKeyId !== binding.keyId ||
requireActiveToolResultKey(catalog).materialProof !==
binding.materialProof ||
completion.barrierDigest !== requiredText(row, 'joinedBarrierDigest') ||
completion.stepRunMutationDigest !==
requiredText(row, 'joinedMutationDigest') ||
completion.completedStepRunDigest !==
requiredText(row, 'joinedCompletedStepRunDigest') ||
completion.runEventId !== requiredText(row, 'joinedRunEventId')
) {
throw unavailable();
}
return Object.freeze({ artifact, binding, completion });
}
async function findRows(
queryable: Queryable,
where: string,
values: readonly unknown[],
): Promise<readonly Row[]> {
const result = await queryable.query<Row>(
`SELECT ${COMPLETION_SELECT}
FROM "ql3"."tool_execution_completions" AS completion
JOIN "ql3"."tool_execution_start_barriers" AS barrier
ON barrier.start_id = completion.start_id
JOIN "ql3"."step_run_mutations" AS mutation
ON mutation.mutation_id = completion.step_run_mutation_id
JOIN "ql3"."run_events" AS event
ON event.id = completion.run_event_id
LEFT JOIN "ql3"."tool_execution_result_key_bindings" AS binding
ON binding.start_id = completion.start_id
LEFT JOIN "ql3"."tool_result_key_catalog_generations" AS catalog
ON catalog.authority = binding.catalog_authority
AND catalog.generation = binding.catalog_generation
WHERE ${where}
LIMIT 2`,
values,
);
return result.rows;
}
async function updateStepRun(
client: PostgresClient,
mutation: Readonly<StepRunMutation>,
): Promise<void> {
const step = mutation.stepRun;
const result = await client.query(
`UPDATE "ql3"."step_runs"
SET status = $1, version = $2, attempt_count = $3,
output_ref = $4, approval_request_id = $5, ready_at_ms = $6,
started_at_ms = $7, finished_at_ms = $8, result_code = $9,
error_summary = $10, updated_at_ms = $11,
last_mutation_id = $12, step_run_digest = $13,
step_run_json = $14::jsonb
WHERE id = $15 AND run_id = $16 AND version = $17
AND step_run_digest = $18 AND status = $19`,
[
step.status,
step.version,
step.attemptCount,
step.outputRef,
step.approvalRequestId,
step.readyAtMs,
step.startedAtMs,
step.finishedAtMs,
step.resultCode,
step.errorSummary,
step.updatedAtMs,
step.lastMutationId,
step.stepRunDigest,
JSON.stringify(step),
step.id,
step.runId,
mutation.expectedStepRunVersion,
mutation.expectedStepRunDigest,
mutation.previousStatus,
],
);
if (result.rowCount !== 1) {
throw new ToolExecutionCompletionConflictError();
}
}
async function updateRun(
client: PostgresClient,
mutation: Readonly<StepRunMutation>,
): Promise<void> {
const result = await client.query(
`UPDATE "ql3"."runs"
SET version = version + 1, event_sequence = event_sequence + 1
WHERE id = $1 AND version = $2 AND event_sequence = $3`,
[
mutation.runId,
mutation.expectedRunVersion,
mutation.expectedRunEventSequence,
],
);
if (result.rowCount !== 1) {
throw new ToolExecutionCompletionConflictError();
}
}
async function insertRunEvent(
client: PostgresClient,
mutation: Readonly<StepRunMutation>,
): Promise<void> {
const event = mutation.event;
await client.query(
`INSERT INTO "ql3"."run_events" (
id, run_id, sequence, type, dedupe_key, actor_type, actor_id,
attempt_id, step_run_id, payload, created_at_ms
) VALUES (
$1, $2, $3, $4, $5, $6, $7, NULL, $8, $9::jsonb, $10
)`,
[
event.id,
event.runId,
event.sequence,
event.type,
event.dedupeKey,
event.actorType,
event.actorId ?? null,
mutation.stepRun.id,
JSON.stringify(event.payload),
event.createdAtMs,
],
);
}
async function insertMutation(
client: PostgresClient,
mutation: Readonly<StepRunMutation>,
): Promise<void> {
await client.query(
`INSERT INTO "ql3"."step_run_mutations" (
mutation_id, mutation_digest, run_id, step_run_id,
step_run_digest, event_id, event_sequence, run_version,
step_run_json, committed_at_ms
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb,
floor(
extract(epoch FROM transaction_timestamp()) * 1000
)::bigint
)`,
[
mutation.mutationId,
mutation.mutationDigest,
mutation.runId,
mutation.stepRun.id,
mutation.stepRun.stepRunDigest,
mutation.event.id,
mutation.event.sequence,
mutation.expectedRunVersion + 1,
JSON.stringify(mutation.stepRun),
],
);
}
async function insertCompletion(
client: PostgresClient,
completion: Readonly<ToolExecutionCompletionRecord>,
artifact: Readonly<ToolExecutionResultArtifact>,
): Promise<void> {
await client.query(
`INSERT INTO "ql3"."tool_execution_completions" (
start_id, artifact_id, project_id, run_id, step_run_id,
started_step_run_version, completed_step_run_version,
barrier_digest, adapter_digest, output_digest,
execution_result_digest, artifact_digest, key_id, algorithm,
plaintext_bytes, step_run_mutation_id, step_run_mutation_digest,
completed_step_run_digest, run_event_id, completed_at_ms,
completion_digest, artifact_json, completion_json
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14,
$15, $16, $17, $18, $19, $20, $21, $22::jsonb, $23::jsonb
)`,
[
completion.startId,
artifact.artifactId,
completion.projectId,
completion.runId,
completion.stepRunId,
completion.startedStepRunVersion,
completion.completedStepRunVersion,
completion.barrierDigest,
completion.adapterDigest,
completion.resultArtifact.outputDigest,
completion.resultArtifact.executionResultDigest,
artifact.artifactDigest,
artifact.keyId,
artifact.algorithm,
artifact.plaintextBytes,
completion.stepRunMutationId,
completion.stepRunMutationDigest,
completion.completedStepRunDigest,
completion.runEventId,
completion.completedAtMs,
completion.completionDigest,
JSON.stringify(artifact),
JSON.stringify(completion),
],
);
}
async function resultKeyBindingForCurrentCatalog(
client: PostgresClient,
command: Readonly<ToolExecutionCompletionCommand>,
): Promise<Readonly<ToolExecutionResultKeyBinding>> {
const result = await client.query<Row>(
`SELECT catalog_json AS "catalogJson"
FROM "ql3"."tool_result_key_catalog_generations"
WHERE authority = 'trusted-tool-results'
ORDER BY generation DESC
LIMIT 1`,
);
if (result.rows.length !== 1) {
throw new ToolExecutionCompletionConflictError();
}
let catalog: Readonly<ToolResultKeyCatalogRecord>;
try {
catalog = normalizeToolResultKeyCatalogRecord(
requiredJson(
result.rows[0]!,
'catalogJson',
) as unknown as ToolResultKeyCatalogRecord,
);
} catch {
throw unavailable();
}
const active = requireActiveToolResultKey(catalog);
if (
JSON.stringify(toolResultKeyCatalogFence(catalog, active)) !==
JSON.stringify(command.resultKeyCatalogFence)
) {
throw new ToolExecutionCompletionConflictError();
}
return toolExecutionResultKeyBinding(command);
}
async function insertResultKeyBinding(
client: PostgresClient,
binding: Readonly<ToolExecutionResultKeyBinding>,
): Promise<void> {
await client.query(
`INSERT INTO "ql3"."tool_execution_result_key_bindings" (
start_id, artifact_id, artifact_digest, catalog_authority,
catalog_generation, catalog_digest, key_id, material_proof,
binding_digest
) VALUES (
$1, $2, $3, 'trusted-tool-results', $4, $5, $6, $7, $8
)`,
[
binding.startId,
binding.artifactId,
binding.artifactDigest,
binding.catalogGeneration,
binding.catalogDigest,
binding.keyId,
binding.materialProof,
binding.bindingDigest,
],
);
}
export class PostgresToolExecutionCompletionRepository
implements ToolExecutionCompletionRepository
{
constructor(private readonly pool: PostgresPool) {
if (
!pool ||
typeof pool.query !== 'function' ||
typeof pool.connect !== 'function'
) {
throw unavailable();
}
}
async findByStartId(
startIdValue: string,
): Promise<Readonly<ToolExecutionCompletionRecord> | null> {
const startId = identity(startIdValue, 'start id');
try {
const rows = await findRows(this.pool, 'completion.start_id = $1', [
startId,
]);
if (rows.length > 1) throw unavailable();
return rows[0] ? valuesFromRow(rows[0]).completion : null;
} catch (error) {
throw mapStorageError(error);
}
}
async findResultArtifact(
artifactIdValue: string,
): Promise<Readonly<ToolExecutionResultArtifact> | null> {
const artifactId = identity(artifactIdValue, 'result Artifact id');
try {
const rows = await findRows(this.pool, 'completion.artifact_id = $1', [
artifactId,
]);
if (rows.length > 1) throw unavailable();
return rows[0] ? valuesFromRow(rows[0]).artifact : null;
} catch (error) {
throw mapStorageError(error);
}
}
async commit(
commandValue: ToolExecutionCompletionCommand,
): Promise<Readonly<CommitToolExecutionCompletionResult>> {
const command = normalizeToolExecutionCompletionCommand(commandValue);
const completion = toolExecutionCompletionRecord(command);
for (
let attempt = 0;
attempt < POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS;
attempt += 1
) {
let client: PostgresClient;
try {
client = await this.pool.connect();
} catch (error) {
throw unavailable(error);
}
let began = false;
try {
await configurePostgresDefinitionTransaction(client);
began = true;
await client.query(CATALOG_TRANSACTION_LOCK);
const existing = await findRows(
client,
`completion.start_id = $1
OR completion.artifact_id = $2
OR completion.step_run_mutation_id = $3
OR completion.run_event_id = $4`,
[
completion.startId,
completion.resultArtifact.artifactId,
completion.stepRunMutationId,
completion.runEventId,
],
);
if (existing.length > 1) {
throw new ToolExecutionCompletionConflictError();
}
if (existing[0]) {
const stored = valuesFromRow(existing[0]);
if (
JSON.stringify(stored.completion) !== JSON.stringify(completion) ||
JSON.stringify(stored.artifact) !==
JSON.stringify(command.resultArtifact) ||
JSON.stringify(stored.binding) !==
JSON.stringify(toolExecutionResultKeyBinding(command))
) {
throw new ToolExecutionCompletionConflictError();
}
await client.query('COMMIT');
began = false;
return Object.freeze({
status: 'existing',
completion: stored.completion,
});
}
const failureConflict = await client.query(
`SELECT 1
FROM "ql3"."tool_execution_failure_completions"
WHERE start_id = $1 OR step_run_mutation_id = $2
OR run_event_id = $3
OR (
run_id = $4 AND step_run_id = $5
AND completed_step_run_version = $6
)
LIMIT 1`,
[
completion.startId,
completion.stepRunMutationId,
completion.runEventId,
completion.runId,
completion.stepRunId,
completion.completedStepRunVersion,
],
);
if (failureConflict.rows.length > 0) {
throw new ToolExecutionCompletionConflictError();
}
const mutation = command.stepRunMutation;
const resultKeyBinding = await resultKeyBindingForCurrentCatalog(
client,
command,
);
const current = await client.query<Row>(
`SELECT
barrier.barrier_json AS "barrierJson",
start_mutation.run_version AS "startedRunVersion",
start_mutation.event_sequence AS "startedEventSequence",
step.kind AS "stepKind", step.status AS "stepStatus",
step.version AS "stepVersion",
step.step_run_digest AS "stepDigest",
run.project_id AS "projectId", run.status AS "runStatus",
run.version AS "runVersion",
run.event_sequence AS "runEventSequence"
FROM "ql3"."tool_execution_start_barriers" AS barrier
JOIN "ql3"."step_run_mutations" AS start_mutation
ON start_mutation.mutation_id = barrier.step_run_mutation_id
JOIN "ql3"."step_runs" AS step
ON step.id = barrier.step_run_id
AND step.run_id = barrier.run_id
JOIN "ql3"."runs" AS run ON run.id = barrier.run_id
WHERE barrier.start_id = $1
LIMIT 2
FOR UPDATE OF step, run`,
[completion.startId],
);
const row = current.rows[0];
let storedBarrier;
try {
storedBarrier = row
? normalizeToolExecutionStartBarrierRecord(
requiredJson(
row,
'barrierJson',
) as unknown as ToolExecutionCompletionCommand['barrier'],
)
: null;
} catch {
throw unavailable();
}
if (
current.rows.length !== 1 ||
!row ||
!storedBarrier ||
JSON.stringify(storedBarrier) !== JSON.stringify(command.barrier) ||
requiredInteger(row, 'startedRunVersion') !==
mutation.expectedRunVersion ||
requiredInteger(row, 'startedEventSequence') !==
mutation.expectedRunEventSequence ||
requiredText(row, 'stepKind') !== 'tool' ||
requiredText(row, 'stepStatus') !== 'running' ||
requiredInteger(row, 'stepVersion') !==
mutation.expectedStepRunVersion ||
requiredText(row, 'stepDigest') !== mutation.expectedStepRunDigest ||
requiredText(row, 'projectId') !== completion.projectId ||
requiredInteger(row, 'runVersion') !== mutation.expectedRunVersion ||
requiredInteger(row, 'runEventSequence') !==
mutation.expectedRunEventSequence ||
TERMINAL_RUN_STATUSES.has(requiredText(row, 'runStatus'))
) {
throw new ToolExecutionCompletionConflictError();
}
await updateStepRun(client, mutation);
await updateRun(client, mutation);
await insertRunEvent(client, mutation);
await insertMutation(client, mutation);
await insertCompletion(client, completion, command.resultArtifact);
await insertResultKeyBinding(client, resultKeyBinding);
await client.query('COMMIT');
began = false;
return Object.freeze({ status: 'created', completion });
} catch (error) {
if (began) await rollbackPostgresDefinitionTransaction(client);
const state = postgresSqlState(error);
if (
state &&
POSTGRES_DEFINITION_RETRYABLE_SQL_STATES.has(state) &&
attempt + 1 < POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS
) {
continue;
}
throw mapStorageError(error);
} finally {
client.release();
}
}
throw unavailable();
}
}
@@ -0,0 +1,476 @@
import {
InvalidToolExecutionEvidenceError,
ToolExecutionEvidenceConflictError,
ToolExecutionEvidenceUnavailableError,
normalizeListToolExecutionEvidenceQuery,
normalizeListToolExecutionEvidenceResult,
normalizeToolExecutionEvidenceBundle,
type ListToolExecutionEvidenceQuery,
type ListToolExecutionEvidenceResult,
type PrepareToolExecutionEvidenceResult,
type ToolExecutionEvidenceBundle,
type ToolExecutionEvidenceRepository,
} from '@qinglong/runtime-core/tool-execution-evidence';
import type {
PostgresClient,
PostgresPool,
PostgresQueryable,
} from '@qinglong/runtime-core';
import {
POSTGRES_DEFINITION_RETRYABLE_SQL_STATES,
POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS,
configurePostgresDefinitionTransaction,
postgresRequiredInteger,
postgresRequiredJsonObject,
postgresRequiredString,
postgresSqlState,
rollbackPostgresDefinitionTransaction,
} from '../repository/definitionRepositorySupport';
type Row = Record<string, unknown>;
const TRACE_ID_PATTERN = /^[0-9a-f]{32}$/;
const SPAN_ID_PATTERN = /^[0-9a-f]{16}$/;
const AUDIT_EVENT_ID_PATTERN =
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
const EVIDENCE_SELECT = `
trace.trace_json AS "traceJson",
receipt.audit_json AS "auditJson",
receipt.receipt_json AS "receiptJson",
trace.trace_id AS "storedTraceId",
trace.span_id AS "storedSpanId",
trace.parent_span_id AS "storedParentSpanId",
trace.project_id AS "traceProjectId",
trace.run_id AS "traceRunId",
trace.step_run_id AS "traceStepRunId",
trace.invocation_plan_digest AS "traceInvocationPlanDigest",
trace.binding_digest AS "traceBindingDigest",
trace.adapter_digest AS "storedAdapterDigest",
trace.redaction_contract_digest AS "storedRedactionContractDigest",
trace.audit_contract_digest AS "storedAuditContractDigest",
trace.created_at_ms AS "traceCreatedAtMs",
trace.trace_digest AS "storedTraceDigest",
receipt.event_id AS "storedEventId",
receipt.project_id AS "receiptProjectId",
receipt.run_id AS "receiptRunId",
receipt.step_run_id AS "receiptStepRunId",
receipt.trace_id AS "receiptTraceId",
receipt.span_id AS "receiptSpanId",
receipt.trace_digest AS "receiptTraceDigest",
receipt.invocation_plan_digest AS "receiptInvocationPlanDigest",
receipt.binding_digest AS "receiptBindingDigest",
receipt.audit_record_digest AS "storedAuditRecordDigest",
receipt.created_at_ms AS "receiptCreatedAtMs",
receipt.receipt_digest AS "storedReceiptDigest"
`;
function unavailable(
cause?: unknown,
): ToolExecutionEvidenceUnavailableError {
return new ToolExecutionEvidenceUnavailableError({
cause: cause instanceof Error ? cause : undefined,
});
}
function identity(value: unknown, pattern: RegExp, label: string): string {
if (typeof value !== 'string' || !pattern.test(value)) {
throw new InvalidToolExecutionEvidenceError(`${label} is invalid`);
}
return value;
}
function requiredText(row: Row, key: string): string {
return postgresRequiredString(row[key], unavailable);
}
function requiredInteger(row: Row, key: string): number {
const value = postgresRequiredInteger(row[key], unavailable);
if (value < 0) throw unavailable();
return value;
}
function nullableText(row: Row, key: string): string | null {
const value = row[key];
if (value === null) return null;
return postgresRequiredString(value, unavailable);
}
function jsonObject(row: Row, key: string): Readonly<Record<string, unknown>> {
return postgresRequiredJsonObject(row[key], unavailable);
}
function bundleFromRow(
row: Row,
): Readonly<ToolExecutionEvidenceBundle> {
let bundle: Readonly<ToolExecutionEvidenceBundle>;
try {
bundle = normalizeToolExecutionEvidenceBundle({
schema: 'qinglong/tool-execution-evidence-bundle@v1',
trace: jsonObject(row, 'traceJson'),
audit: jsonObject(row, 'auditJson'),
receipt: jsonObject(row, 'receiptJson'),
} as unknown as ToolExecutionEvidenceBundle);
} catch {
throw unavailable();
}
const trace = bundle.trace;
const receipt = bundle.receipt;
if (
requiredText(row, 'storedTraceId') !== trace.traceId ||
requiredText(row, 'storedSpanId') !== trace.spanId ||
nullableText(row, 'storedParentSpanId') !== trace.parentSpanId ||
requiredText(row, 'traceProjectId') !== trace.projectId ||
requiredText(row, 'traceRunId') !== trace.runId ||
requiredText(row, 'traceStepRunId') !== trace.stepRunId ||
requiredText(row, 'traceInvocationPlanDigest') !==
trace.invocationPlanDigest ||
requiredText(row, 'traceBindingDigest') !== trace.bindingDigest ||
requiredText(row, 'storedAdapterDigest') !== trace.adapterDigest ||
requiredText(row, 'storedRedactionContractDigest') !==
trace.redactionContractDigest ||
requiredText(row, 'storedAuditContractDigest') !==
trace.auditContractDigest ||
requiredInteger(row, 'traceCreatedAtMs') !== trace.createdAtMs ||
requiredText(row, 'storedTraceDigest') !== trace.traceDigest ||
requiredText(row, 'storedEventId') !== receipt.eventId ||
requiredText(row, 'receiptProjectId') !== receipt.projectId ||
requiredText(row, 'receiptRunId') !== receipt.runId ||
requiredText(row, 'receiptStepRunId') !== receipt.stepRunId ||
requiredText(row, 'receiptTraceId') !== receipt.traceId ||
requiredText(row, 'receiptSpanId') !== receipt.spanId ||
requiredText(row, 'receiptTraceDigest') !== receipt.traceDigest ||
requiredText(row, 'receiptInvocationPlanDigest') !==
receipt.invocationPlanDigest ||
requiredText(row, 'receiptBindingDigest') !== receipt.bindingDigest ||
requiredText(row, 'storedAuditRecordDigest') !==
receipt.auditRecordDigest ||
requiredInteger(row, 'receiptCreatedAtMs') !== receipt.createdAtMs ||
requiredText(row, 'storedReceiptDigest') !== receipt.receiptDigest
) {
throw unavailable();
}
return bundle;
}
function constraintError(error: unknown): boolean {
const state = postgresSqlState(error);
return state === '23503' || state === '23505' || state === '23514';
}
function mapStorageError(error: unknown): Error {
if (
error instanceof InvalidToolExecutionEvidenceError ||
error instanceof ToolExecutionEvidenceConflictError ||
error instanceof ToolExecutionEvidenceUnavailableError
) {
return error;
}
return constraintError(error)
? new ToolExecutionEvidenceConflictError()
: unavailable(error);
}
async function findRows(
queryable: PostgresQueryable,
where: string,
values: readonly unknown[],
limit = 2,
): Promise<readonly Row[]> {
const result = await queryable.query<Row>(
`SELECT ${EVIDENCE_SELECT}
FROM "ql3"."tool_execution_trace_anchors" AS trace
JOIN "ql3"."tool_execution_audit_receipts" AS receipt
ON receipt.trace_id = trace.trace_id
AND receipt.span_id = trace.span_id
WHERE ${where}
LIMIT ${limit}`,
values,
);
return result.rows;
}
async function insertAudit(
client: PostgresClient,
bundle: Readonly<ToolExecutionEvidenceBundle>,
): Promise<void> {
const audit = bundle.audit;
await client.query(
`INSERT INTO "ql3"."security_audit_events" (
event_id, request_id, operation_id, project_id, subject_type,
subject_id, authentication_id, outcome, reasons, project_version,
binding_version, occurred_at_ms
) VALUES (
$1::uuid, $2, $3, $4, $5, $6, $7, $8, $9::jsonb, $10, $11, $12
)`,
[
audit.eventId,
audit.requestId,
audit.operationId,
audit.projectId,
audit.subject?.type ?? null,
audit.subject?.id ?? null,
audit.authenticationId,
audit.outcome,
JSON.stringify(audit.reasons),
audit.fence?.projectVersion ?? null,
audit.fence?.bindingVersion ?? null,
audit.occurredAtMs,
],
);
}
async function insertTraceAndReceipt(
client: PostgresClient,
bundle: Readonly<ToolExecutionEvidenceBundle>,
): Promise<void> {
const trace = bundle.trace;
await client.query(
`INSERT INTO "ql3"."tool_execution_trace_anchors" (
trace_id, span_id, parent_span_id, project_id, run_id, step_run_id,
invocation_plan_digest, binding_digest, adapter_digest,
redaction_contract_digest, audit_contract_digest, created_at_ms,
trace_digest, trace_json
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13,
$14::jsonb
)`,
[
trace.traceId,
trace.spanId,
trace.parentSpanId,
trace.projectId,
trace.runId,
trace.stepRunId,
trace.invocationPlanDigest,
trace.bindingDigest,
trace.adapterDigest,
trace.redactionContractDigest,
trace.auditContractDigest,
trace.createdAtMs,
trace.traceDigest,
JSON.stringify(trace),
],
);
const receipt = bundle.receipt;
await client.query(
`INSERT INTO "ql3"."tool_execution_audit_receipts" (
event_id, project_id, run_id, step_run_id, trace_id, span_id,
trace_digest, invocation_plan_digest, binding_digest,
audit_record_digest, created_at_ms, receipt_digest, audit_json,
receipt_json
) VALUES (
$1::uuid, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12,
$13::jsonb, $14::jsonb
)`,
[
receipt.eventId,
receipt.projectId,
receipt.runId,
receipt.stepRunId,
receipt.traceId,
receipt.spanId,
receipt.traceDigest,
receipt.invocationPlanDigest,
receipt.bindingDigest,
receipt.auditRecordDigest,
receipt.createdAtMs,
receipt.receiptDigest,
JSON.stringify(bundle.audit),
JSON.stringify(receipt),
],
);
}
export class PostgresToolExecutionEvidenceRepository
implements ToolExecutionEvidenceRepository
{
constructor(private readonly pool: PostgresPool) {
if (
!pool ||
typeof pool.query !== 'function' ||
typeof pool.connect !== 'function'
) {
throw unavailable();
}
}
async findByTrace(
traceIdValue: string,
spanIdValue: string,
): Promise<Readonly<ToolExecutionEvidenceBundle> | null> {
const traceId = identity(traceIdValue, TRACE_ID_PATTERN, 'traceId');
const spanId = identity(spanIdValue, SPAN_ID_PATTERN, 'spanId');
try {
const rows = await findRows(
this.pool,
'trace.trace_id = $1 AND trace.span_id = $2',
[traceId, spanId],
);
if (rows.length > 1) throw unavailable();
return rows[0] ? bundleFromRow(rows[0]) : null;
} catch (error) {
throw mapStorageError(error);
}
}
async findByAuditEventId(
eventIdValue: string,
): Promise<Readonly<ToolExecutionEvidenceBundle> | null> {
const eventId = identity(
eventIdValue,
AUDIT_EVENT_ID_PATTERN,
'audit eventId',
);
try {
const rows = await findRows(
this.pool,
'receipt.event_id = $1::uuid',
[eventId],
);
if (rows.length > 1) throw unavailable();
return rows[0] ? bundleFromRow(rows[0]) : null;
} catch (error) {
throw mapStorageError(error);
}
}
async listByRun(
queryValue: ListToolExecutionEvidenceQuery,
): Promise<ListToolExecutionEvidenceResult> {
const query = normalizeListToolExecutionEvidenceQuery(queryValue);
try {
const result = await this.pool.query<Row>(
`SELECT ${EVIDENCE_SELECT}
FROM "ql3"."tool_execution_trace_anchors" AS trace
JOIN "ql3"."tool_execution_audit_receipts" AS receipt
ON receipt.trace_id = trace.trace_id
AND receipt.span_id = trace.span_id
WHERE trace.run_id = $1 AND (
$2::char(32) IS NULL OR trace.created_at_ms > $3 OR
(trace.created_at_ms = $3 AND trace.trace_id > $2) OR
(trace.created_at_ms = $3 AND trace.trace_id = $2
AND trace.span_id > $4)
)
ORDER BY trace.created_at_ms, trace.trace_id, trace.span_id
LIMIT $5`,
[
query.runId,
query.after?.traceId ?? null,
query.after?.createdAtMs ?? 0,
query.after?.spanId ?? '',
query.limit + 1,
],
);
const truncated = result.rows.length > query.limit;
const bundles = result.rows.slice(0, query.limit).map(bundleFromRow);
const last = bundles.at(-1);
return normalizeListToolExecutionEvidenceResult(
{
bundles,
truncated,
...(truncated && last
? {
next: {
createdAtMs: last.trace.createdAtMs,
traceId: last.trace.traceId,
spanId: last.trace.spanId,
},
}
: {}),
},
query,
);
} catch (error) {
throw mapStorageError(error);
}
}
async prepare(
bundleValue: ToolExecutionEvidenceBundle,
): Promise<Readonly<PrepareToolExecutionEvidenceResult>> {
const bundle = normalizeToolExecutionEvidenceBundle(bundleValue);
for (
let attempt = 0;
attempt < POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS;
attempt += 1
) {
let client: PostgresClient;
try {
client = await this.pool.connect();
} catch (error) {
throw unavailable(error);
}
let began = false;
try {
await configurePostgresDefinitionTransaction(client);
began = true;
const existing = await findRows(
client,
'receipt.event_id = $1::uuid OR (trace.trace_id = $2 AND trace.span_id = $3)',
[
bundle.receipt.eventId,
bundle.trace.traceId,
bundle.trace.spanId,
],
3,
);
if (existing.length > 1) {
throw new ToolExecutionEvidenceConflictError();
}
if (existing[0]) {
const stored = bundleFromRow(existing[0]);
if (JSON.stringify(stored) !== JSON.stringify(bundle)) {
throw new ToolExecutionEvidenceConflictError();
}
await client.query('COMMIT');
began = false;
return Object.freeze({ status: 'existing', bundle: stored });
}
const step = await client.query<Row>(
`SELECT step.kind, step.status, run.project_id AS "projectId"
FROM "ql3"."step_runs" AS step
JOIN "ql3"."runs" AS run ON run.id = step.run_id
WHERE step.id = $1 AND step.run_id = $2
LIMIT 2
FOR SHARE OF step, run`,
[bundle.trace.stepRunId, bundle.trace.runId],
);
if (
step.rows.length !== 1 ||
requiredText(step.rows[0]!, 'kind') !== 'tool' ||
!['ready', 'waiting_approval'].includes(
requiredText(step.rows[0]!, 'status'),
) ||
requiredText(step.rows[0]!, 'projectId') !==
bundle.trace.projectId
) {
throw new ToolExecutionEvidenceConflictError();
}
await insertAudit(client, bundle);
await insertTraceAndReceipt(client, bundle);
await client.query('COMMIT');
began = false;
return Object.freeze({ status: 'created', bundle });
} catch (error) {
if (began) await rollbackPostgresDefinitionTransaction(client);
const state = postgresSqlState(error);
if (
((state &&
POSTGRES_DEFINITION_RETRYABLE_SQL_STATES.has(state)) ||
state === '23505') &&
attempt + 1 < POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS
) {
continue;
}
throw mapStorageError(error);
} finally {
client.release();
}
}
throw unavailable();
}
}
@@ -0,0 +1,533 @@
import type {
PostgresClient,
PostgresPool,
PostgresQueryable,
} from '@qinglong/runtime-core';
import {
InvalidToolExecutionFailureCompletionError,
MAX_TOOL_EXECUTION_FAILURE_COMPLETION_JSON_BYTES,
ToolExecutionFailureCompletionConflictError,
ToolExecutionFailureCompletionUnavailableError,
normalizeToolExecutionFailureCompletionCommand,
normalizeToolExecutionFailureCompletionRecord,
toolExecutionFailureCompletionRecord,
type CommitToolExecutionFailureCompletionResult,
type ToolExecutionFailureCompletionCommand,
type ToolExecutionFailureCompletionRecord,
type ToolExecutionFailureCompletionRepository,
} from '@qinglong/runtime-core/tool-execution-failure-completion';
import type { StepRunMutation } from '@qinglong/runtime-core/step-run';
import { normalizeToolExecutionStartBarrierRecord } from '@qinglong/runtime-core/tool-execution-start-barrier';
import {
POSTGRES_DEFINITION_RETRYABLE_SQL_STATES,
POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS,
configurePostgresDefinitionTransaction,
postgresRequiredInteger,
postgresRequiredJsonObject,
postgresRequiredString,
postgresSqlState,
rollbackPostgresDefinitionTransaction,
} from '../repository/definitionRepositorySupport';
type Row = Record<string, unknown>;
type Queryable = Pick<PostgresQueryable, 'query'>;
const IDENTITY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
const TERMINAL_RUN_STATUSES = new Set([
'succeeded',
'failed',
'cancelled',
'timed_out',
]);
const COMPLETION_SELECT = `
completion.completion_json AS "completionJson",
completion.start_id AS "storedStartId",
completion.project_id AS "storedProjectId",
completion.run_id AS "storedRunId",
completion.step_run_id AS "storedStepRunId",
completion.started_step_run_version AS "storedStartedStepRunVersion",
completion.completed_step_run_version AS "storedCompletedStepRunVersion",
completion.barrier_digest AS "storedBarrierDigest",
completion.adapter_digest AS "storedAdapterDigest",
completion.outcome AS "storedOutcome",
completion.result_code AS "storedResultCode",
completion.error_summary AS "storedErrorSummary",
completion.step_run_mutation_id AS "storedMutationId",
completion.step_run_mutation_digest AS "storedMutationDigest",
completion.completed_step_run_digest AS "storedCompletedStepRunDigest",
completion.run_event_id AS "storedRunEventId",
completion.completed_at_ms AS "storedCompletedAtMs",
completion.completion_digest AS "storedCompletionDigest",
barrier.barrier_digest AS "joinedBarrierDigest",
mutation.mutation_digest AS "joinedMutationDigest",
mutation.step_run_digest AS "joinedCompletedStepRunDigest",
event.id AS "joinedRunEventId"
`;
function unavailable(
cause?: unknown,
): ToolExecutionFailureCompletionUnavailableError {
return new ToolExecutionFailureCompletionUnavailableError({
cause: cause instanceof Error ? cause : undefined,
});
}
function requiredText(row: Row, key: string): string {
return postgresRequiredString(row[key], unavailable);
}
function requiredInteger(row: Row, key: string): number {
return postgresRequiredInteger(row[key], unavailable);
}
function requiredJson(row: Row, key: string): Record<string, unknown> {
return postgresRequiredJsonObject(row[key], unavailable);
}
function identity(value: unknown, label: string): string {
if (typeof value !== 'string' || !IDENTITY_PATTERN.test(value)) {
throw new InvalidToolExecutionFailureCompletionError(`${label} is invalid`);
}
return value;
}
function constraintError(error: unknown): boolean {
const state = postgresSqlState(error);
return state === '23503' || state === '23505' || state === '23514';
}
function mapStorageError(error: unknown): Error {
if (
error instanceof InvalidToolExecutionFailureCompletionError ||
error instanceof ToolExecutionFailureCompletionConflictError ||
error instanceof ToolExecutionFailureCompletionUnavailableError
) {
return error;
}
return constraintError(error)
? new ToolExecutionFailureCompletionConflictError()
: unavailable(error);
}
function valuesFromRow(
row: Row,
): Readonly<ToolExecutionFailureCompletionRecord> {
let completion: Readonly<ToolExecutionFailureCompletionRecord>;
try {
completion = normalizeToolExecutionFailureCompletionRecord(
requiredJson(
row,
'completionJson',
) as unknown as ToolExecutionFailureCompletionRecord,
);
} catch {
throw unavailable();
}
if (
Buffer.byteLength(JSON.stringify(completion), 'utf8') >
MAX_TOOL_EXECUTION_FAILURE_COMPLETION_JSON_BYTES ||
completion.startId !== requiredText(row, 'storedStartId') ||
completion.projectId !== requiredText(row, 'storedProjectId') ||
completion.runId !== requiredText(row, 'storedRunId') ||
completion.stepRunId !== requiredText(row, 'storedStepRunId') ||
completion.startedStepRunVersion !==
requiredInteger(row, 'storedStartedStepRunVersion') ||
completion.completedStepRunVersion !==
requiredInteger(row, 'storedCompletedStepRunVersion') ||
completion.barrierDigest !== requiredText(row, 'storedBarrierDigest') ||
completion.adapterDigest !== requiredText(row, 'storedAdapterDigest') ||
completion.outcome !== requiredText(row, 'storedOutcome') ||
completion.resultCode !== requiredText(row, 'storedResultCode') ||
completion.errorSummary !== requiredText(row, 'storedErrorSummary') ||
completion.stepRunMutationId !== requiredText(row, 'storedMutationId') ||
completion.stepRunMutationDigest !==
requiredText(row, 'storedMutationDigest') ||
completion.completedStepRunDigest !==
requiredText(row, 'storedCompletedStepRunDigest') ||
completion.runEventId !== requiredText(row, 'storedRunEventId') ||
completion.completedAtMs !== requiredInteger(row, 'storedCompletedAtMs') ||
completion.completionDigest !==
requiredText(row, 'storedCompletionDigest') ||
completion.barrierDigest !== requiredText(row, 'joinedBarrierDigest') ||
completion.stepRunMutationDigest !==
requiredText(row, 'joinedMutationDigest') ||
completion.completedStepRunDigest !==
requiredText(row, 'joinedCompletedStepRunDigest') ||
completion.runEventId !== requiredText(row, 'joinedRunEventId')
) {
throw unavailable();
}
return completion;
}
async function findRows(
queryable: Queryable,
where: string,
values: readonly unknown[],
): Promise<readonly Row[]> {
const result = await queryable.query<Row>(
`SELECT ${COMPLETION_SELECT}
FROM "ql3"."tool_execution_failure_completions" AS completion
JOIN "ql3"."tool_execution_start_barriers" AS barrier
ON barrier.start_id = completion.start_id
JOIN "ql3"."step_run_mutations" AS mutation
ON mutation.mutation_id = completion.step_run_mutation_id
JOIN "ql3"."run_events" AS event
ON event.id = completion.run_event_id
WHERE ${where}
LIMIT 2`,
values,
);
return result.rows;
}
async function updateStepRun(
client: PostgresClient,
mutation: Readonly<StepRunMutation>,
): Promise<void> {
const step = mutation.stepRun;
const result = await client.query(
`UPDATE "ql3"."step_runs"
SET status = $1, version = $2, attempt_count = $3,
output_ref = $4, approval_request_id = $5, ready_at_ms = $6,
started_at_ms = $7, finished_at_ms = $8, result_code = $9,
error_summary = $10, updated_at_ms = $11,
last_mutation_id = $12, step_run_digest = $13,
step_run_json = $14::jsonb
WHERE id = $15 AND run_id = $16 AND version = $17
AND step_run_digest = $18 AND status = $19`,
[
step.status,
step.version,
step.attemptCount,
step.outputRef,
step.approvalRequestId,
step.readyAtMs,
step.startedAtMs,
step.finishedAtMs,
step.resultCode,
step.errorSummary,
step.updatedAtMs,
step.lastMutationId,
step.stepRunDigest,
JSON.stringify(step),
step.id,
step.runId,
mutation.expectedStepRunVersion,
mutation.expectedStepRunDigest,
mutation.previousStatus,
],
);
if (result.rowCount !== 1) {
throw new ToolExecutionFailureCompletionConflictError();
}
}
async function updateRun(
client: PostgresClient,
mutation: Readonly<StepRunMutation>,
): Promise<void> {
const result = await client.query(
`UPDATE "ql3"."runs"
SET version = version + 1, event_sequence = event_sequence + 1
WHERE id = $1 AND version = $2 AND event_sequence = $3`,
[
mutation.runId,
mutation.expectedRunVersion,
mutation.expectedRunEventSequence,
],
);
if (result.rowCount !== 1) {
throw new ToolExecutionFailureCompletionConflictError();
}
}
async function insertRunEvent(
client: PostgresClient,
mutation: Readonly<StepRunMutation>,
): Promise<void> {
const event = mutation.event;
await client.query(
`INSERT INTO "ql3"."run_events" (
id, run_id, sequence, type, dedupe_key, actor_type, actor_id,
attempt_id, step_run_id, payload, created_at_ms
) VALUES (
$1, $2, $3, $4, $5, $6, $7, NULL, $8, $9::jsonb, $10
)`,
[
event.id,
event.runId,
event.sequence,
event.type,
event.dedupeKey,
event.actorType,
event.actorId ?? null,
mutation.stepRun.id,
JSON.stringify(event.payload),
event.createdAtMs,
],
);
}
async function insertMutation(
client: PostgresClient,
mutation: Readonly<StepRunMutation>,
): Promise<void> {
await client.query(
`INSERT INTO "ql3"."step_run_mutations" (
mutation_id, mutation_digest, run_id, step_run_id,
step_run_digest, event_id, event_sequence, run_version,
step_run_json, committed_at_ms
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb,
floor(
extract(epoch FROM transaction_timestamp()) * 1000
)::bigint
)`,
[
mutation.mutationId,
mutation.mutationDigest,
mutation.runId,
mutation.stepRun.id,
mutation.stepRun.stepRunDigest,
mutation.event.id,
mutation.event.sequence,
mutation.expectedRunVersion + 1,
JSON.stringify(mutation.stepRun),
],
);
}
async function insertCompletion(
client: PostgresClient,
completion: Readonly<ToolExecutionFailureCompletionRecord>,
): Promise<void> {
await client.query(
`INSERT INTO "ql3"."tool_execution_failure_completions" (
start_id, project_id, run_id, step_run_id,
started_step_run_version, completed_step_run_version,
barrier_digest, adapter_digest, outcome, result_code,
error_summary, step_run_mutation_id, step_run_mutation_digest,
completed_step_run_digest, run_event_id, completed_at_ms,
completion_digest, completion_json
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14,
$15, $16, $17, $18::jsonb
)`,
[
completion.startId,
completion.projectId,
completion.runId,
completion.stepRunId,
completion.startedStepRunVersion,
completion.completedStepRunVersion,
completion.barrierDigest,
completion.adapterDigest,
completion.outcome,
completion.resultCode,
completion.errorSummary,
completion.stepRunMutationId,
completion.stepRunMutationDigest,
completion.completedStepRunDigest,
completion.runEventId,
completion.completedAtMs,
completion.completionDigest,
JSON.stringify(completion),
],
);
}
export class PostgresToolExecutionFailureCompletionRepository
implements ToolExecutionFailureCompletionRepository
{
constructor(private readonly pool: PostgresPool) {
if (
!pool ||
typeof pool.query !== 'function' ||
typeof pool.connect !== 'function'
) {
throw unavailable();
}
}
async findByStartId(
startIdValue: string,
): Promise<Readonly<ToolExecutionFailureCompletionRecord> | null> {
const startId = identity(startIdValue, 'start id');
try {
const rows = await findRows(this.pool, 'completion.start_id = $1', [
startId,
]);
if (rows.length > 1) throw unavailable();
return rows[0] ? valuesFromRow(rows[0]) : null;
} catch (error) {
throw mapStorageError(error);
}
}
async commit(
commandValue: ToolExecutionFailureCompletionCommand,
): Promise<Readonly<CommitToolExecutionFailureCompletionResult>> {
const command =
normalizeToolExecutionFailureCompletionCommand(commandValue);
const completion = toolExecutionFailureCompletionRecord(command);
for (
let attempt = 0;
attempt < POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS;
attempt += 1
) {
let client: PostgresClient;
try {
client = await this.pool.connect();
} catch (error) {
throw unavailable(error);
}
let began = false;
try {
await configurePostgresDefinitionTransaction(client);
began = true;
const existing = await findRows(
client,
`completion.start_id = $1
OR completion.step_run_mutation_id = $2
OR completion.run_event_id = $3
OR (
completion.run_id = $4 AND completion.step_run_id = $5
AND completion.completed_step_run_version = $6
)`,
[
completion.startId,
completion.stepRunMutationId,
completion.runEventId,
completion.runId,
completion.stepRunId,
completion.completedStepRunVersion,
],
);
if (existing.length > 1) {
throw new ToolExecutionFailureCompletionConflictError();
}
if (existing[0]) {
const stored = valuesFromRow(existing[0]);
if (JSON.stringify(stored) !== JSON.stringify(completion)) {
throw new ToolExecutionFailureCompletionConflictError();
}
await client.query('COMMIT');
began = false;
return Object.freeze({
status: 'existing',
completion: stored,
});
}
const successConflict = await client.query(
`SELECT 1
FROM "ql3"."tool_execution_completions"
WHERE start_id = $1 OR step_run_mutation_id = $2
OR run_event_id = $3
OR (
run_id = $4 AND step_run_id = $5
AND completed_step_run_version = $6
)
LIMIT 1`,
[
completion.startId,
completion.stepRunMutationId,
completion.runEventId,
completion.runId,
completion.stepRunId,
completion.completedStepRunVersion,
],
);
if (successConflict.rows.length > 0) {
throw new ToolExecutionFailureCompletionConflictError();
}
const mutation = command.stepRunMutation;
const current = await client.query<Row>(
`SELECT
barrier.barrier_json AS "barrierJson",
start_mutation.run_version AS "startedRunVersion",
start_mutation.event_sequence AS "startedEventSequence",
step.kind AS "stepKind", step.status AS "stepStatus",
step.version AS "stepVersion",
step.step_run_digest AS "stepDigest",
run.project_id AS "projectId", run.status AS "runStatus",
run.version AS "runVersion",
run.event_sequence AS "runEventSequence"
FROM "ql3"."tool_execution_start_barriers" AS barrier
JOIN "ql3"."step_run_mutations" AS start_mutation
ON start_mutation.mutation_id = barrier.step_run_mutation_id
JOIN "ql3"."step_runs" AS step
ON step.id = barrier.step_run_id
AND step.run_id = barrier.run_id
JOIN "ql3"."runs" AS run ON run.id = barrier.run_id
WHERE barrier.start_id = $1
LIMIT 2
FOR UPDATE OF step, run`,
[completion.startId],
);
const row = current.rows[0];
let storedBarrier;
try {
storedBarrier = row
? normalizeToolExecutionStartBarrierRecord(
requiredJson(
row,
'barrierJson',
) as unknown as ToolExecutionFailureCompletionCommand['barrier'],
)
: null;
} catch {
throw unavailable();
}
if (
current.rows.length !== 1 ||
!row ||
!storedBarrier ||
JSON.stringify(storedBarrier) !== JSON.stringify(command.barrier) ||
requiredInteger(row, 'startedRunVersion') !==
mutation.expectedRunVersion ||
requiredInteger(row, 'startedEventSequence') !==
mutation.expectedRunEventSequence ||
requiredText(row, 'stepKind') !== 'tool' ||
requiredText(row, 'stepStatus') !== 'running' ||
requiredInteger(row, 'stepVersion') !==
mutation.expectedStepRunVersion ||
requiredText(row, 'stepDigest') !== mutation.expectedStepRunDigest ||
requiredText(row, 'projectId') !== completion.projectId ||
requiredInteger(row, 'runVersion') !== mutation.expectedRunVersion ||
requiredInteger(row, 'runEventSequence') !==
mutation.expectedRunEventSequence ||
TERMINAL_RUN_STATUSES.has(requiredText(row, 'runStatus'))
) {
throw new ToolExecutionFailureCompletionConflictError();
}
await updateStepRun(client, mutation);
await updateRun(client, mutation);
await insertRunEvent(client, mutation);
await insertMutation(client, mutation);
await insertCompletion(client, completion);
await client.query('COMMIT');
began = false;
return Object.freeze({ status: 'created', completion });
} catch (error) {
if (began) await rollbackPostgresDefinitionTransaction(client);
const state = postgresSqlState(error);
if (
state &&
POSTGRES_DEFINITION_RETRYABLE_SQL_STATES.has(state) &&
attempt + 1 < POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS
) {
continue;
}
throw mapStorageError(error);
} finally {
client.release();
}
}
throw unavailable();
}
}
@@ -0,0 +1,630 @@
import {
InvalidToolExecutionStartBarrierError,
ToolExecutionStartBarrierConflictError,
ToolExecutionStartBarrierUnavailableError,
normalizeToolExecutionStartBarrierRecord,
normalizeToolExecutionStartCommand,
toolExecutionStartBarrierRecord,
type PrepareToolExecutionStartResult,
type ToolExecutionStartBarrierRecord,
type ToolExecutionStartBarrierRepository,
type ToolExecutionStartCommand,
} from '@qinglong/runtime-core/tool-execution-start-barrier';
import type {
PostgresClient,
PostgresPool,
PostgresQueryable,
} from '@qinglong/runtime-core';
import {
POSTGRES_DEFINITION_RETRYABLE_SQL_STATES,
POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS,
configurePostgresDefinitionTransaction,
postgresRequiredInteger,
postgresRequiredJsonObject,
postgresRequiredString,
postgresSqlState,
rollbackPostgresDefinitionTransaction,
} from '../repository/definitionRepositorySupport';
type Row = Record<string, unknown>;
const IDENTITY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
const TERMINAL_RUN_STATUSES = new Set([
'succeeded',
'failed',
'cancelled',
'timed_out',
]);
const BARRIER_SELECT = `
barrier.barrier_json AS "barrierJson",
barrier.start_id AS "storedStartId",
barrier.project_id AS "storedProjectId",
barrier.run_id AS "storedRunId",
barrier.step_run_id AS "storedStepRunId",
barrier.started_step_run_version AS "storedStepRunVersion",
barrier.step_run_mutation_id AS "storedMutationId",
barrier.run_event_id AS "storedRunEventId",
barrier.trace_id AS "storedTraceId",
barrier.span_id AS "storedSpanId",
barrier.audit_event_id::text AS "storedAuditEventId",
barrier.command_digest AS "storedCommandDigest",
barrier.barrier_digest AS "storedBarrierDigest",
barrier.started_at_ms AS "storedStartedAtMs",
mutation.mutation_digest AS "storedMutationDigest",
mutation.step_run_digest AS "storedStartedStepRunDigest",
trace.trace_digest AS "storedTraceDigest",
receipt.receipt_digest AS "storedAuditReceiptDigest",
artifact_binding.project_id AS "storedArtifactProjectId",
artifact_binding.action_ref AS "storedArtifactActionRef",
artifact_binding.input_artifact_id AS "storedInputArtifactId",
artifact_binding.input_artifact_digest AS "storedInputArtifactDigest",
artifact_binding.input_digest AS "storedInputDigest",
artifact_binding.preview_artifact_id AS "storedPreviewArtifactId",
artifact_binding.preview_artifact_digest AS "storedPreviewArtifactDigest",
artifact_binding.action_digest AS "storedArtifactActionDigest",
artifact_binding.preview_digest AS "storedPreviewDigest",
artifact_binding.redaction_contract_digest
AS "storedArtifactRedactionContractDigest",
artifact_binding.bound_at_ms AS "storedArtifactBoundAtMs"
`;
function unavailable(
cause?: unknown,
): ToolExecutionStartBarrierUnavailableError {
return new ToolExecutionStartBarrierUnavailableError({
cause: cause instanceof Error ? cause : undefined,
});
}
function requiredText(row: Row, key: string): string {
return postgresRequiredString(row[key], unavailable);
}
function requiredInteger(row: Row, key: string): number {
const value = postgresRequiredInteger(row[key], unavailable);
if (value < 0) throw unavailable();
return value;
}
function identity(value: unknown, label: string): string {
if (typeof value !== 'string' || !IDENTITY_PATTERN.test(value)) {
throw new InvalidToolExecutionStartBarrierError(`${label} is invalid`);
}
return value;
}
function version(value: unknown): number {
if (
!Number.isSafeInteger(value) ||
(value as number) < 2 ||
(value as number) > 2_147_483_647
) {
throw new InvalidToolExecutionStartBarrierError(
'started StepRun version is invalid',
);
}
return value as number;
}
function barrierFromRow(
row: Row,
): Readonly<ToolExecutionStartBarrierRecord> {
let barrier: Readonly<ToolExecutionStartBarrierRecord>;
try {
barrier = normalizeToolExecutionStartBarrierRecord(
postgresRequiredJsonObject(
row.barrierJson,
unavailable,
) as unknown as ToolExecutionStartBarrierRecord,
);
} catch (error) {
if (error instanceof ToolExecutionStartBarrierUnavailableError) {
throw error;
}
throw unavailable(error);
}
if (
barrier.startId !== requiredText(row, 'storedStartId') ||
barrier.projectId !== requiredText(row, 'storedProjectId') ||
barrier.runId !== requiredText(row, 'storedRunId') ||
barrier.stepRunId !== requiredText(row, 'storedStepRunId') ||
barrier.startedStepRunVersion !==
requiredInteger(row, 'storedStepRunVersion') ||
barrier.stepRunMutationId !== requiredText(row, 'storedMutationId') ||
barrier.runEventId !== requiredText(row, 'storedRunEventId') ||
barrier.traceId !== requiredText(row, 'storedTraceId') ||
barrier.spanId !== requiredText(row, 'storedSpanId') ||
barrier.auditEventId !== requiredText(row, 'storedAuditEventId') ||
barrier.commandDigest !== requiredText(row, 'storedCommandDigest') ||
barrier.barrierDigest !== requiredText(row, 'storedBarrierDigest') ||
barrier.startedAtMs !== requiredInteger(row, 'storedStartedAtMs') ||
barrier.stepRunMutationDigest !==
requiredText(row, 'storedMutationDigest') ||
barrier.startedStepRunDigest !==
requiredText(row, 'storedStartedStepRunDigest') ||
barrier.traceDigest !== requiredText(row, 'storedTraceDigest') ||
barrier.auditReceiptDigest !==
requiredText(row, 'storedAuditReceiptDigest') ||
barrier.projectId !== requiredText(row, 'storedArtifactProjectId') ||
barrier.actionRef !== requiredText(row, 'storedArtifactActionRef') ||
barrier.invocationArtifact.artifactId !==
requiredText(row, 'storedInputArtifactId') ||
barrier.invocationArtifact.artifactDigest !==
requiredText(row, 'storedInputArtifactDigest') ||
barrier.invocationArtifact.inputDigest !==
requiredText(row, 'storedInputDigest') ||
barrier.previewArtifact.artifactId !==
requiredText(row, 'storedPreviewArtifactId') ||
barrier.previewArtifact.artifactDigest !==
requiredText(row, 'storedPreviewArtifactDigest') ||
barrier.previewArtifact.actionDigest !==
requiredText(row, 'storedArtifactActionDigest') ||
barrier.previewArtifact.previewDigest !==
requiredText(row, 'storedPreviewDigest') ||
barrier.previewArtifact.redactionContractDigest !==
requiredText(row, 'storedArtifactRedactionContractDigest') ||
barrier.startedAtMs !== requiredInteger(row, 'storedArtifactBoundAtMs')
) {
throw unavailable();
}
return barrier;
}
function constraintError(error: unknown): boolean {
const state = postgresSqlState(error);
return state === '23503' || state === '23505' || state === '23514';
}
function mapStorageError(error: unknown): Error {
if (
error instanceof InvalidToolExecutionStartBarrierError ||
error instanceof ToolExecutionStartBarrierConflictError ||
error instanceof ToolExecutionStartBarrierUnavailableError
) {
return error;
}
return constraintError(error)
? new ToolExecutionStartBarrierConflictError()
: unavailable(error);
}
async function findRows(
queryable: PostgresQueryable,
where: string,
values: readonly unknown[],
): Promise<readonly Row[]> {
const result = await queryable.query<Row>(
`SELECT ${BARRIER_SELECT}
FROM "ql3"."tool_execution_start_barriers" AS barrier
JOIN "ql3"."step_run_mutations" AS mutation
ON mutation.mutation_id = barrier.step_run_mutation_id
JOIN "ql3"."tool_execution_trace_anchors" AS trace
ON trace.trace_id = barrier.trace_id
AND trace.span_id = barrier.span_id
JOIN "ql3"."tool_execution_audit_receipts" AS receipt
ON receipt.event_id = barrier.audit_event_id
LEFT JOIN "ql3"."tool_execution_start_artifact_bindings"
AS artifact_binding
ON artifact_binding.start_id = barrier.start_id
WHERE ${where}
LIMIT 2`,
values,
);
return result.rows;
}
export class PostgresToolExecutionStartBarrierRepository
implements ToolExecutionStartBarrierRepository
{
constructor(private readonly pool: PostgresPool) {
if (
!pool ||
typeof pool.query !== 'function' ||
typeof pool.connect !== 'function'
) {
throw unavailable();
}
}
async findByStartId(
startIdValue: string,
): Promise<Readonly<ToolExecutionStartBarrierRecord> | null> {
const startId = identity(startIdValue, 'start id');
try {
const rows = await findRows(
this.pool,
'barrier.start_id = $1',
[startId],
);
if (rows.length > 1) throw unavailable();
return rows[0] ? barrierFromRow(rows[0]) : null;
} catch (error) {
throw mapStorageError(error);
}
}
async findByStepRun(
runIdValue: string,
stepRunIdValue: string,
startedStepRunVersionValue: number,
): Promise<Readonly<ToolExecutionStartBarrierRecord> | null> {
const runId = identity(runIdValue, 'Run id');
const stepRunId = identity(stepRunIdValue, 'StepRun id');
const startedStepRunVersion = version(startedStepRunVersionValue);
try {
const rows = await findRows(
this.pool,
`barrier.run_id = $1 AND barrier.step_run_id = $2
AND barrier.started_step_run_version = $3`,
[runId, stepRunId, startedStepRunVersion],
);
if (rows.length > 1) throw unavailable();
return rows[0] ? barrierFromRow(rows[0]) : null;
} catch (error) {
throw mapStorageError(error);
}
}
async prepare(
commandValue: ToolExecutionStartCommand,
): Promise<Readonly<PrepareToolExecutionStartResult>> {
const command = normalizeToolExecutionStartCommand(commandValue);
const barrier = toolExecutionStartBarrierRecord(command);
for (
let attempt = 0;
attempt < POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS;
attempt += 1
) {
let client: PostgresClient;
try {
client = await this.pool.connect();
} catch (error) {
throw unavailable(error);
}
let began = false;
try {
await configurePostgresDefinitionTransaction(client);
began = true;
const existing = await findRows(
client,
`barrier.start_id = $1
OR barrier.step_run_mutation_id = $2
OR barrier.run_event_id = $3
OR (barrier.trace_id = $4 AND barrier.span_id = $5)
OR barrier.audit_event_id = $6::uuid
OR (
barrier.run_id = $7 AND barrier.step_run_id = $8
AND barrier.started_step_run_version = $9
)`,
[
barrier.startId,
barrier.stepRunMutationId,
barrier.runEventId,
barrier.traceId,
barrier.spanId,
barrier.auditEventId,
barrier.runId,
barrier.stepRunId,
barrier.startedStepRunVersion,
],
);
if (existing.length > 1) {
throw new ToolExecutionStartBarrierConflictError();
}
if (existing[0]) {
const stored = barrierFromRow(existing[0]);
if (JSON.stringify(stored) !== JSON.stringify(barrier)) {
throw new ToolExecutionStartBarrierConflictError();
}
await client.query('COMMIT');
began = false;
return Object.freeze({ status: 'existing', barrier: stored });
}
const mutation = command.stepRunMutation;
const current = await client.query<Row>(
`SELECT
step.kind AS "stepKind", step.status AS "stepStatus",
step.version AS "stepVersion",
step.step_run_digest AS "stepDigest",
step.definition_ref AS "definitionRef",
step.definition_digest AS "definitionDigest",
run.project_id AS "projectId", run.status AS "runStatus",
run.version AS "runVersion",
run.event_sequence AS "runEventSequence"
FROM "ql3"."step_runs" AS step
JOIN "ql3"."runs" AS run ON run.id = step.run_id
WHERE step.id = $1 AND step.run_id = $2
LIMIT 2
FOR UPDATE OF step, run`,
[barrier.stepRunId, barrier.runId],
);
const row = current.rows[0];
if (
current.rows.length !== 1 ||
!row ||
requiredText(row, 'stepKind') !== 'tool' ||
requiredText(row, 'stepStatus') !== mutation.previousStatus ||
requiredInteger(row, 'stepVersion') !==
mutation.expectedStepRunVersion ||
requiredText(row, 'stepDigest') !==
mutation.expectedStepRunDigest ||
requiredText(row, 'definitionRef') !==
mutation.stepRun.definitionRef ||
requiredText(row, 'definitionDigest') !==
mutation.stepRun.definitionDigest ||
requiredText(row, 'projectId') !== barrier.projectId ||
requiredInteger(row, 'runVersion') !==
mutation.expectedRunVersion ||
requiredInteger(row, 'runEventSequence') !==
mutation.expectedRunEventSequence ||
TERMINAL_RUN_STATUSES.has(requiredText(row, 'runStatus'))
) {
throw new ToolExecutionStartBarrierConflictError();
}
const quarantineFence = await client.query<Row>(
`SELECT "ql3"."plugin_package_tool_start_allowed"(
$1::varchar, $2::varchar, $3::char(64)
) AS "allowed"`,
[
barrier.projectId,
mutation.stepRun.definitionRef,
mutation.stepRun.definitionDigest,
],
);
if (
quarantineFence.rows.length !== 1 ||
quarantineFence.rows[0]?.allowed !== true
) {
throw new ToolExecutionStartBarrierConflictError();
}
const evidence = command.evidence;
const audit = evidence.audit;
await client.query(
`INSERT INTO "ql3"."security_audit_events" (
event_id, request_id, operation_id, project_id, subject_type,
subject_id, authentication_id, outcome, reasons,
project_version, binding_version, occurred_at_ms
) VALUES (
$1::uuid, $2, $3, $4, $5, $6, $7, $8, $9::jsonb,
$10, $11, $12
)`,
[
audit.eventId,
audit.requestId,
audit.operationId,
audit.projectId,
audit.subject?.type ?? null,
audit.subject?.id ?? null,
audit.authenticationId,
audit.outcome,
JSON.stringify(audit.reasons),
audit.fence?.projectVersion ?? null,
audit.fence?.bindingVersion ?? null,
audit.occurredAtMs,
],
);
const trace = evidence.trace;
await client.query(
`INSERT INTO "ql3"."tool_execution_trace_anchors" (
trace_id, span_id, parent_span_id, project_id, run_id,
step_run_id, invocation_plan_digest, binding_digest,
adapter_digest, redaction_contract_digest,
audit_contract_digest, created_at_ms, trace_digest, trace_json
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13,
$14::jsonb
)`,
[
trace.traceId,
trace.spanId,
trace.parentSpanId,
trace.projectId,
trace.runId,
trace.stepRunId,
trace.invocationPlanDigest,
trace.bindingDigest,
trace.adapterDigest,
trace.redactionContractDigest,
trace.auditContractDigest,
trace.createdAtMs,
trace.traceDigest,
JSON.stringify(trace),
],
);
const receipt = evidence.receipt;
await client.query(
`INSERT INTO "ql3"."tool_execution_audit_receipts" (
event_id, project_id, run_id, step_run_id, trace_id, span_id,
trace_digest, invocation_plan_digest, binding_digest,
audit_record_digest, created_at_ms, receipt_digest,
audit_json, receipt_json
) VALUES (
$1::uuid, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12,
$13::jsonb, $14::jsonb
)`,
[
receipt.eventId,
receipt.projectId,
receipt.runId,
receipt.stepRunId,
receipt.traceId,
receipt.spanId,
receipt.traceDigest,
receipt.invocationPlanDigest,
receipt.bindingDigest,
receipt.auditRecordDigest,
receipt.createdAtMs,
receipt.receiptDigest,
JSON.stringify(evidence.audit),
JSON.stringify(receipt),
],
);
const step = mutation.stepRun;
const updatedStep = await client.query(
`UPDATE "ql3"."step_runs"
SET status = $1, version = $2, attempt_count = $3,
output_ref = $4, approval_request_id = $5, ready_at_ms = $6,
started_at_ms = $7, finished_at_ms = $8, result_code = $9,
error_summary = $10, updated_at_ms = $11,
last_mutation_id = $12, step_run_digest = $13,
step_run_json = $14::jsonb
WHERE id = $15 AND run_id = $16 AND version = $17
AND step_run_digest = $18 AND status = $19`,
[
step.status,
step.version,
step.attemptCount,
step.outputRef,
step.approvalRequestId,
step.readyAtMs,
step.startedAtMs,
step.finishedAtMs,
step.resultCode,
step.errorSummary,
step.updatedAtMs,
step.lastMutationId,
step.stepRunDigest,
JSON.stringify(step),
step.id,
step.runId,
mutation.expectedStepRunVersion,
mutation.expectedStepRunDigest,
mutation.previousStatus,
],
);
if (updatedStep.rowCount !== 1) {
throw new ToolExecutionStartBarrierConflictError();
}
const updatedRun = await client.query(
`UPDATE "ql3"."runs"
SET version = version + 1, event_sequence = event_sequence + 1
WHERE id = $1 AND version = $2 AND event_sequence = $3`,
[
mutation.runId,
mutation.expectedRunVersion,
mutation.expectedRunEventSequence,
],
);
if (updatedRun.rowCount !== 1) {
throw new ToolExecutionStartBarrierConflictError();
}
const event = mutation.event;
await client.query(
`INSERT INTO "ql3"."run_events" (
id, run_id, sequence, type, dedupe_key, actor_type, actor_id,
attempt_id, step_run_id, payload, created_at_ms
) VALUES (
$1, $2, $3, $4, $5, $6, $7, NULL, $8, $9::jsonb, $10
)`,
[
event.id,
event.runId,
event.sequence,
event.type,
event.dedupeKey,
event.actorType,
event.actorId ?? null,
step.id,
JSON.stringify(event.payload),
event.createdAtMs,
],
);
await client.query(
`INSERT INTO "ql3"."step_run_mutations" (
mutation_id, mutation_digest, run_id, step_run_id,
step_run_digest, event_id, event_sequence, run_version,
step_run_json, committed_at_ms
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb,
floor(
extract(epoch FROM transaction_timestamp()) * 1000
)::bigint
)`,
[
mutation.mutationId,
mutation.mutationDigest,
mutation.runId,
step.id,
step.stepRunDigest,
event.id,
event.sequence,
mutation.expectedRunVersion + 1,
JSON.stringify(step),
],
);
await client.query(
`INSERT INTO "ql3"."tool_execution_start_barriers" (
start_id, project_id, run_id, step_run_id,
started_step_run_version, step_run_mutation_id, run_event_id,
trace_id, span_id, audit_event_id, command_digest,
barrier_digest, started_at_ms, barrier_json
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10::uuid, $11, $12,
$13, $14::jsonb
)`,
[
barrier.startId,
barrier.projectId,
barrier.runId,
barrier.stepRunId,
barrier.startedStepRunVersion,
barrier.stepRunMutationId,
barrier.runEventId,
barrier.traceId,
barrier.spanId,
barrier.auditEventId,
barrier.commandDigest,
barrier.barrierDigest,
barrier.startedAtMs,
JSON.stringify(barrier),
],
);
await client.query(
`INSERT INTO "ql3"."tool_execution_start_artifact_bindings" (
start_id, project_id, action_ref,
input_artifact_id, input_artifact_digest, input_digest,
preview_artifact_id, preview_artifact_digest, action_digest,
preview_digest, redaction_contract_digest, bound_at_ms
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12
)`,
[
barrier.startId,
barrier.projectId,
barrier.actionRef,
barrier.invocationArtifact.artifactId,
barrier.invocationArtifact.artifactDigest,
barrier.invocationArtifact.inputDigest,
barrier.previewArtifact.artifactId,
barrier.previewArtifact.artifactDigest,
barrier.previewArtifact.actionDigest,
barrier.previewArtifact.previewDigest,
barrier.previewArtifact.redactionContractDigest,
barrier.startedAtMs,
],
);
await client.query('COMMIT');
began = false;
return Object.freeze({ status: 'created', barrier });
} catch (error) {
if (began) await rollbackPostgresDefinitionTransaction(client);
const state = postgresSqlState(error);
if (
((state &&
POSTGRES_DEFINITION_RETRYABLE_SQL_STATES.has(state)) ||
state === '23505') &&
attempt + 1 < POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS
) {
continue;
}
throw mapStorageError(error);
} finally {
client.release();
}
}
throw unavailable();
}
}
@@ -0,0 +1,340 @@
import type { PostgresClient, PostgresPool } from '@qinglong/runtime-core';
import {
InvalidToolInvocationArtifactError,
MAX_TOOL_INVOCATION_INPUT_ARTIFACT_JSON_BYTES,
MAX_TOOL_INVOCATION_PREVIEW_ARTIFACT_JSON_BYTES,
ToolInvocationArtifactConflictError,
ToolInvocationArtifactUnavailableError,
normalizeToolInvocationInputArtifact,
normalizeToolInvocationPreviewArtifact,
type ToolInvocationArtifactRepository,
type ToolInvocationInputArtifact,
type ToolInvocationPreviewArtifact,
} from '@qinglong/runtime-core/tool-invocation-artifact';
import {
POSTGRES_DEFINITION_RETRYABLE_SQL_STATES,
POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS,
configurePostgresDefinitionTransaction,
postgresRequiredInteger,
postgresRequiredJsonObject,
postgresRequiredString,
postgresSqlState,
rollbackPostgresDefinitionTransaction,
} from '../repository/definitionRepositorySupport';
type Row = Record<string, unknown>;
type Queryable = Pick<PostgresPool, 'query'> | Pick<PostgresClient, 'query'>;
const ARTIFACT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
function unavailable(cause?: unknown): ToolInvocationArtifactUnavailableError {
return new ToolInvocationArtifactUnavailableError({
cause: cause instanceof Error ? cause : undefined,
});
}
function artifactId(value: unknown): string {
if (typeof value !== 'string' || !ARTIFACT_ID_PATTERN.test(value)) {
throw new InvalidToolInvocationArtifactError('artifact id is invalid');
}
return value;
}
function serialize(value: unknown, maximumBytes: number): string {
const json = JSON.stringify(value);
if (Buffer.byteLength(json, 'utf8') > maximumBytes) {
throw new InvalidToolInvocationArtifactError(
'durable Artifact JSON exceeds its budget',
);
}
return json;
}
function mappedError(error: unknown): Error {
if (
error instanceof InvalidToolInvocationArtifactError ||
error instanceof ToolInvocationArtifactConflictError ||
error instanceof ToolInvocationArtifactUnavailableError
) {
return error;
}
const state = postgresSqlState(error);
if (state === '23503' || state === '23505' || state === '23514') {
return new ToolInvocationArtifactConflictError();
}
return unavailable(error);
}
function requiredString(value: unknown): string {
return postgresRequiredString(value, unavailable);
}
function requiredInteger(value: unknown): number {
return postgresRequiredInteger(value, unavailable);
}
export class PostgresToolInvocationArtifactRepository
implements ToolInvocationArtifactRepository
{
constructor(private readonly pool: PostgresPool) {
if (
!pool ||
typeof pool.query !== 'function' ||
typeof pool.connect !== 'function'
) {
throw new TypeError(
'PostgreSQL Tool invocation Artifact repository options are invalid',
);
}
}
private parseInput(row: Row): Readonly<ToolInvocationInputArtifact> {
try {
const artifact = normalizeToolInvocationInputArtifact(
postgresRequiredJsonObject(
row.artifactJson,
unavailable,
) as unknown as ToolInvocationInputArtifact,
);
if (
artifact.artifactId !== requiredString(row.artifactId) ||
artifact.projectId !== requiredString(row.projectId) ||
artifact.actionRef !== requiredString(row.actionRef) ||
artifact.inputDigest !== requiredString(row.inputDigest) ||
artifact.invocationActionDigest !==
requiredString(row.invocationActionDigest) ||
artifact.artifactDigest !== requiredString(row.artifactDigest) ||
artifact.keyId !== requiredString(row.keyId) ||
artifact.algorithm !== requiredString(row.algorithm) ||
artifact.plaintextBytes !== requiredInteger(row.plaintextBytes) ||
artifact.sealedAtMs !== requiredInteger(row.sealedAtMs) ||
Buffer.byteLength(JSON.stringify(artifact), 'utf8') >
MAX_TOOL_INVOCATION_INPUT_ARTIFACT_JSON_BYTES
) {
throw unavailable();
}
return artifact;
} catch (error) {
if (error instanceof ToolInvocationArtifactUnavailableError) throw error;
throw unavailable();
}
}
private parsePreview(
row: Row,
): Readonly<ToolInvocationPreviewArtifact> {
try {
const artifact = normalizeToolInvocationPreviewArtifact(
postgresRequiredJsonObject(
row.artifactJson,
unavailable,
) as unknown as ToolInvocationPreviewArtifact,
);
if (
artifact.artifactId !== requiredString(row.artifactId) ||
artifact.projectId !== requiredString(row.projectId) ||
artifact.actionRef !== requiredString(row.actionRef) ||
artifact.actionDigest !== requiredString(row.actionDigest) ||
artifact.previewDigest !== requiredString(row.previewDigest) ||
artifact.redactionContractDigest !==
requiredString(row.redactionContractDigest) ||
artifact.artifactDigest !== requiredString(row.artifactDigest) ||
artifact.byteLength !== requiredInteger(row.byteLength) ||
artifact.sealedAtMs !== requiredInteger(row.sealedAtMs) ||
Buffer.byteLength(JSON.stringify(artifact), 'utf8') >
MAX_TOOL_INVOCATION_PREVIEW_ARTIFACT_JSON_BYTES
) {
throw unavailable();
}
return artifact;
} catch (error) {
if (error instanceof ToolInvocationArtifactUnavailableError) throw error;
throw unavailable();
}
}
private async storedInput(
queryable: Queryable,
id: string,
): Promise<Readonly<ToolInvocationInputArtifact> | null> {
const result = await queryable.query<Row>(
`SELECT
artifact_id AS "artifactId",
project_id AS "projectId",
action_ref AS "actionRef",
input_digest AS "inputDigest",
invocation_action_digest AS "invocationActionDigest",
artifact_digest AS "artifactDigest",
key_id AS "keyId",
algorithm,
plaintext_bytes AS "plaintextBytes",
sealed_at_ms AS "sealedAtMs",
artifact_json AS "artifactJson"
FROM "ql3"."tool_invocation_input_artifacts"
WHERE artifact_id = $1
LIMIT 2`,
[id],
);
if (result.rows.length > 1) throw unavailable();
return result.rows[0] ? this.parseInput(result.rows[0]) : null;
}
private async storedPreview(
queryable: Queryable,
id: string,
): Promise<Readonly<ToolInvocationPreviewArtifact> | null> {
const result = await queryable.query<Row>(
`SELECT
artifact_id AS "artifactId",
project_id AS "projectId",
action_ref AS "actionRef",
action_digest AS "actionDigest",
preview_digest AS "previewDigest",
redaction_contract_digest AS "redactionContractDigest",
artifact_digest AS "artifactDigest",
byte_length AS "byteLength",
sealed_at_ms AS "sealedAtMs",
artifact_json AS "artifactJson"
FROM "ql3"."tool_invocation_preview_artifacts"
WHERE artifact_id = $1
LIMIT 2`,
[id],
);
if (result.rows.length > 1) throw unavailable();
return result.rows[0] ? this.parsePreview(result.rows[0]) : null;
}
async put(
inputValue: ToolInvocationInputArtifact,
previewValue: ToolInvocationPreviewArtifact,
): Promise<Readonly<{ status: 'inserted' | 'existing' }>> {
const input = normalizeToolInvocationInputArtifact(inputValue);
const preview = normalizeToolInvocationPreviewArtifact(previewValue);
if (
input.projectId !== preview.projectId ||
input.actionRef !== preview.actionRef ||
input.sealedAtMs !== preview.sealedAtMs
) {
throw new ToolInvocationArtifactConflictError();
}
serialize(input, MAX_TOOL_INVOCATION_INPUT_ARTIFACT_JSON_BYTES);
serialize(preview, MAX_TOOL_INVOCATION_PREVIEW_ARTIFACT_JSON_BYTES);
for (
let attempt = 1;
attempt <= POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS;
attempt += 1
) {
let client: PostgresClient;
try {
client = await this.pool.connect();
} catch {
throw unavailable();
}
let began = false;
try {
await configurePostgresDefinitionTransaction(client);
began = true;
const storedInput = await this.storedInput(client, input.artifactId);
const storedPreview = await this.storedPreview(
client,
preview.artifactId,
);
if (storedInput || storedPreview) {
if (
!storedInput ||
!storedPreview ||
JSON.stringify(storedInput) !== JSON.stringify(input) ||
JSON.stringify(storedPreview) !== JSON.stringify(preview)
) {
throw new ToolInvocationArtifactConflictError();
}
await client.query('COMMIT');
began = false;
return Object.freeze({ status: 'existing' });
}
await client.query(
`INSERT INTO "ql3"."tool_invocation_input_artifacts" (
artifact_id, project_id, action_ref, input_digest,
invocation_action_digest, artifact_digest, key_id, algorithm,
plaintext_bytes, sealed_at_ms, artifact_json
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11::jsonb
)`,
[
input.artifactId,
input.projectId,
input.actionRef,
input.inputDigest,
input.invocationActionDigest,
input.artifactDigest,
input.keyId,
input.algorithm,
input.plaintextBytes,
input.sealedAtMs,
JSON.stringify(input),
],
);
await client.query(
`INSERT INTO "ql3"."tool_invocation_preview_artifacts" (
artifact_id, project_id, action_ref, action_digest,
preview_digest, redaction_contract_digest, artifact_digest,
byte_length, sealed_at_ms, artifact_json
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10::jsonb
)`,
[
preview.artifactId,
preview.projectId,
preview.actionRef,
preview.actionDigest,
preview.previewDigest,
preview.redactionContractDigest,
preview.artifactDigest,
preview.byteLength,
preview.sealedAtMs,
JSON.stringify(preview),
],
);
await client.query('COMMIT');
began = false;
return Object.freeze({ status: 'inserted' });
} catch (error) {
if (began) await rollbackPostgresDefinitionTransaction(client);
const state = postgresSqlState(error);
if (
attempt < POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS &&
state !== undefined &&
(POSTGRES_DEFINITION_RETRYABLE_SQL_STATES.has(state) ||
state === '23505')
) {
continue;
}
throw mappedError(error);
} finally {
client.release();
}
}
throw unavailable();
}
async findInput(
value: string,
): Promise<Readonly<ToolInvocationInputArtifact> | null> {
try {
return await this.storedInput(this.pool, artifactId(value));
} catch (error) {
throw mappedError(error);
}
}
async findPreview(
value: string,
): Promise<Readonly<ToolInvocationPreviewArtifact> | null> {
try {
return await this.storedPreview(this.pool, artifactId(value));
} catch (error) {
throw mappedError(error);
}
}
}
@@ -0,0 +1,322 @@
import type { PostgresClient, PostgresPool } from '@qinglong/runtime-core';
import {
InvalidToolResultKeyCatalogError,
ToolResultKeyCatalogConflictError,
ToolResultKeyCatalogUnavailableError,
assertToolResultKeyCatalogTransition,
normalizeToolResultKeyCatalogCommand,
normalizeToolResultKeyCatalogRecord,
type CommitToolResultKeyCatalogResult,
type ToolResultKeyCatalogCommand,
type ToolResultKeyCatalogReader,
type ToolResultKeyCatalogRecord,
type ToolResultKeyCatalogRepository,
} from '@qinglong/runtime-core/tool-result-key-catalog';
import {
normalizeToolResultKeyRetirementReceipt,
type ToolResultKeyRetirementReceipt,
} from '@qinglong/runtime-core/tool-result-rekey';
type Row = Record<string, unknown>;
const AUTHORITY = 'trusted-tool-results';
const CATALOG_TRANSACTION_LOCK = 'SELECT pg_advisory_xact_lock(190397473, 3)';
function unavailable(cause?: unknown): ToolResultKeyCatalogUnavailableError {
return new ToolResultKeyCatalogUnavailableError({
cause: cause instanceof Error ? cause : undefined,
});
}
function postgresConstraint(error: unknown): boolean {
return (
!!error &&
typeof error === 'object' &&
typeof (error as { code?: unknown }).code === 'string' &&
['23503', '23505', '23514'].includes((error as { code: string }).code)
);
}
function mapStorageError(error: unknown): Error {
if (
error instanceof InvalidToolResultKeyCatalogError ||
error instanceof ToolResultKeyCatalogConflictError ||
error instanceof ToolResultKeyCatalogUnavailableError
) {
return error;
}
return postgresConstraint(error)
? new ToolResultKeyCatalogConflictError()
: unavailable(error);
}
function requiredText(row: Row, key: string): string {
const value = row[key];
if (typeof value !== 'string') throw unavailable();
return value;
}
function requiredNullableText(row: Row, key: string): string | null {
const value = row[key];
if (value !== null && typeof value !== 'string') throw unavailable();
return value;
}
function requiredInteger(row: Row, key: string): number {
const value = Number(row[key]);
if (!Number.isSafeInteger(value) || value < 0) throw unavailable();
return value;
}
function recordFromRow(row: Row): Readonly<ToolResultKeyCatalogRecord> {
const value = row.catalogJson;
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw unavailable();
}
let catalog: Readonly<ToolResultKeyCatalogRecord>;
try {
catalog = normalizeToolResultKeyCatalogRecord(
value as ToolResultKeyCatalogRecord,
);
const { committedAtMs: _committedAtMs, ...next } = catalog;
normalizeToolResultKeyCatalogCommand({
schema: 'qinglong/tool-result-key-catalog-command@v1',
expectedGeneration: catalog.generation - 1,
expectedCatalogDigest: catalog.previousCatalogDigest,
next,
commandDigest: requiredText(row, 'commandDigest'),
});
} catch {
throw unavailable();
}
if (
catalog.generation !== requiredInteger(row, 'generation') ||
catalog.previousCatalogDigest !==
requiredNullableText(row, 'previousCatalogDigest') ||
catalog.activeKeyId !== requiredNullableText(row, 'activeKeyId') ||
catalog.mutationKind !== requiredText(row, 'mutationKind') ||
catalog.mutationId !== requiredText(row, 'mutationId') ||
catalog.catalogDigest !== requiredText(row, 'catalogDigest') ||
catalog.committedAtMs !== requiredInteger(row, 'committedAtMs')
) {
throw unavailable();
}
return catalog;
}
async function rollback(client: PostgresClient): Promise<void> {
await client.query('ROLLBACK').catch(() => undefined);
}
export class PostgresToolResultKeyCatalogReader
implements ToolResultKeyCatalogReader
{
constructor(protected readonly pool: PostgresPool) {
if (
!pool ||
typeof pool.query !== 'function' ||
typeof pool.connect !== 'function'
) {
throw new TypeError('PostgreSQL Tool result key catalog pool is invalid');
}
}
protected async rows(
client: Pick<PostgresClient, 'query'> | PostgresPool,
suffix = '',
values: readonly unknown[] = [],
lock = false,
): Promise<readonly Row[]> {
const result = await client.query<Row>(
`SELECT
generation,
previous_catalog_digest AS "previousCatalogDigest",
active_key_id AS "activeKeyId",
mutation_kind AS "mutationKind",
mutation_id AS "mutationId",
catalog_digest AS "catalogDigest",
command_digest AS "commandDigest",
committed_at_ms AS "committedAtMs",
catalog_json AS "catalogJson"
FROM "ql3"."tool_result_key_catalog_generations"
WHERE authority = $1 ${suffix}
ORDER BY generation DESC
LIMIT 2${lock ? ' FOR UPDATE' : ''}`,
[AUTHORITY, ...values],
);
return result.rows;
}
async findCurrent(): Promise<Readonly<ToolResultKeyCatalogRecord> | null> {
try {
const rows = await this.rows(this.pool);
if (
rows.length > 1 &&
requiredInteger(rows[0]!, 'generation') ===
requiredInteger(rows[1]!, 'generation')
) {
throw unavailable();
}
return rows[0] ? recordFromRow(rows[0]) : null;
} catch (error) {
throw mapStorageError(error);
}
}
}
export class PostgresToolResultKeyCatalogRepository
extends PostgresToolResultKeyCatalogReader
implements ToolResultKeyCatalogRepository
{
async append(
commandValue: Readonly<ToolResultKeyCatalogCommand>,
): Promise<Readonly<CommitToolResultKeyCatalogResult>> {
const command = normalizeToolResultKeyCatalogCommand(commandValue);
let client: PostgresClient;
try {
client = await this.pool.connect();
} catch (error) {
throw unavailable(error);
}
try {
await client.query('BEGIN ISOLATION LEVEL SERIALIZABLE');
await client.query(CATALOG_TRANSACTION_LOCK);
const replayRows = await this.rows(
client,
'AND (mutation_id = $2 OR generation = $3 OR catalog_digest = $4)',
[
command.next.mutationId,
command.next.generation,
command.next.catalogDigest,
],
);
if (replayRows.length > 1) {
throw new ToolResultKeyCatalogConflictError();
}
if (replayRows[0]) {
const stored = recordFromRow(replayRows[0]);
const { committedAtMs: _storedTime, ...storedSnapshot } = stored;
if (
requiredText(replayRows[0], 'commandDigest') !==
command.commandDigest ||
JSON.stringify(storedSnapshot) !== JSON.stringify(command.next)
) {
throw new ToolResultKeyCatalogConflictError();
}
await client.query('COMMIT');
return Object.freeze({
status: 'existing' as const,
catalog: stored,
});
}
const currentRows = await this.rows(client);
if (
currentRows.length > 1 &&
requiredInteger(currentRows[0]!, 'generation') ===
requiredInteger(currentRows[1]!, 'generation')
) {
throw new ToolResultKeyCatalogConflictError();
}
const current = currentRows[0] ? recordFromRow(currentRows[0]) : null;
assertToolResultKeyCatalogTransition(current, command);
if (command.next.mutationKind === 'retire') {
if (!current) throw new ToolResultKeyCatalogConflictError();
const retired = command.next.keys.find(
(entry) => entry.state === 'retired',
);
const previous = retired
? current.keys.find((entry) => entry.keyId === retired.keyId)
: null;
if (
!retired ||
!previous ||
retired.retirementReceiptDigest === null
) {
throw new ToolResultKeyCatalogConflictError();
}
const receiptResult = await client.query<Row>(
`SELECT receipt_json AS "receiptJson"
FROM "ql3"."tool_result_key_retirement_receipts"
WHERE receipt_digest = $1`,
[retired.retirementReceiptDigest],
);
let receipt: Readonly<ToolResultKeyRetirementReceipt>;
try {
if (receiptResult.rows.length !== 1) {
throw new Error('retirement receipt count');
}
const receiptJson = receiptResult.rows[0]!.receiptJson;
if (
!receiptJson ||
typeof receiptJson !== 'object' ||
Array.isArray(receiptJson) ||
Buffer.byteLength(JSON.stringify(receiptJson), 'utf8') >
64 * 1024
) {
throw new Error('retirement receipt budget');
}
receipt = normalizeToolResultKeyRetirementReceipt(
receiptJson as ToolResultKeyRetirementReceipt,
);
} catch {
throw new ToolResultKeyCatalogConflictError();
}
if (
receipt.receiptDigest !== retired.retirementReceiptDigest ||
receipt.catalogGeneration !== current.generation ||
receipt.catalogDigest !== current.catalogDigest ||
receipt.keyId !== previous.keyId ||
receipt.materialProof !== previous.materialProof
) {
throw new ToolResultKeyCatalogConflictError();
}
}
const clock = await client.query<Row>(
`SELECT floor(
extract(epoch FROM clock_timestamp()) * 1000
)::bigint AS now`,
);
if (clock.rows.length !== 1) throw unavailable();
const committedAtMs = requiredInteger(clock.rows[0]!, 'now');
const catalog = normalizeToolResultKeyCatalogRecord({
...command.next,
committedAtMs,
});
await client.query(
`INSERT INTO "ql3"."tool_result_key_catalog_generations" (
authority, generation, previous_generation,
previous_catalog_digest, active_key_id, mutation_kind,
mutation_id, catalog_digest, command_digest, committed_at_ms,
catalog_json
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10,
$11::jsonb
)`,
[
AUTHORITY,
catalog.generation,
catalog.generation === 1 ? null : catalog.generation - 1,
catalog.previousCatalogDigest,
catalog.activeKeyId,
catalog.mutationKind,
catalog.mutationId,
catalog.catalogDigest,
command.commandDigest,
catalog.committedAtMs,
JSON.stringify(catalog),
],
);
await client.query('COMMIT');
return Object.freeze({
status: 'created' as const,
catalog,
});
} catch (error) {
await rollback(client);
throw mapStorageError(error);
} finally {
client.release();
}
}
}
@@ -0,0 +1,697 @@
import type {
PostgresClient,
PostgresPool,
PostgresQueryable,
} from '@qinglong/runtime-core';
import {
InvalidToolExecutionResultRekeyError,
ToolExecutionResultRekeyConflictError,
ToolExecutionResultRekeyUnavailableError,
ToolResultKeyRetirementCoverageBuilder,
createToolResultKeyRetirementReceipt,
normalizeToolExecutionResultRekeyCommand,
normalizeToolExecutionResultRekeyOverlay,
normalizeToolResultKeyRetirementReceipt,
normalizeToolResultKeyRetirementReceiptCommand,
type CommitToolExecutionResultRekeyResult,
type CommitToolResultKeyRetirementReceiptResult,
type ToolExecutionResultRekeyCommand,
type ToolExecutionResultRekeyOverlay,
type ToolExecutionResultRekeyReader,
type ToolExecutionResultRekeyRepository,
type ToolResultKeyRetirementReceipt,
type ToolResultKeyRetirementReceiptCommand,
type ToolResultKeyRetirementReceiptRepository,
} from '@qinglong/runtime-core/tool-result-rekey';
import {
normalizeToolResultKeyCatalogRecord,
requireActiveToolResultKey,
toolResultKeyCatalogFence,
type ToolResultKeyCatalogRecord,
} from '@qinglong/runtime-core/tool-result-key-catalog';
import {
POSTGRES_DEFINITION_RETRYABLE_SQL_STATES,
POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS,
configurePostgresDefinitionTransaction,
postgresRequiredInteger,
postgresRequiredJsonObject,
postgresRequiredString,
postgresSqlState,
rollbackPostgresDefinitionTransaction,
} from '../repository/definitionRepositorySupport';
type Row = Record<string, unknown>;
type Queryable = Pick<PostgresQueryable, 'query'>;
const AUTHORITY = 'trusted-tool-results';
const CATALOG_TRANSACTION_LOCK = 'SELECT pg_advisory_xact_lock(190397473, 3)';
const IDENTITY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
const COVERAGE_PAGE_SIZE = 64;
function unavailable(
cause?: unknown,
): ToolExecutionResultRekeyUnavailableError {
return new ToolExecutionResultRekeyUnavailableError({
cause: cause instanceof Error ? cause : undefined,
});
}
function requiredText(row: Row, key: string): string {
return postgresRequiredString(row[key], unavailable);
}
function nullableText(row: Row, key: string): string | null {
const value = row[key];
if (value !== null && typeof value !== 'string') throw unavailable();
return value;
}
function requiredInteger(row: Row, key: string): number {
const value = postgresRequiredInteger(row[key], unavailable);
if (value < 0) throw unavailable();
return value;
}
function postgresConstraint(error: unknown): boolean {
const state = postgresSqlState(error);
return state === '23503' || state === '23505' || state === '23514';
}
function mapStorageError(error: unknown): Error {
if (
error instanceof InvalidToolExecutionResultRekeyError ||
error instanceof ToolExecutionResultRekeyConflictError ||
error instanceof ToolExecutionResultRekeyUnavailableError
) {
return error;
}
return postgresConstraint(error)
? new ToolExecutionResultRekeyConflictError()
: unavailable(error);
}
function overlayFromRow(
row: Row,
): Readonly<ToolExecutionResultRekeyOverlay> {
let overlay: Readonly<ToolExecutionResultRekeyOverlay>;
try {
overlay = normalizeToolExecutionResultRekeyOverlay(
postgresRequiredJsonObject(
row.overlayJson,
unavailable,
) as unknown as ToolExecutionResultRekeyOverlay,
);
} catch {
throw unavailable();
}
if (
Buffer.byteLength(JSON.stringify(overlay), 'utf8') > 384 * 1024 ||
overlay.overlayId !== requiredText(row, 'overlayId') ||
overlay.sourceArtifact.artifactId !== requiredText(row, 'artifactId') ||
overlay.sourceBindingDigest !== requiredText(row, 'sourceBindingDigest') ||
overlay.revision !== requiredInteger(row, 'revision') ||
overlay.previousOverlayDigest !== nullableText(row, 'previousOverlayDigest') ||
overlay.fromKeyId !== requiredText(row, 'fromKeyId') ||
overlay.targetCatalogFence.generation !==
requiredInteger(row, 'targetCatalogGeneration') ||
overlay.targetCatalogFence.catalogDigest !==
requiredText(row, 'targetCatalogDigest') ||
overlay.targetCatalogFence.keyId !== requiredText(row, 'targetKeyId') ||
overlay.targetCatalogFence.materialProof !==
requiredText(row, 'targetMaterialProof') ||
overlay.overlayDigest !== requiredText(row, 'overlayDigest') ||
overlay.rekeyedAtMs !== requiredInteger(row, 'rekeyedAtMs')
) {
throw unavailable();
}
return overlay;
}
function receiptFromRow(
row: Row,
): Readonly<ToolResultKeyRetirementReceipt> {
let receipt: Readonly<ToolResultKeyRetirementReceipt>;
try {
receipt = normalizeToolResultKeyRetirementReceipt(
postgresRequiredJsonObject(
row.receiptJson,
unavailable,
) as unknown as ToolResultKeyRetirementReceipt,
);
} catch {
throw unavailable();
}
if (
Buffer.byteLength(JSON.stringify(receipt), 'utf8') > 64 * 1024 ||
receipt.receiptDigest !== requiredText(row, 'receiptDigest') ||
receipt.catalogGeneration !== requiredInteger(row, 'catalogGeneration') ||
receipt.catalogDigest !== requiredText(row, 'catalogDigest') ||
receipt.keyId !== requiredText(row, 'keyId') ||
receipt.materialProof !== requiredText(row, 'materialProof') ||
receipt.mutationId !== requiredText(row, 'mutationId') ||
receipt.bindingCount !== requiredInteger(row, 'bindingCount') ||
receipt.overlayHeadCount !== requiredInteger(row, 'overlayHeadCount') ||
receipt.uncoveredBindingCount !==
requiredInteger(row, 'uncoveredBindingCount') ||
receipt.uncoveredOverlayHeadCount !==
requiredInteger(row, 'uncoveredOverlayHeadCount') ||
receipt.coverageDigest !== requiredText(row, 'coverageDigest') ||
receipt.createdAtMs !== requiredInteger(row, 'createdAtMs')
) {
throw unavailable();
}
return receipt;
}
async function currentCatalog(
queryable: Queryable,
): Promise<Readonly<ToolResultKeyCatalogRecord>> {
const result = await queryable.query<Row>(
`SELECT catalog_json AS "catalogJson"
FROM "ql3"."tool_result_key_catalog_generations"
WHERE authority = $1
ORDER BY generation DESC
LIMIT 1`,
[AUTHORITY],
);
if (result.rows.length !== 1) throw unavailable();
try {
return normalizeToolResultKeyCatalogRecord(
postgresRequiredJsonObject(
result.rows[0]!.catalogJson,
unavailable,
) as unknown as ToolResultKeyCatalogRecord,
);
} catch {
throw unavailable();
}
}
async function overlayRows(
queryable: Queryable,
suffix: string,
values: readonly unknown[],
): Promise<readonly Row[]> {
const result = await queryable.query<Row>(
`SELECT
overlay.overlay_id AS "overlayId",
overlay.artifact_id AS "artifactId",
overlay.source_binding_digest AS "sourceBindingDigest",
overlay.revision AS "revision",
overlay.previous_overlay_digest AS "previousOverlayDigest",
overlay.from_key_id AS "fromKeyId",
overlay.target_catalog_generation AS "targetCatalogGeneration",
overlay.target_catalog_digest AS "targetCatalogDigest",
overlay.target_key_id AS "targetKeyId",
overlay.target_material_proof AS "targetMaterialProof",
overlay.mutation_id AS "mutationId",
overlay.command_digest AS "commandDigest",
overlay.overlay_digest AS "overlayDigest",
overlay.rekeyed_at_ms AS "rekeyedAtMs",
overlay.overlay_json AS "overlayJson"
FROM "ql3"."tool_execution_result_rekey_overlays" AS overlay
${suffix}
ORDER BY overlay.revision DESC
LIMIT 2`,
values,
);
return result.rows;
}
async function receiptRows(
queryable: Queryable,
suffix: string,
values: readonly unknown[],
): Promise<readonly Row[]> {
const result = await queryable.query<Row>(
`SELECT
receipt_digest AS "receiptDigest",
catalog_generation AS "catalogGeneration",
catalog_digest AS "catalogDigest",
key_id AS "keyId",
material_proof AS "materialProof",
mutation_id AS "mutationId",
command_digest AS "commandDigest",
binding_count AS "bindingCount",
overlay_head_count AS "overlayHeadCount",
uncovered_binding_count AS "uncoveredBindingCount",
uncovered_overlay_head_count AS "uncoveredOverlayHeadCount",
coverage_digest AS "coverageDigest",
created_at_ms AS "createdAtMs",
receipt_json AS "receiptJson"
FROM "ql3"."tool_result_key_retirement_receipts"
${suffix}
ORDER BY created_at_ms DESC
LIMIT 2`,
values,
);
return result.rows;
}
export class PostgresToolResultRekeyReader
implements ToolExecutionResultRekeyReader
{
constructor(protected readonly pool: PostgresPool) {
if (
!pool ||
typeof pool.query !== 'function' ||
typeof pool.connect !== 'function'
) {
throw new TypeError('PostgreSQL Tool result rekey pool is invalid');
}
}
async findHeadByArtifactId(
artifactId: string,
): Promise<Readonly<ToolExecutionResultRekeyOverlay> | null> {
if (!IDENTITY_PATTERN.test(artifactId)) {
throw new InvalidToolExecutionResultRekeyError(
'source Artifact id is invalid',
);
}
try {
const rows = await overlayRows(
this.pool,
`JOIN "ql3"."tool_execution_result_rekey_heads" AS head
ON head.artifact_id = overlay.artifact_id
AND head.revision = overlay.revision
AND head.overlay_digest = overlay.overlay_digest
WHERE overlay.artifact_id = $1`,
[artifactId],
);
if (rows.length > 1) throw unavailable();
return rows[0] ? overlayFromRow(rows[0]) : null;
} catch (error) {
throw mapStorageError(error);
}
}
}
export class PostgresToolResultRekeyRepository
extends PostgresToolResultRekeyReader
implements
ToolExecutionResultRekeyRepository,
ToolResultKeyRetirementReceiptRepository
{
private async transaction<T>(
work: (client: PostgresClient) => Promise<T>,
): Promise<T> {
for (
let attempt = 0;
attempt < POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS;
attempt += 1
) {
let client: PostgresClient;
try {
client = await this.pool.connect();
} catch (error) {
throw mapStorageError(error);
}
let began = false;
try {
await configurePostgresDefinitionTransaction(client);
began = true;
await client.query(CATALOG_TRANSACTION_LOCK);
const result = await work(client);
await client.query('COMMIT');
began = false;
return result;
} catch (error) {
if (began) await rollbackPostgresDefinitionTransaction(client);
const state = postgresSqlState(error);
if (
state &&
POSTGRES_DEFINITION_RETRYABLE_SQL_STATES.has(state) &&
attempt + 1 < POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS
) {
continue;
}
throw mapStorageError(error);
} finally {
client.release();
}
}
throw unavailable();
}
append(
commandValue: Readonly<ToolExecutionResultRekeyCommand>,
): Promise<Readonly<CommitToolExecutionResultRekeyResult>> {
const command = normalizeToolExecutionResultRekeyCommand(commandValue);
return this.transaction(async (client) => {
const replayRows = await overlayRows(
client,
`WHERE overlay.mutation_id = $1
OR overlay.overlay_id = $2
OR overlay.overlay_digest = $3`,
[
command.mutationId,
command.overlay.overlayId,
command.overlay.overlayDigest,
],
);
if (replayRows.length > 1) {
throw new ToolExecutionResultRekeyConflictError();
}
if (replayRows[0]) {
const stored = overlayFromRow(replayRows[0]);
if (
requiredText(replayRows[0], 'commandDigest') !==
command.commandDigest ||
JSON.stringify(stored) !== JSON.stringify(command.overlay)
) {
throw new ToolExecutionResultRekeyConflictError();
}
return Object.freeze({
status: 'existing' as const,
overlay: stored,
});
}
const source = await client.query<Row>(
`SELECT
binding.artifact_digest AS "bindingArtifactDigest",
binding.key_id AS "bindingKeyId",
binding.binding_digest AS "bindingDigest",
completion.artifact_digest AS "artifactDigest",
completion.output_digest AS "outputDigest",
completion.execution_result_digest AS "executionResultDigest"
FROM "ql3"."tool_execution_result_key_bindings" AS binding
JOIN "ql3"."tool_execution_completions" AS completion
ON completion.artifact_id = binding.artifact_id
AND completion.start_id = binding.start_id
WHERE binding.artifact_id = $1`,
[command.overlay.sourceArtifact.artifactId],
);
if (source.rows.length !== 1) {
throw new ToolExecutionResultRekeyConflictError();
}
const binding = source.rows[0]!;
if (
requiredText(binding, 'bindingArtifactDigest') !==
command.overlay.sourceArtifact.artifactDigest ||
requiredText(binding, 'artifactDigest') !==
command.overlay.sourceArtifact.artifactDigest ||
requiredText(binding, 'outputDigest') !==
command.overlay.sourceArtifact.outputDigest ||
requiredText(binding, 'executionResultDigest') !==
command.overlay.sourceArtifact.executionResultDigest ||
requiredText(binding, 'bindingDigest') !==
command.overlay.sourceBindingDigest
) {
throw new ToolExecutionResultRekeyConflictError();
}
const headResult = await client.query<Row>(
`SELECT revision, overlay_digest AS "overlayDigest",
target_key_id AS "targetKeyId"
FROM "ql3"."tool_execution_result_rekey_heads"
WHERE artifact_id = $1
FOR UPDATE`,
[command.overlay.sourceArtifact.artifactId],
);
if (headResult.rows.length > 1) throw unavailable();
const head = headResult.rows[0];
const currentRevision = head ? requiredInteger(head, 'revision') : 0;
const currentDigest = head ? requiredText(head, 'overlayDigest') : null;
const fromKeyId = head
? requiredText(head, 'targetKeyId')
: requiredText(binding, 'bindingKeyId');
if (
currentRevision !== command.expectedRevision ||
currentDigest !== command.expectedOverlayDigest ||
command.overlay.fromKeyId !== fromKeyId
) {
throw new ToolExecutionResultRekeyConflictError();
}
const catalog = await currentCatalog(client);
const active = requireActiveToolResultKey(catalog);
const fence = toolResultKeyCatalogFence(catalog, active);
if (
JSON.stringify(fence) !==
JSON.stringify(command.overlay.targetCatalogFence)
) {
throw new ToolExecutionResultRekeyConflictError();
}
await client.query(
`INSERT INTO "ql3"."tool_execution_result_rekey_overlays" (
overlay_id, artifact_id, source_binding_digest, revision,
previous_overlay_digest, from_key_id,
target_catalog_authority, target_catalog_generation,
target_catalog_digest, target_key_id, target_material_proof,
mutation_id, command_digest, overlay_digest, rekeyed_at_ms,
overlay_json
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13,
$14, $15, $16::jsonb
)`,
[
command.overlay.overlayId,
command.overlay.sourceArtifact.artifactId,
command.overlay.sourceBindingDigest,
command.overlay.revision,
command.overlay.previousOverlayDigest,
command.overlay.fromKeyId,
AUTHORITY,
fence.generation,
fence.catalogDigest,
fence.keyId,
fence.materialProof,
command.mutationId,
command.commandDigest,
command.overlay.overlayDigest,
command.overlay.rekeyedAtMs,
JSON.stringify(command.overlay),
],
);
if (head) {
const updated = await client.query(
`UPDATE "ql3"."tool_execution_result_rekey_heads"
SET revision = $1, overlay_id = $2, overlay_digest = $3,
target_catalog_generation = $4,
target_catalog_digest = $5, target_key_id = $6,
updated_at_ms = $7
WHERE artifact_id = $8 AND revision = $9
AND overlay_digest = $10`,
[
command.overlay.revision,
command.overlay.overlayId,
command.overlay.overlayDigest,
fence.generation,
fence.catalogDigest,
fence.keyId,
command.overlay.rekeyedAtMs,
command.overlay.sourceArtifact.artifactId,
command.expectedRevision,
command.expectedOverlayDigest,
],
);
if (updated.rowCount !== 1) {
throw new ToolExecutionResultRekeyConflictError();
}
} else {
await client.query(
`INSERT INTO "ql3"."tool_execution_result_rekey_heads" (
artifact_id, revision, overlay_id, overlay_digest,
target_catalog_generation, target_catalog_digest,
target_key_id, updated_at_ms
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
[
command.overlay.sourceArtifact.artifactId,
command.overlay.revision,
command.overlay.overlayId,
command.overlay.overlayDigest,
fence.generation,
fence.catalogDigest,
fence.keyId,
command.overlay.rekeyedAtMs,
],
);
}
return Object.freeze({
status: 'created' as const,
overlay: command.overlay,
});
});
}
async findByDigest(
receiptDigest: string,
): Promise<Readonly<ToolResultKeyRetirementReceipt> | null> {
if (!/^[0-9a-f]{64}$/.test(receiptDigest)) {
throw new InvalidToolExecutionResultRekeyError(
'retirement receipt digest is invalid',
);
}
try {
const rows = await receiptRows(
this.pool,
'WHERE receipt_digest = $1',
[receiptDigest],
);
if (rows.length > 1) throw unavailable();
return rows[0] ? receiptFromRow(rows[0]) : null;
} catch (error) {
throw mapStorageError(error);
}
}
create(
commandValue: Readonly<ToolResultKeyRetirementReceiptCommand>,
): Promise<Readonly<CommitToolResultKeyRetirementReceiptResult>> {
const command =
normalizeToolResultKeyRetirementReceiptCommand(commandValue);
return this.transaction(async (client) => {
const replayRows = await receiptRows(
client,
'WHERE mutation_id = $1',
[command.mutationId],
);
if (replayRows.length > 1) {
throw new ToolExecutionResultRekeyConflictError();
}
if (replayRows[0]) {
const stored = receiptFromRow(replayRows[0]);
if (
requiredText(replayRows[0], 'commandDigest') !==
command.commandDigest ||
stored.catalogGeneration !== command.expectedCatalogGeneration ||
stored.catalogDigest !== command.expectedCatalogDigest ||
stored.keyId !== command.keyId
) {
throw new ToolExecutionResultRekeyConflictError();
}
return Object.freeze({
status: 'existing' as const,
receipt: stored,
});
}
const catalog = await currentCatalog(client);
if (
catalog.generation !== command.expectedCatalogGeneration ||
catalog.catalogDigest !== command.expectedCatalogDigest
) {
throw new ToolExecutionResultRekeyConflictError();
}
const retiring = catalog.keys.find(
(entry) => entry.keyId === command.keyId,
);
if (!retiring || retiring.state !== 'decrypt_only') {
throw new ToolExecutionResultRekeyConflictError();
}
const decryptableKeyIds = catalog.keys
.filter(
(entry) =>
entry.keyId !== retiring.keyId &&
(entry.state === 'active' || entry.state === 'decrypt_only'),
)
.map((entry) => entry.keyId)
.sort();
const coverage = new ToolResultKeyRetirementCoverageBuilder({
catalogGeneration: catalog.generation,
catalogDigest: catalog.catalogDigest,
keyId: retiring.keyId,
decryptableKeyIds,
});
let cursor = '';
for (;;) {
const page = await client.query<Row>(
`SELECT
binding.artifact_id AS "artifactId",
binding.binding_digest AS "bindingDigest",
binding.key_id AS "bindingKeyId",
head.overlay_digest AS "headOverlayDigest",
head.target_key_id AS "headTargetKeyId",
head.target_catalog_generation AS "headTargetCatalogGeneration",
head.target_catalog_digest AS "headTargetCatalogDigest"
FROM "ql3"."tool_execution_result_key_bindings" AS binding
LEFT JOIN "ql3"."tool_execution_result_rekey_heads" AS head
ON head.artifact_id = binding.artifact_id
WHERE (binding.key_id = $1 OR head.target_key_id = $1)
AND binding.artifact_id > $2
ORDER BY binding.artifact_id
LIMIT $3`,
[retiring.keyId, cursor, COVERAGE_PAGE_SIZE],
);
for (const row of page.rows) {
coverage.add({
artifactId: requiredText(row, 'artifactId'),
bindingDigest: requiredText(row, 'bindingDigest'),
bindingKeyId: requiredText(row, 'bindingKeyId'),
headOverlayDigest: nullableText(row, 'headOverlayDigest'),
headTargetKeyId: nullableText(row, 'headTargetKeyId'),
headTargetCatalogGeneration:
row.headTargetCatalogGeneration === null
? null
: requiredInteger(row, 'headTargetCatalogGeneration'),
headTargetCatalogDigest: nullableText(
row,
'headTargetCatalogDigest',
),
});
}
if (page.rows.length < COVERAGE_PAGE_SIZE) break;
cursor = requiredText(page.rows[page.rows.length - 1]!, 'artifactId');
}
const result = coverage.finish();
if (
result.uncoveredBindingCount !== 0 ||
result.uncoveredOverlayHeadCount !== 0
) {
throw new ToolExecutionResultRekeyConflictError();
}
const clock = await client.query<Row>(
`SELECT floor(
extract(epoch FROM clock_timestamp()) * 1000
)::bigint AS now`,
);
if (clock.rows.length !== 1) throw unavailable();
const receipt = createToolResultKeyRetirementReceipt({
catalogGeneration: catalog.generation,
catalogDigest: catalog.catalogDigest,
keyId: retiring.keyId,
materialProof: retiring.materialProof,
mutationId: command.mutationId,
bindingCount: result.bindingCount,
overlayHeadCount: result.overlayHeadCount,
coverageDigest: result.coverageDigest,
createdAtMs: requiredInteger(clock.rows[0]!, 'now'),
});
await client.query(
`INSERT INTO "ql3"."tool_result_key_retirement_receipts" (
receipt_digest, catalog_authority, catalog_generation,
catalog_digest, key_id, material_proof, mutation_id,
command_digest, binding_count, overlay_head_count,
uncovered_binding_count, uncovered_overlay_head_count,
coverage_digest, created_at_ms, receipt_json
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, 0, 0, $11, $12,
$13::jsonb
)`,
[
receipt.receiptDigest,
AUTHORITY,
receipt.catalogGeneration,
receipt.catalogDigest,
receipt.keyId,
receipt.materialProof,
receipt.mutationId,
command.commandDigest,
receipt.bindingCount,
receipt.overlayHeadCount,
receipt.coverageDigest,
receipt.createdAtMs,
JSON.stringify(receipt),
],
);
return Object.freeze({
status: 'created' as const,
receipt,
});
});
}
}