@pikku/inspector 0.12.59 → 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.
- package/CHANGELOG.md +72 -0
- package/dist/add/add-agent-scorer.d.ts +2 -0
- package/dist/add/add-agent-scorer.js +61 -0
- package/dist/add/add-agent.d.ts +4 -0
- package/dist/add/add-agent.js +442 -0
- package/dist/add/add-channel.d.ts +1 -1
- package/dist/add/add-functions.js +1 -1
- package/dist/add/add-keyed-wiring.js +1 -1
- package/dist/add/add-middleware.js +5 -5
- package/dist/add/add-personas.js +1 -1
- package/dist/add/add-workflow.d.ts +1 -1
- package/dist/inspector.js +1 -1
- package/dist/types.d.ts +23 -22
- package/dist/utils/compute-required-schemas.d.ts +1 -1
- package/dist/utils/contract-hashes.d.ts +2 -1
- package/dist/utils/contract-hashes.js +1 -1
- package/dist/utils/extract-function-name.js +1 -1
- package/dist/utils/extract-services.d.ts +1 -1
- package/dist/utils/filter-inspector-state.js +1 -1
- package/dist/utils/filter-utils.d.ts +1 -1
- package/dist/utils/middleware.d.ts +2 -2
- package/dist/utils/middleware.js +5 -5
- package/dist/utils/permissions.d.ts +1 -1
- package/dist/utils/post-process.d.ts +2 -2
- package/dist/utils/post-process.js +6 -6
- package/dist/utils/resolve-deploy-target.d.ts +1 -1
- package/dist/utils/resolve-function-meta.d.ts +1 -1
- package/dist/utils/resolve-function-types.d.ts +1 -1
- package/dist/utils/resolve-versions.js +1 -1
- package/dist/utils/schema-generator.d.ts +1 -1
- package/dist/utils/serialize-inspector-state.d.ts +3 -3
- package/dist/utils/serialize-inspector-state.js +4 -4
- package/dist/utils/serialize-openapi-json.d.ts +2 -2
- package/dist/utils/workflow/derive-workflow-plan.d.ts +1 -1
- package/dist/utils/workflow/dsl/extract-dsl-workflow.d.ts +1 -1
- package/dist/utils/workflow/graph/convert-dsl-to-graph.d.ts +1 -1
- package/dist/utils/workflow/graph/finalize-workflows.js +1 -1
- package/dist/utils/workflow/graph/serialize-workflow-graph.js +1 -1
- package/dist/utils/workflow/graph/workflow-graph.types.d.ts +2 -2
- package/dist/visit.js +4 -4
- package/package.json +3 -3
- package/src/add/{add-ai-scorer.test.ts → add-agent-scorer.test.ts} +11 -11
- package/src/add/{add-ai-scorer.ts → add-agent-scorer.ts} +4 -4
- package/src/add/{add-ai-agent.test.ts → add-agent.test.ts} +1 -1
- package/src/add/{add-ai-agent.ts → add-agent.ts} +11 -17
- package/src/add/add-auth.ts +1 -1
- package/src/add/add-channel.ts +4 -1
- package/src/add/add-cli.ts +1 -1
- package/src/add/add-functions.ts +9 -4
- package/src/add/add-gateway.ts +1 -1
- package/src/add/add-http-route.ts +1 -1
- package/src/add/add-keyed-wiring.ts +1 -1
- package/src/add/add-middleware.ts +7 -5
- package/src/add/add-permission.ts +1 -1
- package/src/add/add-personas.test.ts +2 -2
- package/src/add/add-personas.ts +3 -2
- package/src/add/add-scenario-step.test.ts +2 -2
- package/src/add/add-scope.test.ts +19 -19
- package/src/add/add-scope.ts +1 -1
- package/src/add/add-system-role.test.ts +12 -12
- package/src/add/add-workflow-graph-input.test.ts +1 -1
- package/src/add/add-workflow.ts +4 -1
- package/src/add/addon-contracts.test.ts +1 -1
- package/src/inspector.ts +1 -1
- package/src/types.ts +29 -23
- package/src/utils/compute-required-schemas.ts +1 -1
- package/src/utils/contract-hashes.test.ts +1 -1
- package/src/utils/contract-hashes.ts +3 -2
- package/src/utils/extract-function-name.ts +1 -1
- package/src/utils/extract-services.ts +4 -1
- package/src/utils/filter-inspector-state.ts +2 -2
- package/src/utils/filter-utils.ts +1 -1
- package/src/utils/middleware.test.ts +23 -23
- package/src/utils/middleware.ts +6 -6
- package/src/utils/permissions.ts +1 -1
- package/src/utils/post-process.test.ts +9 -9
- package/src/utils/post-process.ts +8 -8
- package/src/utils/resolve-deploy-target.ts +1 -1
- package/src/utils/resolve-function-meta.ts +4 -1
- package/src/utils/resolve-function-types.ts +1 -1
- package/src/utils/resolve-versions.ts +5 -2
- package/src/utils/schema-generator.ts +3 -2
- package/src/utils/serialize-inspector-state.ts +7 -7
- package/src/utils/serialize-mcp-json.ts +1 -1
- package/src/utils/serialize-openapi-json.ts +2 -2
- package/src/utils/workflow/derive-workflow-plan.test.ts +1 -1
- package/src/utils/workflow/derive-workflow-plan.ts +1 -1
- package/src/utils/workflow/dsl/extract-approval-policy.test.ts +5 -2
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +2 -2
- package/src/utils/workflow/graph/convert-dsl-to-graph-skip.test.ts +1 -1
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +4 -1
- package/src/utils/workflow/graph/finalize-workflow-wires.ts +1 -1
- package/src/utils/workflow/graph/finalize-workflows.ts +6 -2
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +1 -1
- package/src/utils/workflow/graph/workflow-graph.types.ts +2 -2
- package/src/visit.ts +4 -4
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,75 @@
|
|
|
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
|
+
|
|
1
73
|
## 0.12.59
|
|
2
74
|
|
|
3
75
|
### Patch Changes
|
|
@@ -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)) {
|
|
@@ -508,12 +508,12 @@ export const addMiddleware = (logger, node, checker, state) => {
|
|
|
508
508
|
logger.debug(`• Found channel middleware factory with services: ${services.services.join(', ')}`);
|
|
509
509
|
return;
|
|
510
510
|
}
|
|
511
|
-
if (expression.text === '
|
|
511
|
+
if (expression.text === 'pikkuAgentMiddleware') {
|
|
512
512
|
const arg = args[0];
|
|
513
513
|
if (!arg)
|
|
514
514
|
return;
|
|
515
515
|
if (!ts.isObjectLiteralExpression(arg)) {
|
|
516
|
-
logger.error(`•
|
|
516
|
+
logger.error(`• pikkuAgentMiddleware() requires an object literal argument.`);
|
|
517
517
|
return;
|
|
518
518
|
}
|
|
519
519
|
const allServices = new Set();
|
|
@@ -542,12 +542,12 @@ export const addMiddleware = (logger, node, checker, state) => {
|
|
|
542
542
|
pikkuFuncId = node.parent.name.text;
|
|
543
543
|
}
|
|
544
544
|
else {
|
|
545
|
-
logger.error(`•
|
|
546
|
-
`Extract it to a const: const myMiddleware =
|
|
545
|
+
logger.error(`• pikkuAgentMiddleware() must be assigned to a variable or object property. ` +
|
|
546
|
+
`Extract it to a const: const myMiddleware = pikkuAgentMiddleware(...)`);
|
|
547
547
|
return;
|
|
548
548
|
}
|
|
549
549
|
}
|
|
550
|
-
state.
|
|
550
|
+
state.agentMiddleware.definitions[pikkuFuncId] = {
|
|
551
551
|
services,
|
|
552
552
|
sourceFile: node.getSourceFile().fileName,
|
|
553
553
|
position: node.getStart(),
|
package/dist/add/add-personas.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
|
-
import { DISPOSITIONS } from '@pikku/core/virtual-user';
|
|
2
|
+
import { DISPOSITIONS } from '@pikku/core/ecosystem/virtual-user';
|
|
3
3
|
import { isRunnablePersona } from '@pikku/core/persona';
|
|
4
4
|
import { ErrorCode } from '../error-codes.js';
|
|
5
5
|
import { claimSingleDeclaration } from '../utils/single-declaration.js';
|