@pikku/kysely 0.13.4 → 0.13.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 +106 -0
- package/LICENSE +21 -0
- package/README.md +29 -2
- package/dist/src/kysely-tables.d.ts +8 -0
- package/dist/src/kysely-workflow-mirror.js +37 -25
- package/dist/src/kysely-workflow-run-service.d.ts +0 -5
- package/dist/src/kysely-workflow-run-service.js +4 -16
- package/dist/src/kysely-workflow-service.d.ts +35 -6
- package/dist/src/kysely-workflow-service.js +194 -134
- package/dist/src/schema/index.d.ts +17 -0
- package/dist/src/schema/index.js +96 -9
- package/dist/src/schema/workflow.schema.d.ts +4 -2
- package/dist/src/schema/workflow.schema.js +28 -2
- package/package.json +3 -2
- package/src/kysely-tables.ts +8 -0
- package/src/kysely-workflow-mirror.test.ts +1 -13
- package/src/kysely-workflow-mirror.ts +44 -20
- package/src/kysely-workflow-run-service.ts +7 -20
- package/src/kysely-workflow-service.test.ts +431 -0
- package/src/kysely-workflow-service.ts +212 -161
- package/src/schema/index.ts +138 -13
- package/src/schema/schema.test.ts +183 -0
- package/src/schema/workflow.schema.ts +38 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,109 @@
|
|
|
1
|
+
## 0.13.6
|
|
2
|
+
|
|
3
|
+
### Patch Changes
|
|
4
|
+
|
|
5
|
+
- 8a2c993: Make the workflow service cheaper to run, and fix two ways it lost state.
|
|
6
|
+
|
|
7
|
+
The SQL workflow tables had no indexes at all, so every step read, every
|
|
8
|
+
history walk and every orchestrator tick was a sequential scan; five indexes
|
|
9
|
+
now cover the columns the engine actually queries by. A replay used to ask for
|
|
10
|
+
each step's row individually — O(N) reads per replay, O(N^2) over a run — and
|
|
11
|
+
now takes one read of the run's steps and serves the walk from it. A step
|
|
12
|
+
transition wrote the step row and its history row as two separate statements,
|
|
13
|
+
so a crash between them left a step saying `succeeded` whose history still said
|
|
14
|
+
`running`; both halves are now one transaction, and the history row is found by
|
|
15
|
+
attempt number rather than by sorting on `created_at`, which two attempts can
|
|
16
|
+
share. Resolving a dynamic workflow read and parsed every AI-generated workflow
|
|
17
|
+
in the deployment to `.find()` one by name; it is a point lookup now.
|
|
18
|
+
|
|
19
|
+
Waiting on a run no longer polls at a fixed interval. `pollIntervalMs` became a
|
|
20
|
+
ceiling rather than a cadence: polling starts at 10ms and widens towards it, so
|
|
21
|
+
a workflow that finishes in milliseconds is no longer held for a full second,
|
|
22
|
+
and a long-running one is not read at full rate for its whole life.
|
|
23
|
+
|
|
24
|
+
Two backend-specific defects: Redis kept a run's state as one JSON blob and
|
|
25
|
+
read-modified-wrote it, so parallel branches setting different variables
|
|
26
|
+
overwrote each other — state is a field per variable now, with the old blob
|
|
27
|
+
still read underneath so runs in flight keep what they had. Mongo's
|
|
28
|
+
`setStepScheduled` never wrote history, leaving a queued step reading as never
|
|
29
|
+
dispatched.
|
|
30
|
+
|
|
31
|
+
Also: dispatch no longer JSON round-trips every step payload before handing it
|
|
32
|
+
to a queue that serialises it anyway — the in-process dev queue, which is the
|
|
33
|
+
only one that was relying on it, does it itself now.
|
|
34
|
+
|
|
35
|
+
Two more defects. A transition whose step had no live attempt wrote its status
|
|
36
|
+
to the step row and silently nothing to history — the exact divergence the
|
|
37
|
+
transaction exists to prevent — and now repairs the step and writes the
|
|
38
|
+
missing row. And resolving a dynamic workflow was non-deterministic on all
|
|
39
|
+
three backends: a name can hold several active versions, and none of them
|
|
40
|
+
ordered the candidates, so which one ran could change between two calls
|
|
41
|
+
reading identical data. The newest version wins, with the graph hash breaking
|
|
42
|
+
a tie.
|
|
43
|
+
|
|
44
|
+
The two attempt columns and the five indexes are declared in the workflow
|
|
45
|
+
schema, so a fresh database gets them at boot. An existing one gets them from
|
|
46
|
+
a migration — `pikku db generate` writes the declaration down — rather than
|
|
47
|
+
from DDL issued at boot.
|
|
48
|
+
|
|
49
|
+
- a261006: **Breaking:** removed dynamic workflows — runtime-defined workflow graphs stored in the database and resolved by name instead of by codegen.
|
|
50
|
+
|
|
51
|
+
The feature was already half-gone. Its authoring surface (`createAgentWorkflow`, `saveAgentWorkflow`, `listAgentWorkflows`, `executeAgentWorkflow`, and the AI-agent instruction builder) was deleted in April 2026 along with its entire e2e suite, and nothing has written a dynamic workflow since. What remained could not execute one either: `executeAgentWorkflow` gated on `pikkuState('workflows', 'meta')`, which only codegen ever populates, so a graph that existed solely in the database was never findable. The two backend families had also drifted onto different `source` sentinels (`'ai-agent'` vs `'dynamic-workflow'`), and the two Redis implementations disagreed on key escaping — so at least one of them matched nothing. Rather than keep shipping plumbing for a path no caller could complete, it is removed until it can be reintroduced deliberately.
|
|
52
|
+
|
|
53
|
+
Removed:
|
|
54
|
+
- `getAIGeneratedWorkflows` from `WorkflowService` and `WorkflowRunService`, and from every backend (in-memory, Redis, MongoDB, Kysely, and the Cloudflare Durable Object service and client — the last two were already a `return []` stub and a rejection).
|
|
55
|
+
- The database-lookup fallbacks in `startWorkflow` and `runWorkflowJob` that resolved a workflow name against stored graphs when static meta had no match.
|
|
56
|
+
- `'dynamic-workflow'` from the `WorkflowRuntimeMeta['source']` union.
|
|
57
|
+
- `validateWorkflowWiring` and `computeEntryNodeIds` from `@pikku/core/workflow`. These validated AI-authored graphs and had no callers in core; the inspector keeps its own private entry-node computation for static graph wiring, which is unaffected.
|
|
58
|
+
- The `workflow-created` AI stream event and its AG-UI `pikku:workflow-created` custom event. Its only emitter went with the April deletion, so it could never fire.
|
|
59
|
+
- The console's `console:getAIWorkflows` RPC, the `useAIWorkflows` hook, the "Dynamic" workflow filter and badge, and the trigger-schema scraper that derived an input form from a stored graph's `$ref` bindings.
|
|
60
|
+
|
|
61
|
+
Kept, because static graph workflows depend on them and this is not a change to versioning:
|
|
62
|
+
- `upsertWorkflowVersion`, `getWorkflowVersion`, `updateWorkflowVersionStatus`, and the `workflowVersions` storage in every backend. These back version-mismatch replay: when a deployed graph's hash changes, in-flight runs continue against the exact graph they started on. No schema migration is needed — the table, its columns, and its `(workflowName, graphHash)` upsert key are unchanged.
|
|
63
|
+
- `generateMermaidDiagram`, which renders any workflow graph and is not specific to dynamic ones.
|
|
64
|
+
|
|
65
|
+
Static `pikkuWorkflowGraph` and DSL workflows are entirely unaffected: they resolve from codegen'd meta, which was always the only path that worked.
|
|
66
|
+
|
|
67
|
+
To revive this post-MVP, the deleted authoring code is recoverable in full — its prompt engineering (a compact tool table upfront, full schemas with flattened dotted output paths returned only after a validation failure) is worth reading before rewriting:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
git show f52f3308b^:packages/core/src/wirings/ai-agent/agent-dynamic-workflow.ts
|
|
71
|
+
git show f52f3308b^:packages/core/src/wirings/workflow/graph/graph-validation.ts
|
|
72
|
+
git show f52f3308b --stat # the April removal, incl. the three e2e feature files
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Note that reviving it needs more than restoring those files: the queued-step path (`executeWorkflowStep`), `onError` compensation, and sub-workflow resolution all read static meta only and would need a fallback for a graph that exists solely in the database.
|
|
76
|
+
|
|
77
|
+
- Updated dependencies [8a2c993]
|
|
78
|
+
- Updated dependencies [a261006]
|
|
79
|
+
- Updated dependencies [09973b9]
|
|
80
|
+
- @pikku/core@0.12.71
|
|
81
|
+
|
|
82
|
+
## 0.13.5
|
|
83
|
+
|
|
84
|
+
### Patch Changes
|
|
85
|
+
|
|
86
|
+
- 220a8c4: fix: `ensurePikkuSchema` now sees tables on a `withSchema(...)`-bound connection
|
|
87
|
+
|
|
88
|
+
It read the table name out of its own compiled DDL with a regex that matched the
|
|
89
|
+
first quoted identifier. On a connection bound to a schema that DDL is
|
|
90
|
+
`create table "app"."workflow_runs"`, so it captured `app` — never a real table
|
|
91
|
+
name — concluded every table was missing, and issued a bare `create table` that
|
|
92
|
+
the database rejected with `relation "workflow_runs" already exists`, on every
|
|
93
|
+
boot.
|
|
94
|
+
|
|
95
|
+
The schema half of the name is now parsed, and the lookup matches on the pair
|
|
96
|
+
when the DDL is qualified (falling back to the bare name when it is not, since
|
|
97
|
+
an unqualified statement resolves against a `search_path` that is not knowable
|
|
98
|
+
from here). Error messages name the schema too.
|
|
99
|
+
|
|
100
|
+
Also: applying a schema over a `withSchema(...)`-bound **sqlite** connection now
|
|
101
|
+
explains itself. `withSchema` qualifies foreign key targets along with everything
|
|
102
|
+
else — which postgres requires and sqlite refuses, since a `REFERENCES` clause
|
|
103
|
+
there takes a bare table name — and all the engine says about it is
|
|
104
|
+
`near ".": syntax error`. The failure is now reported with the schema that
|
|
105
|
+
produced it and what to do instead, with the engine error kept as the `cause`.
|
|
106
|
+
|
|
1
107
|
## 0.13.4
|
|
2
108
|
|
|
3
109
|
### Patch Changes
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 - present Yasser Fadl and Pikku contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,30 @@
|
|
|
1
|
-
# @pikku/
|
|
1
|
+
# @pikku/kysely
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Kysely-backed implementations of the Pikku service interfaces — workflow state,
|
|
4
|
+
secrets, credentials, sessions, scopes, channel/event-hub stores and audit.
|
|
5
|
+
|
|
6
|
+
Dialect-neutral. Pair it with a dialect package (`@pikku/kysely-postgres`,
|
|
7
|
+
`@pikku/kysely-mysql`, `@pikku/kysely-sqlite`) where the SQL differs.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @pikku/kysely kysely
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { KyselyWorkflowService, KyselySecretService } from '@pikku/kysely'
|
|
19
|
+
import type { KyselyPikkuDB } from '@pikku/kysely'
|
|
20
|
+
import type { Kysely } from 'kysely'
|
|
21
|
+
|
|
22
|
+
declare const db: Kysely<KyselyPikkuDB>
|
|
23
|
+
|
|
24
|
+
const workflowService = new KyselyWorkflowService(db)
|
|
25
|
+
const secretService = new KyselySecretService(db, { key: encryptionKey })
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Docs
|
|
29
|
+
|
|
30
|
+
https://pikku.dev/docs
|
|
@@ -44,6 +44,8 @@ export interface WorkflowStepTable {
|
|
|
44
44
|
retries: number | null;
|
|
45
45
|
retryDelay: string | null;
|
|
46
46
|
fromStepName: string | null;
|
|
47
|
+
/** The history attempt currently in flight; see WorkflowStepHistoryTable.attempt. */
|
|
48
|
+
currentAttempt: number | null;
|
|
47
49
|
createdAt: Generated<Date>;
|
|
48
50
|
updatedAt: Generated<Date>;
|
|
49
51
|
}
|
|
@@ -53,6 +55,12 @@ export interface WorkflowStepHistoryTable {
|
|
|
53
55
|
status: StepStatus;
|
|
54
56
|
result: string | null;
|
|
55
57
|
error: string | null;
|
|
58
|
+
/**
|
|
59
|
+
* Monotonic attempt number within the step, starting at 1. Orders attempts
|
|
60
|
+
* and identifies the live one without relying on `createdAt`, which cannot
|
|
61
|
+
* separate a retry from the attempt it replaces in the same millisecond.
|
|
62
|
+
*/
|
|
63
|
+
attempt: number | null;
|
|
56
64
|
createdAt: Generated<Date>;
|
|
57
65
|
runningAt: Date | null;
|
|
58
66
|
scheduledAt: Date | null;
|
|
@@ -77,11 +77,12 @@ export class KyselyWorkflowMirror {
|
|
|
77
77
|
retries: step.retries ?? null,
|
|
78
78
|
retryDelay: step.retryDelay?.toString() ?? null,
|
|
79
79
|
fromStepName: step.fromStepName ?? null,
|
|
80
|
+
currentAttempt: 1,
|
|
80
81
|
createdAt: now,
|
|
81
82
|
updatedAt: now,
|
|
82
83
|
})
|
|
83
84
|
.execute();
|
|
84
|
-
await this.insertHistoryRecord(step.stepId, step.status);
|
|
85
|
+
await this.insertHistoryRecord(step.stepId, step.status, 1);
|
|
85
86
|
}
|
|
86
87
|
async setStepRunning(stepId) {
|
|
87
88
|
await this.updateStepStatus(stepId, 'running');
|
|
@@ -126,17 +127,24 @@ export class KyselyWorkflowMirror {
|
|
|
126
127
|
await this.appendOrUpdateLatestHistory(stepId, 'failed', undefined, errorJson);
|
|
127
128
|
}
|
|
128
129
|
async createRetryAttempt(failedStepId, newStep) {
|
|
130
|
+
const previous = await this.db
|
|
131
|
+
.selectFrom('workflowStep')
|
|
132
|
+
.select('currentAttempt')
|
|
133
|
+
.where('workflowStepId', '=', failedStepId)
|
|
134
|
+
.executeTakeFirst();
|
|
135
|
+
const attempt = (previous?.currentAttempt ?? 0) + 1;
|
|
129
136
|
await this.db
|
|
130
137
|
.updateTable('workflowStep')
|
|
131
138
|
.set({
|
|
132
139
|
status: newStep.status,
|
|
133
140
|
result: null,
|
|
134
141
|
error: null,
|
|
142
|
+
currentAttempt: attempt,
|
|
135
143
|
updatedAt: new Date(),
|
|
136
144
|
})
|
|
137
145
|
.where('workflowStepId', '=', failedStepId)
|
|
138
146
|
.execute();
|
|
139
|
-
await this.insertHistoryRecord(failedStepId, newStep.status);
|
|
147
|
+
await this.insertHistoryRecord(failedStepId, newStep.status, attempt);
|
|
140
148
|
}
|
|
141
149
|
async setBranchTaken(stepId, branchKey) {
|
|
142
150
|
await this.db
|
|
@@ -192,12 +200,13 @@ export class KyselyWorkflowMirror {
|
|
|
192
200
|
.where('workflowStepId', '=', stepId)
|
|
193
201
|
.execute();
|
|
194
202
|
}
|
|
195
|
-
async insertHistoryRecord(stepId, status, resultJson, errorJson) {
|
|
203
|
+
async insertHistoryRecord(stepId, status, attempt, resultJson, errorJson) {
|
|
196
204
|
const now = new Date();
|
|
197
205
|
const values = {
|
|
198
206
|
historyId: crypto.randomUUID(),
|
|
199
207
|
workflowStepId: stepId,
|
|
200
208
|
status,
|
|
209
|
+
attempt,
|
|
201
210
|
result: resultJson ?? null,
|
|
202
211
|
error: errorJson ?? null,
|
|
203
212
|
createdAt: now,
|
|
@@ -219,30 +228,33 @@ export class KyselyWorkflowMirror {
|
|
|
219
228
|
* returns the same sequence the executor would have produced.
|
|
220
229
|
*/
|
|
221
230
|
async appendOrUpdateLatestHistory(stepId, status, resultJson, errorJson) {
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
.
|
|
231
|
+
const update = { status };
|
|
232
|
+
if (resultJson !== undefined)
|
|
233
|
+
update.result = resultJson;
|
|
234
|
+
if (errorJson !== undefined)
|
|
235
|
+
update.error = errorJson;
|
|
236
|
+
const tsField = timestampFieldFor(status);
|
|
237
|
+
if (tsField)
|
|
238
|
+
update[tsField] = new Date();
|
|
239
|
+
const result = await this.db
|
|
240
|
+
.updateTable('workflowStepHistory')
|
|
241
|
+
.set(update)
|
|
225
242
|
.where('workflowStepId', '=', stepId)
|
|
226
|
-
|
|
227
|
-
|
|
243
|
+
// Correlate through the step row rather than sorting this table by
|
|
244
|
+
// createdAt: two attempts can land in the same millisecond, and the
|
|
245
|
+
// step row already knows which attempt is current.
|
|
246
|
+
.where('attempt', '=', (eb) => eb
|
|
247
|
+
.selectFrom('workflowStep')
|
|
248
|
+
.select('currentAttempt')
|
|
249
|
+
.where('workflowStepId', '=', stepId))
|
|
228
250
|
.executeTakeFirst();
|
|
229
|
-
if (
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if (tsField)
|
|
237
|
-
update[tsField] = new Date();
|
|
238
|
-
await this.db
|
|
239
|
-
.updateTable('workflowStepHistory')
|
|
240
|
-
.set(update)
|
|
241
|
-
.where('historyId', '=', latest.historyId)
|
|
242
|
-
.execute();
|
|
243
|
-
}
|
|
244
|
-
else {
|
|
245
|
-
await this.insertHistoryRecord(stepId, status, resultJson, errorJson);
|
|
251
|
+
if (Number(result?.numUpdatedRows ?? 0n) === 0) {
|
|
252
|
+
const step = await this.db
|
|
253
|
+
.selectFrom('workflowStep')
|
|
254
|
+
.select('currentAttempt')
|
|
255
|
+
.where('workflowStepId', '=', stepId)
|
|
256
|
+
.executeTakeFirst();
|
|
257
|
+
await this.insertHistoryRecord(stepId, status, step?.currentAttempt ?? 1, resultJson, errorJson);
|
|
246
258
|
}
|
|
247
259
|
}
|
|
248
260
|
}
|
|
@@ -24,11 +24,6 @@ export declare class KyselyWorkflowRunService implements WorkflowRunService {
|
|
|
24
24
|
graph: any;
|
|
25
25
|
source: string;
|
|
26
26
|
} | null>;
|
|
27
|
-
getAIGeneratedWorkflows(agentName?: string): Promise<Array<{
|
|
28
|
-
workflowName: string;
|
|
29
|
-
graphHash: string;
|
|
30
|
-
graph: any;
|
|
31
|
-
}>>;
|
|
32
27
|
deleteRun(id: string): Promise<boolean>;
|
|
33
28
|
private mapRunRow;
|
|
34
29
|
}
|
|
@@ -143,6 +143,7 @@ export class KyselyWorkflowRunService {
|
|
|
143
143
|
'h.status',
|
|
144
144
|
'h.result',
|
|
145
145
|
'h.error',
|
|
146
|
+
'h.attempt',
|
|
146
147
|
'h.createdAt',
|
|
147
148
|
'h.runningAt',
|
|
148
149
|
'h.scheduledAt',
|
|
@@ -150,7 +151,10 @@ export class KyselyWorkflowRunService {
|
|
|
150
151
|
'h.failedAt',
|
|
151
152
|
])
|
|
152
153
|
.where('s.workflowRunId', '=', runId)
|
|
154
|
+
// `attempt` breaks the tie when a retry lands in the same millisecond as
|
|
155
|
+
// the attempt it replaces, which `createdAt` alone cannot order.
|
|
153
156
|
.orderBy('h.createdAt', 'asc')
|
|
157
|
+
.orderBy('h.attempt', 'asc')
|
|
154
158
|
.execute();
|
|
155
159
|
let attemptCounters = {};
|
|
156
160
|
return result.map((row) => {
|
|
@@ -206,22 +210,6 @@ export class KyselyWorkflowRunService {
|
|
|
206
210
|
source: row.source,
|
|
207
211
|
};
|
|
208
212
|
}
|
|
209
|
-
async getAIGeneratedWorkflows(agentName) {
|
|
210
|
-
let query = this.db
|
|
211
|
-
.selectFrom('workflowVersions')
|
|
212
|
-
.select(['workflowName', 'graphHash', 'graph'])
|
|
213
|
-
.where('source', '=', 'dynamic-workflow')
|
|
214
|
-
.where('status', '=', 'active');
|
|
215
|
-
if (agentName) {
|
|
216
|
-
query = query.where('workflowName', 'like', `ai:${agentName}:%`);
|
|
217
|
-
}
|
|
218
|
-
const rows = await query.execute();
|
|
219
|
-
return rows.map((row) => ({
|
|
220
|
-
workflowName: row.workflowName,
|
|
221
|
-
graphHash: row.graphHash,
|
|
222
|
-
graph: parseJson(row.graph),
|
|
223
|
-
}));
|
|
224
|
-
}
|
|
225
213
|
async deleteRun(id) {
|
|
226
214
|
const result = await this.db
|
|
227
215
|
.deleteFrom('workflowRuns')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SerializedError } from '@pikku/core';
|
|
2
2
|
import { PikkuWorkflowService, type WorkflowPlannedStep, type WorkflowQueueOptions, type WorkflowRun, type WorkflowRunWire, type StepState, type StepStatus, type WorkflowStatus, type WorkflowVersionStatus } from '@pikku/core/workflow';
|
|
3
|
-
import type
|
|
3
|
+
import { type Kysely } from 'kysely';
|
|
4
4
|
import type { KyselyPikkuDB } from './kysely-tables.js';
|
|
5
5
|
export declare class KyselyWorkflowService extends PikkuWorkflowService {
|
|
6
6
|
protected db: Kysely<KyselyPikkuDB>;
|
|
@@ -19,11 +19,26 @@ export declare class KyselyWorkflowService extends PikkuWorkflowService {
|
|
|
19
19
|
retryDelay?: string | number;
|
|
20
20
|
}, fromStepName?: string): Promise<StepState>;
|
|
21
21
|
getStepState(runId: string, stepName: string): Promise<StepState>;
|
|
22
|
+
protected listStepStates(runId: string): Promise<Array<StepState & {
|
|
23
|
+
stepName: string;
|
|
24
|
+
}>>;
|
|
22
25
|
getRunHistory(runId: string): Promise<Array<StepState & {
|
|
23
26
|
stepName: string;
|
|
24
27
|
}>>;
|
|
25
28
|
protected setStepRunningImpl(stepId: string): Promise<void>;
|
|
26
29
|
protected setStepScheduledImpl(stepId: string): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Move a step and its current history attempt to the same status in one
|
|
32
|
+
* transaction.
|
|
33
|
+
*
|
|
34
|
+
* Both halves matter: a crash between them used to leave the step row saying
|
|
35
|
+
* `succeeded` while its history row still said `running`, which silently
|
|
36
|
+
* corrupts `getRunHistory` and every timeline reconstruction built on it.
|
|
37
|
+
* The history row is targeted by its `attempt` rather than by a `LIMIT 1`
|
|
38
|
+
* over `created_at`, so a retry that lands in the same millisecond as the
|
|
39
|
+
* attempt it replaces still resolves the newer row.
|
|
40
|
+
*/
|
|
41
|
+
private writeStepTransition;
|
|
27
42
|
private insertHistoryRecord;
|
|
28
43
|
private getTimestampFieldForStatus;
|
|
29
44
|
protected setStepChildRunIdImpl(stepId: string, childRunId: string): Promise<void>;
|
|
@@ -45,6 +60,25 @@ export declare class KyselyWorkflowService extends PikkuWorkflowService {
|
|
|
45
60
|
}>>;
|
|
46
61
|
getNodeResults(runId: string, nodeIds: string[]): Promise<Record<string, any>>;
|
|
47
62
|
protected setBranchTakenImpl(stepId: string, branchKey: string): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Merge one key into the run's JSON state, as a single expression evaluated
|
|
65
|
+
* by the database.
|
|
66
|
+
*
|
|
67
|
+
* The SQL is dialect-specific, so subclasses override this rather than the
|
|
68
|
+
* caller. The default is the SQLite form; MySQL and Postgres differ in how
|
|
69
|
+
* they cast a JSON literal.
|
|
70
|
+
*
|
|
71
|
+
* @param path - JSON path to the key, already quoted (`$."key"`)
|
|
72
|
+
* @param json - The value as JSON text
|
|
73
|
+
*/
|
|
74
|
+
protected jsonSetState(path: string, json: string): import("kysely").RawBuilder<string>;
|
|
75
|
+
/**
|
|
76
|
+
* A JSON path for an arbitrary key. State keys are user-supplied (a graph's
|
|
77
|
+
* `setState` name, an approval's hex-encoded reason), so the key is quoted
|
|
78
|
+
* rather than interpolated bare — an unquoted `$.a.b` would silently address
|
|
79
|
+
* a nested path instead of the key literally called `a.b`.
|
|
80
|
+
*/
|
|
81
|
+
private jsonPathFor;
|
|
48
82
|
protected updateRunStateImpl(runId: string, name: string, value: unknown): Promise<void>;
|
|
49
83
|
getRunState(runId: string): Promise<Record<string, unknown>>;
|
|
50
84
|
protected upsertWorkflowVersionImpl(name: string, graphHash: string, graph: any, source: string, status?: WorkflowVersionStatus): Promise<void>;
|
|
@@ -53,10 +87,5 @@ export declare class KyselyWorkflowService extends PikkuWorkflowService {
|
|
|
53
87
|
graph: any;
|
|
54
88
|
source: string;
|
|
55
89
|
} | null>;
|
|
56
|
-
getAIGeneratedWorkflows(agentName?: string): Promise<Array<{
|
|
57
|
-
workflowName: string;
|
|
58
|
-
graphHash: string;
|
|
59
|
-
graph: any;
|
|
60
|
-
}>>;
|
|
61
90
|
close(): Promise<void>;
|
|
62
91
|
}
|