@pikku/core 0.12.80 → 0.12.83

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 (237) hide show
  1. package/CHANGELOG.md +345 -0
  2. package/dist/errors/index.d.ts +1 -1
  3. package/dist/errors/index.js +1 -1
  4. package/dist/function/function-runner.js +2 -5
  5. package/dist/function/index.d.ts +1 -1
  6. package/dist/index.d.ts +11 -11
  7. package/dist/index.js +3 -3
  8. package/dist/pikku-state.js +4 -0
  9. package/dist/services/ai-agent-runner-service.d.ts +7 -0
  10. package/dist/services/ai-run-state-service.d.ts +10 -0
  11. package/dist/services/in-memory-ai-run-state-service.d.ts +5 -1
  12. package/dist/services/in-memory-ai-run-state-service.js +9 -0
  13. package/dist/services/index.d.ts +15 -16
  14. package/dist/services/index.js +5 -5
  15. package/dist/services/meta-service.d.ts +2 -1
  16. package/dist/services/scoped-credential-service.d.ts +21 -0
  17. package/dist/services/scoped-credential-service.js +53 -0
  18. package/dist/testing/service-tests/ai-storage-service-tests.js +76 -0
  19. package/dist/types/core.types.d.ts +2 -3
  20. package/dist/types/state.types.d.ts +19 -1
  21. package/dist/wirings/actor-flow/index.d.ts +1 -1
  22. package/dist/wirings/ai-agent/ai-agent-finalize.d.ts +58 -0
  23. package/dist/wirings/ai-agent/ai-agent-finalize.js +138 -0
  24. package/dist/wirings/ai-agent/ai-agent-interrupt.js +1 -0
  25. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +2 -8
  26. package/dist/wirings/ai-agent/ai-agent-memory.js +34 -17
  27. package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +7 -0
  28. package/dist/wirings/ai-agent/ai-agent-model-config.js +44 -1
  29. package/dist/wirings/ai-agent/ai-agent-prepare.js +4 -0
  30. package/dist/wirings/ai-agent/ai-agent-runner.js +61 -40
  31. package/dist/wirings/ai-agent/ai-agent-stream.js +89 -36
  32. package/dist/wirings/ai-agent/ai-agent-turn.d.ts +1 -0
  33. package/dist/wirings/ai-agent/ai-agent-turn.js +1 -0
  34. package/dist/wirings/ai-agent/ai-agent.types.d.ts +46 -1
  35. package/dist/wirings/ai-agent/index.d.ts +8 -7
  36. package/dist/wirings/ai-agent/index.js +5 -4
  37. package/dist/wirings/ai-scorer/ai-scorer-grade.d.ts +26 -0
  38. package/dist/wirings/ai-scorer/ai-scorer-grade.js +33 -0
  39. package/dist/wirings/ai-scorer/ai-scorer-judge.d.ts +17 -0
  40. package/dist/wirings/ai-scorer/ai-scorer-judge.js +92 -0
  41. package/dist/wirings/ai-scorer/ai-scorer-live.d.ts +15 -0
  42. package/dist/wirings/ai-scorer/ai-scorer-live.js +38 -0
  43. package/dist/wirings/ai-scorer/ai-scorer-registry.d.ts +18 -0
  44. package/dist/wirings/ai-scorer/ai-scorer-registry.js +46 -0
  45. package/dist/wirings/ai-scorer/ai-scorer-sampling.d.ts +8 -0
  46. package/dist/wirings/ai-scorer/ai-scorer-sampling.js +31 -0
  47. package/dist/wirings/ai-scorer/ai-scorer-snapshots.d.ts +10 -0
  48. package/dist/wirings/ai-scorer/ai-scorer-snapshots.js +40 -0
  49. package/dist/wirings/ai-scorer/ai-scorer-worker.d.ts +15 -0
  50. package/dist/wirings/ai-scorer/ai-scorer-worker.js +58 -0
  51. package/dist/wirings/ai-scorer/ai-scorer.d.ts +39 -0
  52. package/dist/wirings/ai-scorer/ai-scorer.js +40 -0
  53. package/dist/wirings/ai-scorer/ai-scorer.types.d.ts +90 -0
  54. package/dist/wirings/ai-scorer/ai-scorer.types.js +4 -0
  55. package/dist/wirings/ai-scorer/index.d.ts +6 -0
  56. package/dist/wirings/ai-scorer/index.js +5 -0
  57. package/dist/wirings/channel/index.d.ts +5 -6
  58. package/dist/wirings/channel/index.js +3 -4
  59. package/dist/wirings/channel/local/local-channel-runner.js +8 -1
  60. package/dist/wirings/cli/channel/cli-raw-channel-runner.js +9 -1
  61. package/dist/wirings/cli/channel/index.d.ts +1 -2
  62. package/dist/wirings/cli/channel/index.js +0 -1
  63. package/dist/wirings/cli/cli-runner.js +13 -1
  64. package/dist/wirings/credential/index.d.ts +1 -1
  65. package/dist/wirings/gateway/index.d.ts +1 -1
  66. package/dist/wirings/http/http-runner.js +8 -2
  67. package/dist/wirings/http/index.d.ts +1 -2
  68. package/dist/wirings/mcp/index.d.ts +1 -1
  69. package/dist/wirings/mcp/mcp-runner.d.ts +15 -0
  70. package/dist/wirings/mcp/mcp-runner.js +18 -5
  71. package/dist/wirings/persona/index.d.ts +3 -4
  72. package/dist/wirings/persona/index.js +2 -3
  73. package/dist/wirings/queue/index.d.ts +1 -3
  74. package/dist/wirings/queue/index.js +1 -3
  75. package/dist/wirings/rpc/addon-runner.d.ts +8 -0
  76. package/dist/wirings/rpc/addon-runner.js +31 -3
  77. package/dist/wirings/rpc/rpc-runner.js +4 -0
  78. package/dist/wirings/rpc/rpc-types.d.ts +8 -0
  79. package/dist/wirings/rpc/wire-addon.d.ts +25 -0
  80. package/dist/wirings/rpc/wire-addon.js +8 -0
  81. package/dist/wirings/scheduler/index.d.ts +1 -1
  82. package/dist/wirings/trigger/index.d.ts +1 -1
  83. package/dist/wirings/virtual-user/index.d.ts +5 -6
  84. package/dist/wirings/virtual-user/index.js +2 -4
  85. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +85 -15
  86. package/dist/wirings/workflow/feature.d.ts +2 -1
  87. package/dist/wirings/workflow/index.d.ts +5 -16
  88. package/dist/wirings/workflow/index.js +1 -9
  89. package/dist/wirings/workflow/pikku-scenario-service.d.ts +17 -7
  90. package/dist/wirings/workflow/pikku-scenario-service.js +48 -13
  91. package/dist/wirings/workflow/pikku-workflow-service.js +17 -3
  92. package/dist/wirings/workflow/scenario-step.types.d.ts +8 -0
  93. package/dist/wirings/workflow/scenario.types.d.ts +37 -0
  94. package/dist/wirings/workflow/workflow-approval-audit.d.ts +16 -0
  95. package/dist/wirings/workflow/workflow-approval-audit.js +40 -0
  96. package/dist/wirings/workflow/workflow-approval-policy.d.ts +20 -0
  97. package/dist/wirings/workflow/workflow-approval-policy.js +48 -0
  98. package/dist/wirings/workflow/workflow-approval.d.ts +29 -1
  99. package/dist/wirings/workflow/workflow-approval.js +65 -2
  100. package/dist/wirings/workflow/workflow-run-ownership.d.ts +2 -1
  101. package/dist/wirings/workflow/workflow-run-ownership.js +2 -1
  102. package/dist/wirings/workflow/workflow.types.d.ts +2 -37
  103. package/knowledge/decisions/internals/addon-pikku-meta-ships-at-the-package-root-or-under-dist.md +32 -0
  104. package/knowledge/decisions/internals/an-addon-scope-root-loses-to-a-root-the-host-already-declares.md +39 -0
  105. package/knowledge/decisions/internals/index.md +30 -3
  106. package/knowledge/decisions/internals/validate-runs-checks-by-precondition.md +115 -0
  107. package/knowledge/decisions/security/a-function-never-receives-the-secret-service.md +37 -0
  108. package/knowledge/decisions/security/a-workflow-run-is-read-and-approved-by-its-owner.md +30 -14
  109. package/knowledge/decisions/security/an-approval-answer-outlives-the-run-it-answered.md +59 -0
  110. package/knowledge/decisions/security/index.md +3 -1
  111. package/knowledge/questions/index.md +1 -1
  112. package/package.json +3 -2
  113. package/scripts/generate-api-report.mts +143 -18
  114. package/src/api-report.test.ts +2 -2
  115. package/src/errors/index.ts +1 -1
  116. package/src/function/function-runner.test.ts +52 -0
  117. package/src/function/function-runner.ts +5 -9
  118. package/src/function/index.ts +0 -2
  119. package/src/index.ts +0 -35
  120. package/src/pikku-state.ts +5 -0
  121. package/src/public-surface.json +81 -118
  122. package/src/services/ai-agent-runner-service.ts +12 -1
  123. package/src/services/ai-run-state-service.ts +11 -0
  124. package/src/services/in-memory-ai-run-state-service.ts +13 -0
  125. package/src/services/index.ts +7 -58
  126. package/src/services/meta-service.ts +2 -4
  127. package/src/services/scoped-credential-service.test.ts +86 -0
  128. package/src/services/scoped-credential-service.ts +63 -0
  129. package/src/testing/service-tests/ai-storage-service-tests.ts +93 -0
  130. package/src/types/core.types.ts +4 -7
  131. package/src/types/state.types.ts +21 -1
  132. package/src/wirings/actor-flow/index.ts +0 -3
  133. package/src/wirings/ai-agent/ai-agent-finalize.test.ts +186 -0
  134. package/src/wirings/ai-agent/ai-agent-finalize.ts +197 -0
  135. package/src/wirings/ai-agent/ai-agent-interrupt.ts +1 -0
  136. package/src/wirings/ai-agent/ai-agent-memory.ts +54 -38
  137. package/src/wirings/ai-agent/ai-agent-model-config.test.ts +72 -3
  138. package/src/wirings/ai-agent/ai-agent-model-config.ts +49 -1
  139. package/src/wirings/ai-agent/ai-agent-prepare.ts +4 -0
  140. package/src/wirings/ai-agent/ai-agent-runner.ts +71 -40
  141. package/src/wirings/ai-agent/ai-agent-stream-output-hooks.test.ts +353 -0
  142. package/src/wirings/ai-agent/ai-agent-stream.ts +116 -54
  143. package/src/wirings/ai-agent/ai-agent-turn.test.ts +67 -0
  144. package/src/wirings/ai-agent/ai-agent-turn.ts +1 -0
  145. package/src/wirings/ai-agent/ai-agent.types.ts +64 -4
  146. package/src/wirings/ai-agent/index.ts +2 -16
  147. package/src/wirings/ai-scorer/ai-scorer-grade.test.ts +106 -0
  148. package/src/wirings/ai-scorer/ai-scorer-grade.ts +55 -0
  149. package/src/wirings/ai-scorer/ai-scorer-judge.test.ts +143 -0
  150. package/src/wirings/ai-scorer/ai-scorer-judge.ts +120 -0
  151. package/src/wirings/ai-scorer/ai-scorer-live.test.ts +174 -0
  152. package/src/wirings/ai-scorer/ai-scorer-live.ts +56 -0
  153. package/src/wirings/ai-scorer/ai-scorer-registry.ts +63 -0
  154. package/src/wirings/ai-scorer/ai-scorer-sampling.test.ts +34 -0
  155. package/src/wirings/ai-scorer/ai-scorer-sampling.ts +36 -0
  156. package/src/wirings/ai-scorer/ai-scorer-snapshots.test.ts +49 -0
  157. package/src/wirings/ai-scorer/ai-scorer-snapshots.ts +46 -0
  158. package/src/wirings/ai-scorer/ai-scorer-worker.test.ts +122 -0
  159. package/src/wirings/ai-scorer/ai-scorer-worker.ts +69 -0
  160. package/src/wirings/ai-scorer/ai-scorer.ts +76 -0
  161. package/src/wirings/ai-scorer/ai-scorer.types.ts +107 -0
  162. package/src/wirings/ai-scorer/index.ts +24 -0
  163. package/src/wirings/channel/index.ts +1 -20
  164. package/src/wirings/channel/local/local-channel-runner.test.ts +68 -0
  165. package/src/wirings/channel/local/local-channel-runner.ts +8 -1
  166. package/src/wirings/cli/channel/cli-raw-channel-runner.test.ts +23 -0
  167. package/src/wirings/cli/channel/cli-raw-channel-runner.ts +12 -1
  168. package/src/wirings/cli/channel/index.ts +0 -7
  169. package/src/wirings/cli/cli-runner.test.ts +68 -0
  170. package/src/wirings/cli/cli-runner.ts +18 -1
  171. package/src/wirings/credential/index.ts +0 -1
  172. package/src/wirings/gateway/index.ts +0 -3
  173. package/src/wirings/http/http-runner.test.ts +66 -0
  174. package/src/wirings/http/http-runner.ts +10 -2
  175. package/src/wirings/http/index.ts +1 -1
  176. package/src/wirings/mcp/index.ts +0 -1
  177. package/src/wirings/mcp/mcp-runner.test.ts +181 -0
  178. package/src/wirings/mcp/mcp-runner.ts +35 -5
  179. package/src/wirings/persona/index.ts +0 -8
  180. package/src/wirings/queue/index.ts +0 -14
  181. package/src/wirings/rpc/addon-runner.ts +62 -3
  182. package/src/wirings/rpc/addon-secrets.test.ts +391 -0
  183. package/src/wirings/rpc/rpc-runner.test.ts +2 -0
  184. package/src/wirings/rpc/rpc-runner.ts +4 -0
  185. package/src/wirings/rpc/rpc-types.ts +8 -0
  186. package/src/wirings/rpc/wire-addon.ts +33 -0
  187. package/src/wirings/scheduler/index.ts +0 -1
  188. package/src/wirings/trigger/index.ts +0 -1
  189. package/src/wirings/virtual-user/index.ts +0 -16
  190. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +96 -16
  191. package/src/wirings/workflow/feature.ts +2 -5
  192. package/src/wirings/workflow/graph/graph-runner.test.ts +72 -0
  193. package/src/wirings/workflow/index.ts +2 -68
  194. package/src/wirings/workflow/pikku-scenario-service.ts +81 -16
  195. package/src/wirings/workflow/pikku-workflow-service.test.ts +13 -12
  196. package/src/wirings/workflow/pikku-workflow-service.ts +28 -4
  197. package/src/wirings/workflow/scenario-expectations.test.ts +75 -0
  198. package/src/wirings/workflow/scenario-hooks.test.ts +3 -2
  199. package/src/wirings/workflow/scenario-step.types.ts +8 -0
  200. package/src/wirings/workflow/scenario.types.ts +63 -0
  201. package/src/wirings/workflow/workflow-approval-audit.ts +47 -0
  202. package/src/wirings/workflow/workflow-approval-policy.test.ts +524 -0
  203. package/src/wirings/workflow/workflow-approval-policy.ts +68 -0
  204. package/src/wirings/workflow/workflow-approval.ts +113 -9
  205. package/src/wirings/workflow/workflow-run-authority.test.ts +12 -15
  206. package/src/wirings/workflow/workflow-run-ownership.ts +2 -1
  207. package/src/wirings/workflow/workflow.types.ts +1 -63
  208. package/src/wirings-stay-decoupled.test.ts +6 -2
  209. package/tsconfig.tsbuildinfo +1 -1
  210. package/dist/internal.d.ts +0 -3
  211. package/dist/internal.js +0 -2
  212. package/dist/middleware/timeout.d.ts +0 -9
  213. package/dist/middleware/timeout.js +0 -15
  214. package/dist/pikku-response.d.ts +0 -6
  215. package/dist/pikku-response.js +0 -6
  216. package/dist/services/gopass-secrets.d.ts +0 -15
  217. package/dist/services/gopass-secrets.js +0 -76
  218. package/dist/services/http-scenario-actors.d.ts +0 -75
  219. package/dist/services/http-scenario-actors.js +0 -195
  220. package/dist/services/http-user-flow-actors.d.ts +0 -67
  221. package/dist/services/http-user-flow-actors.js +0 -193
  222. package/dist/services/scenario-actors-service.d.ts +0 -127
  223. package/dist/services/scenario-actors-service.js +0 -40
  224. package/dist/services/user-flow-actors-service.d.ts +0 -39
  225. package/dist/wirings/credential/wire-credential.d.ts +0 -48
  226. package/dist/wirings/credential/wire-credential.js +0 -47
  227. package/dist/wirings/oauth2/oauth2-client.d.ts +0 -47
  228. package/dist/wirings/oauth2/oauth2-client.js +0 -263
  229. package/dist/wirings/oauth2/oauth2-routes.d.ts +0 -35
  230. package/dist/wirings/oauth2/oauth2-routes.js +0 -146
  231. package/dist/wirings/scope/wire-scope.d.ts +0 -33
  232. package/dist/wirings/scope/wire-scope.js +0 -32
  233. package/dist/wirings/workflow/dsl/index.d.ts +0 -5
  234. package/dist/wirings/workflow/dsl/index.js +0 -4
  235. package/dist/wirings/workflow/graph/index.d.ts +0 -5
  236. package/dist/wirings/workflow/graph/index.js +0 -4
  237. /package/dist/{services/user-flow-actors-service.js → wirings/workflow/scenario.types.js} +0 -0
@@ -146,3 +146,78 @@ describe('workflow.expectService', () => {
146
146
  )
147
147
  })
148
148
  })
149
+
150
+ describe('workflow.expectScore', () => {
151
+ beforeEach(() => resetPikkuState())
152
+
153
+ const grades = (grade: { score: number; reason?: string }) => ({
154
+ rpcWithWire: async (rpcName: string, data: any) => {
155
+ assert.equal(rpcName, 'pikkuScenarioGradeRun')
156
+ assert.equal(data.runId, 'run-1')
157
+ return grade
158
+ },
159
+ })
160
+
161
+ test('returns the grade when the score clears the bound', async () => {
162
+ const { wire } = await scenarioWire(grades({ score: 0.9 }))
163
+
164
+ const grade = await wire.expectScore('was brief', 'run-1', 'brevity', {
165
+ atLeast: 0.8,
166
+ })
167
+
168
+ assert.equal(grade.score, 0.9)
169
+ })
170
+
171
+ test('a scorer that graded zero fails without a bound being stated', async () => {
172
+ const { wire } = await scenarioWire(grades({ score: 0 }))
173
+
174
+ await assert.rejects(
175
+ wire.expectScore('was brief', 'run-1', 'brevity', { retries: 0 }),
176
+ /expected 'brevity' to grade run run-1 at least 0\.5, got 0/
177
+ )
178
+ })
179
+
180
+ test('the failure carries the reason the judge gave', async () => {
181
+ const { wire } = await scenarioWire(
182
+ grades({ score: 0.2, reason: 'It rambled for four paragraphs' })
183
+ )
184
+
185
+ await assert.rejects(
186
+ wire.expectScore('was brief', 'run-1', 'brevity', {
187
+ atLeast: 0.8,
188
+ retries: 0,
189
+ }),
190
+ /It rambled for four paragraphs/
191
+ )
192
+ })
193
+
194
+ test('atMost fails a score that is too high', async () => {
195
+ const { wire } = await scenarioWire(grades({ score: 0.95 }))
196
+
197
+ await assert.rejects(
198
+ wire.expectScore('was not sycophantic', 'run-1', 'sycophancy', {
199
+ atLeast: 0,
200
+ atMost: 0.3,
201
+ retries: 0,
202
+ }),
203
+ /between 0 and 0\.3, got 0\.95/
204
+ )
205
+ })
206
+
207
+ test('a reference answer reaches the grader', async () => {
208
+ let seen: any
209
+ const { wire } = await scenarioWire({
210
+ rpcWithWire: async (_rpcName: string, data: any) => {
211
+ seen = data
212
+ return { score: 1 }
213
+ },
214
+ })
215
+
216
+ await wire.expectScore('matched the answer key', 'run-1', 'correctness', {
217
+ reference: 'Paris',
218
+ })
219
+
220
+ assert.equal(seen.reference, 'Paris')
221
+ assert.equal(seen.scorer, 'correctness')
222
+ })
223
+ })
@@ -185,8 +185,9 @@ describe('scenario before/after hooks', () => {
185
185
  })
186
186
 
187
187
  test('after reads what the body wrote to the scenario context', async () => {
188
- // The reason the context exists: teardown needs the ids the body minted,
189
- // and a hook only ever receives the run's *input*.
188
+ // The reason the context exists: a hook's `data` is only ever the run's
189
+ // *input*, never what the body produced. The context is the channel the
190
+ // body writes that through, and it reaches the hook on the shared wire.
190
191
  let seen: any
191
192
  await runScenario('contextHandover', {
192
193
  after: async (_services, _data, wire) => {
@@ -229,6 +229,14 @@ export interface ScenarioBrowserProvider {
229
229
  * scenario cannot leave the next signed in as somebody else.
230
230
  */
231
231
  reset?(): Promise<void>
232
+ /**
233
+ * Name the scenario about to run, so anything it captures is filed under it.
234
+ *
235
+ * Called before each scenario. A provider that is never told has to fall
236
+ * back to one shared label, which puts every run's artifacts in a single
237
+ * folder — findable only by timestamp.
238
+ */
239
+ beginScenario?(scenario: string): void
232
240
  /**
233
241
  * Snapshot every open window for a failed scenario. `label` identifies the
234
242
  * scenario in artifact filenames. Never throws: a failure to capture must
@@ -0,0 +1,63 @@
1
+ import type {
2
+ CorePikkuFunctionConfig,
3
+ CorePikkuFunctionHook,
4
+ } from '../../function/functions.types.js'
5
+
6
+ export type {
7
+ ScenarioStepInvocation,
8
+ ScenarioStepMeta,
9
+ PikkuScenarioWire,
10
+ } from './dsl/workflow-dsl.types.js'
11
+
12
+ export type {
13
+ ScenarioStepPhase,
14
+ ScenarioStepKind,
15
+ ScenarioStepOptions,
16
+ PikkuScenarioStepWire,
17
+ ScenarioEnvironment,
18
+ ScenarioSurface,
19
+ ScenarioSurfaceResolution,
20
+ PikkuBrowserWire,
21
+ TestIdSelector,
22
+ ScenarioBrowserProvider,
23
+ ScenarioBrowserFailure,
24
+ } from './scenario-step.types.js'
25
+
26
+ export type CoreFeatureScenario =
27
+ | CorePikkuFunctionConfig<any, any, any>
28
+ | { scenario: CorePikkuFunctionConfig<any, any, any>; data: unknown }
29
+
30
+ export type CoreFeature = {
31
+ name: string
32
+ description?: string
33
+ tags?: string[]
34
+ scenarios: readonly CoreFeatureScenario[]
35
+ before?: CorePikkuFunctionHook
36
+ after?: CorePikkuFunctionHook
37
+ }
38
+
39
+ export type FeatureMetaEntry = {
40
+ scenario: string
41
+ data?: unknown
42
+ }
43
+
44
+ export type FeatureMeta = {
45
+ id: string
46
+ name: string
47
+ description?: string
48
+ tags: string[]
49
+ entries: FeatureMetaEntry[]
50
+ unresolvedEntries: number
51
+ hasBefore: boolean
52
+ hasAfter: boolean
53
+ }
54
+
55
+ export type FeaturesMeta = Record<string, FeatureMeta>
56
+
57
+ export type FeaturePlanEntry = {
58
+ featureId: string
59
+ featureName: string
60
+ scenarioName: string
61
+ data?: unknown
62
+ tags: string[]
63
+ }
@@ -0,0 +1,47 @@
1
+ import { getSingletonServices } from '../../pikku-state.js'
2
+ import type { ApprovalAuditEvent } from './workflow-approval.js'
3
+
4
+ /** The audit type an answer to an approval gate is recorded under. */
5
+ export const APPROVAL_AUDIT_TYPE = 'workflow.approval.decided'
6
+
7
+ /**
8
+ * Record an answer to an approval gate where it outlives the run.
9
+ *
10
+ * The step result carries the settled decision, but it is deleted with the run
11
+ * — `deleteRun` cascades to steps and to history — and a refused attempt never
12
+ * reaches a step at all. An approval is asked for precisely so it can be
13
+ * answered for afterwards, so the answer also goes to the audit sink, which
14
+ * holds no foreign key to the run.
15
+ *
16
+ * A project with no sink wired records nothing, and a sink that fails is logged
17
+ * rather than thrown: the trail must not be the reason a decision is lost.
18
+ */
19
+ export const auditApprovalDecision = async (
20
+ event: ApprovalAuditEvent
21
+ ): Promise<void> => {
22
+ const services = getSingletonServices()
23
+ if (!services?.audit) {
24
+ return
25
+ }
26
+ try {
27
+ await services.audit.audit({
28
+ type: APPROVAL_AUDIT_TYPE,
29
+ source: 'explicit',
30
+ outcome: event.outcome,
31
+ occurredAt: new Date().toISOString(),
32
+ wireType: 'workflow',
33
+ userIdentity: { pikkuUserId: event.decidedBy?.userId },
34
+ metadata: {
35
+ runId: event.runId,
36
+ reason: event.reason,
37
+ scopes: event.decidedBy?.scopes,
38
+ refusal: event.refusal,
39
+ },
40
+ })
41
+ } catch (error) {
42
+ services.logger?.warn(
43
+ `Failed to audit the decision on approval '${event.reason}' for run ${event.runId}`,
44
+ error
45
+ )
46
+ }
47
+ }