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,119 @@
import type { ApiCredentialRepository } from '@qinglong/runtime-core/api-credential';
import type { ApprovalRequestRepository } from '@qinglong/runtime-core/approved-action';
import type { ApprovalRequestDetailSource } from '@qinglong/runtime-core/approval-discovery';
import type { LocalOwnerPepperRepository } from '@qinglong/runtime-core/local-owner-pepper';
import type { ProjectPolicyRepository } from '@qinglong/runtime-core/project-policy';
import type { SecurityAuditSink } from '@qinglong/runtime-core/security-audit';
import { LocalSqliteOperationAuthority } from '../authority/operationAuthority';
import { LocalSqliteOwnerPepperRepository } from '../local-owner/ownerPepperRepository';
import {
confirmLocalSqliteAuthenticatedUserCredentialFence,
LocalSqliteAuthenticatedManagementFenceError,
type LocalSqliteAuthenticatedUserCredentialFence,
} from '../administration/packageManagement';
import {
auditLocalSqliteReadiness,
type LocalSqliteReadinessEvidence,
} from '../readiness/readiness';
import { LocalSqliteApiCredentialRepository } from '../security/apiCredentialRepository';
import { LocalSqliteProjectPolicyRepository } from '../security/projectPolicyRepository';
import { LocalSqliteSecurityAuthorityStore } from '../security/securityAuthorityStore';
import {
assertLocalSqliteOptions,
assertLocalSqlitePathBoundary,
openLocalSqliteClient,
type LocalSqliteDatabaseOptions,
type LocalSqliteProfile,
} from '../storage/config';
import { LocalSqliteApprovalRequestRepository } from './approvalRequestRepository';
import { LocalSqliteApprovalRequestSource } from './approvalRequestSource';
export interface LocalSqliteApprovalDecisionDatabase {
readonly profile: LocalSqliteProfile;
readonly readiness: LocalSqliteReadinessEvidence;
readonly apiCredentials: ApiCredentialRepository;
readonly ownerPepper: Pick<LocalOwnerPepperRepository, 'resolveKey'>;
readonly projectPolicy: ProjectPolicyRepository;
readonly approvals: Pick<ApprovalRequestRepository, 'findById' | 'decide'>;
readonly approvalDetails: ApprovalRequestDetailSource;
readonly securityAudit: SecurityAuditSink;
activateUserCredentialFence(
fence: Readonly<LocalSqliteAuthenticatedUserCredentialFence>,
): void;
confirmUserCredentialFence(): void;
close(): Promise<void>;
}
function sameCredentialFence(
left: Readonly<LocalSqliteAuthenticatedUserCredentialFence>,
right: Readonly<LocalSqliteAuthenticatedUserCredentialFence>,
): boolean {
return (
left.credentialId === right.credentialId &&
left.credentialVersion === right.credentialVersion &&
left.pepperKeyId === right.pepperKeyId &&
left.materialDigest === right.materialDigest &&
left.subjectType === right.subjectType &&
left.subjectId === right.subjectId &&
left.secretDigest === right.secretDigest &&
left.notBeforeAtMs === right.notBeforeAtMs &&
left.expiresAtMs === right.expiresAtMs
);
}
/** Short-lived Owner authority; it never migrates schema or starts workers. */
export async function openLocalSqliteApprovalDecisionDatabase(
options: LocalSqliteDatabaseOptions,
): Promise<LocalSqliteApprovalDecisionDatabase> {
assertLocalSqliteOptions(options);
assertLocalSqlitePathBoundary(options.databasePath, false);
const client = openLocalSqliteClient(options, false);
try {
const readiness = await auditLocalSqliteReadiness(client);
const authority = new LocalSqliteOperationAuthority(client);
let activeFence:
| Readonly<LocalSqliteAuthenticatedUserCredentialFence>
| undefined;
const confirmActiveFence = () => {
if (!activeFence) {
throw new LocalSqliteAuthenticatedManagementFenceError();
}
confirmLocalSqliteAuthenticatedUserCredentialFence(authority, activeFence);
};
const securityAuthority = new LocalSqliteSecurityAuthorityStore(authority);
const approvals = new LocalSqliteApprovalRequestRepository(
authority,
confirmActiveFence,
);
let closePromise: Promise<void> | undefined;
return Object.freeze({
profile: options.profile,
readiness,
apiCredentials: new LocalSqliteApiCredentialRepository(authority),
ownerPepper: new LocalSqliteOwnerPepperRepository(authority),
projectPolicy: new LocalSqliteProjectPolicyRepository(authority),
approvals,
approvalDetails: new LocalSqliteApprovalRequestSource(authority),
securityAudit: securityAuthority,
activateUserCredentialFence(
fence: Readonly<LocalSqliteAuthenticatedUserCredentialFence>,
) {
confirmLocalSqliteAuthenticatedUserCredentialFence(authority, fence);
if (activeFence && !sameCredentialFence(activeFence, fence)) {
throw new LocalSqliteAuthenticatedManagementFenceError();
}
activeFence = Object.freeze({ ...fence });
},
confirmUserCredentialFence: confirmActiveFence,
close() {
if (closePromise) return closePromise;
closePromise = authority.close();
return closePromise;
},
});
} catch (error) {
if (client.isOpen) client.close();
throw error;
}
}
@@ -0,0 +1,622 @@
import type { DatabaseSync } from 'node:sqlite';
import {
ApprovalMutationConflictError,
ApprovalPolicyFenceConflictError,
ApprovalRequestNotFoundError,
ApprovalRequestStateConflictError,
ApprovalUnavailableError,
approvalRequestDigest,
approvedActionDispatchDigest,
consumeApprovalRequest,
decideApprovalRequest,
normalizeApprovalRequestRecord,
normalizeApprovedActionDispatchRecord,
normalizeApprovedActionFence,
type ApprovalRequestRecord,
type ApprovalRequestRepository,
type ApprovedActionDispatchRecord,
type ConsumeDurableApprovalRequestCommand,
type ConsumeDurableApprovalRequestResult,
type CreateApprovalRequestCommand,
type CreateApprovalRequestResult,
type DecideApprovalRequestResult,
type DecideDurableApprovalRequestCommand,
} from '@qinglong/runtime-core/approved-action';
import {
normalizeSecurityAuditRecord,
type SecurityAuditRecord,
} from '@qinglong/runtime-core/security-audit';
import type {
SecurityPolicyFence,
SecuritySubject,
} from '@qinglong/runtime-core/security';
import { LocalSqliteOperationAuthority } from '../authority/operationAuthority';
import {
findLocalApprovedActionExecution,
insertLocalApprovedActionExecutionBaseline,
} from './approvedActionExecutionRepository';
import { createApprovedActionExecution } from '@qinglong/runtime-core/approved-action-execution';
type Row = Record<string, unknown>;
const IDENTIFIER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
function text(row: Row, key: string): string {
const value = row[key];
if (typeof value !== 'string') throw new ApprovalUnavailableError();
return value;
}
function nullableText(row: Row, key: string): string | null {
const value = row[key];
if (value !== null && typeof value !== 'string') {
throw new ApprovalUnavailableError();
}
return value;
}
function integer(row: Row, key: string): number {
const value = row[key];
if (!Number.isSafeInteger(value)) throw new ApprovalUnavailableError();
return value as number;
}
function nullableInteger(row: Row, key: string): number | null {
const value = row[key];
if (value !== null && !Number.isSafeInteger(value)) {
throw new ApprovalUnavailableError();
}
return value as number | null;
}
function same(left: unknown, right: unknown): boolean {
return JSON.stringify(left) === JSON.stringify(right);
}
function parseRequest(row: Row): Readonly<ApprovalRequestRecord> {
try {
const request = normalizeApprovalRequestRecord(
JSON.parse(text(row, 'requestJson')) as ApprovalRequestRecord,
);
if (approvalRequestDigest(request) !== text(row, 'requestDigest')) {
throw new ApprovalUnavailableError();
}
return request;
} catch (error) {
if (error instanceof ApprovalUnavailableError) throw error;
throw new ApprovalUnavailableError();
}
}
function parseDispatch(row: Row): Readonly<ApprovedActionDispatchRecord> {
try {
const dispatch = normalizeApprovedActionDispatchRecord(
JSON.parse(text(row, 'dispatchJson')) as ApprovedActionDispatchRecord,
);
if (
approvedActionDispatchDigest(dispatch) !== text(row, 'dispatchDigest')
) {
throw new ApprovalUnavailableError();
}
return dispatch;
} catch (error) {
if (error instanceof ApprovalUnavailableError) throw error;
throw new ApprovalUnavailableError();
}
}
function parseAudit(row: Row): Readonly<SecurityAuditRecord> {
try {
const subjectType = nullableText(row, 'subjectType');
const subjectId = nullableText(row, 'subjectId');
const fenceProjectVersion = nullableInteger(row, 'fenceProjectVersion');
return normalizeSecurityAuditRecord({
eventId: text(row, 'eventId'),
requestId: text(row, 'requestId'),
operationId: text(row, 'operationId'),
projectId: nullableText(row, 'projectId'),
subject:
subjectType === null || subjectId === null
? null
: {
type: subjectType as SecuritySubject['type'],
id: subjectId,
},
authenticationId: nullableText(row, 'authenticationId'),
outcome: text(row, 'outcome') as SecurityAuditRecord['outcome'],
reasons: JSON.parse(text(row, 'reasonsJson')) as readonly string[],
fence:
fenceProjectVersion === null
? null
: {
projectVersion: fenceProjectVersion,
bindingVersion: nullableInteger(row, 'fenceBindingVersion'),
},
occurredAtMs: integer(row, 'occurredAtMs'),
});
} catch (error) {
if (error instanceof ApprovalUnavailableError) throw error;
throw new ApprovalUnavailableError();
}
}
function storageError(error: unknown): Error {
if (
error instanceof ApprovalMutationConflictError ||
error instanceof ApprovalPolicyFenceConflictError ||
error instanceof ApprovalRequestNotFoundError ||
error instanceof ApprovalRequestStateConflictError ||
error instanceof ApprovalUnavailableError ||
(error instanceof Error &&
error.name.startsWith('Approval') &&
'code' in error)
) {
return error;
}
if (
error &&
typeof error === 'object' &&
'code' in error &&
typeof error.code === 'string' &&
error.code.startsWith('SQLITE_CONSTRAINT')
) {
return new ApprovalMutationConflictError();
}
return new ApprovalUnavailableError({
cause: error instanceof Error ? error : undefined,
});
}
function auditMatches(
audit: Readonly<SecurityAuditRecord>,
expected: Readonly<{
operationId: string;
projectId: string;
subject: Readonly<SecuritySubject>;
authenticationId?: string;
outcome: SecurityAuditRecord['outcome'];
fence: Readonly<SecurityPolicyFence>;
}>,
): boolean {
return (
audit.operationId === expected.operationId &&
audit.projectId === expected.projectId &&
audit.subject?.type === expected.subject.type &&
audit.subject.id === expected.subject.id &&
(expected.authenticationId === undefined ||
audit.authenticationId === expected.authenticationId) &&
audit.outcome === expected.outcome &&
audit.fence?.projectVersion === expected.fence.projectVersion &&
audit.fence.bindingVersion === expected.fence.bindingVersion
);
}
export class LocalSqliteApprovalRequestRepository
implements ApprovalRequestRepository
{
readonly #authority: LocalSqliteOperationAuthority;
readonly #client: DatabaseSync;
readonly #confirmMutation: () => void;
constructor(
authority: LocalSqliteOperationAuthority | DatabaseSync,
confirmMutation: () => void = () => undefined,
) {
if (typeof confirmMutation !== 'function') {
throw new TypeError('Local Approval mutation guard is invalid');
}
this.#authority =
authority instanceof LocalSqliteOperationAuthority
? authority
: new LocalSqliteOperationAuthority(authority);
this.#client = this.#authority.client;
this.#confirmMutation = confirmMutation;
}
#enqueue<T>(work: () => T | Promise<T>): Promise<T> {
return this.#authority.enqueue(
async () => {
try {
return await work();
} catch (error) {
throw storageError(error);
}
},
() => new ApprovalUnavailableError(),
);
}
#request(id: string): Readonly<ApprovalRequestRecord> | null {
const row = this.#client
.prepare(
`SELECT "request_json" AS "requestJson",
"request_digest" AS "requestDigest"
FROM "QingLong3ApprovalRequests"
WHERE "request_id" = ?`,
)
.get(id) as Row | undefined;
return row ? parseRequest(row) : null;
}
#dispatch(id: string): Readonly<ApprovedActionDispatchRecord> | null {
const row = this.#client
.prepare(
`SELECT "dispatch_json" AS "dispatchJson",
"dispatch_digest" AS "dispatchDigest"
FROM "QingLong3ApprovedActionDispatches"
WHERE "dispatch_id" = ?`,
)
.get(id) as Row | undefined;
return row ? parseDispatch(row) : null;
}
#audit(id: string): Readonly<SecurityAuditRecord> | null {
const row = this.#client
.prepare(
`SELECT "event_id" AS "eventId", "request_id" AS "requestId",
"operation_id" AS "operationId", "project_id" AS "projectId",
"subject_type" AS "subjectType", "subject_id" AS "subjectId",
"authentication_id" AS "authenticationId",
"outcome" AS "outcome", "reasons_json" AS "reasonsJson",
"fence_project_version" AS "fenceProjectVersion",
"fence_binding_version" AS "fenceBindingVersion",
"occurred_at_ms" AS "occurredAtMs"
FROM "QingLong3SecurityAuditEvents"
WHERE "event_id" = ?`,
)
.get(id) as Row | undefined;
return row ? parseAudit(row) : null;
}
#assertFence(
projectId: string,
subject: Readonly<SecuritySubject>,
expectedValue: Readonly<SecurityPolicyFence>,
): void {
const expected = normalizeApprovedActionFence(expectedValue);
const row = this.#client
.prepare(
`SELECT project."status" AS "status",
project."version" AS "projectVersion",
(
SELECT max(binding."version")
FROM "QingLong3ProjectRoleBindings" AS binding
WHERE binding."project_id" = project."id"
AND binding."subject_type" = ?
AND binding."subject_id" = ?
) AS "bindingVersion"
FROM "QingLong3Projects" AS project
WHERE project."id" = ?`,
)
.get(subject.type, subject.id, projectId) as Row | undefined;
if (
!row ||
row.status !== 'active' ||
integer(row, 'projectVersion') !== expected.projectVersion ||
nullableInteger(row, 'bindingVersion') !== expected.bindingVersion
) {
throw new ApprovalPolicyFenceConflictError();
}
}
#insertAudit(value: SecurityAuditRecord): void {
const audit = normalizeSecurityAuditRecord(value);
this.#client
.prepare(
`INSERT INTO "QingLong3SecurityAuditEvents" (
"event_id", "request_id", "operation_id", "project_id",
"subject_type", "subject_id", "authentication_id", "outcome",
"reasons_json", "fence_project_version",
"fence_binding_version", "occurred_at_ms"
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
)
.run(
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,
);
}
#insertRequest(request: Readonly<ApprovalRequestRecord>): void {
this.#client
.prepare(
`INSERT INTO "QingLong3ApprovalRequests" (
"request_id", "project_id", "version", "state", "action_type",
"action_ref", "action_digest", "preview_digest",
"requested_by_type", "requested_by_id", "decision_id",
"consumption_id", "dispatch_id", "expires_at_ms", "request_json",
"request_digest", "updated_at_ms"
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
)
.run(
request.id,
request.projectId,
request.version,
request.state,
request.action.actionType,
request.action.actionRef,
request.action.actionDigest,
request.action.previewDigest,
request.requestedBy.type,
request.requestedBy.id,
request.decisionId,
request.consumptionId,
request.dispatchId,
request.expiresAtMs,
JSON.stringify(request),
approvalRequestDigest(request),
request.consumedAtMs ?? request.decidedAtMs ?? request.requestedAtMs,
);
}
#updateRequest(
request: Readonly<ApprovalRequestRecord>,
expectedVersion: number,
): void {
const update = this.#client
.prepare(
`UPDATE "QingLong3ApprovalRequests"
SET "version" = ?, "state" = ?, "decision_id" = ?,
"consumption_id" = ?, "dispatch_id" = ?, "request_json" = ?,
"request_digest" = ?, "updated_at_ms" = ?
WHERE "request_id" = ? AND "version" = ?`,
)
.run(
request.version,
request.state,
request.decisionId,
request.consumptionId,
request.dispatchId,
JSON.stringify(request),
approvalRequestDigest(request),
request.consumedAtMs ?? request.decidedAtMs ?? request.requestedAtMs,
request.id,
expectedVersion,
);
if (update.changes !== 1) {
throw new ApprovalRequestStateConflictError();
}
}
#insertDispatch(dispatch: Readonly<ApprovedActionDispatchRecord>): void {
this.#client
.prepare(
`INSERT INTO "QingLong3ApprovedActionDispatches" (
"dispatch_id", "approval_request_id", "project_id", "action_type",
"action_ref", "action_digest", "preview_digest", "dispatch_json",
"dispatch_digest", "created_at_ms"
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
)
.run(
dispatch.id,
dispatch.approvalRequestId,
dispatch.projectId,
dispatch.action.actionType,
dispatch.action.actionRef,
dispatch.action.actionDigest,
dispatch.action.previewDigest,
JSON.stringify(dispatch),
approvedActionDispatchDigest(dispatch),
dispatch.createdAtMs,
);
}
findById(id: string): Promise<Readonly<ApprovalRequestRecord> | null> {
if (typeof id !== 'string' || !IDENTIFIER_PATTERN.test(id)) {
throw new TypeError('Approval request lookup identity is invalid');
}
return this.#enqueue(() => this.#request(id));
}
findDispatchById(
id: string,
): Promise<Readonly<ApprovedActionDispatchRecord> | null> {
if (typeof id !== 'string' || !IDENTIFIER_PATTERN.test(id)) {
throw new TypeError('Approved action dispatch identity is invalid');
}
return this.#enqueue(() => this.#dispatch(id));
}
create(
command: CreateApprovalRequestCommand,
): Promise<CreateApprovalRequestResult> {
const request = normalizeApprovalRequestRecord(command.request);
const audit = normalizeSecurityAuditRecord(command.audit);
if (
request.state !== 'pending' ||
request.version !== 1 ||
!auditMatches(audit, {
operationId: 'approval.request',
projectId: request.projectId,
subject: request.requestedBy,
outcome: 'approval_required',
fence: request.requestFence,
})
) {
throw new ApprovalMutationConflictError();
}
return this.#enqueue(() => {
this.#client.exec('BEGIN IMMEDIATE');
try {
this.#confirmMutation();
const existing = this.#request(request.id);
if (existing) {
const storedAudit = this.#audit(audit.eventId);
if (
!same(existing, request) ||
!storedAudit ||
!same(storedAudit, audit)
) {
throw new ApprovalMutationConflictError();
}
this.#client.exec('COMMIT');
return Object.freeze({ status: 'existing' as const, request });
}
this.#assertFence(
request.projectId,
request.requestedBy,
request.requestFence,
);
this.#insertRequest(request);
this.#insertAudit(audit);
this.#client.exec('COMMIT');
return Object.freeze({ status: 'created' as const, request });
} catch (error) {
if (this.#client.isTransaction) this.#client.exec('ROLLBACK');
throw error;
}
});
}
decide(
command: DecideDurableApprovalRequestCommand,
): Promise<DecideApprovalRequestResult> {
const audit = normalizeSecurityAuditRecord(command.audit);
return this.#enqueue(() => {
this.#client.exec('BEGIN IMMEDIATE');
try {
this.#confirmMutation();
const current = this.#request(command.requestId);
if (!current) throw new ApprovalRequestNotFoundError();
const request = decideApprovalRequest(current, {
expectedVersion: command.expectedVersion,
decisionId: command.decisionId,
decision: command.decision,
reasonCode: command.reasonCode,
principal: command.principal,
decidedAtMs: command.decidedAtMs,
authorizationFence: command.authorizationFence,
});
if (
!auditMatches(audit, {
operationId: 'approval.decide',
projectId: request.projectId,
subject: command.principal.subject,
authenticationId: command.principal.authenticationId,
outcome: 'allowed',
fence: command.authorizationFence,
})
) {
throw new ApprovalMutationConflictError();
}
if (current.version === 2 && current.decisionId === command.decisionId) {
const storedAudit = this.#audit(audit.eventId);
if (!storedAudit || !same(storedAudit, audit)) {
throw new ApprovalMutationConflictError();
}
this.#client.exec('COMMIT');
return Object.freeze({
status: 'existing' as const,
request,
});
}
this.#assertFence(
request.projectId,
command.principal.subject,
command.authorizationFence,
);
this.#updateRequest(request, command.expectedVersion);
this.#insertAudit(audit);
this.#client.exec('COMMIT');
return Object.freeze({ status: 'decided' as const, request });
} catch (error) {
if (this.#client.isTransaction) this.#client.exec('ROLLBACK');
throw error;
}
});
}
consume(
command: ConsumeDurableApprovalRequestCommand,
): Promise<ConsumeDurableApprovalRequestResult> {
const audit = normalizeSecurityAuditRecord(command.audit);
return this.#enqueue(() => {
this.#client.exec('BEGIN IMMEDIATE');
try {
this.#confirmMutation();
const current = this.#request(command.requestId);
if (!current) throw new ApprovalRequestNotFoundError();
const result = consumeApprovalRequest(current, {
expectedVersion: command.expectedVersion,
consumptionId: command.consumptionId,
dispatchId: command.dispatchId,
action: command.action,
requestedBy: command.requestedBy,
consumedBy: command.consumedBy,
consumedAtMs: command.consumedAtMs,
authorizationFence: command.authorizationFence,
});
if (
!auditMatches(audit, {
operationId: 'approval.consume',
projectId: result.request.projectId,
subject: command.consumedBy,
outcome: 'allowed',
fence: command.authorizationFence,
})
) {
throw new ApprovalMutationConflictError();
}
if (
current.version === 3 &&
current.consumptionId === command.consumptionId
) {
const storedAudit = this.#audit(audit.eventId);
const dispatch = this.#dispatch(result.dispatch.id);
const execution = findLocalApprovedActionExecution(
this.#client,
result.dispatch.id,
);
if (
!storedAudit ||
!same(storedAudit, audit) ||
!dispatch ||
!same(dispatch, result.dispatch) ||
!execution ||
!same(execution, createApprovedActionExecution(result.dispatch))
) {
throw new ApprovalMutationConflictError();
}
this.#client.exec('COMMIT');
return Object.freeze({
status: 'existing' as const,
request: result.request,
dispatch: result.dispatch,
});
}
this.#assertFence(
result.request.projectId,
command.requestedBy,
command.authorizationFence,
);
this.#insertDispatch(result.dispatch);
insertLocalApprovedActionExecutionBaseline(
this.#client,
result.dispatch,
);
this.#updateRequest(result.request, command.expectedVersion);
this.#insertAudit(audit);
this.#client.exec('COMMIT');
return Object.freeze({
status: 'consumed' as const,
request: result.request,
dispatch: result.dispatch,
});
} catch (error) {
if (this.#client.isTransaction) this.#client.exec('ROLLBACK');
throw error;
}
});
}
}
@@ -0,0 +1,242 @@
import {
ApprovalUnavailableError,
approvalRequestDigest,
normalizeApprovalRequestRecord,
type ApprovalRequestRecord,
} from '@qinglong/runtime-core/approved-action';
import {
approvalRequestUpdatedAtMs,
assertApprovalDiscoveryProjectId,
assertApprovalDiscoveryRequestId,
assertApprovalRequestPageSize,
normalizeApprovalRequestCursor,
type ApprovalRequestDetail,
type ApprovalRequestDetailSource,
type ApprovalRequestPage,
type ApprovalRequestSource,
} from '@qinglong/runtime-core/approval-discovery';
import {
normalizeToolInvocationPreviewArtifact,
type ToolInvocationPreviewArtifact,
} from '@qinglong/runtime-core/tool-invocation-artifact';
import { LocalSqliteOperationAuthority } from '../authority/operationAuthority';
type Row = Record<string, unknown>;
function text(row: Row, key: string): string {
const value = row[key];
if (typeof value !== 'string') throw new ApprovalUnavailableError();
return value;
}
function integer(row: Row, key: string): number {
const value = row[key];
if (!Number.isSafeInteger(value) || Number(value) < 0) {
throw new ApprovalUnavailableError();
}
return Number(value);
}
function previewArtifact(
row: Row,
): Readonly<ToolInvocationPreviewArtifact> | null {
if (row.previewArtifactId === null) return null;
try {
const value = normalizeToolInvocationPreviewArtifact(
JSON.parse(text(row, 'previewArtifactJson')) as ToolInvocationPreviewArtifact,
);
if (
value.artifactId !== text(row, 'previewArtifactId') ||
value.projectId !== text(row, 'previewProjectId') ||
value.actionRef !== text(row, 'previewActionRef') ||
value.actionDigest !== text(row, 'previewActionDigest') ||
value.previewDigest !== text(row, 'storedPreviewDigest') ||
value.redactionContractDigest !== text(row, 'redactionContractDigest') ||
value.artifactDigest !== text(row, 'previewArtifactDigest') ||
value.byteLength !== integer(row, 'previewByteLength') ||
value.sealedAtMs !== integer(row, 'previewSealedAtMs')
) {
throw new ApprovalUnavailableError();
}
return value;
} catch (error) {
if (error instanceof ApprovalUnavailableError) throw error;
throw new ApprovalUnavailableError();
}
}
function request(row: Row): Readonly<ApprovalRequestRecord> {
try {
const value = normalizeApprovalRequestRecord(
JSON.parse(text(row, 'requestJson')) as ApprovalRequestRecord,
);
if (
approvalRequestDigest(value) !== text(row, 'requestDigest') ||
approvalRequestUpdatedAtMs(value) !== integer(row, 'updatedAtMs')
) {
throw new ApprovalUnavailableError();
}
return value;
} catch (error) {
if (error instanceof ApprovalUnavailableError) throw error;
throw new ApprovalUnavailableError();
}
}
/** Read-only, Project-scoped Approval history over the shared SQLite queue. */
export class LocalSqliteApprovalRequestSource
implements ApprovalRequestSource, ApprovalRequestDetailSource
{
constructor(private readonly authority: LocalSqliteOperationAuthority) {
if (!(authority instanceof LocalSqliteOperationAuthority)) {
throw new TypeError('Local Approval discovery authority is invalid');
}
}
listApprovalRequests(options: {
readonly projectId: string;
readonly limit: number;
readonly after?: { readonly updatedAtMs: number; readonly requestId: string };
}): Promise<Readonly<ApprovalRequestPage>> {
if (!options || typeof options !== 'object' || Array.isArray(options)) {
throw new TypeError('Approval discovery options are invalid');
}
const keys = Object.keys(options);
if (
!keys.includes('projectId') ||
!keys.includes('limit') ||
keys.some((key) => !['projectId', 'limit', 'after'].includes(key))
) {
throw new TypeError('Approval discovery options shape is invalid');
}
assertApprovalDiscoveryProjectId(options.projectId);
assertApprovalRequestPageSize(options.limit);
const after = options.after
? normalizeApprovalRequestCursor(options.after)
: undefined;
return this.authority.enqueue(
async () => {
try {
const rows = this.authority.client
.prepare(
`SELECT "request_json" AS "requestJson",
"request_digest" AS "requestDigest",
"updated_at_ms" AS "updatedAtMs"
FROM "QingLong3ApprovalRequests"
WHERE "project_id" = ?
AND (
? IS NULL OR "updated_at_ms" < ? OR
("updated_at_ms" = ? AND "request_id" < ?)
)
ORDER BY "updated_at_ms" DESC, "request_id" DESC
LIMIT ?`,
)
.all(
options.projectId,
after?.updatedAtMs ?? null,
after?.updatedAtMs ?? null,
after?.updatedAtMs ?? null,
after?.requestId ?? '',
options.limit + 1,
) as Row[] | undefined;
if (!Array.isArray(rows)) throw new ApprovalUnavailableError();
const truncated = rows.length > options.limit;
const requests = Object.freeze(
rows.slice(0, options.limit).map(request),
);
const last = requests.at(-1);
return Object.freeze({
requests,
truncated,
...(truncated && last
? {
next: Object.freeze({
updatedAtMs: approvalRequestUpdatedAtMs(last),
requestId: last.id,
}),
}
: {}),
});
} catch {
throw new ApprovalUnavailableError();
}
},
() => new ApprovalUnavailableError(),
);
}
getApprovalRequestDetail(options: {
readonly projectId: string;
readonly requestId: string;
}): Promise<Readonly<ApprovalRequestDetail> | null> {
if (
!options ||
typeof options !== 'object' ||
Array.isArray(options) ||
Object.keys(options).length !== 2 ||
!Object.hasOwn(options, 'projectId') ||
!Object.hasOwn(options, 'requestId')
) {
throw new TypeError('Approval detail options are invalid');
}
assertApprovalDiscoveryProjectId(options.projectId);
assertApprovalDiscoveryRequestId(options.requestId);
return this.authority.enqueue(
async () => {
try {
const rows = this.authority.client
.prepare(
`SELECT a."request_json" AS "requestJson",
a."request_digest" AS "requestDigest",
a."updated_at_ms" AS "updatedAtMs",
p."artifact_id" AS "previewArtifactId",
p."project_id" AS "previewProjectId",
p."action_ref" AS "previewActionRef",
p."action_digest" AS "previewActionDigest",
p."preview_digest" AS "storedPreviewDigest",
p."redaction_contract_digest" AS "redactionContractDigest",
p."artifact_digest" AS "previewArtifactDigest",
p."byte_length" AS "previewByteLength",
p."sealed_at_ms" AS "previewSealedAtMs",
p."artifact_json" AS "previewArtifactJson"
FROM "QingLong3ApprovalRequests" a
LEFT JOIN "ToolInvocationPreviewArtifacts" p
ON a."action_type" = 'tool.invoke'
AND p."project_id" = a."project_id"
AND p."action_ref" = a."action_ref"
AND p."action_digest" = a."action_digest"
AND p."preview_digest" = a."preview_digest"
WHERE a."project_id" = ? AND a."request_id" = ?
LIMIT 2`,
)
.all(options.projectId, options.requestId) as Row[] | undefined;
if (!Array.isArray(rows) || rows.length > 1) {
throw new ApprovalUnavailableError();
}
if (!rows[0]) return null;
const approval = request(rows[0]);
const preview = previewArtifact(rows[0]);
if (
preview &&
(approval.action.actionType !== 'tool.invoke' ||
approval.projectId !== preview.projectId ||
approval.action.actionRef !== preview.actionRef ||
approval.action.actionDigest !== preview.actionDigest ||
approval.action.previewDigest !== preview.previewDigest)
) {
throw new ApprovalUnavailableError();
}
return Object.freeze({
request: approval,
preview: preview?.preview ?? null,
});
} catch (error) {
if (error instanceof ApprovalUnavailableError) throw error;
throw new ApprovalUnavailableError();
}
},
() => new ApprovalUnavailableError(),
);
}
}
@@ -0,0 +1,476 @@
import type { DatabaseSync } from 'node:sqlite';
import {
ApprovedActionExecutionFenceConflictError,
ApprovedActionExecutionStateConflictError,
ApprovedActionExecutionUnavailableError,
approvedActionExecutionEffectiveStatus,
claimApprovedActionExecution,
completeApprovedActionExecution,
createApprovedActionExecution,
normalizeApprovedActionExecutionCursor,
normalizeApprovedActionExecutionRecord,
normalizeApprovedActionExecutionSnapshot,
releaseApprovedActionExecutionBeforeStart,
renewApprovedActionExecution,
startApprovedActionExecution,
type ApprovedActionExecutionRecord,
type ApprovedActionExecutionRepository,
type ApprovedActionExecutionSnapshot,
type ClaimApprovedActionExecutionCommand,
type ClaimApprovedActionExecutionResult,
type CompleteApprovedActionExecutionCommand,
type ListDueApprovedActionExecutionsQuery,
type ListDueApprovedActionExecutionsResult,
type ReleaseApprovedActionExecutionBeforeStartCommand,
type RenewApprovedActionExecutionCommand,
type StartApprovedActionExecutionCommand,
} from '@qinglong/runtime-core/approved-action-execution';
import {
normalizeApprovedActionDispatchRecord,
type ApprovedActionDispatchRecord,
} from '@qinglong/runtime-core/approved-action';
import { LocalSqliteOperationAuthority } from '../authority/operationAuthority';
type Row = Record<string, unknown>;
function text(row: Row, key: string): string {
const value = row[key];
if (typeof value !== 'string') {
throw new ApprovedActionExecutionUnavailableError();
}
return value;
}
function parseExecution(row: Row): Readonly<ApprovedActionExecutionRecord> {
try {
const execution = normalizeApprovedActionExecutionRecord(
JSON.parse(text(row, 'executionJson')) as ApprovedActionExecutionRecord,
);
if (execution.executionDigest !== text(row, 'executionDigest')) {
throw new ApprovedActionExecutionUnavailableError();
}
return execution;
} catch (error) {
if (error instanceof ApprovedActionExecutionUnavailableError) throw error;
throw new ApprovedActionExecutionUnavailableError();
}
}
function parseDispatch(row: Row): Readonly<ApprovedActionDispatchRecord> {
try {
return normalizeApprovedActionDispatchRecord(
JSON.parse(text(row, 'dispatchJson')) as ApprovedActionDispatchRecord,
);
} catch {
throw new ApprovedActionExecutionUnavailableError();
}
}
function storageError(error: unknown): Error {
if (
error instanceof ApprovedActionExecutionFenceConflictError ||
error instanceof ApprovedActionExecutionStateConflictError ||
error instanceof ApprovedActionExecutionUnavailableError ||
(error instanceof Error &&
error.name.startsWith('ApprovedActionExecution'))
) {
return error;
}
return new ApprovedActionExecutionUnavailableError({
cause: error instanceof Error ? error : undefined,
});
}
const EXECUTION_COLUMNS = `
"dispatch_id", "dispatch_digest", "project_id", "status", "version",
"attempt_count", "max_attempts", "eligible_at_ms", "next_attempt_at_ms",
"lease_owner", "lease_token", "lease_expires_at_ms", "started_at_ms",
"result_mutation_id", "result_code", "result_digest", "completed_at_ms",
"created_at_ms", "updated_at_ms", "execution_json", "execution_digest"
`;
function executionValues(
execution: Readonly<ApprovedActionExecutionRecord>,
): readonly (string | number | null)[] {
return [
execution.dispatchId,
execution.dispatchDigest,
execution.projectId,
execution.status,
execution.version,
execution.attemptCount,
execution.maxAttempts,
execution.eligibleAtMs,
execution.nextAttemptAtMs,
execution.leaseOwner,
execution.leaseToken,
execution.leaseExpiresAtMs,
execution.startedAtMs,
execution.resultMutationId,
execution.resultCode,
execution.resultDigest,
execution.completedAtMs,
execution.createdAtMs,
execution.updatedAtMs,
JSON.stringify(execution),
execution.executionDigest,
];
}
export function insertLocalApprovedActionExecutionBaseline(
client: DatabaseSync,
dispatchValue: ApprovedActionDispatchRecord,
): Readonly<ApprovedActionExecutionRecord> {
const execution = createApprovedActionExecution(dispatchValue);
client
.prepare(
`INSERT INTO "QingLong3ApprovedActionExecutions" (${EXECUTION_COLUMNS})
VALUES (${new Array(21).fill('?').join(', ')})`,
)
.run(...executionValues(execution));
return execution;
}
export function findLocalApprovedActionExecution(
client: DatabaseSync,
dispatchId: string,
): Readonly<ApprovedActionExecutionRecord> | null {
const row = client
.prepare(
`SELECT "execution_json" AS "executionJson",
"execution_digest" AS "executionDigest"
FROM "QingLong3ApprovedActionExecutions"
WHERE "dispatch_id" = ?`,
)
.get(dispatchId) as Row | undefined;
return row ? parseExecution(row) : null;
}
function snapshot(
client: DatabaseSync,
dispatchId: string,
): Readonly<ApprovedActionExecutionSnapshot> | null {
const row = client
.prepare(
`SELECT execution."execution_json" AS "executionJson",
execution."execution_digest" AS "executionDigest",
dispatch."dispatch_json" AS "dispatchJson"
FROM "QingLong3ApprovedActionExecutions" AS execution
JOIN "QingLong3ApprovedActionDispatches" AS dispatch
ON dispatch."dispatch_id" = execution."dispatch_id"
WHERE execution."dispatch_id" = ?`,
)
.get(dispatchId) as Row | undefined;
if (!row) return null;
return normalizeApprovedActionExecutionSnapshot({
dispatch: parseDispatch(row),
execution: parseExecution(row),
});
}
function replaceExecution(
client: DatabaseSync,
previous: Readonly<ApprovedActionExecutionRecord>,
next: Readonly<ApprovedActionExecutionRecord>,
): void {
const result = client
.prepare(
`UPDATE "QingLong3ApprovedActionExecutions"
SET "dispatch_digest" = ?, "project_id" = ?, "status" = ?,
"version" = ?, "attempt_count" = ?, "max_attempts" = ?,
"eligible_at_ms" = ?, "next_attempt_at_ms" = ?,
"lease_owner" = ?, "lease_token" = ?, "lease_expires_at_ms" = ?,
"started_at_ms" = ?, "result_mutation_id" = ?,
"result_code" = ?, "result_digest" = ?, "completed_at_ms" = ?,
"created_at_ms" = ?, "updated_at_ms" = ?, "execution_json" = ?,
"execution_digest" = ?
WHERE "dispatch_id" = ? AND "version" = ?
AND "execution_digest" = ?`,
)
.run(
...executionValues(next).slice(1),
previous.dispatchId,
previous.version,
previous.executionDigest,
);
if (result.changes !== 1) {
throw new ApprovedActionExecutionFenceConflictError();
}
}
function assertPageSize(value: number): void {
if (!Number.isSafeInteger(value) || value < 1 || value > 64) {
throw new ApprovedActionExecutionStateConflictError();
}
}
function actionTypes(values: readonly string[]): readonly string[] {
if (
!Array.isArray(values) ||
values.length > 64 ||
values.some(
(value) =>
typeof value !== 'string' ||
!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(value),
) ||
new Set(values).size !== values.length
) {
throw new ApprovedActionExecutionStateConflictError();
}
return values;
}
export class LocalSqliteApprovedActionExecutionRepository
implements ApprovedActionExecutionRepository
{
readonly #authority: LocalSqliteOperationAuthority;
readonly #client: DatabaseSync;
constructor(authority: LocalSqliteOperationAuthority | DatabaseSync) {
this.#authority =
authority instanceof LocalSqliteOperationAuthority
? authority
: new LocalSqliteOperationAuthority(authority);
this.#client = this.#authority.client;
}
#enqueue<T>(work: () => T | Promise<T>): Promise<T> {
return this.#authority.enqueue(
async () => {
try {
return await work();
} catch (error) {
throw storageError(error);
}
},
() => new ApprovedActionExecutionUnavailableError(),
);
}
#transaction<T>(work: () => T): T {
this.#client.exec('BEGIN IMMEDIATE');
try {
const result = work();
this.#client.exec('COMMIT');
return result;
} catch (error) {
if (this.#client.isTransaction) this.#client.exec('ROLLBACK');
throw error;
}
}
findExecutionByDispatchId(
dispatchId: string,
): Promise<Readonly<ApprovedActionExecutionSnapshot> | null> {
return this.#enqueue(() => snapshot(this.#client, dispatchId));
}
listDueExecutions(
query: ListDueApprovedActionExecutionsQuery,
): Promise<ListDueApprovedActionExecutionsResult> {
return this.#enqueue(() => {
assertPageSize(query.limit);
const handledActionTypes = actionTypes(query.actionTypes);
const cursor = query.cursor
? normalizeApprovedActionExecutionCursor(query.cursor)
: undefined;
if (!Number.isSafeInteger(query.nowMs) || query.nowMs < 0) {
throw new ApprovedActionExecutionStateConflictError();
}
if (handledActionTypes.length === 0) {
return Object.freeze({
executions: Object.freeze([]),
truncated: false,
});
}
const actionTypePlaceholders = handledActionTypes.map(() => '?').join(',');
const rows = this.#client
.prepare(
`SELECT execution."execution_json" AS "executionJson",
execution."execution_digest" AS "executionDigest",
dispatch."dispatch_json" AS "dispatchJson"
FROM "QingLong3ApprovedActionExecutions" AS execution
JOIN "QingLong3ApprovedActionDispatches" AS dispatch
ON dispatch."dispatch_id" = execution."dispatch_id"
WHERE execution."status" IN ('pending','leased','retry_wait')
AND execution."eligible_at_ms" <= ?
AND dispatch."action_type" IN (${actionTypePlaceholders})
AND (
? IS NULL OR execution."eligible_at_ms" > ? OR
(execution."eligible_at_ms" = ? AND execution."dispatch_id" > ?)
)
ORDER BY execution."eligible_at_ms", execution."dispatch_id"
LIMIT ?`,
)
.all(
query.nowMs,
...handledActionTypes,
cursor?.dispatchId ?? null,
cursor?.eligibleAtMs ?? 0,
cursor?.eligibleAtMs ?? 0,
cursor?.dispatchId ?? '',
query.limit + 1,
) as Row[];
const truncated = rows.length > query.limit;
const pageRows = truncated ? rows.slice(0, query.limit) : rows;
const executions = pageRows.map((row) =>
normalizeApprovedActionExecutionSnapshot({
dispatch: parseDispatch(row),
execution: parseExecution(row),
}),
);
const last = executions.at(-1)?.execution;
return Object.freeze({
executions: Object.freeze(executions),
truncated,
...(truncated && last?.eligibleAtMs !== null
? {
nextCursor: Object.freeze({
eligibleAtMs: last!.eligibleAtMs!,
dispatchId: last!.dispatchId,
}),
}
: {}),
});
});
}
claimExecution(
command: ClaimApprovedActionExecutionCommand,
): Promise<ClaimApprovedActionExecutionResult> {
return this.#enqueue(() =>
this.#transaction(() => {
const current = snapshot(this.#client, command.dispatchId);
if (!current) return Object.freeze({ status: 'not_found' as const });
const effective = approvedActionExecutionEffectiveStatus(
current.execution,
command.nowMs,
);
if (
current.execution.status === 'leased' &&
current.execution.leaseExpiresAtMs !== null &&
current.execution.leaseExpiresAtMs <= command.nowMs &&
current.execution.attemptCount >= current.execution.maxAttempts
) {
return Object.freeze({
status: 'recovery_required' as const,
snapshot: current,
});
}
const due =
current.execution.eligibleAtMs !== null &&
current.execution.eligibleAtMs <= command.nowMs;
if (
effective !== 'pending' &&
effective !== 'retry_wait' &&
!(effective === 'leased' && due)
) {
return Object.freeze({ status: effective, snapshot: current });
}
if (!due) {
return Object.freeze({
status: 'not_due' as const,
snapshot: current,
});
}
const next = claimApprovedActionExecution(current.execution, {
owner: command.owner,
leaseToken: command.leaseToken,
nowMs: command.nowMs,
leaseDurationMs: command.leaseDurationMs,
});
replaceExecution(this.#client, current.execution, next);
return Object.freeze({
status: 'claimed' as const,
snapshot: Object.freeze({
dispatch: current.dispatch,
execution: next,
}),
});
}),
);
}
startExecution(
command: StartApprovedActionExecutionCommand,
): Promise<Readonly<ApprovedActionExecutionSnapshot>> {
return this.#mutate(command.dispatchId, (current) =>
startApprovedActionExecution(current, command),
);
}
renewExecution(
command: RenewApprovedActionExecutionCommand,
): Promise<Readonly<ApprovedActionExecutionSnapshot>> {
return this.#mutate(command.dispatchId, (current) =>
renewApprovedActionExecution(current.execution, {
owner: command.owner,
leaseToken: command.leaseToken,
expectedVersion: command.expectedVersion,
nowMs: command.nowMs,
leaseDurationMs: command.leaseDurationMs,
}),
);
}
releaseExecutionBeforeStart(
command: ReleaseApprovedActionExecutionBeforeStartCommand,
): Promise<Readonly<ApprovedActionExecutionSnapshot>> {
return this.#mutate(command.dispatchId, (current) =>
releaseApprovedActionExecutionBeforeStart(current.execution, {
owner: command.owner,
leaseToken: command.leaseToken,
expectedVersion: command.expectedVersion,
resultMutationId: command.resultMutationId,
resultCode: command.resultCode,
atMs: command.atMs,
...(command.retryAtMs === undefined
? {}
: { retryAtMs: command.retryAtMs }),
}),
);
}
completeExecution(
command: CompleteApprovedActionExecutionCommand,
): Promise<Readonly<ApprovedActionExecutionSnapshot>> {
return this.#mutate(command.dispatchId, (current) =>
completeApprovedActionExecution(current.execution, {
owner: command.owner,
leaseToken: command.leaseToken,
expectedVersion: command.expectedVersion,
resultMutationId: command.resultMutationId,
outcome: command.outcome,
resultCode: command.resultCode,
...(command.resultDigest === undefined
? {}
: { resultDigest: command.resultDigest }),
completedAtMs: command.completedAtMs,
}),
);
}
#mutate(
dispatchId: string,
transition: (
current: Readonly<ApprovedActionExecutionSnapshot>,
) => Readonly<ApprovedActionExecutionRecord>,
): Promise<Readonly<ApprovedActionExecutionSnapshot>> {
return this.#enqueue(() =>
this.#transaction(() => {
const current = snapshot(this.#client, dispatchId);
if (!current) {
throw new ApprovedActionExecutionStateConflictError();
}
const next = transition(current);
replaceExecution(this.#client, current.execution, next);
return Object.freeze({
dispatch: current.dispatch,
execution: next,
});
}),
);
}
}