@pikku/kysely 0.12.9 → 0.12.11

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,36 @@
1
+ ## 0.12.11
2
+
3
+ ### Patch Changes
4
+
5
+ - b9ed73e: Add deterministic workflow planned-step metadata support and SSE init stream payload generation.
6
+ - Persist `deterministic` and `plannedSteps` on workflow runs in core and service adapters.
7
+ - Expose planned-step metadata on workflow run status responses.
8
+ - Emit an initial `type: 'init'` SSE event for deterministic workflow streams before incremental updates.
9
+ - Add CLI tests covering serialized stream route output for init/update/done event behavior.
10
+
11
+ - Updated dependencies [b9ed73e]
12
+ - @pikku/core@0.12.19
13
+
1
14
  ## 0.12.0
2
15
 
16
+ ## 0.12.10
17
+
18
+ ### Patch Changes
19
+
20
+ - 311c0c4: Unify session persistence through SessionStore, remove session blob from ChannelStore
21
+ - PikkuSessionService now persists sessions via SessionStore on set()/clear() instead of every function call
22
+ - ChannelStore no longer stores session data — maps channelId to pikkuUserId only
23
+ - ChannelStore API: setUserSession/getChannelAndSession replaced with setPikkuUserId/getChannel
24
+ - Serverless channel runner resolves sessions from SessionStore using pikkuUserId from ChannelStore
25
+
26
+ - Updated dependencies [311c0c4]
27
+ - @pikku/core@0.12.18
28
+
3
29
  ## 0.12.9
4
30
 
5
31
  ### Patch Changes
6
32
 
7
33
  - 624097e: Add deploy pipeline with provider-agnostic architecture
8
-
9
34
  - Add MetaService with explicit typed API, absorb WiringService reads
10
35
  - Add deployment service, traceId propagation, scoped logger
11
36
  - Rewrite analyzer: one function = one worker, gateways dispatch via RPC
@@ -25,7 +50,6 @@
25
50
  ### Patch Changes
26
51
 
27
52
  - f85c234: Add unified credential system with per-user OAuth and AI agent pre-flight checks
28
-
29
53
  - Unified CredentialService with lazy loading per user via pikkuUserId
30
54
  - wire.getCredential() for typed single credential lookup
31
55
  - MissingCredentialError with structured payload for client-side connect flows
@@ -130,7 +154,6 @@
130
154
  - Updated dependencies [62a8725]
131
155
  - Updated dependencies [62a8725]
132
156
  - Updated dependencies [62a8725]
133
-
134
157
  - @pikku/core@0.12.1
135
158
 
136
159
  - Updated dependencies
@@ -10,6 +10,7 @@ export { KyselySecretService } from './kysely-secret-service.js';
10
10
  export type { KyselySecretServiceConfig } from './kysely-secret-service.js';
11
11
  export { KyselyCredentialService } from './kysely-credential-service.js';
12
12
  export type { KyselyCredentialServiceConfig } from './kysely-credential-service.js';
13
+ export { KyselySessionStore } from './kysely-session-store.js';
13
14
  export type { KyselyPikkuDB } from './kysely-tables.js';
14
15
  export type { WorkflowRunService } from '@pikku/core/workflow';
15
16
  export type { AgentRunService, AgentRunRow } from '@pikku/core/ai-agent';
package/dist/src/index.js CHANGED
@@ -8,3 +8,4 @@ export { KyselyAgentRunService } from './kysely-ai-agent-run-service.js';
8
8
  export { KyselyAIRunStateService } from './kysely-ai-run-state-service.js';
9
9
  export { KyselySecretService } from './kysely-secret-service.js';
10
10
  export { KyselyCredentialService } from './kysely-credential-service.js';
11
+ export { KyselySessionStore } from './kysely-session-store.js';
@@ -1,4 +1,3 @@
1
- import type { CoreUserSession } from '@pikku/core';
2
1
  import type { Channel } from '@pikku/core/channel';
3
2
  import { ChannelStore } from '@pikku/core/channel';
4
3
  import type { Kysely } from 'kysely';
@@ -10,9 +9,9 @@ export declare class KyselyChannelStore extends ChannelStore {
10
9
  init(): Promise<void>;
11
10
  addChannel({ channelId, channelName, openingData, }: Channel): Promise<void>;
12
11
  removeChannels(channelIds: string[]): Promise<void>;
13
- setUserSession(channelId: string, session: CoreUserSession | null): Promise<void>;
14
- getChannelAndSession(channelId: string): Promise<Channel & {
15
- session: CoreUserSession;
12
+ setPikkuUserId(channelId: string, pikkuUserId: string | null): Promise<void>;
13
+ getChannel(channelId: string): Promise<Channel & {
14
+ pikkuUserId?: string;
16
15
  }>;
17
16
  close(): Promise<void>;
18
17
  }
@@ -19,7 +19,7 @@ export class KyselyChannelStore extends ChannelStore {
19
19
  .addColumn('channel_name', 'text', (col) => col.notNull())
20
20
  .addColumn('created_at', 'timestamp', (col) => col.defaultTo(sql `CURRENT_TIMESTAMP`).notNull())
21
21
  .addColumn('opening_data', 'text', (col) => col.notNull().defaultTo('{}'))
22
- .addColumn('user_session', 'text')
22
+ .addColumn('pikku_user_id', 'text')
23
23
  .addColumn('last_wire', 'timestamp', (col) => col.defaultTo(sql `CURRENT_TIMESTAMP`).notNull())
24
24
  .execute();
25
25
  await this.db.schema
@@ -53,17 +53,17 @@ export class KyselyChannelStore extends ChannelStore {
53
53
  .where('channelId', 'in', channelIds)
54
54
  .execute();
55
55
  }
56
- async setUserSession(channelId, session) {
56
+ async setPikkuUserId(channelId, pikkuUserId) {
57
57
  await this.db
58
58
  .updateTable('channels')
59
- .set({ userSession: session ? JSON.stringify(session) : null })
59
+ .set({ pikkuUserId })
60
60
  .where('channelId', '=', channelId)
61
61
  .execute();
62
62
  }
63
- async getChannelAndSession(channelId) {
63
+ async getChannel(channelId) {
64
64
  const row = await this.db
65
65
  .selectFrom('channels')
66
- .select(['channelId', 'channelName', 'openingData', 'userSession'])
66
+ .select(['channelId', 'channelName', 'openingData', 'pikkuUserId'])
67
67
  .where('channelId', '=', channelId)
68
68
  .executeTakeFirst();
69
69
  if (!row) {
@@ -73,7 +73,7 @@ export class KyselyChannelStore extends ChannelStore {
73
73
  channelId: row.channelId,
74
74
  channelName: row.channelName,
75
75
  openingData: parseJson(row.openingData) ?? {},
76
- session: (parseJson(row.userSession) ?? {}),
76
+ pikkuUserId: row.pikkuUserId ?? undefined,
77
77
  };
78
78
  }
79
79
  async close() { }
@@ -0,0 +1,13 @@
1
+ import type { CoreUserSession } from '@pikku/core';
2
+ import type { SessionStore } from '@pikku/core/services';
3
+ import type { Kysely } from 'kysely';
4
+ import type { KyselyPikkuDB } from './kysely-tables.js';
5
+ export declare class KyselySessionStore implements SessionStore {
6
+ private db;
7
+ private initialized;
8
+ constructor(db: Kysely<KyselyPikkuDB>);
9
+ init(): Promise<void>;
10
+ get(pikkuUserId: string): Promise<CoreUserSession | undefined>;
11
+ set(pikkuUserId: string, session: CoreUserSession): Promise<void>;
12
+ clear(pikkuUserId: string): Promise<void>;
13
+ }
@@ -0,0 +1,54 @@
1
+ import { sql } from 'kysely';
2
+ import { parseJson } from './kysely-json.js';
3
+ export class KyselySessionStore {
4
+ db;
5
+ initialized = false;
6
+ constructor(db) {
7
+ this.db = db;
8
+ }
9
+ async init() {
10
+ if (this.initialized) {
11
+ return;
12
+ }
13
+ await this.db.schema
14
+ .createTable('pikku_user_sessions')
15
+ .ifNotExists()
16
+ .addColumn('pikku_user_id', 'text', (col) => col.primaryKey())
17
+ .addColumn('session', 'text', (col) => col.notNull())
18
+ .addColumn('created_at', 'timestamp', (col) => col.defaultTo(sql `CURRENT_TIMESTAMP`).notNull())
19
+ .addColumn('updated_at', 'timestamp', (col) => col.defaultTo(sql `CURRENT_TIMESTAMP`).notNull())
20
+ .execute();
21
+ this.initialized = true;
22
+ }
23
+ async get(pikkuUserId) {
24
+ const row = await this.db
25
+ .selectFrom('pikkuUserSessions')
26
+ .select(['session'])
27
+ .where('pikkuUserId', '=', pikkuUserId)
28
+ .executeTakeFirst();
29
+ if (!row) {
30
+ return undefined;
31
+ }
32
+ return (parseJson(row.session) ?? undefined);
33
+ }
34
+ async set(pikkuUserId, session) {
35
+ await this.db
36
+ .insertInto('pikkuUserSessions')
37
+ .values({
38
+ pikkuUserId,
39
+ session: JSON.stringify(session),
40
+ updatedAt: new Date(),
41
+ })
42
+ .onConflict((oc) => oc.column('pikkuUserId').doUpdateSet({
43
+ session: JSON.stringify(session),
44
+ updatedAt: new Date(),
45
+ }))
46
+ .execute();
47
+ }
48
+ async clear(pikkuUserId) {
49
+ await this.db
50
+ .deleteFrom('pikkuUserSessions')
51
+ .where('pikkuUserId', '=', pikkuUserId)
52
+ .execute();
53
+ }
54
+ }
@@ -5,7 +5,7 @@ export interface ChannelsTable {
5
5
  channelName: string;
6
6
  createdAt: Generated<Date>;
7
7
  openingData: string;
8
- userSession: string | null;
8
+ pikkuUserId: string | null;
9
9
  lastWire: Generated<Date>;
10
10
  }
11
11
  export interface ChannelSubscriptionsTable {
@@ -22,6 +22,8 @@ export interface WorkflowRunsTable {
22
22
  state: Generated<string>;
23
23
  inline: Generated<boolean>;
24
24
  graphHash: string | null;
25
+ deterministic: Generated<boolean>;
26
+ plannedSteps: string | null;
25
27
  wire: string | null;
26
28
  createdAt: Generated<Date>;
27
29
  updatedAt: Generated<Date>;
@@ -153,6 +155,12 @@ export interface CredentialsAuditTable {
153
155
  action: string;
154
156
  performedAt: Generated<Date>;
155
157
  }
158
+ export interface UserSessionsTable {
159
+ pikkuUserId: string;
160
+ session: string;
161
+ createdAt: Generated<Date>;
162
+ updatedAt: Generated<Date>;
163
+ }
156
164
  export interface KyselyPikkuDB {
157
165
  channels: ChannelsTable;
158
166
  channelSubscriptions: ChannelSubscriptionsTable;
@@ -171,4 +179,5 @@ export interface KyselyPikkuDB {
171
179
  secretsAudit: SecretsAuditTable;
172
180
  credentials: CredentialsTable;
173
181
  credentialsAudit: CredentialsAuditTable;
182
+ pikkuUserSessions: UserSessionsTable;
174
183
  }
@@ -17,6 +17,8 @@ export class KyselyWorkflowRunService {
17
17
  'error',
18
18
  'inline',
19
19
  'graphHash',
20
+ 'deterministic',
21
+ 'plannedSteps',
20
22
  'wire',
21
23
  'createdAt',
22
24
  'updatedAt',
@@ -46,6 +48,8 @@ export class KyselyWorkflowRunService {
46
48
  'error',
47
49
  'inline',
48
50
  'graphHash',
51
+ 'deterministic',
52
+ 'plannedSteps',
49
53
  'wire',
50
54
  'createdAt',
51
55
  'updatedAt',
@@ -204,6 +208,8 @@ export class KyselyWorkflowRunService {
204
208
  error: parseJson(row.error),
205
209
  inline: row.inline,
206
210
  graphHash: row.graphHash,
211
+ deterministic: row.deterministic,
212
+ plannedSteps: parseJson(row.plannedSteps),
207
213
  wire: parseJson(row.wire) ?? { type: 'unknown' },
208
214
  createdAt: new Date(row.createdAt),
209
215
  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 { Kysely } from 'kysely';
4
4
  import type { KyselyPikkuDB } from './kysely-tables.js';
5
5
  export declare class KyselyWorkflowService extends PikkuWorkflowService {
@@ -8,7 +8,10 @@ export declare class KyselyWorkflowService extends PikkuWorkflowService {
8
8
  private runService;
9
9
  constructor(db: Kysely<KyselyPikkuDB>);
10
10
  init(): Promise<void>;
11
- createRun(workflowName: string, input: any, inline: boolean, graphHash: string, wire: WorkflowRunWire): Promise<string>;
11
+ createRun(workflowName: string, input: any, inline: boolean, graphHash: string, wire: WorkflowRunWire, options?: {
12
+ deterministic?: boolean;
13
+ plannedSteps?: WorkflowPlannedStep[];
14
+ }): Promise<string>;
12
15
  getRun(id: string): Promise<WorkflowRun | null>;
13
16
  updateRunStatus(id: string, status: WorkflowStatus, output?: any, error?: SerializedError): Promise<void>;
14
17
  insertStepState(runId: string, stepName: string, rpcName: string | null, data: any, stepOptions?: {
@@ -29,6 +29,8 @@ export class KyselyWorkflowService extends PikkuWorkflowService {
29
29
  .addColumn('state', 'text', (col) => col.defaultTo('{}'))
30
30
  .addColumn('inline', 'boolean', (col) => col.defaultTo(false))
31
31
  .addColumn('graph_hash', 'text')
32
+ .addColumn('deterministic', 'boolean', (col) => col.defaultTo(false))
33
+ .addColumn('planned_steps', 'text')
32
34
  .addColumn('wire', 'text')
33
35
  .addColumn('created_at', 'timestamp', (col) => col.defaultTo(sql `CURRENT_TIMESTAMP`).notNull())
34
36
  .addColumn('updated_at', 'timestamp', (col) => col.defaultTo(sql `CURRENT_TIMESTAMP`).notNull())
@@ -95,7 +97,7 @@ export class KyselyWorkflowService extends PikkuWorkflowService {
95
97
  .execute();
96
98
  this.initialized = true;
97
99
  }
98
- async createRun(workflowName, input, inline, graphHash, wire) {
100
+ async createRun(workflowName, input, inline, graphHash, wire, options) {
99
101
  const id = crypto.randomUUID();
100
102
  await this.db
101
103
  .insertInto('workflowRuns')
@@ -106,6 +108,10 @@ export class KyselyWorkflowService extends PikkuWorkflowService {
106
108
  input: JSON.stringify(input),
107
109
  inline,
108
110
  graphHash: graphHash ?? null,
111
+ deterministic: options?.deterministic ?? false,
112
+ plannedSteps: options?.plannedSteps
113
+ ? JSON.stringify(options.plannedSteps)
114
+ : null,
109
115
  wire: wire ? JSON.stringify(wire) : null,
110
116
  })
111
117
  .execute();
@@ -1 +1 @@
1
- {"root":["../src/index.ts","../src/kysely-ai-agent-run-service.ts","../src/kysely-ai-run-state-service.ts","../src/kysely-ai-storage-service.ts","../src/kysely-channel-store.ts","../src/kysely-credential-service.ts","../src/kysely-deployment-service.ts","../src/kysely-eventhub-store.ts","../src/kysely-json.ts","../src/kysely-secret-service.ts","../src/kysely-tables.ts","../src/kysely-workflow-run-service.ts","../src/kysely-workflow-service.ts","../bin/pikku-kysely-pure.ts"],"version":"5.9.3"}
1
+ {"root":["../src/index.ts","../src/kysely-ai-agent-run-service.ts","../src/kysely-ai-run-state-service.ts","../src/kysely-ai-storage-service.ts","../src/kysely-channel-store.ts","../src/kysely-credential-service.ts","../src/kysely-deployment-service.ts","../src/kysely-eventhub-store.ts","../src/kysely-json.ts","../src/kysely-secret-service.ts","../src/kysely-session-store.ts","../src/kysely-tables.ts","../src/kysely-workflow-run-service.ts","../src/kysely-workflow-service.ts","../bin/pikku-kysely-pure.ts"],"version":"5.9.3"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/kysely",
3
- "version": "0.12.9",
3
+ "version": "0.12.11",
4
4
  "author": "yasser.fadl@gmail.com",
5
5
  "license": "MIT",
6
6
  "module": "dist/src/index.js",
@@ -20,7 +20,7 @@
20
20
  "prepublishOnly": "yarn build"
21
21
  },
22
22
  "peerDependencies": {
23
- "@pikku/core": "^0.12.15"
23
+ "@pikku/core": "^0.12.19"
24
24
  },
25
25
  "dependencies": {
26
26
  "kysely": "^0.28.12"
package/src/index.ts CHANGED
@@ -10,6 +10,7 @@ export { KyselySecretService } from './kysely-secret-service.js'
10
10
  export type { KyselySecretServiceConfig } from './kysely-secret-service.js'
11
11
  export { KyselyCredentialService } from './kysely-credential-service.js'
12
12
  export type { KyselyCredentialServiceConfig } from './kysely-credential-service.js'
13
+ export { KyselySessionStore } from './kysely-session-store.js'
13
14
 
14
15
  export type { KyselyPikkuDB } from './kysely-tables.js'
15
16
  export type { WorkflowRunService } from '@pikku/core/workflow'
@@ -1,4 +1,3 @@
1
- import type { CoreUserSession } from '@pikku/core'
2
1
  import type { Channel } from '@pikku/core/channel'
3
2
  import { ChannelStore } from '@pikku/core/channel'
4
3
  import type { Kysely } from 'kysely'
@@ -27,7 +26,7 @@ export class KyselyChannelStore extends ChannelStore {
27
26
  col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
28
27
  )
29
28
  .addColumn('opening_data', 'text', (col) => col.notNull().defaultTo('{}'))
30
- .addColumn('user_session', 'text')
29
+ .addColumn('pikku_user_id', 'text')
31
30
  .addColumn('last_wire', 'timestamp', (col) =>
32
31
  col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
33
32
  )
@@ -75,23 +74,23 @@ export class KyselyChannelStore extends ChannelStore {
75
74
  .execute()
76
75
  }
77
76
 
78
- public async setUserSession(
77
+ public async setPikkuUserId(
79
78
  channelId: string,
80
- session: CoreUserSession | null
79
+ pikkuUserId: string | null
81
80
  ): Promise<void> {
82
81
  await this.db
83
82
  .updateTable('channels')
84
- .set({ userSession: session ? JSON.stringify(session) : null })
83
+ .set({ pikkuUserId })
85
84
  .where('channelId', '=', channelId)
86
85
  .execute()
87
86
  }
88
87
 
89
- public async getChannelAndSession(
88
+ public async getChannel(
90
89
  channelId: string
91
- ): Promise<Channel & { session: CoreUserSession }> {
90
+ ): Promise<Channel & { pikkuUserId?: string }> {
92
91
  const row = await this.db
93
92
  .selectFrom('channels')
94
- .select(['channelId', 'channelName', 'openingData', 'userSession'])
93
+ .select(['channelId', 'channelName', 'openingData', 'pikkuUserId'])
95
94
  .where('channelId', '=', channelId)
96
95
  .executeTakeFirst()
97
96
 
@@ -103,7 +102,7 @@ export class KyselyChannelStore extends ChannelStore {
103
102
  channelId: row.channelId,
104
103
  channelName: row.channelName,
105
104
  openingData: parseJson(row.openingData) ?? {},
106
- session: (parseJson(row.userSession) ?? {}) as CoreUserSession,
105
+ pikkuUserId: row.pikkuUserId ?? undefined,
107
106
  }
108
107
  }
109
108
 
@@ -17,6 +17,7 @@ import { KyselyAIStorageService } from './kysely-ai-storage-service.js'
17
17
  import { KyselyAgentRunService } from './kysely-ai-agent-run-service.js'
18
18
  import { KyselySecretService } from './kysely-secret-service.js'
19
19
  import { KyselyCredentialService } from './kysely-credential-service.js'
20
+ import { KyselySessionStore } from './kysely-session-store.js'
20
21
 
21
22
  function createSqliteDb(): Kysely<KyselyPikkuDB> {
22
23
  return new Kysely<KyselyPikkuDB>({
@@ -109,6 +110,11 @@ function registerTests(
109
110
  await s.init()
110
111
  return s
111
112
  },
113
+ sessionStore: async () => {
114
+ const s = new KyselySessionStore(getDb())
115
+ await s.init()
116
+ return s
117
+ },
112
118
  },
113
119
  })
114
120
 
@@ -0,0 +1,73 @@
1
+ import type { CoreUserSession } from '@pikku/core'
2
+ import type { SessionStore } from '@pikku/core/services'
3
+ import type { Kysely } from 'kysely'
4
+ import { sql } from 'kysely'
5
+ import type { KyselyPikkuDB } from './kysely-tables.js'
6
+ import { parseJson } from './kysely-json.js'
7
+
8
+ export class KyselySessionStore implements SessionStore {
9
+ private initialized = false
10
+
11
+ constructor(private db: Kysely<KyselyPikkuDB>) {}
12
+
13
+ public async init(): Promise<void> {
14
+ if (this.initialized) {
15
+ return
16
+ }
17
+
18
+ await this.db.schema
19
+ .createTable('pikku_user_sessions')
20
+ .ifNotExists()
21
+ .addColumn('pikku_user_id', 'text', (col) => col.primaryKey())
22
+ .addColumn('session', 'text', (col) => col.notNull())
23
+ .addColumn('created_at', 'timestamp', (col) =>
24
+ col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
25
+ )
26
+ .addColumn('updated_at', 'timestamp', (col) =>
27
+ col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
28
+ )
29
+ .execute()
30
+
31
+ this.initialized = true
32
+ }
33
+
34
+ async get(pikkuUserId: string): Promise<CoreUserSession | undefined> {
35
+ const row = await this.db
36
+ .selectFrom('pikkuUserSessions')
37
+ .select(['session'])
38
+ .where('pikkuUserId', '=', pikkuUserId)
39
+ .executeTakeFirst()
40
+
41
+ if (!row) {
42
+ return undefined
43
+ }
44
+
45
+ return (parseJson(row.session) ?? undefined) as
46
+ | CoreUserSession
47
+ | undefined
48
+ }
49
+
50
+ async set(pikkuUserId: string, session: CoreUserSession): Promise<void> {
51
+ await this.db
52
+ .insertInto('pikkuUserSessions')
53
+ .values({
54
+ pikkuUserId,
55
+ session: JSON.stringify(session),
56
+ updatedAt: new Date(),
57
+ })
58
+ .onConflict((oc) =>
59
+ oc.column('pikkuUserId').doUpdateSet({
60
+ session: JSON.stringify(session),
61
+ updatedAt: new Date(),
62
+ })
63
+ )
64
+ .execute()
65
+ }
66
+
67
+ async clear(pikkuUserId: string): Promise<void> {
68
+ await this.db
69
+ .deleteFrom('pikkuUserSessions')
70
+ .where('pikkuUserId', '=', pikkuUserId)
71
+ .execute()
72
+ }
73
+ }
@@ -10,7 +10,7 @@ export interface ChannelsTable {
10
10
  channelName: string
11
11
  createdAt: Generated<Date>
12
12
  openingData: string
13
- userSession: string | null
13
+ pikkuUserId: string | null
14
14
  lastWire: Generated<Date>
15
15
  }
16
16
 
@@ -29,6 +29,8 @@ export interface WorkflowRunsTable {
29
29
  state: Generated<string>
30
30
  inline: Generated<boolean>
31
31
  graphHash: string | null
32
+ deterministic: Generated<boolean>
33
+ plannedSteps: string | null
32
34
  wire: string | null
33
35
  createdAt: Generated<Date>
34
36
  updatedAt: Generated<Date>
@@ -175,6 +177,13 @@ export interface CredentialsAuditTable {
175
177
  performedAt: Generated<Date>
176
178
  }
177
179
 
180
+ export interface UserSessionsTable {
181
+ pikkuUserId: string
182
+ session: string
183
+ createdAt: Generated<Date>
184
+ updatedAt: Generated<Date>
185
+ }
186
+
178
187
  export interface KyselyPikkuDB {
179
188
  channels: ChannelsTable
180
189
  channelSubscriptions: ChannelSubscriptionsTable
@@ -193,4 +202,5 @@ export interface KyselyPikkuDB {
193
202
  secretsAudit: SecretsAuditTable
194
203
  credentials: CredentialsTable
195
204
  credentialsAudit: CredentialsAuditTable
205
+ pikkuUserSessions: UserSessionsTable
196
206
  }
@@ -30,6 +30,8 @@ export class KyselyWorkflowRunService implements WorkflowRunService {
30
30
  'error',
31
31
  'inline',
32
32
  'graphHash',
33
+ 'deterministic',
34
+ 'plannedSteps',
33
35
  'wire',
34
36
  'createdAt',
35
37
  'updatedAt',
@@ -64,6 +66,8 @@ export class KyselyWorkflowRunService implements WorkflowRunService {
64
66
  'error',
65
67
  'inline',
66
68
  'graphHash',
69
+ 'deterministic',
70
+ 'plannedSteps',
67
71
  'wire',
68
72
  'createdAt',
69
73
  'updatedAt',
@@ -255,6 +259,8 @@ export class KyselyWorkflowRunService implements WorkflowRunService {
255
259
  error: parseJson(row.error),
256
260
  inline: row.inline as boolean | undefined,
257
261
  graphHash: row.graphHash as string | undefined,
262
+ deterministic: row.deterministic as boolean | undefined,
263
+ plannedSteps: parseJson(row.plannedSteps),
258
264
  wire: parseJson(row.wire) ?? { type: 'unknown' },
259
265
  createdAt: new Date(row.createdAt as string),
260
266
  updatedAt: new Date(row.updatedAt as string),
@@ -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,
@@ -43,6 +44,8 @@ export class KyselyWorkflowService extends PikkuWorkflowService {
43
44
  .addColumn('state', 'text', (col) => col.defaultTo('{}'))
44
45
  .addColumn('inline', 'boolean', (col) => col.defaultTo(false))
45
46
  .addColumn('graph_hash', 'text')
47
+ .addColumn('deterministic', 'boolean', (col) => col.defaultTo(false))
48
+ .addColumn('planned_steps', 'text')
46
49
  .addColumn('wire', 'text')
47
50
  .addColumn('created_at', 'timestamp', (col) =>
48
51
  col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()
@@ -139,7 +142,11 @@ export class KyselyWorkflowService extends PikkuWorkflowService {
139
142
  input: any,
140
143
  inline: boolean,
141
144
  graphHash: string,
142
- wire: WorkflowRunWire
145
+ wire: WorkflowRunWire,
146
+ options?: {
147
+ deterministic?: boolean
148
+ plannedSteps?: WorkflowPlannedStep[]
149
+ }
143
150
  ): Promise<string> {
144
151
  const id = crypto.randomUUID()
145
152
  await this.db
@@ -151,6 +158,10 @@ export class KyselyWorkflowService extends PikkuWorkflowService {
151
158
  input: JSON.stringify(input),
152
159
  inline,
153
160
  graphHash: graphHash ?? null,
161
+ deterministic: options?.deterministic ?? false,
162
+ plannedSteps: options?.plannedSteps
163
+ ? JSON.stringify(options.plannedSteps)
164
+ : null,
154
165
  wire: wire ? JSON.stringify(wire) : null,
155
166
  })
156
167
  .execute()