@aiwg/cli 2026.10.4 → 2026.10.6
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/agentic/code/addons/composition-engine/lib/runtime.mjs +1401 -0
- package/agentic/code/addons/composition-engine/lib/validator.mjs +513 -0
- package/agentic/code/addons/composition-engine/schemas/flow-graph.schema.json +372 -0
- package/agentic/code/providers/model-capabilities.v1.json +74 -50
- package/bin/aiwg.mjs +3 -1
- package/dist/src/artifacts/browser-export.js +1 -0
- package/dist/src/artifacts/citation-parser.js +8 -1
- package/dist/src/artifacts/cli.js +72 -9
- package/dist/src/artifacts/fortemi-core-query-adapter.js +2 -0
- package/dist/src/artifacts/fortemi-core-sync.js +26 -8
- package/dist/src/artifacts/graph-query.js +18 -8
- package/dist/src/artifacts/index-adapters.js +339 -0
- package/dist/src/artifacts/index-builder.js +146 -16
- package/dist/src/artifacts/index-files.js +15 -4
- package/dist/src/artifacts/index-preparation.js +91 -26
- package/dist/src/artifacts/index-reader.js +44 -0
- package/dist/src/artifacts/index-refresh.js +130 -56
- package/dist/src/artifacts/index-status.js +20 -5
- package/dist/src/artifacts/index-worker.mjs +9 -7
- package/dist/src/artifacts/memory-maintenance.js +503 -0
- package/dist/src/artifacts/prebuilt-relations.js +3 -1
- package/dist/src/artifacts/query-engine.js +263 -108
- package/dist/src/artifacts/relations-builder.js +2 -2
- package/dist/src/artifacts/source-graph.js +2 -2
- package/dist/src/artifacts/types.js +9 -1
- package/dist/src/cli/handlers/decision.js +19 -1
- package/dist/src/cli/handlers/guided-setup.js +470 -0
- package/dist/src/cli/handlers/help.js +2 -2
- package/dist/src/cli/handlers/housekeeper.js +1110 -0
- package/dist/src/cli/handlers/index.js +4 -2
- package/dist/src/cli/handlers/init.js +41 -6
- package/dist/src/cli/handlers/local-executor.js +5 -3
- package/dist/src/cli/handlers/mc.js +509 -53
- package/dist/src/cli/handlers/models.js +32 -2
- package/dist/src/cli/handlers/ralph-launcher.js +76 -4
- package/dist/src/cli/handlers/rules.js +1 -1
- package/dist/src/cli/handlers/script-runner.js +73 -2
- package/dist/src/cli/handlers/serve.js +5 -3
- package/dist/src/cli/handlers/sessions.js +40 -6
- package/dist/src/cli/handlers/setup.js +428 -1
- package/dist/src/cli/handlers/skill-lint.js +1 -1
- package/dist/src/cli/handlers/subcommands.js +118 -8
- package/dist/src/cli/handlers/use.js +279 -93
- package/dist/src/cli/handlers/workspace.js +3 -1
- package/dist/src/cli/prompt-utils.js +8 -0
- package/dist/src/cli/router.js +16 -1
- package/dist/src/config/aiwg-config.js +42 -0
- package/dist/src/config/cli.js +7 -3
- package/dist/src/config/decision-config.js +14 -1
- package/dist/src/config/native-sandbox-warnings.js +25 -0
- package/dist/src/contributors/validation.js +6 -5
- package/dist/src/decision/admission.js +7 -3
- package/dist/src/decision/benchmark.js +469 -0
- package/dist/src/decision/ledger.js +22 -3
- package/dist/src/decision/pricing.js +147 -0
- package/dist/src/decision/qualification/gate-evidence.js +18 -15
- package/dist/src/decision/qualification/load-staged.js +539 -0
- package/dist/src/decision/qualification/load.js +109 -33
- package/dist/src/decision/qualification/runner.js +5 -2
- package/dist/src/decision/review/service.js +6 -4
- package/dist/src/decision/review/validate.js +23 -2
- package/dist/src/decision/savings.js +8 -3
- package/dist/src/discovery/index.js +492 -0
- package/dist/src/discovery/interview.js +233 -0
- package/dist/src/discovery/plan-apply.js +571 -0
- package/dist/src/discovery/plan-store.js +171 -0
- package/dist/src/discovery/plan-types.js +2 -0
- package/dist/src/discovery/plan-validation.js +139 -0
- package/dist/src/discovery/types.js +2 -0
- package/dist/src/extensions/commands/definitions.js +49 -9
- package/dist/src/extensions/manifest.js +71 -44
- package/dist/src/extensions/project-local-discovery.js +1 -1
- package/dist/src/extensions/project-quickref.js +18 -17
- package/dist/src/extensions/validation.js +32 -29
- package/dist/src/housekeeper/daemon-config.js +148 -0
- package/dist/src/housekeeper/ipc.js +84 -0
- package/dist/src/housekeeper/lifecycle.js +148 -0
- package/dist/src/housekeeper/platform.js +35 -0
- package/dist/src/housekeeper/status.js +70 -0
- package/dist/src/housekeeper/units.js +114 -0
- package/dist/src/jobs/flow.js +22 -21
- package/dist/src/lint/rule-activation.js +20 -5
- package/dist/src/lint/runner.js +1 -1
- package/dist/src/mcp/cli.mjs +5 -6
- package/dist/src/mcp/config-file.mjs +6 -2
- package/dist/src/mcp/config-layers.mjs +23 -7
- package/dist/src/mcp/profiles.js +18 -31
- package/dist/src/mcp/profiles.mjs +18 -32
- package/dist/src/mcp/registry.js +7 -27
- package/dist/src/mcp/registry.mjs +7 -28
- package/dist/src/mcp/server.mjs +34 -1
- package/dist/src/mcp/tools/agentic-sandbox.mjs +8 -7
- package/dist/src/mcp/tools/command-run.mjs +3 -2
- package/dist/src/mcp/tools/decision.mjs +49 -48
- package/dist/src/mcp/tools/discovery.mjs +46 -21
- package/dist/src/mcp/tools/orchestration.mjs +22 -22
- package/dist/src/mcp/tools/subsystems.mjs +9 -8
- package/dist/src/mcp/tools/worker-invoke.mjs +17 -4
- package/dist/src/mcp/tools/writer-profiles.mjs +1 -1
- package/dist/src/memory/audit.js +719 -0
- package/dist/src/memory/canonical-context.js +392 -13
- package/dist/src/memory/canonical-maintenance.js +95 -0
- package/dist/src/memory/cli-result.js +22 -0
- package/dist/src/memory/cli.js +176 -0
- package/dist/src/memory/context-pack.js +57 -6
- package/dist/src/memory/cost.js +512 -0
- package/dist/src/memory/firewall-classification.d.mts +7 -0
- package/dist/src/memory/firewall-classification.mjs +32 -0
- package/dist/src/memory/index.js +8 -0
- package/dist/src/memory/line-maintenance.js +68 -0
- package/dist/src/memory/maintenance-cli.js +286 -0
- package/dist/src/memory/maintenance-contract.js +136 -0
- package/dist/src/memory/maintenance-index.js +18 -0
- package/dist/src/memory/maintenance-owner.js +2 -0
- package/dist/src/memory/maintenance-record.js +138 -0
- package/dist/src/memory/maintenance.js +254 -0
- package/dist/src/memory/provider-memory-adapter.js +154 -0
- package/dist/src/memory/recommend-cli.js +83 -0
- package/dist/src/memory/recommend.js +269 -0
- package/dist/src/memory/scheduled.js +81 -0
- package/dist/src/memory/taxonomy.js +44 -0
- package/dist/src/mission-protocol/codecs.js +19 -9
- package/dist/src/models/config-loader.js +11 -1
- package/dist/src/models/model-capabilities.v1.json +74 -50
- package/dist/src/models/provider-policy.js +125 -62
- package/dist/src/models/resolver.js +45 -8
- package/dist/src/models/worker-installation.d.mts +7 -0
- package/dist/src/models/worker-installation.mjs +169 -37
- package/dist/src/models/wrapper-route.js +1 -1
- package/dist/src/output-modes/registry.js +8 -7
- package/dist/src/plugin/metadata-validator.js +1 -1
- package/dist/src/providers/deploy-version.d.mts +1 -0
- package/dist/src/providers/deploy-version.mjs +17 -0
- package/dist/src/providers/provider-definitions.js +19 -16
- package/dist/src/providers/skill-ownership.d.mts +11 -2
- package/dist/src/providers/skill-ownership.mjs +35 -17
- package/dist/src/rules/activation.d.mts +11 -2
- package/dist/src/rules/activation.mjs +82 -6
- package/dist/src/rules/inventory.js +1 -1
- package/dist/src/rules/rule-frontmatter.schema.json +2 -0
- package/dist/src/schema/zod-validation.d.mts +6 -0
- package/dist/src/schema/zod-validation.mjs +50 -0
- package/dist/src/serve/executor-registry.js +297 -20
- package/dist/src/serve/executor-token.d.mts +1 -0
- package/dist/src/serve/executor-token.mjs +8 -0
- package/dist/src/serve/shared-host-scheduler.js +1102 -31
- package/dist/src/serve/worker-invocation.js +114 -15
- package/dist/src/sessions/adapters/claude.js +19 -18
- package/dist/src/sessions/adapters/codex.js +33 -19
- package/dist/src/sessions/adapters/copilot.js +14 -13
- package/dist/src/sessions/adapters/cursor.js +19 -18
- package/dist/src/sessions/adapters/deepseek-harness.js +9 -8
- package/dist/src/sessions/adapters/factory.js +11 -10
- package/dist/src/sessions/adapters/generic.js +32 -23
- package/dist/src/sessions/adapters/grokbot.js +35 -35
- package/dist/src/sessions/adapters/hermes.js +20 -19
- package/dist/src/sessions/adapters/muse.js +50 -49
- package/dist/src/sessions/adapters/omp.js +5 -4
- package/dist/src/sessions/adapters/openclaw.js +30 -29
- package/dist/src/sessions/adapters/opencode.js +26 -26
- package/dist/src/sessions/adapters/openhuman.js +24 -23
- package/dist/src/sessions/adapters/pi.js +5 -4
- package/dist/src/sessions/adapters/windsurf.js +8 -8
- package/dist/src/sessions/analytics.js +10 -9
- package/dist/src/sessions/batch-contracts.js +1 -1
- package/dist/src/sessions/batch-import.js +202 -2
- package/dist/src/sessions/candidates.js +7 -6
- package/dist/src/sessions/contracts.js +73 -54
- package/dist/src/sessions/coverage-preflight.js +26 -1
- package/dist/src/sessions/output-registration.js +11 -10
- package/dist/src/sessions/policy.js +18 -7
- package/dist/src/sessions/refresh.js +2 -3
- package/dist/src/sessions/repository.js +4 -2
- package/dist/src/sessions/workspace-discovery.js +92 -26
- package/dist/src/smiths/context-pipeline/aiwg-md.js +18 -1
- package/dist/src/smiths/context-pipeline/claude-hook.js +42 -6
- package/dist/src/smiths/context-pipeline/parallelism-section.js +72 -5
- package/dist/src/smiths/context-pipeline/workspace-context.js +106 -10
- package/dist/src/update/checker.d.mts +4 -0
- package/dist/src/update/checker.mjs +14 -0
- package/dist/src/writing/exemplar-selection.js +6 -6
- package/dist/src/writing/voice-evaluation.js +24 -23
- package/dist/src/writing/writer-migration.js +14 -13
- package/dist/src/writing/writer-profile.js +19 -18
- package/dist/src/writing/writing-brief.js +18 -17
- package/dist/src/writing/writing-receipt.js +47 -46
- package/package.json +5 -2
- package/schemas/decision/DecisionReview.schema.json +78 -1
- package/schemas/mission-v1.schema.json +3 -0
- package/tools/agents/deploy-agents.mjs +67 -39
- package/tools/agents/providers/antigravity.mjs +6 -1
- package/tools/agents/providers/base.mjs +19 -13
- package/tools/agents/providers/claude.mjs +1 -1
- package/tools/agents/providers/codex.mjs +50 -56
- package/tools/agents/providers/copilot.mjs +8 -2
- package/tools/agents/providers/cursor.mjs +1 -1
- package/tools/agents/providers/deepseek-harness.mjs +1 -1
- package/tools/agents/providers/factory.mjs +7 -1
- package/tools/agents/providers/grok-build.mjs +18 -2
- package/tools/agents/providers/grokbot.mjs +1 -1
- package/tools/agents/providers/hermes.mjs +7 -13
- package/tools/agents/providers/muse-hooks.mjs +21 -14
- package/tools/agents/providers/muse.mjs +7 -2
- package/tools/agents/providers/omp.mjs +28 -4
- package/tools/agents/providers/opencode.mjs +14 -5
- package/tools/agents/providers/openhuman.mjs +1 -1
- package/tools/agents/providers/pi.mjs +1 -1
- package/tools/agents/providers/provider-prepare-coordinator.mjs +29 -9
- package/tools/agents/providers/rule-delivery.mjs +263 -71
- package/tools/agents/providers/windsurf.mjs +7 -0
- package/tools/cli/workspace-status.mjs +5 -0
- package/tools/commands/deploy-prompts-codex.mjs +11 -8
- package/tools/ralph-external/lib/codex-adapter.mjs +6 -2
- package/tools/ralph-external/lib/muse-adapter.mjs +12 -2
- package/tools/ralph-external/lib/worker-health.mjs +1 -1
- package/tools/ralph-external/lib/worker-isolation.mjs +65 -9
- package/tools/ralph-external/process-monitor.mjs +7 -4
- package/tools/ralph-external/session-launcher.mjs +280 -32
- package/tools/skills/deploy-skills-codex.mjs +11 -7
- package/vscode-extension/schemas/aiwg.config.v1.json +11 -1
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
|
|
18
18
|
import { lstat, mkdtemp, readFile, readlink, realpath, rmdir } from 'fs/promises';
|
|
19
19
|
import { basename, dirname, isAbsolute, posix, relative, resolve, sep, win32 } from 'path';
|
|
20
|
-
import { assertConfigDestination, assertNoSymlinkParents, writeConfigAtomic } from './config-file.mjs';
|
|
21
|
-
import { resolveCredentialPolicy, stricterCredentialPolicy, warnCredentialPolicyRelaxation, warnDroppedOverlayPolicy } from './credentials.mjs';
|
|
20
|
+
import { assertConfigDestination, assertConfigObject, assertNoSymlinkParents, writeConfigAtomic } from './config-file.mjs';
|
|
21
|
+
import { credentialPolicyViolations, resolveCredentialPolicy, stricterCredentialPolicy, warnCredentialPolicyRelaxation, warnDroppedOverlayPolicy } from './credentials.mjs';
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* `platform` lets tests validate Windows path semantics (`C:\org`, UNC
|
|
@@ -62,9 +62,15 @@ export function resolveConfigLayers(configDirOverride, env = process.env, { plat
|
|
|
62
62
|
return layers.length > 0 ? layers : null;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
async function readLayer(path) {
|
|
65
|
+
async function readLayer(path, collection) {
|
|
66
66
|
try {
|
|
67
|
-
|
|
67
|
+
const parsed = JSON.parse(await readFile(path, 'utf-8'));
|
|
68
|
+
try {
|
|
69
|
+
assertConfigObject(parsed, collection);
|
|
70
|
+
} catch {
|
|
71
|
+
throw new Error(`Invalid MCP configuration layer ${path}: expected an object root and an object ${collection} map`);
|
|
72
|
+
}
|
|
73
|
+
return parsed;
|
|
68
74
|
} catch (error) {
|
|
69
75
|
if (error?.code === 'ENOENT') return null;
|
|
70
76
|
if (error instanceof SyntaxError) throw new Error(`Invalid JSON in MCP configuration layer ${path}`);
|
|
@@ -115,7 +121,7 @@ export async function loadLayered(layers, filename, collection, defaults) {
|
|
|
115
121
|
let lowerPolicySource;
|
|
116
122
|
const writeLayer = layers.length - 1;
|
|
117
123
|
for (const [index, dir] of layers.entries()) {
|
|
118
|
-
const parsed = await readLayer(resolve(dir, filename));
|
|
124
|
+
const parsed = await readLayer(resolve(dir, filename), collection);
|
|
119
125
|
if (!parsed) continue;
|
|
120
126
|
const { [collection]: entries, ...rest } = parsed;
|
|
121
127
|
if (index === writeLayer) {
|
|
@@ -298,16 +304,26 @@ export async function assertLayerWriteDestination(layers, filename) {
|
|
|
298
304
|
/** Persist atomically, then refresh ownership only after a successful write. */
|
|
299
305
|
export async function saveLayerData(file, data, collection, layers, layering, explicitPolicy = false) {
|
|
300
306
|
if (layers) await assertLayerWriteDestination(layers, basename(file));
|
|
307
|
+
// Validate the current bytes even if the caller loaded and cached an older
|
|
308
|
+
// version, or called save(data) on a fresh standalone registry.
|
|
309
|
+
await readLayer(file, collection);
|
|
301
310
|
const persisted = layering ? writeLayerData(data, collection, layering, explicitPolicy) : data;
|
|
302
311
|
// The active identity layer file is always 0600: it is the personal write
|
|
303
312
|
// layer of an active AIWG_CONFIG_LAYERS configuration and may carry secrets.
|
|
304
313
|
// Without layering, `file` is whatever single directory the caller
|
|
305
314
|
// configured (which may be a shared, group-readable organisation checkout
|
|
306
315
|
// maintained directly, not a private identity directory): an existing file
|
|
307
|
-
// keeps its mode
|
|
316
|
+
// keeps its mode unless the persisted servers carry literal credentials,
|
|
317
|
+
// while a newly created file starts private at 0600.
|
|
318
|
+
const info = layers ? null : await assertConfigDestination(file);
|
|
319
|
+
const tighten = info && (info.mode & 0o077) !== 0 && collection === 'servers'
|
|
320
|
+
&& Object.values(persisted[collection] || {}).some(server => credentialPolicyViolations(server, 'references').length > 0);
|
|
308
321
|
await writeConfigAtomic(file, JSON.stringify(persisted, null, 2) + '\n', layers
|
|
309
322
|
? { userScope: true, rejectSymlinkParents: true }
|
|
310
|
-
: { newFileMode: 0o600, rejectSymlinkParents: true });
|
|
323
|
+
: { newFileMode: 0o600, rejectSymlinkParents: true, ...(tighten ? { mode: 0o600 } : {}) });
|
|
324
|
+
if (tighten) {
|
|
325
|
+
console.warn(`Warning: tightened MCP registry ${file} from ${(info.mode & 0o7777).toString(8).padStart(4, '0')} to 0600 because it contains literal credentials.`);
|
|
326
|
+
}
|
|
311
327
|
if (layering) {
|
|
312
328
|
layering.own = new Set(Object.keys(persisted[collection] || {}));
|
|
313
329
|
layering.ownFields = new Set(Object.keys(persisted).filter(field => field !== collection));
|
package/dist/src/mcp/profiles.js
CHANGED
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
*
|
|
10
10
|
* @implements #889
|
|
11
11
|
*/
|
|
12
|
-
import { resolveCredentialPolicy } from './credentials.mjs';
|
|
13
12
|
import { isLowerLayerEntry, loadLayered, resolveProfileExtends, resolveConfigLayers, saveLayerData } from './config-layers.mjs';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
13
|
+
import { writeConfigAtomic } from './config-file.mjs';
|
|
14
|
+
import { readFile } from 'fs/promises';
|
|
15
|
+
import { parse, resolve } from 'path';
|
|
16
16
|
import { resolveConfigDir } from '../config/user-config.js';
|
|
17
17
|
// ─────────────────────────────────────────────
|
|
18
18
|
// Constants
|
|
@@ -81,38 +81,21 @@ export class McpProfileRegistry {
|
|
|
81
81
|
async load() {
|
|
82
82
|
if (this.cache)
|
|
83
83
|
return this.cache;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
this.layering = layered.layering;
|
|
88
|
-
return this.cache;
|
|
89
|
-
}
|
|
90
|
-
const filePath = this.getPath();
|
|
91
|
-
try {
|
|
92
|
-
const content = await readFile(filePath, 'utf-8');
|
|
93
|
-
const parsed = JSON.parse(content);
|
|
94
|
-
this.cache = {
|
|
95
|
-
...DEFAULT_DATA,
|
|
96
|
-
...parsed,
|
|
97
|
-
profiles: parsed.profiles || {},
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
catch {
|
|
101
|
-
this.cache = { ...DEFAULT_DATA, profiles: {} };
|
|
102
|
-
}
|
|
103
|
-
try {
|
|
104
|
-
resolveCredentialPolicy({ registryPolicy: this.cache.credentialPolicy, env: {} });
|
|
105
|
-
}
|
|
106
|
-
catch (error) {
|
|
107
|
-
this.cache = null;
|
|
108
|
-
throw new Error(`Invalid MCP credential policy in ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
109
|
-
}
|
|
84
|
+
const loaded = await loadLayered(this.layers ?? [this.configDir], PROFILES_FILENAME, 'profiles', DEFAULT_DATA);
|
|
85
|
+
this.cache = loaded.data;
|
|
86
|
+
this.layering = this.layers ? loaded.layering : null;
|
|
110
87
|
return this.cache;
|
|
111
88
|
}
|
|
112
89
|
async save(data = this.cache) {
|
|
113
90
|
if (!data)
|
|
114
91
|
return;
|
|
115
|
-
|
|
92
|
+
// Load ownership before a fresh save so inherited fields stay in their layer.
|
|
93
|
+
let layering = this.layering;
|
|
94
|
+
if (this.layers && !layering) {
|
|
95
|
+
await this.load();
|
|
96
|
+
layering = this.layering;
|
|
97
|
+
}
|
|
98
|
+
await saveLayerData(this.getPath(), data, 'profiles', this.layers, layering);
|
|
116
99
|
this.cache = data;
|
|
117
100
|
if (this.layers) {
|
|
118
101
|
this.clearCache();
|
|
@@ -311,7 +294,11 @@ export class McpProfileRegistry {
|
|
|
311
294
|
const toExport = profileName
|
|
312
295
|
? { [profileName]: data.profiles[profileName] }
|
|
313
296
|
: data.profiles;
|
|
314
|
-
await
|
|
297
|
+
await writeConfigAtomic(filePath, JSON.stringify({ profiles: toExport }, null, 2) + '\n', {
|
|
298
|
+
userScope: true,
|
|
299
|
+
rejectSymlinkParents: true,
|
|
300
|
+
symlinkRoot: parse(resolve(filePath)).root,
|
|
301
|
+
});
|
|
315
302
|
}
|
|
316
303
|
async initPresets() {
|
|
317
304
|
const data = structuredClone(await this.load());
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
* @implements #889
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { resolveCredentialPolicy } from './credentials.mjs';
|
|
14
13
|
import { isLowerLayerEntry, loadLayered, resolveProfileExtends, resolveConfigLayers, saveLayerData } from './config-layers.mjs';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
14
|
+
import { writeConfigAtomic } from './config-file.mjs';
|
|
15
|
+
import { readFile } from 'fs/promises';
|
|
16
|
+
import { parse, resolve } from 'path';
|
|
17
17
|
import { homedir } from 'os';
|
|
18
18
|
import { existsSync } from 'fs';
|
|
19
19
|
|
|
@@ -112,39 +112,21 @@ export class McpProfileRegistry {
|
|
|
112
112
|
async load() {
|
|
113
113
|
if (this.#cache) return this.#cache;
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
this.#layering = layered.layering;
|
|
119
|
-
return this.#cache;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const filePath = this.getPath();
|
|
123
|
-
try {
|
|
124
|
-
const content = await readFile(filePath, 'utf-8');
|
|
125
|
-
const parsed = JSON.parse(content);
|
|
126
|
-
this.#cache = {
|
|
127
|
-
...DEFAULT_DATA,
|
|
128
|
-
...parsed,
|
|
129
|
-
profiles: parsed.profiles || {},
|
|
130
|
-
};
|
|
131
|
-
} catch {
|
|
132
|
-
this.#cache = { ...DEFAULT_DATA, profiles: {} };
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
try {
|
|
136
|
-
resolveCredentialPolicy({ registryPolicy: this.#cache.credentialPolicy, env: {} });
|
|
137
|
-
} catch (error) {
|
|
138
|
-
this.#cache = null;
|
|
139
|
-
throw new Error(`Invalid MCP credential policy in ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
140
|
-
}
|
|
141
|
-
|
|
115
|
+
const loaded = await loadLayered(this.#layers ?? [this.#configDir], PROFILES_FILENAME, 'profiles', DEFAULT_DATA);
|
|
116
|
+
this.#cache = loaded.data;
|
|
117
|
+
this.#layering = this.#layers ? loaded.layering : null;
|
|
142
118
|
return this.#cache;
|
|
143
119
|
}
|
|
144
120
|
|
|
145
121
|
async save(data = this.#cache) {
|
|
146
122
|
if (!data) return;
|
|
147
|
-
|
|
123
|
+
// Load ownership before a fresh save so inherited fields stay in their layer.
|
|
124
|
+
let layering = this.#layering;
|
|
125
|
+
if (this.#layers && !layering) {
|
|
126
|
+
await this.load();
|
|
127
|
+
layering = this.#layering;
|
|
128
|
+
}
|
|
129
|
+
await saveLayerData(this.getPath(), data, 'profiles', this.#layers, layering);
|
|
148
130
|
this.#cache = data;
|
|
149
131
|
if (this.#layers) {
|
|
150
132
|
this.clearCache();
|
|
@@ -402,7 +384,11 @@ export class McpProfileRegistry {
|
|
|
402
384
|
throw new Error(`Profile "${profileName}" not found.`);
|
|
403
385
|
}
|
|
404
386
|
|
|
405
|
-
await
|
|
387
|
+
await writeConfigAtomic(filePath, JSON.stringify({ profiles: toExport }, null, 2) + '\n', {
|
|
388
|
+
userScope: true,
|
|
389
|
+
rejectSymlinkParents: true,
|
|
390
|
+
symlinkRoot: parse(resolve(filePath)).root,
|
|
391
|
+
});
|
|
406
392
|
}
|
|
407
393
|
|
|
408
394
|
/** Install preset profiles (does not overwrite existing) */
|
package/dist/src/mcp/registry.js
CHANGED
|
@@ -120,32 +120,9 @@ export class McpServerRegistry {
|
|
|
120
120
|
async load() {
|
|
121
121
|
if (this.cache)
|
|
122
122
|
return this.cache;
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
this.layering = layered.layering;
|
|
127
|
-
return this.cache;
|
|
128
|
-
}
|
|
129
|
-
const filePath = this.getPath();
|
|
130
|
-
try {
|
|
131
|
-
const content = await readFile(filePath, 'utf-8');
|
|
132
|
-
const parsed = JSON.parse(content);
|
|
133
|
-
this.cache = {
|
|
134
|
-
...DEFAULT_REGISTRY,
|
|
135
|
-
...parsed,
|
|
136
|
-
servers: parsed.servers || {},
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
catch {
|
|
140
|
-
this.cache = { ...DEFAULT_REGISTRY, servers: {} };
|
|
141
|
-
}
|
|
142
|
-
try {
|
|
143
|
-
resolveCredentialPolicy({ registryPolicy: this.cache.credentialPolicy, env: {} });
|
|
144
|
-
}
|
|
145
|
-
catch (error) {
|
|
146
|
-
this.cache = null;
|
|
147
|
-
throw new Error(`Invalid MCP credential policy in ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
148
|
-
}
|
|
123
|
+
const loaded = await loadLayered(this.layers ?? [this.configDir], REGISTRY_FILENAME, 'servers', DEFAULT_REGISTRY);
|
|
124
|
+
this.cache = loaded.data;
|
|
125
|
+
this.layering = this.layers ? loaded.layering : null;
|
|
149
126
|
return this.cache;
|
|
150
127
|
}
|
|
151
128
|
/** Save the registry to disk */
|
|
@@ -536,6 +513,9 @@ async function injectJson(registry, servers, configPath, provider, dryRun, resul
|
|
|
536
513
|
return result;
|
|
537
514
|
}
|
|
538
515
|
async function injectToml(registry, servers, configPath, provider, dryRun, result, userScope, projectRoot, toolPlan = null) {
|
|
516
|
+
// A deliberately aliased Codex home is trusted; descendants below it are not.
|
|
517
|
+
const destinationRoot = projectRoot ?? (process.env.CODEX_HOME || resolve(process.env.HOME || process.env.USERPROFILE || homedir(), '.codex'));
|
|
518
|
+
await assertConfigDestination(configPath, destinationRoot);
|
|
539
519
|
let existing = '';
|
|
540
520
|
try {
|
|
541
521
|
existing = await readFile(configPath, 'utf-8');
|
|
@@ -553,7 +533,7 @@ async function injectToml(registry, servers, configPath, provider, dryRun, resul
|
|
|
553
533
|
result.serversInjected.push(server.name);
|
|
554
534
|
}
|
|
555
535
|
if (!dryRun) {
|
|
556
|
-
await writeConfigAtomic(configPath, existing, { userScope, projectRoot });
|
|
536
|
+
await writeConfigAtomic(configPath, existing, { userScope, projectRoot: destinationRoot });
|
|
557
537
|
for (const server of servers) {
|
|
558
538
|
await registry.recordInjection(server.name, provider);
|
|
559
539
|
}
|
|
@@ -115,33 +115,9 @@ export class McpServerRegistry {
|
|
|
115
115
|
async load() {
|
|
116
116
|
if (this.#cache) return this.#cache;
|
|
117
117
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
this.#layering = layered.layering;
|
|
122
|
-
return this.#cache;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const filePath = this.getPath();
|
|
126
|
-
try {
|
|
127
|
-
const content = await readFile(filePath, 'utf-8');
|
|
128
|
-
const parsed = JSON.parse(content);
|
|
129
|
-
this.#cache = {
|
|
130
|
-
...DEFAULT_REGISTRY,
|
|
131
|
-
...parsed,
|
|
132
|
-
servers: parsed.servers || {},
|
|
133
|
-
};
|
|
134
|
-
} catch {
|
|
135
|
-
this.#cache = { ...DEFAULT_REGISTRY, servers: {} };
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
try {
|
|
139
|
-
resolveCredentialPolicy({ registryPolicy: this.#cache.credentialPolicy, env: {} });
|
|
140
|
-
} catch (error) {
|
|
141
|
-
this.#cache = null;
|
|
142
|
-
throw new Error(`Invalid MCP credential policy in ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
143
|
-
}
|
|
144
|
-
|
|
118
|
+
const loaded = await loadLayered(this.#layers ?? [this.#configDir], REGISTRY_FILENAME, 'servers', DEFAULT_REGISTRY);
|
|
119
|
+
this.#cache = loaded.data;
|
|
120
|
+
this.#layering = this.#layers ? loaded.layering : null;
|
|
145
121
|
return this.#cache;
|
|
146
122
|
}
|
|
147
123
|
|
|
@@ -488,6 +464,9 @@ async function injectJson(registry, servers, configPath, provider, dryRun, resul
|
|
|
488
464
|
}
|
|
489
465
|
|
|
490
466
|
async function injectToml(registry, servers, configPath, provider, dryRun, result, userScope, projectRoot, toolPlan = null) {
|
|
467
|
+
// A deliberately aliased Codex home is trusted; descendants below it are not.
|
|
468
|
+
const destinationRoot = projectRoot ?? (process.env.CODEX_HOME || resolve(process.env.HOME || process.env.USERPROFILE || homedir(), '.codex'));
|
|
469
|
+
await assertConfigDestination(configPath, destinationRoot);
|
|
491
470
|
let existing = '';
|
|
492
471
|
try {
|
|
493
472
|
existing = await readFile(configPath, 'utf-8');
|
|
@@ -504,7 +483,7 @@ async function injectToml(registry, servers, configPath, provider, dryRun, resul
|
|
|
504
483
|
}
|
|
505
484
|
|
|
506
485
|
if (!dryRun) {
|
|
507
|
-
await writeConfigAtomic(configPath, existing, { userScope, projectRoot });
|
|
486
|
+
await writeConfigAtomic(configPath, existing, { userScope, projectRoot: destinationRoot });
|
|
508
487
|
|
|
509
488
|
for (const server of servers) {
|
|
510
489
|
await registry.recordInjection(server.name, provider);
|
package/dist/src/mcp/server.mjs
CHANGED
|
@@ -34,6 +34,27 @@ const FRAMEWORKS = {
|
|
|
34
34
|
'media-marketing-kit': path.join(AIWG_ROOT, 'agentic/code/frameworks/media-marketing-kit')
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
+
/** Keep the published object contracts stable across native Zod converters. */
|
|
38
|
+
function retainToolJsonSchema(schema, seen = new Set()) {
|
|
39
|
+
if (!(schema instanceof z.ZodType) || seen.has(schema)) return;
|
|
40
|
+
seen.add(schema);
|
|
41
|
+
const definition = schema._zod.def;
|
|
42
|
+
if (schema instanceof z.ZodObject) {
|
|
43
|
+
const catchall = definition.catchall;
|
|
44
|
+
if ((!catchall && Object.keys(schema.shape).length > 0) || catchall instanceof z.ZodNever || catchall instanceof z.ZodUnknown) {
|
|
45
|
+
z.globalRegistry.add(schema, { ...z.globalRegistry.get(schema), additionalProperties: catchall instanceof z.ZodUnknown });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (schema instanceof z.ZodRecord && definition.keyType instanceof z.ZodString && !definition.keyType._zod.def.checks?.length) {
|
|
49
|
+
z.globalRegistry.add(schema, { ...z.globalRegistry.get(schema), propertyNames: undefined });
|
|
50
|
+
}
|
|
51
|
+
for (const value of Object.values(definition)) {
|
|
52
|
+
if (value instanceof z.ZodType) retainToolJsonSchema(value, seen);
|
|
53
|
+
else if (Array.isArray(value)) for (const child of value) retainToolJsonSchema(child, seen);
|
|
54
|
+
else if (value && typeof value === 'object') for (const child of Object.values(value)) retainToolJsonSchema(child, seen);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
37
58
|
/**
|
|
38
59
|
* Create and configure the AIWG MCP Server
|
|
39
60
|
*/
|
|
@@ -43,6 +64,18 @@ export async function createServer() {
|
|
|
43
64
|
version: '1.0.0'
|
|
44
65
|
});
|
|
45
66
|
|
|
67
|
+
const registerTool = server.registerTool.bind(server);
|
|
68
|
+
server.registerTool = (name, config, handler) => {
|
|
69
|
+
const schemas = {};
|
|
70
|
+
for (const key of ['inputSchema', 'outputSchema']) {
|
|
71
|
+
if (!config[key]) continue;
|
|
72
|
+
const schema = config[key] instanceof z.ZodType ? config[key] : z.object(config[key]);
|
|
73
|
+
retainToolJsonSchema(schema);
|
|
74
|
+
schemas[key] = schema;
|
|
75
|
+
}
|
|
76
|
+
return registerTool(name, { ...config, ...schemas }, handler);
|
|
77
|
+
};
|
|
78
|
+
|
|
46
79
|
// ============================================
|
|
47
80
|
// TOOLS
|
|
48
81
|
// ============================================
|
|
@@ -248,7 +281,7 @@ export async function createServer() {
|
|
|
248
281
|
description: 'Render AIWG template with provided variables',
|
|
249
282
|
inputSchema: {
|
|
250
283
|
template: z.string().describe('Template path (e.g., "sdlc/intake/project-intake-template.md")'),
|
|
251
|
-
variables: z.record(z.string()).optional().describe('Template variable substitutions')
|
|
284
|
+
variables: z.record(z.string(), z.string()).optional().describe('Template variable substitutions')
|
|
252
285
|
}
|
|
253
286
|
}, async ({ template, variables = {} }) => {
|
|
254
287
|
try {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import { readFile, stat } from 'node:fs/promises';
|
|
12
12
|
import { z } from 'zod';
|
|
13
|
+
import { integer, dateTime } from '../../schema/zod-validation.mjs';
|
|
13
14
|
import { assertNoWorkerBinding, mcpError, mcpJson } from '../helpers.mjs';
|
|
14
15
|
|
|
15
16
|
const FLEET_VERSION = 'agentic-orchestration/v1';
|
|
@@ -173,15 +174,15 @@ const scopeSchema = {
|
|
|
173
174
|
instance_id: z.string().min(1).max(255).regex(/^[^\r\n]+$/),
|
|
174
175
|
agent_id: z.string().min(1).max(255).regex(/^[^\r\n]+$/),
|
|
175
176
|
};
|
|
176
|
-
const activityFilterSchema = z.
|
|
177
|
+
const activityFilterSchema = z.strictObject({
|
|
177
178
|
event_name: z.string().min(1).max(255).optional(), collector: z.string().min(1).max(255).optional(),
|
|
178
179
|
trust: z.string().min(1).max(255).optional(), plane: z.string().min(1).max(255).optional(),
|
|
179
180
|
outcome: z.string().min(1).max(255).optional(), session_id: z.string().min(1).max(255).optional(),
|
|
180
181
|
mission_id: z.string().min(1).max(255).optional(), task_id: z.string().min(1).max(255).optional(),
|
|
181
182
|
tool_call_id: z.string().min(1).max(255).optional(), command_id: z.string().min(1).max(255).optional(),
|
|
182
183
|
process_id: z.string().min(1).max(255).optional(), trace_id: z.string().regex(/^[0-9a-f]{32}$/).optional(),
|
|
183
|
-
since:
|
|
184
|
-
}).
|
|
184
|
+
since: dateTime().optional(), until: dateTime().optional(), limit: integer().min(1).max(1000).optional(),
|
|
185
|
+
}).optional();
|
|
185
186
|
|
|
186
187
|
function scopeHeaders(args) {
|
|
187
188
|
return Object.fromEntries(Object.entries(SCOPE_HEADERS).map(([key, header]) => [header, args[key]]));
|
|
@@ -206,17 +207,17 @@ export function registerAgenticSandboxToolset(server, { client = new AgenticSand
|
|
|
206
207
|
() => client.request('/api/v2/fleet/workloads', { validate: requireInventory }));
|
|
207
208
|
register('sandbox-fleet-get', { title: 'Get Agentic Sandbox fleet workload', description: 'Read one revisioned fleet workload by child identity.', inputSchema: { contract_version: z.literal(FLEET_VERSION).default(FLEET_VERSION), child_id: z.string().min(1).max(255) }, annotations: { readOnlyHint: true, destructiveHint: false } },
|
|
208
209
|
({ child_id }) => client.request(`/api/v2/fleet/workloads/${encodeURIComponent(child_id)}`, { validate: requireFleetRecord }));
|
|
209
|
-
register('sandbox-fleet-reconcile-preview', { title: 'Preview fleet reconciliation', description: 'Compute a read-only reconciliation preview from inventory; never calls POST /reconcile.', inputSchema: { contract_version: z.literal(FLEET_VERSION).default(FLEET_VERSION), before_revision:
|
|
210
|
+
register('sandbox-fleet-reconcile-preview', { title: 'Preview fleet reconciliation', description: 'Compute a read-only reconciliation preview from inventory; never calls POST /reconcile.', inputSchema: { contract_version: z.literal(FLEET_VERSION).default(FLEET_VERSION), before_revision: integer().min(0), child_ids: z.array(z.string().min(1).max(255)).max(1000) }, annotations: { readOnlyHint: true, destructiveHint: false } },
|
|
210
211
|
async ({ before_revision, child_ids }) => client.request('/api/v2/fleet/workloads', { validate: (value) => {
|
|
211
212
|
const inventory = requireInventory(value);
|
|
212
213
|
const byId = new Map(inventory.records.map((record) => [record.lineage.child_id, record]));
|
|
213
214
|
return { document_type: 'reconciliation-preview', api_version: FLEET_VERSION, before_revision, inventory_revision: inventory.inventory_revision, stale: before_revision !== inventory.inventory_revision, rows: child_ids.map((child_id) => ({ child_id, present: byId.has(child_id), observed_state: byId.get(child_id)?.status?.observed_state ?? 'unknown', revision: byId.get(child_id)?.status?.revision ?? null })) };
|
|
214
215
|
} }));
|
|
215
|
-
register('sandbox-fleet-admit', { title: 'Admit Agentic Sandbox fleet workload', description: 'Mutating fleet admission; requires an exact v1 workload and confirmed=true.', inputSchema: { contract_version: z.literal(FLEET_VERSION).default(FLEET_VERSION), workload: z.record(z.unknown()), confirmed: z.boolean().default(false) }, annotations: { readOnlyHint: false, destructiveHint: true } },
|
|
216
|
+
register('sandbox-fleet-admit', { title: 'Admit Agentic Sandbox fleet workload', description: 'Mutating fleet admission; requires an exact v1 workload and confirmed=true.', inputSchema: { contract_version: z.literal(FLEET_VERSION).default(FLEET_VERSION), workload: z.record(z.string(), z.unknown()), confirmed: z.boolean().default(false) }, annotations: { readOnlyHint: false, destructiveHint: true } },
|
|
216
217
|
({ workload, confirmed }) => confirmed ? client.request('/api/v2/fleet/workloads', { method: 'POST', body: requireFleetRecord(workload), validate: (value) => ({ replayed: value?.replayed === true, workload: requireFleetRecord(value?.workload) }) }) : confirmationError('sandbox-fleet-admit'));
|
|
217
|
-
register('sandbox-fleet-observe', { title: 'Record fleet workload observation', description: 'Mutating monotonic observation update; requires confirmed=true and expected revision.', inputSchema: { contract_version: z.literal(FLEET_VERSION).default(FLEET_VERSION), child_id: z.string().min(1).max(255), expected_revision:
|
|
218
|
+
register('sandbox-fleet-observe', { title: 'Record fleet workload observation', description: 'Mutating monotonic observation update; requires confirmed=true and expected revision.', inputSchema: { contract_version: z.literal(FLEET_VERSION).default(FLEET_VERSION), child_id: z.string().min(1).max(255), expected_revision: integer().min(0), status: z.record(z.string(), z.unknown()), runtime_identity: z.strictObject({ session_id: z.string().optional(), task_id: z.string().optional(), command_id: z.string().optional() }).optional(), confirmed: z.boolean().default(false) }, annotations: { readOnlyHint: false, destructiveHint: true } },
|
|
218
219
|
({ child_id, expected_revision, status, runtime_identity, confirmed }) => confirmed ? client.request(`/api/v2/fleet/workloads/${encodeURIComponent(child_id)}/observations`, { method: 'POST', body: requireSafePayload({ expected_revision, status, ...(runtime_identity ? { runtime_identity } : {}) }, 'fleet observation'), validate: requireFleetRecord }) : confirmationError('sandbox-fleet-observe'));
|
|
219
|
-
register('sandbox-fleet-reconcile', { title: 'Reconcile fleet workloads', description: 'Mutating restart reconciliation; requires confirmed=true.', inputSchema: { contract_version: z.literal(FLEET_VERSION).default(FLEET_VERSION), before_revision:
|
|
220
|
+
register('sandbox-fleet-reconcile', { title: 'Reconcile fleet workloads', description: 'Mutating restart reconciliation; requires confirmed=true.', inputSchema: { contract_version: z.literal(FLEET_VERSION).default(FLEET_VERSION), before_revision: integer().min(0), child_ids: z.array(z.string().min(1).max(255)).max(1000), confirmed: z.boolean().default(false) }, annotations: { readOnlyHint: false, destructiveHint: true } },
|
|
220
221
|
({ before_revision, child_ids, confirmed }) => confirmed ? client.request('/api/v2/fleet/reconcile', { method: 'POST', body: { before_revision, child_ids }, validate: requireReconciliation }) : confirmationError('sandbox-fleet-reconcile'));
|
|
221
222
|
|
|
222
223
|
for (const [kind, eventsRequired] of [['coverage', false], ['timeline', true]]) {
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { z } from 'zod';
|
|
20
|
+
import { integer } from '../../schema/zod-validation.mjs';
|
|
20
21
|
import {
|
|
21
22
|
runAiwgCli,
|
|
22
23
|
loadCommandAllowList,
|
|
@@ -31,10 +32,10 @@ export function registerCommandRunTool(server) {
|
|
|
31
32
|
description: 'Dispatch to any allow-listed AIWG CLI command (`aiwg <command> [args...]`). The full surface of ~94 commands is reachable. Destructive commands require `confirmed: true`.',
|
|
32
33
|
inputSchema: {
|
|
33
34
|
command: z.string().describe('Command name from `command-list` (e.g. "use", "discover", "doctor")'),
|
|
34
|
-
args: z.array(z.string()).
|
|
35
|
+
args: z.array(z.string()).prefault([]).describe('Argument array (never shell-interpreted)'),
|
|
35
36
|
project_dir: z.string().optional().describe('Working directory for the command'),
|
|
36
37
|
confirmed: z.boolean().default(false).describe('Required for destructive commands (true to proceed)'),
|
|
37
|
-
timeout_ms:
|
|
38
|
+
timeout_ms: integer().min(1000).max(600_000).default(120_000).describe('Timeout (1s–10min, default 2min)'),
|
|
38
39
|
},
|
|
39
40
|
annotations: {
|
|
40
41
|
destructiveHint: true, // conservative default; many subcommands are read-only but the surface is broad
|
|
@@ -1,102 +1,103 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { integer, number } from '../../schema/zod-validation.mjs';
|
|
2
3
|
import { mcpError, mcpJson } from '../helpers.mjs';
|
|
3
4
|
|
|
4
5
|
const STATUS = z.enum(['success', 'abstained', 'denied', 'unavailable', 'error', 'cancelled']);
|
|
5
|
-
const ENVELOPE = z.
|
|
6
|
-
const LIVE_PLAN = z.
|
|
7
|
-
const PATTERN_SUMMARY = z.
|
|
6
|
+
const ENVELOPE = z.looseObject({ schema: z.string() });
|
|
7
|
+
const LIVE_PLAN = z.looseObject({ mode: z.literal('live'), status: z.enum(['ready', 'skipped', 'denied']), reason: z.string(), executes: z.literal(false) });
|
|
8
|
+
const PATTERN_SUMMARY = z.looseObject({
|
|
8
9
|
id: z.string(),
|
|
9
10
|
version: z.string(),
|
|
10
11
|
status: z.string(),
|
|
11
12
|
primitive: z.string().optional(),
|
|
12
|
-
})
|
|
13
|
-
const PATTERN_LIST = z.
|
|
13
|
+
});
|
|
14
|
+
const PATTERN_LIST = z.looseObject({
|
|
14
15
|
schema: z.literal('aiwg-decision-pattern-list/v1'),
|
|
15
16
|
patterns: z.array(PATTERN_SUMMARY),
|
|
16
|
-
})
|
|
17
|
-
const PATTERN_SHOW = z.
|
|
17
|
+
});
|
|
18
|
+
const PATTERN_SHOW = z.looseObject({
|
|
18
19
|
schema: z.literal('aiwg-decision-pattern-show/v1'),
|
|
19
20
|
pack: PATTERN_SUMMARY,
|
|
20
|
-
inputSchema: z.unknown(),
|
|
21
|
-
outputSchema: z.unknown(),
|
|
21
|
+
inputSchema: z.unknown().optional(),
|
|
22
|
+
outputSchema: z.unknown().optional(),
|
|
22
23
|
livePlan: LIVE_PLAN,
|
|
23
|
-
})
|
|
24
|
-
const COMPACT_EVALUATION = z.
|
|
24
|
+
});
|
|
25
|
+
const COMPACT_EVALUATION = z.looseObject({
|
|
25
26
|
status: STATUS,
|
|
26
27
|
reason: z.string(),
|
|
27
|
-
attempts:
|
|
28
|
-
value: z.unknown().nullable(),
|
|
29
|
-
})
|
|
30
|
-
const COMPACT_RESULT = z.
|
|
28
|
+
attempts: number(),
|
|
29
|
+
value: z.unknown().nullable().optional(),
|
|
30
|
+
});
|
|
31
|
+
const COMPACT_RESULT = z.looseObject({
|
|
31
32
|
status: STATUS,
|
|
32
33
|
rulesetStatus: z.string(),
|
|
33
34
|
reason: z.string(),
|
|
34
35
|
matchedRules: z.array(z.string()),
|
|
35
36
|
evaluations: z.record(z.string(), COMPACT_EVALUATION),
|
|
36
|
-
outcome: z.unknown().nullable(),
|
|
37
|
-
})
|
|
38
|
-
const EVALUATE = z.
|
|
37
|
+
outcome: z.unknown().nullable().optional(),
|
|
38
|
+
});
|
|
39
|
+
const EVALUATE = z.looseObject({
|
|
39
40
|
schema: z.literal('aiwg-decision-evaluate/v1'),
|
|
40
41
|
status: STATUS,
|
|
41
42
|
reason: z.string(),
|
|
42
|
-
exitCode:
|
|
43
|
+
exitCode: number(),
|
|
43
44
|
compact: COMPACT_RESULT.nullable().optional(),
|
|
44
|
-
result: z.unknown().nullable(),
|
|
45
|
-
})
|
|
46
|
-
const VALIDATION = z.
|
|
45
|
+
result: z.unknown().nullable().optional(),
|
|
46
|
+
});
|
|
47
|
+
const VALIDATION = z.looseObject({
|
|
47
48
|
schema: z.literal('aiwg-decision-validation/v1'),
|
|
48
49
|
target: z.enum(['request', 'definition', 'ruleset', 'binding']),
|
|
49
50
|
valid: z.boolean(),
|
|
50
|
-
summary: z.
|
|
51
|
+
summary: z.looseObject({
|
|
51
52
|
kind: z.string().nullable().optional(),
|
|
52
53
|
apiVersion: z.string().nullable().optional(),
|
|
53
54
|
id: z.string().nullable().optional(),
|
|
54
55
|
version: z.string().nullable().optional(),
|
|
55
|
-
})
|
|
56
|
+
}),
|
|
56
57
|
errors: z.array(z.string()),
|
|
57
|
-
})
|
|
58
|
-
const CAPABILITIES = z.
|
|
58
|
+
});
|
|
59
|
+
const CAPABILITIES = z.looseObject({
|
|
59
60
|
schema: z.literal('aiwg-decision-driver-capabilities/v1'),
|
|
60
61
|
enabled: z.boolean(),
|
|
61
62
|
offlineReady: z.boolean(),
|
|
62
63
|
primitives: z.array(z.string()),
|
|
63
|
-
backend: z.
|
|
64
|
-
})
|
|
65
|
-
const OFFLINE_RUN = z.
|
|
64
|
+
backend: z.looseObject({ configured: z.boolean(), probed: z.boolean(), status: z.literal('not-probed') }),
|
|
65
|
+
});
|
|
66
|
+
const OFFLINE_RUN = z.looseObject({
|
|
66
67
|
schema: z.literal('aiwg-decision-offline-run/v1'),
|
|
67
68
|
status: STATUS,
|
|
68
69
|
route: z.enum(['accept', 'review', 'deny']),
|
|
69
70
|
reason: z.string(),
|
|
70
|
-
evaluations: z.array(z.
|
|
71
|
+
evaluations: z.array(z.looseObject({
|
|
71
72
|
alias: z.string(),
|
|
72
73
|
primitive: z.string(),
|
|
73
74
|
status: z.string(),
|
|
74
75
|
reason: z.string(),
|
|
75
|
-
attempts:
|
|
76
|
-
value: z.unknown(),
|
|
77
|
-
})
|
|
76
|
+
attempts: number(),
|
|
77
|
+
value: z.unknown().optional(),
|
|
78
|
+
})),
|
|
78
79
|
checks: z.array(z.unknown()),
|
|
79
|
-
})
|
|
80
|
-
const SYNTHETIC_SETUP = z.
|
|
80
|
+
});
|
|
81
|
+
const SYNTHETIC_SETUP = z.looseObject({
|
|
81
82
|
schema: z.literal('aiwg-decision-synthetic-setup/v1'),
|
|
82
|
-
pattern: z.
|
|
83
|
+
pattern: z.looseObject({ id: z.string(), version: z.string() }),
|
|
83
84
|
files: z.record(z.string(), z.unknown()),
|
|
84
|
-
})
|
|
85
|
-
const ASK = z.
|
|
85
|
+
});
|
|
86
|
+
const ASK = z.looseObject({
|
|
86
87
|
schema: z.literal('aiwg-decision-ask/v1'),
|
|
87
88
|
status: z.enum(['answered', 'fallback']),
|
|
88
|
-
answer: z.union([z.string(), z.boolean(),
|
|
89
|
+
answer: z.union([z.string(), z.boolean(), number()]).nullable(),
|
|
89
90
|
confidence: z.number().min(0).max(1).nullable(),
|
|
90
91
|
probability: z.number().min(0).max(1).optional(),
|
|
91
92
|
fallback: z.enum(['llm']).nullable(),
|
|
92
93
|
reason: z.string(),
|
|
93
94
|
model: z.string().nullable(),
|
|
94
95
|
usage: z.object({
|
|
95
|
-
inputTokens:
|
|
96
|
-
outputTokens:
|
|
96
|
+
inputTokens: integer().nonnegative().nullable(),
|
|
97
|
+
outputTokens: integer().nonnegative().nullable(),
|
|
97
98
|
}).nullable(),
|
|
98
|
-
latencyMs: z.
|
|
99
|
-
})
|
|
99
|
+
latencyMs: number().check(z.gte(0)).meta({ type: 'number', minimum: 0 }),
|
|
100
|
+
});
|
|
100
101
|
|
|
101
102
|
let driverPromise;
|
|
102
103
|
|
|
@@ -176,7 +177,7 @@ export function registerDecisionToolset(server) {
|
|
|
176
177
|
description: 'Validate a request, definition, ruleset or binding without importing modules or resolving secrets.',
|
|
177
178
|
inputSchema: {
|
|
178
179
|
target: z.enum(['request', 'definition', 'ruleset', 'binding']),
|
|
179
|
-
document: z.unknown().describe('Inline decision document or dispatcher request object'),
|
|
180
|
+
document: z.unknown().optional().describe('Inline decision document or dispatcher request object'),
|
|
180
181
|
},
|
|
181
182
|
annotations: { readOnlyHint: true },
|
|
182
183
|
}, async ({ target, document }) => (await driver()).validateDecisionValue(target, document, 'mcp-inline'), VALIDATION);
|
|
@@ -198,12 +199,12 @@ export function registerDecisionToolset(server) {
|
|
|
198
199
|
question: z.string().min(1),
|
|
199
200
|
yes_no: z.boolean().default(false),
|
|
200
201
|
choices: z.array(z.string()).min(2).max(255).optional().describe('Choice ids, each optionally written as id=description so Jev knows what the option means'),
|
|
201
|
-
scale: z.object({ low:
|
|
202
|
+
scale: z.object({ low: integer(), high: integer() }).optional(),
|
|
202
203
|
context: z.string().optional(),
|
|
203
204
|
threshold: z.number().min(0).max(1).default(0.8),
|
|
204
|
-
timeout_ms:
|
|
205
|
-
passes:
|
|
206
|
-
escalation_passes:
|
|
205
|
+
timeout_ms: integer().positive().default(15000),
|
|
206
|
+
passes: integer().positive().optional(),
|
|
207
|
+
escalation_passes: integer().positive().optional(),
|
|
207
208
|
caller: z.string().optional().describe('Optional decision-point attribution tag, recorded in the decision ledger (#2946)'),
|
|
208
209
|
},
|
|
209
210
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
|