@pikku/inspector 0.12.56 → 0.12.59

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 (45) hide show
  1. package/CHANGELOG.md +185 -0
  2. package/dist/add/add-ai-agent.js +25 -0
  3. package/dist/add/add-ai-scorer.d.ts +2 -0
  4. package/dist/add/add-ai-scorer.js +61 -0
  5. package/dist/add/add-wire-addon.js +26 -0
  6. package/dist/add/scenario-instrumentation.d.ts +6 -1
  7. package/dist/add/scenario-instrumentation.js +6 -0
  8. package/dist/error-codes.d.ts +2 -0
  9. package/dist/error-codes.js +3 -0
  10. package/dist/inspector.js +7 -3
  11. package/dist/types.d.ts +24 -0
  12. package/dist/utils/filter-inspector-state.js +8 -0
  13. package/dist/utils/post-process.d.ts +13 -3
  14. package/dist/utils/post-process.js +51 -21
  15. package/dist/utils/serialize-inspector-state.d.ts +11 -0
  16. package/dist/utils/serialize-inspector-state.js +8 -0
  17. package/dist/utils/workflow/dsl/extract-dsl-workflow.js +13 -4
  18. package/dist/utils/workflow/dsl/patterns.d.ts +2 -1
  19. package/dist/utils/workflow/dsl/patterns.js +8 -5
  20. package/dist/utils/workflow/graph/workflow-graph.types.d.ts +2 -1
  21. package/dist/visit.js +2 -0
  22. package/package.json +3 -3
  23. package/src/add/add-ai-agent.ts +35 -0
  24. package/src/add/add-ai-scorer.test.ts +197 -0
  25. package/src/add/add-ai-scorer.ts +83 -0
  26. package/src/add/add-functions.ts +1 -1
  27. package/src/add/add-scenario-step.test.ts +21 -1
  28. package/src/add/add-wire-addon.test.ts +74 -0
  29. package/src/add/add-wire-addon.ts +22 -0
  30. package/src/add/scenario-instrumentation.ts +6 -0
  31. package/src/error-codes.ts +4 -0
  32. package/src/inspector.ts +7 -3
  33. package/src/types.ts +21 -0
  34. package/src/utils/filter-inspector-state.ts +8 -0
  35. package/src/utils/post-process.test.ts +138 -0
  36. package/src/utils/post-process.ts +69 -22
  37. package/src/utils/serialize-inspector-state.ts +16 -0
  38. package/src/utils/workflow/dsl/extract-approval-policy.test.ts +68 -0
  39. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +13 -5
  40. package/src/utils/workflow/dsl/patterns.ts +8 -5
  41. package/src/utils/workflow/graph/workflow-graph.types.ts +1 -1
  42. package/src/visit.ts +2 -0
  43. package/tsconfig.tsbuildinfo +1 -1
  44. package/src/utils/secret-brokers.test.ts +0 -53
  45. package/src/utils/secret-brokers.ts +0 -11
@@ -0,0 +1,68 @@
1
+ import { describe, test } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import * as ts from 'typescript'
4
+ import { extractDSLWorkflow } from './extract-dsl-workflow.js'
5
+ import type { ApprovalStepMeta } from '@pikku/core/workflow'
6
+
7
+ const checker = {
8
+ getSymbolAtLocation: () => undefined,
9
+ getAliasedSymbol: () => undefined,
10
+ getShorthandAssignmentValueSymbol: () => undefined,
11
+ getTypeAtLocation: () => undefined,
12
+ } as unknown as ts.TypeChecker
13
+
14
+ /** Extracts the single approval step from a workflow body. */
15
+ const approvalStepFor = (options: string): ApprovalStepMeta => {
16
+ const file = ts.createSourceFile(
17
+ 'wf.ts',
18
+ `const wf = pikkuWorkflowFunc({
19
+ func: async (services, data, { workflow }) => {
20
+ await workflow.approval('Release funds', ${options})
21
+ },
22
+ })`,
23
+ ts.ScriptTarget.Latest,
24
+ true
25
+ )
26
+
27
+ let node: ts.Node | undefined
28
+ const visit = (n: ts.Node) => {
29
+ if (ts.isCallExpression(n) && n.expression.getText() === 'pikkuWorkflowFunc') {
30
+ node = n
31
+ }
32
+ ts.forEachChild(n, visit)
33
+ }
34
+ visit(file)
35
+ assert.ok(node, 'failed to find the workflow definition')
36
+
37
+ const result = extractDSLWorkflow(node, checker)
38
+ assert.equal(result.status, 'ok', JSON.stringify(result))
39
+ const step = result.steps?.find(
40
+ (s): s is ApprovalStepMeta => s.type === 'approval'
41
+ )
42
+ assert.ok(step, 'no approval step was extracted')
43
+ return step
44
+ }
45
+
46
+ describe('an approval gate carries its policy into the graph', () => {
47
+ test('approvers and approverScope survive extraction', () => {
48
+ const step = approvalStepFor(
49
+ `{ schema: DecisionSchema, approvers: 'not-initiator', approverScope: 'payments:approve' }`
50
+ )
51
+ assert.equal(step.approvers, 'not-initiator')
52
+ assert.equal(step.approverScope, 'payments:approve')
53
+ })
54
+
55
+ test('expiry still survives alongside them', () => {
56
+ const step = approvalStepFor(
57
+ `{ schema: DecisionSchema, expiry: '3d', approvers: 'owner' }`
58
+ )
59
+ assert.equal(step.expiry, '3d')
60
+ assert.equal(step.approvers, 'owner')
61
+ })
62
+
63
+ test('a gate declaring no policy records none', () => {
64
+ const step = approvalStepFor(`{ schema: DecisionSchema }`)
65
+ assert.equal(step.approvers, undefined)
66
+ assert.equal(step.approverScope, undefined)
67
+ })
68
+ })
@@ -15,13 +15,13 @@ import type {
15
15
  SwitchCaseMeta,
16
16
  FilterStepMeta,
17
17
  ArrayPredicateStepMeta,
18
- ScenarioStepMeta,
19
18
  InputSource,
20
19
  OutputBinding,
21
20
  Condition,
22
21
  WorkflowContext,
23
22
  ContextVariable,
24
23
  } from '@pikku/core/workflow'
24
+ import type { ScenarioStepMeta } from '@pikku/core/scenario'
25
25
  import {
26
26
  isWorkflowDoCall,
27
27
  isWorkflowExpectEventuallyCall,
@@ -1051,14 +1051,15 @@ function extractApprovalStep(
1051
1051
  step.outputVar = outputVar
1052
1052
  }
1053
1053
  // The `schema` option is a runtime value validated inside the workflow body,
1054
- // so it is deliberately not serialized here — only `expiry`, which the graph
1055
- // and planned-step ladder need in order to describe the gate.
1054
+ // so it is deliberately not serialized here — only the literal options the
1055
+ // graph and planned-step ladder need in order to describe the gate.
1056
1056
  const options = args[1]
1057
1057
  if (options && ts.isObjectLiteralExpression(options)) {
1058
1058
  for (const prop of options.properties) {
1059
+ if (!ts.isPropertyAssignment(prop)) continue
1060
+ const name = prop.name.getText()
1059
1061
  if (
1060
- ts.isPropertyAssignment(prop) &&
1061
- prop.name.getText() === 'expiry' &&
1062
+ name === 'expiry' &&
1062
1063
  (ts.isStringLiteral(prop.initializer) ||
1063
1064
  ts.isNumericLiteral(prop.initializer))
1064
1065
  ) {
@@ -1066,6 +1067,13 @@ function extractApprovalStep(
1066
1067
  ? Number(prop.initializer.text)
1067
1068
  : prop.initializer.text
1068
1069
  }
1070
+ if (name === 'approvers' && ts.isStringLiteral(prop.initializer)) {
1071
+ step.approvers = prop.initializer
1072
+ .text as ApprovalStepMeta['approvers']
1073
+ }
1074
+ if (name === 'approverScope' && ts.isStringLiteral(prop.initializer)) {
1075
+ step.approverScope = prop.initializer.text
1076
+ }
1069
1077
  }
1070
1078
  }
1071
1079
  return step
@@ -77,20 +77,23 @@ export function getScenarioStepPhase(
77
77
  /**
78
78
  * The expectation helpers that assert without being a `then`.
79
79
  *
80
- * `expectService` and `expectError` are inline steps rather than scenario
81
- * steps, so they carry no phase and never reach the step meta — but each one is
82
- * a witness: a recorded service call, or a refusal. PKU680 has to see them, or
83
- * a scenario whose only assertion is one of these reads as asserting nothing.
80
+ * `expectService`, `expectError` and `expectScore` are inline steps rather than
81
+ * scenario steps, so they carry no phase and never reach the step meta — but
82
+ * each one is a witness: a recorded service call, a refusal, or a graded run.
83
+ * PKU680 has to see them, or a scenario whose only assertion is one of these
84
+ * reads as asserting nothing.
84
85
  */
85
86
  const SCENARIO_EXPECTATION_CALLS = [
86
87
  'expectService',
87
88
  'expectError',
88
89
  'expectEventually',
90
+ 'expectScore',
89
91
  ] as const
90
92
 
91
93
  /**
92
94
  * Check if a call expression is one of the scenario expectation helpers —
93
- * `scenario.expectService()`, `.expectError()` or `.expectEventually()`.
95
+ * `scenario.expectService()`, `.expectError()`, `.expectEventually()` or
96
+ * `.expectScore()`.
94
97
  */
95
98
  export function isScenarioExpectationCall(node: ts.CallExpression): boolean {
96
99
  return (
@@ -113,10 +113,10 @@ export type FlowType =
113
113
  // Import and re-export context types from core
114
114
  import type {
115
115
  ContextVariable,
116
- ScenarioStepPhase,
117
116
  WorkflowContext,
118
117
  WorkflowPlannedStep,
119
118
  } from '@pikku/core/workflow'
119
+ import type { ScenarioStepPhase } from '@pikku/core/scenario'
120
120
 
121
121
  export type { ContextVariable, WorkflowContext }
122
122
 
package/src/visit.ts CHANGED
@@ -34,6 +34,7 @@ import { addVariable } from './add/add-variable.js'
34
34
  import { addWorkflowGraph } from './add/add-workflow-graph.js'
35
35
  import { addFeature } from './add/add-feature.js'
36
36
  import { addAIAgent } from './add/add-ai-agent.js'
37
+ import { addAIScorer } from './add/add-ai-scorer.js'
37
38
  import { addApprovalDescription } from './add/add-approval-description.js'
38
39
 
39
40
  export const visitSetup = (
@@ -172,6 +173,7 @@ export const visitRoutes = (
172
173
  addWorkflowGraph(logger, node, checker, state, nextOptions)
173
174
  addFeature(logger, node, checker, state, nextOptions)
174
175
  addAIAgent(logger, node, checker, state, nextOptions)
176
+ addAIScorer(logger, node, checker, state, nextOptions)
175
177
 
176
178
  ts.forEachChild(node, (child) =>
177
179
  visitRoutes(logger, checker, child, state, nextOptions)