@pikku/mongodb 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/src/index.d.ts +12 -0
  3. package/dist/src/index.js +9 -0
  4. package/dist/src/mongodb-agent-run-service.d.ts +21 -0
  5. package/dist/src/mongodb-agent-run-service.js +127 -0
  6. package/dist/src/mongodb-ai-storage-service.d.ts +41 -0
  7. package/dist/src/mongodb-ai-storage-service.js +408 -0
  8. package/dist/src/mongodb-channel-store.d.ts +19 -0
  9. package/dist/src/mongodb-channel-store.js +52 -0
  10. package/dist/src/mongodb-deployment-service.d.ts +17 -0
  11. package/dist/src/mongodb-deployment-service.js +75 -0
  12. package/dist/src/mongodb-eventhub-store.d.ts +13 -0
  13. package/dist/src/mongodb-eventhub-store.js +37 -0
  14. package/dist/src/mongodb-secret-service.d.ts +30 -0
  15. package/dist/src/mongodb-secret-service.js +110 -0
  16. package/dist/src/mongodb-workflow-run-service.d.ts +36 -0
  17. package/dist/src/mongodb-workflow-run-service.js +146 -0
  18. package/dist/src/mongodb-workflow-service.d.ts +55 -0
  19. package/dist/src/mongodb-workflow-service.js +351 -0
  20. package/dist/src/pikku-mongodb.d.ts +12 -0
  21. package/dist/src/pikku-mongodb.js +37 -0
  22. package/dist/tsconfig.tsbuildinfo +1 -0
  23. package/package.json +29 -0
  24. package/run-tests.sh +53 -0
  25. package/src/index.ts +14 -0
  26. package/src/mongodb-agent-run-service.ts +206 -0
  27. package/src/mongodb-ai-storage-service.ts +576 -0
  28. package/src/mongodb-channel-store.ts +96 -0
  29. package/src/mongodb-deployment-service.ts +114 -0
  30. package/src/mongodb-eventhub-store.ts +52 -0
  31. package/src/mongodb-secret-service.ts +178 -0
  32. package/src/mongodb-services.test.ts +149 -0
  33. package/src/mongodb-workflow-run-service.ts +246 -0
  34. package/src/mongodb-workflow-service.ts +583 -0
  35. package/src/pikku-mongodb.ts +41 -0
  36. package/tsconfig.json +17 -0
@@ -0,0 +1,52 @@
1
+ import { ChannelStore } from '@pikku/core/channel';
2
+ export class MongoDBChannelStore extends ChannelStore {
3
+ db;
4
+ initialized = false;
5
+ channels;
6
+ subscriptions;
7
+ constructor(db) {
8
+ super();
9
+ this.db = db;
10
+ }
11
+ async init() {
12
+ if (this.initialized)
13
+ return;
14
+ this.channels = this.db.collection('channels');
15
+ this.subscriptions = this.db.collection('channel_subscriptions');
16
+ await this.subscriptions.createIndex({ channelId: 1, topic: 1 }, { unique: true });
17
+ await this.subscriptions.createIndex({ topic: 1 });
18
+ this.initialized = true;
19
+ }
20
+ async addChannel({ channelId, channelName, openingData, }) {
21
+ await this.channels.insertOne({
22
+ _id: channelId,
23
+ channelName,
24
+ openingData: openingData || {},
25
+ userSession: null,
26
+ createdAt: new Date(),
27
+ lastWire: new Date(),
28
+ });
29
+ }
30
+ async removeChannels(channelIds) {
31
+ if (channelIds.length === 0)
32
+ return;
33
+ await this.channels.deleteMany({ _id: { $in: channelIds } });
34
+ await this.subscriptions.deleteMany({ channelId: { $in: channelIds } });
35
+ }
36
+ async setUserSession(channelId, session) {
37
+ await this.channels.updateOne({ _id: channelId }, { $set: { userSession: session } });
38
+ }
39
+ async getChannelAndSession(channelId) {
40
+ const row = await this.channels.findOne({ _id: channelId });
41
+ if (!row) {
42
+ throw new Error(`Channel not found: ${channelId}`);
43
+ }
44
+ return {
45
+ channelId: row._id,
46
+ channelName: row.channelName,
47
+ openingData: row.openingData ?? {},
48
+ session: (row.userSession ?? {}),
49
+ };
50
+ }
51
+ async close() { }
52
+ }
@@ -0,0 +1,17 @@
1
+ import type { DeploymentService, DeploymentServiceConfig, DeploymentConfig, DeploymentInfo } from '@pikku/core/services';
2
+ import type { Db } from 'mongodb';
3
+ export declare class MongoDBDeploymentService implements DeploymentService {
4
+ private db;
5
+ private initialized;
6
+ private heartbeatTimer?;
7
+ private deploymentConfig?;
8
+ private heartbeatInterval;
9
+ private heartbeatTtl;
10
+ private deployments;
11
+ constructor(config: DeploymentServiceConfig, db: Db);
12
+ init(): Promise<void>;
13
+ start(config: DeploymentConfig): Promise<void>;
14
+ stop(): Promise<void>;
15
+ findFunction(name: string): Promise<DeploymentInfo[]>;
16
+ private sendHeartbeat;
17
+ }
@@ -0,0 +1,75 @@
1
+ import { getAllFunctionNames } from '@pikku/core/function';
2
+ export class MongoDBDeploymentService {
3
+ db;
4
+ initialized = false;
5
+ heartbeatTimer;
6
+ deploymentConfig;
7
+ heartbeatInterval;
8
+ heartbeatTtl;
9
+ deployments;
10
+ constructor(config, db) {
11
+ this.db = db;
12
+ this.heartbeatInterval = config.heartbeatInterval ?? 10000;
13
+ this.heartbeatTtl = config.heartbeatTtl ?? 30000;
14
+ }
15
+ async init() {
16
+ if (this.initialized)
17
+ return;
18
+ this.deployments = this.db.collection('pikku_deployments');
19
+ await this.deployments.createIndex({ lastHeartbeat: 1 });
20
+ await this.deployments.createIndex({ functions: 1 });
21
+ this.initialized = true;
22
+ }
23
+ async start(config) {
24
+ const functions = config.functions ?? getAllFunctionNames();
25
+ this.deploymentConfig = { ...config, functions };
26
+ const now = new Date();
27
+ await this.deployments.updateOne({ _id: config.deploymentId }, {
28
+ $set: {
29
+ endpoint: config.endpoint,
30
+ lastHeartbeat: now,
31
+ functions,
32
+ },
33
+ $setOnInsert: {
34
+ _id: config.deploymentId,
35
+ createdAt: now,
36
+ },
37
+ }, { upsert: true });
38
+ this.heartbeatTimer = setInterval(() => this.sendHeartbeat(), this.heartbeatInterval);
39
+ }
40
+ async stop() {
41
+ if (this.heartbeatTimer) {
42
+ clearInterval(this.heartbeatTimer);
43
+ this.heartbeatTimer = undefined;
44
+ }
45
+ if (this.deploymentConfig) {
46
+ await this.deployments.deleteOne({
47
+ _id: this.deploymentConfig.deploymentId,
48
+ });
49
+ }
50
+ }
51
+ async findFunction(name) {
52
+ const cutoff = new Date(Date.now() - this.heartbeatTtl);
53
+ const result = await this.deployments
54
+ .find({
55
+ functions: name,
56
+ lastHeartbeat: { $gt: cutoff },
57
+ })
58
+ .sort({ lastHeartbeat: -1 })
59
+ .toArray();
60
+ return result.map((row) => ({
61
+ deploymentId: row._id,
62
+ endpoint: row.endpoint,
63
+ }));
64
+ }
65
+ async sendHeartbeat() {
66
+ if (!this.deploymentConfig)
67
+ return;
68
+ try {
69
+ await this.deployments.updateOne({ _id: this.deploymentConfig.deploymentId }, { $set: { lastHeartbeat: new Date() } });
70
+ }
71
+ catch {
72
+ // Heartbeat failed, will retry on next interval
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,13 @@
1
+ import { EventHubStore } from '@pikku/core/channel';
2
+ import type { Db } from 'mongodb';
3
+ export declare class MongoDBEventHubStore extends EventHubStore {
4
+ private db;
5
+ private initialized;
6
+ private subscriptions;
7
+ constructor(db: Db);
8
+ init(): Promise<void>;
9
+ getChannelIdsForTopic(topic: string): Promise<string[]>;
10
+ subscribe(topic: string, channelId: string): Promise<boolean>;
11
+ unsubscribe(topic: string, channelId: string): Promise<boolean>;
12
+ close(): Promise<void>;
13
+ }
@@ -0,0 +1,37 @@
1
+ import { EventHubStore } from '@pikku/core/channel';
2
+ export class MongoDBEventHubStore extends EventHubStore {
3
+ db;
4
+ initialized = false;
5
+ subscriptions;
6
+ constructor(db) {
7
+ super();
8
+ this.db = db;
9
+ }
10
+ async init() {
11
+ if (this.initialized)
12
+ return;
13
+ this.subscriptions = this.db.collection('channel_subscriptions');
14
+ this.initialized = true;
15
+ }
16
+ async getChannelIdsForTopic(topic) {
17
+ const result = await this.subscriptions
18
+ .find({ topic })
19
+ .project({ channelId: 1 })
20
+ .toArray();
21
+ return result.map((row) => row.channelId);
22
+ }
23
+ async subscribe(topic, channelId) {
24
+ try {
25
+ await this.subscriptions.updateOne({ channelId, topic }, { $setOnInsert: { channelId, topic } }, { upsert: true });
26
+ return true;
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ }
32
+ async unsubscribe(topic, channelId) {
33
+ const result = await this.subscriptions.deleteOne({ channelId, topic });
34
+ return result.deletedCount > 0;
35
+ }
36
+ async close() { }
37
+ }
@@ -0,0 +1,30 @@
1
+ import type { SecretService } from '@pikku/core/services';
2
+ import type { Db } from 'mongodb';
3
+ export interface MongoDBSecretServiceConfig {
4
+ key: string;
5
+ keyVersion?: number;
6
+ previousKey?: string;
7
+ audit?: boolean;
8
+ auditReads?: boolean;
9
+ }
10
+ export declare class MongoDBSecretService implements SecretService {
11
+ private db;
12
+ private initialized;
13
+ private key;
14
+ private keyVersion;
15
+ private previousKey?;
16
+ private audit;
17
+ private auditReads;
18
+ private secrets;
19
+ private secretsAudit;
20
+ constructor(db: Db, config: MongoDBSecretServiceConfig);
21
+ init(): Promise<void>;
22
+ private logAudit;
23
+ private getKEK;
24
+ getSecret(key: string): Promise<string>;
25
+ getSecretJSON<R = {}>(key: string): Promise<R>;
26
+ hasSecret(key: string): Promise<boolean>;
27
+ setSecretJSON(key: string, value: unknown): Promise<void>;
28
+ deleteSecret(key: string): Promise<void>;
29
+ rotateKEK(): Promise<number>;
30
+ }
@@ -0,0 +1,110 @@
1
+ import { envelopeEncrypt, envelopeDecrypt, envelopeRewrap, } from '@pikku/core/crypto-utils';
2
+ export class MongoDBSecretService {
3
+ db;
4
+ initialized = false;
5
+ key;
6
+ keyVersion;
7
+ previousKey;
8
+ audit;
9
+ auditReads;
10
+ secrets;
11
+ secretsAudit;
12
+ constructor(db, config) {
13
+ this.db = db;
14
+ this.key = config.key;
15
+ this.keyVersion = config.keyVersion ?? 1;
16
+ this.previousKey = config.previousKey;
17
+ this.audit = config.audit ?? false;
18
+ this.auditReads = config.auditReads ?? false;
19
+ }
20
+ async init() {
21
+ if (this.initialized)
22
+ return;
23
+ this.secrets = this.db.collection('secrets');
24
+ this.secretsAudit = this.db.collection('secrets_audit');
25
+ await this.secrets.createIndex({ _id: 1 });
26
+ if (this.audit) {
27
+ await this.secretsAudit.createIndex({ secretKey: 1 });
28
+ }
29
+ this.initialized = true;
30
+ }
31
+ async logAudit(secretKey, action) {
32
+ if (!this.audit)
33
+ return;
34
+ if (action === 'read' && !this.auditReads)
35
+ return;
36
+ await this.secretsAudit.insertOne({
37
+ _id: crypto.randomUUID(),
38
+ secretKey,
39
+ action,
40
+ performedAt: new Date(),
41
+ });
42
+ }
43
+ getKEK(version) {
44
+ if (version === this.keyVersion)
45
+ return this.key;
46
+ if (this.previousKey)
47
+ return this.previousKey;
48
+ throw new Error(`No KEK available for key_version ${version}`);
49
+ }
50
+ async getSecret(key) {
51
+ const row = await this.secrets.findOne({ _id: key });
52
+ if (!row)
53
+ throw new Error(`Secret not found: ${key}`);
54
+ const kek = this.getKEK(row.keyVersion);
55
+ const result = await envelopeDecrypt(kek, row.ciphertext, row.wrappedDek);
56
+ await this.logAudit(key, 'read');
57
+ return result;
58
+ }
59
+ async getSecretJSON(key) {
60
+ const raw = await this.getSecret(key);
61
+ return JSON.parse(raw);
62
+ }
63
+ async hasSecret(key) {
64
+ const count = await this.secrets.countDocuments({ _id: key }, { limit: 1 });
65
+ return count > 0;
66
+ }
67
+ async setSecretJSON(key, value) {
68
+ const plaintext = JSON.stringify(value);
69
+ const { ciphertext, wrappedDEK } = await envelopeEncrypt(this.key, plaintext);
70
+ const now = new Date();
71
+ await this.secrets.updateOne({ _id: key }, {
72
+ $set: {
73
+ ciphertext,
74
+ wrappedDek: wrappedDEK,
75
+ keyVersion: this.keyVersion,
76
+ updatedAt: now,
77
+ },
78
+ $setOnInsert: {
79
+ _id: key,
80
+ createdAt: now,
81
+ },
82
+ }, { upsert: true });
83
+ await this.logAudit(key, 'write');
84
+ }
85
+ async deleteSecret(key) {
86
+ await this.secrets.deleteOne({ _id: key });
87
+ await this.logAudit(key, 'delete');
88
+ }
89
+ async rotateKEK() {
90
+ if (!this.previousKey) {
91
+ throw new Error('No previousKey configured — nothing to rotate from');
92
+ }
93
+ const rows = await this.secrets
94
+ .find({ keyVersion: { $lt: this.keyVersion } })
95
+ .project({ _id: 1, wrappedDek: 1 })
96
+ .toArray();
97
+ for (const row of rows) {
98
+ const newWrappedDEK = await envelopeRewrap(this.previousKey, this.key, row.wrappedDek);
99
+ await this.secrets.updateOne({ _id: row._id }, {
100
+ $set: {
101
+ wrappedDek: newWrappedDEK,
102
+ keyVersion: this.keyVersion,
103
+ updatedAt: new Date(),
104
+ },
105
+ });
106
+ await this.logAudit(row._id, 'rotate');
107
+ }
108
+ return rows.length;
109
+ }
110
+ }
@@ -0,0 +1,36 @@
1
+ import type { WorkflowRun, StepState, WorkflowRunService } from '@pikku/core/workflow';
2
+ import type { Db } from 'mongodb';
3
+ export declare class MongoDBWorkflowRunService implements WorkflowRunService {
4
+ private runs;
5
+ private steps;
6
+ private stepHistory;
7
+ private versions;
8
+ constructor(db: Db);
9
+ listRuns(options?: {
10
+ workflowName?: string;
11
+ status?: string;
12
+ limit?: number;
13
+ offset?: number;
14
+ }): Promise<WorkflowRun[]>;
15
+ getRun(id: string): Promise<WorkflowRun | null>;
16
+ getRunSteps(runId: string): Promise<Array<StepState & {
17
+ stepName: string;
18
+ rpcName?: string;
19
+ data?: any;
20
+ }>>;
21
+ getRunHistory(runId: string): Promise<Array<StepState & {
22
+ stepName: string;
23
+ }>>;
24
+ getDistinctWorkflowNames(): Promise<string[]>;
25
+ getWorkflowVersion(name: string, graphHash: string): Promise<{
26
+ graph: any;
27
+ source: string;
28
+ } | null>;
29
+ getAIGeneratedWorkflows(agentName?: string): Promise<Array<{
30
+ workflowName: string;
31
+ graphHash: string;
32
+ graph: any;
33
+ }>>;
34
+ deleteRun(id: string): Promise<boolean>;
35
+ private mapRunRow;
36
+ }
@@ -0,0 +1,146 @@
1
+ export class MongoDBWorkflowRunService {
2
+ runs;
3
+ steps;
4
+ stepHistory;
5
+ versions;
6
+ constructor(db) {
7
+ this.runs = db.collection('workflow_runs');
8
+ this.steps = db.collection('workflow_step');
9
+ this.stepHistory = db.collection('workflow_step_history');
10
+ this.versions = db.collection('workflow_versions');
11
+ }
12
+ async listRuns(options) {
13
+ const { workflowName, status, limit = 50, offset = 0 } = options ?? {};
14
+ const filter = {};
15
+ if (workflowName)
16
+ filter.workflow = workflowName;
17
+ if (status)
18
+ filter.status = status;
19
+ const result = await this.runs
20
+ .find(filter)
21
+ .sort({ createdAt: -1 })
22
+ .skip(offset)
23
+ .limit(limit)
24
+ .toArray();
25
+ return result.map((row) => this.mapRunRow(row));
26
+ }
27
+ async getRun(id) {
28
+ const row = await this.runs.findOne({ _id: id });
29
+ if (!row)
30
+ return null;
31
+ return this.mapRunRow(row);
32
+ }
33
+ async getRunSteps(runId) {
34
+ const result = await this.steps
35
+ .find({ workflowRunId: runId })
36
+ .sort({ createdAt: 1 })
37
+ .toArray();
38
+ const stepIds = result.map((r) => r._id);
39
+ const historyCounts = await this.stepHistory
40
+ .aggregate([
41
+ { $match: { workflowStepId: { $in: stepIds } } },
42
+ { $group: { _id: '$workflowStepId', count: { $sum: 1 } } },
43
+ ])
44
+ .toArray();
45
+ const countMap = new Map(historyCounts.map((h) => [h._id, h.count]));
46
+ return result.map((row) => ({
47
+ stepId: row._id,
48
+ stepName: row.stepName,
49
+ rpcName: row.rpcName ?? undefined,
50
+ data: row.data ?? undefined,
51
+ status: row.status,
52
+ result: row.result ?? undefined,
53
+ error: row.error ?? undefined,
54
+ attemptCount: countMap.get(row._id) ?? 1,
55
+ retries: row.retries != null ? Number(row.retries) : undefined,
56
+ retryDelay: row.retryDelay ?? undefined,
57
+ createdAt: new Date(row.createdAt),
58
+ updatedAt: new Date(row.updatedAt),
59
+ }));
60
+ }
61
+ async getRunHistory(runId) {
62
+ const steps = await this.steps.find({ workflowRunId: runId }).toArray();
63
+ const stepMap = new Map(steps.map((s) => [s._id, s]));
64
+ const stepIds = steps.map((s) => s._id);
65
+ const history = await this.stepHistory
66
+ .find({ workflowStepId: { $in: stepIds } })
67
+ .sort({ createdAt: 1 })
68
+ .toArray();
69
+ let attemptCounters = {};
70
+ return history.map((row) => {
71
+ const stepId = row.workflowStepId;
72
+ const step = stepMap.get(stepId);
73
+ attemptCounters[stepId] = (attemptCounters[stepId] ?? 0) + 1;
74
+ return {
75
+ stepId,
76
+ stepName: step.stepName,
77
+ status: row.status,
78
+ result: row.result ?? undefined,
79
+ error: row.error ?? undefined,
80
+ attemptCount: attemptCounters[stepId],
81
+ retries: step.retries != null ? Number(step.retries) : undefined,
82
+ retryDelay: step.retryDelay ?? undefined,
83
+ createdAt: new Date(row.createdAt),
84
+ updatedAt: new Date(row.createdAt),
85
+ runningAt: row.runningAt ? new Date(row.runningAt) : undefined,
86
+ scheduledAt: row.scheduledAt ? new Date(row.scheduledAt) : undefined,
87
+ succeededAt: row.succeededAt ? new Date(row.succeededAt) : undefined,
88
+ failedAt: row.failedAt ? new Date(row.failedAt) : undefined,
89
+ };
90
+ });
91
+ }
92
+ async getDistinctWorkflowNames() {
93
+ const result = await this.runs.distinct('workflow');
94
+ return result.sort();
95
+ }
96
+ async getWorkflowVersion(name, graphHash) {
97
+ const row = await this.versions.findOne({
98
+ workflowName: name,
99
+ graphHash,
100
+ });
101
+ if (!row)
102
+ return null;
103
+ return {
104
+ graph: row.graph,
105
+ source: row.source,
106
+ };
107
+ }
108
+ async getAIGeneratedWorkflows(agentName) {
109
+ const filter = { source: 'ai-agent', status: 'active' };
110
+ if (agentName) {
111
+ const escaped = agentName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
112
+ filter.workflowName = { $regex: `^ai:${escaped}:` };
113
+ }
114
+ const rows = await this.versions.find(filter).toArray();
115
+ return rows.map((row) => ({
116
+ workflowName: row.workflowName,
117
+ graphHash: row.graphHash,
118
+ graph: row.graph,
119
+ }));
120
+ }
121
+ async deleteRun(id) {
122
+ const steps = await this.steps.find({ workflowRunId: id }).toArray();
123
+ const stepIds = steps.map((s) => s._id);
124
+ if (stepIds.length > 0) {
125
+ await this.stepHistory.deleteMany({ workflowStepId: { $in: stepIds } });
126
+ await this.steps.deleteMany({ workflowRunId: id });
127
+ }
128
+ const result = await this.runs.deleteOne({ _id: id });
129
+ return result.deletedCount > 0;
130
+ }
131
+ mapRunRow(row) {
132
+ return {
133
+ id: row._id,
134
+ workflow: row.workflow,
135
+ status: row.status,
136
+ input: row.input,
137
+ output: row.output ?? undefined,
138
+ error: row.error ?? undefined,
139
+ inline: row.inline,
140
+ graphHash: row.graphHash ?? undefined,
141
+ wire: row.wire ?? { type: 'unknown' },
142
+ createdAt: new Date(row.createdAt),
143
+ updatedAt: new Date(row.updatedAt),
144
+ };
145
+ }
146
+ }
@@ -0,0 +1,55 @@
1
+ import type { SerializedError } from '@pikku/core';
2
+ import { PikkuWorkflowService, type WorkflowRun, type WorkflowRunWire, type StepState, type WorkflowStatus, type WorkflowVersionStatus } from '@pikku/core/workflow';
3
+ import type { Db } from 'mongodb';
4
+ export declare class MongoDBWorkflowService extends PikkuWorkflowService {
5
+ private initialized;
6
+ private runService;
7
+ private runs;
8
+ private steps;
9
+ private stepHistory;
10
+ private versions;
11
+ constructor(db: Db);
12
+ init(): Promise<void>;
13
+ createRun(workflowName: string, input: any, inline: boolean, graphHash: string, wire: WorkflowRunWire): Promise<string>;
14
+ getRun(id: string): Promise<WorkflowRun | null>;
15
+ updateRunStatus(id: string, status: WorkflowStatus, output?: any, error?: SerializedError): Promise<void>;
16
+ insertStepState(runId: string, stepName: string, rpcName: string | null, data: any, stepOptions?: {
17
+ retries?: number;
18
+ retryDelay?: string | number;
19
+ }): Promise<StepState>;
20
+ getStepState(runId: string, stepName: string): Promise<StepState>;
21
+ getRunHistory(runId: string): Promise<Array<StepState & {
22
+ stepName: string;
23
+ }>>;
24
+ setStepRunning(stepId: string): Promise<void>;
25
+ setStepScheduled(stepId: string): Promise<void>;
26
+ private insertHistoryRecord;
27
+ private getTimestampFieldForStatus;
28
+ setStepResult(stepId: string, result: any): Promise<void>;
29
+ setStepError(stepId: string, error: Error): Promise<void>;
30
+ createRetryAttempt(stepId: string, status: 'pending' | 'running'): Promise<StepState>;
31
+ withRunLock<T>(id: string, fn: () => Promise<T>): Promise<T>;
32
+ withStepLock<T>(_runId: string, _stepName: string, fn: () => Promise<T>): Promise<T>;
33
+ getCompletedGraphState(runId: string): Promise<{
34
+ completedNodeIds: string[];
35
+ failedNodeIds: string[];
36
+ branchKeys: Record<string, string>;
37
+ }>;
38
+ getNodesWithoutSteps(runId: string, nodeIds: string[]): Promise<string[]>;
39
+ getNodeResults(runId: string, nodeIds: string[]): Promise<Record<string, any>>;
40
+ setBranchTaken(stepId: string, branchKey: string): Promise<void>;
41
+ updateRunState(runId: string, name: string, value: unknown): Promise<void>;
42
+ getRunState(runId: string): Promise<Record<string, unknown>>;
43
+ upsertWorkflowVersion(name: string, graphHash: string, graph: any, source: string, status?: WorkflowVersionStatus): Promise<void>;
44
+ updateWorkflowVersionStatus(name: string, graphHash: string, status: WorkflowVersionStatus): Promise<void>;
45
+ getWorkflowVersion(name: string, graphHash: string): Promise<{
46
+ graph: any;
47
+ source: string;
48
+ } | null>;
49
+ getAIGeneratedWorkflows(agentName?: string): Promise<Array<{
50
+ workflowName: string;
51
+ graphHash: string;
52
+ graph: any;
53
+ }>>;
54
+ close(): Promise<void>;
55
+ }