@pikku/inspector 0.11.2 → 0.12.1

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 (211) hide show
  1. package/CHANGELOG.md +36 -1
  2. package/OPTIMIZATION-PLAN.md +195 -0
  3. package/dist/add/add-ai-agent.d.ts +2 -0
  4. package/dist/add/add-ai-agent.js +314 -0
  5. package/dist/add/add-channel.js +81 -61
  6. package/dist/add/add-cli.d.ts +1 -1
  7. package/dist/add/add-cli.js +42 -19
  8. package/dist/add/add-file-extends-core-type.d.ts +1 -1
  9. package/dist/add/add-file-with-config.d.ts +1 -1
  10. package/dist/add/add-file-with-factory.d.ts +1 -1
  11. package/dist/add/add-file-with-factory.js +2 -0
  12. package/dist/add/add-functions.d.ts +1 -1
  13. package/dist/add/add-functions.js +256 -82
  14. package/dist/add/add-http-route.d.ts +20 -10
  15. package/dist/add/add-http-route.js +156 -66
  16. package/dist/add/add-http-routes.d.ts +5 -0
  17. package/dist/add/add-http-routes.js +160 -0
  18. package/dist/add/add-keyed-wiring.d.ts +12 -0
  19. package/dist/add/add-keyed-wiring.js +97 -0
  20. package/dist/add/add-mcp-prompt.d.ts +1 -1
  21. package/dist/add/add-mcp-prompt.js +14 -9
  22. package/dist/add/add-mcp-resource.d.ts +1 -1
  23. package/dist/add/add-mcp-resource.js +14 -9
  24. package/dist/add/add-middleware.d.ts +1 -4
  25. package/dist/add/add-middleware.js +364 -79
  26. package/dist/add/add-permission.d.ts +1 -1
  27. package/dist/add/add-permission.js +152 -40
  28. package/dist/add/add-queue-worker.d.ts +1 -1
  29. package/dist/add/add-queue-worker.js +18 -12
  30. package/dist/add/add-rpc-invocations.d.ts +3 -3
  31. package/dist/add/add-rpc-invocations.js +24 -10
  32. package/dist/add/add-schedule.d.ts +1 -1
  33. package/dist/add/add-schedule.js +11 -5
  34. package/dist/add/add-secret.d.ts +3 -0
  35. package/dist/add/add-secret.js +82 -0
  36. package/dist/add/add-trigger.d.ts +2 -0
  37. package/dist/add/add-trigger.js +87 -0
  38. package/dist/add/add-variable.d.ts +1 -0
  39. package/dist/add/add-variable.js +8 -0
  40. package/dist/add/add-wire-addon.d.ts +7 -0
  41. package/dist/add/add-wire-addon.js +70 -0
  42. package/dist/add/add-workflow-graph.d.ts +3 -2
  43. package/dist/add/add-workflow-graph.js +143 -406
  44. package/dist/add/add-workflow.d.ts +1 -1
  45. package/dist/add/add-workflow.js +6 -4
  46. package/dist/error-codes.d.ts +15 -1
  47. package/dist/error-codes.js +20 -1
  48. package/dist/index.d.ts +9 -8
  49. package/dist/index.js +5 -4
  50. package/dist/inspector.d.ts +2 -2
  51. package/dist/inspector.js +95 -15
  52. package/dist/schema-generator.d.ts +1 -0
  53. package/dist/schema-generator.js +1 -0
  54. package/dist/types-map.js +10 -1
  55. package/dist/types.d.ts +180 -50
  56. package/dist/utils/compute-required-schemas.d.ts +4 -0
  57. package/dist/utils/compute-required-schemas.js +40 -0
  58. package/dist/utils/contract-hashes.d.ts +52 -0
  59. package/dist/utils/contract-hashes.js +269 -0
  60. package/dist/utils/custom-types-generator.d.ts +9 -0
  61. package/dist/utils/custom-types-generator.js +71 -0
  62. package/dist/utils/detect-schema-vendor.d.ts +22 -0
  63. package/dist/utils/detect-schema-vendor.js +76 -0
  64. package/dist/utils/does-type-extend-core-type.d.ts +1 -1
  65. package/dist/utils/ensure-function-metadata.d.ts +6 -3
  66. package/dist/utils/ensure-function-metadata.js +220 -6
  67. package/dist/utils/extract-function-name.d.ts +5 -16
  68. package/dist/utils/extract-function-name.js +86 -291
  69. package/dist/utils/extract-services.d.ts +2 -1
  70. package/dist/utils/extract-services.js +25 -1
  71. package/dist/utils/filter-inspector-state.d.ts +1 -1
  72. package/dist/utils/filter-inspector-state.js +107 -23
  73. package/dist/utils/filter-utils.d.ts +2 -2
  74. package/dist/utils/get-files-and-methods.d.ts +1 -1
  75. package/dist/utils/get-property-value.d.ts +6 -1
  76. package/dist/utils/get-property-value.js +28 -3
  77. package/dist/utils/hash.d.ts +2 -0
  78. package/dist/utils/hash.js +23 -0
  79. package/dist/utils/middleware.d.ts +9 -32
  80. package/dist/utils/middleware.js +80 -66
  81. package/dist/utils/permissions.d.ts +4 -4
  82. package/dist/utils/permissions.js +10 -10
  83. package/dist/utils/post-process.d.ts +11 -11
  84. package/dist/utils/post-process.js +247 -24
  85. package/dist/utils/resolve-addon-package.d.ts +16 -0
  86. package/dist/utils/resolve-addon-package.js +34 -0
  87. package/dist/utils/resolve-function-types.d.ts +6 -0
  88. package/dist/utils/resolve-function-types.js +29 -0
  89. package/dist/utils/resolve-identifier.d.ts +10 -0
  90. package/dist/utils/resolve-identifier.js +36 -0
  91. package/dist/utils/resolve-versions.d.ts +2 -0
  92. package/dist/utils/resolve-versions.js +78 -0
  93. package/dist/utils/schema-generator.d.ts +9 -0
  94. package/dist/utils/schema-generator.js +209 -0
  95. package/dist/utils/serialize-inspector-state.d.ts +70 -23
  96. package/dist/utils/serialize-inspector-state.js +98 -22
  97. package/dist/utils/serialize-mcp-json.d.ts +2 -0
  98. package/dist/utils/serialize-mcp-json.js +99 -0
  99. package/dist/utils/serialize-middleware-groups-meta.d.ts +12 -0
  100. package/dist/utils/serialize-middleware-groups-meta.js +28 -0
  101. package/dist/utils/serialize-openapi-json.d.ts +85 -0
  102. package/dist/utils/serialize-openapi-json.js +151 -0
  103. package/dist/utils/serialize-permissions-groups-meta.d.ts +6 -0
  104. package/dist/utils/serialize-permissions-groups-meta.js +31 -0
  105. package/dist/utils/validate-auth-sessionless.d.ts +3 -0
  106. package/dist/utils/validate-auth-sessionless.js +14 -0
  107. package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +34 -102
  108. package/dist/utils/workflow/dsl/extract-dsl-workflow.d.ts +1 -1
  109. package/dist/utils/workflow/dsl/extract-dsl-workflow.js +23 -4
  110. package/dist/utils/workflow/graph/convert-dsl-to-graph.js +12 -10
  111. package/dist/utils/workflow/graph/finalize-workflow-wires.d.ts +3 -0
  112. package/dist/utils/workflow/graph/finalize-workflow-wires.js +276 -0
  113. package/dist/utils/workflow/graph/finalize-workflows.d.ts +2 -0
  114. package/dist/utils/workflow/graph/finalize-workflows.js +75 -0
  115. package/dist/utils/workflow/graph/index.d.ts +2 -0
  116. package/dist/utils/workflow/graph/index.js +2 -0
  117. package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +0 -8
  118. package/dist/utils/workflow/graph/serialize-workflow-graph.js +1 -3
  119. package/dist/utils/workflow/graph/workflow-graph.types.d.ts +53 -79
  120. package/dist/utils/workflow/graph/workflow-graph.types.js +1 -1
  121. package/dist/visit.d.ts +1 -1
  122. package/dist/visit.js +13 -6
  123. package/package.json +14 -4
  124. package/src/add/add-ai-agent.ts +468 -0
  125. package/src/add/add-channel.ts +103 -79
  126. package/src/add/add-cli.ts +68 -24
  127. package/src/add/add-file-extends-core-type.ts +1 -1
  128. package/src/add/add-file-with-config.ts +1 -1
  129. package/src/add/add-file-with-factory.ts +3 -1
  130. package/src/add/add-functions.ts +349 -103
  131. package/src/add/add-http-route.ts +263 -89
  132. package/src/add/add-http-routes.ts +229 -0
  133. package/src/add/add-keyed-wiring.ts +151 -0
  134. package/src/add/add-mcp-prompt.ts +27 -16
  135. package/src/add/add-mcp-resource.ts +28 -16
  136. package/src/add/add-middleware.ts +482 -80
  137. package/src/add/add-permission.ts +199 -40
  138. package/src/add/add-queue-worker.ts +25 -20
  139. package/src/add/add-rpc-invocations.ts +28 -11
  140. package/src/add/add-schedule.ts +17 -12
  141. package/src/add/add-secret.ts +140 -0
  142. package/src/add/add-trigger.ts +154 -0
  143. package/src/add/add-variable.ts +9 -0
  144. package/src/add/add-wire-addon.ts +80 -0
  145. package/src/add/add-workflow-graph.ts +180 -522
  146. package/src/add/add-workflow.ts +7 -6
  147. package/src/error-codes.ts +25 -1
  148. package/src/index.ts +23 -13
  149. package/src/inspector.ts +139 -19
  150. package/src/schema-generator.ts +1 -0
  151. package/src/types-map.ts +12 -1
  152. package/src/types.ts +199 -69
  153. package/src/utils/compute-required-schemas.ts +48 -0
  154. package/src/utils/contract-hashes.test.ts +553 -0
  155. package/src/utils/contract-hashes.ts +386 -0
  156. package/src/utils/custom-types-generator.ts +88 -0
  157. package/src/utils/detect-schema-vendor.ts +90 -0
  158. package/src/utils/does-type-extend-core-type.ts +1 -1
  159. package/src/utils/ensure-function-metadata.ts +325 -8
  160. package/src/utils/extract-function-name.ts +101 -351
  161. package/src/utils/extract-services.ts +35 -2
  162. package/src/utils/filter-inspector-state.test.ts +37 -25
  163. package/src/utils/filter-inspector-state.ts +146 -32
  164. package/src/utils/filter-utils.test.ts +1 -1
  165. package/src/utils/filter-utils.ts +2 -2
  166. package/src/utils/get-files-and-methods.ts +1 -1
  167. package/src/utils/get-property-value.ts +42 -4
  168. package/src/utils/hash.ts +26 -0
  169. package/src/utils/middleware.test.ts +204 -0
  170. package/src/utils/middleware.ts +131 -69
  171. package/src/utils/permissions.test.ts +35 -12
  172. package/src/utils/permissions.ts +12 -12
  173. package/src/utils/post-process.ts +306 -44
  174. package/src/utils/resolve-addon-package.ts +49 -0
  175. package/src/utils/resolve-function-types.ts +42 -0
  176. package/src/utils/resolve-identifier.ts +46 -0
  177. package/src/utils/resolve-versions.test.ts +249 -0
  178. package/src/utils/resolve-versions.ts +105 -0
  179. package/src/utils/schema-generator.ts +329 -0
  180. package/src/utils/serialize-inspector-state.ts +184 -43
  181. package/src/utils/serialize-mcp-json.ts +145 -0
  182. package/src/utils/serialize-middleware-groups-meta.ts +33 -0
  183. package/src/utils/serialize-openapi-json.ts +277 -0
  184. package/src/utils/serialize-permissions-groups-meta.ts +35 -0
  185. package/src/utils/test-data/inspector-state.json +69 -66
  186. package/src/utils/validate-auth-sessionless.ts +29 -0
  187. package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +43 -119
  188. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +26 -6
  189. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +17 -10
  190. package/src/utils/workflow/graph/finalize-workflow-wires.ts +310 -0
  191. package/src/utils/workflow/graph/finalize-workflows.ts +100 -0
  192. package/src/utils/workflow/graph/index.ts +5 -0
  193. package/src/utils/workflow/graph/serialize-workflow-graph.ts +1 -8
  194. package/src/utils/workflow/graph/workflow-graph.types.ts +29 -78
  195. package/src/visit.ts +19 -7
  196. package/tsconfig.tsbuildinfo +1 -1
  197. package/dist/add/add-forge-credential.d.ts +0 -8
  198. package/dist/add/add-forge-credential.js +0 -77
  199. package/dist/add/add-forge-node.d.ts +0 -7
  200. package/dist/add/add-forge-node.js +0 -77
  201. package/dist/add/add-mcp-tool.d.ts +0 -2
  202. package/dist/add/add-mcp-tool.js +0 -81
  203. package/dist/utils/extract-service-metadata.d.ts +0 -19
  204. package/dist/utils/extract-service-metadata.js +0 -244
  205. package/dist/utils/write-service-metadata.d.ts +0 -13
  206. package/dist/utils/write-service-metadata.js +0 -37
  207. package/src/add/add-forge-credential.ts +0 -119
  208. package/src/add/add-forge-node.ts +0 -132
  209. package/src/add/add-mcp-tool.ts +0 -141
  210. package/src/utils/extract-service-metadata.ts +0 -353
  211. package/src/utils/write-service-metadata.ts +0 -51
package/dist/visit.js CHANGED
@@ -2,21 +2,24 @@ import * as ts from 'typescript';
2
2
  import { addFileWithFactory } from './add/add-file-with-factory.js';
3
3
  import { addFileExtendsCoreType } from './add/add-file-extends-core-type.js';
4
4
  import { addHTTPRoute } from './add/add-http-route.js';
5
+ import { addHTTPRoutes } from './add/add-http-routes.js';
5
6
  import { addSchedule } from './add/add-schedule.js';
7
+ import { addTrigger } from './add/add-trigger.js';
6
8
  import { addQueueWorker } from './add/add-queue-worker.js';
7
9
  import { addWorkflow } from './add/add-workflow.js';
8
10
  import { addMCPResource } from './add/add-mcp-resource.js';
9
- import { addMCPTool } from './add/add-mcp-tool.js';
10
11
  import { addMCPPrompt } from './add/add-mcp-prompt.js';
11
12
  import { addFunctions } from './add/add-functions.js';
12
13
  import { addChannel } from './add/add-channel.js';
13
14
  import { addRPCInvocations } from './add/add-rpc-invocations.js';
15
+ import { addWireAddon } from './add/add-wire-addon.js';
14
16
  import { addMiddleware } from './add/add-middleware.js';
15
17
  import { addPermission } from './add/add-permission.js';
16
18
  import { addCLI, addCLIRenderers } from './add/add-cli.js';
17
- import { addForgeNode } from './add/add-forge-node.js';
18
- import { addForgeCredential } from './add/add-forge-credential.js';
19
+ import { addSecret, addOAuth2Credential } from './add/add-secret.js';
20
+ import { addVariable } from './add/add-variable.js';
19
21
  import { addWorkflowGraph } from './add/add-workflow-graph.js';
22
+ import { addAIAgent } from './add/add-ai-agent.js';
20
23
  export const visitSetup = (logger, checker, node, state, options) => {
21
24
  addFileExtendsCoreType(node, checker, state.singletonServicesTypeImportMap, 'CoreSingletonServices', state);
22
25
  addFileExtendsCoreType(node, checker, state.wireServicesTypeImportMap, 'CoreServices', state);
@@ -26,6 +29,7 @@ export const visitSetup = (logger, checker, node, state, options) => {
26
29
  addFileWithFactory(node, checker, state.wireServicesFactories, 'CreateWireServices', state);
27
30
  addFileWithFactory(node, checker, state.configFactories, 'CreateConfig');
28
31
  addRPCInvocations(node, state, logger);
32
+ addWireAddon(node, state, logger);
29
33
  addMiddleware(logger, node, checker, state, options);
30
34
  addPermission(logger, node, checker, state, options);
31
35
  addWorkflow(logger, node, checker, state, options);
@@ -33,17 +37,20 @@ export const visitSetup = (logger, checker, node, state, options) => {
33
37
  };
34
38
  export const visitRoutes = (logger, checker, node, state, options) => {
35
39
  addFunctions(logger, node, checker, state, options);
40
+ addSecret(logger, node, checker, state, options);
41
+ addOAuth2Credential(logger, node, checker, state, options);
42
+ addVariable(logger, node, checker, state, options);
36
43
  addHTTPRoute(logger, node, checker, state, options);
44
+ addHTTPRoutes(logger, node, checker, state, options);
37
45
  addSchedule(logger, node, checker, state, options);
46
+ addTrigger(logger, node, checker, state, options);
38
47
  addQueueWorker(logger, node, checker, state, options);
39
48
  addChannel(logger, node, checker, state, options);
40
49
  addCLI(logger, node, checker, state, options);
41
50
  addCLIRenderers(logger, node, checker, state, options);
42
51
  addMCPResource(logger, node, checker, state, options);
43
- addMCPTool(logger, node, checker, state, options);
44
52
  addMCPPrompt(logger, node, checker, state, options);
45
- addForgeNode(logger, node, checker, state, options);
46
- addForgeCredential(logger, node, checker, state, options);
47
53
  addWorkflowGraph(logger, node, checker, state, options);
54
+ addAIAgent(logger, node, checker, state, options);
48
55
  ts.forEachChild(node, (child) => visitRoutes(logger, checker, child, state, options));
49
56
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/inspector",
3
- "version": "0.11.2",
3
+ "version": "0.12.1",
4
4
  "author": "yasser.fadl@gmail.com",
5
5
  "license": "BUSL-1.1",
6
6
  "type": "module",
@@ -12,6 +12,11 @@
12
12
  "import": "./dist/index.js",
13
13
  "default": "./dist/index.js"
14
14
  },
15
+ "./schema-generator": {
16
+ "types": "./dist/schema-generator.d.ts",
17
+ "import": "./dist/schema-generator.js",
18
+ "default": "./dist/schema-generator.js"
19
+ },
15
20
  "./workflow-graph": {
16
21
  "types": "./dist/utils/workflow/graph/index.d.ts",
17
22
  "import": "./dist/utils/workflow/graph/index.js",
@@ -28,12 +33,17 @@
28
33
  "test:coverage": "bash run-tests.sh --coverage"
29
34
  },
30
35
  "dependencies": {
31
- "@pikku/core": "^0.11.2",
36
+ "@openapi-contrib/json-schema-to-openapi-schema": "^4.3.1",
37
+ "@pikku/core": "^0.12.1",
32
38
  "path-to-regexp": "^8.3.0",
33
- "typescript": "^5.9"
39
+ "ts-json-schema-generator": "^2.5.0",
40
+ "tsx": "^4.21.0",
41
+ "typescript": "^5.9",
42
+ "zod": "^4.3.6",
43
+ "zod-to-ts": "^2.0.0"
34
44
  },
35
45
  "devDependencies": {
36
- "@types/node": "^24.10.1"
46
+ "@types/node": "^24.10.12"
37
47
  },
38
48
  "engines": {
39
49
  "node": ">=18"
@@ -0,0 +1,468 @@
1
+ import * as ts from 'typescript'
2
+ import {
3
+ getPropertyValue,
4
+ getCommonWireMetaData,
5
+ } from '../utils/get-property-value.js'
6
+ import { extractWireNames } from '../utils/post-process.js'
7
+ import type { AddWiring, InspectorLogger, SchemaRef } from '../types.js'
8
+ import {
9
+ extractFunctionName,
10
+ funcIdToTypeName,
11
+ } from '../utils/extract-function-name.js'
12
+ import {
13
+ resolveMiddleware,
14
+ resolveChannelMiddleware,
15
+ resolveAIMiddleware,
16
+ } from '../utils/middleware.js'
17
+ import { resolvePermissions } from '../utils/permissions.js'
18
+ import { ErrorCode } from '../error-codes.js'
19
+ import { detectSchemaVendorOrError } from '../utils/detect-schema-vendor.js'
20
+
21
+ function resolveToolReferences(
22
+ obj: ts.ObjectLiteralExpression,
23
+ checker: ts.TypeChecker,
24
+ agentName: string,
25
+ logger: InspectorLogger
26
+ ): string[] | null {
27
+ const property = obj.properties.find(
28
+ (p) =>
29
+ ts.isPropertyAssignment(p) &&
30
+ ts.isIdentifier(p.name) &&
31
+ p.name.text === 'tools'
32
+ )
33
+
34
+ if (!property || !ts.isPropertyAssignment(property)) {
35
+ return null
36
+ }
37
+
38
+ const initializer = property.initializer
39
+ if (!ts.isArrayLiteralExpression(initializer)) {
40
+ return null
41
+ }
42
+
43
+ const resolved: string[] = []
44
+
45
+ for (const element of initializer.elements) {
46
+ if (ts.isStringLiteral(element)) {
47
+ logger.critical(
48
+ ErrorCode.INVALID_VALUE,
49
+ `AI agent '${agentName}' tools array contains a string literal '${element.text}'. ` +
50
+ `Use a function reference instead (e.g., import the pikkuFunc variable).`
51
+ )
52
+ continue
53
+ }
54
+
55
+ if (ts.isCallExpression(element) && ts.isIdentifier(element.expression)) {
56
+ const calleeName = element.expression.text
57
+
58
+ if (calleeName === 'workflow') {
59
+ const [firstArg] = element.arguments
60
+ if (firstArg && ts.isStringLiteral(firstArg)) {
61
+ resolved.push(`workflow:${firstArg.text}`)
62
+ continue
63
+ }
64
+ }
65
+
66
+ if (calleeName === 'addon') {
67
+ const [firstArg] = element.arguments
68
+ if (firstArg && ts.isStringLiteral(firstArg)) {
69
+ resolved.push(firstArg.text)
70
+ continue
71
+ }
72
+ }
73
+ }
74
+
75
+ if (ts.isIdentifier(element)) {
76
+ const rpcName = resolveIdentifierToRpcName(element, checker)
77
+ if (rpcName) {
78
+ resolved.push(rpcName)
79
+ continue
80
+ }
81
+
82
+ logger.critical(
83
+ ErrorCode.INVALID_VALUE,
84
+ `AI agent '${agentName}' tools array contains identifier '${element.text}' ` +
85
+ `that could not be resolved to a pikkuFunc.`
86
+ )
87
+ }
88
+ }
89
+
90
+ return resolved.length > 0 ? resolved : null
91
+ }
92
+
93
+ function resolveAgentReferences(
94
+ obj: ts.ObjectLiteralExpression,
95
+ checker: ts.TypeChecker,
96
+ agentName: string,
97
+ logger: InspectorLogger
98
+ ): string[] | null {
99
+ const property = obj.properties.find(
100
+ (p) =>
101
+ ts.isPropertyAssignment(p) &&
102
+ ts.isIdentifier(p.name) &&
103
+ p.name.text === 'agents'
104
+ )
105
+
106
+ if (!property || !ts.isPropertyAssignment(property)) {
107
+ return null
108
+ }
109
+
110
+ const initializer = property.initializer
111
+ if (!ts.isArrayLiteralExpression(initializer)) {
112
+ return null
113
+ }
114
+
115
+ const resolved: string[] = []
116
+
117
+ for (const element of initializer.elements) {
118
+ if (ts.isStringLiteral(element)) {
119
+ logger.critical(
120
+ ErrorCode.INVALID_VALUE,
121
+ `AI agent '${agentName}' agents array contains a string literal '${element.text}'. ` +
122
+ `Use an agent reference instead (e.g., import the pikkuAIAgent variable).`
123
+ )
124
+ continue
125
+ }
126
+
127
+ if (ts.isIdentifier(element)) {
128
+ const name = resolveIdentifierToAgentName(element, checker)
129
+ if (name) {
130
+ resolved.push(name)
131
+ continue
132
+ }
133
+
134
+ logger.critical(
135
+ ErrorCode.INVALID_VALUE,
136
+ `AI agent '${agentName}' agents array contains identifier '${element.text}' ` +
137
+ `that could not be resolved to a pikkuAIAgent.`
138
+ )
139
+ }
140
+ }
141
+
142
+ return resolved.length > 0 ? resolved : null
143
+ }
144
+
145
+ function resolveIdentifierToRpcName(
146
+ identifier: ts.Identifier,
147
+ checker: ts.TypeChecker
148
+ ): string | null {
149
+ const symbol = checker.getSymbolAtLocation(identifier)
150
+ if (!symbol) return null
151
+
152
+ let resolved = symbol
153
+ if (resolved.flags & ts.SymbolFlags.Alias) {
154
+ resolved = checker.getAliasedSymbol(resolved) ?? resolved
155
+ }
156
+
157
+ const decl = resolved.valueDeclaration ?? resolved.declarations?.[0]
158
+ if (!decl) return null
159
+
160
+ if (ts.isVariableDeclaration(decl) && decl.initializer) {
161
+ if (
162
+ ts.isCallExpression(decl.initializer) &&
163
+ ts.isIdentifier(decl.initializer.expression)
164
+ ) {
165
+ const callName = decl.initializer.expression.text
166
+ if (
167
+ callName === 'pikkuFunc' ||
168
+ callName === 'pikkuSessionlessFunc' ||
169
+ callName === 'pikkuVoidFunc'
170
+ ) {
171
+ const firstArg = decl.initializer.arguments[0]
172
+ if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
173
+ for (const prop of firstArg.properties) {
174
+ if (
175
+ ts.isPropertyAssignment(prop) &&
176
+ ts.isIdentifier(prop.name) &&
177
+ prop.name.text === 'override' &&
178
+ ts.isStringLiteral(prop.initializer)
179
+ ) {
180
+ return prop.initializer.text
181
+ }
182
+ }
183
+ }
184
+
185
+ if (ts.isIdentifier(decl.name)) {
186
+ return decl.name.text
187
+ }
188
+ }
189
+ }
190
+ }
191
+
192
+ return null
193
+ }
194
+
195
+ function resolveIdentifierToAgentName(
196
+ identifier: ts.Identifier,
197
+ checker: ts.TypeChecker
198
+ ): string | null {
199
+ const symbol = checker.getSymbolAtLocation(identifier)
200
+ if (!symbol) return null
201
+
202
+ let resolved = symbol
203
+ if (resolved.flags & ts.SymbolFlags.Alias) {
204
+ resolved = checker.getAliasedSymbol(resolved) ?? resolved
205
+ }
206
+
207
+ const decl = resolved.valueDeclaration ?? resolved.declarations?.[0]
208
+ if (!decl) return null
209
+
210
+ if (ts.isVariableDeclaration(decl) && decl.initializer) {
211
+ if (
212
+ ts.isCallExpression(decl.initializer) &&
213
+ ts.isIdentifier(decl.initializer.expression) &&
214
+ decl.initializer.expression.text === 'pikkuAIAgent'
215
+ ) {
216
+ if (ts.isIdentifier(decl.name)) {
217
+ return decl.name.text
218
+ }
219
+ }
220
+ }
221
+
222
+ return null
223
+ }
224
+
225
+ export const addAIAgent: AddWiring = (
226
+ logger,
227
+ node,
228
+ checker,
229
+ state,
230
+ options
231
+ ) => {
232
+ if (!ts.isCallExpression(node)) {
233
+ return
234
+ }
235
+
236
+ const args = node.arguments
237
+ const firstArg = args[0]
238
+ const expression = node.expression
239
+
240
+ if (!ts.isIdentifier(expression) || expression.text !== 'pikkuAIAgent') {
241
+ return
242
+ }
243
+
244
+ if (!firstArg) {
245
+ return
246
+ }
247
+
248
+ const { exportedName } = extractFunctionName(node, checker, state.rootDir)
249
+
250
+ if (ts.isObjectLiteralExpression(firstArg)) {
251
+ const obj = firstArg
252
+
253
+ const nameValue = getPropertyValue(obj, 'name') as string | null
254
+ const { disabled, tags, summary, description, errors } =
255
+ getCommonWireMetaData(obj, 'AI agent', nameValue, logger)
256
+
257
+ if (disabled) return
258
+
259
+ const modelValue = getPropertyValue(obj, 'model') as string | null
260
+
261
+ const instructionsValue = getPropertyValue(obj, 'instructions') as
262
+ | string
263
+ | string[]
264
+ | null
265
+
266
+ const maxStepsValue = getPropertyValue(obj, 'maxSteps') as number | null
267
+ const temperatureValue = getPropertyValue(obj, 'temperature') as
268
+ | number
269
+ | null
270
+ const toolChoiceValue = getPropertyValue(obj, 'toolChoice') as string | null
271
+ const toolsValue = resolveToolReferences(
272
+ obj,
273
+ checker,
274
+ nameValue || '',
275
+ logger
276
+ )
277
+
278
+ if (toolsValue) {
279
+ for (const toolName of toolsValue) {
280
+ if (toolName.startsWith('workflow:') || toolName.includes(':')) continue
281
+ const funcFile = state.functions.files.get(toolName)
282
+ if (funcFile && !state.rpc.internalFiles.has(toolName)) {
283
+ state.rpc.internalFiles.set(toolName, funcFile)
284
+ }
285
+ }
286
+ }
287
+
288
+ const agentsValue = resolveAgentReferences(
289
+ obj,
290
+ checker,
291
+ nameValue || '',
292
+ logger
293
+ )
294
+
295
+ if (!nameValue) {
296
+ logger.critical(
297
+ ErrorCode.MISSING_NAME,
298
+ "AI agent is missing the required 'name' property."
299
+ )
300
+ return
301
+ }
302
+
303
+ const agentKey = exportedName || nameValue
304
+
305
+ if (!description) {
306
+ logger.critical(
307
+ ErrorCode.MISSING_DESCRIPTION,
308
+ `AI agent '${nameValue}' is missing a description.`
309
+ )
310
+ return
311
+ }
312
+
313
+ const resolveSchemaRef = (
314
+ identifier: ts.Identifier,
315
+ context: string
316
+ ): SchemaRef | null => {
317
+ const symbol = checker.getSymbolAtLocation(identifier)
318
+ if (!symbol) return null
319
+
320
+ const decl = symbol.valueDeclaration || symbol.declarations?.[0]
321
+ if (!decl) return null
322
+
323
+ let sourceFile: string
324
+
325
+ if (ts.isImportSpecifier(decl)) {
326
+ const aliasedSymbol = checker.getAliasedSymbol(symbol)
327
+ if (aliasedSymbol) {
328
+ const aliasedDecl =
329
+ aliasedSymbol.valueDeclaration || aliasedSymbol.declarations?.[0]
330
+ if (aliasedDecl) {
331
+ sourceFile = aliasedDecl.getSourceFile().fileName
332
+ } else {
333
+ return null
334
+ }
335
+ } else {
336
+ return null
337
+ }
338
+ } else {
339
+ sourceFile = decl.getSourceFile().fileName
340
+ }
341
+
342
+ const vendor = detectSchemaVendorOrError(
343
+ identifier,
344
+ checker,
345
+ logger,
346
+ context,
347
+ sourceFile
348
+ )
349
+ if (!vendor) return null
350
+
351
+ return {
352
+ variableName: identifier.text,
353
+ sourceFile,
354
+ vendor,
355
+ }
356
+ }
357
+
358
+ let inputSchema: string | null = null
359
+ let outputSchema: string | null = null
360
+ let workingMemorySchema: string | null = null
361
+ const capitalizedName = funcIdToTypeName(agentKey)
362
+
363
+ for (const prop of obj.properties) {
364
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
365
+ const propName = prop.name.text
366
+ if (propName === 'input' || propName === 'output') {
367
+ if (ts.isIdentifier(prop.initializer)) {
368
+ const context = `AI agent '${nameValue}' ${propName}`
369
+ const ref = resolveSchemaRef(prop.initializer, context)
370
+ if (ref) {
371
+ const schemaName = `${capitalizedName}${propName.charAt(0).toUpperCase() + propName.slice(1)}`
372
+ state.schemaLookup.set(schemaName, ref)
373
+ state.functions.typesMap.addCustomType(schemaName, 'unknown', [])
374
+ if (propName === 'input') {
375
+ inputSchema = schemaName
376
+ } else {
377
+ outputSchema = schemaName
378
+ }
379
+ }
380
+ } else if (ts.isCallExpression(prop.initializer)) {
381
+ const schemaName = `${capitalizedName}${propName.charAt(0).toUpperCase() + propName.slice(1)}`
382
+ logger.critical(
383
+ ErrorCode.INLINE_SCHEMA,
384
+ `Inline schemas are not supported for '${propName}' in AI agent '${nameValue}'.\n` +
385
+ ` Extract to an exported variable:\n` +
386
+ ` export const ${schemaName} = ${prop.initializer.getText()}\n` +
387
+ ` Then use: ${propName}: ${schemaName}`
388
+ )
389
+ }
390
+ } else if (
391
+ propName === 'memory' &&
392
+ ts.isObjectLiteralExpression(prop.initializer)
393
+ ) {
394
+ for (const memProp of prop.initializer.properties) {
395
+ if (
396
+ ts.isPropertyAssignment(memProp) &&
397
+ ts.isIdentifier(memProp.name) &&
398
+ memProp.name.text === 'workingMemory' &&
399
+ ts.isIdentifier(memProp.initializer)
400
+ ) {
401
+ const context = `AI agent '${nameValue}' workingMemory`
402
+ const ref = resolveSchemaRef(memProp.initializer, context)
403
+ if (ref) {
404
+ const schemaName = `${capitalizedName}WorkingMemory`
405
+ state.schemaLookup.set(schemaName, ref)
406
+ state.functions.typesMap.addCustomType(
407
+ schemaName,
408
+ 'unknown',
409
+ []
410
+ )
411
+ workingMemorySchema = schemaName
412
+ }
413
+ }
414
+ }
415
+ }
416
+ }
417
+ }
418
+
419
+ const middleware = resolveMiddleware(state, obj, tags, checker)
420
+ const channelMiddleware = resolveChannelMiddleware(
421
+ state,
422
+ obj,
423
+ tags,
424
+ checker
425
+ )
426
+ const aiMiddleware = resolveAIMiddleware(state, obj, checker)
427
+ const permissions = resolvePermissions(state, obj, tags, checker)
428
+
429
+ state.serviceAggregation.usedFunctions.add(agentKey)
430
+ extractWireNames(middleware).forEach((name) =>
431
+ state.serviceAggregation.usedMiddleware.add(name)
432
+ )
433
+ extractWireNames(permissions).forEach((name) =>
434
+ state.serviceAggregation.usedPermissions.add(name)
435
+ )
436
+
437
+ if (exportedName) {
438
+ state.agents.files.set(agentKey, {
439
+ path: node.getSourceFile().fileName,
440
+ exportedName,
441
+ })
442
+ }
443
+
444
+ state.agents.agentsMeta[agentKey] = {
445
+ name: nameValue,
446
+ description,
447
+ instructions: instructionsValue || '',
448
+ model: modelValue || '',
449
+ summary,
450
+ errors,
451
+ ...(maxStepsValue !== null && { maxSteps: maxStepsValue }),
452
+ ...(temperatureValue !== null && { temperature: temperatureValue }),
453
+ ...(toolChoiceValue !== null && {
454
+ toolChoice: toolChoiceValue as 'auto' | 'required' | 'none',
455
+ }),
456
+ ...(toolsValue !== null && { tools: toolsValue }),
457
+ ...(agentsValue !== null && { agents: agentsValue }),
458
+ tags,
459
+ inputSchema,
460
+ outputSchema,
461
+ workingMemorySchema,
462
+ middleware,
463
+ channelMiddleware,
464
+ aiMiddleware,
465
+ permissions,
466
+ }
467
+ }
468
+ }