@pikku/core 0.12.7 → 0.12.9
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 +30 -0
- package/dist/env.d.ts +1 -0
- package/dist/env.js +1 -0
- package/dist/errors/errors.d.ts +14 -0
- package/dist/errors/errors.js +27 -0
- package/dist/handle-error.js +2 -1
- package/dist/middleware/auth-bearer.js +10 -1
- package/dist/middleware/auth-cookie.js +34 -25
- package/dist/middleware/timeout.js +11 -5
- package/dist/pikku-state.js +1 -1
- package/dist/services/local-content.d.ts +7 -4
- package/dist/services/local-content.js +42 -12
- package/dist/services/local-secrets.js +2 -2
- package/dist/testing/service-tests.js +1 -1
- package/dist/wirings/ai-agent/ai-agent-prepare.js +2 -1
- package/dist/wirings/ai-agent/ai-agent-runner.js +2 -1
- package/dist/wirings/ai-agent/ai-agent-stream.js +2 -1
- package/dist/wirings/channel/local/local-channel-runner.js +11 -6
- package/dist/wirings/http/http-runner.js +4 -2
- package/dist/wirings/http/pikku-fetch-http-request.js +11 -1
- package/dist/wirings/http/web-request.js +3 -1
- package/dist/wirings/workflow/graph/graph-runner.d.ts +7 -0
- package/dist/wirings/workflow/graph/graph-runner.js +72 -7
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +3 -0
- package/dist/wirings/workflow/pikku-workflow-service.js +59 -16
- package/dist/wirings/workflow/workflow.types.d.ts +4 -0
- package/package.json +1 -1
- package/src/env.ts +1 -0
- package/src/errors/errors.ts +35 -0
- package/src/handle-error.ts +2 -1
- package/src/middleware/auth-bearer.ts +10 -1
- package/src/middleware/auth-cookie.ts +11 -4
- package/src/middleware/timeout.ts +14 -7
- package/src/pikku-state.ts +1 -1
- package/src/services/local-content.ts +61 -13
- package/src/services/local-secrets.test.ts +2 -2
- package/src/services/local-secrets.ts +2 -2
- package/src/testing/service-tests.ts +1 -1
- package/src/wirings/ai-agent/ai-agent-prepare.ts +2 -1
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +34 -0
- package/src/wirings/ai-agent/ai-agent-runner.ts +2 -1
- package/src/wirings/ai-agent/ai-agent-stream.ts +2 -1
- package/src/wirings/channel/local/local-channel-runner.ts +11 -6
- package/src/wirings/http/http-runner.ts +4 -2
- package/src/wirings/http/pikku-fetch-http-request.ts +11 -1
- package/src/wirings/http/web-request.ts +3 -1
- package/src/wirings/workflow/graph/graph-runner.test.ts +42 -0
- package/src/wirings/workflow/graph/graph-runner.ts +84 -7
- package/src/wirings/workflow/pikku-workflow-service.test.ts +109 -0
- package/src/wirings/workflow/pikku-workflow-service.ts +95 -25
- package/src/wirings/workflow/workflow.types.ts +4 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
import type { WorkflowService } from '../../services/workflow-service.js'
|
|
27
27
|
import { PikkuError, addError } from '../../errors/error-handler.js'
|
|
28
28
|
import { RPCNotFoundError } from '../rpc/rpc-runner.js'
|
|
29
|
+
import { ChildWorkflowStartedException } from './graph/graph-runner.js'
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* Exception thrown when workflow needs to pause for async step
|
|
@@ -111,6 +112,10 @@ const WORKFLOW_END_STATES: ReadonlySet<string> = new Set([
|
|
|
111
112
|
export abstract class PikkuWorkflowService implements WorkflowService {
|
|
112
113
|
private inlineRuns = new Set<string>()
|
|
113
114
|
|
|
115
|
+
protected get logger() {
|
|
116
|
+
return getSingletonServices()?.logger
|
|
117
|
+
}
|
|
118
|
+
|
|
114
119
|
constructor() {}
|
|
115
120
|
|
|
116
121
|
/**
|
|
@@ -386,12 +391,17 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
386
391
|
data: any
|
|
387
392
|
): Promise<void> {
|
|
388
393
|
const queueService = this.verifyQueueService()
|
|
389
|
-
await queueService.add(
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
394
|
+
await queueService.add(
|
|
395
|
+
this.getConfig().stepWorkerQueueName,
|
|
396
|
+
JSON.parse(
|
|
397
|
+
JSON.stringify({
|
|
398
|
+
runId,
|
|
399
|
+
stepName,
|
|
400
|
+
rpcName,
|
|
401
|
+
data,
|
|
402
|
+
})
|
|
403
|
+
)
|
|
404
|
+
)
|
|
395
405
|
}
|
|
396
406
|
|
|
397
407
|
/**
|
|
@@ -447,7 +457,9 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
447
457
|
|
|
448
458
|
if (workflowMeta.source === 'graph' || workflowMeta.source === 'ai-agent') {
|
|
449
459
|
const shouldInline =
|
|
450
|
-
options?.inline ||
|
|
460
|
+
options?.inline ||
|
|
461
|
+
workflowMeta.inline ||
|
|
462
|
+
!getSingletonServices()?.queueService
|
|
451
463
|
return runWorkflowGraph(
|
|
452
464
|
this,
|
|
453
465
|
name,
|
|
@@ -472,7 +484,9 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
472
484
|
}
|
|
473
485
|
|
|
474
486
|
const shouldInline =
|
|
475
|
-
options?.inline ||
|
|
487
|
+
options?.inline ||
|
|
488
|
+
workflowMeta.inline ||
|
|
489
|
+
!getSingletonServices()?.queueService
|
|
476
490
|
|
|
477
491
|
const runId = await this.createRun(
|
|
478
492
|
name,
|
|
@@ -484,16 +498,22 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
484
498
|
|
|
485
499
|
if (shouldInline) {
|
|
486
500
|
this.inlineRuns.add(runId)
|
|
487
|
-
|
|
488
|
-
.
|
|
501
|
+
try {
|
|
502
|
+
await this.runWorkflowJob(runId, rpcService)
|
|
503
|
+
} catch (error: any) {
|
|
504
|
+
if (
|
|
505
|
+
error.name !== 'WorkflowAsyncException' &&
|
|
506
|
+
error.name !== 'WorkflowCancelledException' &&
|
|
507
|
+
error.name !== 'WorkflowSuspendedException'
|
|
508
|
+
) {
|
|
489
509
|
getSingletonServices()!.logger.error(
|
|
490
510
|
`Workflow ${name} (run ${runId}) failed:`,
|
|
491
|
-
|
|
511
|
+
error
|
|
492
512
|
)
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
513
|
+
}
|
|
514
|
+
} finally {
|
|
515
|
+
this.inlineRuns.delete(runId)
|
|
516
|
+
}
|
|
497
517
|
} else {
|
|
498
518
|
await this.resumeWorkflow(runId)
|
|
499
519
|
}
|
|
@@ -553,6 +573,18 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
553
573
|
|
|
554
574
|
if (workflowMeta?.source === 'graph') {
|
|
555
575
|
await continueGraph(this, runId, run.workflow)
|
|
576
|
+
const updatedRun = await this.getRun(runId)
|
|
577
|
+
if (updatedRun?.status === 'completed') {
|
|
578
|
+
await this.onChildWorkflowCompleted(updatedRun, updatedRun.output)
|
|
579
|
+
} else if (
|
|
580
|
+
updatedRun?.status === 'failed' ||
|
|
581
|
+
updatedRun?.status === 'cancelled'
|
|
582
|
+
) {
|
|
583
|
+
await this.onChildWorkflowFailed(
|
|
584
|
+
updatedRun,
|
|
585
|
+
new Error(updatedRun.error?.message || 'Child workflow failed')
|
|
586
|
+
)
|
|
587
|
+
}
|
|
556
588
|
return
|
|
557
589
|
}
|
|
558
590
|
|
|
@@ -587,6 +619,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
587
619
|
)
|
|
588
620
|
|
|
589
621
|
await this.updateRunStatus(runId, 'completed', result)
|
|
622
|
+
await this.onChildWorkflowCompleted(run, result)
|
|
590
623
|
} catch (error: any) {
|
|
591
624
|
if (error instanceof WorkflowAsyncException) {
|
|
592
625
|
throw error
|
|
@@ -598,6 +631,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
598
631
|
stack: '',
|
|
599
632
|
code: 'WORKFLOW_CANCELLED',
|
|
600
633
|
})
|
|
634
|
+
await this.onChildWorkflowFailed(run, error)
|
|
601
635
|
throw error
|
|
602
636
|
}
|
|
603
637
|
|
|
@@ -615,12 +649,41 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
615
649
|
stack: error.stack,
|
|
616
650
|
code: error.code,
|
|
617
651
|
})
|
|
652
|
+
await this.onChildWorkflowFailed(run, error)
|
|
618
653
|
|
|
619
654
|
throw error
|
|
620
655
|
}
|
|
621
656
|
})
|
|
622
657
|
}
|
|
623
658
|
|
|
659
|
+
private async onChildWorkflowCompleted(
|
|
660
|
+
childRun: WorkflowRun,
|
|
661
|
+
result: any
|
|
662
|
+
): Promise<void> {
|
|
663
|
+
const { parentRunId, parentStepId } = childRun.wire ?? {}
|
|
664
|
+
if (!parentRunId || !parentStepId) return
|
|
665
|
+
|
|
666
|
+
this.logger?.debug(
|
|
667
|
+
`Child workflow ${childRun.id} completed, updating parent step ${parentStepId}`
|
|
668
|
+
)
|
|
669
|
+
await this.setStepResult(parentStepId, result)
|
|
670
|
+
await this.resumeWorkflow(parentRunId)
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
private async onChildWorkflowFailed(
|
|
674
|
+
childRun: WorkflowRun,
|
|
675
|
+
error: Error
|
|
676
|
+
): Promise<void> {
|
|
677
|
+
const { parentRunId, parentStepId } = childRun.wire ?? {}
|
|
678
|
+
if (!parentRunId || !parentStepId) return
|
|
679
|
+
|
|
680
|
+
this.logger?.debug(
|
|
681
|
+
`Child workflow ${childRun.id} failed, updating parent step ${parentStepId}`
|
|
682
|
+
)
|
|
683
|
+
await this.setStepError(parentStepId, error)
|
|
684
|
+
await this.resumeWorkflow(parentRunId)
|
|
685
|
+
}
|
|
686
|
+
|
|
624
687
|
private async runVersionMismatchFallback(
|
|
625
688
|
run: WorkflowRun,
|
|
626
689
|
currentMeta: { source: string },
|
|
@@ -666,9 +729,8 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
666
729
|
// Get step state
|
|
667
730
|
let stepState = await this.getStepState(runId, stepName)
|
|
668
731
|
|
|
669
|
-
// Idempotency - if already succeeded,
|
|
732
|
+
// Idempotency - if already succeeded, nothing to do
|
|
670
733
|
if (stepState.status === 'succeeded') {
|
|
671
|
-
await this.resumeWorkflow(runId)
|
|
672
734
|
return
|
|
673
735
|
}
|
|
674
736
|
|
|
@@ -682,8 +744,7 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
682
744
|
stepState = await this.createRetryAttempt(stepState.stepId, 'running')
|
|
683
745
|
}
|
|
684
746
|
|
|
685
|
-
if (stepState.status === 'pending') {
|
|
686
|
-
// Mark pending step as running before execution
|
|
747
|
+
if (stepState.status === 'pending' || stepState.status === 'scheduled') {
|
|
687
748
|
await this.setStepRunning(stepState.stepId)
|
|
688
749
|
}
|
|
689
750
|
|
|
@@ -725,6 +786,13 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
725
786
|
// Resume orchestrator to continue workflow
|
|
726
787
|
await this.resumeWorkflow(runId)
|
|
727
788
|
} catch (error: any) {
|
|
789
|
+
if (error instanceof ChildWorkflowStartedException) {
|
|
790
|
+
this.logger?.debug(
|
|
791
|
+
`Workflow step '${stepName}': child workflow ${error.childRunId} started, waiting for completion`
|
|
792
|
+
)
|
|
793
|
+
return
|
|
794
|
+
}
|
|
795
|
+
|
|
728
796
|
if (error instanceof RPCNotFoundError) {
|
|
729
797
|
await this.setStepError(stepState.stepId, error)
|
|
730
798
|
await this.updateRunStatus(runId, 'suspended', undefined, {
|
|
@@ -848,12 +916,14 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
848
916
|
|
|
849
917
|
await getSingletonServices()!.queueService!.add(
|
|
850
918
|
this.getConfig().stepWorkerQueueName,
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
919
|
+
JSON.parse(
|
|
920
|
+
JSON.stringify({
|
|
921
|
+
runId,
|
|
922
|
+
stepName,
|
|
923
|
+
rpcName,
|
|
924
|
+
data,
|
|
925
|
+
})
|
|
926
|
+
),
|
|
857
927
|
{
|
|
858
928
|
// attempts includes initial attempt, retries doesn't
|
|
859
929
|
attempts: retries + 1,
|
|
@@ -40,6 +40,7 @@ export interface WorkflowRunWire {
|
|
|
40
40
|
type: string
|
|
41
41
|
id?: string
|
|
42
42
|
parentRunId?: string
|
|
43
|
+
parentStepId?: string
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
export interface WorkflowServiceConfig {
|
|
@@ -224,6 +225,7 @@ export type WorkflowsMeta = Record<
|
|
|
224
225
|
steps: WorkflowStepMeta[]
|
|
225
226
|
context?: WorkflowContext
|
|
226
227
|
dsl?: boolean
|
|
228
|
+
inline?: boolean
|
|
227
229
|
}
|
|
228
230
|
>
|
|
229
231
|
|
|
@@ -243,6 +245,8 @@ export interface WorkflowRuntimeMeta {
|
|
|
243
245
|
description?: string
|
|
244
246
|
/** Tags for organization */
|
|
245
247
|
tags?: string[]
|
|
248
|
+
/** If true, workflow always executes inline without queues */
|
|
249
|
+
inline?: boolean
|
|
246
250
|
/** Serialized nodes */
|
|
247
251
|
nodes?: Record<string, any>
|
|
248
252
|
/** Entry node IDs for graph workflows (computed at build time) */
|