@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
package/CHANGELOG.md CHANGED
@@ -1,3 +1,130 @@
1
+ ## 0.12.60
2
+
3
+ ### Patch Changes
4
+
5
+ - 7406bfe: Rename the agent runtime from `AI*` to `Agent*` (#596)
6
+
7
+ `AI` described the model provider, not the thing being named. Every symbol that
8
+ belongs to the agent runtime now says `Agent`; the symbols that genuinely wrap a
9
+ model provider — `AIEmbeddingService`, `AIProviderOptions`, `AIEmbedParams`,
10
+ `AITranscriptionParams`, `AIGenerateImageParams` and their siblings, and the
11
+ `@pikku/ai-vercel` / `@pikku/ai-deepinfra` / `@pikku/ai-voice` packages — keep
12
+ their names.
13
+
14
+ **Wiring**
15
+ - `pikkuAIAgent` → `pikkuAgent`, `pikkuAIScorer` → `pikkuAgentScorer`,
16
+ `pikkuAIJudge` → `pikkuAgentJudge`
17
+ - `CoreAIAgent` → `CoreAgent`, `AIAgentInput` → `AgentInput`, `AIAgentStep` →
18
+ `AgentStep`, `AIMessage` → `AgentMessage`, and the rest of the agent types
19
+ - `AIAgentRunnerService` → `AgentRunnerService`, `AIStorageService` →
20
+ `AgentStorageService`, `AIRunStateService` → `AgentRunStateService`
21
+
22
+ **Entry points**
23
+
24
+ `@pikku/core/agent` → `@pikku/core/agent`, `@pikku/core/agent-scorer` →
25
+ `@pikku/core/agent-scorer`.
26
+
27
+ **Queues**
28
+
29
+ The scorer queues are now `agent-score-fast` and `agent-score-slow`. Drain the
30
+ old `ai-score-fast` / `ai-score-slow` queues before deploying — jobs still
31
+ sitting on them when the new workers start will never be picked up.
32
+
33
+ **Scaffolds**
34
+
35
+ The agent scaffold pikku wrote for your project — `<scaffold>/agent/agent.gen.ts`
36
+ and its schemas file — imports `@pikku/core/ai-agent`, which no longer exists. A
37
+ scaffold is normally written once and then left alone, so `pikku all` would find
38
+ it present and leave the broken import in place. It now deletes an agent scaffold
39
+ importing either removed entry point and regenerates it in the same run. Anything
40
+ you added to that file goes with it, so move local edits out first.
41
+
42
+ **Database**
43
+
44
+ The agent tables are renamed: `ai_threads`, `ai_message`, `ai_tool_call`,
45
+ `ai_working_memory`, `ai_run` and `ai_run_score` become `agent_threads`,
46
+ `agent_message`, `agent_tool_call`, `agent_working_memory`, `agent_run` and
47
+ `agent_run_score`, along with their indexes and the `ai_working_memory_pk`
48
+ constraint. The same rename applies to the MongoDB collections.
49
+
50
+ `ensurePikkuSchema` creates tables it cannot find, so an existing database will
51
+ get empty `agent_*` tables and leave the old data stranded in `ai_*`. Rename
52
+ them before the first boot on the new version:
53
+
54
+ ```sql
55
+ ALTER TABLE ai_threads RENAME TO agent_threads;
56
+ ALTER TABLE ai_message RENAME TO agent_message;
57
+ ALTER TABLE ai_tool_call RENAME TO agent_tool_call;
58
+ ALTER TABLE ai_working_memory RENAME TO agent_working_memory;
59
+ ALTER TABLE ai_run RENAME TO agent_run;
60
+ ALTER TABLE ai_run_score RENAME TO agent_run_score;
61
+ ```
62
+
63
+ - 6794681: Publish the ecosystem surface as per-area sub-barrels under `@pikku/core/ecosystem/*`, and point generated code, the CLI, the inspector and the runtime adapters at them.
64
+
65
+ 348 names that only generated code, the toolchain or a runtime adapter ever imports now have a second home on `@pikku/core/ecosystem/<area>` — one sub-barrel per area, matching how core already publishes its entrypoints, so no single barrel grows without bound and a consumer only pulls in the area it uses.
66
+
67
+ This step is additive: every name is still exported from the entrypoint it was published from before, so nothing downstream breaks. Removing them from the app-facing barrels is a later change, and needs a release carrying `./ecosystem/*` first.
68
+
69
+ - Updated dependencies [7406bfe]
70
+ - Updated dependencies [6794681]
71
+ - @pikku/core@0.12.84
72
+
73
+ ## 0.12.59
74
+
75
+ ### Patch Changes
76
+
77
+ - 02c4fe5: fix(core,inspector): let a host grant an addon secrets it could not declare
78
+
79
+ Scoping an addon's `SecretService` to its `declaredSecrets` left generic addons
80
+ with nothing readable: `declaredSecrets` is derived from the addon package's own
81
+ source, but the secrets an addon like `@pikku/addon-graph` reads are named by the
82
+ consuming app's workflow nodes at runtime. Every authenticated `graph:httpRequest`
83
+ threw.
84
+
85
+ `wireAddon` now takes `secretGrants: string[]` and `credentialGrants: string[]`,
86
+ completing the grant family alongside `secretOverrides` (grant + rename) and
87
+ `globalSecrets` (grant everything, with a reason). Grants name the secret as the
88
+ addon reads it, since the scope check runs before the override map renames it —
89
+ which is also why an override's key grants and its value does not.
90
+
91
+ A grant naming a secret the project does not declare is an `INVALID_VALUE`
92
+ critical at codegen, resolved through the override map before lookup.
93
+
94
+ - 438b776: Move the scenario and feature surface off `@pikku/core/workflow` and onto
95
+ `@pikku/core/scenario`. Scenarios extend workflows, so the production workflow
96
+ wiring no longer names a scenario module in its import graph. Feature and
97
+ scenario types are declared in their own `scenario.types.ts` rather than in
98
+ `workflow.types.ts`. Import `requireActor`, `requireScenarioEnv`, `pollUntil`,
99
+ `createCookieJar`, `addFeature`, `ScenarioHttpResponse` and the rest from
100
+ `@pikku/core/scenario`; `HttpPersonasConfig` now comes from
101
+ `@pikku/core/persona` rather than `@pikku/core/services`.
102
+ - ad63f47: feat(cli): warn before codegen when a linked dependency splits a package's type identity
103
+
104
+ `pikku all` now runs the split-type-identity check as a preflight, beside the
105
+ existing `@pikku/core` one, and warns with `PKU719` naming each package, both
106
+ versions and both paths.
107
+
108
+ It has to run _before_ the work rather than after it fails. The failure it
109
+ explains is a V8 heap OOM, which aborts the process — `process.on('exit')`,
110
+ `uncaughtException` and `finally` never run, so nothing printed after the fact is
111
+ ever seen. By the time there is a symptom, the only thing that can help is
112
+ already on screen above it. Without this the user sees a codegen step that dies
113
+ of memory pressure with no indication that two copies of one package are the
114
+ reason, and the obvious next move is to raise `--max-old-space-size`, which
115
+ hides it further.
116
+
117
+ Warns rather than throws: a skewed linked dependency is a strong signal, not a
118
+ certainty, and refusing to build on a heuristic would break working setups.
119
+ `PIKKU_SKIP_TYPE_IDENTITY_CHECK=1` silences it, matching
120
+ `PIKKU_ALLOW_DUPLICATE_CORE`. It also swallows its own errors — it runs on every
121
+ codegen, so it must never be the reason a build stops.
122
+
123
+ - Updated dependencies [02c4fe5]
124
+ - Updated dependencies [438b776]
125
+ - Updated dependencies [438b776]
126
+ - @pikku/core@0.12.83
127
+
1
128
  ## 0.12.58
2
129
 
3
130
  ### Patch Changes
@@ -0,0 +1,2 @@
1
+ import type { AddWiring } from '../types.js';
2
+ export declare const addAgentScorer: AddWiring;
@@ -0,0 +1,61 @@
1
+ import * as ts from 'typescript';
2
+ import { getPropertyValue } from '../utils/get-property-value.js';
3
+ import { extractFunctionName } from '../utils/extract-function-name.js';
4
+ import { ErrorCode } from '../error-codes.js';
5
+ /**
6
+ * `pikkuAgentScorer` grades in code and `pikkuAgentJudge` grades with a model, which
7
+ * is the whole difference between the two lanes — so the lane is read off which
8
+ * constructor was called rather than from a field an author could contradict.
9
+ */
10
+ const LANES = {
11
+ pikkuAgentScorer: 'fast',
12
+ pikkuAgentJudge: 'slow',
13
+ };
14
+ export const addAgentScorer = (logger, node, checker, state) => {
15
+ if (!ts.isCallExpression(node))
16
+ return;
17
+ const expression = node.expression;
18
+ if (!ts.isIdentifier(expression))
19
+ return;
20
+ const lane = LANES[expression.text];
21
+ if (!lane)
22
+ return;
23
+ const firstArg = node.arguments[0];
24
+ if (!firstArg || !ts.isObjectLiteralExpression(firstArg))
25
+ return;
26
+ const obj = firstArg;
27
+ const { exportedName } = extractFunctionName(node, checker, state.rootDir);
28
+ const nameValue = getPropertyValue(obj, 'name');
29
+ if (!nameValue) {
30
+ logger.critical(ErrorCode.MISSING_NAME, "AI scorer is missing the required 'name' property.");
31
+ return;
32
+ }
33
+ const description = getPropertyValue(obj, 'description');
34
+ if (!description) {
35
+ logger.critical(ErrorCode.MISSING_DESCRIPTION, `AI scorer '${nameValue}' is missing the required 'description' property.`);
36
+ return;
37
+ }
38
+ const sampleRateValue = getPropertyValue(obj, 'sampleRate');
39
+ if (sampleRateValue !== null &&
40
+ (sampleRateValue < 0 || sampleRateValue > 1)) {
41
+ logger.critical(ErrorCode.INVALID_VALUE, `AI scorer '${nameValue}' has a sampleRate of ${sampleRateValue} — it is the fraction of live runs to grade, so it must be between 0 and 1.`);
42
+ return;
43
+ }
44
+ const requiresReference = getPropertyValue(obj, 'requiresReference') ?? false;
45
+ const scorerKey = exportedName || nameValue;
46
+ if (exportedName) {
47
+ state.scorers.files.set(scorerKey, {
48
+ path: node.getSourceFile().fileName,
49
+ exportedName,
50
+ });
51
+ }
52
+ state.scorers.scorersMeta[scorerKey] = {
53
+ name: nameValue,
54
+ description,
55
+ lane,
56
+ sampleRate: sampleRateValue ?? 1,
57
+ requiresReference,
58
+ sourceFile: node.getSourceFile().fileName,
59
+ exportedName: exportedName || undefined,
60
+ };
61
+ };
@@ -0,0 +1,4 @@
1
+ import * as ts from 'typescript';
2
+ import type { AddWiring, InspectorLogger } from '../types.js';
3
+ export declare function resolveWorkflowReferences(obj: ts.ObjectLiteralExpression, checker: ts.TypeChecker, agentName: string, logger: InspectorLogger): string[] | null;
4
+ export declare const addAgent: AddWiring;
@@ -0,0 +1,442 @@
1
+ import * as ts from 'typescript';
2
+ import { getPropertyValue, getCommonWireMetaData, } from '../utils/get-property-value.js';
3
+ import { extractWireNames } from '../utils/post-process.js';
4
+ import { extractFunctionName, funcIdToTypeName, } from '../utils/extract-function-name.js';
5
+ import { resolveMiddleware, resolveChannelMiddleware, resolveAgentMiddleware, } from '../utils/middleware.js';
6
+ import { resolvePermissions } from '../utils/permissions.js';
7
+ import { ErrorCode } from '../error-codes.js';
8
+ import { detectSchemaVendorOrError } from '../utils/detect-schema-vendor.js';
9
+ function resolveToolReferences(obj, checker, agentName, logger) {
10
+ const property = obj.properties.find((p) => ts.isPropertyAssignment(p) &&
11
+ ts.isIdentifier(p.name) &&
12
+ p.name.text === 'tools');
13
+ if (!property || !ts.isPropertyAssignment(property)) {
14
+ return null;
15
+ }
16
+ const initializer = property.initializer;
17
+ if (!ts.isArrayLiteralExpression(initializer)) {
18
+ return null;
19
+ }
20
+ const resolved = [];
21
+ for (const element of initializer.elements) {
22
+ if (ts.isStringLiteral(element)) {
23
+ logger.critical(ErrorCode.INVALID_VALUE, `AI agent '${agentName}' tools array contains a string literal '${element.text}'. ` +
24
+ `Use a function reference instead (e.g., import the pikkuFunc variable).`);
25
+ continue;
26
+ }
27
+ if (ts.isCallExpression(element) && ts.isIdentifier(element.expression)) {
28
+ const calleeName = element.expression.text;
29
+ if (calleeName === 'workflow') {
30
+ const [firstArg] = element.arguments;
31
+ if (firstArg && ts.isStringLiteral(firstArg)) {
32
+ resolved.push(`workflow:${firstArg.text}`);
33
+ continue;
34
+ }
35
+ }
36
+ if (calleeName === 'ref') {
37
+ const [firstArg] = element.arguments;
38
+ if (firstArg && ts.isStringLiteral(firstArg)) {
39
+ resolved.push(firstArg.text);
40
+ continue;
41
+ }
42
+ }
43
+ }
44
+ if (ts.isIdentifier(element)) {
45
+ const rpcName = resolveIdentifierToRpcName(element, checker);
46
+ if (rpcName) {
47
+ resolved.push(rpcName);
48
+ continue;
49
+ }
50
+ logger.critical(ErrorCode.INVALID_VALUE, `AI agent '${agentName}' tools array contains identifier '${element.text}' ` +
51
+ `that could not be resolved to a pikkuFunc.`);
52
+ }
53
+ }
54
+ return resolved.length > 0 ? resolved : null;
55
+ }
56
+ function resolveAgentReferences(obj, checker, agentName, logger) {
57
+ const property = obj.properties.find((p) => ts.isPropertyAssignment(p) &&
58
+ ts.isIdentifier(p.name) &&
59
+ p.name.text === 'agents');
60
+ if (!property || !ts.isPropertyAssignment(property)) {
61
+ return null;
62
+ }
63
+ const initializer = property.initializer;
64
+ if (!ts.isArrayLiteralExpression(initializer)) {
65
+ return null;
66
+ }
67
+ const resolved = [];
68
+ for (const element of initializer.elements) {
69
+ if (ts.isStringLiteral(element)) {
70
+ logger.critical(ErrorCode.INVALID_VALUE, `AI agent '${agentName}' agents array contains a string literal '${element.text}'. ` +
71
+ `Use an agent reference instead (e.g., import the pikkuAgent variable).`);
72
+ continue;
73
+ }
74
+ if (ts.isIdentifier(element)) {
75
+ const name = resolveIdentifierToAgentName(element, checker);
76
+ if (name) {
77
+ resolved.push(name);
78
+ continue;
79
+ }
80
+ logger.critical(ErrorCode.INVALID_VALUE, `AI agent '${agentName}' agents array contains identifier '${element.text}' ` +
81
+ `that could not be resolved to a pikkuAgent.`);
82
+ }
83
+ }
84
+ return resolved.length > 0 ? resolved : null;
85
+ }
86
+ export function resolveWorkflowReferences(obj, checker, agentName, logger) {
87
+ const property = obj.properties.find((p) => ts.isPropertyAssignment(p) &&
88
+ ts.isIdentifier(p.name) &&
89
+ p.name.text === 'workflows');
90
+ if (!property || !ts.isPropertyAssignment(property)) {
91
+ return null;
92
+ }
93
+ const initializer = property.initializer;
94
+ if (!ts.isArrayLiteralExpression(initializer)) {
95
+ return null;
96
+ }
97
+ const resolved = [];
98
+ for (const element of initializer.elements) {
99
+ if (ts.isStringLiteral(element)) {
100
+ logger.critical(ErrorCode.INVALID_VALUE, `AI agent '${agentName}' workflows array contains a string literal '${element.text}'. ` +
101
+ `Use a workflow reference instead (e.g., ref('<workflowName>') or import the pikkuWorkflowGraph variable).`);
102
+ continue;
103
+ }
104
+ if (ts.isCallExpression(element) && ts.isIdentifier(element.expression)) {
105
+ const calleeName = element.expression.text;
106
+ if (calleeName === 'ref' || calleeName === 'workflow') {
107
+ const [firstArg] = element.arguments;
108
+ if (firstArg && ts.isStringLiteral(firstArg)) {
109
+ resolved.push(firstArg.text);
110
+ continue;
111
+ }
112
+ }
113
+ }
114
+ if (ts.isIdentifier(element)) {
115
+ const name = resolveIdentifierToWorkflowName(element, checker);
116
+ if (name) {
117
+ resolved.push(name);
118
+ continue;
119
+ }
120
+ logger.critical(ErrorCode.INVALID_VALUE, `AI agent '${agentName}' workflows array contains identifier '${element.text}' ` +
121
+ `that could not be resolved to a pikkuWorkflowGraph.`);
122
+ }
123
+ }
124
+ return resolved.length > 0 ? resolved : null;
125
+ }
126
+ function resolveIdentifierToWorkflowName(identifier, checker) {
127
+ const symbol = checker.getSymbolAtLocation(identifier);
128
+ if (!symbol)
129
+ return null;
130
+ let resolved = symbol;
131
+ if (resolved.flags & ts.SymbolFlags.Alias) {
132
+ resolved = checker.getAliasedSymbol(resolved) ?? resolved;
133
+ }
134
+ const decl = resolved.valueDeclaration ?? resolved.declarations?.[0];
135
+ if (!decl)
136
+ return null;
137
+ if (ts.isVariableDeclaration(decl) && decl.initializer) {
138
+ if (ts.isCallExpression(decl.initializer) &&
139
+ ts.isIdentifier(decl.initializer.expression) &&
140
+ (decl.initializer.expression.text === 'pikkuWorkflowGraph' ||
141
+ decl.initializer.expression.text === 'pikkuWorkflow' ||
142
+ decl.initializer.expression.text === 'pikkuWorkflowComplex')) {
143
+ const firstArg = decl.initializer.arguments[0];
144
+ if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
145
+ for (const prop of firstArg.properties) {
146
+ if (ts.isPropertyAssignment(prop) &&
147
+ ts.isIdentifier(prop.name) &&
148
+ prop.name.text === 'name' &&
149
+ ts.isStringLiteral(prop.initializer)) {
150
+ return prop.initializer.text;
151
+ }
152
+ }
153
+ }
154
+ if (ts.isIdentifier(decl.name)) {
155
+ return decl.name.text;
156
+ }
157
+ }
158
+ }
159
+ return null;
160
+ }
161
+ function resolveIdentifierToRpcName(identifier, checker) {
162
+ const symbol = checker.getSymbolAtLocation(identifier);
163
+ if (!symbol)
164
+ return null;
165
+ let resolved = symbol;
166
+ if (resolved.flags & ts.SymbolFlags.Alias) {
167
+ resolved = checker.getAliasedSymbol(resolved) ?? resolved;
168
+ }
169
+ const decl = resolved.valueDeclaration ?? resolved.declarations?.[0];
170
+ if (!decl)
171
+ return null;
172
+ if (ts.isVariableDeclaration(decl) && decl.initializer) {
173
+ if (ts.isCallExpression(decl.initializer) &&
174
+ ts.isIdentifier(decl.initializer.expression)) {
175
+ const callName = decl.initializer.expression.text;
176
+ if (callName === 'pikkuFunc' ||
177
+ callName === 'pikkuSessionlessFunc' ||
178
+ callName === 'pikkuVoidFunc') {
179
+ const firstArg = decl.initializer.arguments[0];
180
+ if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
181
+ for (const prop of firstArg.properties) {
182
+ if (ts.isPropertyAssignment(prop) &&
183
+ ts.isIdentifier(prop.name) &&
184
+ prop.name.text === 'override' &&
185
+ ts.isStringLiteral(prop.initializer)) {
186
+ return prop.initializer.text;
187
+ }
188
+ }
189
+ }
190
+ if (ts.isIdentifier(decl.name)) {
191
+ return decl.name.text;
192
+ }
193
+ }
194
+ }
195
+ }
196
+ return null;
197
+ }
198
+ function resolveIdentifierToAgentName(identifier, checker) {
199
+ const symbol = checker.getSymbolAtLocation(identifier);
200
+ if (!symbol)
201
+ return null;
202
+ let resolved = symbol;
203
+ if (resolved.flags & ts.SymbolFlags.Alias) {
204
+ resolved = checker.getAliasedSymbol(resolved) ?? resolved;
205
+ }
206
+ const decl = resolved.valueDeclaration ?? resolved.declarations?.[0];
207
+ if (!decl)
208
+ return null;
209
+ if (ts.isVariableDeclaration(decl) && decl.initializer) {
210
+ if (ts.isCallExpression(decl.initializer) &&
211
+ ts.isIdentifier(decl.initializer.expression) &&
212
+ decl.initializer.expression.text === 'pikkuAgent') {
213
+ if (ts.isIdentifier(decl.name)) {
214
+ return decl.name.text;
215
+ }
216
+ }
217
+ }
218
+ return null;
219
+ }
220
+ /**
221
+ * The scorers an agent asks to be graded by, which are plain names rather than
222
+ * references: a scorer is resolved out of the registry at grading time, not
223
+ * imported by the agent, so nothing here has a declaration to chase.
224
+ */
225
+ function resolveScorerNames(obj, agentName, logger) {
226
+ const property = obj.properties.find((p) => ts.isPropertyAssignment(p) &&
227
+ ts.isIdentifier(p.name) &&
228
+ p.name.text === 'scorers');
229
+ if (!property || !ts.isPropertyAssignment(property))
230
+ return null;
231
+ if (!ts.isArrayLiteralExpression(property.initializer))
232
+ return null;
233
+ const names = [];
234
+ for (const element of property.initializer.elements) {
235
+ if (!ts.isStringLiteral(element)) {
236
+ logger.critical(ErrorCode.INVALID_VALUE, `AI agent '${agentName}' lists a scorer that is not an inline string: \`${element.getText()}\`. A scorer is named, not imported, so the name has to be readable from source.`);
237
+ continue;
238
+ }
239
+ names.push(element.text);
240
+ }
241
+ return names.length > 0 ? names : null;
242
+ }
243
+ export const addAgent = (logger, node, checker, state, options) => {
244
+ if (!ts.isCallExpression(node)) {
245
+ return;
246
+ }
247
+ const args = node.arguments;
248
+ const firstArg = args[0];
249
+ const expression = node.expression;
250
+ if (!ts.isIdentifier(expression) || expression.text !== 'pikkuAgent') {
251
+ return;
252
+ }
253
+ if (!firstArg) {
254
+ return;
255
+ }
256
+ const { exportedName } = extractFunctionName(node, checker, state.rootDir);
257
+ if (ts.isObjectLiteralExpression(firstArg)) {
258
+ const obj = firstArg;
259
+ const nameValue = getPropertyValue(obj, 'name');
260
+ const { disabled, tags, summary, description, errors } = getCommonWireMetaData(obj, 'AI agent', nameValue, logger, checker);
261
+ if (disabled)
262
+ return;
263
+ const modelValue = getPropertyValue(obj, 'model');
264
+ const roleValue = getPropertyValue(obj, 'role');
265
+ const personalityValue = getPropertyValue(obj, 'personality');
266
+ const goalValue = getPropertyValue(obj, 'goal');
267
+ const maxStepsValue = getPropertyValue(obj, 'maxSteps');
268
+ const temperatureValue = getPropertyValue(obj, 'temperature');
269
+ const toolChoiceValue = getPropertyValue(obj, 'toolChoice');
270
+ const scorersValue = resolveScorerNames(obj, nameValue || '', logger);
271
+ const toolsValue = resolveToolReferences(obj, checker, nameValue || '', logger);
272
+ if (toolsValue) {
273
+ for (const toolName of toolsValue) {
274
+ if (toolName.startsWith('workflow:') || toolName.includes(':'))
275
+ continue;
276
+ const funcFile = state.functions.files.get(toolName);
277
+ if (funcFile && !state.rpc.internalFiles.has(toolName)) {
278
+ state.rpc.internalFiles.set(toolName, funcFile);
279
+ }
280
+ }
281
+ }
282
+ const agentsValue = resolveAgentReferences(obj, checker, nameValue || '', logger);
283
+ const workflowsValue = resolveWorkflowReferences(obj, checker, nameValue || '', logger);
284
+ if (workflowsValue) {
285
+ for (const workflowName of workflowsValue) {
286
+ state.workflows.invokedWorkflows.add(workflowName);
287
+ }
288
+ }
289
+ if (!nameValue) {
290
+ logger.critical(ErrorCode.MISSING_NAME, "AI agent is missing the required 'name' property.");
291
+ return;
292
+ }
293
+ const agentKey = exportedName || nameValue;
294
+ if (!description) {
295
+ logger.critical(ErrorCode.MISSING_DESCRIPTION, `AI agent '${nameValue}' is missing a description.`);
296
+ return;
297
+ }
298
+ const resolveSchemaRef = (identifier, context) => {
299
+ const symbol = checker.getSymbolAtLocation(identifier);
300
+ if (!symbol)
301
+ return null;
302
+ const decl = symbol.valueDeclaration || symbol.declarations?.[0];
303
+ if (!decl)
304
+ return null;
305
+ let sourceFile;
306
+ if (ts.isImportSpecifier(decl)) {
307
+ const aliasedSymbol = checker.getAliasedSymbol(symbol);
308
+ if (aliasedSymbol) {
309
+ const aliasedDecl = aliasedSymbol.valueDeclaration || aliasedSymbol.declarations?.[0];
310
+ if (aliasedDecl) {
311
+ sourceFile = aliasedDecl.getSourceFile().fileName;
312
+ }
313
+ else {
314
+ return null;
315
+ }
316
+ }
317
+ else {
318
+ return null;
319
+ }
320
+ }
321
+ else {
322
+ sourceFile = decl.getSourceFile().fileName;
323
+ }
324
+ const vendor = detectSchemaVendorOrError(identifier, checker, logger, context, sourceFile);
325
+ if (!vendor)
326
+ return null;
327
+ return {
328
+ variableName: identifier.text,
329
+ sourceFile,
330
+ vendor,
331
+ };
332
+ };
333
+ let inputSchema = null;
334
+ let outputSchema = null;
335
+ let workingMemorySchema = null;
336
+ const capitalizedName = funcIdToTypeName(agentKey);
337
+ for (const prop of obj.properties) {
338
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
339
+ const propName = prop.name.text;
340
+ if (propName === 'input' || propName === 'output') {
341
+ if (ts.isIdentifier(prop.initializer)) {
342
+ const context = `AI agent '${nameValue}' ${propName}`;
343
+ const ref = resolveSchemaRef(prop.initializer, context);
344
+ if (ref) {
345
+ const schemaName = `${capitalizedName}${propName.charAt(0).toUpperCase() + propName.slice(1)}`;
346
+ state.schemaLookup.set(schemaName, ref);
347
+ state.functions.typesMap.addCustomType(schemaName, 'unknown', []);
348
+ if (propName === 'input') {
349
+ inputSchema = schemaName;
350
+ }
351
+ else {
352
+ outputSchema = schemaName;
353
+ }
354
+ }
355
+ }
356
+ else if (ts.isCallExpression(prop.initializer)) {
357
+ const schemaName = `${capitalizedName}${propName.charAt(0).toUpperCase() + propName.slice(1)}`;
358
+ logger.critical(ErrorCode.INLINE_SCHEMA, `Inline schemas are not supported for '${propName}' in AI agent '${nameValue}'.\n` +
359
+ ` Extract to an exported variable:\n` +
360
+ ` export const ${schemaName} = ${prop.initializer.getText()}\n` +
361
+ ` Then use: ${propName}: ${schemaName}`);
362
+ }
363
+ }
364
+ else if (propName === 'memory' &&
365
+ ts.isObjectLiteralExpression(prop.initializer)) {
366
+ for (const memProp of prop.initializer.properties) {
367
+ if (ts.isPropertyAssignment(memProp) &&
368
+ ts.isIdentifier(memProp.name) &&
369
+ memProp.name.text === 'workingMemory' &&
370
+ ts.isIdentifier(memProp.initializer)) {
371
+ const context = `AI agent '${nameValue}' workingMemory`;
372
+ const ref = resolveSchemaRef(memProp.initializer, context);
373
+ if (ref) {
374
+ const schemaName = `${capitalizedName}WorkingMemory`;
375
+ state.schemaLookup.set(schemaName, ref);
376
+ state.functions.typesMap.addCustomType(schemaName, 'unknown', []);
377
+ workingMemorySchema = schemaName;
378
+ }
379
+ }
380
+ }
381
+ }
382
+ }
383
+ }
384
+ const middleware = resolveMiddleware(state, obj, tags, checker);
385
+ const channelMiddleware = resolveChannelMiddleware(state, obj, tags, checker);
386
+ const agentMiddleware = resolveAgentMiddleware(state, obj, checker);
387
+ const permissions = resolvePermissions(state, obj, tags, checker);
388
+ state.serviceAggregation.usedFunctions.add(agentKey);
389
+ extractWireNames(middleware).forEach((name) => state.serviceAggregation.usedMiddleware.add(name));
390
+ extractWireNames(permissions).forEach((name) => state.serviceAggregation.usedPermissions.add(name));
391
+ if (exportedName) {
392
+ state.agents.files.set(agentKey, {
393
+ path: node.getSourceFile().fileName,
394
+ exportedName,
395
+ });
396
+ }
397
+ state.agents.agentsMeta[agentKey] = {
398
+ name: nameValue,
399
+ description,
400
+ role: roleValue || undefined,
401
+ personality: personalityValue || undefined,
402
+ goal: goalValue || '',
403
+ model: modelValue || '',
404
+ summary,
405
+ errors,
406
+ sourceFile: node.getSourceFile().fileName,
407
+ exportedName: exportedName || undefined,
408
+ ...(maxStepsValue !== null && { maxSteps: maxStepsValue }),
409
+ ...(temperatureValue !== null && { temperature: temperatureValue }),
410
+ ...(toolChoiceValue !== null && {
411
+ toolChoice: toolChoiceValue,
412
+ }),
413
+ ...(toolsValue !== null && { tools: toolsValue }),
414
+ ...(agentsValue !== null && { agents: agentsValue }),
415
+ ...(workflowsValue !== null && { workflows: workflowsValue }),
416
+ ...(scorersValue !== null && { scorers: scorersValue }),
417
+ tags,
418
+ inputSchema,
419
+ outputSchema,
420
+ workingMemorySchema,
421
+ middleware,
422
+ channelMiddleware,
423
+ agentMiddleware,
424
+ permissions,
425
+ };
426
+ // AI agent functions require platform services that aren't visible
427
+ // through parameter destructuring
428
+ const funcMeta = state.functions.meta[agentKey];
429
+ if (funcMeta?.services) {
430
+ for (const svc of [
431
+ 'agentStorage',
432
+ 'agentRunState',
433
+ 'agentRunService',
434
+ 'agentRunner',
435
+ ]) {
436
+ if (!funcMeta.services.services.includes(svc)) {
437
+ funcMeta.services.services.push(svc);
438
+ }
439
+ }
440
+ }
441
+ }
442
+ };
@@ -1,6 +1,6 @@
1
1
  import * as ts from 'typescript';
2
2
  import { ErrorCode } from '../error-codes.js';
3
- import type { ChannelMeta } from '@pikku/core/channel';
3
+ import type { ChannelMeta } from '@pikku/core/ecosystem/channel';
4
4
  import type { InspectorState, AddWiring } from '../types.js';
5
5
  /**
6
6
  * Build out the nested message-routes by looking up each handler
@@ -3,7 +3,7 @@ import { detectSchemaVendorOrError } from '../utils/detect-schema-vendor.js';
3
3
  import { extractFunctionName, funcIdToTypeName, } from '../utils/extract-function-name.js';
4
4
  import { extractFunctionNode } from '../utils/extract-function-node.js';
5
5
  import { extractUsedWires } from '../utils/extract-services.js';
6
- import { formatVersionedId, parseVersionedId } from '@pikku/core';
6
+ import { formatVersionedId, parseVersionedId, } from '@pikku/core/ecosystem/types';
7
7
  /** Binding keys a `pikkuScenarioStep` may declare, in report order. */
8
8
  const SCENARIO_SURFACE_KEYS = [
9
9
  'browser',
@@ -2,7 +2,7 @@ import * as ts from 'typescript';
2
2
  import { getPropertyValue, getArrayPropertyValue, assertStringLiteralProperty, } from '../utils/get-property-value.js';
3
3
  import { ErrorCode } from '../error-codes.js';
4
4
  import { detectSchemaVendorOrError } from '../utils/detect-schema-vendor.js';
5
- import { parseDurationString } from '@pikku/core';
5
+ import { parseDurationString } from '@pikku/core/ecosystem/types';
6
6
  export const createAddKeyedWiring = (config) => {
7
7
  return (logger, node, checker, state, _options) => {
8
8
  if (!ts.isCallExpression(node)) {