@pikku/core 0.12.148 → 0.12.151

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 (139) hide show
  1. package/dist/function/function-runner.js +23 -0
  2. package/dist/function/trace-queue-service.d.ts +26 -0
  3. package/dist/function/trace-queue-service.js +48 -0
  4. package/dist/mesh.d.ts +2 -2
  5. package/dist/mesh.js +3 -3
  6. package/dist/middleware/index.d.ts +1 -1
  7. package/dist/middleware/index.js +1 -1
  8. package/dist/middleware/mesh-auth.d.ts +3 -2
  9. package/dist/middleware/mesh-auth.js +7 -2
  10. package/dist/middleware/remote-jobs-secret.d.ts +1 -1
  11. package/dist/middleware/require-origin.d.ts +19 -0
  12. package/dist/middleware/require-origin.js +36 -2
  13. package/dist/services/deployment-service.d.ts +1 -1
  14. package/dist/services/incoming-webhook-service.d.ts +4 -2
  15. package/dist/services/incoming-webhook-service.js +4 -3
  16. package/dist/services/queue-webhook-service.js +7 -1
  17. package/dist/services/remote-jobs.d.ts +6 -1
  18. package/dist/services/scheduler-service.d.ts +3 -1
  19. package/dist/services/webhook-service.d.ts +2 -0
  20. package/dist/services/workflow-service.d.ts +4 -1
  21. package/dist/testing/index.d.ts +4 -0
  22. package/dist/testing/index.js +2 -0
  23. package/dist/testing/queue-identity-transport-tests.d.ts +18 -0
  24. package/dist/testing/queue-identity-transport-tests.js +162 -0
  25. package/dist/testing/queue-trace-conformance.d.ts +26 -0
  26. package/dist/testing/queue-trace-conformance.js +259 -0
  27. package/dist/testing/service-tests/channel-store-tests.js +11 -0
  28. package/dist/testing/service-tests/queued-workflow-harness.d.ts +18 -1
  29. package/dist/testing/service-tests/queued-workflow-harness.js +19 -3
  30. package/dist/testing/service-tests/workflow-service-tests.js +12 -0
  31. package/dist/testing/trace-matrix/assertions.d.ts +3 -0
  32. package/dist/testing/trace-matrix/assertions.js +190 -0
  33. package/dist/testing/trace-matrix/chain-harness.d.ts +119 -0
  34. package/dist/testing/trace-matrix/chain-harness.js +777 -0
  35. package/dist/testing/trace-matrix/dimensions.d.ts +55 -0
  36. package/dist/testing/trace-matrix/dimensions.js +305 -0
  37. package/dist/testing/trace-matrix/pairwise.d.ts +20 -0
  38. package/dist/testing/trace-matrix/pairwise.js +75 -0
  39. package/dist/testing/trace-matrix/report.d.ts +1 -0
  40. package/dist/testing/trace-matrix/report.js +16 -0
  41. package/dist/testing/trace-matrix/transports.d.ts +19 -0
  42. package/dist/testing/trace-matrix/transports.js +27 -0
  43. package/dist/testing/trace-matrix/workflow-cases.d.ts +24 -0
  44. package/dist/testing/trace-matrix/workflow-cases.js +147 -0
  45. package/dist/trace.d.ts +66 -0
  46. package/dist/trace.js +97 -0
  47. package/dist/types/core.types.d.ts +6 -2
  48. package/dist/wirings/addon/addon-reach.d.ts +3 -0
  49. package/dist/wirings/addon/addon-reach.js +9 -0
  50. package/dist/wirings/addon/define-plugin.d.ts +16 -0
  51. package/dist/wirings/addon/wire-addon.js +5 -0
  52. package/dist/wirings/addon/wire-remote-addon.d.ts +11 -0
  53. package/dist/wirings/agent/agent-finalize.d.ts +2 -0
  54. package/dist/wirings/agent/agent-finalize.js +1 -1
  55. package/dist/wirings/agent/agent-prepare.d.ts +12 -2
  56. package/dist/wirings/agent/agent-prepare.js +38 -14
  57. package/dist/wirings/agent/agent-rpc.js +2 -1
  58. package/dist/wirings/agent/agent-runner.js +5 -6
  59. package/dist/wirings/agent/agent-stream.js +6 -6
  60. package/dist/wirings/agent/voice-input.d.ts +1 -1
  61. package/dist/wirings/agent/voice-output.d.ts +1 -1
  62. package/dist/wirings/agent-scorer/agent-scorer-live.d.ts +4 -2
  63. package/dist/wirings/agent-scorer/agent-scorer-live.js +2 -2
  64. package/dist/wirings/channel/channel-common.d.ts +4 -1
  65. package/dist/wirings/channel/channel-common.js +3 -1
  66. package/dist/wirings/channel/channel-handler.js +2 -0
  67. package/dist/wirings/channel/channel-host-rpc.d.ts +1 -1
  68. package/dist/wirings/channel/channel-host-rpc.js +2 -2
  69. package/dist/wirings/channel/channel-rpc-responder.d.ts +7 -1
  70. package/dist/wirings/channel/channel-rpc-responder.js +2 -2
  71. package/dist/wirings/channel/channel-rpc-service.d.ts +1 -1
  72. package/dist/wirings/channel/channel-rpc-service.js +2 -2
  73. package/dist/wirings/channel/channel-rpc.types.d.ts +5 -2
  74. package/dist/wirings/channel/channel-rpc.types.js +1 -1
  75. package/dist/wirings/channel/channel-store.d.ts +4 -2
  76. package/dist/wirings/channel/channel-store.js +2 -2
  77. package/dist/wirings/channel/channel.types.d.ts +3 -0
  78. package/dist/wirings/channel/local/local-channel-runner.js +16 -5
  79. package/dist/wirings/channel/pikku-abstract-channel-handler.d.ts +2 -0
  80. package/dist/wirings/channel/pikku-abstract-channel-handler.js +3 -1
  81. package/dist/wirings/channel/serverless/serverless-channel-runner.js +27 -4
  82. package/dist/wirings/gateway/gateway-runner.js +3 -1
  83. package/dist/wirings/http/http-runner.d.ts +4 -4
  84. package/dist/wirings/http/http-runner.js +14 -18
  85. package/dist/wirings/http/http.types.d.ts +3 -3
  86. package/dist/wirings/mcp/mcp-runner.js +15 -5
  87. package/dist/wirings/queue/in-memory-queue-service.d.ts +1 -1
  88. package/dist/wirings/queue/in-memory-queue-service.js +8 -2
  89. package/dist/wirings/queue/index.d.ts +1 -0
  90. package/dist/wirings/queue/index.js +1 -0
  91. package/dist/wirings/queue/queue-runner.d.ts +3 -4
  92. package/dist/wirings/queue/queue-runner.js +8 -7
  93. package/dist/wirings/queue/queue-trace.d.ts +18 -0
  94. package/dist/wirings/queue/queue-trace.js +42 -0
  95. package/dist/wirings/queue/queue.types.d.ts +7 -0
  96. package/dist/wirings/queue/remote-queue-job.d.ts +1 -1
  97. package/dist/wirings/queue/remote-queue-job.js +5 -3
  98. package/dist/wirings/rpc/rpc-runner.d.ts +7 -1
  99. package/dist/wirings/rpc/rpc-runner.js +43 -21
  100. package/dist/wirings/rpc/rpc-types.d.ts +2 -0
  101. package/dist/wirings/scheduler/remote-scheduled-job.d.ts +1 -1
  102. package/dist/wirings/scheduler/remote-scheduled-job.js +2 -2
  103. package/dist/wirings/scheduler/scheduler-runner.d.ts +4 -4
  104. package/dist/wirings/scheduler/scheduler-runner.js +7 -6
  105. package/dist/wirings/trigger/pikku-trigger-service.js +7 -4
  106. package/dist/wirings/trigger/webhook-source-runner.d.ts +7 -1
  107. package/dist/wirings/trigger/webhook-source-runner.js +11 -3
  108. package/dist/wirings/workflow/graph/graph-runner.d.ts +1 -1
  109. package/dist/wirings/workflow/graph/graph-runner.js +49 -30
  110. package/dist/wirings/workflow/index.d.ts +3 -1
  111. package/dist/wirings/workflow/index.js +1 -0
  112. package/dist/wirings/workflow/pikku-scenario-service.js +2 -0
  113. package/dist/wirings/workflow/pikku-workflow-service.d.ts +37 -40
  114. package/dist/wirings/workflow/pikku-workflow-service.js +122 -133
  115. package/dist/wirings/workflow/workflow-child-step.d.ts +2 -2
  116. package/dist/wirings/workflow/workflow-child-step.js +11 -4
  117. package/dist/wirings/workflow/workflow-compensation.d.ts +6 -0
  118. package/dist/wirings/workflow/workflow-compensation.js +15 -1
  119. package/dist/wirings/workflow/workflow-constants.d.ts +13 -0
  120. package/dist/wirings/workflow/workflow-constants.js +13 -0
  121. package/dist/wirings/workflow/workflow-dsl-pass.d.ts +2 -0
  122. package/dist/wirings/workflow/workflow-dsl-pass.js +27 -11
  123. package/dist/wirings/workflow/workflow-recovery.d.ts +131 -3
  124. package/dist/wirings/workflow/workflow-recovery.js +233 -7
  125. package/dist/wirings/workflow/workflow-run-engine.types.d.ts +1 -0
  126. package/dist/wirings/workflow/workflow-run-mirror.d.ts +4 -0
  127. package/dist/wirings/workflow/workflow-run-mirror.js +16 -0
  128. package/dist/wirings/workflow/workflow-run-status.d.ts +2 -0
  129. package/dist/wirings/workflow/workflow-run-status.js +13 -0
  130. package/dist/wirings/workflow/workflow-run-trace.d.ts +22 -0
  131. package/dist/wirings/workflow/workflow-run-trace.js +24 -0
  132. package/dist/wirings/workflow/workflow-step-claim.d.ts +18 -0
  133. package/dist/wirings/workflow/workflow-step-claim.js +47 -0
  134. package/dist/wirings/workflow/workflow-step-retry.d.ts +6 -2
  135. package/dist/wirings/workflow/workflow-step-retry.js +8 -3
  136. package/dist/wirings/workflow/workflow-wire.d.ts +12 -0
  137. package/dist/wirings/workflow/workflow-wire.js +33 -0
  138. package/dist/wirings/workflow/workflow.types.d.ts +16 -0
  139. package/package.json +2 -1
@@ -1,10 +1,52 @@
1
1
  import { DEFAULT_STEP_RETRIES, isStepLeaseLive } from './workflow-constants.js';
2
2
  import { WorkflowStepFunctionMismatchError, WorkflowStepLeaseExpiredError, } from './workflow-errors.js';
3
+ import { deriveInvocationId } from './workflow-invocation-id.js';
4
+ import { parseTraceparent, tracedLogger } from '../../trace.js';
5
+ const warnLostStep = async (log, runId, stepName, attempt, message) => {
6
+ // The step has already moved on, so no failure here may reach the claim.
7
+ try {
8
+ const logger = log.logger();
9
+ if (!logger)
10
+ return;
11
+ const traceparent = await log.traceparent(runId).catch(() => undefined);
12
+ tracedLogger(logger, parseTraceparent(traceparent)?.traceId).warn({
13
+ message,
14
+ runId,
15
+ stepName,
16
+ attempt,
17
+ invocationId: deriveInvocationId(runId, stepName),
18
+ });
19
+ }
20
+ catch { }
21
+ };
22
+ export const warnStepRerun = (log, runId, stepName, attempt) => warnLostStep(log, runId, stepName, attempt, `Workflow step '${stepName}' (run ${runId}) lost its worker and is running again as attempt ${attempt}`);
23
+ export const warnStepAttemptsExhausted = (log, runId, stepName, attempts) => warnLostStep(log, runId, stepName, attempts, `Workflow step '${stepName}' (run ${runId}) lost its worker on attempt ${attempts} with no attempts left, so it failed`);
3
24
  /**
4
25
  * Whether a step whose worker vanished may be handed to another one, or has
5
26
  * run out of attempts to spend on that.
6
27
  */
7
28
  export const leaseAttemptsExhausted = (stepState) => stepState.attemptCount >= (stepState.retries ?? DEFAULT_STEP_RETRIES) + 1;
29
+ /**
30
+ * A pass holds its run's lease for as long as it works a step inline, so one
31
+ * that meets its own step already `running` is looking at a pass that died
32
+ * mid-step. Running it again is a new attempt, counted as one so a step that
33
+ * takes its process down every time runs out instead of looping forever. The
34
+ * invocation id comes from the step's name, so the attempt keeps it.
35
+ */
36
+ export const takeBackLostInlineStep = async (store, runId, stepName, stepState) => {
37
+ if (stepState.status !== 'running') {
38
+ return stepState;
39
+ }
40
+ if (leaseAttemptsExhausted(stepState)) {
41
+ const error = new WorkflowStepLeaseExpiredError(runId, stepName, stepState.attemptCount);
42
+ await store.setStepError(stepState.stepId, error, stepState.attemptCount);
43
+ await store.logStepAttemptsExhausted?.(runId, stepName, stepState.attemptCount);
44
+ throw error;
45
+ }
46
+ const attempt = await store.createRetryAttempt(stepState.stepId, 'pending');
47
+ await store.logStepRerun?.(runId, stepName, attempt.attemptCount);
48
+ return attempt;
49
+ };
8
50
  /**
9
51
  * Decide whether this dispatch owns the step, by reading its state and then
10
52
  * writing it — which only excludes a concurrent dispatch when the caller holds
@@ -29,6 +71,7 @@ export const claimStepByReadThenWrite = async (store, runId, stepName, rpcName,
29
71
  }
30
72
  if (leaseAttemptsExhausted(stepState)) {
31
73
  await store.setStepError(stepState.stepId, new WorkflowStepLeaseExpiredError(runId, stepName, stepState.attemptCount), stepState.attemptCount);
74
+ await store.logStepAttemptsExhausted?.(runId, stepName, stepState.attemptCount);
32
75
  await store.resumeWorkflow?.(runId);
33
76
  return null;
34
77
  }
@@ -41,6 +84,10 @@ export const claimStepByReadThenWrite = async (store, runId, stepName, rpcName,
41
84
  if (stepState.status === 'failed' || stepState.status === 'running') {
42
85
  const attempt = await store.createRetryAttempt(stepState.stepId, 'running');
43
86
  await store.refreshStepLease(attempt.stepId, leaseMs);
87
+ // A `failed` step threw, and this is its ordinary retry.
88
+ if (stepState.status === 'running') {
89
+ await store.logStepRerun?.(runId, stepName, attempt.attemptCount);
90
+ }
44
91
  return attempt;
45
92
  }
46
93
  if (stepState.status === 'pending' || stepState.status === 'scheduled') {
@@ -9,6 +9,10 @@ export type StepRetryStore = {
9
9
  /**
10
10
  * Spend a step's retry budget here and now, rather than by handing the run back
11
11
  * to the orchestrator between attempts. For an inline run there is nothing to
12
- * hand it back to.
12
+ * hand it back to. Nothing ends the run between attempts either, so a forward
13
+ * step passes `beforeAttempt` to stop once the run has been cancelled.
14
+ *
15
+ * `retries` counts what follows the first attempt, as it does on the queue, so
16
+ * a step gets `retries + 1` attempts.
13
17
  */
14
- export declare const runInlineRetryLoop: (store: StepRetryStore, stepState: StepState, retries: number, retryDelay: WorkflowStepOptions['retryDelay'], doWork: (currentStepState: StepState) => Promise<any>, onError?: (error: any) => Promise<void>) => Promise<any>;
18
+ export declare const runInlineRetryLoop: (store: StepRetryStore, stepState: StepState, retries: number, retryDelay: WorkflowStepOptions['retryDelay'], doWork: (currentStepState: StepState) => Promise<any>, onError?: (error: any) => Promise<void>, beforeAttempt?: () => Promise<void>) => Promise<any>;
@@ -2,11 +2,16 @@ import { getDurationInMilliseconds } from '../../time-utils.js';
2
2
  /**
3
3
  * Spend a step's retry budget here and now, rather than by handing the run back
4
4
  * to the orchestrator between attempts. For an inline run there is nothing to
5
- * hand it back to.
5
+ * hand it back to. Nothing ends the run between attempts either, so a forward
6
+ * step passes `beforeAttempt` to stop once the run has been cancelled.
7
+ *
8
+ * `retries` counts what follows the first attempt, as it does on the queue, so
9
+ * a step gets `retries + 1` attempts.
6
10
  */
7
- export const runInlineRetryLoop = async (store, stepState, retries, retryDelay, doWork, onError) => {
11
+ export const runInlineRetryLoop = async (store, stepState, retries, retryDelay, doWork, onError, beforeAttempt) => {
8
12
  let currentStepState = stepState;
9
13
  while (true) {
14
+ await beforeAttempt?.();
10
15
  let result;
11
16
  try {
12
17
  await store.setStepRunning(currentStepState.stepId);
@@ -16,7 +21,7 @@ export const runInlineRetryLoop = async (store, stepState, retries, retryDelay,
16
21
  if (onError)
17
22
  await onError(error);
18
23
  await store.setStepError(currentStepState.stepId, error);
19
- if (currentStepState.attemptCount >= retries) {
24
+ if (currentStepState.attemptCount > retries) {
20
25
  throw error;
21
26
  }
22
27
  currentStepState = await store.createRetryAttempt(currentStepState.stepId, 'pending');
@@ -0,0 +1,12 @@
1
+ import type { PikkuWorkflowWire, WorkflowApprovalOptions, WorkflowRun } from './workflow.types.js';
2
+ /** The steps a wire drives, already bound to its run. */
3
+ export type WorkflowWireSteps = {
4
+ getRun: () => Promise<WorkflowRun | null | undefined>;
5
+ verifyStepName: (stepName: string) => void;
6
+ rpcStep: (stepName: string, rpcName: string, data: any, options?: any) => Promise<any>;
7
+ inlineStep: (stepName: string, fn: any, options?: any) => Promise<any>;
8
+ sleepStep: (stepName: string, ms: number) => Promise<void>;
9
+ suspendStep: (reason: string) => Promise<void>;
10
+ approvalStep: (reason: string, options: WorkflowApprovalOptions) => Promise<any>;
11
+ };
12
+ export declare const buildWorkflowWire: (name: string, runId: string, addonNamespace: string | null | undefined, steps: WorkflowWireSteps) => PikkuWorkflowWire;
@@ -0,0 +1,33 @@
1
+ import { getDurationInMilliseconds } from '../../time-utils.js';
2
+ import { milestoneStepName } from './workflow-constants.js';
3
+ export const buildWorkflowWire = (name, runId, addonNamespace, steps) => ({
4
+ name,
5
+ runId,
6
+ getRun: async () => (await steps.getRun()),
7
+ do: async (stepName, rpcNameOrFn, dataOrOptions, options) => {
8
+ steps.verifyStepName(stepName);
9
+ if (typeof rpcNameOrFn === 'string') {
10
+ const resolvedRpcName = addonNamespace && !rpcNameOrFn.includes(':')
11
+ ? `${addonNamespace}:${rpcNameOrFn}`
12
+ : rpcNameOrFn;
13
+ return await steps.rpcStep(stepName, resolvedRpcName, dataOrOptions, options);
14
+ }
15
+ return await steps.inlineStep(stepName, rpcNameOrFn, dataOrOptions);
16
+ },
17
+ sleep: async (stepName, duration) => {
18
+ steps.verifyStepName(stepName);
19
+ await steps.sleepStep(stepName, getDurationInMilliseconds(duration));
20
+ },
21
+ suspend: async (reason) => {
22
+ steps.verifyStepName(reason);
23
+ await steps.suspendStep(reason);
24
+ },
25
+ milestone: async (name) => {
26
+ steps.verifyStepName(name);
27
+ await steps.inlineStep(milestoneStepName(name), async () => ({ name }));
28
+ },
29
+ approval: (async (reason, options) => {
30
+ steps.verifyStepName(reason);
31
+ return await steps.approvalStep(reason, options);
32
+ }),
33
+ });
@@ -16,6 +16,8 @@ export interface WorkflowRunWire {
16
16
  parentRunId?: string;
17
17
  parentStepId?: string;
18
18
  pikkuUserId?: string;
19
+ /** The W3C trace every hop of the run stays on. Set when the run is created. */
20
+ traceparent?: string;
19
21
  }
20
22
  /**
21
23
  * The resolved `workflow` section of the app config: default retries and the queue names the engine uses.
@@ -26,6 +28,20 @@ export interface WorkflowServiceConfig {
26
28
  orchestratorQueueName: string;
27
29
  stepWorkerQueueName: string;
28
30
  sleeperRPCName: string;
31
+ /**
32
+ * How `pikku dev` and `pikku serve` sweep for work a dead process left
33
+ * behind. They do by default; `false` turns it off.
34
+ */
35
+ recovery?: WorkflowRecoveryOptions | false;
36
+ }
37
+ /** Tuning for `startRecovery`. */
38
+ export interface WorkflowRecoveryOptions {
39
+ /** How often to sweep after the startup pass. Default 30s. */
40
+ intervalMs?: number;
41
+ /** Idle time before a `running` run with nothing in flight is re-driven. Default 30s. */
42
+ stalledAfterMs?: number;
43
+ /** Age at which a dispatched step's job is presumed lost. Default 30s. */
44
+ undispatchedAfterMs?: number;
29
45
  }
30
46
  /**
31
47
  * How a workflow service routes its jobs. `per-workflow` (the default) uses a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/core",
3
- "version": "0.12.148",
3
+ "version": "0.12.151",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/pikkujs/pikku.git",
@@ -76,6 +76,7 @@
76
76
  "./oauth2": "./dist/wirings/oauth2/index.js",
77
77
  "./errors": "./dist/errors/index.js",
78
78
  "./services": "./dist/services/index.js",
79
+ "./trace": "./dist/trace.js",
79
80
  "./flag": "./dist/wirings/flag/index.js",
80
81
  "./services/local-meta": "./dist/services/meta-service.js",
81
82
  "./services/in-memory-queue": "./dist/wirings/queue/in-memory-queue-service.js",