@aiwg/cli 2026.9.2 → 2026.9.4
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/README.md +21 -0
- package/agentic/code/providers/antigravity/provider-contract.v1.json +121 -0
- package/agentic/code/providers/capability-matrix.yaml +87 -1
- package/agentic/code/providers/model-capabilities.v1.json +31 -0
- package/agentic/code/providers/model-catalog.v1.json +29 -0
- package/agentic/code/providers/omp/README.md +58 -0
- package/agentic/code/providers/omp/aiwg-bridge.ts +52 -0
- package/dist/src/agents/agent-deployer.js +18 -0
- package/dist/src/agents/agent-packager.js +25 -0
- package/dist/src/artifacts/backends/sqlite-backend.js +18 -10
- package/dist/src/artifacts/query-engine.js +30 -0
- package/dist/src/cli/agent-spawn.js +13 -2
- package/dist/src/cli/handlers/help.js +3 -1
- package/dist/src/cli/handlers/init.js +2 -0
- package/dist/src/cli/handlers/models.js +1 -1
- package/dist/src/cli/handlers/runtime-info.js +8 -1
- package/dist/src/cli/handlers/session.js +5 -4
- package/dist/src/cli/handlers/sessions.js +26 -9
- package/dist/src/cli/handlers/setup.js +9 -2
- package/dist/src/cli/handlers/steward.js +13 -2
- package/dist/src/cli/handlers/subcommands.js +11 -0
- package/dist/src/cli/handlers/team.js +68 -7
- package/dist/src/cli/handlers/use.js +158 -9
- package/dist/src/cli/scope-resolver.js +7 -0
- package/dist/src/config/aiwg-config.js +1 -0
- package/dist/src/dataset/fortemi-live-qualification.d.ts +53 -0
- package/dist/src/dataset/fortemi-live-qualification.js +297 -0
- package/dist/src/dataset/index.d.ts +1 -0
- package/dist/src/dataset/index.js +1 -0
- package/dist/src/mcp/cli.mjs +30 -1
- package/dist/src/mcp/omp-config.mjs +128 -0
- package/dist/src/mcp/registry.js +45 -5
- package/dist/src/mcp/registry.mjs +29 -6
- package/dist/src/models/model-capabilities.v1.json +31 -0
- package/dist/src/models/model-catalog.v1.json +29 -0
- package/dist/src/models/model-discovery.js +46 -5
- package/dist/src/models/provider-policy.js +5 -3
- package/dist/src/plugin/skill-command-translator.js +2 -0
- package/dist/src/providers/capability-matrix.yaml +87 -1
- package/dist/src/providers/omp-agent.mjs +40 -0
- package/dist/src/providers/omp-diagnostics.mjs +15 -0
- package/dist/src/providers/omp-paths.mjs +38 -0
- package/dist/src/providers/provider-definitions.js +83 -0
- package/dist/src/providers/provider-definitions.mjs +25 -1
- package/dist/src/providers/provider-inventory.js +2 -0
- package/dist/src/sessions/adapters/omp.js +203 -0
- package/dist/src/sessions/batch-import.js +7 -0
- package/dist/src/sessions/contracts.js +1 -1
- package/dist/src/sessions/importer.js +4 -3
- package/dist/src/sessions/index.js +1 -0
- package/dist/src/sessions/readers.js +4 -3
- package/dist/src/sessions/workspace-discovery.js +12 -2
- package/dist/src/skills/deployer.js +21 -1
- package/dist/src/smiths/agentsmith/generator.js +1 -0
- package/dist/src/smiths/context-pipeline/parallelism-section.js +2 -0
- package/dist/src/smiths/context-pipeline/provider-policy.js +2 -2
- package/dist/src/smiths/context-pipeline/workspace-context.js +2 -2
- package/dist/src/storage/backends/fortemi.js +142 -17
- package/dist/src/storage/fortemi-qualification-receipt.js +206 -0
- package/dist/src/storage/fortemi-qualification.js +67 -6
- package/dist/src/storage/index.js +1 -0
- package/package.json +2 -1
- package/schemas/dataset/fortemi-live-qualification-receipt.v1.schema.json +132 -0
- package/tools/agents/deploy-agents.mjs +6 -3
- package/tools/agents/providers/antigravity.mjs +147 -0
- package/tools/agents/providers/omp.d.mts +4 -0
- package/tools/agents/providers/omp.mjs +256 -0
- package/tools/providers/antigravity-transport.mjs +124 -0
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
import fs from 'fs/promises';
|
|
13
13
|
import path from 'path';
|
|
14
14
|
import os from 'os';
|
|
15
|
+
import { pathToFileURL } from 'node:url';
|
|
16
|
+
import { resolveOmpPaths } from '../../providers/omp-paths.mjs';
|
|
15
17
|
import YAML from 'yaml';
|
|
16
18
|
import { createScriptRunner } from './script-runner.js';
|
|
17
19
|
import { getFrameworkRoot, getVersionInfo } from '../../channel/manager.mjs';
|
|
@@ -247,6 +249,36 @@ function resolveFrameworkDir(framework) {
|
|
|
247
249
|
* aiwg-dev is contributor-only tooling — not for end users.
|
|
248
250
|
*/
|
|
249
251
|
export const USE_ALL_DISALLOW = new Set(['aiwg-dev']);
|
|
252
|
+
/** Full-framework setup requires the corpus omitted by the lightweight CLI. */
|
|
253
|
+
async function bundledSetupPrerequisiteMessage(frameworkRoot) {
|
|
254
|
+
let packageName;
|
|
255
|
+
try {
|
|
256
|
+
packageName = JSON.parse(await fs.readFile(path.join(frameworkRoot, 'package.json'), 'utf8')).name;
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
// Embedded callers and source fixtures need not have package metadata.
|
|
260
|
+
return undefined;
|
|
261
|
+
}
|
|
262
|
+
if (packageName !== '@aiwg/cli')
|
|
263
|
+
return undefined;
|
|
264
|
+
const hasCorpus = (await Promise.all(['frameworks', 'addons'].map(async (kind) => {
|
|
265
|
+
try {
|
|
266
|
+
return (await fs.stat(path.join(frameworkRoot, 'agentic/code', kind))).isDirectory();
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
}))).every(Boolean);
|
|
272
|
+
if (hasCorpus)
|
|
273
|
+
return undefined;
|
|
274
|
+
return [
|
|
275
|
+
'Bundled framework setup requires the full aiwg package. This @aiwg/cli installation does not include framework and addon sources.',
|
|
276
|
+
'Replace the lightweight global package, then rerun your setup command:',
|
|
277
|
+
' npm uninstall -g @aiwg/cli',
|
|
278
|
+
' npm install -g aiwg',
|
|
279
|
+
'@aiwg/cli can still query signed web resources and deploy external project-local bundles.',
|
|
280
|
+
].join('\n');
|
|
281
|
+
}
|
|
250
282
|
/**
|
|
251
283
|
* Discover all addon names from the filesystem, minus the disallow list.
|
|
252
284
|
*/
|
|
@@ -1041,10 +1073,10 @@ async function reconcileDeployedSkillAssets(bundlePath, target, provider, option
|
|
|
1041
1073
|
}
|
|
1042
1074
|
const paths = getProviderPaths(provider);
|
|
1043
1075
|
const kernelSkillsPath = getProviderKernelSkillsPath(provider);
|
|
1044
|
-
const deployRoots =
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1076
|
+
const deployRoots = provider === 'omp' && options.scope === 'user'
|
|
1077
|
+
? [path.join(resolveOmpPaths({ cwd: target }).agentDir, 'skills')]
|
|
1078
|
+
: [...new Set([paths.skills, kernelSkillsPath]
|
|
1079
|
+
.filter((value) => Boolean(value)).map(value => resolveDeployPath(target, value)))];
|
|
1048
1080
|
for (const skillName of skillDirs) {
|
|
1049
1081
|
const sourceSkillDir = path.join(skillsRoot, skillName);
|
|
1050
1082
|
const sourceSkillMd = path.join(sourceSkillDir, 'SKILL.md');
|
|
@@ -1055,6 +1087,8 @@ async function reconcileDeployedSkillAssets(bundlePath, target, provider, option
|
|
|
1055
1087
|
catch {
|
|
1056
1088
|
continue;
|
|
1057
1089
|
}
|
|
1090
|
+
if (options.skipUndeployed && !(await Promise.all(deployRoots.map(root => fileExists(path.join(root, skillName, 'SKILL.md'))))).some(Boolean))
|
|
1091
|
+
continue;
|
|
1058
1092
|
const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1] ?? '';
|
|
1059
1093
|
const declaredEntrypoint = frontmatter
|
|
1060
1094
|
.match(/^[ \t]+entrypoint:\s*["']?([^"'\s]+)["']?\s*$/m)?.[1];
|
|
@@ -1097,6 +1131,11 @@ async function reconcileDeployedSkillAssets(bundlePath, target, provider, option
|
|
|
1097
1131
|
if (!deployedSkillRoot)
|
|
1098
1132
|
throw new Error(`deployed skill '${skillName}' not found while reconciling support assets`);
|
|
1099
1133
|
const destination = path.join(deployedSkillRoot, skillName, ...normalized.split('/'));
|
|
1134
|
+
if (provider === 'omp') {
|
|
1135
|
+
const adapter = await import(pathToFileURL(path.join(await getFrameworkRoot(), 'tools/agents/providers/omp.mjs')).href);
|
|
1136
|
+
adapter.deploySkillSupportAsset(source, destination, { quiet: true });
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
1100
1139
|
await fs.mkdir(path.dirname(destination), { recursive: true });
|
|
1101
1140
|
await fs.copyFile(source, destination);
|
|
1102
1141
|
const mode = (await fs.stat(source)).mode & 0o777;
|
|
@@ -1348,6 +1387,18 @@ async function deployProjectLocalBundles(opts) {
|
|
|
1348
1387
|
}
|
|
1349
1388
|
}
|
|
1350
1389
|
}
|
|
1390
|
+
// Automatic reconciliation (also used by refresh/upgrade) must restore the
|
|
1391
|
+
// project search cache for existing bundles, just as a named local install
|
|
1392
|
+
// does. Named installs refresh once after all selected providers finish.
|
|
1393
|
+
if (deployed > 0 && !dryRun && !onlyBundleId) {
|
|
1394
|
+
try {
|
|
1395
|
+
await rebuildExternalBundleIndex(projectDir, 'project', verbose);
|
|
1396
|
+
}
|
|
1397
|
+
catch (error) {
|
|
1398
|
+
failed++;
|
|
1399
|
+
ui.warn(`Project-local index refresh failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1351
1402
|
// Refresh the project kernel quickref from either legacy operator input or
|
|
1352
1403
|
// managed project-local discovery whenever bundles deploy.
|
|
1353
1404
|
const { hasProjectQuickref, deployProjectQuickref } = await import('../../extensions/project-quickref.js');
|
|
@@ -1968,6 +2019,65 @@ async function rebuildExternalBundleIndex(projectDir, graph, verbose) {
|
|
|
1968
2019
|
}
|
|
1969
2020
|
ui.dim(` Refreshed ${graph}-scope capability index`);
|
|
1970
2021
|
}
|
|
2022
|
+
/** User OMP writes use native ownership receipts instead of an unconditional copy. */
|
|
2023
|
+
async function deployOmpUserSource(opts) {
|
|
2024
|
+
const adapter = await import(pathToFileURL(path.join(opts.frameworkRoot, 'tools/agents/providers/omp.mjs')).href);
|
|
2025
|
+
await adapter.deploy({ srcRoot: opts.source, target: opts.target, provider: 'omp', scope: 'user',
|
|
2026
|
+
mode: opts.mode ?? 'general', deployCommands: true, deploySkills: true, deployRules: true,
|
|
2027
|
+
copyStandardSkills: opts.copyAll, quiet: true, deployVersion: (await getVersionInfo()).version });
|
|
2028
|
+
const sourceBundles = new Set([opts.source]);
|
|
2029
|
+
const { resourceDirs } = resolveOmpPaths({ cwd: opts.target });
|
|
2030
|
+
const entries = {
|
|
2031
|
+
agents: [], commands: [], skills: [], rules: [], behaviors: [],
|
|
2032
|
+
};
|
|
2033
|
+
const belongsToBundle = (entry) => {
|
|
2034
|
+
if (entry.provider !== 'omp' || typeof entry.source !== 'string')
|
|
2035
|
+
return false;
|
|
2036
|
+
if (entry.transformation === 'omp-extension')
|
|
2037
|
+
return true;
|
|
2038
|
+
const relative = path.relative(opts.source, entry.source);
|
|
2039
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
2040
|
+
};
|
|
2041
|
+
const locations = resourceDirs;
|
|
2042
|
+
for (const kind of Object.keys(entries)) {
|
|
2043
|
+
const directory = locations[kind];
|
|
2044
|
+
if (kind === 'skills') {
|
|
2045
|
+
let children;
|
|
2046
|
+
try {
|
|
2047
|
+
children = await fs.readdir(directory);
|
|
2048
|
+
}
|
|
2049
|
+
catch {
|
|
2050
|
+
continue;
|
|
2051
|
+
}
|
|
2052
|
+
for (const child of children) {
|
|
2053
|
+
try {
|
|
2054
|
+
const receipt = JSON.parse(await fs.readFile(path.join(directory, child, '.aiwg-manifest.json'), 'utf8'));
|
|
2055
|
+
if (receipt.managed?.['SKILL.md'] && belongsToBundle(receipt.managed['SKILL.md'])) {
|
|
2056
|
+
entries.skills.push(child);
|
|
2057
|
+
const source = receipt.managed['SKILL.md'].source;
|
|
2058
|
+
if (typeof source === 'string')
|
|
2059
|
+
sourceBundles.add(path.dirname(path.dirname(path.dirname(source))));
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
catch { /* operator resources have no OMP receipt */ }
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
else {
|
|
2066
|
+
try {
|
|
2067
|
+
const receipt = JSON.parse(await fs.readFile(path.join(directory, '.aiwg-manifest.json'), 'utf8'));
|
|
2068
|
+
entries[kind] = Object.entries(receipt.managed ?? {}).filter(([, entry]) => belongsToBundle(entry)).map(([name]) => name);
|
|
2069
|
+
}
|
|
2070
|
+
catch { /* no deployed resources of this kind */ }
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
for (const source of sourceBundles)
|
|
2074
|
+
await reconcileDeployedSkillAssets(source, opts.target, 'omp', { strictReferences: false, scope: 'user', skipUndeployed: !opts.copyAll });
|
|
2075
|
+
const { recordUserDeploy } = await import('../../config/user-registry.js');
|
|
2076
|
+
await recordUserDeploy({ framework: opts.bundle, provider: 'omp',
|
|
2077
|
+
version: (await getVersionInfo()).version, source: 'bundled',
|
|
2078
|
+
counts: { agents: entries.agents.length, commands: entries.commands.length,
|
|
2079
|
+
skills: entries.skills.length, rules: entries.rules.length }, entries });
|
|
2080
|
+
}
|
|
1971
2081
|
async function mirrorProjectLocalBundleToUserScope(opts) {
|
|
1972
2082
|
const paths = getProviderPaths(opts.provider);
|
|
1973
2083
|
const resolveProjectPath = (value) => !value ? '' : path.isAbsolute(value) ? value : path.join(opts.target, value);
|
|
@@ -2226,6 +2336,13 @@ export class UseHandler {
|
|
|
2226
2336
|
if (framework === 'cockpit') {
|
|
2227
2337
|
return installCockpit(ctx, remainingArgs);
|
|
2228
2338
|
}
|
|
2339
|
+
// Check before auto-init, global staging, or deployment can alter a project.
|
|
2340
|
+
// Web lookup does not materialize the corpus required by bundled setup.
|
|
2341
|
+
if (VALID_FRAMEWORKS.includes(framework)) {
|
|
2342
|
+
const prerequisite = await bundledSetupPrerequisiteMessage(ctx.frameworkRoot || await getFrameworkRoot());
|
|
2343
|
+
if (prerequisite)
|
|
2344
|
+
return { exitCode: 1, message: prerequisite };
|
|
2345
|
+
}
|
|
2229
2346
|
// Structured logger for this invocation. Records go to both stderr (if
|
|
2230
2347
|
// verbose level) and ~/.aiwg/logs/aiwg-YYYY-MM-DD.jsonl with full
|
|
2231
2348
|
// provenance (invocation_id, aiwg_version, git_sha, etc.). #925.
|
|
@@ -2701,10 +2818,25 @@ export class UseHandler {
|
|
|
2701
2818
|
if (isAddon || isExtension) {
|
|
2702
2819
|
const providerIdx = remainingArgs.findIndex(a => a === '--provider' || a === '--platform');
|
|
2703
2820
|
const explicitAddonProvider = providerIdx >= 0 && remainingArgs[providerIdx + 1] ? remainingArgs[providerIdx + 1] : null;
|
|
2704
|
-
const provider = explicitAddonProvider ?? (config?.providers?.[0] ?? 'claude');
|
|
2821
|
+
const provider = resolveBuiltInProviderForUse(explicitAddonProvider ?? (config?.providers?.[0] ?? 'claude')).provider;
|
|
2705
2822
|
const targetIdx = remainingArgs.findIndex(a => a === '--target');
|
|
2706
2823
|
const target = targetIdx >= 0 && remainingArgs[targetIdx + 1] ? remainingArgs[targetIdx + 1] : process.cwd();
|
|
2707
2824
|
const dryRunAddon = remainingArgs.includes('--dry-run');
|
|
2825
|
+
let addonScope;
|
|
2826
|
+
try {
|
|
2827
|
+
addonScope = detectScope(remainingArgs);
|
|
2828
|
+
if (addonScope === 'project' && remainingArgs.includes('--user'))
|
|
2829
|
+
addonScope = 'user';
|
|
2830
|
+
}
|
|
2831
|
+
catch (error) {
|
|
2832
|
+
return { exitCode: 1, message: `Error: ${error instanceof Error ? error.message : String(error)}` };
|
|
2833
|
+
}
|
|
2834
|
+
if (addonScope === 'user' && !USER_SCOPE_PATHS[provider]) {
|
|
2835
|
+
return {
|
|
2836
|
+
exitCode: 1,
|
|
2837
|
+
message: `--scope user not supported for provider '${provider}' — see docs/customization/user-scope-deployment.md for the supported list`,
|
|
2838
|
+
};
|
|
2839
|
+
}
|
|
2708
2840
|
const runner = createScriptRunner(frameworkRoot);
|
|
2709
2841
|
const addonBaseArgs = ['--deploy-commands', '--deploy-skills', '--deploy-rules'];
|
|
2710
2842
|
// An explicitly selected upstream addon must be self-contained in the
|
|
@@ -2797,6 +2929,14 @@ export class UseHandler {
|
|
|
2797
2929
|
};
|
|
2798
2930
|
}
|
|
2799
2931
|
}
|
|
2932
|
+
if (!dryRunAddon && provider === 'omp' && (detectScope(remainingArgs) === 'user' || remainingArgs.includes('--user'))) {
|
|
2933
|
+
try {
|
|
2934
|
+
await deployOmpUserSource({ frameworkRoot, source: addonSource, target, bundle: framework, copyAll: true });
|
|
2935
|
+
}
|
|
2936
|
+
catch (error) {
|
|
2937
|
+
return { exitCode: 1, message: `OMP user deployment failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2800
2940
|
// Register only artifacts actually written by a confirmed deployment.
|
|
2801
2941
|
if (!dryRunAddon) {
|
|
2802
2942
|
try {
|
|
@@ -2922,7 +3062,7 @@ export class UseHandler {
|
|
|
2922
3062
|
catch {
|
|
2923
3063
|
// Profile selection is optional — don't fail deployment
|
|
2924
3064
|
}
|
|
2925
|
-
if (framework === 'aiwg-utils' &&
|
|
3065
|
+
if (framework === 'aiwg-utils' && !['pi', 'omp'].includes(provider) && !remainingArgs.includes('--dry-run')) {
|
|
2926
3066
|
const wrapperValidation = await validateDeployedModelWrappers({
|
|
2927
3067
|
provider: normalizeProviderDefinitionId(provider) ?? provider,
|
|
2928
3068
|
target,
|
|
@@ -3025,7 +3165,7 @@ export class UseHandler {
|
|
|
3025
3165
|
? withProviderOverride(deployFilteredArgs, provider)
|
|
3026
3166
|
: deployFilteredArgs;
|
|
3027
3167
|
const bulkKernelOnly = framework === 'all'
|
|
3028
|
-
&&
|
|
3168
|
+
&& !['pi', 'omp'].includes(provider)
|
|
3029
3169
|
&& !remainingArgs.includes('--copy-all')
|
|
3030
3170
|
&& !remainingArgs.includes('--copy-standard-skills');
|
|
3031
3171
|
if (bulkKernelOnly)
|
|
@@ -3234,7 +3374,7 @@ export class UseHandler {
|
|
|
3234
3374
|
// Pi model selection/headless routing is delivered by #2151. Until that
|
|
3235
3375
|
// adapter exists, do not require model-wrapper artifacts that Pi cannot
|
|
3236
3376
|
// load; resource deployment remains independently valid.
|
|
3237
|
-
if (!dryRun && !skipUtils && !bulkKernelOnly &&
|
|
3377
|
+
if (!dryRun && !skipUtils && !bulkKernelOnly && !['pi', 'omp'].includes(provider)) {
|
|
3238
3378
|
const wrapperValidation = await validateDeployedModelWrappers({
|
|
3239
3379
|
provider,
|
|
3240
3380
|
target,
|
|
@@ -3424,7 +3564,16 @@ export class UseHandler {
|
|
|
3424
3564
|
// mirror, record the deploy in the per-user registry at
|
|
3425
3565
|
// ~/.aiwg/installed.json so `aiwg list --scope user` and `aiwg remove
|
|
3426
3566
|
// --scope user` can find it from any cwd.
|
|
3427
|
-
if (scope === 'user' && provider
|
|
3567
|
+
if (scope === 'user' && provider === 'omp' && !dryRun) {
|
|
3568
|
+
try {
|
|
3569
|
+
await deployOmpUserSource({ frameworkRoot, source: frameworkRoot, target, bundle: framework, mode,
|
|
3570
|
+
copyAll: remainingArgs.includes('--copy-all') || remainingArgs.includes('--copy-standard-skills') });
|
|
3571
|
+
}
|
|
3572
|
+
catch (error) {
|
|
3573
|
+
return { exitCode: 1, message: `OMP user deployment failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
3574
|
+
}
|
|
3575
|
+
}
|
|
3576
|
+
if (scope === 'user' && provider !== 'openhuman' && provider !== 'omp' && !dryRun) {
|
|
3428
3577
|
try {
|
|
3429
3578
|
const paths = getProviderPaths(provider);
|
|
3430
3579
|
const resolveProjectPath = (p) => !p ? '' : path.isAbsolute(p) ? p : path.join(target, p);
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* Per ADR-4 §2 path map. Per ADR-4 §1: `--scope user` and `--scope
|
|
10
10
|
* project` are mutually exclusive; default is `project`.
|
|
11
11
|
*/
|
|
12
|
+
import { resolveOmpPaths } from '../providers/omp-paths.mjs';
|
|
12
13
|
import { homedir } from 'node:os';
|
|
13
14
|
import * as path from 'node:path';
|
|
14
15
|
import { resolveHermesHome, resolveHermesHomePath } from '../providers/hermes-home.js';
|
|
@@ -57,6 +58,12 @@ export const USER_SCOPE_PATHS = {
|
|
|
57
58
|
rules: '',
|
|
58
59
|
behaviors: '',
|
|
59
60
|
},
|
|
61
|
+
get omp() {
|
|
62
|
+
const { agentDir } = resolveOmpPaths();
|
|
63
|
+
return { agents: path.join(agentDir, 'agents'), skills: path.join(agentDir, 'skills'),
|
|
64
|
+
commands: path.join(agentDir, 'prompts'), rules: path.join(agentDir, 'rules'),
|
|
65
|
+
behaviors: path.join(agentDir, 'extensions') };
|
|
66
|
+
},
|
|
60
67
|
pi: {
|
|
61
68
|
// Pi's global resource root is configurable. Unlike project deployment,
|
|
62
69
|
// user-scope resources belong under the effective agent directory. Keep
|
|
@@ -89,6 +89,7 @@ export function resolveDelivery(delivery) {
|
|
|
89
89
|
* - unknown: conservative 4 default.
|
|
90
90
|
*/
|
|
91
91
|
export const PROVIDER_PARALLELISM_DEFAULTS = {
|
|
92
|
+
antigravity: { max_parallel_subagents: 4, max_parallel_ralph_loops: 2, max_parallel_mc_missions: 4 },
|
|
92
93
|
claude: { max_parallel_subagents: 4, max_parallel_ralph_loops: 2, max_parallel_mc_missions: 4 },
|
|
93
94
|
codex: { max_parallel_subagents: 10, max_parallel_ralph_loops: 3, max_parallel_mc_missions: 6 },
|
|
94
95
|
copilot: { max_parallel_subagents: 10, max_parallel_ralph_loops: 3, max_parallel_mc_missions: 6 },
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { McpClientLike } from "../storage/backends/fortemi.js";
|
|
2
|
+
export declare const FORTEMI_DATASET_LIVE_CONTRACT: "aiwg.fortemi-dataset-live-qualification/v1";
|
|
3
|
+
export declare const FORTEMI_DATASET_CAPABILITIES_TOOL = "dataset_capabilities";
|
|
4
|
+
export declare const FORTEMI_DATASET_EXECUTE_TOOL = "dataset_execute";
|
|
5
|
+
export interface FortemiDatasetLiveReceipt {
|
|
6
|
+
contract: typeof FORTEMI_DATASET_LIVE_CONTRACT;
|
|
7
|
+
outcome: "pending" | "supported";
|
|
8
|
+
diagnostic: "CONFORMANCE_FORTEMI_DATASET_CONTRACT_UNAVAILABLE" | "CONFORMANCE_FORTEMI_DATASET_PREFLIGHT_SUPPORTED";
|
|
9
|
+
receiptDigest: string;
|
|
10
|
+
bindings: {
|
|
11
|
+
aiwgCommit: string;
|
|
12
|
+
endpointFingerprint: string;
|
|
13
|
+
toolSchemaDigest: string;
|
|
14
|
+
};
|
|
15
|
+
observed: {
|
|
16
|
+
serverName: string;
|
|
17
|
+
serverVersion: string;
|
|
18
|
+
};
|
|
19
|
+
namespace: string;
|
|
20
|
+
operations: Array<{
|
|
21
|
+
tool: string;
|
|
22
|
+
compatible: boolean;
|
|
23
|
+
code: string;
|
|
24
|
+
}>;
|
|
25
|
+
mutation: {
|
|
26
|
+
authorized: false;
|
|
27
|
+
attempted: false;
|
|
28
|
+
};
|
|
29
|
+
resources: {
|
|
30
|
+
maxDurationMs: number;
|
|
31
|
+
durationMs: number;
|
|
32
|
+
maxToolCount: number;
|
|
33
|
+
observedToolCount: number;
|
|
34
|
+
maxSchemaBytes: number;
|
|
35
|
+
observedSchemaBytes: number;
|
|
36
|
+
networkAttempts: number;
|
|
37
|
+
toolCalls: number;
|
|
38
|
+
};
|
|
39
|
+
startedAt: string;
|
|
40
|
+
endedAt: string;
|
|
41
|
+
}
|
|
42
|
+
/** Read-only discovery. It never invokes a Fortemi tool, even when the proposed contract is present. */
|
|
43
|
+
export declare function qualifyFortemiDatasetLivePreflight(input: {
|
|
44
|
+
client: McpClientLike;
|
|
45
|
+
endpointUrl: string;
|
|
46
|
+
aiwgCommit: string;
|
|
47
|
+
maxDurationMs?: number;
|
|
48
|
+
now?: () => Date;
|
|
49
|
+
}): Promise<FortemiDatasetLiveReceipt>;
|
|
50
|
+
export declare function verifyFortemiDatasetLiveReceipt(value: unknown): string[];
|
|
51
|
+
/** Atomically creates private evidence without replacing an existing receipt. */
|
|
52
|
+
export declare function writeFortemiDatasetLiveReceipt(path: string, receipt: FortemiDatasetLiveReceipt): Promise<void>;
|
|
53
|
+
//# sourceMappingURL=fortemi-live-qualification.d.ts.map
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { link, mkdir, unlink, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
import { endpointFingerprint, fortemiReceiptDigest, } from "../storage/fortemi-qualification-receipt.js";
|
|
5
|
+
export const FORTEMI_DATASET_LIVE_CONTRACT = "aiwg.fortemi-dataset-live-qualification/v1";
|
|
6
|
+
export const FORTEMI_DATASET_CAPABILITIES_TOOL = "dataset_capabilities";
|
|
7
|
+
export const FORTEMI_DATASET_EXECUTE_TOOL = "dataset_execute";
|
|
8
|
+
const UUID_NAMESPACE = /^aiwg-dataset-qualification-[0-9a-f-]{36}$/u;
|
|
9
|
+
const COMMIT = /^[0-9a-f]{40}$/u;
|
|
10
|
+
const DIGEST = /^sha256:[0-9a-f]{64}$/u;
|
|
11
|
+
const SAFE = /^[A-Za-z0-9._:/-]+$/u;
|
|
12
|
+
const REQUIRED_TOOLS = [
|
|
13
|
+
FORTEMI_DATASET_CAPABILITIES_TOOL,
|
|
14
|
+
FORTEMI_DATASET_EXECUTE_TOOL,
|
|
15
|
+
];
|
|
16
|
+
const MAX_TOOL_COUNT = 256;
|
|
17
|
+
const MAX_SCHEMA_BYTES = 1_048_576;
|
|
18
|
+
function record(value) {
|
|
19
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
20
|
+
? value
|
|
21
|
+
: undefined;
|
|
22
|
+
}
|
|
23
|
+
function exactKeys(value, allowed) {
|
|
24
|
+
const keys = Object.keys(value);
|
|
25
|
+
return (keys.length === allowed.length && keys.every((key) => allowed.includes(key)));
|
|
26
|
+
}
|
|
27
|
+
function material(receipt) {
|
|
28
|
+
const { receiptDigest: _digest, ...rest } = receipt;
|
|
29
|
+
return rest;
|
|
30
|
+
}
|
|
31
|
+
function inputSchemaCompatible(schema, required) {
|
|
32
|
+
const candidate = record(schema);
|
|
33
|
+
const properties = record(candidate?.properties);
|
|
34
|
+
const declaredRequired = candidate?.required;
|
|
35
|
+
if (candidate?.type !== "object" ||
|
|
36
|
+
!properties ||
|
|
37
|
+
!Array.isArray(declaredRequired))
|
|
38
|
+
return false;
|
|
39
|
+
if (!declaredRequired.every((key) => typeof key === "string") ||
|
|
40
|
+
new Set(declaredRequired).size !== declaredRequired.length)
|
|
41
|
+
return false;
|
|
42
|
+
return Object.entries(required).every(([key, type]) => {
|
|
43
|
+
const property = record(properties[key]);
|
|
44
|
+
return declaredRequired.includes(key) && property?.type === type;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async function bounded(promise, timeoutMs) {
|
|
48
|
+
let timer;
|
|
49
|
+
return Promise.race([
|
|
50
|
+
promise,
|
|
51
|
+
new Promise((_, reject) => {
|
|
52
|
+
timer = setTimeout(() => reject(new Error("CONFORMANCE_FORTEMI_DATASET_PREFLIGHT_TIMEOUT")), timeoutMs);
|
|
53
|
+
}),
|
|
54
|
+
]).finally(() => {
|
|
55
|
+
if (timer)
|
|
56
|
+
clearTimeout(timer);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/** Read-only discovery. It never invokes a Fortemi tool, even when the proposed contract is present. */
|
|
60
|
+
export async function qualifyFortemiDatasetLivePreflight(input) {
|
|
61
|
+
if (!COMMIT.test(input.aiwgCommit))
|
|
62
|
+
throw new Error("CONFORMANCE_INVALID_AIWG_COMMIT");
|
|
63
|
+
const requestedDuration = input.maxDurationMs ?? 5_000;
|
|
64
|
+
if (!Number.isFinite(requestedDuration))
|
|
65
|
+
throw new Error("CONFORMANCE_INVALID_RESOURCE_BOUND");
|
|
66
|
+
const maxDurationMs = Math.max(250, Math.min(Math.trunc(requestedDuration), 30_000));
|
|
67
|
+
const now = input.now ?? (() => new Date());
|
|
68
|
+
const startedAt = now().toISOString();
|
|
69
|
+
const namespace = `aiwg-dataset-qualification-${randomUUID()}`;
|
|
70
|
+
let schemas = [];
|
|
71
|
+
try {
|
|
72
|
+
if (!input.client.listTools)
|
|
73
|
+
throw new Error("CONFORMANCE_FORTEMI_TOOL_DISCOVERY_UNAVAILABLE");
|
|
74
|
+
const discovered = await bounded(input.client.listTools(), maxDurationMs);
|
|
75
|
+
schemas = discovered.tools ?? [];
|
|
76
|
+
if (!Array.isArray(schemas))
|
|
77
|
+
throw new Error("CONFORMANCE_FORTEMI_TOOL_INVENTORY_INVALID");
|
|
78
|
+
const inventory = schemas;
|
|
79
|
+
const observedSchemaBytes = Buffer.byteLength(JSON.stringify(inventory), "utf8");
|
|
80
|
+
if (inventory.length > MAX_TOOL_COUNT ||
|
|
81
|
+
observedSchemaBytes > MAX_SCHEMA_BYTES)
|
|
82
|
+
throw new Error("CONFORMANCE_RESOURCE_ENVELOPE_EXCEEDED");
|
|
83
|
+
const tools = new Map(inventory.map((tool) => [tool.name, tool]));
|
|
84
|
+
const specifications = [
|
|
85
|
+
{
|
|
86
|
+
tool: FORTEMI_DATASET_CAPABILITIES_TOOL,
|
|
87
|
+
required: { contract_version: "string" },
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
tool: FORTEMI_DATASET_EXECUTE_TOOL,
|
|
91
|
+
required: {
|
|
92
|
+
contract_version: "string",
|
|
93
|
+
namespace: "string",
|
|
94
|
+
plan: "object",
|
|
95
|
+
records: "array",
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
const checks = specifications.map(({ tool, required }) => {
|
|
100
|
+
const unique = inventory.filter((candidate) => candidate.name === tool).length === 1;
|
|
101
|
+
const compatible = unique && inputSchemaCompatible(tools.get(tool)?.inputSchema, required);
|
|
102
|
+
return {
|
|
103
|
+
tool,
|
|
104
|
+
compatible,
|
|
105
|
+
code: compatible
|
|
106
|
+
? "FORTEMI_DATASET_TOOL_SCHEMA_COMPATIBLE"
|
|
107
|
+
: tools.has(tool)
|
|
108
|
+
? "FORTEMI_DATASET_TOOL_SCHEMA_DRIFT"
|
|
109
|
+
: "FORTEMI_DATASET_TOOL_MISSING",
|
|
110
|
+
};
|
|
111
|
+
});
|
|
112
|
+
const supported = checks.every((check) => check.compatible);
|
|
113
|
+
const endedAt = now().toISOString();
|
|
114
|
+
const server = input.client.serverVersion?.() ?? {};
|
|
115
|
+
const safeObserved = (value) => value && SAFE.test(value) ? value : "unreported";
|
|
116
|
+
const base = {
|
|
117
|
+
contract: FORTEMI_DATASET_LIVE_CONTRACT,
|
|
118
|
+
outcome: supported ? "supported" : "pending",
|
|
119
|
+
diagnostic: supported
|
|
120
|
+
? "CONFORMANCE_FORTEMI_DATASET_PREFLIGHT_SUPPORTED"
|
|
121
|
+
: "CONFORMANCE_FORTEMI_DATASET_CONTRACT_UNAVAILABLE",
|
|
122
|
+
bindings: {
|
|
123
|
+
aiwgCommit: input.aiwgCommit,
|
|
124
|
+
endpointFingerprint: endpointFingerprint(input.endpointUrl),
|
|
125
|
+
toolSchemaDigest: fortemiReceiptDigest(schemas),
|
|
126
|
+
},
|
|
127
|
+
observed: {
|
|
128
|
+
serverName: safeObserved(server.name),
|
|
129
|
+
serverVersion: safeObserved(server.version),
|
|
130
|
+
},
|
|
131
|
+
namespace,
|
|
132
|
+
operations: checks,
|
|
133
|
+
mutation: { authorized: false, attempted: false },
|
|
134
|
+
resources: {
|
|
135
|
+
maxDurationMs,
|
|
136
|
+
durationMs: Date.parse(endedAt) - Date.parse(startedAt),
|
|
137
|
+
maxToolCount: MAX_TOOL_COUNT,
|
|
138
|
+
observedToolCount: inventory.length,
|
|
139
|
+
maxSchemaBytes: MAX_SCHEMA_BYTES,
|
|
140
|
+
observedSchemaBytes,
|
|
141
|
+
networkAttempts: 1,
|
|
142
|
+
toolCalls: 0,
|
|
143
|
+
},
|
|
144
|
+
startedAt,
|
|
145
|
+
endedAt,
|
|
146
|
+
};
|
|
147
|
+
const receipt = { ...base, receiptDigest: fortemiReceiptDigest(base) };
|
|
148
|
+
const errors = verifyFortemiDatasetLiveReceipt(receipt);
|
|
149
|
+
if (errors.length)
|
|
150
|
+
throw new Error(errors.join(","));
|
|
151
|
+
return receipt;
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
await input.client.close?.();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
export function verifyFortemiDatasetLiveReceipt(value) {
|
|
158
|
+
const errors = [];
|
|
159
|
+
const top = record(value);
|
|
160
|
+
if (!top)
|
|
161
|
+
return ["CONFORMANCE_RECEIPT_SHAPE_INVALID"];
|
|
162
|
+
const receipt = value;
|
|
163
|
+
if (!record(receipt.bindings) ||
|
|
164
|
+
!record(receipt.observed) ||
|
|
165
|
+
!record(receipt.mutation) ||
|
|
166
|
+
!record(receipt.resources) ||
|
|
167
|
+
!Array.isArray(receipt.operations) ||
|
|
168
|
+
!receipt.operations.every((item) => Boolean(record(item)))) {
|
|
169
|
+
return ["CONFORMANCE_RECEIPT_SHAPE_INVALID"];
|
|
170
|
+
}
|
|
171
|
+
if (!exactKeys(top, [
|
|
172
|
+
"contract",
|
|
173
|
+
"outcome",
|
|
174
|
+
"diagnostic",
|
|
175
|
+
"receiptDigest",
|
|
176
|
+
"bindings",
|
|
177
|
+
"observed",
|
|
178
|
+
"namespace",
|
|
179
|
+
"operations",
|
|
180
|
+
"mutation",
|
|
181
|
+
"resources",
|
|
182
|
+
"startedAt",
|
|
183
|
+
"endedAt",
|
|
184
|
+
]) ||
|
|
185
|
+
!exactKeys(receipt.bindings, [
|
|
186
|
+
"aiwgCommit",
|
|
187
|
+
"endpointFingerprint",
|
|
188
|
+
"toolSchemaDigest",
|
|
189
|
+
]) ||
|
|
190
|
+
!exactKeys(receipt.observed, [
|
|
191
|
+
"serverName",
|
|
192
|
+
"serverVersion",
|
|
193
|
+
]) ||
|
|
194
|
+
!exactKeys(receipt.mutation, [
|
|
195
|
+
"authorized",
|
|
196
|
+
"attempted",
|
|
197
|
+
]) ||
|
|
198
|
+
!exactKeys(receipt.resources, [
|
|
199
|
+
"maxDurationMs",
|
|
200
|
+
"durationMs",
|
|
201
|
+
"maxToolCount",
|
|
202
|
+
"observedToolCount",
|
|
203
|
+
"maxSchemaBytes",
|
|
204
|
+
"observedSchemaBytes",
|
|
205
|
+
"networkAttempts",
|
|
206
|
+
"toolCalls",
|
|
207
|
+
]) ||
|
|
208
|
+
!receipt.operations.every((item) => exactKeys(item, [
|
|
209
|
+
"tool",
|
|
210
|
+
"compatible",
|
|
211
|
+
"code",
|
|
212
|
+
])))
|
|
213
|
+
errors.push("CONFORMANCE_RECEIPT_SHAPE_INVALID");
|
|
214
|
+
if (receipt.contract !== FORTEMI_DATASET_LIVE_CONTRACT)
|
|
215
|
+
errors.push("CONFORMANCE_RECEIPT_CONTRACT_MISMATCH");
|
|
216
|
+
if (!COMMIT.test(receipt.bindings.aiwgCommit) ||
|
|
217
|
+
!DIGEST.test(receipt.bindings.endpointFingerprint) ||
|
|
218
|
+
!DIGEST.test(receipt.bindings.toolSchemaDigest))
|
|
219
|
+
errors.push("CONFORMANCE_RECEIPT_BINDING_INVALID");
|
|
220
|
+
if (!UUID_NAMESPACE.test(receipt.namespace))
|
|
221
|
+
errors.push("CONFORMANCE_RECEIPT_NAMESPACE_INVALID");
|
|
222
|
+
if (![
|
|
223
|
+
receipt.observed.serverName,
|
|
224
|
+
receipt.observed.serverVersion,
|
|
225
|
+
...receipt.operations.flatMap((item) => [item.tool, item.code]),
|
|
226
|
+
].every((value) => SAFE.test(value)))
|
|
227
|
+
errors.push("CONFORMANCE_RECEIPT_UNSAFE_VALUE");
|
|
228
|
+
if (receipt.mutation.authorized !== false ||
|
|
229
|
+
receipt.mutation.attempted !== false ||
|
|
230
|
+
receipt.resources.toolCalls !== 0 ||
|
|
231
|
+
receipt.resources.networkAttempts !== 1)
|
|
232
|
+
errors.push("CONFORMANCE_RECEIPT_MUTATION_INVALID");
|
|
233
|
+
const operationNames = receipt.operations.map((operation) => operation.tool);
|
|
234
|
+
const inventoryValid = receipt.operations.length === REQUIRED_TOOLS.length &&
|
|
235
|
+
REQUIRED_TOOLS.every((tool) => operationNames.filter((name) => name === tool).length === 1);
|
|
236
|
+
if (!inventoryValid ||
|
|
237
|
+
receipt.operations.some((operation) => operation.code !==
|
|
238
|
+
(operation.compatible
|
|
239
|
+
? "FORTEMI_DATASET_TOOL_SCHEMA_COMPATIBLE"
|
|
240
|
+
: operation.code === "FORTEMI_DATASET_TOOL_MISSING"
|
|
241
|
+
? operation.code
|
|
242
|
+
: "FORTEMI_DATASET_TOOL_SCHEMA_DRIFT")))
|
|
243
|
+
errors.push("CONFORMANCE_RECEIPT_OPERATION_INVALID");
|
|
244
|
+
const supported = inventoryValid &&
|
|
245
|
+
receipt.operations.every((operation) => operation.compatible);
|
|
246
|
+
const expectedDiagnostic = supported
|
|
247
|
+
? "CONFORMANCE_FORTEMI_DATASET_PREFLIGHT_SUPPORTED"
|
|
248
|
+
: "CONFORMANCE_FORTEMI_DATASET_CONTRACT_UNAVAILABLE";
|
|
249
|
+
if (receipt.outcome !== (supported ? "supported" : "pending") ||
|
|
250
|
+
receipt.diagnostic !== expectedDiagnostic)
|
|
251
|
+
errors.push("CONFORMANCE_RECEIPT_OUTCOME_INVALID");
|
|
252
|
+
const start = Date.parse(receipt.startedAt);
|
|
253
|
+
const end = Date.parse(receipt.endedAt);
|
|
254
|
+
if (!Number.isFinite(start) ||
|
|
255
|
+
!Number.isFinite(end) ||
|
|
256
|
+
end < start ||
|
|
257
|
+
receipt.resources.durationMs !== end - start)
|
|
258
|
+
errors.push("CONFORMANCE_RECEIPT_TIME_INVALID");
|
|
259
|
+
if (!Number.isInteger(receipt.resources.maxDurationMs) ||
|
|
260
|
+
receipt.resources.maxDurationMs < 250 ||
|
|
261
|
+
receipt.resources.maxDurationMs > 30_000 ||
|
|
262
|
+
!Number.isInteger(receipt.resources.durationMs) ||
|
|
263
|
+
receipt.resources.durationMs < 0 ||
|
|
264
|
+
receipt.resources.maxToolCount !== MAX_TOOL_COUNT ||
|
|
265
|
+
!Number.isInteger(receipt.resources.observedToolCount) ||
|
|
266
|
+
receipt.resources.observedToolCount < 0 ||
|
|
267
|
+
receipt.resources.observedToolCount > MAX_TOOL_COUNT ||
|
|
268
|
+
receipt.resources.maxSchemaBytes !== MAX_SCHEMA_BYTES ||
|
|
269
|
+
!Number.isInteger(receipt.resources.observedSchemaBytes) ||
|
|
270
|
+
receipt.resources.observedSchemaBytes < 0 ||
|
|
271
|
+
receipt.resources.observedSchemaBytes > MAX_SCHEMA_BYTES)
|
|
272
|
+
errors.push("CONFORMANCE_RECEIPT_RESOURCES_INVALID");
|
|
273
|
+
if (!DIGEST.test(receipt.receiptDigest) ||
|
|
274
|
+
receipt.receiptDigest !== fortemiReceiptDigest(material(receipt)))
|
|
275
|
+
errors.push("CONFORMANCE_RECEIPT_DIGEST_MISMATCH");
|
|
276
|
+
return errors;
|
|
277
|
+
}
|
|
278
|
+
/** Atomically creates private evidence without replacing an existing receipt. */
|
|
279
|
+
export async function writeFortemiDatasetLiveReceipt(path, receipt) {
|
|
280
|
+
const errors = verifyFortemiDatasetLiveReceipt(receipt);
|
|
281
|
+
if (errors.length)
|
|
282
|
+
throw new Error(errors.join(","));
|
|
283
|
+
await mkdir(dirname(path), { recursive: true });
|
|
284
|
+
const temporary = `${path}.tmp-${process.pid}`;
|
|
285
|
+
await writeFile(temporary, `${JSON.stringify(receipt, null, 2)}\n`, {
|
|
286
|
+
encoding: "utf8",
|
|
287
|
+
mode: 0o600,
|
|
288
|
+
flag: "wx",
|
|
289
|
+
});
|
|
290
|
+
try {
|
|
291
|
+
await link(temporary, path);
|
|
292
|
+
}
|
|
293
|
+
finally {
|
|
294
|
+
await unlink(temporary).catch(() => undefined);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
//# sourceMappingURL=fortemi-live-qualification.js.map
|
|
@@ -14,6 +14,7 @@ export * from './orchestration-repository.js';
|
|
|
14
14
|
export * from './file-orchestration-repository.js';
|
|
15
15
|
export * from './local-execution-backend.js';
|
|
16
16
|
export * from './fortemi-execution-bridge.js';
|
|
17
|
+
export * from './fortemi-live-qualification.js';
|
|
17
18
|
export * from './orchestration-service.js';
|
|
18
19
|
export * from './presentation.js';
|
|
19
20
|
export * from './conformance-types.js';
|
|
@@ -14,6 +14,7 @@ export * from './orchestration-repository.js';
|
|
|
14
14
|
export * from './file-orchestration-repository.js';
|
|
15
15
|
export * from './local-execution-backend.js';
|
|
16
16
|
export * from './fortemi-execution-bridge.js';
|
|
17
|
+
export * from './fortemi-live-qualification.js';
|
|
17
18
|
export * from './orchestration-service.js';
|
|
18
19
|
export * from './presentation.js';
|
|
19
20
|
export * from './conformance-types.js';
|