@aiwg/cli 2026.10.0 → 2026.10.3
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/providers/capability-matrix.yaml +270 -0
- package/agentic/code/providers/model-capabilities.v1.json +3091 -24
- package/bin/aiwg.mjs +86 -21
- package/dist/src/a2a/codecs.js +9 -0
- package/dist/src/artifacts/corpus-tools/vision-extract.js +105 -15
- package/dist/src/artifacts/repair.js +9 -4
- package/dist/src/channel/manager.mjs +59 -10
- package/dist/src/cli/child-process-result.js +40 -0
- package/dist/src/cli/env.js +38 -16
- package/dist/src/cli/flag-parsing.js +236 -0
- package/dist/src/cli/handlers/decision.js +297 -2
- package/dist/src/cli/handlers/diagnose.js +9 -2
- package/dist/src/cli/handlers/feedback.js +14 -5
- package/dist/src/cli/handlers/init.js +245 -30
- package/dist/src/cli/handlers/mc.js +23 -0
- package/dist/src/cli/handlers/models.js +69 -6
- package/dist/src/cli/handlers/ralph.js +3 -0
- package/dist/src/cli/handlers/refresh.js +16 -0
- package/dist/src/cli/handlers/regenerate.js +14 -0
- package/dist/src/cli/handlers/runtime-info.js +24 -9
- package/dist/src/cli/handlers/script-runner.js +16 -6
- package/dist/src/cli/handlers/session.js +396 -62
- package/dist/src/cli/handlers/sessions.js +146 -40
- package/dist/src/cli/handlers/setup-manifest.js +439 -39
- package/dist/src/cli/handlers/setup.js +139 -39
- package/dist/src/cli/handlers/subcommands.js +3 -1
- package/dist/src/cli/handlers/team.js +18 -8
- package/dist/src/cli/handlers/use.js +13 -0
- package/dist/src/cli/handlers/utilities.js +106 -23
- package/dist/src/cli/prompt-utils.js +72 -0
- package/dist/src/cli/provider-resolution.js +11 -0
- package/dist/src/config/aiwg-config.js +268 -4
- package/dist/src/config/cli.js +146 -19
- package/dist/src/config/dispatch-thresholds-runtime.mjs +38 -0
- package/dist/src/config/project-artifacts-health.mjs +14 -3
- package/dist/src/config/user-config.js +36 -10
- package/dist/src/decision/adapters/jev.js +80 -28
- package/dist/src/decision/ask.js +352 -0
- package/dist/src/decision/driver.js +4 -0
- package/dist/src/decision/entry.js +38 -6
- package/dist/src/decision/heldout/collector.js +10 -1
- package/dist/src/decision/index.js +3 -0
- package/dist/src/decision/interop.js +60 -8
- package/dist/src/decision/jev-credentials.js +121 -0
- package/dist/src/decision/ledger.js +839 -0
- package/dist/src/decision/validate.js +6 -3
- package/dist/src/effects/dispatch-verifier.js +252 -0
- package/dist/src/effects/index.js +1 -0
- package/dist/src/features/installer.js +2 -2
- package/dist/src/flow/graph-metadata.js +5 -0
- package/dist/src/gates/providers/loader.js +3 -1
- package/dist/src/installation/manager-command.mjs +1 -1
- package/dist/src/installation/manager.mjs +17 -2
- package/dist/src/lint/runner.js +2 -2
- package/dist/src/mcp/adapters/codex-runtime.js +360 -56
- package/dist/src/mcp/cli.mjs +587 -77
- package/dist/src/mcp/config-file.mjs +279 -0
- package/dist/src/mcp/config-layers.mjs +348 -0
- package/dist/src/mcp/credentials.mjs +484 -0
- package/dist/src/mcp/grok-build-config.mjs +16 -26
- package/dist/src/mcp/helpers.mjs +106 -2
- package/dist/src/mcp/omp-config.mjs +50 -29
- package/dist/src/mcp/profiles.js +97 -23
- package/dist/src/mcp/profiles.mjs +99 -27
- package/dist/src/mcp/registry.js +264 -57
- package/dist/src/mcp/registry.mjs +198 -45
- package/dist/src/mcp/server.mjs +403 -20
- package/dist/src/mcp/toml-string.mjs +9 -0
- package/dist/src/mcp/toml-strip-mcp.mjs +135 -0
- package/dist/src/mcp/tool-filters.mjs +357 -0
- package/dist/src/mcp/tools/agentic-sandbox.mjs +3 -1
- package/dist/src/mcp/tools/decision.mjs +58 -0
- package/dist/src/mcp/tools/orchestration.mjs +38 -0
- package/dist/src/mcp/tools/subsystems.mjs +39 -8
- package/dist/src/mcp/tools/worker-invoke.mjs +1539 -0
- package/dist/src/mcp/tools/worker-shell-control.mjs +258 -0
- package/dist/src/mcp/tools/worker-stdin-runtime.mjs +79 -0
- package/dist/src/memory/project-registry.js +2 -2
- package/dist/src/mission-protocol/codecs.js +639 -1
- package/dist/src/mission-protocol/types.js +110 -0
- package/dist/src/mission-protocol/worker-status.js +2 -0
- package/dist/src/models/config-loader.js +70 -13
- package/dist/src/models/grok-build-models.js +93 -41
- package/dist/src/models/model-capabilities.v1.json +3091 -24
- package/dist/src/models/model-discovery.js +862 -63
- package/dist/src/models/provider-policy.js +625 -11
- package/dist/src/models/qualification-digest.mjs +17 -0
- package/dist/src/models/resolver.js +259 -0
- package/dist/src/models/worker-direct-readiness.js +529 -0
- package/dist/src/models/worker-dispatch-offer.js +169 -0
- package/dist/src/models/worker-health-readiness.js +131 -0
- package/dist/src/models/worker-installation.mjs +879 -0
- package/dist/src/models/wrapper-route.js +18 -0
- package/dist/src/ops/registry.js +9 -14
- package/dist/src/plugins/standalone-packager.js +4 -3
- package/dist/src/process/bounded-child-process.mjs +387 -0
- package/dist/src/process/cli-command.mjs +75 -0
- package/dist/src/providers/capability-matrix.js +8 -0
- package/dist/src/providers/capability-matrix.yaml +270 -0
- package/dist/src/providers/grok-build-headless.js +43 -21
- package/dist/src/providers/provider-definitions.js +9 -1
- package/dist/src/providers/provider-definitions.mjs +9 -2
- package/dist/src/serve/executor-registry.js +16 -24
- package/dist/src/serve/fleet-mission-conductor.js +9 -0
- package/dist/src/serve/mission-conductor.js +9 -0
- package/dist/src/serve/shared-host-scheduler.js +2 -0
- package/dist/src/serve/worker-invocation.js +538 -0
- package/dist/src/serve/worker-stdin-frame.mjs +107 -0
- package/dist/src/sessions/adapter-registry.js +45 -0
- package/dist/src/sessions/adapters/generic.js +37 -3
- package/dist/src/sessions/adapters/grokbot-locator.js +111 -0
- package/dist/src/sessions/adapters/grokbot.js +423 -24
- package/dist/src/sessions/adapters/muse-workspace-scan.js +108 -0
- package/dist/src/sessions/adapters/muse.js +68 -29
- package/dist/src/sessions/adapters/pi.js +1 -0
- package/dist/src/sessions/batch-contracts.js +13 -1
- package/dist/src/sessions/batch-import.js +23 -42
- package/dist/src/sessions/candidates.js +47 -11
- package/dist/src/sessions/contracts.js +35 -0
- package/dist/src/sessions/coverage-preflight.js +134 -0
- package/dist/src/sessions/discovery.js +3 -0
- package/dist/src/sessions/grok-build-cli.js +67 -5
- package/dist/src/sessions/importer.js +24 -8
- package/dist/src/sessions/index.js +2 -0
- package/dist/src/sessions/repository.js +54 -13
- package/dist/src/sessions/workspace-discovery.js +99 -37
- package/dist/src/skills/adapters/clawhub.js +10 -8
- package/dist/src/skills/adapters/openclaw.js +10 -7
- package/dist/src/smiths/context-pipeline/parallelism-section.js +192 -3
- package/dist/src/smiths/context-pipeline/workspace-context.js +116 -9
- package/dist/src/smiths/hook-bridge/codex-translator.js +6 -5
- package/dist/src/smiths/mcpsmith/example.js +1 -1
- package/dist/src/smiths/toolsmith/runtime-discovery.mjs +10 -14
- package/dist/src/storage/backends/logseq.js +68 -7
- package/dist/src/storage/fortemi-qualification-receipt.js +2 -2
- package/dist/src/testing/generators/test-code-generator.js +57 -30
- package/dist/src/uhp/client.js +11 -1
- package/dist/src/uhp/mission.js +5 -0
- package/dist/src/update/checker.mjs +5 -5
- package/dist/src/update/notifier.mjs +54 -2
- package/dist/src/writing/fidelity.js +18 -1
- package/package.json +18 -1
- package/schemas/mission-v1.schema.json +2429 -0
- package/schemas/models/model-catalog.v1.schema.json +301 -0
- package/schemas/models/model-config-compatibility.v1.schema.json +281 -0
- package/schemas/models/model-policy.v1.schema.json +14 -0
- package/schemas/models/provider-model-capabilities.v1.schema.json +1066 -0
- package/tools/_resolve-impl.mjs +39 -0
- package/tools/agents/deploy-agents.mjs +15 -2
- package/tools/agents/providers/base.mjs +63 -43
- package/tools/agents/providers/grokbot.mjs +11 -12
- package/tools/agents/providers/hermes.mjs +6 -3
- package/tools/agents/providers/omp.mjs +5 -2
- package/tools/agents/providers/openclaw-translator.mjs +19 -5
- package/tools/providers/deepseek-harness-transport.mjs +358 -0
- package/tools/providers/omp-transport.mjs +192 -0
- package/tools/ralph-external/lib/claude-adapter.mjs +389 -0
- package/tools/ralph-external/lib/codex-adapter.mjs +432 -0
- package/tools/ralph-external/lib/deepseek-harness-adapter.mjs +397 -0
- package/tools/ralph-external/lib/factory-adapter.mjs +503 -0
- package/tools/ralph-external/lib/muse-adapter.mjs +364 -0
- package/tools/ralph-external/lib/omp-adapter.mjs +315 -0
- package/tools/ralph-external/lib/opencode-adapter.mjs +575 -0
- package/tools/ralph-external/lib/openhuman-adapter.mjs +75 -0
- package/tools/ralph-external/lib/pi-adapter.mjs +333 -0
- package/tools/ralph-external/lib/provider-adapter.mjs +690 -0
- package/tools/ralph-external/lib/worker-health.mjs +439 -0
- package/vscode-extension/schemas/aiwg.config.v1.json +1953 -0
|
@@ -146,7 +146,7 @@ export class LogseqAdapter {
|
|
|
146
146
|
}
|
|
147
147
|
// Defensive: refuse to write `id::` block IDs ourselves. Logseq
|
|
148
148
|
// auto-assigns them; if the caller smuggled one in, strip it.
|
|
149
|
-
body = body
|
|
149
|
+
body = stripBlockIds(body);
|
|
150
150
|
await writeFile(abs, body, 'utf-8');
|
|
151
151
|
}
|
|
152
152
|
async list(prefix) {
|
|
@@ -221,6 +221,34 @@ function expandHome(p) {
|
|
|
221
221
|
}
|
|
222
222
|
return p;
|
|
223
223
|
}
|
|
224
|
+
function stripBlockIds(content) {
|
|
225
|
+
const chunks = [];
|
|
226
|
+
let start = 0;
|
|
227
|
+
while (start < content.length) {
|
|
228
|
+
let property = start;
|
|
229
|
+
while (property < content.length && /\s/u.test(content[property]))
|
|
230
|
+
property++;
|
|
231
|
+
if (content.startsWith('id::', property)) {
|
|
232
|
+
// Only the property's own line belongs to the block ID.
|
|
233
|
+
let end = property + 4;
|
|
234
|
+
while (end < content.length && !/[\n\r\u2028\u2029]/u.test(content[end]))
|
|
235
|
+
end++;
|
|
236
|
+
if (content[end] === '\r' && content[end + 1] === '\n')
|
|
237
|
+
end++;
|
|
238
|
+
start = end < content.length ? end + 1 : content.length;
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
// Keep the original line and any preceding blank lines verbatim.
|
|
242
|
+
let end = property;
|
|
243
|
+
while (end < content.length && !/[\n\r\u2028\u2029]/u.test(content[end]))
|
|
244
|
+
end++;
|
|
245
|
+
if (end < content.length)
|
|
246
|
+
end++;
|
|
247
|
+
chunks.push(content.slice(start, end));
|
|
248
|
+
start = end;
|
|
249
|
+
}
|
|
250
|
+
return chunks.join('');
|
|
251
|
+
}
|
|
224
252
|
/**
|
|
225
253
|
* If `content` starts with a `---` YAML block, parse the block and
|
|
226
254
|
* return the parsed fields plus the body without the YAML. Otherwise
|
|
@@ -230,18 +258,51 @@ function expandHome(p) {
|
|
|
230
258
|
* Anything more complex falls back to dropping the value as a string.
|
|
231
259
|
*/
|
|
232
260
|
function extractYamlFrontmatter(content) {
|
|
233
|
-
|
|
234
|
-
|
|
261
|
+
if (!content.startsWith('---'))
|
|
262
|
+
return null;
|
|
263
|
+
let openingEnd = 3;
|
|
264
|
+
let firstNewline = -1;
|
|
265
|
+
let lastNewline = -1;
|
|
266
|
+
while (openingEnd < content.length && /\s/u.test(content[openingEnd])) {
|
|
267
|
+
if (content[openingEnd] === '\n') {
|
|
268
|
+
if (firstNewline < 0)
|
|
269
|
+
firstNewline = openingEnd;
|
|
270
|
+
lastNewline = openingEnd;
|
|
271
|
+
}
|
|
272
|
+
openingEnd++;
|
|
273
|
+
}
|
|
274
|
+
if (firstNewline < 0)
|
|
235
275
|
return null;
|
|
236
|
-
|
|
276
|
+
let yamlStart = lastNewline + 1;
|
|
277
|
+
let closing = content.indexOf('\n---', yamlStart);
|
|
278
|
+
if (closing < 0) {
|
|
279
|
+
closing = content.indexOf('\n---', firstNewline + 1);
|
|
280
|
+
if (closing < 0)
|
|
281
|
+
return null;
|
|
282
|
+
yamlStart = firstNewline + 1;
|
|
283
|
+
for (let index = yamlStart; index < closing; index++) {
|
|
284
|
+
if (content[index] === '\n')
|
|
285
|
+
yamlStart = index + 1;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const yamlBody = content.slice(yamlStart, content[closing - 1] === '\r' ? closing - 1 : closing);
|
|
289
|
+
let bodyStart = closing + 4;
|
|
290
|
+
while (bodyStart < content.length && /\s/u.test(content[bodyStart]))
|
|
291
|
+
bodyStart++;
|
|
292
|
+
const body = content.slice(bodyStart);
|
|
237
293
|
const fields = {};
|
|
238
294
|
for (const line of yamlBody.split(/\r?\n/)) {
|
|
239
295
|
if (line.trim().length === 0 || line.trim().startsWith('#'))
|
|
240
296
|
continue;
|
|
241
|
-
const
|
|
242
|
-
if (
|
|
297
|
+
const separator = line.indexOf(':');
|
|
298
|
+
if (separator < 0)
|
|
299
|
+
continue;
|
|
300
|
+
const k = line.slice(0, separator).trimEnd();
|
|
301
|
+
if (!/^[A-Za-z_][A-Za-z0-9_-]*$/.test(k))
|
|
302
|
+
continue;
|
|
303
|
+
const rawV = line.slice(separator + 1).trimStart();
|
|
304
|
+
if (/[\r\u2028\u2029]/u.test(rawV))
|
|
243
305
|
continue;
|
|
244
|
-
const [, k, rawV] = m;
|
|
245
306
|
fields[k] = parseYamlScalar(rawV.trim());
|
|
246
307
|
}
|
|
247
308
|
return { fields, body };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { link, mkdir, unlink, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname } from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import { boundedExecFileSync } from "../process/bounded-child-process.mjs";
|
|
5
5
|
export const FORTEMI_QUALIFICATION_RECEIPT = "aiwg.fortemi-live-qualification-receipt/v1";
|
|
6
6
|
const DIGEST = /^sha256:[0-9a-f]{64}$/;
|
|
7
7
|
const COMMIT = /^[0-9a-f]{40}$/;
|
|
@@ -23,7 +23,7 @@ function hasCompleteOperationInventory(operations) {
|
|
|
23
23
|
names.every((name) => REQUIRED_OPERATIONS.has(name)));
|
|
24
24
|
}
|
|
25
25
|
export function resolveFortemiQualificationSource(env = process.env, cwd = process.cwd()) {
|
|
26
|
-
const git = (...args) =>
|
|
26
|
+
const git = (...args) => boundedExecFileSync("git", args, 'quick', {
|
|
27
27
|
cwd,
|
|
28
28
|
encoding: "utf8",
|
|
29
29
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -31,6 +31,7 @@ export class TestCodeGenerator {
|
|
|
31
31
|
const warnings = [];
|
|
32
32
|
const files = [];
|
|
33
33
|
try {
|
|
34
|
+
this.validateInputs(suite);
|
|
34
35
|
// Group test cases by level
|
|
35
36
|
const unitTests = suite.testCases.filter(tc => tc.level === 'unit');
|
|
36
37
|
const integrationTests = suite.testCases.filter(tc => tc.level === 'integration');
|
|
@@ -80,6 +81,33 @@ export class TestCodeGenerator {
|
|
|
80
81
|
};
|
|
81
82
|
}
|
|
82
83
|
}
|
|
84
|
+
/** Validate declared string inputs before rendering any files. */
|
|
85
|
+
validateInputs(suite) {
|
|
86
|
+
const requireString = (value, field) => {
|
|
87
|
+
if (typeof value !== 'string')
|
|
88
|
+
throw new Error(`${field} must be a string`);
|
|
89
|
+
};
|
|
90
|
+
requireString(suite.name, 'suite.name');
|
|
91
|
+
for (const [index, testCase] of suite.testCases.entries()) {
|
|
92
|
+
const field = `testCases[${index}]`;
|
|
93
|
+
requireString(testCase.name, `${field}.name`);
|
|
94
|
+
if (!Array.isArray(testCase.testData))
|
|
95
|
+
throw new Error(`${field}.testData must be an array`);
|
|
96
|
+
for (const [dataIndex, data] of testCase.testData.entries()) {
|
|
97
|
+
const dataField = `${field}.testData[${dataIndex}]`;
|
|
98
|
+
requireString(data.name, `${dataField}.name`);
|
|
99
|
+
if (!Array.isArray(data.examples))
|
|
100
|
+
throw new Error(`${dataField}.examples must be an array`);
|
|
101
|
+
for (const [exampleIndex, example] of data.examples.entries()) {
|
|
102
|
+
requireString(example, `${dataField}.examples[${exampleIndex}]`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
for (const [stepIndex, step] of testCase.steps.entries()) {
|
|
106
|
+
if (step.testData !== undefined)
|
|
107
|
+
requireString(step.testData, `${field}.steps[${stepIndex}].testData`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
83
111
|
/**
|
|
84
112
|
* Generate a test file for unit or integration tests
|
|
85
113
|
*/
|
|
@@ -95,7 +123,7 @@ export class TestCodeGenerator {
|
|
|
95
123
|
lines.push(this.generateImports(level));
|
|
96
124
|
lines.push('');
|
|
97
125
|
// Test suite
|
|
98
|
-
lines.push(`describe(
|
|
126
|
+
lines.push(`describe(${JSON.stringify(`${suite.name} - ${level.charAt(0).toUpperCase() + level.slice(1)} Tests`)}, () => {`);
|
|
99
127
|
// Setup/teardown
|
|
100
128
|
if (this.options.includeSetup) {
|
|
101
129
|
lines.push(this.generateSetup(indent, suite));
|
|
@@ -137,7 +165,7 @@ export class TestCodeGenerator {
|
|
|
137
165
|
}
|
|
138
166
|
lines.push('');
|
|
139
167
|
// Test suite
|
|
140
|
-
lines.push(`test.describe(
|
|
168
|
+
lines.push(`test.describe(${JSON.stringify(`${suite.name} - E2E Tests`)}, () => {`);
|
|
141
169
|
// Setup
|
|
142
170
|
if (this.options.includeSetup) {
|
|
143
171
|
lines.push(`${indent}test.beforeEach(async ({ page }) => {`);
|
|
@@ -167,12 +195,12 @@ export class TestCodeGenerator {
|
|
|
167
195
|
if (!this.options.includeComments)
|
|
168
196
|
return '';
|
|
169
197
|
return `/**
|
|
170
|
-
* ${_level.toUpperCase()} Tests: ${suite.name}
|
|
198
|
+
* ${_level.toUpperCase()} Tests: ${this.escapeComment(suite.name)}
|
|
171
199
|
*
|
|
172
|
-
* Auto-generated from ${suite.useCaseId}
|
|
173
|
-
* Generated at: ${suite.generatedAt}
|
|
200
|
+
* Auto-generated from ${this.escapeComment(suite.useCaseId)}
|
|
201
|
+
* Generated at: ${this.escapeComment(suite.generatedAt)}
|
|
174
202
|
*
|
|
175
|
-
* @module test/${_level}/${suite.useCaseId.toLowerCase()}
|
|
203
|
+
* @module test/${_level}/${this.escapeComment(suite.useCaseId.toLowerCase())}
|
|
176
204
|
*/`;
|
|
177
205
|
}
|
|
178
206
|
/**
|
|
@@ -246,19 +274,19 @@ export class TestCodeGenerator {
|
|
|
246
274
|
// Test description comment
|
|
247
275
|
if (this.options.includeComments) {
|
|
248
276
|
lines.push(`${indent}/**`);
|
|
249
|
-
lines.push(`${indent} * ${testCase.id}: ${testCase.name}`);
|
|
250
|
-
lines.push(`${indent} * ${testCase.description}`);
|
|
251
|
-
lines.push(`${indent} * Type: ${testCase.type}`);
|
|
252
|
-
lines.push(`${indent} * Priority: ${testCase.priority}`);
|
|
277
|
+
lines.push(`${indent} * ${this.escapeComment(testCase.id)}: ${this.escapeComment(testCase.name)}`);
|
|
278
|
+
lines.push(`${indent} * ${this.escapeComment(testCase.description)}`);
|
|
279
|
+
lines.push(`${indent} * Type: ${this.escapeComment(testCase.type)}`);
|
|
280
|
+
lines.push(`${indent} * Priority: ${this.escapeComment(testCase.priority)}`);
|
|
253
281
|
lines.push(`${indent} */`);
|
|
254
282
|
}
|
|
255
283
|
// Test function
|
|
256
|
-
lines.push(`${indent}it(
|
|
284
|
+
lines.push(`${indent}it(${JSON.stringify(testCase.name)}, async () => {`);
|
|
257
285
|
// Preconditions
|
|
258
286
|
if (testCase.preconditions.length > 0) {
|
|
259
287
|
lines.push(`${indent2}// Preconditions:`);
|
|
260
288
|
for (const pre of testCase.preconditions) {
|
|
261
|
-
lines.push(`${indent2}// - ${pre}`);
|
|
289
|
+
lines.push(`${indent2}// - ${this.escapeComment(pre)}`);
|
|
262
290
|
}
|
|
263
291
|
lines.push('');
|
|
264
292
|
}
|
|
@@ -267,7 +295,7 @@ export class TestCodeGenerator {
|
|
|
267
295
|
if (testCase.testData.length > 0) {
|
|
268
296
|
lines.push(`${indent2}const testData = {`);
|
|
269
297
|
for (const data of testCase.testData) {
|
|
270
|
-
lines.push(`${indent3}${data.name}: ${this.getExampleValue(data.examples)}, // ${data.type}: ${data.constraints.join(', ')}`);
|
|
298
|
+
lines.push(`${indent3}${JSON.stringify(data.name)}: ${this.getExampleValue(data.examples)}, // ${this.escapeComment(data.type)}: ${this.escapeComment(data.constraints.join(', '))}`);
|
|
271
299
|
}
|
|
272
300
|
lines.push(`${indent2}};`);
|
|
273
301
|
}
|
|
@@ -282,7 +310,7 @@ export class TestCodeGenerator {
|
|
|
282
310
|
step.action.toLowerCase().includes('check')) {
|
|
283
311
|
continue; // Skip verification steps for Act section
|
|
284
312
|
}
|
|
285
|
-
lines.push(`${indent2}// Step ${step.number}: ${step.action}`);
|
|
313
|
+
lines.push(`${indent2}// Step ${this.escapeComment(step.number)}: ${this.escapeComment(step.action)}`);
|
|
286
314
|
lines.push(`${indent2}// TODO: Implement action`);
|
|
287
315
|
}
|
|
288
316
|
lines.push('');
|
|
@@ -293,7 +321,7 @@ export class TestCodeGenerator {
|
|
|
293
321
|
!step.action.toLowerCase().includes('check')) {
|
|
294
322
|
continue; // Skip action steps for Assert section
|
|
295
323
|
}
|
|
296
|
-
lines.push(`${indent2}// ${step.expectedResult}`);
|
|
324
|
+
lines.push(`${indent2}// ${this.escapeComment(step.expectedResult)}`);
|
|
297
325
|
lines.push(`${indent2}// expect(result).toEqual(expected);`);
|
|
298
326
|
}
|
|
299
327
|
// Default assertion if no verify steps
|
|
@@ -308,7 +336,7 @@ export class TestCodeGenerator {
|
|
|
308
336
|
lines.push('');
|
|
309
337
|
lines.push(`${indent2}// Postconditions:`);
|
|
310
338
|
for (const post of testCase.postconditions) {
|
|
311
|
-
lines.push(`${indent2}// - ${post}`);
|
|
339
|
+
lines.push(`${indent2}// - ${this.escapeComment(post)}`);
|
|
312
340
|
}
|
|
313
341
|
}
|
|
314
342
|
lines.push(`${indent}});`);
|
|
@@ -323,15 +351,15 @@ export class TestCodeGenerator {
|
|
|
323
351
|
// Test description comment
|
|
324
352
|
if (this.options.includeComments) {
|
|
325
353
|
lines.push(`${indent}/**`);
|
|
326
|
-
lines.push(`${indent} * ${testCase.id}: ${testCase.name}`);
|
|
327
|
-
lines.push(`${indent} * ${testCase.description}`);
|
|
354
|
+
lines.push(`${indent} * ${this.escapeComment(testCase.id)}: ${this.escapeComment(testCase.name)}`);
|
|
355
|
+
lines.push(`${indent} * ${this.escapeComment(testCase.description)}`);
|
|
328
356
|
lines.push(`${indent} */`);
|
|
329
357
|
}
|
|
330
358
|
// Test function
|
|
331
|
-
lines.push(`${indent}test(
|
|
359
|
+
lines.push(`${indent}test(${JSON.stringify(testCase.name)}, async ({ page }) => {`);
|
|
332
360
|
// Steps
|
|
333
361
|
for (const step of testCase.steps) {
|
|
334
|
-
lines.push(`${indent2}// Step ${step.number}: ${step.action}`);
|
|
362
|
+
lines.push(`${indent2}// Step ${this.escapeComment(step.number)}: ${this.escapeComment(step.action)}`);
|
|
335
363
|
lines.push(this.generateE2EStepCode(step, indent2));
|
|
336
364
|
lines.push('');
|
|
337
365
|
}
|
|
@@ -350,7 +378,7 @@ export class TestCodeGenerator {
|
|
|
350
378
|
return `${indent}await page.click('selector'); // TODO: Update selector`;
|
|
351
379
|
}
|
|
352
380
|
if (action.includes('enter') || action.includes('type') || action.includes('input')) {
|
|
353
|
-
return `${indent}await page.fill('selector',
|
|
381
|
+
return `${indent}await page.fill('selector', ${JSON.stringify(step.testData || 'test-data')}); // TODO: Update`;
|
|
354
382
|
}
|
|
355
383
|
if (action.includes('navigate') || action.includes('go to')) {
|
|
356
384
|
return `${indent}await page.goto('/path'); // TODO: Update path`;
|
|
@@ -359,15 +387,14 @@ export class TestCodeGenerator {
|
|
|
359
387
|
return `${indent}await page.waitForSelector('selector'); // TODO: Update selector`;
|
|
360
388
|
}
|
|
361
389
|
if (action.includes('verify') || action.includes('check') || action.includes('see')) {
|
|
362
|
-
return `${indent}await expect(page.locator('selector')).toBeVisible(); // ${step.expectedResult}`;
|
|
390
|
+
return `${indent}await expect(page.locator('selector')).toBeVisible(); // ${this.escapeComment(step.expectedResult)}`;
|
|
363
391
|
}
|
|
364
|
-
return `${indent}// TODO: Implement step - ${step.action}`;
|
|
392
|
+
return `${indent}// TODO: Implement step - ${this.escapeComment(step.action)}`;
|
|
365
393
|
}
|
|
366
|
-
/**
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
return str.replace(/'/g, "\\'").replace(/"/g, '\\"');
|
|
394
|
+
/** Keep metadata within a single line or block comment. */
|
|
395
|
+
escapeComment(value) {
|
|
396
|
+
return String(value).replace(/\*\//g, '* /')
|
|
397
|
+
.replace(/[\r\n\u2028\u2029]/g, (char) => '\\u' + char.charCodeAt(0).toString(16).padStart(4, '0'));
|
|
371
398
|
}
|
|
372
399
|
/**
|
|
373
400
|
* Get example value from test data
|
|
@@ -376,9 +403,9 @@ export class TestCodeGenerator {
|
|
|
376
403
|
if (examples.length === 0)
|
|
377
404
|
return "'test-value'";
|
|
378
405
|
const example = examples[0];
|
|
379
|
-
if (
|
|
406
|
+
if (/^\d+$/.test(example) && String(Number(example)) === example)
|
|
380
407
|
return example;
|
|
381
|
-
return
|
|
408
|
+
return JSON.stringify(example);
|
|
382
409
|
}
|
|
383
410
|
}
|
|
384
411
|
//# sourceMappingURL=test-code-generator.js.map
|
package/dist/src/uhp/client.js
CHANGED
|
@@ -5,6 +5,7 @@ import path from 'node:path';
|
|
|
5
5
|
import { DEFAULT_UHP_LIMITS, isUhpLoopback, isUhpPrivateAddress, resolveUhpLimits, validateUhpEndpoint } from './config.js';
|
|
6
6
|
import { parseUhpError, redactUhpText, UhpError } from './errors.js';
|
|
7
7
|
import { parseUhpEventStream } from './sse.js';
|
|
8
|
+
import { assertMissionWorkerHandoff } from '../mission-protocol/codecs.js';
|
|
8
9
|
import { UHP_VERSION, } from './types.js';
|
|
9
10
|
const ID_PATTERNS = {
|
|
10
11
|
harness: /^chrn_[A-Za-z0-9_-]+$/,
|
|
@@ -100,6 +101,7 @@ function ensureDiscovery(value) {
|
|
|
100
101
|
return discovery;
|
|
101
102
|
}
|
|
102
103
|
function ensureResponse(value) {
|
|
104
|
+
assertMissionWorkerHandoff(value, 'uhp');
|
|
103
105
|
if (!value || typeof value !== 'object')
|
|
104
106
|
throw new UhpError('invalid_response', 'UHP response must be an object', { remoteState: 'unknown' });
|
|
105
107
|
const response = value;
|
|
@@ -238,6 +240,7 @@ export class UhpClient {
|
|
|
238
240
|
return this.json(harnessId ? `v1/harnesses/${encodeURIComponent(assertId('harness', harnessId))}/models` : 'v1/models');
|
|
239
241
|
}
|
|
240
242
|
async createResponse(request, options = {}) {
|
|
243
|
+
assertMissionWorkerHandoff(request, 'uhp');
|
|
241
244
|
const normalized = { ...request, stream: false, metadata: { ...request.metadata, ...(request.metadata?.harness_id || !this.profile.defaultHarness ? {} : { harness_id: this.profile.defaultHarness }) }, ...(request.model || !this.profile.defaultModel ? {} : { model: this.profile.defaultModel }) };
|
|
242
245
|
if ((normalized.timeout_seconds ?? this.limits.maxTaskSeconds) > this.limits.maxTaskSeconds)
|
|
243
246
|
throw new UhpError('task_budget_exceeded', 'Requested UHP task duration exceeds the profile limit', { remoteState: 'not-started' });
|
|
@@ -267,6 +270,7 @@ export class UhpClient {
|
|
|
267
270
|
}
|
|
268
271
|
}
|
|
269
272
|
async *streamResponse(request, options = {}) {
|
|
273
|
+
assertMissionWorkerHandoff(request, 'uhp');
|
|
270
274
|
const normalized = { ...request, stream: true, metadata: { ...request.metadata, ...(request.metadata?.harness_id || !this.profile.defaultHarness ? {} : { harness_id: this.profile.defaultHarness }) } };
|
|
271
275
|
const digest = canonicalUhpRequestDigest(normalized);
|
|
272
276
|
const key = options.idempotencyKey ?? `aiwg-${digest}`;
|
|
@@ -285,7 +289,12 @@ export class UhpClient {
|
|
|
285
289
|
throw parseUhpError(response.status, parseJson(await readBounded(response)), secret ? [secret] : []);
|
|
286
290
|
if (!response.headers.get('content-type')?.toLowerCase().startsWith('text/event-stream') || !response.body)
|
|
287
291
|
throw new UhpError('invalid_stream', 'UHP streaming response must be text/event-stream', { remoteState: 'unknown' });
|
|
288
|
-
|
|
292
|
+
for await (const event of parseUhpEventStream(response.body, { inactivityTimeoutMs: this.limits.inactivityTimeoutMs, secrets: secret ? [secret] : [], signal: options.signal })) {
|
|
293
|
+
assertMissionWorkerHandoff(event, 'uhp');
|
|
294
|
+
if (event.response)
|
|
295
|
+
assertMissionWorkerHandoff(event.response, 'uhp');
|
|
296
|
+
yield event;
|
|
297
|
+
}
|
|
289
298
|
}
|
|
290
299
|
async readResponse(responseId) {
|
|
291
300
|
return ensureResponse(await this.json(`v1/responses/${encodeURIComponent(assertId('response', responseId))}`));
|
|
@@ -295,6 +304,7 @@ export class UhpClient {
|
|
|
295
304
|
return this.readResponse(responseId);
|
|
296
305
|
}
|
|
297
306
|
async continueResponse(previousResponseId, request, options = {}) {
|
|
307
|
+
assertMissionWorkerHandoff(request, 'uhp');
|
|
298
308
|
const previous = await this.readResponse(previousResponseId);
|
|
299
309
|
const harness = previous.metadata.harness_id;
|
|
300
310
|
if (request.metadata?.harness_id && harness && request.metadata.harness_id !== harness)
|
package/dist/src/uhp/mission.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { UHP_VERSION } from './types.js';
|
|
2
2
|
import { decodeMission } from '../mission-protocol/index.js';
|
|
3
|
+
import { assertMissionWorkerHandoff } from '../mission-protocol/codecs.js';
|
|
3
4
|
function collectArtifacts(response) {
|
|
4
5
|
const artifacts = new Map();
|
|
5
6
|
const visit = (value) => {
|
|
@@ -48,6 +49,10 @@ function collectInputFiles(request) {
|
|
|
48
49
|
return files;
|
|
49
50
|
}
|
|
50
51
|
export function projectUhpResponseToMission(profile, response, request = { input: '' }, event) {
|
|
52
|
+
assertMissionWorkerHandoff(response, 'uhp');
|
|
53
|
+
assertMissionWorkerHandoff(request, 'uhp');
|
|
54
|
+
if (event)
|
|
55
|
+
assertMissionWorkerHandoff(event, 'uhp');
|
|
51
56
|
const known = ['in_progress', 'completed', 'failed', 'incomplete', 'cancelled'].includes(response.status);
|
|
52
57
|
const state = response.status === 'in_progress' ? 'running' : known ? response.status : 'unknown';
|
|
53
58
|
const metadata = response.metadata ?? {};
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import fs from 'fs/promises';
|
|
13
13
|
import path from 'path';
|
|
14
14
|
import https from 'https';
|
|
15
|
-
import {
|
|
15
|
+
import { boundedExecFileSync } from '../process/bounded-child-process.mjs';
|
|
16
16
|
import { createInterface } from 'readline';
|
|
17
17
|
import { loadConfig, saveConfig, getChannel, getPackageRoot } from '../channel/manager.mjs';
|
|
18
18
|
import { updateInstallation } from './service.mjs';
|
|
@@ -68,15 +68,15 @@ async function fetchLatestNpmVersion() {
|
|
|
68
68
|
async function checkGitUpdates(edgePath) {
|
|
69
69
|
try {
|
|
70
70
|
// Fetch latest without merging
|
|
71
|
-
|
|
71
|
+
boundedExecFileSync('git', ['fetch', '--quiet'], 'network', { cwd: edgePath, timeout: 10000 });
|
|
72
72
|
|
|
73
73
|
// Check if local is behind remote
|
|
74
|
-
const localHash =
|
|
74
|
+
const localHash = boundedExecFileSync('git', ['rev-parse', 'HEAD'], 'quick', {
|
|
75
75
|
cwd: edgePath,
|
|
76
76
|
encoding: 'utf8',
|
|
77
77
|
}).trim();
|
|
78
78
|
|
|
79
|
-
const remoteHash =
|
|
79
|
+
const remoteHash = boundedExecFileSync('git', ['rev-parse', 'origin/main'], 'quick', {
|
|
80
80
|
cwd: edgePath,
|
|
81
81
|
encoding: 'utf8',
|
|
82
82
|
}).trim();
|
|
@@ -322,7 +322,7 @@ async function checkEdgeUpdates(config) {
|
|
|
322
322
|
console.log('');
|
|
323
323
|
console.log('Updating edge installation...');
|
|
324
324
|
try {
|
|
325
|
-
|
|
325
|
+
boundedExecFileSync('git', ['pull', '--ff-only'], 'network', { cwd: config.edgePath, stdio: 'inherit' });
|
|
326
326
|
console.log('Update complete!');
|
|
327
327
|
} catch (error) {
|
|
328
328
|
console.error('Update failed. Run manually:');
|
|
@@ -8,7 +8,11 @@
|
|
|
8
8
|
* `bin/aiwg.mjs`. It spawns a **detached, unref()'d** child Node
|
|
9
9
|
* process that runs this file with `--check` and writes the result
|
|
10
10
|
* to `~/.aiwg/update-notifier.json`. The child exits immediately
|
|
11
|
-
* after writing; the parent never waits on it.
|
|
11
|
+
* after writing; the parent never waits on it. The child's
|
|
12
|
+
* environment is an explicit allow-list (`notifierChildEnv()`), never
|
|
13
|
+
* the whole parent environment — see that function's doc comment.
|
|
14
|
+
* `bin/aiwg.mjs` also never calls this at all for a command that
|
|
15
|
+
* itself handles a credential (`isCredentialHandlingCommand()`).
|
|
12
16
|
*
|
|
13
17
|
* 2. On the *next* invocation, `maybePrintNotice()` reads the cache
|
|
14
18
|
* and prints a single-line "update available" message to stderr.
|
|
@@ -217,6 +221,54 @@ async function runCheck(packageRoot) {
|
|
|
217
221
|
});
|
|
218
222
|
}
|
|
219
223
|
|
|
224
|
+
/**
|
|
225
|
+
* Environment variable names copied from the parent into the background
|
|
226
|
+
* check child (go/no-go review, blocker 1B) — never the whole environment,
|
|
227
|
+
* which is what let a secret-bearing variable set for an entirely
|
|
228
|
+
* unrelated command (or read by one of this CLI's own credential-handling
|
|
229
|
+
* commands) reach this child too, visible to `NODE_DEBUG=child_process`
|
|
230
|
+
* and to anything else that can observe a child's own environment while it
|
|
231
|
+
* runs. Chosen by reading what this file's own `--check` path (module load
|
|
232
|
+
* + `runCheck()`) actually consults:
|
|
233
|
+
*
|
|
234
|
+
* PATH general process hygiene
|
|
235
|
+
* HOME, USERPROFILE, HOMEDRIVE, HOMEPATH `os.homedir()` (POSIX / Windows),
|
|
236
|
+
* consulted by `resolveUserConfigDir()`
|
|
237
|
+
* AIWG_CONFIG `resolveUserConfigDir()`'s own override
|
|
238
|
+
* NODE_EXTRA_CA_CERTS Node's own TLS trust-store
|
|
239
|
+
* augmentation — needed for the
|
|
240
|
+
* registry HTTPS GET to succeed
|
|
241
|
+
* behind a custom-CA proxy
|
|
242
|
+
*
|
|
243
|
+
* `fetchLatestVersion()` is a plain `https.get()` with no proxy-aware
|
|
244
|
+
* agent, so `HTTP(S)_PROXY`/`NO_PROXY` have no effect on it today and are
|
|
245
|
+
* deliberately left out — add them here (and give `fetchLatestVersion()` a
|
|
246
|
+
* proxy-aware agent) in the same change if proxy support is ever added.
|
|
247
|
+
* `loadInstallationIdentity()` is imported but never called by `--check`
|
|
248
|
+
* (only by the parent, before deciding whether to spawn at all), so its own
|
|
249
|
+
* env reads do not apply here either.
|
|
250
|
+
*/
|
|
251
|
+
const NOTIFIER_CHILD_ENV_ALLOWLIST = Object.freeze([
|
|
252
|
+
'PATH', 'HOME', 'USERPROFILE', 'HOMEDRIVE', 'HOMEPATH', 'AIWG_CONFIG', 'NODE_EXTRA_CA_CERTS',
|
|
253
|
+
]);
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Build the background check child's environment: only the allow-listed
|
|
257
|
+
* names, copied from `parentEnv` when present, plus the child-identity
|
|
258
|
+
* marker the child's own entry point does not otherwise need but existing
|
|
259
|
+
* tooling may key off of. Exported for the unit test that pins "only
|
|
260
|
+
* allow-listed names cross over, even when the parent env holds an extra
|
|
261
|
+
* secret-shaped variable" (go/no-go review, blocker 1 tests).
|
|
262
|
+
*/
|
|
263
|
+
export function notifierChildEnv(parentEnv = process.env) {
|
|
264
|
+
const env = {};
|
|
265
|
+
for (const name of NOTIFIER_CHILD_ENV_ALLOWLIST) {
|
|
266
|
+
if (parentEnv[name] !== undefined) env[name] = parentEnv[name];
|
|
267
|
+
}
|
|
268
|
+
env.AIWG_UPDATE_NOTIFIER_CHILD = '1';
|
|
269
|
+
return env;
|
|
270
|
+
}
|
|
271
|
+
|
|
220
272
|
/**
|
|
221
273
|
* Schedule a background update check if one hasn't run in the last
|
|
222
274
|
* CHECK_INTERVAL_MS. Non-blocking: spawns a detached child process and
|
|
@@ -248,7 +300,7 @@ export function scheduleBackgroundCheck(packageRoot) {
|
|
|
248
300
|
{
|
|
249
301
|
detached: true,
|
|
250
302
|
stdio: 'ignore',
|
|
251
|
-
env:
|
|
303
|
+
env: notifierChildEnv(),
|
|
252
304
|
},
|
|
253
305
|
);
|
|
254
306
|
// Unref so the parent can exit without waiting for the child. This is
|
|
@@ -2,6 +2,23 @@ import { parseWritingBrief, writingBriefHash, applyProofreadCorrections } from '
|
|
|
2
2
|
function matches(text, pattern) {
|
|
3
3
|
return [...text.matchAll(pattern)].map(m => ({ text: m[0], start: m.index, end: m.index + m[0].length }));
|
|
4
4
|
}
|
|
5
|
+
function sentences(text) {
|
|
6
|
+
const result = [];
|
|
7
|
+
let start = 0;
|
|
8
|
+
for (let index = 0; index < text.length; index++) {
|
|
9
|
+
const character = text[index];
|
|
10
|
+
if (character === '\n' || character === '.' || character === '!' || character === '?') {
|
|
11
|
+
if (index > start) {
|
|
12
|
+
const end = character === '\n' ? index : index + 1;
|
|
13
|
+
result.push({ text: text.slice(start, end), start, end });
|
|
14
|
+
}
|
|
15
|
+
start = index + 1;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (start < text.length)
|
|
19
|
+
result.push({ text: text.slice(start), start, end: text.length });
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
5
22
|
/** Literal invariants fail closed; lexical signals locate changes requiring semantic review. */
|
|
6
23
|
export function assessWritingFidelity(original, candidate, value) {
|
|
7
24
|
const brief = value ? parseWritingBrief(value) : undefined;
|
|
@@ -40,7 +57,7 @@ export function assessWritingFidelity(original, candidate, value) {
|
|
|
40
57
|
}
|
|
41
58
|
const firstPerson = /\b(?:I|we|my|our|me|us)\b/giu;
|
|
42
59
|
const allowedPersonal = brief?.authorClaims.map(c => c.text) ?? [];
|
|
43
|
-
for (const claim of
|
|
60
|
+
for (const claim of sentences(candidate)) {
|
|
44
61
|
if (matches(claim.text, firstPerson).length && !original.includes(claim.text.trim()) && !allowedPersonal.some(text => text === claim.text.trim())) {
|
|
45
62
|
changes.push({ kind: 'first-person', side: 'candidate', start: claim.start, end: claim.end, reason: 'New first-person wording lacks exact supplied author grounding; review is required.' });
|
|
46
63
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiwg/cli",
|
|
3
|
-
"version": "2026.10.
|
|
3
|
+
"version": "2026.10.3",
|
|
4
4
|
"description": "Lightweight AIWG CLI for signed, versioned web-backed resources.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,6 +30,9 @@
|
|
|
30
30
|
"schemas/security/",
|
|
31
31
|
"schemas/decision/",
|
|
32
32
|
"schemas/dataset/",
|
|
33
|
+
"schemas/mission-v1.schema.json",
|
|
34
|
+
"schemas/models/",
|
|
35
|
+
"vscode-extension/schemas/aiwg.config.v1.json",
|
|
33
36
|
"dist/src/",
|
|
34
37
|
"!dist/**/*.d.ts",
|
|
35
38
|
"!dist/**/*.map",
|
|
@@ -38,6 +41,19 @@
|
|
|
38
41
|
"dist/src/resources/web-release.d.ts",
|
|
39
42
|
"dist/src/dataset/**/*.d.ts",
|
|
40
43
|
"tools/_resolve-impl.mjs",
|
|
44
|
+
"tools/ralph-external/lib/provider-adapter.mjs",
|
|
45
|
+
"tools/ralph-external/lib/worker-health.mjs",
|
|
46
|
+
"tools/ralph-external/lib/claude-adapter.mjs",
|
|
47
|
+
"tools/ralph-external/lib/codex-adapter.mjs",
|
|
48
|
+
"tools/ralph-external/lib/opencode-adapter.mjs",
|
|
49
|
+
"tools/ralph-external/lib/factory-adapter.mjs",
|
|
50
|
+
"tools/ralph-external/lib/pi-adapter.mjs",
|
|
51
|
+
"tools/ralph-external/lib/omp-adapter.mjs",
|
|
52
|
+
"tools/ralph-external/lib/deepseek-harness-adapter.mjs",
|
|
53
|
+
"tools/ralph-external/lib/muse-adapter.mjs",
|
|
54
|
+
"tools/ralph-external/lib/openhuman-adapter.mjs",
|
|
55
|
+
"tools/providers/omp-transport.mjs",
|
|
56
|
+
"tools/providers/deepseek-harness-transport.mjs",
|
|
41
57
|
"tools/agents/deploy-agents.mjs",
|
|
42
58
|
"tools/agents/providers/",
|
|
43
59
|
"tools/providers/antigravity-transport.mjs",
|
|
@@ -71,6 +87,7 @@
|
|
|
71
87
|
"dependencies": {
|
|
72
88
|
"@fortemi/core": "2026.7.15",
|
|
73
89
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
90
|
+
"acorn": "^8.16.0",
|
|
74
91
|
"ajv": "^8.20.0",
|
|
75
92
|
"ajv-formats": "^3.0.1",
|
|
76
93
|
"chalk": "^4.1.2",
|