@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
@@ -1,8 +1,9 @@
1
1
  export { LogLevel } from './logger.js';
2
2
  export { ScopedSecretService } from './scoped-secret-service.js';
3
- export { PikkuSessionService, createMiddlewareSessionWireProps, createFunctionSessionWireProps, } from './user-session-service.js';
3
+ export { ScopedCredentialService } from './scoped-credential-service.js';
4
+ export { PikkuSessionService, createMiddlewareSessionWireProps, } from './user-session-service.js';
4
5
  export { TypedSecretService } from './typed-secret-service.js';
5
- export { PikkuCredentialWireService, createMiddlewareCredentialWireProps, createWireServicesCredentialWireProps, } from './credential-wire-service.js';
6
+ export { PikkuCredentialWireService } from './credential-wire-service.js';
6
7
  export { TypedVariablesService } from './typed-variables-service.js';
7
8
  export { LocalSecretService } from './local-secrets.js';
8
9
  export { LocalEmailService } from './local-email-service.js';
@@ -16,11 +17,10 @@ export { InMemoryTriggerService } from './in-memory-trigger-service.js';
16
17
  export { InMemoryAIRunStateService } from './in-memory-ai-run-state-service.js';
17
18
  export { LocalGatewayService } from './local-gateway-service.js';
18
19
  export type { ContentService, SignContentKeyArgs, SignURLArgs, GetUploadURLArgs, UploadURLResult, BucketKeyArgs, WriteFileArgs, CopyFileArgs, } from './content-service.js';
19
- export type { ScenarioPersona, ResolvedPersona, ScenarioPersonaOf, ScenarioPersonas, ScenarioInvokeOptions, ScenarioRpcMap, ScenarioHttpResponse, } from './personas-service.js';
20
- export type { HttpPersonasConfig } from './http-personas.js';
20
+ export type { ScenarioPersona, ResolvedPersona, ScenarioPersonas, } from './personas-service.js';
21
21
  export type { JWTService } from './jwt-service.js';
22
- export type { EmailService, EmailTemplateReference, SendEmailInput, SendEmailResult, SendHTMLEmailInput, SendTemplateEmailInput, SendTextEmailInput, } from './email-service.js';
23
- export { DEFAULT_WEBHOOK_RETRIES, DEFAULT_WEBHOOK_SIGNATURE_HEADER, PIKKU_OUTGOING_WEBHOOK_QUEUE_NAME, WebhookService, type SendWebhookInput, type SendWebhookResult, type WebhookAttemptRecord, type WebhookAttemptResult, type WebhookDeliveryRecord, type WebhookDeliveryWithAttempts, type WebhookJobData, type WebhookServiceConfig, } from './webhook-service.js';
22
+ export type { EmailService, SendEmailInput, SendEmailResult, SendHTMLEmailInput, SendTemplateEmailInput, SendTextEmailInput, } from './email-service.js';
23
+ export { DEFAULT_WEBHOOK_RETRIES, PIKKU_OUTGOING_WEBHOOK_QUEUE_NAME, WebhookService, type SendWebhookInput, type SendWebhookResult, type WebhookAttemptResult, type WebhookDeliveryRecord, type WebhookDeliveryWithAttempts, type WebhookJobData, type WebhookServiceConfig, } from './webhook-service.js';
24
24
  export type { Logger } from './logger.js';
25
25
  export type { SecretService, SecretValues } from './secret-service.js';
26
26
  export type { VariablesService } from './variables-service.js';
@@ -31,23 +31,22 @@ export type { TriggerService } from './trigger-service.js';
31
31
  export type { GatewayService } from './gateway-service.js';
32
32
  export type { DeploymentService, DeploymentConfig, DeploymentInfo, DeploymentServiceConfig, } from './deployment-service.js';
33
33
  export type { AIStorageService } from './ai-storage-service.js';
34
- export type { AIAgentRunnerParams, AIAgentRunnerResult, AIAgentStepResult, AIEmbedManyParams, AIEmbedManyResult, AIEmbedParams, AIEmbedResult, AIGenerateImageParams, AIGenerateImagePrompt, AIGenerateImageResult, AIGenerateSpeechParams, AIGenerateSpeechResult, AIProviderOptions, AIRerankParams, AIRerankResult, AITranscriptionParams, AITranscriptionResult, AIAgentRunnerService, } from './ai-agent-runner-service.js';
34
+ export type { AIAgentRunnerParams, AIAgentRunnerResult, AIAgentStepResult, AIAgentRunnerService, } from './ai-agent-runner-service.js';
35
35
  export type { AIEmbeddingService } from './ai-embedding-service.js';
36
- export type { CreateRunInput, AIRunStateService, } from './ai-run-state-service.js';
37
- export type { CredentialStatus, CredentialMeta, } from './typed-secret-service.js';
36
+ export type { CreateRunInput, SaveScoreInput, AIRunStateService, } from './ai-run-state-service.js';
37
+ export type { CredentialMeta } from './typed-secret-service.js';
38
38
  export type { CredentialService } from './credential-service.js';
39
39
  export { TypedCredentialService } from './typed-credential-service.js';
40
- export type { CredentialStatusInfo, CredentialMetaInfo, } from './typed-credential-service.js';
41
- export type { VariableStatus, VariableMeta } from './typed-variables-service.js';
40
+ export type { CredentialMetaInfo } from './typed-credential-service.js';
41
+ export type { VariableMeta } from './typed-variables-service.js';
42
42
  export type { MetaService } from './meta-service.js';
43
43
  export type { SessionStore } from './session-store.js';
44
44
  export type { ScopeService, Role } from './scope-service.js';
45
- export { assertRoleIsMutable, assertRoleNameAvailable, roleLockReason, } from './system-role-guard.js';
46
45
  export type { IsSystemRole } from './system-role-guard.js';
47
- export { NoopAuditService, createInvocationAudit, resolveAuditConfig, resolveAuditUserIdentityFromWire, } from './audit-service.js';
48
- export type { AuditConfig, AuditDurability, AuditEvent, AuditEventBatch, AuditLog, AuditLogWriteInput, AuditOutcome, AuditService, AuditSource, AuditUserIdentity, ResolvedAuditConfig, } from './audit-service.js';
46
+ export { NoopAuditService, createInvocationAudit } from './audit-service.js';
47
+ export type { AuditConfig, AuditDurability, AuditEvent, AuditEventBatch, AuditLog, AuditService, AuditUserIdentity, ResolvedAuditConfig, } from './audit-service.js';
49
48
  export { InMemorySessionStore } from './in-memory-session-store.js';
50
- export type { MCPMeta, RPCMetaRecord, ServiceMeta, ServicesMetaRecord, MiddlewareDefinitionMeta, MiddlewareInstanceMeta, GroupMeta, MiddlewareGroupsMeta, PermissionDefinitionMeta, PermissionsGroupsMeta, FunctionsMeta, FunctionMeta, MiddlewareMeta, PermissionMeta, AgentsMeta, AgentMeta, EmailsMeta, EmailTemplateMeta, EmailTemplateLocaleMeta, EmailTemplateAssets, } from './meta-service.js';
51
- export type { CoverageService, CoverageSnapshot, LineHits, ScriptCoverage, FunctionCoverage, CoverageRange, CoverageStatus, FunctionCoverageEntry, FunctionCoverageReport, CoverageFunctionMeta, } from './v8-coverage-service.js';
49
+ export type { MCPMeta, RPCMetaRecord, ServiceMeta, ServicesMetaRecord, MiddlewareDefinitionMeta, MiddlewareInstanceMeta, GroupMeta, MiddlewareGroupsMeta, PermissionDefinitionMeta, PermissionsGroupsMeta, FunctionsMeta, FunctionMeta, MiddlewareMeta, PermissionMeta, AgentsMeta, AgentMeta, EmailsMeta, EmailTemplateMeta, } from './meta-service.js';
50
+ export type { CoverageService, ScriptCoverage, FunctionCoverageEntry, FunctionCoverageReport, CoverageFunctionMeta, } from './v8-coverage-service.js';
52
51
  export { StubTracker, createStubProxy, getStubTracker, isTestRun, stub, spy, type StubCall, } from './stub-tracker.js';
53
52
  export { SecretHostNotAllowedError, assertSecretAllowedForHost, } from './secret-host-binding.js';
@@ -1,8 +1,9 @@
1
1
  export { LogLevel } from './logger.js';
2
2
  export { ScopedSecretService } from './scoped-secret-service.js';
3
- export { PikkuSessionService, createMiddlewareSessionWireProps, createFunctionSessionWireProps, } from './user-session-service.js';
3
+ export { ScopedCredentialService } from './scoped-credential-service.js';
4
+ export { PikkuSessionService, createMiddlewareSessionWireProps, } from './user-session-service.js';
4
5
  export { TypedSecretService } from './typed-secret-service.js';
5
- export { PikkuCredentialWireService, createMiddlewareCredentialWireProps, createWireServicesCredentialWireProps, } from './credential-wire-service.js';
6
+ export { PikkuCredentialWireService } from './credential-wire-service.js';
6
7
  export { TypedVariablesService } from './typed-variables-service.js';
7
8
  export { LocalSecretService } from './local-secrets.js';
8
9
  export { LocalEmailService } from './local-email-service.js';
@@ -15,10 +16,9 @@ export { InMemoryQueueService } from './in-memory-queue-service.js';
15
16
  export { InMemoryTriggerService } from './in-memory-trigger-service.js';
16
17
  export { InMemoryAIRunStateService } from './in-memory-ai-run-state-service.js';
17
18
  export { LocalGatewayService } from './local-gateway-service.js';
18
- export { DEFAULT_WEBHOOK_RETRIES, DEFAULT_WEBHOOK_SIGNATURE_HEADER, PIKKU_OUTGOING_WEBHOOK_QUEUE_NAME, WebhookService, } from './webhook-service.js';
19
+ export { DEFAULT_WEBHOOK_RETRIES, PIKKU_OUTGOING_WEBHOOK_QUEUE_NAME, WebhookService, } from './webhook-service.js';
19
20
  export { TypedCredentialService } from './typed-credential-service.js';
20
- export { assertRoleIsMutable, assertRoleNameAvailable, roleLockReason, } from './system-role-guard.js';
21
- export { NoopAuditService, createInvocationAudit, resolveAuditConfig, resolveAuditUserIdentityFromWire, } from './audit-service.js';
21
+ export { NoopAuditService, createInvocationAudit } from './audit-service.js';
22
22
  export { InMemorySessionStore } from './in-memory-session-store.js';
23
23
  export { StubTracker, createStubProxy, getStubTracker, isTestRun, stub, spy, } from './stub-tracker.js';
24
24
  export { SecretHostNotAllowedError, assertSecretAllowedForHost, } from './secret-host-binding.js';
@@ -5,7 +5,8 @@ import type { ScheduledTasksMeta } from '../wirings/scheduler/scheduler.types.js
5
5
  import type { QueueWorkersMeta } from '../wirings/queue/queue.types.js';
6
6
  import type { CLIMeta } from '../wirings/cli/cli.types.js';
7
7
  import type { MCPResourceMeta, MCPToolMeta, MCPPromptMeta } from '../wirings/mcp/mcp.types.js';
8
- import type { FeaturesMeta, WorkflowsMeta } from '../wirings/workflow/workflow.types.js';
8
+ import type { WorkflowsMeta } from '../wirings/workflow/workflow.types.js';
9
+ import type { FeaturesMeta } from '../wirings/workflow/scenario.types.js';
9
10
  import type { ResolvedPersona } from './personas-service.js';
10
11
  import type { SystemRoleDefinitionsMeta } from '../wirings/role/role.types.js';
11
12
  import type { TriggerMeta, TriggerSourceMeta } from '../wirings/trigger/trigger.types.js';
@@ -0,0 +1,21 @@
1
+ import type { CredentialService } from './credential-service.js';
2
+ /**
3
+ * A `CredentialService` narrowed to the credentials a package declared.
4
+ *
5
+ * Unlike `ScopedSecretService` this stays writable — an addon refreshing its
6
+ * own OAuth token is the normal case. What it cannot do is reach a credential
7
+ * it never declared, or enumerate the app's users.
8
+ */
9
+ export declare class ScopedCredentialService implements CredentialService {
10
+ private credentials;
11
+ private allowedNames;
12
+ constructor(credentials: CredentialService, allowedNames: Set<string>);
13
+ private assertAllowed;
14
+ get<T = unknown>(name: string, userId?: string): Promise<T | null>;
15
+ set(name: string, value: unknown, userId?: string): Promise<void>;
16
+ delete(name: string, userId?: string): Promise<void>;
17
+ has(name: string, userId?: string): Promise<boolean>;
18
+ getAll(userId: string): Promise<Record<string, unknown>>;
19
+ getUsersWithCredential(name: string): Promise<string[]>;
20
+ getAllUsers(): Promise<string[]>;
21
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * A `CredentialService` narrowed to the credentials a package declared.
3
+ *
4
+ * Unlike `ScopedSecretService` this stays writable — an addon refreshing its
5
+ * own OAuth token is the normal case. What it cannot do is reach a credential
6
+ * it never declared, or enumerate the app's users.
7
+ */
8
+ export class ScopedCredentialService {
9
+ credentials;
10
+ allowedNames;
11
+ constructor(credentials, allowedNames) {
12
+ this.credentials = credentials;
13
+ this.allowedNames = allowedNames;
14
+ }
15
+ assertAllowed(name) {
16
+ if (!this.allowedNames.has(name)) {
17
+ throw new Error(`Access denied to credential: ${name}`);
18
+ }
19
+ }
20
+ async get(name, userId) {
21
+ this.assertAllowed(name);
22
+ return this.credentials.get(name, userId);
23
+ }
24
+ async set(name, value, userId) {
25
+ this.assertAllowed(name);
26
+ return this.credentials.set(name, value, userId);
27
+ }
28
+ async delete(name, userId) {
29
+ this.assertAllowed(name);
30
+ return this.credentials.delete(name, userId);
31
+ }
32
+ async has(name, userId) {
33
+ this.assertAllowed(name);
34
+ return this.credentials.has(name, userId);
35
+ }
36
+ async getAll(userId) {
37
+ const all = await this.credentials.getAll(userId);
38
+ const scoped = {};
39
+ for (const name of this.allowedNames) {
40
+ if (name in all) {
41
+ scoped[name] = all[name];
42
+ }
43
+ }
44
+ return scoped;
45
+ }
46
+ async getUsersWithCredential(name) {
47
+ this.assertAllowed(name);
48
+ return this.credentials.getUsersWithCredential(name);
49
+ }
50
+ async getAllUsers() {
51
+ throw new Error('Access denied: enumerating users is not allowed in a scoped credential service');
52
+ }
53
+ }
@@ -168,6 +168,82 @@ export const defineAiStorageServiceTests = (name, aiStorageService) => {
168
168
  assert.ok(runs.length >= 1);
169
169
  assert.ok(runs.every((r) => r.threadId === thread.id));
170
170
  });
171
+ test('saveScore and getScores', async () => {
172
+ const thread = await storage.createThread('resource-score');
173
+ const now = new Date();
174
+ const runId = await storage.createRun({
175
+ agentName: 'scored-agent',
176
+ threadId: thread.id,
177
+ resourceId: 'resource-score',
178
+ status: 'completed',
179
+ usage: { inputTokens: 10, outputTokens: 5, model: 'test' },
180
+ createdAt: now,
181
+ updatedAt: now,
182
+ });
183
+ await storage.saveScore({
184
+ runId,
185
+ scorerName: 'tool-error-rate',
186
+ score: 1,
187
+ });
188
+ await storage.saveScore({
189
+ runId,
190
+ scorerName: 'helpfulness',
191
+ score: 0.75,
192
+ reason: 'answered the question but skipped the fee',
193
+ metadata: { subScores: { accuracy: 0.9 }, judgeTokens: 412 },
194
+ });
195
+ const scores = await storage.getScores(runId);
196
+ assert.equal(scores.length, 2);
197
+ const byName = new Map(scores.map((s) => [s.scorerName, s]));
198
+ const clean = byName.get('tool-error-rate');
199
+ assert.ok(clean);
200
+ assert.equal(clean.score, 1);
201
+ assert.equal(clean.reason, undefined);
202
+ assert.equal(clean.metadata, undefined);
203
+ const helpful = byName.get('helpfulness');
204
+ assert.ok(helpful);
205
+ // A fractional score has to survive the round trip: an integer column
206
+ // would quietly turn every grade into 0 or 1.
207
+ assert.equal(helpful.score, 0.75);
208
+ assert.equal(helpful.reason, 'answered the question but skipped the fee');
209
+ assert.deepEqual(helpful.metadata, {
210
+ subScores: { accuracy: 0.9 },
211
+ judgeTokens: 412,
212
+ });
213
+ assert.ok(helpful.createdAt instanceof Date);
214
+ });
215
+ test('a re-grade appends rather than replacing the grade that was acted on', async () => {
216
+ const thread = await storage.createThread('resource-regrade');
217
+ const now = new Date();
218
+ const runId = await storage.createRun({
219
+ agentName: 'regraded-agent',
220
+ threadId: thread.id,
221
+ resourceId: 'resource-regrade',
222
+ status: 'completed',
223
+ usage: { inputTokens: 1, outputTokens: 1, model: 'test' },
224
+ createdAt: now,
225
+ updatedAt: now,
226
+ });
227
+ await storage.saveScore({ runId, scorerName: 'helpfulness', score: 0.2 });
228
+ await storage.saveScore({ runId, scorerName: 'helpfulness', score: 0.8 });
229
+ const scores = await storage.getScores(runId);
230
+ assert.equal(scores.length, 2);
231
+ assert.deepEqual(scores.map((s) => s.score), [0.2, 0.8]);
232
+ });
233
+ test('getScores is empty for a run nothing graded', async () => {
234
+ const thread = await storage.createThread('resource-ungraded');
235
+ const now = new Date();
236
+ const runId = await storage.createRun({
237
+ agentName: 'ungraded-agent',
238
+ threadId: thread.id,
239
+ resourceId: 'resource-ungraded',
240
+ status: 'completed',
241
+ usage: { inputTokens: 1, outputTokens: 1, model: 'test' },
242
+ createdAt: now,
243
+ updatedAt: now,
244
+ });
245
+ assert.deepEqual(await storage.getScores(runId), []);
246
+ });
171
247
  test('resolveApproval', async () => {
172
248
  const thread = await storage.createThread('resource-8');
173
249
  const now = new Date();
@@ -11,7 +11,8 @@ import type { PikkuMCP } from '../wirings/mcp/mcp.types.js';
11
11
  import type { PikkuScheduledTask } from '../wirings/scheduler/scheduler.types.js';
12
12
  import type { PikkuQueue, QueueService } from '../wirings/queue/queue.types.js';
13
13
  import type { PikkuCLI } from '../wirings/cli/cli.types.js';
14
- import type { PikkuWorkflowWire, PikkuScenarioWire, WorkflowService, WorkflowServiceConfig, WorkflowStepWire } from '../wirings/workflow/workflow.types.js';
14
+ import type { PikkuWorkflowWire, WorkflowService, WorkflowServiceConfig, WorkflowStepWire } from '../wirings/workflow/workflow.types.js';
15
+ import type { PikkuScenarioWire } from '../wirings/workflow/scenario.types.js';
15
16
  import type { PikkuBrowserWire, PikkuScenarioStepWire, ScenarioStepKind, ScenarioSurface } from '../wirings/workflow/scenario-step.types.js';
16
17
  import type { PikkuGraphWire } from '../wirings/workflow/graph/workflow-graph.types.js';
17
18
  import type { PikkuTrigger } from '../wirings/trigger/trigger.types.js';
@@ -142,8 +143,6 @@ export type FunctionRuntimeMeta = {
142
143
  audit?: {
143
144
  durability: AuditDurability;
144
145
  };
145
- /** Keeps the full `SecretService`. Set by the inspector, read by the runner. */
146
- secretBroker?: boolean;
147
146
  version?: number;
148
147
  approvalRequired?: boolean;
149
148
  approvalDescription?: string;
@@ -6,10 +6,12 @@ import type { CLIMeta, CLIProgramState } from '../wirings/cli/cli.types.js';
6
6
  import type { HTTPMethod, CoreHTTPFunctionWiring, HTTPWiringsMeta } from '../wirings/http/http.types.js';
7
7
  import type { CoreMCPResource, MCPResourceMeta, MCPToolMeta, CoreMCPPrompt, MCPPromptMeta } from '../wirings/mcp/mcp.types.js';
8
8
  import type { CoreAIAgent, AIAgentMeta } from '../wirings/ai-agent/ai-agent.types.js';
9
+ import type { PikkuAIScorer, ScorerMeta } from '../wirings/ai-scorer/ai-scorer.types.js';
9
10
  import type { CoreGateway, GatewaysMeta } from '../wirings/gateway/gateway.types.js';
10
11
  import type { CoreQueueWorker, QueueWorkersMeta } from '../wirings/queue/queue.types.js';
11
12
  import type { CoreScheduledTask, ScheduledTasksMeta } from '../wirings/scheduler/scheduler.types.js';
12
- import type { CoreWorkflow, CoreFeature, WorkflowsRuntimeMeta } from '../wirings/workflow/workflow.types.js';
13
+ import type { CoreWorkflow, WorkflowsRuntimeMeta } from '../wirings/workflow/workflow.types.js';
14
+ import type { CoreFeature } from '../wirings/workflow/scenario.types.js';
13
15
  import type { CoreTrigger, CoreTriggerSource, TriggerMeta, TriggerSourceMeta } from '../wirings/trigger/trigger.types.js';
14
16
  import type { FunctionsMeta, CorePikkuMiddleware, CorePikkuMiddlewareGroup, CreateConfig, CreateSingletonServices, CreateWireServices, CoreConfig, CoreSingletonServices, CoreServices, CoreUserSession } from './core.types.js';
15
17
  import type { CorePikkuChannelMiddleware } from '../wirings/channel/channel.types.js';
@@ -40,6 +42,14 @@ export interface PikkuPackageState {
40
42
  variableOverrides?: Record<string, string>;
41
43
  /** Per-instance name-aliases: logical name the addon reads -> actual project credential name */
42
44
  credentialOverrides?: Record<string, string>;
45
+ /** Secrets the host lends this instance, named as the addon reads them */
46
+ secretGrants?: string[];
47
+ /** Credentials the host lends this instance, named as the addon reads them */
48
+ credentialGrants?: string[];
49
+ /** Why this instance gets the whole `SecretService` rather than one scoped to its declared secrets */
50
+ globalSecrets?: string;
51
+ /** Why this instance gets the whole `CredentialService` rather than one scoped to its declared credentials */
52
+ globalCredentials?: string;
43
53
  /** Set by `wireRemoteAddon`: this namespace is consumed remotely over HTTP, not bundled */
44
54
  remote?: boolean;
45
55
  /** Remote host base URL (wireRemoteAddon) — string or resolver over singleton services */
@@ -97,6 +107,10 @@ export interface PikkuPackageState {
97
107
  agent: {
98
108
  agents: Map<string, CoreAIAgent>;
99
109
  agentsMeta: AIAgentMeta;
110
+ scorers: Map<string, PikkuAIScorer>;
111
+ scorersMeta: ScorerMeta;
112
+ /** Alias -> `provider/model`, from the `models` table in pikku.config.json. */
113
+ modelAliases: Record<string, string>;
100
114
  };
101
115
  gateway: {
102
116
  gateways: Map<string, CoreGateway>;
@@ -141,5 +155,9 @@ export interface PikkuPackageState {
141
155
  oauth2?: boolean;
142
156
  }> | null;
143
157
  requiredParentServices: string[] | null;
158
+ /** The secret keys this package is allowed to read, emitted by the CLI from
159
+ * what the package declares and is seen to read. `null` when the package
160
+ * was never analysed, which scopes it to nothing. */
161
+ declaredSecrets: string[] | null;
144
162
  };
145
163
  }
@@ -1,2 +1,2 @@
1
- export type { ActorFlowApprovalPolicy, ActorFlowVerdict, ConverseOptions, TargetAgentReply, TargetPendingApproval, TargetAgentDriver, } from './actor-flow.types.js';
1
+ export type { ActorFlowVerdict, ConverseOptions, TargetAgentDriver, } from './actor-flow.types.js';
2
2
  export { runConversation, type RunConversationParams, type ActorLLM, } from './run-conversation.js';
@@ -0,0 +1,58 @@
1
+ import type { AIAgentStep, AIMessage, PikkuAIMiddlewareHooks } from './ai-agent.types.js';
2
+ import type { AIRunStateService } from '../../services/ai-run-state-service.js';
3
+ export type RunUsage = {
4
+ inputTokens: number;
5
+ outputTokens: number;
6
+ model?: string;
7
+ };
8
+ /**
9
+ * Everything a finished run produced, after all middleware has had its say.
10
+ *
11
+ * One snapshot feeds persistence and anything that grades the run, so that what
12
+ * a scorer is shown is exactly what was stored — including the redactions.
13
+ */
14
+ export type FinalizedRun = {
15
+ runId: string;
16
+ agentName: string;
17
+ threadId: string;
18
+ resourceId?: string;
19
+ /** The prompt the run answered — what a scorer grades the answer against. */
20
+ input: string;
21
+ text: string;
22
+ steps: AIAgentStep[];
23
+ usage: RunUsage;
24
+ };
25
+ /**
26
+ * The prompt a run answered: the most recent user turn, which is what the model
27
+ * was last asked. On a resumed run the earlier turns are context, not the ask.
28
+ */
29
+ export declare const lastUserMessageText: (messages: AIMessage[]) => string;
30
+ /**
31
+ * Run the `modifyOutput` chain over a finished non-streaming run.
32
+ *
33
+ * Reverse order, matching the input chain: the middleware registered first
34
+ * wraps the others, so it sees the output last.
35
+ */
36
+ export declare const applyOutputMiddleware: (aiMiddlewares: PikkuAIMiddlewareHooks[], singletonServices: any, input: {
37
+ text: string;
38
+ messages: AIMessage[];
39
+ steps: AIAgentStep[];
40
+ usage: {
41
+ inputTokens: number;
42
+ outputTokens: number;
43
+ };
44
+ }) => Promise<{
45
+ text: string;
46
+ messages: AIMessage[];
47
+ steps: AIAgentStep[];
48
+ }>;
49
+ /**
50
+ * The one place a run ends successfully.
51
+ *
52
+ * Every path that completes a run — streamed, non-streamed, and resumed after a
53
+ * tool approval — goes through here, so that anything terminal is reachable
54
+ * from all of them and cannot be reordered by a middleware author. Nothing here
55
+ * may rewrite the run: by this point the output middleware has resolved and, on
56
+ * the streaming path, the client already has the reply.
57
+ */
58
+ export declare const finalizeAgentRun: (aiRunState: AIRunStateService, run: FinalizedRun) => Promise<void>;
@@ -0,0 +1,138 @@
1
+ import { pikkuState } from '../../pikku-state.js';
2
+ import { scoreFinishedRun } from '../ai-scorer/ai-scorer-live.js';
3
+ import { recordScoreSnapshot } from '../ai-scorer/ai-scorer-snapshots.js';
4
+ /**
5
+ * The prompt a run answered: the most recent user turn, which is what the model
6
+ * was last asked. On a resumed run the earlier turns are context, not the ask.
7
+ */
8
+ export const lastUserMessageText = (messages) => {
9
+ for (let i = messages.length - 1; i >= 0; i--) {
10
+ const message = messages[i];
11
+ if (message?.role !== 'user')
12
+ continue;
13
+ if (typeof message.content === 'string')
14
+ return message.content;
15
+ if (Array.isArray(message.content)) {
16
+ return message.content
17
+ .filter((part) => part.type === 'text')
18
+ .map((part) => part.text)
19
+ .join('\n');
20
+ }
21
+ }
22
+ return '';
23
+ };
24
+ const flattenToolCalls = (steps) => steps.flatMap((step) => step.toolCalls ?? []);
25
+ /**
26
+ * Push a rewritten flat tool-call list back onto the steps it came from,
27
+ * preserving the step boundaries. A hook that adds or drops calls would make
28
+ * the boundaries meaningless, so a length change collapses them into the last
29
+ * step rather than silently mis-attributing calls to the wrong step.
30
+ */
31
+ const distributeToolCalls = (steps, toolCalls) => {
32
+ if (toolCalls.length !== flattenToolCalls(steps).length) {
33
+ return steps.map((step, index) => ({
34
+ ...step,
35
+ toolCalls: index === steps.length - 1 ? toolCalls : [],
36
+ }));
37
+ }
38
+ let cursor = 0;
39
+ return steps.map((step) => {
40
+ if (!step.toolCalls)
41
+ return step;
42
+ const next = toolCalls.slice(cursor, cursor + step.toolCalls.length);
43
+ cursor += step.toolCalls.length;
44
+ return { ...step, toolCalls: next };
45
+ });
46
+ };
47
+ /**
48
+ * Run the `modifyOutput` chain over a finished non-streaming run.
49
+ *
50
+ * Reverse order, matching the input chain: the middleware registered first
51
+ * wraps the others, so it sees the output last.
52
+ */
53
+ export const applyOutputMiddleware = async (aiMiddlewares, singletonServices, input) => {
54
+ let text = input.text;
55
+ let messages = input.messages;
56
+ let steps = input.steps;
57
+ let toolCalls = flattenToolCalls(steps);
58
+ for (let i = aiMiddlewares.length - 1; i >= 0; i--) {
59
+ const mw = aiMiddlewares[i];
60
+ if (!mw.modifyOutput)
61
+ continue;
62
+ const result = await mw.modifyOutput(singletonServices, {
63
+ text,
64
+ messages,
65
+ usage: {
66
+ inputTokens: input.usage.inputTokens,
67
+ outputTokens: input.usage.outputTokens,
68
+ },
69
+ toolCalls,
70
+ });
71
+ text = result.text;
72
+ messages = result.messages;
73
+ if (result.toolCalls) {
74
+ toolCalls = result.toolCalls;
75
+ steps = distributeToolCalls(steps, toolCalls);
76
+ }
77
+ }
78
+ return { text, messages, steps };
79
+ };
80
+ /**
81
+ * The one place a run ends successfully.
82
+ *
83
+ * Every path that completes a run — streamed, non-streamed, and resumed after a
84
+ * tool approval — goes through here, so that anything terminal is reachable
85
+ * from all of them and cannot be reordered by a middleware author. Nothing here
86
+ * may rewrite the run: by this point the output middleware has resolved and, on
87
+ * the streaming path, the client already has the reply.
88
+ */
89
+ export const finalizeAgentRun = async (aiRunState, run) => {
90
+ await aiRunState.updateRun(run.runId, {
91
+ status: 'completed',
92
+ ...(run.usage.model
93
+ ? {
94
+ usage: {
95
+ inputTokens: run.usage.inputTokens,
96
+ outputTokens: run.usage.outputTokens,
97
+ model: run.usage.model,
98
+ },
99
+ }
100
+ : {}),
101
+ });
102
+ // Read rather than `getSingletonServices()`: a process that never registered
103
+ // them grades nothing, which is not an error at the point a run has already
104
+ // succeeded.
105
+ const services = pikkuState(null, 'package', 'singletonServices');
106
+ if (!services)
107
+ return;
108
+ const snapshot = {
109
+ runId: run.runId,
110
+ agentName: run.agentName,
111
+ threadId: run.threadId,
112
+ ...(run.resourceId !== undefined ? { resourceId: run.resourceId } : {}),
113
+ input: run.input,
114
+ output: run.text,
115
+ toolCalls: run.steps.flatMap((step) => (step.toolCalls ?? []).map((call) => ({
116
+ name: call.name,
117
+ args: call.args,
118
+ result: call.result,
119
+ ...(call.error !== undefined ? { error: call.error } : {}),
120
+ }))),
121
+ usage: {
122
+ inputTokens: run.usage.inputTokens,
123
+ outputTokens: run.usage.outputTokens,
124
+ ...(run.usage.model !== undefined ? { model: run.usage.model } : {}),
125
+ },
126
+ };
127
+ // The same object a scenario grades, so an asserted score and a sampled one
128
+ // are the same measurement. A no-op unless a dev server turned retention on.
129
+ recordScoreSnapshot(snapshot);
130
+ // Best-effort and last: the client already has its answer, so a grading
131
+ // failure must not surface as a failed run.
132
+ try {
133
+ await scoreFinishedRun(snapshot, services);
134
+ }
135
+ catch (error) {
136
+ services.logger?.error(`[pikku] Live scoring failed for run ${run.runId}`, error);
137
+ }
138
+ };
@@ -240,6 +240,7 @@ export const persistOrphanedToolResults = async (handle, storage, threadId) => {
240
240
  : typeof entry.result === 'string'
241
241
  ? entry.result
242
242
  : JSON.stringify(entry.result),
243
+ ...(entry.error ? { error: entry.error } : {}),
243
244
  })),
244
245
  undelivered: true,
245
246
  createdAt: new Date(),
@@ -1,5 +1,5 @@
1
1
  import type { CoreSingletonServices } from '../../types/core.types.js';
2
- import type { CoreAIAgent, AIAgentMemoryConfig, AIAgentInput, AIMessage } from './ai-agent.types.js';
2
+ import type { CoreAIAgent, AIAgentMemoryConfig, AIAgentInput, AIAgentStep, AIMessage } from './ai-agent.types.js';
3
3
  import type { AIStorageService } from '../../services/ai-storage-service.js';
4
4
  import type { Logger } from '../../services/logger.js';
5
5
  import type { SchemaService } from '../../services/schema-service.js';
@@ -14,13 +14,7 @@ export declare function loadContextMessages(memoryConfig: AIAgentMemoryConfig |
14
14
  export declare function saveMessages(storage: AIStorageService | undefined, threadId: string, resourceId: string, memoryConfig: AIAgentMemoryConfig | undefined, userMessage: AIMessage | null | undefined, result: {
15
15
  text: string;
16
16
  uiSpec?: unknown;
17
- steps: {
18
- toolCalls?: {
19
- name: string;
20
- args: Record<string, unknown>;
21
- result: string;
22
- }[];
23
- }[];
17
+ steps: Pick<AIAgentStep, 'toolCalls'>[];
24
18
  }): Promise<string>;
25
19
  export declare function trimMessages(messages: AIMessage[], maxTokenBudget?: number): AIMessage[];
26
20
  export declare function parseWorkingMemory(text: string): Record<string, unknown> | null;
@@ -102,6 +102,7 @@ export async function saveMessages(storage, threadId, resourceId, memoryConfig,
102
102
  id: toolCallIds[i],
103
103
  name: tc.name,
104
104
  result: tc.result,
105
+ ...(tc.error ? { error: tc.error } : {}),
105
106
  })),
106
107
  createdAt: new Date(),
107
108
  });
@@ -256,9 +257,39 @@ export function stripWorkingMemoryForStreaming(text) {
256
257
  }
257
258
  return output;
258
259
  }
260
+ async function persistWorkingMemory(options, workingMemory) {
261
+ if (!options.storage)
262
+ return;
263
+ const existing = (await options.storage.getWorkingMemory(options.threadId, 'thread')) ?? {};
264
+ const merged = deepMergeWorkingMemory(existing, workingMemory);
265
+ if (options.schemaService && options.workingMemorySchemaName) {
266
+ try {
267
+ await options.schemaService.validateSchema(options.workingMemorySchemaName, merged);
268
+ }
269
+ catch (err) {
270
+ options.logger?.warn(`Working memory validation failed: ${err instanceof Error ? err.message : String(err)}`);
271
+ return;
272
+ }
273
+ }
274
+ await options.storage.saveWorkingMemory(options.threadId, 'thread', merged);
275
+ }
259
276
  export function createWorkingMemoryMiddleware(options) {
260
277
  return {
261
278
  modifyOutputStream: async (_services, { event, state }) => {
279
+ // A streamed step ends at its `usage` event, or at `done` for a model
280
+ // that reports no usage. That is the last moment the unstripped text is
281
+ // still reachable: it lives only in `state.rawText`, because the strip
282
+ // below runs before anything downstream — including the channel that
283
+ // accumulates what `modifyOutput` would later be handed.
284
+ if (event.type === 'usage' || event.type === 'done') {
285
+ const { workingMemory } = extractWorkingMemory(state.rawText ?? '');
286
+ state.rawText = '';
287
+ state.emittedVisibleText = '';
288
+ if (workingMemory) {
289
+ await persistWorkingMemory(options, workingMemory);
290
+ }
291
+ return event;
292
+ }
262
293
  if (event.type !== 'text-delta')
263
294
  return event;
264
295
  const rawText = `${state.rawText ?? ''}${event.text}`;
@@ -275,24 +306,10 @@ export function createWorkingMemoryMiddleware(options) {
275
306
  return null;
276
307
  return { ...event, text: delta };
277
308
  },
278
- modifyOutput: async (_services, { text, messages, usage }) => {
309
+ modifyOutput: async (_services, { text, messages }) => {
279
310
  const { workingMemory, cleanedText } = extractWorkingMemory(text);
280
- if (workingMemory && options.storage) {
281
- const existing = (await options.storage.getWorkingMemory(options.threadId, 'thread')) ?? {};
282
- const merged = deepMergeWorkingMemory(existing, workingMemory);
283
- let valid = true;
284
- if (options.schemaService && options.workingMemorySchemaName) {
285
- try {
286
- await options.schemaService.validateSchema(options.workingMemorySchemaName, merged);
287
- }
288
- catch (err) {
289
- valid = false;
290
- options.logger?.warn(`Working memory validation failed: ${err instanceof Error ? err.message : String(err)}`);
291
- }
292
- }
293
- if (valid) {
294
- await options.storage.saveWorkingMemory(options.threadId, 'thread', merged);
295
- }
311
+ if (workingMemory) {
312
+ await persistWorkingMemory(options, workingMemory);
296
313
  }
297
314
  return {
298
315
  text: cleanedText,
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Resolves a model name to a concrete `provider/model`. Aliases come from the
3
+ * `models` table in pikku.config.json; a name containing `/` is already
4
+ * concrete. Read from the main package, not the calling addon's — which model
5
+ * a tier points at is the hosting app's decision.
6
+ */
7
+ export declare const resolveModelAlias: (model: string) => string;
1
8
  export declare function resolveModelConfig(_agentName: string, agent: {
2
9
  model: string;
3
10
  temperature?: number;