@pikku/inspector 0.12.58 → 0.12.60

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 (103) hide show
  1. package/CHANGELOG.md +127 -0
  2. package/dist/add/add-agent-scorer.d.ts +2 -0
  3. package/dist/add/add-agent-scorer.js +61 -0
  4. package/dist/add/add-agent.d.ts +4 -0
  5. package/dist/add/add-agent.js +442 -0
  6. package/dist/add/add-channel.d.ts +1 -1
  7. package/dist/add/add-functions.js +1 -1
  8. package/dist/add/add-keyed-wiring.js +1 -1
  9. package/dist/add/add-middleware.js +5 -5
  10. package/dist/add/add-personas.js +1 -1
  11. package/dist/add/add-wire-addon.js +10 -0
  12. package/dist/add/add-workflow.d.ts +1 -1
  13. package/dist/error-codes.d.ts +1 -0
  14. package/dist/error-codes.js +1 -0
  15. package/dist/inspector.js +1 -1
  16. package/dist/types.d.ts +27 -22
  17. package/dist/utils/compute-required-schemas.d.ts +1 -1
  18. package/dist/utils/contract-hashes.d.ts +2 -1
  19. package/dist/utils/contract-hashes.js +1 -1
  20. package/dist/utils/extract-function-name.js +1 -1
  21. package/dist/utils/extract-services.d.ts +1 -1
  22. package/dist/utils/filter-inspector-state.js +1 -1
  23. package/dist/utils/filter-utils.d.ts +1 -1
  24. package/dist/utils/middleware.d.ts +2 -2
  25. package/dist/utils/middleware.js +5 -5
  26. package/dist/utils/permissions.d.ts +1 -1
  27. package/dist/utils/post-process.d.ts +2 -2
  28. package/dist/utils/post-process.js +24 -12
  29. package/dist/utils/resolve-deploy-target.d.ts +1 -1
  30. package/dist/utils/resolve-function-meta.d.ts +1 -1
  31. package/dist/utils/resolve-function-types.d.ts +1 -1
  32. package/dist/utils/resolve-versions.js +1 -1
  33. package/dist/utils/schema-generator.d.ts +1 -1
  34. package/dist/utils/serialize-inspector-state.d.ts +5 -3
  35. package/dist/utils/serialize-inspector-state.js +4 -4
  36. package/dist/utils/serialize-openapi-json.d.ts +2 -2
  37. package/dist/utils/workflow/derive-workflow-plan.d.ts +1 -1
  38. package/dist/utils/workflow/dsl/extract-dsl-workflow.d.ts +1 -1
  39. package/dist/utils/workflow/graph/convert-dsl-to-graph.d.ts +1 -1
  40. package/dist/utils/workflow/graph/finalize-workflows.js +1 -1
  41. package/dist/utils/workflow/graph/serialize-workflow-graph.js +1 -1
  42. package/dist/utils/workflow/graph/workflow-graph.types.d.ts +2 -1
  43. package/dist/visit.js +4 -4
  44. package/package.json +3 -3
  45. package/src/add/{add-ai-scorer.test.ts → add-agent-scorer.test.ts} +11 -11
  46. package/src/add/{add-ai-scorer.ts → add-agent-scorer.ts} +4 -4
  47. package/src/add/{add-ai-agent.test.ts → add-agent.test.ts} +1 -1
  48. package/src/add/{add-ai-agent.ts → add-agent.ts} +11 -17
  49. package/src/add/add-auth.ts +1 -1
  50. package/src/add/add-channel.ts +4 -1
  51. package/src/add/add-cli.ts +1 -1
  52. package/src/add/add-functions.ts +9 -4
  53. package/src/add/add-gateway.ts +1 -1
  54. package/src/add/add-http-route.ts +1 -1
  55. package/src/add/add-keyed-wiring.ts +1 -1
  56. package/src/add/add-middleware.ts +7 -5
  57. package/src/add/add-permission.ts +1 -1
  58. package/src/add/add-personas.test.ts +2 -2
  59. package/src/add/add-personas.ts +3 -2
  60. package/src/add/add-scenario-step.test.ts +2 -1
  61. package/src/add/add-scope.test.ts +19 -19
  62. package/src/add/add-scope.ts +1 -1
  63. package/src/add/add-system-role.test.ts +12 -12
  64. package/src/add/add-wire-addon.test.ts +31 -0
  65. package/src/add/add-wire-addon.ts +8 -0
  66. package/src/add/add-workflow-graph-input.test.ts +1 -1
  67. package/src/add/add-workflow.ts +4 -1
  68. package/src/add/addon-contracts.test.ts +1 -1
  69. package/src/error-codes.ts +1 -0
  70. package/src/inspector.ts +1 -1
  71. package/src/types.ts +33 -23
  72. package/src/utils/compute-required-schemas.ts +1 -1
  73. package/src/utils/contract-hashes.test.ts +1 -1
  74. package/src/utils/contract-hashes.ts +3 -2
  75. package/src/utils/extract-function-name.ts +1 -1
  76. package/src/utils/extract-services.ts +4 -1
  77. package/src/utils/filter-inspector-state.ts +2 -2
  78. package/src/utils/filter-utils.ts +1 -1
  79. package/src/utils/middleware.test.ts +23 -23
  80. package/src/utils/middleware.ts +6 -6
  81. package/src/utils/permissions.ts +1 -1
  82. package/src/utils/post-process.test.ts +76 -9
  83. package/src/utils/post-process.ts +36 -14
  84. package/src/utils/resolve-deploy-target.ts +1 -1
  85. package/src/utils/resolve-function-meta.ts +4 -1
  86. package/src/utils/resolve-function-types.ts +1 -1
  87. package/src/utils/resolve-versions.ts +5 -2
  88. package/src/utils/schema-generator.ts +3 -2
  89. package/src/utils/serialize-inspector-state.ts +9 -7
  90. package/src/utils/serialize-mcp-json.ts +1 -1
  91. package/src/utils/serialize-openapi-json.ts +2 -2
  92. package/src/utils/workflow/derive-workflow-plan.test.ts +1 -1
  93. package/src/utils/workflow/derive-workflow-plan.ts +1 -1
  94. package/src/utils/workflow/dsl/extract-approval-policy.test.ts +5 -2
  95. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +2 -2
  96. package/src/utils/workflow/graph/convert-dsl-to-graph-skip.test.ts +1 -1
  97. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +4 -1
  98. package/src/utils/workflow/graph/finalize-workflow-wires.ts +1 -1
  99. package/src/utils/workflow/graph/finalize-workflows.ts +6 -2
  100. package/src/utils/workflow/graph/serialize-workflow-graph.ts +1 -1
  101. package/src/utils/workflow/graph/workflow-graph.types.ts +2 -2
  102. package/src/visit.ts +4 -4
  103. package/tsconfig.tsbuildinfo +1 -1
@@ -60,6 +60,8 @@ export function addWireAddon(
60
60
  let secretOverrides: Record<string, string> | undefined
61
61
  let variableOverrides: Record<string, string> | undefined
62
62
  let credentialOverrides: Record<string, string> | undefined
63
+ let secretGrants: string[] | undefined
64
+ let credentialGrants: string[] | undefined
63
65
  let globalSecrets: string | undefined
64
66
  let globalCredentials: string | undefined
65
67
 
@@ -104,6 +106,10 @@ export function addWireAddon(
104
106
  ts.isObjectLiteralExpression(prop.initializer)
105
107
  ) {
106
108
  credentialOverrides = parseStringRecord(prop.initializer)
109
+ } else if (key === 'secretGrants') {
110
+ secretGrants = parseStringArray(prop.initializer)
111
+ } else if (key === 'credentialGrants') {
112
+ credentialGrants = parseStringArray(prop.initializer)
107
113
  } else if (key === 'globalSecrets') {
108
114
  // The grant is real at runtime whatever the reason evaluates to, so a
109
115
  // non-literal is recorded as its source text rather than dropped.
@@ -130,6 +136,8 @@ export function addWireAddon(
130
136
  secretOverrides,
131
137
  variableOverrides,
132
138
  credentialOverrides,
139
+ secretGrants,
140
+ credentialGrants,
133
141
  globalSecrets,
134
142
  globalCredentials,
135
143
  })
@@ -36,7 +36,7 @@ describe('addWorkflowGraph — node input extraction', () => {
36
36
  graphFile,
37
37
  [
38
38
  "import { pikkuWorkflowGraph } from '@pikku/core/workflow'",
39
- "import { template } from '@pikku/core/workflow'",
39
+ "import { template } from '@pikku/core/ecosystem/workflow'",
40
40
  'export const myGraph = pikkuWorkflowGraph({',
41
41
  " name: 'input-graph',",
42
42
  " nodes: { edit: 'edit' },",
@@ -3,7 +3,10 @@ import type { AddWiring, InspectorState } from '../types.js'
3
3
  import { extractFunctionName } from '../utils/extract-function-name.js'
4
4
  import { extractFunctionNode } from '../utils/extract-function-node.js'
5
5
  import { ErrorCode } from '../error-codes.js'
6
- import type { WorkflowStepMeta, WorkflowContext } from '@pikku/core/workflow'
6
+ import type {
7
+ WorkflowStepMeta,
8
+ WorkflowContext,
9
+ } from '@pikku/core/ecosystem/workflow'
7
10
  import {
8
11
  extractStringLiteral,
9
12
  isStringLike,
@@ -178,7 +178,7 @@ describe('addon contract metadata', () => {
178
178
  await writeFile(
179
179
  appFile,
180
180
  [
181
- "import { wireAddon } from '@pikku/core/rpc'",
181
+ "import { wireAddon } from '@pikku/core/ecosystem/rpc'",
182
182
  "import { wireHTTPRoutes } from '@pikku/core/http'",
183
183
  "import { wireChannel } from '@pikku/core/channel'",
184
184
  "import { wireCLI } from '@pikku/core/cli'",
@@ -105,6 +105,7 @@ export enum ErrorCode {
105
105
  // Dependency integrity errors
106
106
  DUPLICATE_CORE_VERSION = 'PKU717',
107
107
  CORE_VERSION_SKEW = 'PKU718',
108
+ SPLIT_TYPE_IDENTITY = 'PKU719',
108
109
 
109
110
  // Data classification errors
110
111
  PII_IN_OUTPUT = 'PKU910',
package/src/inspector.ts CHANGED
@@ -221,7 +221,7 @@ export function getInitialInspectorState(rootDir: string): InspectorState {
221
221
  instances: {},
222
222
  tagMiddleware: new Map(),
223
223
  },
224
- aiMiddleware: {
224
+ agentMiddleware: {
225
225
  definitions: {},
226
226
  },
227
227
  permissions: {
package/src/types.ts CHANGED
@@ -1,35 +1,41 @@
1
1
  import type * as ts from 'typescript'
2
2
  import type { SecretUsage } from './utils/extract-secret-usage.js'
3
- import type { ChannelMessageMeta, ChannelsMeta } from '@pikku/core/channel'
4
- import type { GatewaysMeta } from '@pikku/core/gateway'
5
- import type { HTTPWiringsMeta } from '@pikku/core/http'
6
- import type { ScheduledTasksMeta } from '@pikku/core/scheduler'
7
- import type { TriggerMeta, TriggerSourceMeta } from '@pikku/core/trigger'
8
- import type { QueueWorkersMeta } from '@pikku/core/queue'
9
- import type { WorkflowsMeta } from '@pikku/core/workflow'
3
+ import type {
4
+ ChannelMessageMeta,
5
+ ChannelsMeta,
6
+ } from '@pikku/core/ecosystem/channel'
7
+ import type { GatewaysMeta } from '@pikku/core/ecosystem/gateway'
8
+ import type { HTTPWiringsMeta } from '@pikku/core/ecosystem/http'
9
+ import type { ScheduledTasksMeta } from '@pikku/core/ecosystem/scheduler'
10
+ import type {
11
+ TriggerMeta,
12
+ TriggerSourceMeta,
13
+ } from '@pikku/core/ecosystem/trigger'
14
+ import type { QueueWorkersMeta } from '@pikku/core/ecosystem/queue'
15
+ import type { WorkflowsMeta } from '@pikku/core/ecosystem/workflow'
10
16
  import type {
11
17
  MCPResourceMeta,
12
18
  MCPToolMeta,
13
19
  MCPPromptMeta,
14
- } from '@pikku/core/mcp'
15
- import type { AIAgentMeta } from '@pikku/core/ai-agent'
16
- import type { ScorerMeta } from '@pikku/core/ai-scorer'
17
- import type { CLIMeta } from '@pikku/core/cli'
18
- import type { CLICommandMeta } from '@pikku/core/cli'
19
- import type { NodesMeta } from '@pikku/core/node'
20
- import type { SecretDefinitions } from '@pikku/core/secret'
21
- import type { CredentialDefinitions } from '@pikku/core/credential'
22
- import type { ScopeDefinitions } from '@pikku/core/scope'
23
- import type { SystemRoleDefinitions } from '@pikku/core/role'
24
- import type { PersonaDefinitions } from '@pikku/core/persona'
25
- import type { VariableDefinitions } from '@pikku/core/variable'
20
+ } from '@pikku/core/ecosystem/mcp'
21
+ import type { AgentsMeta } from '@pikku/core/ecosystem/agent'
22
+ import type { ScorerMeta } from '@pikku/core/ecosystem/agent-scorer'
23
+ import type { CLIMeta } from '@pikku/core/ecosystem/cli'
24
+ import type { CLICommandMeta } from '@pikku/core/ecosystem/cli'
25
+ import type { NodesMeta } from '@pikku/core/ecosystem/node'
26
+ import type { SecretDefinitions } from '@pikku/core/ecosystem/secret'
27
+ import type { CredentialDefinitions } from '@pikku/core/ecosystem/credential'
28
+ import type { ScopeDefinitions } from '@pikku/core/ecosystem/scope'
29
+ import type { SystemRoleDefinitions } from '@pikku/core/ecosystem/role'
30
+ import type { PersonaDefinitions } from '@pikku/core/ecosystem/persona'
31
+ import type { VariableDefinitions } from '@pikku/core/ecosystem/variable'
26
32
  import type { TypesMap } from './types-map.js'
33
+ import type { FunctionsMeta } from '@pikku/core/ecosystem/services'
27
34
  import type {
28
- FunctionsMeta,
29
35
  FunctionServicesMeta,
30
36
  FunctionWiresMeta,
31
37
  JSONValue,
32
- } from '@pikku/core'
38
+ } from '@pikku/core/ecosystem/types'
33
39
  import type { OpenAPISpecInfo } from './utils/serialize-openapi-json.js'
34
40
  import type { ErrorCode, CodedDiagnostic } from './error-codes.js'
35
41
  import type {
@@ -588,6 +594,10 @@ export interface InspectorState {
588
594
  secretOverrides?: Record<string, string>
589
595
  variableOverrides?: Record<string, string>
590
596
  credentialOverrides?: Record<string, string>
597
+ /** Secrets the app lends this instance, named as the addon reads them. */
598
+ secretGrants?: string[]
599
+ /** Credentials the app lends this instance, named as the addon reads them. */
600
+ credentialGrants?: string[]
591
601
  /**
592
602
  * The app's stated reason for handing this instance the whole
593
603
  * `SecretService` instead of one scoped to its declared secrets.
@@ -609,7 +619,7 @@ export interface InspectorState {
609
619
  files: Set<string>
610
620
  }
611
621
  agents: {
612
- agentsMeta: AIAgentMeta
622
+ agentsMeta: AgentsMeta
613
623
  files: Map<string, { path: string; exportedName: string }>
614
624
  }
615
625
  scorers: {
@@ -677,7 +687,7 @@ export interface InspectorState {
677
687
  }
678
688
  middleware: InspectorMiddlewareState
679
689
  channelMiddleware: InspectorChannelMiddlewareState
680
- aiMiddleware: InspectorAIMiddlewareState
690
+ agentMiddleware: InspectorAIMiddlewareState
681
691
  permissions: InspectorPermissionState
682
692
  serviceAggregation: {
683
693
  requiredServices: Set<string>
@@ -1,4 +1,4 @@
1
- import type { FunctionsMeta } from '@pikku/core'
1
+ import type { FunctionsMeta } from '@pikku/core/ecosystem/services'
2
2
  import type { TypesMap } from '../types-map.js'
3
3
  import type { SchemaRef } from '../types.js'
4
4
 
@@ -10,7 +10,7 @@ import {
10
10
  extractContractsFromMeta,
11
11
  } from './contract-hashes.js'
12
12
  import type { ContractEntry, VersionManifest } from './contract-hashes.js'
13
- import type { FunctionsMeta } from '@pikku/core'
13
+ import type { FunctionsMeta } from '@pikku/core/ecosystem/services'
14
14
  import { ErrorCode } from '../error-codes.js'
15
15
 
16
16
  function makeContracts(
@@ -1,5 +1,6 @@
1
- import type { FunctionsMeta, JSONValue } from '@pikku/core'
2
- import { parseVersionedId } from '@pikku/core'
1
+ import type { FunctionsMeta } from '@pikku/core/ecosystem/services'
2
+ import type { JSONValue } from '@pikku/core/ecosystem/types'
3
+ import { parseVersionedId } from '@pikku/core/ecosystem/types'
3
4
  import type { TypesMap } from '../types-map.js'
4
5
  import { ErrorCode } from '../error-codes.js'
5
6
  import { canonicalJSON, hashString } from './hash.js'
@@ -1,7 +1,7 @@
1
1
  import * as ts from 'typescript'
2
2
  import { createHash } from 'crypto'
3
3
  import { relative } from 'path'
4
- import { formatVersionedId } from '@pikku/core'
4
+ import { formatVersionedId } from '@pikku/core/ecosystem/types'
5
5
 
6
6
  /**
7
7
  * Deterministic placeholder id for an anonymous/unnamed pikku function or
@@ -1,5 +1,8 @@
1
1
  import * as ts from 'typescript'
2
- import type { FunctionServicesMeta, FunctionWiresMeta } from '@pikku/core'
2
+ import type {
3
+ FunctionServicesMeta,
4
+ FunctionWiresMeta,
5
+ } from '@pikku/core/ecosystem/types'
3
6
 
4
7
  /**
5
8
  * Extract services from a function's first parameter destructuring pattern
@@ -3,8 +3,8 @@ import type {
3
3
  InspectorFilters,
4
4
  InspectorLogger,
5
5
  } from '../types.js'
6
- import type { PikkuWiringTypes } from '@pikku/core'
7
- import { parseVersionedId } from '@pikku/core'
6
+ import type { PikkuWiringTypes } from '@pikku/core/ecosystem/types'
7
+ import { parseVersionedId } from '@pikku/core/ecosystem/types'
8
8
  import { aggregateRequiredServices } from './post-process.js'
9
9
  import { resolveDeployTarget } from './resolve-deploy-target.js'
10
10
 
@@ -1,5 +1,5 @@
1
1
  import type { InspectorFilters, InspectorLogger } from '../types.js'
2
- import type { PikkuWiringTypes } from '@pikku/core'
2
+ import type { PikkuWiringTypes } from '@pikku/core/ecosystem/types'
3
3
 
4
4
  /**
5
5
  * Match a value against a pattern with wildcard support
@@ -1,6 +1,6 @@
1
1
  import { describe, test } from 'node:test'
2
2
  import assert from 'node:assert'
3
- import { resolveAIMiddleware } from './middleware.js'
3
+ import { resolveAgentMiddleware } from './middleware.js'
4
4
  import { getInitialInspectorState } from '../inspector.js'
5
5
  import * as ts from 'typescript'
6
6
 
@@ -26,39 +26,39 @@ function createMockChecker(): ts.TypeChecker {
26
26
  return {} as any
27
27
  }
28
28
 
29
- describe('resolveAIMiddleware', () => {
30
- test('should return undefined when no aiMiddleware property exists', () => {
29
+ describe('resolveAgentMiddleware', () => {
30
+ test('should return undefined when no agentMiddleware property exists', () => {
31
31
  const state = getInitialInspectorState('/test')
32
32
  const src = createSourceFile('const x = { name: "test" }')
33
33
  const obj = getObjectLiteral(src)
34
34
  const checker = createMockChecker()
35
35
 
36
- const result = resolveAIMiddleware(state, obj, checker)
36
+ const result = resolveAgentMiddleware(state, obj, checker)
37
37
 
38
38
  assert.strictEqual(result, undefined)
39
39
  })
40
40
 
41
- test('should return undefined when aiMiddleware array is empty', () => {
41
+ test('should return undefined when agentMiddleware array is empty', () => {
42
42
  const state = getInitialInspectorState('/test')
43
- const src = createSourceFile('const x = { aiMiddleware: [] }')
43
+ const src = createSourceFile('const x = { agentMiddleware: [] }')
44
44
  const obj = getObjectLiteral(src)
45
45
  const checker = createMockChecker()
46
46
 
47
- const result = resolveAIMiddleware(state, obj, checker)
47
+ const result = resolveAgentMiddleware(state, obj, checker)
48
48
 
49
49
  assert.strictEqual(result, undefined)
50
50
  })
51
51
 
52
- test('should resolve aiMiddleware identifiers as wire metadata', () => {
52
+ test('should resolve agentMiddleware identifiers as wire metadata', () => {
53
53
  const state = getInitialInspectorState('/test')
54
- state.aiMiddleware.definitions['myAIMW'] = {
54
+ state.agentMiddleware.definitions['myAIMW'] = {
55
55
  services: { optimized: true, services: ['logger'] },
56
56
  sourceFile: '/test/middleware.ts',
57
57
  position: 0,
58
58
  exportedName: 'myAIMW',
59
59
  }
60
60
 
61
- const code = 'const x = { aiMiddleware: [myAIMW] }'
61
+ const code = 'const x = { agentMiddleware: [myAIMW] }'
62
62
  const program = ts.createProgram({
63
63
  rootNames: ['test.ts'],
64
64
  options: { target: ts.ScriptTarget.ESNext },
@@ -77,7 +77,7 @@ describe('resolveAIMiddleware', () => {
77
77
  const sourceFile = program.getSourceFile('test.ts')!
78
78
  const obj = getObjectLiteral(sourceFile)
79
79
 
80
- const result = resolveAIMiddleware(state, obj, checker)
80
+ const result = resolveAgentMiddleware(state, obj, checker)
81
81
 
82
82
  assert.ok(result)
83
83
  assert.strictEqual(result.length, 1)
@@ -88,22 +88,22 @@ describe('resolveAIMiddleware', () => {
88
88
  })
89
89
  })
90
90
 
91
- test('should resolve multiple aiMiddleware entries', () => {
91
+ test('should resolve multiple agentMiddleware entries', () => {
92
92
  const state = getInitialInspectorState('/test')
93
- state.aiMiddleware.definitions['firstMW'] = {
93
+ state.agentMiddleware.definitions['firstMW'] = {
94
94
  services: { optimized: true, services: ['logger'] },
95
95
  sourceFile: '/test/middleware.ts',
96
96
  position: 0,
97
97
  exportedName: 'firstMW',
98
98
  }
99
- state.aiMiddleware.definitions['secondMW'] = {
99
+ state.agentMiddleware.definitions['secondMW'] = {
100
100
  services: { optimized: true, services: [] },
101
101
  sourceFile: '/test/middleware.ts',
102
102
  position: 100,
103
103
  exportedName: 'secondMW',
104
104
  }
105
105
 
106
- const code = 'const x = { aiMiddleware: [firstMW, secondMW] }'
106
+ const code = 'const x = { agentMiddleware: [firstMW, secondMW] }'
107
107
  const program = ts.createProgram({
108
108
  rootNames: ['test.ts'],
109
109
  options: { target: ts.ScriptTarget.ESNext },
@@ -122,7 +122,7 @@ describe('resolveAIMiddleware', () => {
122
122
  const sourceFile = program.getSourceFile('test.ts')!
123
123
  const obj = getObjectLiteral(sourceFile)
124
124
 
125
- const result = resolveAIMiddleware(state, obj, checker)
125
+ const result = resolveAgentMiddleware(state, obj, checker)
126
126
 
127
127
  assert.ok(result)
128
128
  assert.strictEqual(result.length, 2)
@@ -132,16 +132,16 @@ describe('resolveAIMiddleware', () => {
132
132
  assert.strictEqual((result[1] as any).name, 'secondMW')
133
133
  })
134
134
 
135
- test('should mark inline aiMiddleware (null exportedName)', () => {
135
+ test('should mark inline agentMiddleware (null exportedName)', () => {
136
136
  const state = getInitialInspectorState('/test')
137
- state.aiMiddleware.definitions['inlineMW'] = {
137
+ state.agentMiddleware.definitions['inlineMW'] = {
138
138
  services: { optimized: true, services: [] },
139
139
  sourceFile: '/test/middleware.ts',
140
140
  position: 0,
141
141
  exportedName: null,
142
142
  }
143
143
 
144
- const code = 'const x = { aiMiddleware: [inlineMW] }'
144
+ const code = 'const x = { agentMiddleware: [inlineMW] }'
145
145
  const program = ts.createProgram({
146
146
  rootNames: ['test.ts'],
147
147
  options: { target: ts.ScriptTarget.ESNext },
@@ -160,7 +160,7 @@ describe('resolveAIMiddleware', () => {
160
160
  const sourceFile = program.getSourceFile('test.ts')!
161
161
  const obj = getObjectLiteral(sourceFile)
162
162
 
163
- const result = resolveAIMiddleware(state, obj, checker)
163
+ const result = resolveAgentMiddleware(state, obj, checker)
164
164
 
165
165
  assert.ok(result)
166
166
  assert.strictEqual(result.length, 1)
@@ -171,10 +171,10 @@ describe('resolveAIMiddleware', () => {
171
171
  })
172
172
  })
173
173
 
174
- test('should handle aiMiddleware referencing unknown definitions', () => {
174
+ test('should handle agentMiddleware referencing unknown definitions', () => {
175
175
  const state = getInitialInspectorState('/test')
176
176
 
177
- const code = 'const x = { aiMiddleware: [unknownMW] }'
177
+ const code = 'const x = { agentMiddleware: [unknownMW] }'
178
178
  const program = ts.createProgram({
179
179
  rootNames: ['test.ts'],
180
180
  options: { target: ts.ScriptTarget.ESNext },
@@ -193,7 +193,7 @@ describe('resolveAIMiddleware', () => {
193
193
  const sourceFile = program.getSourceFile('test.ts')!
194
194
  const obj = getObjectLiteral(sourceFile)
195
195
 
196
- const result = resolveAIMiddleware(state, obj, checker)
196
+ const result = resolveAgentMiddleware(state, obj, checker)
197
197
 
198
198
  assert.ok(result)
199
199
  assert.strictEqual(result.length, 1)
@@ -1,5 +1,5 @@
1
1
  import * as ts from 'typescript'
2
- import type { MiddlewareMetadata } from '@pikku/core'
2
+ import type { MiddlewareMetadata } from '@pikku/core/ecosystem/types'
3
3
  import { extractFunctionName } from './extract-function-name.js'
4
4
  import type { InspectorState } from '../types.js'
5
5
 
@@ -214,24 +214,24 @@ export function resolveHTTPMiddlewareFromObject(
214
214
  )
215
215
  }
216
216
 
217
- function getAIMiddlewareNode(
217
+ function getAgentMiddlewareNode(
218
218
  obj: ts.ObjectLiteralExpression
219
219
  ): ts.Expression | undefined {
220
220
  const prop = obj.properties.find(
221
221
  (p) =>
222
222
  ts.isPropertyAssignment(p) &&
223
223
  ts.isIdentifier(p.name) &&
224
- p.name.text === 'aiMiddleware'
224
+ p.name.text === 'agentMiddleware'
225
225
  ) as ts.PropertyAssignment | undefined
226
226
  return prop?.initializer
227
227
  }
228
228
 
229
- export function resolveAIMiddleware(
229
+ export function resolveAgentMiddleware(
230
230
  state: InspectorState,
231
231
  obj: ts.ObjectLiteralExpression,
232
232
  checker: ts.TypeChecker
233
233
  ): MiddlewareMetadata[] | undefined {
234
- const explicitNode = getAIMiddlewareNode(obj)
234
+ const explicitNode = getAgentMiddlewareNode(obj)
235
235
  if (!explicitNode) return undefined
236
236
 
237
237
  const names = extractMiddlewarePikkuNames(
@@ -240,7 +240,7 @@ export function resolveAIMiddleware(
240
240
  state.rootDir
241
241
  )
242
242
  const resolved: MiddlewareMetadata[] = names.map((name) => {
243
- const def = state.aiMiddleware.definitions[name]
243
+ const def = state.agentMiddleware.definitions[name]
244
244
  return {
245
245
  type: 'wire' as const,
246
246
  name,
@@ -1,5 +1,5 @@
1
1
  import * as ts from 'typescript'
2
- import type { PermissionMetadata } from '@pikku/core'
2
+ import type { PermissionMetadata } from '@pikku/core/ecosystem/types'
3
3
  import { extractFunctionName } from './extract-function-name.js'
4
4
  import type { InspectorState } from '../types.js'
5
5
 
@@ -59,6 +59,33 @@ const makeOverrideState = (
59
59
  },
60
60
  }) as unknown as Omit<InspectorState, 'typesLookup'>
61
61
 
62
+ const makeGrantState = (
63
+ kind: 'secrets' | 'credentials',
64
+ declaredNames: string[],
65
+ decl: {
66
+ secretGrants?: string[]
67
+ credentialGrants?: string[]
68
+ secretOverrides?: Record<string, string>
69
+ credentialOverrides?: Record<string, string>
70
+ }
71
+ ): Omit<InspectorState, 'typesLookup'> =>
72
+ ({
73
+ rpc: {
74
+ wireAddonDeclarations: new Map([
75
+ ['slack-marketing', { package: '@addon/slack', ...decl }],
76
+ ]),
77
+ },
78
+ secrets: {
79
+ definitions:
80
+ kind === 'secrets' ? declaredNames.map((name) => ({ name })) : [],
81
+ },
82
+ credentials: {
83
+ definitions:
84
+ kind === 'credentials' ? declaredNames.map((name) => ({ name })) : [],
85
+ },
86
+ variables: { definitions: [] },
87
+ }) as unknown as Omit<InspectorState, 'typesLookup'>
88
+
62
89
  function makeState(
63
90
  overrides: {
64
91
  usedFunctions?: string[]
@@ -115,7 +142,7 @@ function makeState(
115
142
 
116
143
  const CONSOLE_PARENT_SERVICES = [
117
144
  'metaService',
118
- 'aiAgentRunner',
145
+ 'agentRunner',
119
146
  'deploymentService',
120
147
  'credentialService',
121
148
  ]
@@ -160,7 +187,7 @@ describe('aggregateRequiredServices — per-function addon services', () => {
160
187
  runAgent: {
161
188
  services: {
162
189
  optimized: true,
163
- services: ['aiAgentRunner', 'deploymentService'],
190
+ services: ['agentRunner', 'deploymentService'],
164
191
  },
165
192
  },
166
193
  },
@@ -170,7 +197,7 @@ describe('aggregateRequiredServices — per-function addon services', () => {
170
197
  aggregateRequiredServices(state)
171
198
  const required = state.serviceAggregation.requiredServices
172
199
  assert.ok(required.has('metaService'))
173
- assert.ok(!required.has('aiAgentRunner'))
200
+ assert.ok(!required.has('agentRunner'))
174
201
  assert.ok(!required.has('deploymentService'))
175
202
  assert.ok(!required.has('credentialService'))
176
203
  })
@@ -231,7 +258,7 @@ describe('aggregateRequiredServices — per-function addon services', () => {
231
258
  const required = state.serviceAggregation.requiredServices
232
259
  assert.ok(required.has('kysely'))
233
260
  assert.ok(!required.has('metaService'))
234
- assert.ok(!required.has('aiAgentRunner'))
261
+ assert.ok(!required.has('agentRunner'))
235
262
  })
236
263
 
237
264
  test('no used addon functions adds no parent services', () => {
@@ -252,7 +279,7 @@ describe('aggregateRequiredServices — per-function addon services', () => {
252
279
  aggregateRequiredServices(state)
253
280
  const required = state.serviceAggregation.requiredServices
254
281
  assert.ok(!required.has('metaService'))
255
- assert.ok(!required.has('aiAgentRunner'))
282
+ assert.ok(!required.has('agentRunner'))
256
283
  })
257
284
 
258
285
  test('internal services in addon function meta never force the blanket', () => {
@@ -273,7 +300,7 @@ describe('aggregateRequiredServices — per-function addon services', () => {
273
300
  aggregateRequiredServices(state)
274
301
  const required = state.serviceAggregation.requiredServices
275
302
  assert.ok(required.has('metaService'))
276
- assert.ok(!required.has('aiAgentRunner'))
303
+ assert.ok(!required.has('agentRunner'))
277
304
  assert.ok(!required.has('rpc'))
278
305
  })
279
306
 
@@ -318,7 +345,7 @@ describe('aggregateRequiredServices — per-function addon services', () => {
318
345
  aggregateRequiredServices(state)
319
346
  const required = state.serviceAggregation.requiredServices
320
347
  assert.ok(required.has('metaService'))
321
- assert.ok(!required.has('aiAgentRunner'))
348
+ assert.ok(!required.has('agentRunner'))
322
349
  })
323
350
 
324
351
  test('multiple used addon functions union their parent services', () => {
@@ -330,7 +357,7 @@ describe('aggregateRequiredServices — per-function addon services', () => {
330
357
  services: { optimized: true, services: ['metaService'] },
331
358
  },
332
359
  runAgent: {
333
- services: { optimized: true, services: ['aiAgentRunner'] },
360
+ services: { optimized: true, services: ['agentRunner'] },
334
361
  },
335
362
  },
336
363
  },
@@ -339,7 +366,7 @@ describe('aggregateRequiredServices — per-function addon services', () => {
339
366
  aggregateRequiredServices(state)
340
367
  const required = state.serviceAggregation.requiredServices
341
368
  assert.ok(required.has('metaService'))
342
- assert.ok(required.has('aiAgentRunner'))
369
+ assert.ok(required.has('agentRunner'))
343
370
  assert.ok(!required.has('deploymentService'))
344
371
  assert.ok(!required.has('credentialService'))
345
372
  })
@@ -414,6 +441,46 @@ describe('override validation resolves the override target (value), not the logi
414
441
  validateVariableOverrides(logger, state)
415
442
  assert.deepEqual(criticals, [])
416
443
  })
444
+
445
+ test('validateSecretOverrides accepts a grant naming a declared secret', () => {
446
+ const { logger, criticals } = makeCriticalLogger()
447
+ const state = makeGrantState('secrets', ['STRIPE_KEY'], {
448
+ secretGrants: ['STRIPE_KEY'],
449
+ })
450
+ validateSecretOverrides(logger, state)
451
+ assert.deepEqual(criticals, [])
452
+ })
453
+
454
+ test('validateSecretOverrides flags a grant naming no declared secret', () => {
455
+ const { logger, criticals } = makeCriticalLogger()
456
+ const state = makeGrantState('secrets', ['STRIPE_KEY'], {
457
+ secretGrants: ['STIRPE_KEY'],
458
+ })
459
+ validateSecretOverrides(logger, state)
460
+ assert.equal(criticals.length, 1)
461
+ assert.match(criticals[0]!.message, /STIRPE_KEY/)
462
+ assert.match(criticals[0]!.message, /STRIPE_KEY/)
463
+ })
464
+
465
+ test('validateSecretOverrides resolves a grant through its override before looking it up', () => {
466
+ const { logger, criticals } = makeCriticalLogger()
467
+ const state = makeGrantState('secrets', ['PROD_EMAIL_KEY'], {
468
+ secretGrants: ['MAILGUN_KEY'],
469
+ secretOverrides: { MAILGUN_KEY: 'PROD_EMAIL_KEY' },
470
+ })
471
+ validateSecretOverrides(logger, state)
472
+ assert.deepEqual(criticals, [])
473
+ })
474
+
475
+ test('validateCredentialOverrides flags a grant naming no declared credential', () => {
476
+ const { logger, criticals } = makeCriticalLogger()
477
+ const state = makeGrantState('credentials', ['marketing_cred'], {
478
+ credentialGrants: ['ghost_cred'],
479
+ })
480
+ validateCredentialOverrides(logger, state)
481
+ assert.equal(criticals.length, 1)
482
+ assert.match(criticals[0]!.message, /ghost_cred/)
483
+ })
417
484
  })
418
485
 
419
486
  const makeRemoteState = (