@pikku/core 0.12.69 → 0.12.71

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 (118) hide show
  1. package/CHANGELOG.md +422 -0
  2. package/LICENSE +21 -0
  3. package/README.md +34 -2
  4. package/dist/function/functions.types.d.ts +27 -0
  5. package/dist/index.d.ts +1 -1
  6. package/dist/internal.d.ts +1 -1
  7. package/dist/internal.js +1 -1
  8. package/dist/pikku-state.js +1 -0
  9. package/dist/services/http-scenario-actors.d.ts +12 -4
  10. package/dist/services/http-scenario-actors.js +47 -45
  11. package/dist/services/in-memory-queue-service.d.ts +6 -0
  12. package/dist/services/in-memory-queue-service.js +8 -1
  13. package/dist/services/in-memory-workflow-service.d.ts +3 -5
  14. package/dist/services/in-memory-workflow-service.js +10 -19
  15. package/dist/services/index.d.ts +2 -1
  16. package/dist/services/index.js +1 -0
  17. package/dist/services/meta-service.d.ts +5 -1
  18. package/dist/services/meta-service.js +44 -18
  19. package/dist/services/scenario-actors-service.d.ts +108 -2
  20. package/dist/services/scenario-actors-service.js +40 -1
  21. package/dist/services/workflow-service.d.ts +7 -5
  22. package/dist/types/core.types.d.ts +28 -3
  23. package/dist/types/state.types.d.ts +3 -1
  24. package/dist/wirings/actor-flow/actor-flow.types.d.ts +1 -1
  25. package/dist/wirings/actor-flow/index.d.ts +1 -1
  26. package/dist/wirings/actor-flow/run-conversation.d.ts +10 -10
  27. package/dist/wirings/actor-flow/run-conversation.js +27 -27
  28. package/dist/wirings/ai-agent/ai-agent-agui.js +0 -8
  29. package/dist/wirings/ai-agent/ai-agent-prepare.js +1 -2
  30. package/dist/wirings/ai-agent/ai-agent.types.d.ts +0 -6
  31. package/dist/wirings/cli/command-parser.js +11 -1
  32. package/dist/wirings/rpc/rpc-runner.js +1 -1
  33. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +52 -3
  34. package/dist/wirings/workflow/feature.d.ts +28 -0
  35. package/dist/wirings/workflow/feature.js +57 -0
  36. package/dist/wirings/workflow/graph/graph-runner.js +3 -2
  37. package/dist/wirings/workflow/graph/graph-validation.d.ts +0 -2
  38. package/dist/wirings/workflow/graph/graph-validation.js +0 -142
  39. package/dist/wirings/workflow/graph/index.d.ts +1 -1
  40. package/dist/wirings/workflow/graph/index.js +1 -1
  41. package/dist/wirings/workflow/index.d.ts +13 -3
  42. package/dist/wirings/workflow/index.js +15 -2
  43. package/dist/wirings/workflow/pikku-scenario-service.d.ts +121 -0
  44. package/dist/wirings/workflow/pikku-scenario-service.js +419 -0
  45. package/dist/wirings/workflow/pikku-workflow-service.d.ts +170 -23
  46. package/dist/wirings/workflow/pikku-workflow-service.js +338 -297
  47. package/dist/wirings/workflow/scenario-cookie-jar.d.ts +29 -0
  48. package/dist/wirings/workflow/scenario-cookie-jar.js +51 -0
  49. package/dist/wirings/workflow/scenario-poll.d.ts +20 -0
  50. package/dist/wirings/workflow/scenario-poll.js +25 -0
  51. package/dist/wirings/workflow/scenario-prose.d.ts +38 -0
  52. package/dist/wirings/workflow/scenario-prose.js +45 -0
  53. package/dist/wirings/workflow/scenario-step-guards.d.ts +16 -0
  54. package/dist/wirings/workflow/scenario-step-guards.js +29 -0
  55. package/dist/wirings/workflow/scenario-step.types.d.ts +148 -0
  56. package/dist/wirings/workflow/scenario-step.types.js +1 -0
  57. package/dist/wirings/workflow/workflow.types.d.ts +82 -8
  58. package/package.json +3 -1
  59. package/src/function/functions.types.ts +32 -0
  60. package/src/index.ts +1 -0
  61. package/src/internal.ts +5 -1
  62. package/src/pikku-state.ts +1 -0
  63. package/src/services/http-scenario-actors.test.ts +85 -1
  64. package/src/services/http-scenario-actors.ts +65 -51
  65. package/src/services/in-memory-queue-service.test.ts +66 -1
  66. package/src/services/in-memory-queue-service.ts +13 -2
  67. package/src/services/in-memory-workflow-service.ts +12 -25
  68. package/src/services/index.ts +5 -0
  69. package/src/services/meta-service.test.ts +79 -0
  70. package/src/services/meta-service.ts +61 -26
  71. package/src/services/scenario-actors-service.ts +157 -2
  72. package/src/services/workflow-service.ts +7 -4
  73. package/src/types/core.types.ts +34 -2
  74. package/src/types/state.types.ts +3 -0
  75. package/src/wirings/actor-flow/actor-flow.types.ts +1 -1
  76. package/src/wirings/actor-flow/index.ts +1 -1
  77. package/src/wirings/actor-flow/run-conversation.test.ts +12 -6
  78. package/src/wirings/actor-flow/run-conversation.ts +36 -41
  79. package/src/wirings/ai-agent/ai-agent-agui.test.ts +0 -16
  80. package/src/wirings/ai-agent/ai-agent-agui.ts +0 -9
  81. package/src/wirings/ai-agent/ai-agent-prepare.ts +1 -2
  82. package/src/wirings/ai-agent/ai-agent.types.ts +0 -7
  83. package/src/wirings/cli/command-parser.test.ts +60 -0
  84. package/src/wirings/cli/command-parser.ts +12 -1
  85. package/src/wirings/rpc/rpc-runner.test.ts +28 -5
  86. package/src/wirings/rpc/rpc-runner.ts +1 -1
  87. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +86 -2
  88. package/src/wirings/workflow/feature.test.ts +131 -0
  89. package/src/wirings/workflow/feature.ts +78 -0
  90. package/src/wirings/workflow/graph/graph-runner.ts +3 -2
  91. package/src/wirings/workflow/graph/graph-validation.test.ts +1 -144
  92. package/src/wirings/workflow/graph/graph-validation.ts +0 -196
  93. package/src/wirings/workflow/graph/index.ts +1 -5
  94. package/src/wirings/workflow/index.ts +73 -6
  95. package/src/wirings/workflow/pikku-scenario-service.ts +682 -0
  96. package/src/wirings/workflow/pikku-workflow-service.test.ts +55 -0
  97. package/src/wirings/workflow/pikku-workflow-service.ts +572 -419
  98. package/src/wirings/workflow/scenario-cookie-jar.test.ts +108 -0
  99. package/src/wirings/workflow/scenario-cookie-jar.ts +65 -0
  100. package/src/wirings/workflow/scenario-expectations.test.ts +153 -0
  101. package/src/wirings/workflow/scenario-hooks.test.ts +212 -0
  102. package/src/wirings/workflow/scenario-poll.test.ts +66 -0
  103. package/src/wirings/workflow/scenario-poll.ts +36 -0
  104. package/src/wirings/workflow/scenario-prose.test.ts +152 -0
  105. package/src/wirings/workflow/scenario-prose.ts +79 -0
  106. package/src/wirings/workflow/scenario-service.test.ts +155 -0
  107. package/src/wirings/workflow/scenario-step-guards.ts +43 -0
  108. package/src/wirings/workflow/scenario-step.test.ts +442 -9
  109. package/src/wirings/workflow/scenario-step.types.ts +157 -0
  110. package/src/wirings/workflow/workflow-dispatch-durability.test.ts +1 -1
  111. package/src/wirings/workflow/workflow-dispatch-payload.test.ts +59 -0
  112. package/src/wirings/workflow/workflow-mirror.test.ts +178 -0
  113. package/src/wirings/workflow/workflow-replay-snapshot.test.ts +139 -0
  114. package/src/wirings/workflow/workflow-run-context.test.ts +177 -0
  115. package/src/wirings/workflow/workflow-run-polling.test.ts +132 -0
  116. package/src/wirings/workflow/workflow-step-ordinal.test.ts +4 -4
  117. package/src/wirings/workflow/workflow.types.ts +99 -5
  118. package/tsconfig.tsbuildinfo +1 -1
@@ -1,5 +1,5 @@
1
- import type { SerializedError } from '../../types/core.types.js';
2
- import type { ApprovalOutcome, PikkuScenarioWire, StepState, StepStatus, WorkflowPlannedStep, WorkflowRun, WorkflowRunMirror, WorkflowRunStatus, WorkflowRunWire, WorkflowStatus, WorkflowVersionStatus, WorkflowQueueOptions, WorkflowStepOptions } from './workflow.types.js';
1
+ import type { PikkuWire, SerializedError } from '../../types/core.types.js';
2
+ import type { ApprovalOutcome, CoreWorkflow, PikkuWorkflowWire, StepState, StepStatus, WorkflowPlannedStep, WorkflowRun, WorkflowRunMirror, WorkflowRunStatus, WorkflowRunWire, WorkflowStatus, WorkflowVersionStatus, WorkflowQueueOptions, WorkflowStepOptions } from './workflow.types.js';
3
3
  import type { WorkflowService } from '../../services/workflow-service.js';
4
4
  import type { ScenarioActors } from '../../services/scenario-actors-service.js';
5
5
  import { PikkuError } from '../../errors/error-handler.js';
@@ -89,13 +89,77 @@ export declare class WorkflowStepNameNotString extends Error {
89
89
  constructor(stepName: any);
90
90
  }
91
91
  /**
92
- * Abstract workflow state service
93
- * Implementations provide pluggable storage backends (SQLite, PostgreSQL, etc.)
94
- * Combines orchestration and step execution
92
+ * Everything an extension needs to run something of its own around a run's
93
+ * function: the registration it was resolved from, the run itself (so `input`
94
+ * is the same object the function is called with), and the wire it is given.
95
95
  */
96
+ export interface RunLifecycleContext {
97
+ runId: string;
98
+ run: WorkflowRun;
99
+ workflowMeta: any;
100
+ workflow: CoreWorkflow;
101
+ wire: PikkuWire;
102
+ packageName: string | null;
103
+ }
104
+ /**
105
+ * The slice of the run engine an extension is allowed to drive, handed to it at
106
+ * construction. It exists so that recording a durable step stays available to
107
+ * an extension without `inlineStep` and friends becoming public API on every
108
+ * workflow service a production app instantiates.
109
+ */
110
+ export interface WorkflowRunEngine {
111
+ inlineStep(runId: string, logicalStepName: string, fn: Function, stepOptions?: WorkflowStepOptions, data?: any, funcName?: string): Promise<any>;
112
+ updateRunStatus(runId: string, status: WorkflowStatus, output?: any, error?: SerializedError): Promise<void>;
113
+ onChildWorkflowFailed(run: WorkflowRun, error: unknown): Promise<void>;
114
+ verifyStepName(stepName: unknown): void;
115
+ }
116
+ /**
117
+ * A capability layered onto a run without being a workflow service itself.
118
+ *
119
+ * The engine names nothing about what an extension is for: a bundler drops an
120
+ * unused *module* but never an unused class member, so the alternative — a
121
+ * subclass carrying the capability — puts it in every server built on Pikku
122
+ * whether or not the app ever uses it. Scenarios are the one implementation
123
+ * today (`PikkuScenarioService` in `@pikku/core/scenario`).
124
+ */
125
+ export interface WorkflowRunExtension {
126
+ /** Per-run state resolved once the run has an id. */
127
+ attachRunContext(runId: string, workflowMeta: any, options?: Record<string, any>): Promise<void>;
128
+ /** Release whatever `attachRunContext` stored. */
129
+ detachRunContext(runId: string): void;
130
+ /** Contribute extra members to the wire a run's function is invoked with. */
131
+ decorateRunWire(wire: PikkuWire, context: {
132
+ runId: string;
133
+ workflowMeta: any;
134
+ workflowWire: PikkuWorkflowWire;
135
+ }): void;
136
+ /** Contribute extra members to the workflow wire itself, in place. */
137
+ decorateWorkflowWire(workflowWire: PikkuWorkflowWire, context: {
138
+ name: string;
139
+ runId: string;
140
+ rpcService: any;
141
+ addonNamespace?: string | null;
142
+ }): void;
143
+ /**
144
+ * Called immediately before the run's function, inside the run lock. Throwing
145
+ * skips the function and fails the run.
146
+ */
147
+ onBeforeRunFunc(context: RunLifecycleContext): Promise<void>;
148
+ /**
149
+ * Called once the run reaches a terminal state, in a `finally`, so it runs
150
+ * whether the function passed or threw. `interrupted` means the run suspended
151
+ * or went async and is still mid-flight. Throwing here propagates, replacing
152
+ * a pending failure, so an extension that must not mask one handles that
153
+ * itself.
154
+ */
155
+ onAfterRunFunc(context: RunLifecycleContext, outcome: 'completed' | 'failed' | 'interrupted', failure: unknown): Promise<void>;
156
+ }
96
157
  export declare abstract class PikkuWorkflowService implements WorkflowService {
97
- private inlineRuns;
98
- private runActors;
158
+ private runExtension?;
159
+ private runContexts;
160
+ private contextFor;
161
+ /** Drop a run's context once nothing is holding it open. */
162
+ private releaseContext;
99
163
  protected get logger(): import("../../services/logger.js").Logger;
100
164
  protected mirror?: WorkflowRunMirror;
101
165
  protected readonly queueStrategy: 'per-workflow' | 'shared-groups';
@@ -105,7 +169,18 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
105
169
  wireQueues?: boolean;
106
170
  mirror?: WorkflowRunMirror;
107
171
  } & WorkflowQueueOptions);
108
- private safeMirror;
172
+ /**
173
+ * Perform a state write, then shadow it to the mirror.
174
+ *
175
+ * The mirror is an observability sink, never a second source of truth, and
176
+ * both halves of that follow from this one shape: it is only ever told about
177
+ * a write that already landed, and a mirror that is down or throwing cannot
178
+ * fail — or even be seen by — the workflow it is watching.
179
+ *
180
+ * @param write - the authoritative write; its result is what the caller gets
181
+ * @param mirror - shadows the write, given the live mirror and what was written
182
+ */
183
+ private mirrored;
109
184
  /**
110
185
  * Wire the queue-based orchestrator/step/sleeper workers.
111
186
  * Subclasses that orchestrate without queues (e.g. Durable Objects) should
@@ -179,7 +254,10 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
179
254
  * Creates pending step in both workflow_step and workflow_step_history
180
255
  * @param runId - Run ID
181
256
  * @param stepName - Step cache key
182
- * @param rpcName - RPC function name
257
+ * @param rpcName - The name this step was dispatched by: an RPC for a
258
+ * `workflow.do` step, a step function for a scenario step, null for a
259
+ * closure. Nothing dispatches off this value — it is recorded so a reader
260
+ * can join a step back to the function that ran it.
183
261
  * @param data - Step input data
184
262
  * @param stepOptions - Step options (retries, retryDelay)
185
263
  * @returns Step state with generated stepId
@@ -323,11 +401,6 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
323
401
  graph: any;
324
402
  source: string;
325
403
  } | null>;
326
- abstract getAIGeneratedWorkflows(agentName?: string): Promise<Array<{
327
- workflowName: string;
328
- graphHash: string;
329
- graph: any;
330
- }>>;
331
404
  /**
332
405
  * Resume a paused workflow by triggering the orchestrator
333
406
  * @param runId - Run ID
@@ -382,18 +455,28 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
382
455
  * false to fall through to inline `setTimeout` behavior.
383
456
  */
384
457
  protected scheduleSleep(runId: string, stepId: string, duration: number | string): Promise<boolean>;
385
- /** Build HTTP scenario actors for a run started without them; undefined when SCENARIO_ACTOR_SECRET or the API URL is missing */
386
- private resolveScenarioActors;
458
+ /**
459
+ * Install the one extension a run may have, built from a handle onto the run
460
+ * engine so that `inlineStep` and friends stay protected rather than becoming
461
+ * public API. Returns the extension, so the caller keeps a typed reference to
462
+ * whatever it just built.
463
+ */
464
+ setRunExtension<T extends WorkflowRunExtension>(create: (engine: WorkflowRunEngine) => T): T;
465
+ getRunExtension(): WorkflowRunExtension | undefined;
387
466
  /**
388
467
  * Start a new workflow run
389
468
  * Automatically detects workflow type (DSL or graph) from meta and executes accordingly
390
469
  * @param options.inline - If true, execute workflow directly without queue service
391
470
  * @param options.startNode - Starting node ID for graph workflows (from wire config)
471
+ * @param options.onRunCreated - Called with the run id the moment the run exists.
472
+ * An inline run that fails throws instead of returning, so this is the only
473
+ * way a caller can still read that run back — its steps, and which one failed.
392
474
  */
393
475
  startWorkflow<I>(name: string, input: I, wire: WorkflowRunWire, rpcService: any, options?: {
394
476
  inline?: boolean;
395
477
  startNode?: string;
396
478
  actors?: ScenarioActors;
479
+ onRunCreated?: (runId: string) => void;
397
480
  }): Promise<{
398
481
  runId: string;
399
482
  }>;
@@ -401,9 +484,59 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
401
484
  pollIntervalMs?: number;
402
485
  wire?: WorkflowRunWire;
403
486
  }): Promise<any>;
404
- private stepOrdinals;
405
- private stepLineage;
406
- private resetStepOrdinals;
487
+ /**
488
+ * Read a run until it reaches an end state, backing off as it drags on.
489
+ *
490
+ * A fixed interval is wrong at both ends: it makes a workflow that finished
491
+ * in milliseconds wait out the whole interval anyway, and it keeps reading a
492
+ * long-running one at full rate for as long as it lasts. Starting short and
493
+ * growing to `maxIntervalMs` returns quick runs promptly while a slow run's
494
+ * read cost grows logarithmically rather than linearly with its duration.
495
+ */
496
+ protected awaitRunEnd(runId: string, maxIntervalMs: number): Promise<WorkflowRun>;
497
+ /**
498
+ * Wait between two reads of a run.
499
+ *
500
+ * Its own method so the backoff schedule can be asserted on directly. Timing
501
+ * a poll loop by the clock measures the host's scheduler as much as the
502
+ * policy — `setTimeout(40)` routinely returns late on a loaded runner — which
503
+ * makes the obvious test both slow and flaky.
504
+ */
505
+ protected waitBeforeNextRead(ms: number): Promise<void>;
506
+ /**
507
+ * Every step of a run in one read, or `null` if this backend has no bulk read.
508
+ *
509
+ * A replay walks the DSL body from the top, and each step it passes asks for
510
+ * its own row — so a run of N steps costs N reads per replay and O(N^2) over
511
+ * its lifetime. Backends that can answer this in a single query collapse that
512
+ * to one read per replay.
513
+ */
514
+ protected listStepStates(_runId: string): Promise<Array<StepState & {
515
+ stepName: string;
516
+ }> | null>;
517
+ /**
518
+ * Begin a replay pass: fresh ordinal counters, and one read of the steps the
519
+ * run has already taken so the walk back to where it left off is served from
520
+ * memory. Safe because a pass reaches each step key at most once, and the
521
+ * steps it replays past are `succeeded` and therefore immutable.
522
+ */
523
+ private beginReplay;
524
+ private endReplay;
525
+ /**
526
+ * The step row for `stepName`, creating it if the run has not reached it
527
+ * before. Served from the replay snapshot when one is loaded.
528
+ */
529
+ private loadOrCreateStep;
530
+ /**
531
+ * The run's immutable half — which workflow it is, the wire it was started
532
+ * on, its input. `getRun` is otherwise called several times per step for
533
+ * answers that were all fixed at creation, so a replay reads it once and
534
+ * hands the same object to everyone who only needs that half.
535
+ *
536
+ * Anyone who needs `status`, `output`, `error` or `state` must call `getRun`:
537
+ * those move while the run executes, and a cached copy would be a lie.
538
+ */
539
+ private getRunIdentity;
407
540
  /** The step the DSL walk last reached (the predecessor for the next step). */
408
541
  private lastStepName;
409
542
  /**
@@ -416,7 +549,7 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
416
549
  runWorkflowJob(runId: string, rpcService: any): Promise<void>;
417
550
  private runWorkflowJobInner;
418
551
  private onChildWorkflowCompleted;
419
- private onChildWorkflowFailed;
552
+ protected onChildWorkflowFailed(childRun: WorkflowRun, error: Error): Promise<void>;
420
553
  private runVersionMismatchFallback;
421
554
  /**
422
555
  * Execute a single workflow step (called by worker)
@@ -455,7 +588,21 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
455
588
  */
456
589
  private runStepCompensation;
457
590
  private rpcStep;
458
- private inlineStep;
591
+ protected inlineStep(runId: string, logicalStepName: string, fn: Function, stepOptions?: WorkflowStepOptions,
592
+ /**
593
+ * The input this step was called with, recorded on the run so a reporter can
594
+ * name the values under test. A closure step has none; a scenario step does.
595
+ */
596
+ data?: any,
597
+ /**
598
+ * The name this step was dispatched by, for the kinds of inline step that
599
+ * have one. A closure step has no name; a scenario step is a step RPC, so
600
+ * it records the step function that ran — which is the only way to join a
601
+ * step back to its declaration when its durable name was built at runtime
602
+ * (a step called in a loop reaches the run as `sees @pikku/addon-todos`,
603
+ * declared as `sees ${packageName}`).
604
+ */
605
+ rpcName?: string | null): Promise<any>;
459
606
  private sleepStep;
460
607
  /**
461
608
  * Derive the durable step name for a suspend point from its `reason`, so each
@@ -507,8 +654,8 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
507
654
  */
508
655
  approveStep(runId: string, reason: string, decision: unknown): Promise<void>;
509
656
  private approvalStep;
510
- createWorkflowWire(name: string, runId: string, rpcService: any, addonNamespace?: string | null): PikkuScenarioWire;
511
- private verifyStepName;
657
+ createWorkflowWire(name: string, runId: string, rpcService: any, addonNamespace?: string | null): PikkuWorkflowWire;
658
+ protected verifyStepName(stepName: string): void;
512
659
  private getConfig;
513
660
  /**
514
661
  * Get the orchestrator queue name for a specific workflow.