@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.
- package/CHANGELOG.md +127 -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-wire-addon.js +10 -0
- package/dist/add/add-workflow.d.ts +1 -1
- package/dist/error-codes.d.ts +1 -0
- package/dist/error-codes.js +1 -0
- package/dist/inspector.js +1 -1
- package/dist/types.d.ts +27 -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 +24 -12
- 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 +5 -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 -1
- 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 -1
- 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-wire-addon.test.ts +31 -0
- package/src/add/add-wire-addon.ts +8 -0
- 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/error-codes.ts +1 -0
- package/src/inspector.ts +1 -1
- package/src/types.ts +33 -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 +76 -9
- package/src/utils/post-process.ts +36 -14
- 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 +9 -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,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,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)) {
|