@pikku/mongodb 0.12.4 → 0.12.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,11 +1,37 @@
1
1
  # @pikku/mongodb
2
2
 
3
+ ## 0.12.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 4b5c75b: feat(auth-js): wire OIDC config (issuer/tenantId) as variables, expand provider registry
8
+ - Move `issuer` and `tenantId` out of the secret blob for OIDC providers (auth0, okta, azure-ad, keycloak, cognito, microsoft-entra-id) — they are public config URLs, not secrets. Now registered via `wireVariable` and loaded at runtime via `services.variables.get()`.
9
+ - Expand provider registry from 13 to 31 providers: reddit, notion, instagram, zoom, figma, tiktok, threads, patreon, dropbox, bitbucket, hubspot, salesforce, atlassian, strava, keycloak, cognito, microsoft-entra-id added.
10
+ - `serialize-auth-gen` emits `wireVariable({...})` declarations and `services.variables.get()` calls in the generated factory for OIDC providers.
11
+ - Integration verifier exercises real `/auth/providers` endpoint with `LocalSecretService` + `LocalVariablesService`, including a spy test proving `services.variables.get('AUTH0_ISSUER')` is called at request time.
12
+
13
+ - Updated dependencies [4b5c75b]
14
+ - Updated dependencies [4b5c75b]
15
+ - @pikku/core@0.12.27
16
+
17
+ ## 0.12.5
18
+
19
+ ### Patch Changes
20
+
21
+ - b9ed73e: Add deterministic workflow planned-step metadata support and SSE init stream payload generation.
22
+ - Persist `deterministic` and `plannedSteps` on workflow runs in core and service adapters.
23
+ - Expose planned-step metadata on workflow run status responses.
24
+ - Emit an initial `type: 'init'` SSE event for deterministic workflow streams before incremental updates.
25
+ - Add CLI tests covering serialized stream route output for init/update/done event behavior.
26
+
27
+ - Updated dependencies [b9ed73e]
28
+ - @pikku/core@0.12.19
29
+
3
30
  ## 0.12.4
4
31
 
5
32
  ### Patch Changes
6
33
 
7
34
  - 311c0c4: Unify session persistence through SessionStore, remove session blob from ChannelStore
8
-
9
35
  - PikkuSessionService now persists sessions via SessionStore on set()/clear() instead of every function call
10
36
  - ChannelStore no longer stores session data — maps channelId to pikkuUserId only
11
37
  - ChannelStore API: setUserSession/getChannelAndSession replaced with setPikkuUserId/getChannel
@@ -8,6 +8,7 @@ export declare class MongoDBAgentRunService implements AgentRunService {
8
8
  constructor(db: Db);
9
9
  listThreads(options?: {
10
10
  agentName?: string;
11
+ resourceId?: string;
11
12
  limit?: number;
12
13
  offset?: number;
13
14
  }): Promise<AIThread[]>;
@@ -10,8 +10,11 @@ export class MongoDBAgentRunService {
10
10
  this.runs = db.collection('ai_run');
11
11
  }
12
12
  async listThreads(options) {
13
- const { agentName, limit = 50, offset = 0 } = options ?? {};
13
+ const { agentName, resourceId, limit = 50, offset = 0 } = options ?? {};
14
14
  let filter = {};
15
+ if (resourceId) {
16
+ filter.resourceId = resourceId;
17
+ }
15
18
  if (agentName) {
16
19
  const threadIds = await this.runs.distinct('threadId', {
17
20
  agentName,
@@ -14,5 +14,8 @@ export declare class MongoDBChannelStore extends ChannelStore {
14
14
  getChannel(channelId: string): Promise<Channel & {
15
15
  pikkuUserId?: string;
16
16
  }>;
17
+ setState(channelId: string, state: unknown): Promise<void>;
18
+ getState(channelId: string): Promise<unknown | undefined>;
19
+ clearState(channelId: string): Promise<void>;
17
20
  close(): Promise<void>;
18
21
  }
@@ -48,5 +48,15 @@ export class MongoDBChannelStore extends ChannelStore {
48
48
  pikkuUserId: row.pikkuUserId ?? undefined,
49
49
  };
50
50
  }
51
+ async setState(channelId, state) {
52
+ await this.channels.updateOne({ _id: channelId }, { $set: { state } });
53
+ }
54
+ async getState(channelId) {
55
+ const row = await this.channels.findOne({ _id: channelId }, { projection: { state: 1 } });
56
+ return row?.state ?? undefined;
57
+ }
58
+ async clearState(channelId) {
59
+ await this.channels.updateOne({ _id: channelId }, { $unset: { state: '' } });
60
+ }
51
61
  async close() { }
52
62
  }
@@ -21,10 +21,10 @@ export declare class MongoDBSecretService implements SecretService {
21
21
  init(): Promise<void>;
22
22
  private logAudit;
23
23
  private getKEK;
24
- getSecret(key: string): Promise<string>;
25
- getSecretJSON<R = {}>(key: string): Promise<R>;
24
+ getSecret<T = string>(key: string): Promise<T>;
26
25
  hasSecret(key: string): Promise<boolean>;
27
- setSecretJSON(key: string, value: unknown): Promise<void>;
26
+ setSecret(key: string, value: unknown): Promise<void>;
28
27
  deleteSecret(key: string): Promise<void>;
28
+ getSecrets(keys: string[]): Promise<Record<string, unknown>>;
29
29
  rotateKEK(): Promise<number>;
30
30
  }
@@ -56,17 +56,12 @@ export class MongoDBSecretService {
56
56
  await this.logAudit(key, 'read');
57
57
  return result;
58
58
  }
59
- async getSecretJSON(key) {
60
- const raw = await this.getSecret(key);
61
- return JSON.parse(raw);
62
- }
63
59
  async hasSecret(key) {
64
60
  const count = await this.secrets.countDocuments({ _id: key }, { limit: 1 });
65
61
  return count > 0;
66
62
  }
67
- async setSecretJSON(key, value) {
68
- const plaintext = JSON.stringify(value);
69
- const { ciphertext, wrappedDEK } = await envelopeEncrypt(this.key, plaintext);
63
+ async setSecret(key, value) {
64
+ const { ciphertext, wrappedDEK } = await envelopeEncrypt(this.key, value);
70
65
  const now = new Date();
71
66
  await this.secrets.updateOne({ _id: key }, {
72
67
  $set: {
@@ -86,6 +81,22 @@ export class MongoDBSecretService {
86
81
  await this.secrets.deleteOne({ _id: key });
87
82
  await this.logAudit(key, 'delete');
88
83
  }
84
+ async getSecrets(keys) {
85
+ const rows = await this.secrets
86
+ .find({ _id: { $in: keys } })
87
+ .toArray();
88
+ const out = {};
89
+ for (const row of rows) {
90
+ try {
91
+ const kek = this.getKEK(row.keyVersion);
92
+ out[row._id] = await envelopeDecrypt(kek, row.ciphertext, row.wrappedDek);
93
+ }
94
+ catch {
95
+ // skip secrets that fail to decrypt
96
+ }
97
+ }
98
+ return out;
99
+ }
89
100
  async rotateKEK() {
90
101
  if (!this.previousKey) {
91
102
  throw new Error('No previousKey configured — nothing to rotate from');
@@ -138,6 +138,8 @@ export class MongoDBWorkflowRunService {
138
138
  error: row.error ?? undefined,
139
139
  inline: row.inline,
140
140
  graphHash: row.graphHash ?? undefined,
141
+ deterministic: row.deterministic ?? undefined,
142
+ plannedSteps: row.plannedSteps ?? undefined,
141
143
  wire: row.wire ?? { type: 'unknown' },
142
144
  createdAt: new Date(row.createdAt),
143
145
  updatedAt: new Date(row.updatedAt),
@@ -1,5 +1,5 @@
1
1
  import type { SerializedError } from '@pikku/core';
2
- import { PikkuWorkflowService, type WorkflowRun, type WorkflowRunWire, type StepState, type WorkflowStatus, type WorkflowVersionStatus } from '@pikku/core/workflow';
2
+ import { PikkuWorkflowService, type WorkflowPlannedStep, type WorkflowRun, type WorkflowRunWire, type StepState, type WorkflowStatus, type WorkflowVersionStatus } from '@pikku/core/workflow';
3
3
  import type { Db } from 'mongodb';
4
4
  export declare class MongoDBWorkflowService extends PikkuWorkflowService {
5
5
  private initialized;
@@ -10,10 +10,13 @@ export declare class MongoDBWorkflowService extends PikkuWorkflowService {
10
10
  private versions;
11
11
  constructor(db: Db);
12
12
  init(): Promise<void>;
13
- createRun(workflowName: string, input: any, inline: boolean, graphHash: string, wire: WorkflowRunWire): Promise<string>;
13
+ protected createRunImpl(workflowName: string, input: any, inline: boolean, graphHash: string, wire: WorkflowRunWire, options?: {
14
+ deterministic?: boolean;
15
+ plannedSteps?: WorkflowPlannedStep[];
16
+ }): Promise<string>;
14
17
  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?: {
18
+ protected updateRunStatusImpl(id: string, status: WorkflowStatus, output?: any, error?: SerializedError): Promise<void>;
19
+ protected insertStepStateImpl(runId: string, stepName: string, rpcName: string | null, data: any, stepOptions?: {
17
20
  retries?: number;
18
21
  retryDelay?: string | number;
19
22
  }): Promise<StepState>;
@@ -21,14 +24,14 @@ export declare class MongoDBWorkflowService extends PikkuWorkflowService {
21
24
  getRunHistory(runId: string): Promise<Array<StepState & {
22
25
  stepName: string;
23
26
  }>>;
24
- setStepRunning(stepId: string): Promise<void>;
25
- setStepScheduled(stepId: string): Promise<void>;
27
+ protected setStepRunningImpl(stepId: string): Promise<void>;
28
+ protected setStepScheduledImpl(stepId: string): Promise<void>;
26
29
  private insertHistoryRecord;
27
30
  private getTimestampFieldForStatus;
28
- setStepChildRunId(stepId: string, childRunId: string): Promise<void>;
29
- setStepResult(stepId: string, result: any): Promise<void>;
30
- setStepError(stepId: string, error: Error): Promise<void>;
31
- createRetryAttempt(stepId: string, status: 'pending' | 'running'): Promise<StepState>;
31
+ protected setStepChildRunIdImpl(stepId: string, childRunId: string): Promise<void>;
32
+ protected setStepResultImpl(stepId: string, result: any): Promise<void>;
33
+ protected setStepErrorImpl(stepId: string, error: Error): Promise<void>;
34
+ protected createRetryAttemptImpl(stepId: string, status: 'pending' | 'running'): Promise<StepState>;
32
35
  withRunLock<T>(id: string, fn: () => Promise<T>): Promise<T>;
33
36
  withStepLock<T>(_runId: string, _stepName: string, fn: () => Promise<T>): Promise<T>;
34
37
  getCompletedGraphState(runId: string): Promise<{
@@ -38,11 +41,11 @@ export declare class MongoDBWorkflowService extends PikkuWorkflowService {
38
41
  }>;
39
42
  getNodesWithoutSteps(runId: string, nodeIds: string[]): Promise<string[]>;
40
43
  getNodeResults(runId: string, nodeIds: string[]): Promise<Record<string, any>>;
41
- setBranchTaken(stepId: string, branchKey: string): Promise<void>;
42
- updateRunState(runId: string, name: string, value: unknown): Promise<void>;
44
+ protected setBranchTakenImpl(stepId: string, branchKey: string): Promise<void>;
45
+ protected updateRunStateImpl(runId: string, name: string, value: unknown): Promise<void>;
43
46
  getRunState(runId: string): Promise<Record<string, unknown>>;
44
- upsertWorkflowVersion(name: string, graphHash: string, graph: any, source: string, status?: WorkflowVersionStatus): Promise<void>;
45
- updateWorkflowVersionStatus(name: string, graphHash: string, status: WorkflowVersionStatus): Promise<void>;
47
+ protected upsertWorkflowVersionImpl(name: string, graphHash: string, graph: any, source: string, status?: WorkflowVersionStatus): Promise<void>;
48
+ protected updateWorkflowVersionStatusImpl(name: string, graphHash: string, status: WorkflowVersionStatus): Promise<void>;
46
49
  getWorkflowVersion(name: string, graphHash: string): Promise<{
47
50
  graph: any;
48
51
  source: string;
@@ -28,7 +28,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
28
28
  await this.versions.createIndex({ workflowName: 1, graphHash: 1 }, { unique: true });
29
29
  this.initialized = true;
30
30
  }
31
- async createRun(workflowName, input, inline, graphHash, wire) {
31
+ async createRunImpl(workflowName, input, inline, graphHash, wire, options) {
32
32
  const id = crypto.randomUUID();
33
33
  const now = new Date();
34
34
  await this.runs.insertOne({
@@ -41,6 +41,8 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
41
41
  state: {},
42
42
  inline,
43
43
  graphHash,
44
+ deterministic: options?.deterministic ?? false,
45
+ plannedSteps: options?.plannedSteps ?? [],
44
46
  wire,
45
47
  createdAt: now,
46
48
  updatedAt: now,
@@ -50,7 +52,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
50
52
  async getRun(id) {
51
53
  return this.runService.getRun(id);
52
54
  }
53
- async updateRunStatus(id, status, output, error) {
55
+ async updateRunStatusImpl(id, status, output, error) {
54
56
  await this.runs.updateOne({ _id: id }, {
55
57
  $set: {
56
58
  status,
@@ -60,7 +62,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
60
62
  },
61
63
  });
62
64
  }
63
- async insertStepState(runId, stepName, rpcName, data, stepOptions) {
65
+ async insertStepStateImpl(runId, stepName, rpcName, data, stepOptions) {
64
66
  const stepId = crypto.randomUUID();
65
67
  const now = new Date();
66
68
  await this.steps.insertOne({
@@ -117,7 +119,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
117
119
  async getRunHistory(runId) {
118
120
  return this.runService.getRunHistory(runId);
119
121
  }
120
- async setStepRunning(stepId) {
122
+ async setStepRunningImpl(stepId) {
121
123
  await this.steps.updateOne({ _id: stepId }, { $set: { status: 'running', updatedAt: new Date() } });
122
124
  const latestHistory = await this.stepHistory
123
125
  .find({ workflowStepId: stepId })
@@ -128,7 +130,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
128
130
  await this.stepHistory.updateOne({ _id: latestHistory[0]._id }, { $set: { status: 'running' } });
129
131
  }
130
132
  }
131
- async setStepScheduled(stepId) {
133
+ async setStepScheduledImpl(stepId) {
132
134
  await this.steps.updateOne({ _id: stepId }, { $set: { status: 'scheduled', updatedAt: new Date() } });
133
135
  }
134
136
  async insertHistoryRecord(stepId, status, result, error) {
@@ -165,7 +167,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
165
167
  return 'createdAt';
166
168
  }
167
169
  }
168
- async setStepChildRunId(stepId, childRunId) {
170
+ async setStepChildRunIdImpl(stepId, childRunId) {
169
171
  await this.steps.updateOne({ _id: stepId }, {
170
172
  $set: {
171
173
  childRunId,
@@ -173,7 +175,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
173
175
  },
174
176
  });
175
177
  }
176
- async setStepResult(stepId, result) {
178
+ async setStepResultImpl(stepId, result) {
177
179
  await this.steps.updateOne({ _id: stepId }, {
178
180
  $set: {
179
181
  status: 'succeeded',
@@ -191,7 +193,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
191
193
  await this.stepHistory.updateOne({ _id: latestHistory[0]._id }, { $set: { status: 'succeeded', result } });
192
194
  }
193
195
  }
194
- async setStepError(stepId, error) {
196
+ async setStepErrorImpl(stepId, error) {
195
197
  const serializedError = {
196
198
  message: error.message,
197
199
  stack: error.stack,
@@ -214,7 +216,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
214
216
  await this.stepHistory.updateOne({ _id: latestHistory[0]._id }, { $set: { status: 'failed', error: serializedError } });
215
217
  }
216
218
  }
217
- async createRetryAttempt(stepId, status) {
219
+ async createRetryAttemptImpl(stepId, status) {
218
220
  await this.steps.updateOne({ _id: stepId }, {
219
221
  $set: {
220
222
  status,
@@ -307,10 +309,10 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
307
309
  }
308
310
  return results;
309
311
  }
310
- async setBranchTaken(stepId, branchKey) {
312
+ async setBranchTakenImpl(stepId, branchKey) {
311
313
  await this.steps.updateOne({ _id: stepId }, { $set: { branchTaken: branchKey, updatedAt: new Date() } });
312
314
  }
313
- async updateRunState(runId, name, value) {
315
+ async updateRunStateImpl(runId, name, value) {
314
316
  if (!/^[a-zA-Z0-9_]+$/.test(name)) {
315
317
  throw new Error('Invalid state key name');
316
318
  }
@@ -327,7 +329,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
327
329
  return {};
328
330
  return row.state ?? {};
329
331
  }
330
- async upsertWorkflowVersion(name, graphHash, graph, source, status) {
332
+ async upsertWorkflowVersionImpl(name, graphHash, graph, source, status) {
331
333
  await this.versions.updateOne({ workflowName: name, graphHash }, {
332
334
  $setOnInsert: {
333
335
  workflowName: name,
@@ -339,7 +341,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
339
341
  },
340
342
  }, { upsert: true });
341
343
  }
342
- async updateWorkflowVersionStatus(name, graphHash, status) {
344
+ async updateWorkflowVersionStatusImpl(name, graphHash, status) {
343
345
  await this.versions.updateOne({ workflowName: name, graphHash }, { $set: { status } });
344
346
  }
345
347
  async getWorkflowVersion(name, graphHash) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/mongodb",
3
- "version": "0.12.4",
3
+ "version": "0.12.6",
4
4
  "author": "yasser.fadl@gmail.com",
5
5
  "license": "MIT",
6
6
  "module": "dist/src/index.js",
@@ -17,7 +17,7 @@
17
17
  "prepublishOnly": "yarn build"
18
18
  },
19
19
  "peerDependencies": {
20
- "@pikku/core": "^0.12.18"
20
+ "@pikku/core": "^0.12.27"
21
21
  },
22
22
  "dependencies": {
23
23
  "mongodb": "^6.12.0"
@@ -26,5 +26,8 @@
26
26
  "mongodb": "^6.12.0",
27
27
  "mongodb-memory-server": "^10.4.0",
28
28
  "typescript": "^5.9"
29
+ },
30
+ "exports": {
31
+ ".": "./dist/src/index.js"
29
32
  }
30
33
  }
@@ -70,13 +70,18 @@ export class MongoDBAgentRunService implements AgentRunService {
70
70
 
71
71
  async listThreads(options?: {
72
72
  agentName?: string
73
+ resourceId?: string
73
74
  limit?: number
74
75
  offset?: number
75
76
  }): Promise<AIThread[]> {
76
- const { agentName, limit = 50, offset = 0 } = options ?? {}
77
+ const { agentName, resourceId, limit = 50, offset = 0 } = options ?? {}
77
78
 
78
79
  let filter: Record<string, any> = {}
79
80
 
81
+ if (resourceId) {
82
+ filter.resourceId = resourceId
83
+ }
84
+
80
85
  if (agentName) {
81
86
  const threadIds = await this.runs.distinct('threadId', {
82
87
  agentName,
@@ -8,6 +8,7 @@ interface ChannelDoc {
8
8
  createdAt: Date
9
9
  openingData: any
10
10
  pikkuUserId: string | null
11
+ state?: unknown
11
12
  lastWire: Date
12
13
  }
13
14
 
@@ -88,5 +89,21 @@ export class MongoDBChannelStore extends ChannelStore {
88
89
  }
89
90
  }
90
91
 
92
+ public async setState(channelId: string, state: unknown): Promise<void> {
93
+ await this.channels.updateOne({ _id: channelId }, { $set: { state } })
94
+ }
95
+
96
+ public async getState(channelId: string): Promise<unknown | undefined> {
97
+ const row = await this.channels.findOne(
98
+ { _id: channelId },
99
+ { projection: { state: 1 } }
100
+ )
101
+ return row?.state ?? undefined
102
+ }
103
+
104
+ public async clearState(channelId: string): Promise<void> {
105
+ await this.channels.updateOne({ _id: channelId }, { $unset: { state: '' } })
106
+ }
107
+
91
108
  public async close(): Promise<void> {}
92
109
  }
@@ -87,36 +87,23 @@ export class MongoDBSecretService implements SecretService {
87
87
  throw new Error(`No KEK available for key_version ${version}`)
88
88
  }
89
89
 
90
- async getSecret(key: string): Promise<string> {
90
+ async getSecret<T = string>(key: string): Promise<T> {
91
91
  const row = await this.secrets.findOne({ _id: key })
92
92
  if (!row) throw new Error('Requested secret not found')
93
93
 
94
94
  const kek = this.getKEK(row.keyVersion)
95
- const result = await envelopeDecrypt<string>(
96
- kek,
97
- row.ciphertext,
98
- row.wrappedDek
99
- )
95
+ const result = await envelopeDecrypt<T>(kek, row.ciphertext, row.wrappedDek)
100
96
  await this.logAudit(key, 'read')
101
97
  return result
102
98
  }
103
99
 
104
- async getSecretJSON<R = {}>(key: string): Promise<R> {
105
- const raw = await this.getSecret(key)
106
- return JSON.parse(raw) as R
107
- }
108
-
109
100
  async hasSecret(key: string): Promise<boolean> {
110
101
  const count = await this.secrets.countDocuments({ _id: key }, { limit: 1 })
111
102
  return count > 0
112
103
  }
113
104
 
114
- async setSecretJSON(key: string, value: unknown): Promise<void> {
115
- const plaintext = JSON.stringify(value)
116
- const { ciphertext, wrappedDEK } = await envelopeEncrypt(
117
- this.key,
118
- plaintext
119
- )
105
+ async setSecret(key: string, value: unknown): Promise<void> {
106
+ const { ciphertext, wrappedDEK } = await envelopeEncrypt(this.key, value)
120
107
  const now = new Date()
121
108
 
122
109
  await this.secrets.updateOne(
@@ -144,6 +131,26 @@ export class MongoDBSecretService implements SecretService {
144
131
  await this.logAudit(key, 'delete')
145
132
  }
146
133
 
134
+ async getSecrets(keys: string[]): Promise<Record<string, unknown>> {
135
+ const rows = await this.secrets
136
+ .find({ _id: { $in: keys } } as any)
137
+ .toArray()
138
+ const out: Record<string, unknown> = {}
139
+ for (const row of rows) {
140
+ try {
141
+ const kek = this.getKEK(row.keyVersion)
142
+ out[row._id] = await envelopeDecrypt(
143
+ kek,
144
+ row.ciphertext,
145
+ row.wrappedDek
146
+ )
147
+ } catch {
148
+ // skip secrets that fail to decrypt
149
+ }
150
+ }
151
+ return out
152
+ }
153
+
147
154
  async rotateKEK(): Promise<number> {
148
155
  if (!this.previousKey) {
149
156
  throw new Error('No previousKey configured — nothing to rotate from')
@@ -71,7 +71,7 @@ function registerTests(name: string, getDb: () => Db) {
71
71
  auditReads: true,
72
72
  })
73
73
  await service.init()
74
- await service.setSecretJSON('audit-test', 'value')
74
+ await service.setSecret('audit-test', 'value')
75
75
  await service.getSecret('audit-test')
76
76
  await service.deleteSecret('audit-test')
77
77
 
@@ -94,7 +94,7 @@ function registerTests(name: string, getDb: () => Db) {
94
94
  auditReads: false,
95
95
  })
96
96
  await service.init()
97
- await service.setSecretJSON('no-read-audit', 'value')
97
+ await service.setSecret('no-read-audit', 'value')
98
98
  await service.getSecret('no-read-audit')
99
99
 
100
100
  const logs = await getDb()
@@ -1,4 +1,5 @@
1
1
  import type {
2
+ WorkflowPlannedStep,
2
3
  WorkflowRun,
3
4
  StepState,
4
5
  WorkflowStatus,
@@ -16,6 +17,8 @@ interface WorkflowRunDoc {
16
17
  state: Record<string, unknown>
17
18
  inline: boolean
18
19
  graphHash: string | null
20
+ deterministic?: boolean
21
+ plannedSteps?: WorkflowPlannedStep[]
19
22
  wire: any | null
20
23
  createdAt: Date
21
24
  updatedAt: Date
@@ -238,6 +241,8 @@ export class MongoDBWorkflowRunService implements WorkflowRunService {
238
241
  error: row.error ?? undefined,
239
242
  inline: row.inline,
240
243
  graphHash: row.graphHash ?? undefined,
244
+ deterministic: row.deterministic ?? undefined,
245
+ plannedSteps: row.plannedSteps ?? undefined,
241
246
  wire: row.wire ?? { type: 'unknown' },
242
247
  createdAt: new Date(row.createdAt),
243
248
  updatedAt: new Date(row.updatedAt),
@@ -1,6 +1,7 @@
1
1
  import type { SerializedError } from '@pikku/core'
2
2
  import {
3
3
  PikkuWorkflowService,
4
+ type WorkflowPlannedStep,
4
5
  type WorkflowRun,
5
6
  type WorkflowRunWire,
6
7
  type StepState,
@@ -20,6 +21,8 @@ interface WorkflowRunDoc {
20
21
  state: Record<string, unknown>
21
22
  inline: boolean
22
23
  graphHash: string | null
24
+ deterministic?: boolean
25
+ plannedSteps?: WorkflowPlannedStep[]
23
26
  wire: any | null
24
27
  createdAt: Date
25
28
  updatedAt: Date
@@ -107,12 +110,16 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
107
110
  this.initialized = true
108
111
  }
109
112
 
110
- async createRun(
113
+ protected async createRunImpl(
111
114
  workflowName: string,
112
115
  input: any,
113
116
  inline: boolean,
114
117
  graphHash: string,
115
- wire: WorkflowRunWire
118
+ wire: WorkflowRunWire,
119
+ options?: {
120
+ deterministic?: boolean
121
+ plannedSteps?: WorkflowPlannedStep[]
122
+ }
116
123
  ): Promise<string> {
117
124
  const id = crypto.randomUUID()
118
125
  const now = new Date()
@@ -127,6 +134,8 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
127
134
  state: {},
128
135
  inline,
129
136
  graphHash,
137
+ deterministic: options?.deterministic ?? false,
138
+ plannedSteps: options?.plannedSteps ?? [],
130
139
  wire,
131
140
  createdAt: now,
132
141
  updatedAt: now,
@@ -139,7 +148,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
139
148
  return this.runService.getRun(id)
140
149
  }
141
150
 
142
- async updateRunStatus(
151
+ protected async updateRunStatusImpl(
143
152
  id: string,
144
153
  status: WorkflowStatus,
145
154
  output?: any,
@@ -158,7 +167,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
158
167
  )
159
168
  }
160
169
 
161
- async insertStepState(
170
+ protected async insertStepStateImpl(
162
171
  runId: string,
163
172
  stepName: string,
164
173
  rpcName: string | null,
@@ -234,7 +243,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
234
243
  return this.runService.getRunHistory(runId)
235
244
  }
236
245
 
237
- async setStepRunning(stepId: string): Promise<void> {
246
+ protected async setStepRunningImpl(stepId: string): Promise<void> {
238
247
  await this.steps.updateOne(
239
248
  { _id: stepId },
240
249
  { $set: { status: 'running', updatedAt: new Date() } }
@@ -254,7 +263,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
254
263
  }
255
264
  }
256
265
 
257
- async setStepScheduled(stepId: string): Promise<void> {
266
+ protected async setStepScheduledImpl(stepId: string): Promise<void> {
258
267
  await this.steps.updateOne(
259
268
  { _id: stepId },
260
269
  { $set: { status: 'scheduled', updatedAt: new Date() } }
@@ -304,7 +313,10 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
304
313
  }
305
314
  }
306
315
 
307
- async setStepChildRunId(stepId: string, childRunId: string): Promise<void> {
316
+ protected async setStepChildRunIdImpl(
317
+ stepId: string,
318
+ childRunId: string
319
+ ): Promise<void> {
308
320
  await this.steps.updateOne(
309
321
  { _id: stepId },
310
322
  {
@@ -316,7 +328,10 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
316
328
  )
317
329
  }
318
330
 
319
- async setStepResult(stepId: string, result: any): Promise<void> {
331
+ protected async setStepResultImpl(
332
+ stepId: string,
333
+ result: any
334
+ ): Promise<void> {
320
335
  await this.steps.updateOne(
321
336
  { _id: stepId },
322
337
  {
@@ -343,7 +358,10 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
343
358
  }
344
359
  }
345
360
 
346
- async setStepError(stepId: string, error: Error): Promise<void> {
361
+ protected async setStepErrorImpl(
362
+ stepId: string,
363
+ error: Error
364
+ ): Promise<void> {
347
365
  const serializedError: SerializedError = {
348
366
  message: error.message,
349
367
  stack: error.stack,
@@ -376,7 +394,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
376
394
  }
377
395
  }
378
396
 
379
- async createRetryAttempt(
397
+ protected async createRetryAttemptImpl(
380
398
  stepId: string,
381
399
  status: 'pending' | 'running'
382
400
  ): Promise<StepState> {
@@ -503,14 +521,17 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
503
521
  return results
504
522
  }
505
523
 
506
- async setBranchTaken(stepId: string, branchKey: string): Promise<void> {
524
+ protected async setBranchTakenImpl(
525
+ stepId: string,
526
+ branchKey: string
527
+ ): Promise<void> {
507
528
  await this.steps.updateOne(
508
529
  { _id: stepId },
509
530
  { $set: { branchTaken: branchKey, updatedAt: new Date() } }
510
531
  )
511
532
  }
512
533
 
513
- async updateRunState(
534
+ protected async updateRunStateImpl(
514
535
  runId: string,
515
536
  name: string,
516
537
  value: unknown
@@ -538,7 +559,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
538
559
  return row.state ?? {}
539
560
  }
540
561
 
541
- async upsertWorkflowVersion(
562
+ protected async upsertWorkflowVersionImpl(
542
563
  name: string,
543
564
  graphHash: string,
544
565
  graph: any,
@@ -561,7 +582,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
561
582
  )
562
583
  }
563
584
 
564
- async updateWorkflowVersionStatus(
585
+ protected async updateWorkflowVersionStatusImpl(
565
586
  name: string,
566
587
  graphHash: string,
567
588
  status: WorkflowVersionStatus