@aiwg/cli 2026.8.17 → 2026.8.19
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/bin/aiwg.mjs +24 -2
- package/dist/src/a2a/agent-card.js +4 -1
- package/dist/src/a2a/client.js +148 -68
- package/dist/src/a2a/codecs.js +480 -0
- package/dist/src/a2a/events.js +226 -0
- package/dist/src/a2a/hitl-driver.js +8 -6
- package/dist/src/a2a/hitl.js +2 -1
- package/dist/src/a2a/http.js +85 -5
- package/dist/src/a2a/protocol.js +136 -0
- package/dist/src/a2a/types.js +4 -14
- package/dist/src/a2a/webhook.js +101 -4
- package/dist/src/artifacts/index-builder.js +63 -12
- package/dist/src/artifacts/index-files.js +26 -5
- package/dist/src/artifacts/query-engine.js +67 -67
- package/dist/src/artifacts/stats.js +6 -2
- package/dist/src/artifacts/types.js +1 -1
- package/dist/src/audit/operator-decision.js +15 -1
- package/dist/src/channel/manager.mjs +89 -17
- package/dist/src/cli/handlers/index.js +3 -1
- package/dist/src/cli/handlers/installation.js +79 -0
- package/dist/src/cli/handlers/refresh.js +6 -2
- package/dist/src/cli/handlers/runtime-info.js +9 -1
- package/dist/src/cli/handlers/serve.js +107 -4
- package/dist/src/cli/handlers/session.js +12 -26
- package/dist/src/cli/handlers/use.js +19 -4
- package/dist/src/cli/handlers/utilities.js +4 -0
- package/dist/src/cli/handlers/version.js +4 -0
- package/dist/src/config/user-config-dir.mjs +29 -0
- package/dist/src/config/user-config.js +4 -22
- package/dist/src/extensions/commands/definitions.js +20 -1
- package/dist/src/features/catalog.js +2 -1
- package/dist/src/flow/graph-metadata.js +56 -0
- package/dist/src/installation/manager-command.mjs +31 -0
- package/dist/src/installation/manager.mjs +264 -0
- package/dist/src/serve/a2a-terminal-observer.js +28 -5
- package/dist/src/serve/dispatch-router.js +32 -4
- package/dist/src/serve/executor-registry.js +29 -0
- package/dist/src/serve/mission-conductor.js +15 -1
- package/dist/src/serve/stack-adapters.js +2 -1
- package/dist/src/serve/telemetry.js +5 -1
- package/dist/src/smiths/context-pipeline/claude-hook.js +8 -5
- package/dist/src/smiths/context-pipeline/line-endings.js +12 -0
- package/dist/src/smiths/context-pipeline/managed-hook.js +8 -5
- package/dist/src/smiths/context-pipeline/workspace-context.js +3 -1
- package/dist/src/update/checker.mjs +16 -15
- package/dist/src/update/notifier.mjs +8 -3
- package/dist/src/update/service.mjs +51 -5
- package/package.json +1 -1
- package/tools/agents/deploy-agents.mjs +28 -8
- package/tools/agents/providers/base.mjs +5 -3
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @source @src/artifacts/types.ts
|
|
8
8
|
* @tests @test/unit/artifacts/stats.test.ts
|
|
9
9
|
*/
|
|
10
|
-
import { GRAPH_CONFIGS, loadUserGraphConfigs } from './types.js';
|
|
10
|
+
import { GRAPH_CONFIGS, loadGlobalGraphConfigs, loadUserGraphConfigs } from './types.js';
|
|
11
11
|
import { loadIndexStats, loadGraphIndexFile } from './index-reader.js';
|
|
12
12
|
import { collectGraphIndexFiles, indexPathFor } from './index-files.js';
|
|
13
13
|
/** Calculate coverage over the same current file set used by the index builder. */
|
|
@@ -30,6 +30,8 @@ async function calculateCoverage(cwd, stats, graphType) {
|
|
|
30
30
|
*/
|
|
31
31
|
export async function showStats(cwd, options = {}) {
|
|
32
32
|
const { graph } = options;
|
|
33
|
+
loadUserGraphConfigs(cwd);
|
|
34
|
+
loadGlobalGraphConfigs();
|
|
33
35
|
if (graph) {
|
|
34
36
|
// Single graph mode
|
|
35
37
|
const stats = loadGraphIndexFile(cwd, 'stats.json', graph);
|
|
@@ -42,7 +44,6 @@ export async function showStats(cwd, options = {}) {
|
|
|
42
44
|
return;
|
|
43
45
|
}
|
|
44
46
|
// No graph specified: show all graphs with defaultBuild=true
|
|
45
|
-
loadUserGraphConfigs(cwd);
|
|
46
47
|
const graphTypes = Object.entries(GRAPH_CONFIGS)
|
|
47
48
|
.filter(([, config]) => config.defaultBuild)
|
|
48
49
|
.map(([name]) => name);
|
|
@@ -128,6 +129,9 @@ async function renderStats(cwd, stats, options, graphType) {
|
|
|
128
129
|
// Dependency graph
|
|
129
130
|
console.log('Dependency Graph:');
|
|
130
131
|
console.log(` Total edges: ${stats.graphMetrics.totalEdges}`);
|
|
132
|
+
if (stats.graphMetrics.markdownLinkEdges !== undefined) {
|
|
133
|
+
console.log(` Markdown link edges:${String(stats.graphMetrics.markdownLinkEdges).padStart(3)}`);
|
|
134
|
+
}
|
|
131
135
|
if (stats.graphMetrics.canonicalEdges !== undefined) {
|
|
132
136
|
console.log(` Canonical edges: ${stats.graphMetrics.canonicalEdges}`);
|
|
133
137
|
console.log(` Outgoing declares: ${stats.graphMetrics.outgoingDeclarations}`);
|
|
@@ -104,7 +104,7 @@ export const INDEX_VERSION = '1.0.0';
|
|
|
104
104
|
* making the serialized index schema incompatible; a mismatch simply forces a
|
|
105
105
|
* one-time content re-extraction during the next incremental build.
|
|
106
106
|
*/
|
|
107
|
-
export const INDEX_EXTRACTOR_VERSION = '2026.
|
|
107
|
+
export const INDEX_EXTRACTOR_VERSION = '2026.08.24.1';
|
|
108
108
|
/**
|
|
109
109
|
* Built-in graph definitions
|
|
110
110
|
*/
|
|
@@ -22,8 +22,9 @@ export function createDecisionRecord(input, previousHash) {
|
|
|
22
22
|
const actor = redact(input.actor);
|
|
23
23
|
const correlation = redact(input.correlation);
|
|
24
24
|
const runtime = input.runtime ? redact(input.runtime) : undefined;
|
|
25
|
+
const graph = input.graph ? redact(input.graph) : undefined;
|
|
25
26
|
const reason = redact(input.reason);
|
|
26
|
-
const detected = [...actor.paths, ...correlation.paths, ...(runtime?.paths ?? []), ...reason.paths];
|
|
27
|
+
const detected = [...actor.paths, ...correlation.paths, ...(runtime?.paths ?? []), ...(graph?.paths ?? []), ...reason.paths];
|
|
27
28
|
const unsigned = {
|
|
28
29
|
schema_version: OPERATOR_DECISION_SCHEMA,
|
|
29
30
|
event_id: input.event_id ?? randomUUID(),
|
|
@@ -36,6 +37,7 @@ export function createDecisionRecord(input, previousHash) {
|
|
|
36
37
|
classification: input.classification,
|
|
37
38
|
correlation: correlation.value,
|
|
38
39
|
...(runtime ? { runtime: runtime.value } : {}),
|
|
40
|
+
...(graph ? { graph: graph.value } : {}),
|
|
39
41
|
...(input.policy_ref ? { policy_ref: input.policy_ref } : {}),
|
|
40
42
|
redacted_fields: [...new Set([...(input.redacted_fields ?? []), ...detected])].sort(),
|
|
41
43
|
previous_hash: previousHash,
|
|
@@ -74,6 +76,15 @@ export function toOpenTelemetryLog(record) {
|
|
|
74
76
|
'aiwg.provider.id': record.correlation.provider_id,
|
|
75
77
|
'aiwg.sandbox.task_id': record.correlation.sandbox_task_id,
|
|
76
78
|
'aiwg.prompt.id': record.correlation.prompt_id,
|
|
79
|
+
'aiwg.graph.id': record.graph?.graph_id,
|
|
80
|
+
'aiwg.graph.version': record.graph?.graph_version,
|
|
81
|
+
'aiwg.graph.run_id': record.graph?.run_id,
|
|
82
|
+
'aiwg.graph.node_id': record.graph?.node_id,
|
|
83
|
+
'aiwg.graph.node_run_id': record.graph?.node_run_id,
|
|
84
|
+
'aiwg.graph.edge_id': record.graph?.edge_id,
|
|
85
|
+
'aiwg.graph.route_name': record.graph?.route_name,
|
|
86
|
+
'aiwg.graph.checkpoint_id': record.graph?.checkpoint_id,
|
|
87
|
+
'aiwg.graph.replay_parent_run_id': record.graph?.replay_parent_run_id,
|
|
77
88
|
}).filter(([, value]) => value !== undefined).map(([key, value]) => ({
|
|
78
89
|
key,
|
|
79
90
|
value: { stringValue: String(value) },
|
|
@@ -135,6 +146,9 @@ function validateInput(input) {
|
|
|
135
146
|
throw new Error('a non-empty operator reason is required');
|
|
136
147
|
if (!Object.values(input.correlation).some(Boolean))
|
|
137
148
|
throw new Error('at least one correlation identifier is required');
|
|
149
|
+
if (input.graph && ![input.graph.graph_id, input.graph.graph_version, input.graph.run_id, input.graph.node_id, input.graph.node_run_id].every(value => typeof value === 'string' && value.length > 0)) {
|
|
150
|
+
throw new Error('graph decision context requires graph, run, node, and node-run identity');
|
|
151
|
+
}
|
|
138
152
|
if (input.timestamp && !Number.isFinite(Date.parse(input.timestamp)))
|
|
139
153
|
throw new Error('timestamp must be valid ISO time');
|
|
140
154
|
}
|
|
@@ -13,8 +13,17 @@ import fs from 'fs/promises';
|
|
|
13
13
|
import { readFileSync, existsSync } from 'fs';
|
|
14
14
|
import path from 'path';
|
|
15
15
|
import { fileURLToPath } from 'url';
|
|
16
|
-
import { execSync, spawn } from 'child_process';
|
|
16
|
+
import { execFileSync, execSync, spawn } from 'child_process';
|
|
17
17
|
import os from 'os';
|
|
18
|
+
import { resolveUserConfigDir } from '../config/user-config-dir.mjs';
|
|
19
|
+
import {
|
|
20
|
+
assertCanonicalInstallation,
|
|
21
|
+
createInstallationIdentity,
|
|
22
|
+
inferInstallationMethod,
|
|
23
|
+
inspectInstallation,
|
|
24
|
+
loadInstallationIdentity,
|
|
25
|
+
saveInstallationIdentity,
|
|
26
|
+
} from '../installation/manager.mjs';
|
|
18
27
|
|
|
19
28
|
/**
|
|
20
29
|
* Run a command with inherited stdio, applying a wall-clock timeout so a
|
|
@@ -80,8 +89,6 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
80
89
|
const __dirname = path.dirname(__filename);
|
|
81
90
|
|
|
82
91
|
// Configuration paths
|
|
83
|
-
const CONFIG_DIR = path.join(os.homedir(), '.aiwg');
|
|
84
|
-
const CONFIG_FILE = path.join(CONFIG_DIR, 'channel.json');
|
|
85
92
|
const EDGE_INSTALL_PATH = path.join(os.homedir(), '.local', 'share', 'ai-writing-guide');
|
|
86
93
|
const REPO_URL = 'https://github.com/jmagly/aiwg.git';
|
|
87
94
|
|
|
@@ -132,22 +139,65 @@ export function getPackageRoot() {
|
|
|
132
139
|
* Load channel configuration
|
|
133
140
|
* @returns {Promise<object>} Channel configuration
|
|
134
141
|
*/
|
|
135
|
-
export async function loadConfig() {
|
|
142
|
+
export async function loadConfig(options = {}) {
|
|
143
|
+
const configDir = resolveUserConfigDir(options);
|
|
144
|
+
const configFile = path.join(configDir, 'channel.json');
|
|
145
|
+
let legacy = {};
|
|
136
146
|
try {
|
|
137
|
-
const data = await fs.readFile(
|
|
138
|
-
|
|
147
|
+
const data = await fs.readFile(configFile, 'utf8');
|
|
148
|
+
legacy = JSON.parse(data);
|
|
139
149
|
} catch {
|
|
140
|
-
|
|
150
|
+
// Legacy state is optional. The canonical identity below is authoritative.
|
|
141
151
|
}
|
|
152
|
+
const actualRoot = options.actualRoot ?? getPackageRoot();
|
|
153
|
+
const identity = loadInstallationIdentity({ ...options, actualRoot, legacyConfig: legacy });
|
|
154
|
+
if (!identity) return { ...DEFAULT_CONFIG, ...legacy };
|
|
155
|
+
return {
|
|
156
|
+
...DEFAULT_CONFIG,
|
|
157
|
+
...legacy,
|
|
158
|
+
channel: identity.channel,
|
|
159
|
+
edgePath: identity.edgePath ?? legacy.edgePath ?? EDGE_INSTALL_PATH,
|
|
160
|
+
devMode: identity.runMode === 'development',
|
|
161
|
+
lastUpdateCheck: identity.lastUpdateCheck,
|
|
162
|
+
updateCheckInterval: identity.updateCheckInterval,
|
|
163
|
+
checkOnStartup: identity.checkOnStartup,
|
|
164
|
+
installation: identity,
|
|
165
|
+
};
|
|
142
166
|
}
|
|
143
167
|
|
|
144
168
|
/**
|
|
145
169
|
* Save channel configuration
|
|
146
170
|
* @param {object} config - Configuration to save
|
|
147
171
|
*/
|
|
148
|
-
export async function saveConfig(config) {
|
|
149
|
-
|
|
150
|
-
|
|
172
|
+
export async function saveConfig(config, options = {}) {
|
|
173
|
+
const configDir = resolveUserConfigDir(options);
|
|
174
|
+
const configFile = path.join(configDir, 'channel.json');
|
|
175
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
176
|
+
const { installation: _installation, ...legacyConfig } = config;
|
|
177
|
+
await fs.writeFile(configFile, `${JSON.stringify(legacyConfig, null, 2)}\n`);
|
|
178
|
+
|
|
179
|
+
const actualRoot = options.actualRoot ?? getPackageRoot();
|
|
180
|
+
const previous = loadInstallationIdentity({ ...options, actualRoot, legacyConfig: config });
|
|
181
|
+
if (previous) {
|
|
182
|
+
const development = config.devMode === true;
|
|
183
|
+
const root = development && config.edgePath ? config.edgePath : previous.root;
|
|
184
|
+
const method = development ? 'source' : previous.method;
|
|
185
|
+
saveInstallationIdentity(createInstallationIdentity({
|
|
186
|
+
...options,
|
|
187
|
+
actualRoot,
|
|
188
|
+
root,
|
|
189
|
+
method,
|
|
190
|
+
runMode: development ? 'development' : 'normal',
|
|
191
|
+
channel: config.channel ?? previous.channel,
|
|
192
|
+
edgePath: config.edgePath ?? previous.edgePath,
|
|
193
|
+
managerExecutable: development ? undefined : previous.managerExecutable,
|
|
194
|
+
updateStrategy: development ? 'source-git' : previous.updateStrategy,
|
|
195
|
+
lastUpdateCheck: config.lastUpdateCheck ?? previous.lastUpdateCheck,
|
|
196
|
+
updateCheckInterval: config.updateCheckInterval ?? previous.updateCheckInterval,
|
|
197
|
+
checkOnStartup: config.checkOnStartup ?? previous.checkOnStartup,
|
|
198
|
+
recordedAt: previous.recordedAt,
|
|
199
|
+
}), options);
|
|
200
|
+
}
|
|
151
201
|
}
|
|
152
202
|
|
|
153
203
|
/**
|
|
@@ -178,7 +228,7 @@ export async function getFrameworkRoot() {
|
|
|
178
228
|
}
|
|
179
229
|
}
|
|
180
230
|
|
|
181
|
-
return getPackageRoot();
|
|
231
|
+
return config.installation?.root ?? getPackageRoot();
|
|
182
232
|
}
|
|
183
233
|
|
|
184
234
|
/**
|
|
@@ -316,12 +366,16 @@ export async function switchToDev(devPath) {
|
|
|
316
366
|
*/
|
|
317
367
|
export async function switchToNext() {
|
|
318
368
|
const config = await loadConfig();
|
|
369
|
+
const status = assertCanonicalInstallation({ actualRoot: getPackageRoot() });
|
|
370
|
+
if (status.identity.method !== 'npm' || !status.identity.managerExecutable) {
|
|
371
|
+
throw new Error('The next channel requires a canonical npm installation with a recorded package-manager executable.');
|
|
372
|
+
}
|
|
319
373
|
|
|
320
374
|
console.log('Switching to next channel (alpha/beta/RC — latest pre-release)...');
|
|
321
375
|
console.log('');
|
|
322
376
|
|
|
323
377
|
try {
|
|
324
|
-
|
|
378
|
+
execFileSync(status.identity.managerExecutable, ['install', '--global', 'aiwg@next'], { stdio: 'inherit' });
|
|
325
379
|
} catch (error) {
|
|
326
380
|
console.error('Failed to install aiwg@next:', error.message);
|
|
327
381
|
console.error('Check that npm is available and you have write access to the global prefix.');
|
|
@@ -346,12 +400,16 @@ export async function switchToNext() {
|
|
|
346
400
|
*/
|
|
347
401
|
export async function switchToNightly() {
|
|
348
402
|
const config = await loadConfig();
|
|
403
|
+
const status = assertCanonicalInstallation({ actualRoot: getPackageRoot() });
|
|
404
|
+
if (status.identity.method !== 'npm' || !status.identity.managerExecutable) {
|
|
405
|
+
throw new Error('The nightly channel requires a canonical npm installation with a recorded package-manager executable.');
|
|
406
|
+
}
|
|
349
407
|
|
|
350
408
|
console.log('Switching to nightly channel (latest automated snapshot)...');
|
|
351
409
|
console.log('');
|
|
352
410
|
|
|
353
411
|
try {
|
|
354
|
-
|
|
412
|
+
execFileSync(status.identity.managerExecutable, ['install', '--global', 'aiwg@nightly'], { stdio: 'inherit' });
|
|
355
413
|
} catch (error) {
|
|
356
414
|
console.error('Failed to install aiwg@nightly:', error.message);
|
|
357
415
|
console.error('Check that npm is available and you have write access to the global prefix.');
|
|
@@ -376,17 +434,29 @@ export async function switchToNightly() {
|
|
|
376
434
|
export async function switchToStable() {
|
|
377
435
|
const config = await loadConfig();
|
|
378
436
|
|
|
379
|
-
console.log('Switching to stable channel
|
|
437
|
+
console.log('Switching to the stable channel...');
|
|
380
438
|
console.log('');
|
|
381
439
|
|
|
382
440
|
config.channel = 'stable';
|
|
383
441
|
config.devMode = false;
|
|
384
442
|
await saveConfig(config);
|
|
443
|
+
const actualRoot = getPackageRoot();
|
|
444
|
+
saveInstallationIdentity(createInstallationIdentity({
|
|
445
|
+
actualRoot,
|
|
446
|
+
root: actualRoot,
|
|
447
|
+
method: inferInstallationMethod(actualRoot),
|
|
448
|
+
runMode: 'normal',
|
|
449
|
+
channel: 'stable',
|
|
450
|
+
edgePath: config.edgePath,
|
|
451
|
+
lastUpdateCheck: config.lastUpdateCheck,
|
|
452
|
+
updateCheckInterval: config.updateCheckInterval,
|
|
453
|
+
checkOnStartup: config.checkOnStartup,
|
|
454
|
+
}));
|
|
385
455
|
|
|
386
456
|
console.log('Switched to stable channel.');
|
|
387
|
-
console.log('You are now using the
|
|
457
|
+
console.log('You are now using the canonical installed package.');
|
|
388
458
|
console.log('');
|
|
389
|
-
console.log('To update:
|
|
459
|
+
console.log('To update: aiwg refresh --channel latest');
|
|
390
460
|
console.log('To switch to edge: aiwg --use-main');
|
|
391
461
|
}
|
|
392
462
|
|
|
@@ -415,6 +485,7 @@ function normalizeRepoUrl(repository) {
|
|
|
415
485
|
export async function getVersionInfo() {
|
|
416
486
|
const config = await loadConfig();
|
|
417
487
|
const packageRoot = getPackageRoot();
|
|
488
|
+
const installation = inspectInstallation({ actualRoot: packageRoot, identity: config.installation });
|
|
418
489
|
|
|
419
490
|
// Read package.json version
|
|
420
491
|
const packageJsonPath = path.join(packageRoot, 'package.json');
|
|
@@ -441,6 +512,7 @@ export async function getVersionInfo() {
|
|
|
441
512
|
version,
|
|
442
513
|
channel,
|
|
443
514
|
packageRoot,
|
|
515
|
+
installation,
|
|
444
516
|
devMode: config.devMode || false,
|
|
445
517
|
// Public-facing URLs — single source of truth is package.json, so user-visible
|
|
446
518
|
// stamps/links never hardcode the internal build origin. The published package
|
|
@@ -480,7 +552,7 @@ export async function updateEdge() {
|
|
|
480
552
|
const config = await loadConfig();
|
|
481
553
|
|
|
482
554
|
if (config.channel !== 'edge') {
|
|
483
|
-
console.log('Not in edge channel. Use
|
|
555
|
+
console.log('Not in edge channel. Use `aiwg update` for the canonical installed channel.');
|
|
484
556
|
return;
|
|
485
557
|
}
|
|
486
558
|
|
|
@@ -63,12 +63,13 @@ import { costReportHandler } from './cost-report.js';
|
|
|
63
63
|
import { evidenceHandler } from './evidence.js';
|
|
64
64
|
import { artifactVerifyHandler } from './artifact-verify.js';
|
|
65
65
|
import { outputModeHandler } from './output-mode.js';
|
|
66
|
+
import { installationHandler } from './installation.js';
|
|
66
67
|
// Re-export individual handlers
|
|
67
68
|
export {
|
|
68
69
|
// Maintenance
|
|
69
70
|
helpHandler, versionHandler, authHandler, doctorHandler, contextFirewallHandler, updateHandler, refreshHandler, regenerateHandler, workspaceContextHandler,
|
|
70
71
|
// Framework management
|
|
71
|
-
useHandler, listHandler, removeHandler, promoteHandler, installHandler, packagesHandler, marketplaceHandler, initHandler, setupHandler, setupGenerateHandler, setupRunHandler, setupValidateHandler, issueHandler, issueAuditHandler, runHandler, jobHandler, costReportHandler, evidenceHandler, artifactVerifyHandler, outputModeHandler,
|
|
72
|
+
useHandler, listHandler, removeHandler, promoteHandler, installHandler, packagesHandler, marketplaceHandler, initHandler, setupHandler, setupGenerateHandler, setupRunHandler, setupValidateHandler, issueHandler, issueAuditHandler, runHandler, jobHandler, costReportHandler, evidenceHandler, artifactVerifyHandler, outputModeHandler, installationHandler,
|
|
72
73
|
// Project
|
|
73
74
|
newBundleHandler, quickrefHandler, newProjectHandler, sessionHandler, sessionsHandler,
|
|
74
75
|
// Workspace
|
|
@@ -125,6 +126,7 @@ export const allHandlers = [
|
|
|
125
126
|
contextFirewallHandler,
|
|
126
127
|
updateHandler,
|
|
127
128
|
refreshHandler,
|
|
129
|
+
installationHandler,
|
|
128
130
|
regenerateHandler,
|
|
129
131
|
workspaceContextHandler,
|
|
130
132
|
// Framework management
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { getPackageRoot } from '../../channel/manager.mjs';
|
|
2
|
+
import { adoptInstallation, inspectInstallation, loadInstallationIdentity, switchInstallation, } from '../../installation/manager.mjs';
|
|
3
|
+
function valueAfter(args, flag) {
|
|
4
|
+
const index = args.indexOf(flag);
|
|
5
|
+
return index >= 0 ? args[index + 1] : undefined;
|
|
6
|
+
}
|
|
7
|
+
function display(status, json) {
|
|
8
|
+
if (json) {
|
|
9
|
+
console.log(JSON.stringify(status, null, 2));
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
console.log('\nCanonical AIWG Installation');
|
|
13
|
+
console.log('===========================');
|
|
14
|
+
console.log(`State: ${status.state}`);
|
|
15
|
+
console.log(`Canonical method: ${status.identity?.method ?? '(unrecorded)'}`);
|
|
16
|
+
console.log(`Canonical root: ${status.identity?.root ?? '(unrecorded)'}`);
|
|
17
|
+
console.log(`Manager: ${status.identity?.managerExecutable ?? '(internal)'}`);
|
|
18
|
+
console.log(`Update strategy: ${status.identity?.updateStrategy ?? '(unrecorded)'}`);
|
|
19
|
+
console.log(`Run mode: ${status.identity?.runMode ?? '(unrecorded)'}`);
|
|
20
|
+
console.log(`Release channel: ${status.identity?.channel ?? '(unrecorded)'}`);
|
|
21
|
+
console.log(`Actual method: ${status.actualMethod}`);
|
|
22
|
+
console.log(`Actual root: ${status.actualRoot}`);
|
|
23
|
+
if (status.drift.length > 0) {
|
|
24
|
+
console.log('Drift:');
|
|
25
|
+
for (const item of status.drift)
|
|
26
|
+
console.log(` - ${item}`);
|
|
27
|
+
}
|
|
28
|
+
console.log('');
|
|
29
|
+
}
|
|
30
|
+
export const installationHandler = {
|
|
31
|
+
id: 'installation',
|
|
32
|
+
name: 'Installation',
|
|
33
|
+
description: 'Inspect, adopt, or deliberately switch the canonical global installation',
|
|
34
|
+
category: 'maintenance',
|
|
35
|
+
aliases: [],
|
|
36
|
+
async execute(ctx) {
|
|
37
|
+
const [action = 'show'] = ctx.args;
|
|
38
|
+
const json = ctx.args.includes('--json');
|
|
39
|
+
const actualRoot = getPackageRoot();
|
|
40
|
+
const common = {
|
|
41
|
+
actualRoot,
|
|
42
|
+
configDir: valueAfter(ctx.args, '--config-dir'),
|
|
43
|
+
managerExecutable: valueAfter(ctx.args, '--manager'),
|
|
44
|
+
channel: valueAfter(ctx.args, '--channel'),
|
|
45
|
+
};
|
|
46
|
+
if (action === 'show') {
|
|
47
|
+
const identity = loadInstallationIdentity({ ...common, createIfMissing: true });
|
|
48
|
+
display(inspectInstallation({ ...common, identity, probeManager: true }), json);
|
|
49
|
+
return { exitCode: 0 };
|
|
50
|
+
}
|
|
51
|
+
if (action === 'adopt') {
|
|
52
|
+
const method = valueAfter(ctx.args, '--method');
|
|
53
|
+
const status = adoptInstallation({
|
|
54
|
+
...common,
|
|
55
|
+
method,
|
|
56
|
+
runMode: valueAfter(ctx.args, '--run-mode'),
|
|
57
|
+
});
|
|
58
|
+
display(status, json);
|
|
59
|
+
return { exitCode: 0 };
|
|
60
|
+
}
|
|
61
|
+
if (action === 'switch') {
|
|
62
|
+
const root = valueAfter(ctx.args, '--root');
|
|
63
|
+
const method = valueAfter(ctx.args, '--method');
|
|
64
|
+
if (!root || !method) {
|
|
65
|
+
return { exitCode: 2, message: 'Usage: aiwg installation switch --root <path> --method <npm|web|source> [--manager <absolute-path>]' };
|
|
66
|
+
}
|
|
67
|
+
const status = switchInstallation({
|
|
68
|
+
...common,
|
|
69
|
+
root,
|
|
70
|
+
method,
|
|
71
|
+
runMode: valueAfter(ctx.args, '--run-mode'),
|
|
72
|
+
});
|
|
73
|
+
display(status, json);
|
|
74
|
+
return { exitCode: 0 };
|
|
75
|
+
}
|
|
76
|
+
return { exitCode: 2, message: 'Usage: aiwg installation <show|adopt|switch> [options]' };
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=installation.js.map
|
|
@@ -205,6 +205,7 @@ export const refreshHandler = {
|
|
|
205
205
|
if (!quiet)
|
|
206
206
|
ui.info(dryRun ? 'Would refresh remote packages...' : 'Refreshing remote packages...');
|
|
207
207
|
const deploymentFailures = [];
|
|
208
|
+
let updateFailure = null;
|
|
208
209
|
if (!dryRun) {
|
|
209
210
|
try {
|
|
210
211
|
const refreshed = await refreshAllPackages();
|
|
@@ -242,8 +243,10 @@ export const refreshHandler = {
|
|
|
242
243
|
ui.success('Package up to date');
|
|
243
244
|
}
|
|
244
245
|
else {
|
|
245
|
-
|
|
246
|
-
|
|
246
|
+
updateFailure = { exitCode: updateResult.exitCode };
|
|
247
|
+
if (!quiet) {
|
|
248
|
+
ui.warn('Installation update failed; continuing with re-deployment. Run `aiwg installation show` for canonical-install diagnostics.');
|
|
249
|
+
}
|
|
247
250
|
}
|
|
248
251
|
}
|
|
249
252
|
}
|
|
@@ -463,6 +466,7 @@ export const refreshHandler = {
|
|
|
463
466
|
channel: channel || undefined,
|
|
464
467
|
staleAgentRemovals,
|
|
465
468
|
deploymentFailures,
|
|
469
|
+
updateFailure,
|
|
466
470
|
});
|
|
467
471
|
console.log(output);
|
|
468
472
|
}
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import path from 'path';
|
|
12
12
|
import { AiwgError, EXIT_CODES, handlerResultFromError } from '../errors.js';
|
|
13
|
+
import { getPackageRoot } from '../../channel/manager.mjs';
|
|
14
|
+
import { inspectInstallation } from '../../installation/manager.mjs';
|
|
13
15
|
function isMissingRuntimeCatalogError(error) {
|
|
14
16
|
return error instanceof Error && error.message.includes('No catalog found');
|
|
15
17
|
}
|
|
@@ -237,8 +239,9 @@ async function handleRuntimeInfo(args, cwd = process.cwd()) {
|
|
|
237
239
|
await discovery.discover();
|
|
238
240
|
summary = await discovery.getSummary();
|
|
239
241
|
}
|
|
242
|
+
const installation = inspectInstallation({ actualRoot: getPackageRoot() });
|
|
240
243
|
if (hasJson) {
|
|
241
|
-
console.log(JSON.stringify(summary, null, 2));
|
|
244
|
+
console.log(JSON.stringify({ ...summary, installation }, null, 2));
|
|
242
245
|
}
|
|
243
246
|
else {
|
|
244
247
|
console.log(`\nRuntime Environment Summary`);
|
|
@@ -256,6 +259,11 @@ async function handleRuntimeInfo(args, cwd = process.cwd()) {
|
|
|
256
259
|
console.log(`\nTotal: ${summary.totalTools} verified tools`);
|
|
257
260
|
console.log(`\nLast Discovery: ${summary.lastDiscovery}`);
|
|
258
261
|
console.log(`Catalog: ${summary.catalogPath}`);
|
|
262
|
+
console.log(`\nAIWG Installation:`);
|
|
263
|
+
console.log(` Canonical: ${installation.identity?.method ?? 'unrecorded'} at ${installation.identity?.root ?? '(unrecorded)'}`);
|
|
264
|
+
console.log(` Actual: ${installation.actualMethod} at ${installation.actualRoot}`);
|
|
265
|
+
console.log(` Run mode: ${installation.identity?.runMode ?? '(unrecorded)'}`);
|
|
266
|
+
console.log(` State: ${installation.state}`);
|
|
259
267
|
// Scheduler backend detection
|
|
260
268
|
const { execSync } = await import('child_process');
|
|
261
269
|
let schedulerBackend = 'external trigger required (system cron/systemd/CI)';
|
|
@@ -27,8 +27,14 @@ const pushSecretRegistry = new PushSecretRegistry();
|
|
|
27
27
|
const webhookIdempotency = new IdempotencyCache();
|
|
28
28
|
const DEFAULT_PORT = 7337;
|
|
29
29
|
const DEFAULT_HOST = '127.0.0.1';
|
|
30
|
+
function readA2AProtocolPolicy(value) {
|
|
31
|
+
const policy = value ?? '0.3';
|
|
32
|
+
if (policy === '0.3' || policy === '1.0' || policy === 'auto')
|
|
33
|
+
return policy;
|
|
34
|
+
throw new Error(`AIWG_A2A_PROTOCOL_POLICY must be 0.3, 1.0, or auto (received '${policy}')`);
|
|
35
|
+
}
|
|
30
36
|
/**
|
|
31
|
-
* Parse
|
|
37
|
+
* Parse dashboard and A2A negotiation flags from args.
|
|
32
38
|
*/
|
|
33
39
|
function parseServeArgs(args) {
|
|
34
40
|
let port = DEFAULT_PORT;
|
|
@@ -36,6 +42,9 @@ function parseServeArgs(args) {
|
|
|
36
42
|
let open = true;
|
|
37
43
|
let readOnly = false;
|
|
38
44
|
let sandbox = null;
|
|
45
|
+
let a2aProtocolPolicy;
|
|
46
|
+
let allowA2AProtocolFallback;
|
|
47
|
+
let allowLegacyExecutorFallback;
|
|
39
48
|
for (let i = 0; i < args.length; i++) {
|
|
40
49
|
const arg = args[i];
|
|
41
50
|
if (arg === '--port' && args[i + 1]) {
|
|
@@ -58,8 +67,33 @@ function parseServeArgs(args) {
|
|
|
58
67
|
else if (arg === '--read-only') {
|
|
59
68
|
readOnly = true;
|
|
60
69
|
}
|
|
70
|
+
else if (arg === '--a2a-protocol' && args[i + 1]) {
|
|
71
|
+
a2aProtocolPolicy = readA2AProtocolPolicy(args[i + 1]);
|
|
72
|
+
i++;
|
|
73
|
+
}
|
|
74
|
+
else if (arg === '--a2a-protocol-fallback') {
|
|
75
|
+
allowA2AProtocolFallback = true;
|
|
76
|
+
}
|
|
77
|
+
else if (arg === '--no-a2a-protocol-fallback') {
|
|
78
|
+
allowA2AProtocolFallback = false;
|
|
79
|
+
}
|
|
80
|
+
else if (arg === '--a2a-legacy-executor-fallback') {
|
|
81
|
+
allowLegacyExecutorFallback = true;
|
|
82
|
+
}
|
|
83
|
+
else if (arg === '--no-a2a-legacy-executor-fallback') {
|
|
84
|
+
allowLegacyExecutorFallback = false;
|
|
85
|
+
}
|
|
61
86
|
}
|
|
62
|
-
return {
|
|
87
|
+
return {
|
|
88
|
+
port,
|
|
89
|
+
host,
|
|
90
|
+
open,
|
|
91
|
+
readOnly,
|
|
92
|
+
sandbox,
|
|
93
|
+
...(a2aProtocolPolicy ? { a2aProtocolPolicy } : {}),
|
|
94
|
+
...(allowA2AProtocolFallback !== undefined ? { allowA2AProtocolFallback } : {}),
|
|
95
|
+
...(allowLegacyExecutorFallback !== undefined ? { allowLegacyExecutorFallback } : {}),
|
|
96
|
+
};
|
|
63
97
|
}
|
|
64
98
|
// ============================================================
|
|
65
99
|
// WebSocket routing (#851)
|
|
@@ -499,6 +533,13 @@ async function setupWebSockets(httpServer, readOnly) {
|
|
|
499
533
|
* @see #1277 — moves the integration suite off spawn-based testing
|
|
500
534
|
*/
|
|
501
535
|
export async function startServer(opts) {
|
|
536
|
+
const configuredA2AProtocolPolicy = opts.a2aProtocolPolicy
|
|
537
|
+
?? readA2AProtocolPolicy(process.env['AIWG_A2A_PROTOCOL_POLICY']);
|
|
538
|
+
const configuredA2AProtocolFallback = opts.allowA2AProtocolFallback
|
|
539
|
+
?? process.env['AIWG_A2A_PROTOCOL_FALLBACK'] === 'true';
|
|
540
|
+
const configuredLegacyExecutorFallback = configuredA2AProtocolPolicy !== '1.0'
|
|
541
|
+
&& (opts.allowLegacyExecutorFallback
|
|
542
|
+
?? process.env['AIWG_A2A_LEGACY_EXECUTOR_FALLBACK'] !== 'false');
|
|
502
543
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
503
544
|
let honoMod;
|
|
504
545
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -760,8 +801,32 @@ export async function startServer(opts) {
|
|
|
760
801
|
let a2aInstanceId;
|
|
761
802
|
let dispatchPath = 'v2';
|
|
762
803
|
let a2aTask = undefined;
|
|
804
|
+
let a2aProtocolVersion;
|
|
805
|
+
let a2aInterface;
|
|
806
|
+
let a2aFallbackReason;
|
|
807
|
+
const a2aProtocolPolicy = configuredA2AProtocolPolicy;
|
|
763
808
|
try {
|
|
764
809
|
const result = await routeDispatch(executor, payload, {
|
|
810
|
+
a2aProtocolPolicy,
|
|
811
|
+
allowA2AProtocolFallback: configuredA2AProtocolFallback,
|
|
812
|
+
allowLegacyExecutorFallback: configuredLegacyExecutorFallback,
|
|
813
|
+
onA2AProtocolSelection: (info) => {
|
|
814
|
+
telemetryStore.ingest(createEvent('a2a.protocol.selected', sessionId, {
|
|
815
|
+
selected_version: info.selected,
|
|
816
|
+
policy: info.policy,
|
|
817
|
+
...(info.interface ? {
|
|
818
|
+
protocol_binding: info.interface.protocolBinding,
|
|
819
|
+
interface_url: info.interface.url,
|
|
820
|
+
} : {}),
|
|
821
|
+
}, missionId));
|
|
822
|
+
},
|
|
823
|
+
onA2AProtocolFallback: (info) => {
|
|
824
|
+
telemetryStore.ingest(createEvent('a2a.protocol.fallback', sessionId, {
|
|
825
|
+
from_version: info.from,
|
|
826
|
+
to_version: info.to,
|
|
827
|
+
reason: info.reason,
|
|
828
|
+
}, missionId));
|
|
829
|
+
},
|
|
765
830
|
onV1Fallback: (info) => {
|
|
766
831
|
logServeWarn('dispatch', `v1 fallback for executor ${info.executorId}: ${info.reason}`);
|
|
767
832
|
telemetryStore.ingest(createEvent('v1.dispatch.fallback', sessionId, info, missionId));
|
|
@@ -775,6 +840,9 @@ export async function startServer(opts) {
|
|
|
775
840
|
dispatchPath = result.dispatchPath;
|
|
776
841
|
a2aInstanceId = result.a2aInstanceId;
|
|
777
842
|
a2aTask = result.task;
|
|
843
|
+
a2aProtocolVersion = result.a2aProtocolVersion;
|
|
844
|
+
a2aInterface = result.a2aInterface;
|
|
845
|
+
a2aFallbackReason = result.a2aFallbackReason;
|
|
778
846
|
if (result.estimatedStart)
|
|
779
847
|
estimatedStart = result.estimatedStart;
|
|
780
848
|
}
|
|
@@ -796,12 +864,22 @@ export async function startServer(opts) {
|
|
|
796
864
|
return c.json({ error: errorTag, detail: msg }, status);
|
|
797
865
|
}
|
|
798
866
|
// 4. Record the mission and emit telemetry
|
|
867
|
+
if (a2aProtocolVersion && a2aInterface) {
|
|
868
|
+
executorRegistry.recordA2AProtocolSelection(executor.executorId, {
|
|
869
|
+
policy: a2aProtocolPolicy,
|
|
870
|
+
selectedVersion: a2aProtocolVersion,
|
|
871
|
+
interface: a2aInterface,
|
|
872
|
+
...(a2aFallbackReason ? { fallbackReason: a2aFallbackReason } : {}),
|
|
873
|
+
});
|
|
874
|
+
}
|
|
799
875
|
executorRegistry.assignMission(missionId, executor.executorId);
|
|
800
876
|
if (dispatchPath === 'v2' && a2aTask && a2aInstanceId) {
|
|
801
877
|
void observeA2ATerminalState(executorRegistry, executor, missionId, a2aInstanceId, a2aTask, {
|
|
802
878
|
onError: (err) => {
|
|
803
879
|
logServeWarn('dispatch', `A2A terminal observer failed for mission ${missionId}: ${err.message ?? String(err)}`);
|
|
804
880
|
},
|
|
881
|
+
...(a2aProtocolVersion ? { protocolVersion: a2aProtocolVersion } : {}),
|
|
882
|
+
...(a2aInterface ? { selectedInterface: a2aInterface } : {}),
|
|
805
883
|
});
|
|
806
884
|
}
|
|
807
885
|
telemetryStore.ingest(createEvent('mission.dispatch', sessionId, {
|
|
@@ -809,6 +887,8 @@ export async function startServer(opts) {
|
|
|
809
887
|
executorId: executor.executorId,
|
|
810
888
|
objective: payload.objective,
|
|
811
889
|
completion: payload.completion,
|
|
890
|
+
...(a2aProtocolVersion ? { a2a_protocol_version: a2aProtocolVersion } : {}),
|
|
891
|
+
...(a2aFallbackReason ? { a2a_fallback_reason: a2aFallbackReason } : {}),
|
|
812
892
|
}, missionId));
|
|
813
893
|
// 5. Return 202 Accepted
|
|
814
894
|
const dispatchResp = {
|
|
@@ -819,6 +899,14 @@ export async function startServer(opts) {
|
|
|
819
899
|
};
|
|
820
900
|
if (a2aInstanceId)
|
|
821
901
|
dispatchResp.a2a_instance_id = a2aInstanceId;
|
|
902
|
+
if (a2aProtocolVersion)
|
|
903
|
+
dispatchResp.a2a_protocol_version = a2aProtocolVersion;
|
|
904
|
+
if (a2aInterface) {
|
|
905
|
+
dispatchResp.a2a_protocol_binding = a2aInterface.protocolBinding;
|
|
906
|
+
dispatchResp.a2a_interface_url = a2aInterface.url;
|
|
907
|
+
}
|
|
908
|
+
if (a2aFallbackReason)
|
|
909
|
+
dispatchResp.a2a_fallback_reason = a2aFallbackReason;
|
|
822
910
|
if (estimatedStart)
|
|
823
911
|
dispatchResp.estimated_start = estimatedStart;
|
|
824
912
|
return c.json(dispatchResp, 202);
|
|
@@ -947,6 +1035,7 @@ export async function startServer(opts) {
|
|
|
947
1035
|
const result = await handleWebhook(configId, bodyBuf, signature, eventId, {
|
|
948
1036
|
registry: pushSecretRegistry,
|
|
949
1037
|
idempotency: webhookIdempotency,
|
|
1038
|
+
contentType: c.req.header('content-type') ?? undefined,
|
|
950
1039
|
route: async (entry, event) => {
|
|
951
1040
|
// Append to mission recentEvents via a synthesized envelope.
|
|
952
1041
|
// The 'mission.webhook' event type falls through the registry's
|
|
@@ -990,11 +1079,17 @@ export async function startServer(opts) {
|
|
|
990
1079
|
if (!p.secret || typeof p.secret !== 'string' || p.secret.length < 16) {
|
|
991
1080
|
return c.json({ error: 'secret is required and must be ≥16 chars' }, 400);
|
|
992
1081
|
}
|
|
1082
|
+
if (p.protocolVersion === '1.0' && !p.taskId) {
|
|
1083
|
+
return c.json({ error: 'taskId is required for A2A 1.0 push ownership scope' }, 400);
|
|
1084
|
+
}
|
|
993
1085
|
pushSecretRegistry.register({
|
|
994
1086
|
configId: p.configId,
|
|
995
1087
|
secret: p.secret,
|
|
996
1088
|
...(p.missionId ? { missionId: p.missionId } : {}),
|
|
997
1089
|
...(p.taskId ? { taskId: p.taskId } : {}),
|
|
1090
|
+
...(p.contextId ? { contextId: p.contextId } : {}),
|
|
1091
|
+
...(p.protocolVersion ? { protocolVersion: p.protocolVersion } : {}),
|
|
1092
|
+
...(p.taskOwner ? { taskOwner: p.taskOwner } : {}),
|
|
998
1093
|
...(p.metadata ? { metadata: p.metadata } : {}),
|
|
999
1094
|
});
|
|
1000
1095
|
return c.json({ ok: true, configId: p.configId }, 201);
|
|
@@ -1749,10 +1844,18 @@ export const serveHandler = {
|
|
|
1749
1844
|
category: 'project',
|
|
1750
1845
|
aliases: [],
|
|
1751
1846
|
async execute(ctx) {
|
|
1752
|
-
const { port, host, open, readOnly } = parseServeArgs(ctx.args);
|
|
1847
|
+
const { port, host, open, readOnly, a2aProtocolPolicy, allowA2AProtocolFallback, allowLegacyExecutorFallback, } = parseServeArgs(ctx.args);
|
|
1753
1848
|
let server;
|
|
1754
1849
|
try {
|
|
1755
|
-
server = await startServer({
|
|
1850
|
+
server = await startServer({
|
|
1851
|
+
port,
|
|
1852
|
+
host,
|
|
1853
|
+
readOnly,
|
|
1854
|
+
frameworkRoot: ctx.frameworkRoot,
|
|
1855
|
+
...(a2aProtocolPolicy ? { a2aProtocolPolicy } : {}),
|
|
1856
|
+
...(allowA2AProtocolFallback !== undefined ? { allowA2AProtocolFallback } : {}),
|
|
1857
|
+
...(allowLegacyExecutorFallback !== undefined ? { allowLegacyExecutorFallback } : {}),
|
|
1858
|
+
});
|
|
1756
1859
|
}
|
|
1757
1860
|
catch (error) {
|
|
1758
1861
|
const { handlerResultFromError } = await import('../errors.js');
|