@openclaw/plugin-inspector 0.3.9 → 0.3.11
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 +21 -0
- package/package.json +8 -2
- package/src/advanced.js +1 -0
- package/src/capture-cli.js +12 -1
- package/src/capture-config.js +49 -1
- package/src/cli.js +12 -1
- package/src/compatibility-report.js +4 -4
- package/src/contract-coverage.js +6 -1
- package/src/contract-probes.js +5 -0
- package/src/execution-results.js +10 -0
- package/src/fixture-summary.js +101 -4
- package/src/index.js +3 -0
- package/src/inspector.js +24 -0
- package/src/issues.js +13 -2
- package/src/report.js +4 -0
- package/src/runtime-reconciliation.js +4 -1
- package/src/sdk-mock.js +61 -1
- package/src/synthetic-entrypoint.js +47 -0
- package/src/synthetic-probes-cli.js +2 -35
- package/src/synthetic-probes.js +59 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.3.11 - 2026-05-26
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Classify the latest generated kitchen-sink registrars for meeting notes, node CLI features, hosted media, model catalogs, embedding providers, and session actions.
|
|
10
|
+
- Stop classifying package source entrypoints as missing when the published package provides built runtime entrypoints, and collapse SDK alias findings into a single compat-gap row.
|
|
11
|
+
- Treat compat-gap issues as reconciled contract coverage for their own compatibility record.
|
|
12
|
+
- Count passed synthetic hook probes as runtime coverage so conversation-access and `before_tool_call` inspector gaps close when probe artifacts prove them.
|
|
13
|
+
- Keep mock-SDK synthetic probes in-process so retained hook and registration handlers remain callable, and harden dynamic root SDK mock exports.
|
|
14
|
+
- Synthesize nested manifest config samples for optional object settings with required inner shape.
|
|
15
|
+
- Populate message and agent lifecycle synthetic hook payloads with the fields telemetry plugins commonly read.
|
|
16
|
+
- Resolve plugin manifests from parent directories when runtime capture starts from built `dist` entrypoints.
|
|
17
|
+
- Capture CLIs no longer treat `--output`, `--plugin-root`, or `--sdk` flag values as the positional entrypoint. Thanks @KrasimirKralev.
|
|
18
|
+
- Mock-SDK TypeScript capture now falls back to Node's strip-only parser on Node 26.
|
|
19
|
+
|
|
20
|
+
## 0.3.10 - 2026-05-03
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Accept valid mocked capture output when plugin code leaves `process.exitCode` dirty.
|
|
25
|
+
|
|
5
26
|
## 0.3.9 - 2026-05-03
|
|
6
27
|
|
|
7
28
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/plugin-inspector",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.11",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Offline compatibility inspector for OpenClaw plugins.",
|
|
6
6
|
"type": "module",
|
|
@@ -56,7 +56,13 @@
|
|
|
56
56
|
"release:readiness": "npm run release:local && npm run release:crabpot",
|
|
57
57
|
"release:local": "npm run check",
|
|
58
58
|
"release:notes": "node scripts/release-notes.mjs --unreleased",
|
|
59
|
-
"test": "node --test test/*.test.js"
|
|
59
|
+
"test": "node --test test/*.test.js",
|
|
60
|
+
"check:changed": "npm run check",
|
|
61
|
+
"test:changed": "npm test",
|
|
62
|
+
"crabbox:hydrate": "crabbox actions hydrate",
|
|
63
|
+
"crabbox:run": "crabbox run",
|
|
64
|
+
"crabbox:stop": "crabbox stop",
|
|
65
|
+
"crabbox:warmup": "crabbox warmup"
|
|
60
66
|
},
|
|
61
67
|
"keywords": [
|
|
62
68
|
"openclaw",
|
package/src/advanced.js
CHANGED
|
@@ -194,6 +194,7 @@ export {
|
|
|
194
194
|
writeRuntimeCaptureReport,
|
|
195
195
|
} from "./runtime-capture-report.js";
|
|
196
196
|
export { createMockSdkPackage } from "./sdk-mock.js";
|
|
197
|
+
export { runEntrypointSyntheticProbes } from "./synthetic-entrypoint.js";
|
|
197
198
|
export {
|
|
198
199
|
buildSyntheticProbePlan,
|
|
199
200
|
defaultSyntheticHookContexts,
|
package/src/capture-cli.js
CHANGED
|
@@ -11,7 +11,7 @@ try {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
async function run(commandArgs) {
|
|
14
|
-
const entrypoint = commandArgs
|
|
14
|
+
const entrypoint = findEntrypoint(commandArgs);
|
|
15
15
|
const outputPath = readFlag(commandArgs, "--output");
|
|
16
16
|
const pluginRoot = readFlag(commandArgs, "--plugin-root");
|
|
17
17
|
const mockSdk = readMockSdkFlag(commandArgs) ?? true;
|
|
@@ -40,6 +40,17 @@ function readFlag(commandArgs, name) {
|
|
|
40
40
|
return commandArgs[index + 1] ?? null;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
function findEntrypoint(commandArgs) {
|
|
44
|
+
const flagsWithValues = new Set(["--output", "--plugin-root", "--sdk"]);
|
|
45
|
+
const consumedIndexes = new Set();
|
|
46
|
+
for (const [index, arg] of commandArgs.entries()) {
|
|
47
|
+
if (flagsWithValues.has(arg)) {
|
|
48
|
+
consumedIndexes.add(index + 1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return commandArgs.find((arg, index) => !arg.startsWith("-") && !consumedIndexes.has(index)) ?? null;
|
|
52
|
+
}
|
|
53
|
+
|
|
43
54
|
function readMockSdkFlag(commandArgs) {
|
|
44
55
|
const sdk = readFlag(commandArgs, "--sdk");
|
|
45
56
|
if (sdk === "mock") {
|
package/src/capture-config.js
CHANGED
|
@@ -17,7 +17,10 @@ export async function captureApiOptionsForPlugin(apiOptions = {}, options = {})
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
async function readSamplePluginConfig(pluginRoot) {
|
|
20
|
-
const manifestPath =
|
|
20
|
+
const manifestPath = await findNearestManifestPath(pluginRoot);
|
|
21
|
+
if (!manifestPath) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
21
24
|
let manifest;
|
|
22
25
|
try {
|
|
23
26
|
manifest = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
@@ -29,6 +32,23 @@ async function readSamplePluginConfig(pluginRoot) {
|
|
|
29
32
|
return isPlainObject(sample) && Object.keys(sample).length > 0 ? sample : undefined;
|
|
30
33
|
}
|
|
31
34
|
|
|
35
|
+
async function findNearestManifestPath(pluginRoot) {
|
|
36
|
+
let current = path.resolve(pluginRoot);
|
|
37
|
+
while (true) {
|
|
38
|
+
const manifestPath = path.join(current, "openclaw.plugin.json");
|
|
39
|
+
try {
|
|
40
|
+
await readFile(manifestPath, "utf8");
|
|
41
|
+
return manifestPath;
|
|
42
|
+
} catch {}
|
|
43
|
+
|
|
44
|
+
const parent = path.dirname(current);
|
|
45
|
+
if (parent === current) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
current = parent;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
32
52
|
function sampleJsonSchema(schema, context = {}) {
|
|
33
53
|
if (!isPlainObject(schema)) {
|
|
34
54
|
return undefined;
|
|
@@ -83,6 +103,16 @@ function sampleObjectSchema(schema) {
|
|
|
83
103
|
}
|
|
84
104
|
}
|
|
85
105
|
|
|
106
|
+
if (!hasNonBooleanSample(output, properties)) {
|
|
107
|
+
const key = preferredNestedConfigKey(properties);
|
|
108
|
+
if (key) {
|
|
109
|
+
const value = sampleJsonSchema(properties[key], { key });
|
|
110
|
+
if (value !== undefined) {
|
|
111
|
+
output[key] = value;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
86
116
|
if (Object.keys(output).length === 0 && Number(schema.minProperties ?? 0) > 0) {
|
|
87
117
|
const key = preferredSamplePropertyKey(properties);
|
|
88
118
|
if (key) {
|
|
@@ -96,6 +126,24 @@ function sampleObjectSchema(schema) {
|
|
|
96
126
|
return output;
|
|
97
127
|
}
|
|
98
128
|
|
|
129
|
+
function hasNonBooleanSample(output, properties) {
|
|
130
|
+
return Object.keys(output).some((key) => properties[key]?.type !== "boolean");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function preferredNestedConfigKey(properties) {
|
|
134
|
+
for (const key of ["embedding", "credentials", "auth", "provider", ...Object.keys(properties)]) {
|
|
135
|
+
const schema = properties[key];
|
|
136
|
+
if (!isPlainObject(schema)) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const required = Array.isArray(schema.required) ? schema.required : [];
|
|
140
|
+
if ((schema.type === "object" || schema.properties) && (Number(schema.minProperties ?? 0) > 0 || required.length > 0)) {
|
|
141
|
+
return key;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
|
|
99
147
|
function preferredSamplePropertyKey(properties) {
|
|
100
148
|
for (const key of ["provider", "model", "apiKey", "id", "name", ...Object.keys(properties)]) {
|
|
101
149
|
if (Object.prototype.hasOwnProperty.call(properties, key)) {
|
package/src/cli.js
CHANGED
|
@@ -223,7 +223,7 @@ async function runCiCompatibilityReport({ allowExecution, capture, configPath, m
|
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
async function runCapture(commandArgs) {
|
|
226
|
-
const entrypoint = commandArgs
|
|
226
|
+
const entrypoint = findCaptureEntrypoint(commandArgs);
|
|
227
227
|
const outputPath = readFlag(commandArgs, "--output");
|
|
228
228
|
const pluginRoot = readFlag(commandArgs, "--plugin-root");
|
|
229
229
|
const mockSdk = readMockSdkFlag(commandArgs) ?? commandArgs.includes("--mock-sdk");
|
|
@@ -252,6 +252,17 @@ function readFlag(commandArgs, name) {
|
|
|
252
252
|
return commandArgs[index + 1] ?? null;
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
+
function findCaptureEntrypoint(commandArgs) {
|
|
256
|
+
const flagsWithValues = new Set(["--output", "--plugin-root", "--sdk"]);
|
|
257
|
+
const consumedIndexes = new Set();
|
|
258
|
+
for (const [index, arg] of commandArgs.entries()) {
|
|
259
|
+
if (flagsWithValues.has(arg)) {
|
|
260
|
+
consumedIndexes.add(index + 1);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return commandArgs.find((arg, index) => !arg.startsWith("-") && !consumedIndexes.has(index)) ?? null;
|
|
264
|
+
}
|
|
265
|
+
|
|
255
266
|
function readOptionalPathFlag(commandArgs, name, defaultPath) {
|
|
256
267
|
const index = commandArgs.indexOf(name);
|
|
257
268
|
if (index === -1) {
|
|
@@ -58,9 +58,9 @@ export function renderCompatibilityMarkdownReport(report, options = {}) {
|
|
|
58
58
|
options,
|
|
59
59
|
),
|
|
60
60
|
"",
|
|
61
|
-
"## Live Issues",
|
|
61
|
+
"## Other Live Issues",
|
|
62
62
|
"",
|
|
63
|
-
issuesTable(report.issues.filter((issue) => issue.issueClass === "live-issue"), options),
|
|
63
|
+
issuesTable(report.issues.filter((issue) => issue.issueClass === "live-issue" && issue.severity !== "P0"), options),
|
|
64
64
|
"",
|
|
65
65
|
"## Compat Gaps",
|
|
66
66
|
"",
|
|
@@ -183,9 +183,9 @@ export function renderCompatibilityIssuesReport(report, options = {}) {
|
|
|
183
183
|
options,
|
|
184
184
|
),
|
|
185
185
|
"",
|
|
186
|
-
"## Live Issues",
|
|
186
|
+
"## Other Live Issues",
|
|
187
187
|
"",
|
|
188
|
-
issuesTable(report.issues.filter((issue) => issue.issueClass === "live-issue"), options),
|
|
188
|
+
issuesTable(report.issues.filter((issue) => issue.issueClass === "live-issue" && issue.severity !== "P0"), options),
|
|
189
189
|
"",
|
|
190
190
|
"## Compat Gaps",
|
|
191
191
|
"",
|
package/src/contract-coverage.js
CHANGED
|
@@ -157,10 +157,15 @@ function requireCompatRecordReconciliation(report, errors) {
|
|
|
157
157
|
.filter((finding) => finding.code === "missing-compat-record")
|
|
158
158
|
.map((finding) => `${finding.fixture}:${finding.compatRecord}`),
|
|
159
159
|
);
|
|
160
|
+
const compatGapRecords = new Set(
|
|
161
|
+
report.issues
|
|
162
|
+
.filter((issue) => issue.issueClass === "compat-gap" && issue.compatRecord)
|
|
163
|
+
.map((issue) => `${issue.fixture}:${issue.compatRecord}`),
|
|
164
|
+
);
|
|
160
165
|
|
|
161
166
|
for (const finding of [...report.warnings, ...report.suggestions].filter((item) => item.compatRecord)) {
|
|
162
167
|
const key = `${finding.fixture}:${finding.compatRecord}`;
|
|
163
|
-
if (!presentRecords.has(key) && !missingRecords.has(key)) {
|
|
168
|
+
if (!presentRecords.has(key) && !missingRecords.has(key) && !compatGapRecords.has(key)) {
|
|
164
169
|
errors.push(`${finding.fixture}: compat record ${finding.compatRecord} was not reconciled`);
|
|
165
170
|
}
|
|
166
171
|
}
|
package/src/contract-probes.js
CHANGED
|
@@ -114,6 +114,11 @@ export const contractProbeRules = {
|
|
|
114
114
|
contract: "Package and OpenClaw manifest versions stay aligned for release compatibility reporting.",
|
|
115
115
|
target: "package-loader",
|
|
116
116
|
},
|
|
117
|
+
"manifest-name-missing": {
|
|
118
|
+
id: "manifest.metadata.name",
|
|
119
|
+
contract: "OpenClaw plugin manifests declare a human-readable display name for registry and tooling metadata.",
|
|
120
|
+
target: "manifest-loader",
|
|
121
|
+
},
|
|
117
122
|
"package-plugin-api-compat-missing": {
|
|
118
123
|
id: "package.compat.plugin-api-range",
|
|
119
124
|
contract: "Package metadata declares the OpenClaw plugin API range used by the plugin.",
|
package/src/execution-results.js
CHANGED
|
@@ -204,6 +204,7 @@ function summarizeArtifact({ artifactPath, parsed, rootDir }) {
|
|
|
204
204
|
: "capture";
|
|
205
205
|
const fixture = normalizedArtifactPath.split("/").at(-2) ?? "unknown";
|
|
206
206
|
if (kind === "synthetic") {
|
|
207
|
+
const passed = (parsed.results ?? []).filter((result) => result.status === "pass");
|
|
207
208
|
return {
|
|
208
209
|
artifactPath: normalizedArtifactPath,
|
|
209
210
|
fixture,
|
|
@@ -211,6 +212,8 @@ function summarizeArtifact({ artifactPath, parsed, rootDir }) {
|
|
|
211
212
|
entrypoint: scrubPath(parsed.entrypoint, { rootDir }),
|
|
212
213
|
status: parsed.status,
|
|
213
214
|
summary: parsed.summary,
|
|
215
|
+
captured: passed.map(syntheticRuntimeCaptureKey).filter(Boolean),
|
|
216
|
+
passed,
|
|
214
217
|
failures: (parsed.results ?? []).filter((result) => result.status === "fail"),
|
|
215
218
|
blocked: (parsed.results ?? []).filter((result) => result.status === "blocked"),
|
|
216
219
|
};
|
|
@@ -261,6 +264,13 @@ function summarizeArtifactResult(artifact) {
|
|
|
261
264
|
return `${artifact.capturedCount} captured`;
|
|
262
265
|
}
|
|
263
266
|
|
|
267
|
+
function syntheticRuntimeCaptureKey(result) {
|
|
268
|
+
if (!result.kind || !result.seam) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
return `${result.kind}:${result.seam}`;
|
|
272
|
+
}
|
|
273
|
+
|
|
264
274
|
function auditFindingCount(parsed) {
|
|
265
275
|
const vulnerabilities = parsed.metadata?.vulnerabilities;
|
|
266
276
|
if (vulnerabilities && typeof vulnerabilities === "object") {
|
package/src/fixture-summary.js
CHANGED
|
@@ -233,6 +233,26 @@ export function classifyPackageContracts({ fixture, inspection, fixtureReport })
|
|
|
233
233
|
});
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
+
const missingManifestNames = fixtureReport.pluginManifests.filter(
|
|
237
|
+
(manifest) => typeof manifest.name !== "string" || manifest.name.trim().length === 0,
|
|
238
|
+
);
|
|
239
|
+
if (missingManifestNames.length > 0) {
|
|
240
|
+
warnings.push({
|
|
241
|
+
fixture: fixture.id,
|
|
242
|
+
code: "manifest-name-missing",
|
|
243
|
+
level: "warning",
|
|
244
|
+
message: "openclaw.plugin.json does not declare a display name",
|
|
245
|
+
evidence: missingManifestNames.map((manifest) => manifest.path ?? "openclaw.plugin.json"),
|
|
246
|
+
});
|
|
247
|
+
decisions.push({
|
|
248
|
+
fixture: fixture.id,
|
|
249
|
+
decision: "plugin-upstream-fix",
|
|
250
|
+
seam: "manifest-metadata",
|
|
251
|
+
action: "Ask the plugin to declare openclaw.plugin.json name so registries and tools can derive a human-readable title.",
|
|
252
|
+
evidence: missingManifestNames.map((manifest) => manifest.path ?? "openclaw.plugin.json").join(", "),
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
236
256
|
if (packageSummary.openclaw && !packageSummary.openclaw.compatPluginApi) {
|
|
237
257
|
warnings.push({
|
|
238
258
|
fixture: fixture.id,
|
|
@@ -340,9 +360,12 @@ export function classifyPackageContracts({ fixture, inspection, fixtureReport })
|
|
|
340
360
|
});
|
|
341
361
|
}
|
|
342
362
|
|
|
343
|
-
const
|
|
363
|
+
const entrypoints = packageSummary.openclaw?.entrypoints ?? [];
|
|
364
|
+
const missingEntrypoints = entrypoints.filter((entrypoint) => !entrypoint.exists);
|
|
344
365
|
const buildEntrypoints = missingEntrypoints.filter((entrypoint) => entrypoint.requiresBuild);
|
|
345
|
-
const plainMissingEntrypoints = missingEntrypoints.filter(
|
|
366
|
+
const plainMissingEntrypoints = missingEntrypoints.filter(
|
|
367
|
+
(entrypoint) => !entrypoint.requiresBuild && !hasUsablePackageRuntimeEntrypoint(entrypoint, packageSummary, entrypoints),
|
|
368
|
+
);
|
|
346
369
|
|
|
347
370
|
if (buildEntrypoints.length > 0) {
|
|
348
371
|
suggestions.push({
|
|
@@ -920,6 +943,46 @@ function collectOpenClawEntrypoints(packageDir, openclaw, options) {
|
|
|
920
943
|
});
|
|
921
944
|
}
|
|
922
945
|
|
|
946
|
+
function hasUsablePackageRuntimeEntrypoint(entrypoint, packageSummary, entrypoints) {
|
|
947
|
+
if (!isSourceEntrypoint(entrypoint.specifier)) {
|
|
948
|
+
return false;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
const runtimeBuildSpecifier = runtimeBuildSpecifierFor(entrypoint.specifier);
|
|
952
|
+
if (
|
|
953
|
+
entrypoints.some(
|
|
954
|
+
(candidate) =>
|
|
955
|
+
candidate.exists &&
|
|
956
|
+
candidate.requiresBuild &&
|
|
957
|
+
normalizeEntrypointSpecifier(candidate.specifier) === normalizeEntrypointSpecifier(runtimeBuildSpecifier),
|
|
958
|
+
)
|
|
959
|
+
) {
|
|
960
|
+
return true;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
if (entrypoint.kind === "extension" && entrypoints.some((candidate) => candidate.kind === "runtimeExtension" && candidate.exists)) {
|
|
964
|
+
return true;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
const packageDir = path.dirname(packageSummary.path);
|
|
968
|
+
return existsSync(path.resolve(packageDir, runtimeBuildSpecifier));
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
function isSourceEntrypoint(specifier) {
|
|
972
|
+
return /\.(?:ts|tsx)$/.test(specifier);
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
function runtimeBuildSpecifierFor(specifier) {
|
|
976
|
+
const normalized = normalizeEntrypointSpecifier(specifier);
|
|
977
|
+
const basename = path.posix.basename(normalized).replace(/\.(?:ts|tsx)$/, ".js");
|
|
978
|
+
return `./dist/${basename}`;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
function normalizeEntrypointSpecifier(specifier) {
|
|
982
|
+
const normalized = specifier.replaceAll("\\", "/");
|
|
983
|
+
return normalized.startsWith("./") ? normalized : `./${normalized}`;
|
|
984
|
+
}
|
|
985
|
+
|
|
923
986
|
async function findPackageFiles(root, options, depth = 0) {
|
|
924
987
|
if (!existsSync(root) || depth > options.maxDepth) {
|
|
925
988
|
return [];
|
|
@@ -1059,8 +1122,13 @@ function packageNpmPackIssues(packageSummary, fixtureReport) {
|
|
|
1059
1122
|
});
|
|
1060
1123
|
}
|
|
1061
1124
|
|
|
1062
|
-
const
|
|
1063
|
-
|
|
1125
|
+
const entrypoints = packageSummary.openclaw?.entrypoints ?? [];
|
|
1126
|
+
const missingEntrypoints = entrypoints
|
|
1127
|
+
.filter(
|
|
1128
|
+
(entrypoint) =>
|
|
1129
|
+
!repoPathIncludedInNpmPack(packageSummary, entrypoint.relativePath) &&
|
|
1130
|
+
!hasPackagedRuntimeEntrypoint(entrypoint, packageSummary, entrypoints),
|
|
1131
|
+
)
|
|
1064
1132
|
.map((entrypoint) => `${entrypoint.kind}:${entrypoint.specifier} -> ${entrypoint.relativePath}`) ?? [];
|
|
1065
1133
|
if (missingEntrypoints.length > 0) {
|
|
1066
1134
|
findings.push({
|
|
@@ -1088,6 +1156,35 @@ function packageNpmPackMissingMetadata(packageSummary, fixtureReport) {
|
|
|
1088
1156
|
return missing;
|
|
1089
1157
|
}
|
|
1090
1158
|
|
|
1159
|
+
function hasPackagedRuntimeEntrypoint(entrypoint, packageSummary, entrypoints) {
|
|
1160
|
+
if (!isSourceEntrypoint(entrypoint.specifier)) {
|
|
1161
|
+
return false;
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
const runtimeBuildSpecifier = runtimeBuildSpecifierFor(entrypoint.specifier);
|
|
1165
|
+
const matchingRuntimeEntrypoint = entrypoints.find(
|
|
1166
|
+
(candidate) =>
|
|
1167
|
+
candidate.requiresBuild &&
|
|
1168
|
+
normalizeEntrypointSpecifier(candidate.specifier) === normalizeEntrypointSpecifier(runtimeBuildSpecifier),
|
|
1169
|
+
);
|
|
1170
|
+
if (matchingRuntimeEntrypoint && repoPathIncludedInNpmPack(packageSummary, matchingRuntimeEntrypoint.relativePath)) {
|
|
1171
|
+
return true;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
if (
|
|
1175
|
+
entrypoint.kind === "extension" &&
|
|
1176
|
+
entrypoints.some(
|
|
1177
|
+
(candidate) => candidate.kind === "runtimeExtension" && repoPathIncludedInNpmPack(packageSummary, candidate.relativePath),
|
|
1178
|
+
)
|
|
1179
|
+
) {
|
|
1180
|
+
return true;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
const packageDir = path.posix.dirname(normalizeRepoPath(packageSummary.path));
|
|
1184
|
+
const runtimeBuildPath = path.posix.join(packageDir === "." ? "" : packageDir, normalizeEntrypointSpecifier(runtimeBuildSpecifier));
|
|
1185
|
+
return repoPathIncludedInNpmPack(packageSummary, runtimeBuildPath);
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1091
1188
|
function packageMinHostVersionDrift(packageSummary) {
|
|
1092
1189
|
const openclaw = packageSummary.openclaw;
|
|
1093
1190
|
if (!nonEmptyString(openclaw?.install?.minHostVersion) || !nonEmptyString(openclaw?.buildOpenClawVersion)) {
|
package/src/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import * as refDiffApi from "./ref-diff.js";
|
|
|
14
14
|
import * as reportApi from "./report.js";
|
|
15
15
|
import * as runtimeProfileApi from "./runtime-profile.js";
|
|
16
16
|
import * as runtimeReconciliationApi from "./runtime-reconciliation.js";
|
|
17
|
+
import * as syntheticEntrypointApi from "./synthetic-entrypoint.js";
|
|
17
18
|
import * as syntheticProbeSuiteApi from "./synthetic-probe-suite.js";
|
|
18
19
|
import * as syntheticProbesApi from "./synthetic-probes.js";
|
|
19
20
|
|
|
@@ -123,6 +124,7 @@ export const synthetic = Object.freeze({
|
|
|
123
124
|
renderPlan: syntheticProbesApi.renderSyntheticProbeMarkdown,
|
|
124
125
|
validatePlan: syntheticProbesApi.validateSyntheticProbePlan,
|
|
125
126
|
runCaptured: syntheticProbesApi.runCapturedSyntheticProbes,
|
|
127
|
+
runEntrypoint: syntheticEntrypointApi.runEntrypointSyntheticProbes,
|
|
126
128
|
registrationExecutionProfiles: syntheticProbesApi.syntheticRegistrationExecutionProfiles,
|
|
127
129
|
defaultHookEvents: syntheticProbesApi.defaultSyntheticHookEvents,
|
|
128
130
|
defaultHookContexts: syntheticProbesApi.defaultSyntheticHookContexts,
|
|
@@ -234,6 +236,7 @@ export {
|
|
|
234
236
|
applyRuntimeExecutionCoverage,
|
|
235
237
|
buildRuntimeExecutionCoverage,
|
|
236
238
|
} from "./runtime-reconciliation.js";
|
|
239
|
+
export { runEntrypointSyntheticProbes } from "./synthetic-entrypoint.js";
|
|
237
240
|
export { buildSyntheticProbePlanFromReport } from "./synthetic-probe-suite.js";
|
|
238
241
|
export {
|
|
239
242
|
buildSyntheticProbePlan,
|
package/src/inspector.js
CHANGED
|
@@ -234,10 +234,34 @@ export async function captureEntrypointWithMockSdk(entrypoint, options = {}) {
|
|
|
234
234
|
);
|
|
235
235
|
return JSON.parse(stdout);
|
|
236
236
|
} catch (error) {
|
|
237
|
+
const captured = parseCaptureResultFromStdout(error?.stdout);
|
|
238
|
+
if (captured) {
|
|
239
|
+
return captured;
|
|
240
|
+
}
|
|
237
241
|
throw classifyMockSdkCaptureError(error);
|
|
238
242
|
}
|
|
239
243
|
}
|
|
240
244
|
|
|
245
|
+
function parseCaptureResultFromStdout(stdout) {
|
|
246
|
+
if (!stdout) {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
const parsed = JSON.parse(stdout);
|
|
251
|
+
if (
|
|
252
|
+
parsed &&
|
|
253
|
+
typeof parsed === "object" &&
|
|
254
|
+
typeof parsed.status === "string" &&
|
|
255
|
+
Array.isArray(parsed.captured)
|
|
256
|
+
) {
|
|
257
|
+
return parsed;
|
|
258
|
+
}
|
|
259
|
+
} catch {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
|
|
241
265
|
export function classifyMockSdkCaptureError(error) {
|
|
242
266
|
const rawMessage = [error?.stderr, error?.stdout, error?.message].filter(Boolean).join("\n");
|
|
243
267
|
const missingExport = rawMessage.match(/does not provide an export named ['"]([^'"]+)['"]/)?.[1];
|
package/src/issues.js
CHANGED
|
@@ -14,6 +14,7 @@ export const knownIssueCodes = new Set([
|
|
|
14
14
|
"conversation-access-hook",
|
|
15
15
|
"legacy-before-agent-start",
|
|
16
16
|
"legacy-root-sdk-import",
|
|
17
|
+
"manifest-name-missing",
|
|
17
18
|
"manifest-unknown-contracts",
|
|
18
19
|
"manifest-unknown-fields",
|
|
19
20
|
"missing-expected-seam",
|
|
@@ -111,6 +112,12 @@ export const issueMetadataByCode = {
|
|
|
111
112
|
decision: "inspector-follow-up",
|
|
112
113
|
title: "fixture no longer exposes an expected seam",
|
|
113
114
|
},
|
|
115
|
+
"manifest-name-missing": {
|
|
116
|
+
severity: "P2",
|
|
117
|
+
owner: "plugin",
|
|
118
|
+
decision: "plugin-upstream-fix",
|
|
119
|
+
title: "manifest display name is missing",
|
|
120
|
+
},
|
|
114
121
|
"manifest-unknown-contracts": {
|
|
115
122
|
severity: "P1",
|
|
116
123
|
owner: "plugin",
|
|
@@ -342,7 +349,10 @@ export function summarizeIssueClasses(issues) {
|
|
|
342
349
|
}
|
|
343
350
|
|
|
344
351
|
function issueClassFor(code, options) {
|
|
345
|
-
if (
|
|
352
|
+
if (code === "sdk-export-missing" && options.compatRecord) {
|
|
353
|
+
return "compat-gap";
|
|
354
|
+
}
|
|
355
|
+
if (["unknown-hook-name", "unknown-registration-name", "package-entrypoint-missing"].includes(code)) {
|
|
346
356
|
return "live-issue";
|
|
347
357
|
}
|
|
348
358
|
if (code === "missing-compat-record") {
|
|
@@ -369,6 +379,7 @@ function issueClassFor(code, options) {
|
|
|
369
379
|
[
|
|
370
380
|
"manifest-unknown-contracts",
|
|
371
381
|
"manifest-unknown-fields",
|
|
382
|
+
"manifest-name-missing",
|
|
372
383
|
"package-json-missing",
|
|
373
384
|
"package-manifest-version-drift",
|
|
374
385
|
"package-min-host-version-drift",
|
|
@@ -397,7 +408,7 @@ function severityForClass(code, defaultSeverity, options) {
|
|
|
397
408
|
if (
|
|
398
409
|
options.issueClass === "live-issue" &&
|
|
399
410
|
["none", "untracked"].includes(options.compatStatus) &&
|
|
400
|
-
["unknown-hook-name", "unknown-registration-name", "package-entrypoint-missing"
|
|
411
|
+
["unknown-hook-name", "unknown-registration-name", "package-entrypoint-missing"].includes(code)
|
|
401
412
|
) {
|
|
402
413
|
return "P0";
|
|
403
414
|
}
|
package/src/report.js
CHANGED
|
@@ -225,6 +225,10 @@ export function classifyCompatRecordCoverage({ targetOpenClaw, findings, suggest
|
|
|
225
225
|
continue;
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
if (finding.code === "sdk-export-missing") {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
|
|
228
232
|
suggestions.push({
|
|
229
233
|
fixture: finding.fixture,
|
|
230
234
|
code: "missing-compat-record",
|
|
@@ -28,7 +28,7 @@ export function applyRuntimeExecutionCoverage({ findings = [], executionResults
|
|
|
28
28
|
export function buildRuntimeExecutionCoverage(executionResults) {
|
|
29
29
|
const fixtures = new Map();
|
|
30
30
|
for (const artifact of executionResults?.artifacts ?? []) {
|
|
31
|
-
if (artifact.kind
|
|
31
|
+
if (!["capture", "synthetic"].includes(artifact.kind)) {
|
|
32
32
|
continue;
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -84,6 +84,9 @@ function expectedRuntimeCaptureKeys(finding) {
|
|
|
84
84
|
if (finding.code === "conversation-access-hook") {
|
|
85
85
|
return names.map((name) => `hook:${name}`);
|
|
86
86
|
}
|
|
87
|
+
if (finding.code === "before-tool-call-probe") {
|
|
88
|
+
return ["hook:before_tool_call"];
|
|
89
|
+
}
|
|
87
90
|
return [];
|
|
88
91
|
}
|
|
89
92
|
|
package/src/sdk-mock.js
CHANGED
|
@@ -377,6 +377,9 @@ function parseModuleImports(text) {
|
|
|
377
377
|
];
|
|
378
378
|
for (const pattern of patterns) {
|
|
379
379
|
for (const match of text.matchAll(pattern)) {
|
|
380
|
+
if (isTypeOnlyImportOrExport(match[0], match[1] ?? "")) {
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
380
383
|
const specifier = match[2];
|
|
381
384
|
if (specifier) {
|
|
382
385
|
entries.push({ specifier, names: parseNamedImports(match[1] ?? "") });
|
|
@@ -389,6 +392,10 @@ function parseModuleImports(text) {
|
|
|
389
392
|
return entries;
|
|
390
393
|
}
|
|
391
394
|
|
|
395
|
+
function isTypeOnlyImportOrExport(statement, clause) {
|
|
396
|
+
return /^\s*import\s+type\b/u.test(statement) || /^\s*export\s+type\b/u.test(statement) || /^\s*type\b/u.test(clause);
|
|
397
|
+
}
|
|
398
|
+
|
|
392
399
|
function parseNamedImports(clause) {
|
|
393
400
|
const names = new Set();
|
|
394
401
|
const named = /\{([\s\S]*?)\}/.exec(clause)?.[1] ?? clause;
|
|
@@ -459,11 +466,22 @@ export async function resolve(specifier, context, nextResolve) {
|
|
|
459
466
|
export async function load(url, context, nextLoad) {
|
|
460
467
|
if (url.startsWith("file:") && /\\.[cm]?ts$/u.test(fileURLToPath(url))) {
|
|
461
468
|
const rawSource = await readFile(fileURLToPath(url), "utf8");
|
|
462
|
-
return { format: "module", source:
|
|
469
|
+
return { format: "module", source: stripPluginTypeScript(rawSource), shortCircuit: true };
|
|
463
470
|
}
|
|
464
471
|
return nextLoad(url, context);
|
|
465
472
|
}
|
|
466
473
|
|
|
474
|
+
function stripPluginTypeScript(source) {
|
|
475
|
+
try {
|
|
476
|
+
return stripTypeScriptTypes(source, { mode: "transform" });
|
|
477
|
+
} catch (error) {
|
|
478
|
+
if (error?.code !== "ERR_INVALID_ARG_VALUE") {
|
|
479
|
+
throw error;
|
|
480
|
+
}
|
|
481
|
+
return stripTypeScriptTypes(source, { mode: "strip" });
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
467
485
|
function moduleUrl(filePath) {
|
|
468
486
|
return { url: pathToFileURL(filePath).href, shortCircuit: true };
|
|
469
487
|
}
|
|
@@ -1665,11 +1683,53 @@ export const OPENAI_RESPONSES_STREAM_HOOKS = buildProviderStreamFamilyHooks("ope
|
|
|
1665
1683
|
export const OPENROUTER_THINKING_STREAM_HOOKS = buildProviderStreamFamilyHooks("openrouter-thinking");
|
|
1666
1684
|
export const TOOL_STREAM_DEFAULT_ON_HOOKS = buildProviderStreamFamilyHooks("tool-stream-default");
|
|
1667
1685
|
export const pluginSdkMock = true;
|
|
1686
|
+
${dynamicExportNames.length > 0 ? mockValueRuntimeSource() : ""}
|
|
1668
1687
|
${dynamicExportNames.map(genericExportStatement).join("\n")}
|
|
1669
1688
|
|
|
1670
1689
|
export default {
|
|
1671
1690
|
${[...exportNames].map((name) => ` ${name},`).join("\n")}
|
|
1672
1691
|
};
|
|
1692
|
+
`;
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
function mockValueRuntimeSource() {
|
|
1696
|
+
return `function createMockValue(name) {
|
|
1697
|
+
function fn(...args) {
|
|
1698
|
+
if (name === "resolvePreferredOpenClawTmpDir") {
|
|
1699
|
+
return process.env.TMPDIR || "/tmp";
|
|
1700
|
+
}
|
|
1701
|
+
if (name.startsWith("normalize")) {
|
|
1702
|
+
return typeof args[0] === "string" ? args[0] : "";
|
|
1703
|
+
}
|
|
1704
|
+
if (name === "jsonResult") {
|
|
1705
|
+
return { type: "json", value: args[0] };
|
|
1706
|
+
}
|
|
1707
|
+
if (name === "readStringParam") {
|
|
1708
|
+
return typeof args[0] === "string" ? args[0] : "";
|
|
1709
|
+
}
|
|
1710
|
+
return createMockValue(name);
|
|
1711
|
+
}
|
|
1712
|
+
return new Proxy(fn, {
|
|
1713
|
+
get(_target, property) {
|
|
1714
|
+
if (property === "then") {
|
|
1715
|
+
return undefined;
|
|
1716
|
+
}
|
|
1717
|
+
if (property === Symbol.toPrimitive) {
|
|
1718
|
+
return () => name;
|
|
1719
|
+
}
|
|
1720
|
+
if (property === "toString") {
|
|
1721
|
+
return () => name;
|
|
1722
|
+
}
|
|
1723
|
+
if (property === "valueOf") {
|
|
1724
|
+
return () => name;
|
|
1725
|
+
}
|
|
1726
|
+
return createMockValue(\`\${name}.\${String(property)}\`);
|
|
1727
|
+
},
|
|
1728
|
+
construct() {
|
|
1729
|
+
return createMockValue(name);
|
|
1730
|
+
},
|
|
1731
|
+
});
|
|
1732
|
+
}
|
|
1673
1733
|
`;
|
|
1674
1734
|
}
|
|
1675
1735
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { rmSync } from "node:fs";
|
|
2
|
+
import { mkdtemp } from "node:fs/promises";
|
|
3
|
+
import { register } from "node:module";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { pathToFileURL } from "node:url";
|
|
7
|
+
import { captureEntrypoint } from "./inspector.js";
|
|
8
|
+
import { createMockSdkPackage } from "./sdk-mock.js";
|
|
9
|
+
import { runCapturedSyntheticProbes } from "./synthetic-probes.js";
|
|
10
|
+
|
|
11
|
+
export async function runEntrypointSyntheticProbes(entrypoint, options = {}) {
|
|
12
|
+
const capture = await captureEntrypointForSyntheticProbes(entrypoint, {
|
|
13
|
+
...options,
|
|
14
|
+
apiOptions: {
|
|
15
|
+
...(options.apiOptions ?? {}),
|
|
16
|
+
retainHandlers: true,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
return runCapturedSyntheticProbes(capture, options);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function captureEntrypointForSyntheticProbes(entrypoint, options) {
|
|
23
|
+
if (options.mockSdk !== true) {
|
|
24
|
+
return captureEntrypoint(entrypoint, options);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const cwd = options.cwd ?? process.cwd();
|
|
28
|
+
const resolvedEntrypoint = path.resolve(cwd, entrypoint);
|
|
29
|
+
const pluginRoot = path.resolve(cwd, options.pluginRoot ?? path.dirname(resolvedEntrypoint));
|
|
30
|
+
const workspace = await mkdtemp(path.join(os.tmpdir(), "plugin-inspector-synthetic-mock-sdk-"));
|
|
31
|
+
cleanupTempDirOnExit(workspace);
|
|
32
|
+
const { loaderPath } = await createMockSdkPackage(workspace, { pluginRoot });
|
|
33
|
+
register(pathToFileURL(loaderPath));
|
|
34
|
+
|
|
35
|
+
return captureEntrypoint(entrypoint, {
|
|
36
|
+
...options,
|
|
37
|
+
cwd,
|
|
38
|
+
mockSdk: false,
|
|
39
|
+
pluginRoot,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function cleanupTempDirOnExit(dir) {
|
|
44
|
+
process.once("exit", () => {
|
|
45
|
+
rmSync(dir, { force: true, recursive: true });
|
|
46
|
+
});
|
|
47
|
+
}
|
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { mkdtemp } from "node:fs/promises";
|
|
4
|
-
import { register } from "node:module";
|
|
5
|
-
import os from "node:os";
|
|
6
|
-
import path from "node:path";
|
|
7
|
-
import { pathToFileURL } from "node:url";
|
|
8
|
-
import { captureEntrypoint, runCapturedSyntheticProbes, writeArtifacts } from "./advanced.js";
|
|
9
|
-
import { createMockSdkPackage } from "./sdk-mock.js";
|
|
2
|
+
import { runEntrypointSyntheticProbes, writeArtifacts } from "./advanced.js";
|
|
10
3
|
|
|
11
4
|
const args = process.argv.slice(2);
|
|
12
5
|
|
|
@@ -33,12 +26,10 @@ async function run(commandArgs) {
|
|
|
33
26
|
throw new Error("synthetic probes import plugin code; rerun with PLUGIN_INSPECTOR_EXECUTE_ISOLATED=1 in an isolated workspace");
|
|
34
27
|
}
|
|
35
28
|
|
|
36
|
-
const
|
|
29
|
+
const results = await runEntrypointSyntheticProbes(entrypoint, {
|
|
37
30
|
mockSdk,
|
|
38
31
|
pluginRoot,
|
|
39
32
|
apiOptions: { retainHandlers: true },
|
|
40
|
-
});
|
|
41
|
-
const results = await runCapturedSyntheticProbes(capture, {
|
|
42
33
|
includeLifecycle,
|
|
43
34
|
includeChannelRuntime,
|
|
44
35
|
includeProviderCapabilities,
|
|
@@ -52,30 +43,6 @@ async function run(commandArgs) {
|
|
|
52
43
|
}
|
|
53
44
|
}
|
|
54
45
|
|
|
55
|
-
async function captureForSyntheticProbes(entrypoint, options) {
|
|
56
|
-
if (options.mockSdk !== true) {
|
|
57
|
-
return captureEntrypoint(entrypoint, options);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const resolvedEntrypoint = path.resolve(process.cwd(), entrypoint);
|
|
61
|
-
const pluginRoot = path.resolve(process.cwd(), options.pluginRoot ?? path.dirname(resolvedEntrypoint));
|
|
62
|
-
const workspace = await mkdtemp(path.join(os.tmpdir(), "plugin-inspector-synthetic-mock-sdk-"));
|
|
63
|
-
cleanupTempDirOnExit(workspace);
|
|
64
|
-
const { loaderPath } = await createMockSdkPackage(workspace, { pluginRoot });
|
|
65
|
-
register(pathToFileURL(loaderPath));
|
|
66
|
-
return captureEntrypoint(entrypoint, {
|
|
67
|
-
...options,
|
|
68
|
-
mockSdk: false,
|
|
69
|
-
pluginRoot,
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function cleanupTempDirOnExit(dir) {
|
|
74
|
-
process.once("exit", () => {
|
|
75
|
-
rmSync(dir, { force: true, recursive: true });
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
46
|
function readFlag(commandArgs, name) {
|
|
80
47
|
const index = commandArgs.indexOf(name);
|
|
81
48
|
if (index === -1) {
|
package/src/synthetic-probes.js
CHANGED
|
@@ -89,6 +89,11 @@ export const syntheticRegistrationExecutionProfiles = {
|
|
|
89
89
|
callableProperties: [],
|
|
90
90
|
reason: "detached task runtimes are captured as registration metadata before async task execution",
|
|
91
91
|
},
|
|
92
|
+
registerEmbeddingProvider: {
|
|
93
|
+
mode: "metadata-only",
|
|
94
|
+
callableProperties: [],
|
|
95
|
+
reason: "embedding providers are captured as registration metadata before provider runtime execution",
|
|
96
|
+
},
|
|
92
97
|
registerGatewayDiscoveryService: {
|
|
93
98
|
mode: "metadata-only",
|
|
94
99
|
callableProperties: [],
|
|
@@ -116,11 +121,21 @@ export const syntheticRegistrationExecutionProfiles = {
|
|
|
116
121
|
callableProperties: [],
|
|
117
122
|
reason: "image generation providers are captured as registration metadata before provider runtime execution",
|
|
118
123
|
},
|
|
124
|
+
registerHostedMediaResolver: {
|
|
125
|
+
mode: "metadata-only",
|
|
126
|
+
callableProperties: [],
|
|
127
|
+
reason: "hosted media resolvers are captured as registration metadata before media URL resolution",
|
|
128
|
+
},
|
|
119
129
|
registerMemoryPromptSection: {
|
|
120
130
|
mode: "metadata-only",
|
|
121
131
|
callableProperties: [],
|
|
122
132
|
reason: "memory prompt section renderers are captured as metadata before prompt-runtime execution",
|
|
123
133
|
},
|
|
134
|
+
registerMeetingNotesSourceProvider: {
|
|
135
|
+
mode: "metadata-only",
|
|
136
|
+
callableProperties: [],
|
|
137
|
+
reason: "meeting notes source providers are captured as registration metadata before source discovery",
|
|
138
|
+
},
|
|
124
139
|
registerMediaUnderstandingProvider: {
|
|
125
140
|
mode: "metadata-only",
|
|
126
141
|
callableProperties: [],
|
|
@@ -161,11 +176,21 @@ export const syntheticRegistrationExecutionProfiles = {
|
|
|
161
176
|
callableProperties: [],
|
|
162
177
|
reason: "migration providers are captured as registration metadata before migration runtime execution",
|
|
163
178
|
},
|
|
179
|
+
registerModelCatalogProvider: {
|
|
180
|
+
mode: "metadata-only",
|
|
181
|
+
callableProperties: [],
|
|
182
|
+
reason: "model catalog providers are captured as registration metadata before catalog runtime execution",
|
|
183
|
+
},
|
|
164
184
|
registerMusicGenerationProvider: {
|
|
165
185
|
mode: "metadata-only",
|
|
166
186
|
callableProperties: [],
|
|
167
187
|
reason: "music generation providers are captured as registration metadata before provider runtime execution",
|
|
168
188
|
},
|
|
189
|
+
registerNodeCliFeature: {
|
|
190
|
+
mode: "metadata-only",
|
|
191
|
+
callableProperties: [],
|
|
192
|
+
reason: "node CLI features are captured as registration metadata before host CLI integration",
|
|
193
|
+
},
|
|
169
194
|
registerNodeHostCommand: {
|
|
170
195
|
mode: "metadata-only",
|
|
171
196
|
callableProperties: [],
|
|
@@ -206,6 +231,11 @@ export const syntheticRegistrationExecutionProfiles = {
|
|
|
206
231
|
callableProperties: [],
|
|
207
232
|
reason: "security audit collectors are captured as registration metadata before filesystem or policy scans",
|
|
208
233
|
},
|
|
234
|
+
registerSessionAction: {
|
|
235
|
+
mode: "metadata-only",
|
|
236
|
+
callableProperties: [],
|
|
237
|
+
reason: "session actions are captured as registration metadata before session runtime execution",
|
|
238
|
+
},
|
|
209
239
|
registerService: {
|
|
210
240
|
mode: "lifecycle-opt-in",
|
|
211
241
|
callableProperties: ["start", "stop", "dispose"],
|
|
@@ -267,12 +297,17 @@ export const defaultSyntheticHookEvents = {
|
|
|
267
297
|
runId: "run-fixture",
|
|
268
298
|
agentId: "agent-fixture",
|
|
269
299
|
conversationId: "conversation-fixture",
|
|
300
|
+
success: true,
|
|
301
|
+
durationMs: 1,
|
|
302
|
+
error: null,
|
|
303
|
+
messages: [{ role: "assistant", content: "[redacted fixture output]" }],
|
|
270
304
|
status: "completed",
|
|
271
305
|
transcript: [{ role: "assistant", content: "[redacted fixture output]" }],
|
|
272
306
|
},
|
|
273
307
|
before_agent_start: {
|
|
274
308
|
agentId: "agent-fixture",
|
|
275
309
|
runId: "run-fixture",
|
|
310
|
+
prompt: "fixture prompt",
|
|
276
311
|
config: { source: "plugin-inspector" },
|
|
277
312
|
},
|
|
278
313
|
before_prompt_build: {
|
|
@@ -303,6 +338,18 @@ export const defaultSyntheticHookEvents = {
|
|
|
303
338
|
model: "gpt-5.4",
|
|
304
339
|
output: { role: "assistant", content: "[redacted fixture output]" },
|
|
305
340
|
},
|
|
341
|
+
message_received: {
|
|
342
|
+
from: "fixture-user",
|
|
343
|
+
content: "fixture inbound message",
|
|
344
|
+
messageId: "message-fixture",
|
|
345
|
+
},
|
|
346
|
+
message_sent: {
|
|
347
|
+
to: "fixture-user",
|
|
348
|
+
content: "fixture outbound message",
|
|
349
|
+
success: true,
|
|
350
|
+
error: null,
|
|
351
|
+
messageId: "message-fixture-reply",
|
|
352
|
+
},
|
|
306
353
|
subagent_delivery_target: {
|
|
307
354
|
childSessionKey: "child-session",
|
|
308
355
|
agentId: "agent-child",
|
|
@@ -361,6 +408,18 @@ export const defaultSyntheticHookContexts = {
|
|
|
361
408
|
agentId: "agent-fixture",
|
|
362
409
|
sessionId: "session-fixture",
|
|
363
410
|
},
|
|
411
|
+
message_received: {
|
|
412
|
+
runId: "run-fixture",
|
|
413
|
+
agentId: "agent-fixture",
|
|
414
|
+
sessionId: "session-fixture",
|
|
415
|
+
channelId: "fixture-channel",
|
|
416
|
+
},
|
|
417
|
+
message_sent: {
|
|
418
|
+
runId: "run-fixture",
|
|
419
|
+
agentId: "agent-fixture",
|
|
420
|
+
sessionId: "session-fixture",
|
|
421
|
+
channelId: "fixture-channel",
|
|
422
|
+
},
|
|
364
423
|
subagent_delivery_target: {
|
|
365
424
|
runId: "run-fixture",
|
|
366
425
|
parentAgentId: "agent-parent",
|