@aiwg/cli 2026.10.9 → 2026.10.10
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/model-capabilities.v1.json +197 -3
- package/bin/aiwg.mjs +8 -0
- package/dist/src/cli/handlers/mc-admission.js +39 -0
- package/dist/src/cli/handlers/mc.js +2 -31
- package/dist/src/cli/handlers/models-help.js +52 -0
- package/dist/src/cli/handlers/models.js +11 -81
- package/dist/src/cli/handlers/use-addon-profile.js +72 -0
- package/dist/src/cli/handlers/use.js +191 -230
- package/dist/src/cli/ui.js +1 -0
- package/dist/src/config/aiwg-config.js +38 -184
- package/dist/src/config/cli-help.js +54 -0
- package/dist/src/config/cli.js +5 -69
- package/dist/src/config/config-normalization.js +10 -0
- package/dist/src/config/worker-activation-gate.js +26 -0
- package/dist/src/config/worker-adoption-cli.js +102 -0
- package/dist/src/config/worker-adoption.js +368 -0
- package/dist/src/config/worker-config-digest.js +14 -0
- package/dist/src/config/worker-group-templates.js +150 -0
- package/dist/src/config/worker-guidance-warning.js +28 -0
- package/dist/src/config/worker-owner-lock.js +152 -0
- package/dist/src/config/worker-parallelism.js +154 -0
- package/dist/src/config/worker-write.js +75 -0
- package/dist/src/decision/telemetry/exporter.js +2 -1
- package/dist/src/decision/telemetry/index.js +1 -0
- package/dist/src/decision/telemetry/mapping.js +43 -0
- package/dist/src/decision/telemetry/otlp-http.js +5 -2
- package/dist/src/decision/telemetry/redaction.js +6 -1
- package/dist/src/decision/telemetry/types.js +3 -0
- package/dist/src/decision/telemetry/worker.js +69 -0
- package/dist/src/lint/rule-activation.js +15 -7
- package/dist/src/lint/runner.js +2 -2
- package/dist/src/mcp/server.mjs +2 -11
- package/dist/src/mcp/stdio-lifecycle.mjs +27 -0
- package/dist/src/mcp/tools/worker-failure-diagnostics.mjs +9 -0
- package/dist/src/mcp/tools/worker-invoke.mjs +6 -6
- package/dist/src/mission-protocol/codecs.js +8 -38
- package/dist/src/mission-protocol/worker-contract-diagnostics.js +62 -0
- package/dist/src/mission-protocol/worker-invocation-types.js +2 -0
- package/dist/src/models/conductor-data-policy.d.mts +8 -0
- package/dist/src/models/conductor-data-policy.mjs +190 -0
- package/dist/src/models/config-loader.js +19 -3
- package/dist/src/models/config-writer.js +27 -0
- package/dist/src/models/model-capabilities.v1.json +197 -3
- package/dist/src/models/provider-policy.js +7 -155
- package/dist/src/models/resolver.js +2 -293
- package/dist/src/models/worker-artifact-binding.mjs +12 -0
- package/dist/src/models/worker-direct-readiness-policy.js +155 -0
- package/dist/src/models/worker-direct-readiness.js +9 -218
- package/dist/src/models/worker-health-readiness.js +105 -26
- package/dist/src/models/worker-installation.mjs +12 -14
- package/dist/src/models/worker-member-references.js +64 -0
- package/dist/src/models/worker-plan.js +317 -0
- package/dist/src/models/worker-selection-policy.js +217 -0
- package/dist/src/models/wrapper-deployment.js +1 -1
- package/dist/src/serve/shared-host-admission-checks.js +116 -0
- package/dist/src/serve/shared-host-scheduler.js +25 -107
- package/dist/src/serve/shared-host-starvation.js +73 -0
- package/dist/src/serve/worker-artifacts.d.mts +4 -0
- package/dist/src/serve/worker-artifacts.mjs +56 -0
- package/dist/src/serve/worker-command-request.js +68 -0
- package/dist/src/serve/worker-invocation-paths.js +144 -0
- package/dist/src/serve/worker-invocation-preparation.js +169 -0
- package/dist/src/serve/worker-invocation.js +24 -291
- package/dist/src/serve/worker-stdin-frame.d.mts +2 -2
- package/dist/src/serve/worker-stdin-frame.mjs +6 -6
- package/dist/src/smiths/context-pipeline/parallelism-section.js +19 -7
- package/package.json +9 -1
- package/schemas/mission-v1.schema.json +20 -19
- package/schemas/models/model-config-compatibility.v1.schema.json +16 -0
- package/tools/agents/providers/claude-post-deploy.mjs +35 -0
- package/tools/agents/providers/claude.mjs +3 -30
- package/tools/agents/providers/codex-post-deploy.mjs +13 -0
- package/tools/agents/providers/codex.mjs +2 -8
- package/tools/agents/providers/conductor-data-guard.mjs +61 -0
- package/tools/agents/providers/rule-delivery.mjs +3 -16
- package/tools/agents/providers/rule-presentation.mjs +38 -0
- package/tools/plugin/package-plugins.mjs +1 -1
- package/tools/ralph-external/lib/claude-adapter.mjs +2 -2
- package/tools/ralph-external/lib/codex-adapter.mjs +3 -3
- package/tools/ralph-external/lib/managed-worker-attempt.mjs +86 -0
- package/tools/ralph-external/lib/provider-adapter.mjs +11 -260
- package/tools/ralph-external/lib/session-attempt.mjs +85 -0
- package/tools/ralph-external/lib/strict-worker-json.mjs +190 -0
- package/tools/ralph-external/lib/worker-artifacts.mjs +209 -0
- package/tools/ralph-external/lib/worker-final.mjs +143 -0
- package/tools/ralph-external/lib/worker-inputs.mjs +55 -0
- package/tools/ralph-external/lib/worker-isolation.mjs +2 -44
- package/tools/ralph-external/lib/worker-launch-cli.mjs +47 -0
- package/tools/ralph-external/lib/worker-native-sandbox.mjs +83 -0
- package/tools/ralph-external/session-launcher.mjs +73 -217
- package/vscode-extension/schemas/aiwg.config.v1.json +161 -2
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Apply project settings and context after provider file deployment.
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { initializeFrameworkWorkspace, ensureDir } from './base.mjs';
|
|
5
|
+
import { deployHookFile } from './claude.mjs';
|
|
6
|
+
import { deployConductorDataGuard } from './conductor-data-guard.mjs';
|
|
7
|
+
|
|
8
|
+
export async function postDeploy(targetDir, opts) {
|
|
9
|
+
initializeFrameworkWorkspace(targetDir, opts.mode, opts.dryRun, opts.srcRoot);
|
|
10
|
+
await deployConductorDataGuard(targetDir, 'claude', opts);
|
|
11
|
+
|
|
12
|
+
// Claude-specific post-deployment (settings.json, etc.)
|
|
13
|
+
const claudeDir = path.join(targetDir, '.claude');
|
|
14
|
+
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
15
|
+
|
|
16
|
+
// If settings.json doesn't exist, create a minimal one
|
|
17
|
+
if (!fs.existsSync(settingsPath) && !opts.dryRun) {
|
|
18
|
+
ensureDir(claudeDir);
|
|
19
|
+
const settings = {
|
|
20
|
+
version: '1.0',
|
|
21
|
+
created: new Date().toISOString(),
|
|
22
|
+
aiwg: {
|
|
23
|
+
enabled: true,
|
|
24
|
+
mode: opts.mode || 'all'
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
|
|
28
|
+
console.log('Created .claude/settings.json');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Hook file architecture: write AIWG.md and wire @AIWG.md into CLAUDE.md
|
|
32
|
+
if (opts.srcRoot && opts.contextFiles !== false) {
|
|
33
|
+
deployHookFile(targetDir, opts);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { postDeploy } from './claude-post-deploy.mjs';
|
|
2
|
+
export { postDeploy };
|
|
1
3
|
import { importImpl } from '../../_resolve-impl.mjs';
|
|
2
4
|
/**
|
|
3
5
|
* Claude Code Provider
|
|
@@ -43,7 +45,6 @@ import {
|
|
|
43
45
|
pruneStaleAiwgSkills,
|
|
44
46
|
computeAllKernelNames,
|
|
45
47
|
parseFrontmatter,
|
|
46
|
-
initializeFrameworkWorkspace,
|
|
47
48
|
filterAgentFiles,
|
|
48
49
|
getAddonAgentFiles,
|
|
49
50
|
getAddonCommandFiles,
|
|
@@ -395,34 +396,6 @@ export function createAgentsMd(target, srcRoot, dryRun) {
|
|
|
395
396
|
// Post-Deployment
|
|
396
397
|
// ============================================================================
|
|
397
398
|
|
|
398
|
-
export async function postDeploy(targetDir, opts) {
|
|
399
|
-
// Initialize framework workspace structure
|
|
400
|
-
initializeFrameworkWorkspace(targetDir, opts.mode, opts.dryRun, opts.srcRoot);
|
|
401
|
-
|
|
402
|
-
// Claude-specific post-deployment (settings.json, etc.)
|
|
403
|
-
const claudeDir = path.join(targetDir, '.claude');
|
|
404
|
-
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
405
|
-
|
|
406
|
-
// If settings.json doesn't exist, create a minimal one
|
|
407
|
-
if (!fs.existsSync(settingsPath) && !opts.dryRun) {
|
|
408
|
-
ensureDir(claudeDir);
|
|
409
|
-
const settings = {
|
|
410
|
-
version: '1.0',
|
|
411
|
-
created: new Date().toISOString(),
|
|
412
|
-
aiwg: {
|
|
413
|
-
enabled: true,
|
|
414
|
-
mode: opts.mode || 'all'
|
|
415
|
-
}
|
|
416
|
-
};
|
|
417
|
-
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
|
|
418
|
-
console.log('Created .claude/settings.json');
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// Hook file architecture: write AIWG.md and wire @AIWG.md into CLAUDE.md
|
|
422
|
-
if (opts.srcRoot && opts.contextFiles !== false) {
|
|
423
|
-
deployHookFile(targetDir, opts);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
399
|
|
|
427
400
|
/**
|
|
428
401
|
* Deploy the AIWG.md hook file and add @AIWG.md directive to CLAUDE.md.
|
|
@@ -444,7 +417,7 @@ function interpolateHookTokens(content, counts, targetDir) {
|
|
|
444
417
|
});
|
|
445
418
|
}
|
|
446
419
|
|
|
447
|
-
function deployHookFile(targetDir, opts) {
|
|
420
|
+
export function deployHookFile(targetDir, opts) {
|
|
448
421
|
const { srcRoot, dryRun, counts } = opts;
|
|
449
422
|
const templatePath = path.join(srcRoot, 'agentic', 'code', 'frameworks', 'sdlc-complete', 'templates', 'project', 'AIWG.md');
|
|
450
423
|
const hookDest = path.join(targetDir, 'AIWG.md');
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Apply project settings and context after provider file deployment.
|
|
2
|
+
import { initializeFrameworkWorkspace } from './base.mjs';
|
|
3
|
+
import { createAgentsMd } from './codex.mjs';
|
|
4
|
+
import { deployConductorDataGuard } from './conductor-data-guard.mjs';
|
|
5
|
+
|
|
6
|
+
export async function postDeploy(targetDir, opts) {
|
|
7
|
+
initializeFrameworkWorkspace(targetDir, opts.mode, opts.dryRun, opts.srcRoot);
|
|
8
|
+
await deployConductorDataGuard(targetDir, 'codex', opts);
|
|
9
|
+
|
|
10
|
+
if (opts.createAgentsMd) {
|
|
11
|
+
if (opts.contextFiles !== false) createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { postDeploy } from './codex-post-deploy.mjs';
|
|
2
|
+
export { postDeploy };
|
|
1
3
|
import { importImpl } from '../../_resolve-impl.mjs';
|
|
2
4
|
/**
|
|
3
5
|
* OpenAI Codex Provider
|
|
@@ -47,7 +49,6 @@ import {
|
|
|
47
49
|
writeFile,
|
|
48
50
|
deployFiles,
|
|
49
51
|
createAgentsMdFromTemplate,
|
|
50
|
-
initializeFrameworkWorkspace,
|
|
51
52
|
getAddonAgentFiles,
|
|
52
53
|
getAddonCommandFiles,
|
|
53
54
|
getAddonSkillDirs,
|
|
@@ -511,13 +512,6 @@ export function generatePluginBundle(targetDir, opts = {}) {
|
|
|
511
512
|
// Post-Deployment
|
|
512
513
|
// ============================================================================
|
|
513
514
|
|
|
514
|
-
export async function postDeploy(targetDir, opts) {
|
|
515
|
-
initializeFrameworkWorkspace(targetDir, opts.mode, opts.dryRun, opts.srcRoot);
|
|
516
|
-
|
|
517
|
-
if (opts.createAgentsMd) {
|
|
518
|
-
if (opts.contextFiles !== false) createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
515
|
|
|
522
516
|
// ============================================================================
|
|
523
517
|
// File Extension
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Compile the existing project data policy into project-local provider refusals.
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { importImpl, resolveImpl } from '../../_resolve-impl.mjs';
|
|
6
|
+
|
|
7
|
+
const quote = value => `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
8
|
+
|
|
9
|
+
// Local-only paths are declared only in project/user models.json or project
|
|
10
|
+
// parallelism (the built-in defaults declare none). Skip the full model-config
|
|
11
|
+
// load when none of them mention the field, so ordinary deploys never depend
|
|
12
|
+
// on the framework default config (absent from the slim CLI package).
|
|
13
|
+
function declaresLocalOnlyPaths(targetDir) {
|
|
14
|
+
const userDir = process.env.AIWG_CONFIG || path.join(os.homedir(), '.config/aiwg');
|
|
15
|
+
return [path.join(targetDir, 'models.json'), path.join(userDir, 'models.json'), path.join(targetDir, '.aiwg/aiwg.config')]
|
|
16
|
+
.some(file => { try { return fs.readFileSync(file, 'utf8').includes('localOnlyPaths'); } catch { return false; } });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function deployConductorDataGuard(targetDir, provider, opts = {}) {
|
|
20
|
+
if (!declaresLocalOnlyPaths(targetDir)) return [];
|
|
21
|
+
// Always resolve policy through the existing loader, including inherited restrictions.
|
|
22
|
+
const { ConfigLoader } = await importImpl(import.meta.url, 'models/config-loader.js');
|
|
23
|
+
const { readAiwgConfig } = await importImpl(import.meta.url, 'config/aiwg-config.js');
|
|
24
|
+
const guard = await importImpl(import.meta.url, 'models/conductor-data-policy.mjs');
|
|
25
|
+
const config = await readAiwgConfig(targetDir);
|
|
26
|
+
const models = await new ConfigLoader(targetDir).load(config?.parallelism);
|
|
27
|
+
const paths = guard.collectLocalOnlyPaths(models.defaults.workerSelection, config?.parallelism);
|
|
28
|
+
if (!paths.length || opts.dryRun) return paths;
|
|
29
|
+
const root = path.resolve(targetDir);
|
|
30
|
+
if (provider === 'claude') {
|
|
31
|
+
const settingsPath = path.join(root, '.claude/settings.json');
|
|
32
|
+
const settings = fs.existsSync(settingsPath) ? JSON.parse(fs.readFileSync(settingsPath, 'utf8')) : {};
|
|
33
|
+
const hookPath = path.join(root, '.claude/hooks/aiwg-local-only.mjs');
|
|
34
|
+
const hookSource = fs.readFileSync(resolveImpl(import.meta.url, 'models/conductor-data-policy.mjs'), 'utf8');
|
|
35
|
+
fs.mkdirSync(path.dirname(hookPath), { recursive: true });
|
|
36
|
+
fs.writeFileSync(hookPath, `${hookSource}\nrunConductorDataHook(${JSON.stringify(root)}, ${JSON.stringify(paths)});\n`);
|
|
37
|
+
settings.permissions ??= {};
|
|
38
|
+
settings.permissions.deny = [...new Set([...(settings.permissions.deny ?? []), ...paths.flatMap(file => [
|
|
39
|
+
`Read(//${path.join(root, file).slice(1)})`, `Read(//${path.join(root, file).slice(1)}/**)`,
|
|
40
|
+
])])];
|
|
41
|
+
settings.hooks ??= {};
|
|
42
|
+
const command = `node ${quote(hookPath)}`;
|
|
43
|
+
const entries = settings.hooks.PreToolUse ?? [];
|
|
44
|
+
settings.hooks.PreToolUse = [...entries.filter(entry => !entry.hooks?.some(hook => hook.command === command)),
|
|
45
|
+
{ matcher: '.*', hooks: [{ type: 'command', command }] }];
|
|
46
|
+
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
|
|
47
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
48
|
+
} else if (provider === 'codex') {
|
|
49
|
+
const rulesPath = path.join(root, '.codex/rules/aiwg-local-only.rules');
|
|
50
|
+
const lines = ['# AIWG local-only inputs: explicit command prefixes only; not filesystem isolation.'];
|
|
51
|
+
for (const file of paths) for (const spelling of [file, `./${file}`, path.join(root, file)]) {
|
|
52
|
+
for (const command of ['cat', 'head', 'tail', 'less', 'more', 'sed', 'awk', 'grep', 'rg']) {
|
|
53
|
+
lines.push(`prefix_rule(pattern = ${JSON.stringify([command, spelling])}, decision = "forbidden")`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
fs.mkdirSync(path.dirname(rulesPath), { recursive: true });
|
|
57
|
+
fs.writeFileSync(rulesPath, lines.join('\n') + '\n');
|
|
58
|
+
}
|
|
59
|
+
console.log(`Local-only conductor guard (${provider}): ${paths.join(', ')}; shell coverage is limited.`);
|
|
60
|
+
return paths;
|
|
61
|
+
}
|
|
@@ -20,6 +20,8 @@ const { guardRulesInlineBudget } = await importImpl(import.meta.url, 'rules/inli
|
|
|
20
20
|
const { userConfigFile } = await importImpl(import.meta.url, 'config/user-config-dir.mjs');
|
|
21
21
|
const { resolveProjectAiwgDir } = await importImpl(import.meta.url, 'config/project-artifacts-runtime.mjs');
|
|
22
22
|
import { writeContextFileSync, CONTEXT_STAGE_ENV } from './context-write.mjs';
|
|
23
|
+
import { projectOnDemandReferences, renderRuleKernelIndex } from './rule-presentation.mjs';
|
|
24
|
+
export { renderRuleKernelIndex } from './rule-presentation.mjs';
|
|
23
25
|
const PREPARATION_HOOK = Symbol.for('aiwg.provider-preparation-hook.v1');
|
|
24
26
|
|
|
25
27
|
export function installRuleDeploymentPreparationHook(hook) {
|
|
@@ -298,21 +300,6 @@ export async function prepareRuleDelivery(opts) {
|
|
|
298
300
|
return { rules, bindings, targets, pins: pinned.rules, pinWarnings: pinned.warnings };
|
|
299
301
|
}
|
|
300
302
|
|
|
301
|
-
export function renderRuleKernelIndex(rules, nativePaths = false) {
|
|
302
|
-
const summary = rules.filter(rule => rule.tagged && (rule.canonicalActivation || rule.activation) === 'summary');
|
|
303
|
-
const paths = rules.filter(rule => rule.tagged && (rule.canonicalActivation || rule.activation) === 'paths');
|
|
304
|
-
const kernels = rules.filter(rule => rule.tagged && (rule.canonicalActivation || rule.activation) === 'kernel');
|
|
305
|
-
if (!summary.length && !paths.length && !kernels.length) return '';
|
|
306
|
-
const lines = ['# Rule kernel index', ''];
|
|
307
|
-
for (const rule of kernels) lines.push(`- ${rule.name} — ${rule.summary || 'Fetch and follow the full rule before governed work.'} Fetch and follow: \`aiwg show rule ${rule.name} --body\`.`, '');
|
|
308
|
-
for (const rule of summary) lines.push(`- ${rule.name} — ${rule.summary} Fetch and follow: \`aiwg show rule ${rule.name} --body\`.`, '');
|
|
309
|
-
if (paths.length) {
|
|
310
|
-
lines.push(nativePaths ? '## Path rules (native)' : '## Path rules (prompt emulation)', '',
|
|
311
|
-
nativePaths ? 'The provider loads these rules for matching paths; full contracts remain available through the commands below.' : 'Before reading or changing files, compare intended project-relative paths with the globs below and fetch matching rules before their first read/write. If paths are initially unknown, check when they become known. An agent-only task has no path match. This is prompt emulation, not a filesystem hook.', '');
|
|
312
|
-
for (const rule of paths) lines.push(`- ${rule.name} | ${rule.paths.map(glob => `\`${glob}\``).join(', ')} | ${rule.summary} | \`aiwg show rule ${rule.name} --body\``, '');
|
|
313
|
-
}
|
|
314
|
-
return lines.join('\n');
|
|
315
|
-
}
|
|
316
303
|
|
|
317
304
|
function managed(content, opts) {
|
|
318
305
|
return addManagedMarker(content, opts.deployVersion, opts.deploySource || 'bundled');
|
|
@@ -334,7 +321,7 @@ function putStartup(file, body) {
|
|
|
334
321
|
|
|
335
322
|
/** Tagged rules bypass legacy aggregate writers. Untagged projections retain their tier policy. */
|
|
336
323
|
function projectRules(opts, provider) {
|
|
337
|
-
const
|
|
324
|
+
const rules = projectOnDemandReferences(opts.ruleDelivery.rules, ['claude', 'cursor'].includes(provider.name));
|
|
338
325
|
if (opts.contextFiles !== false && provider.name === 'hermes') {
|
|
339
326
|
// The old context generator carries hardcoded corpus directives. Only the
|
|
340
327
|
// selected contracts below may activate policy; preserve operator prose.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** Render deployed rule references without links to catalog-only siblings. */
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
export function projectOnDemandReferences(rules, nativePaths = false) {
|
|
5
|
+
const byFile = new Map(rules.map(rule => [path.resolve(rule.file), rule]));
|
|
6
|
+
return rules.map(rule => {
|
|
7
|
+
const rewrite = content => content?.replace(
|
|
8
|
+
/\[[^\]\n]+\]\((?:\.\/)?([\w-]+)\.md\)[ \t]*\(`aiwg show rule \1(?: --body)?`, on-demand\)/g,
|
|
9
|
+
(reference, name) => {
|
|
10
|
+
const sibling = byFile.get(path.resolve(path.dirname(rule.file), `${name}.md`));
|
|
11
|
+
// Only known catalog rules can replace a relative file reference. Keep
|
|
12
|
+
// unknown/broken links and links to files selected for native delivery.
|
|
13
|
+
if (!sibling?.tagged || sibling.name !== name || sibling.activation === 'kernel' ||
|
|
14
|
+
(nativePaths && sibling.activation === 'paths')) return reference;
|
|
15
|
+
return `\`aiwg show rule ${name} --body\` (on-demand)`;
|
|
16
|
+
},
|
|
17
|
+
);
|
|
18
|
+
return { ...rule, content: rewrite(rule.content), body: rewrite(rule.body),
|
|
19
|
+
...(rule.coreBody === undefined ? {} : { coreBody: rewrite(rule.coreBody) }) };
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function renderRuleKernelIndex(rules, nativePaths = false) {
|
|
24
|
+
const summary = rules.filter(rule => rule.tagged && (rule.canonicalActivation || rule.activation) === 'summary');
|
|
25
|
+
const paths = rules.filter(rule => rule.tagged && (rule.canonicalActivation || rule.activation) === 'paths');
|
|
26
|
+
const kernels = rules.filter(rule => rule.tagged && (rule.canonicalActivation || rule.activation) === 'kernel');
|
|
27
|
+
if (!summary.length && !paths.length && !kernels.length) return '';
|
|
28
|
+
const lines = ['# Rule kernel index', ''];
|
|
29
|
+
for (const rule of kernels) lines.push(`- ${rule.name} — ${rule.summary || 'Fetch and follow the full rule before governed work.'} Fetch and follow: \`aiwg show rule ${rule.name} --body\`.`, '');
|
|
30
|
+
for (const rule of summary) lines.push(`- ${rule.name} — ${rule.summary} Fetch and follow: \`aiwg show rule ${rule.name} --body\`.`, '');
|
|
31
|
+
if (paths.length) {
|
|
32
|
+
lines.push(nativePaths ? '## Path rules (native)' : '## Path rules (prompt emulation)', '',
|
|
33
|
+
nativePaths ? 'The provider loads these rules for matching paths; full contracts remain available through the commands below.' : 'Before reading or changing files, compare intended project-relative paths with the globs below and fetch matching rules before their first read/write. If paths are initially unknown, check when they become known. An agent-only task has no path match. This is prompt emulation, not a filesystem hook.', '');
|
|
34
|
+
for (const rule of paths) lines.push(`- ${rule.name} | ${rule.paths.map(glob => `\`${glob}\``).join(', ')} | ${rule.summary} | \`aiwg show rule ${rule.name} --body\``, '');
|
|
35
|
+
}
|
|
36
|
+
return lines.join('\n');
|
|
37
|
+
}
|
|
38
|
+
|
|
@@ -41,7 +41,7 @@ const PLUGIN_CONFIGS = {
|
|
|
41
41
|
skills: 'agentic/code/frameworks/sdlc-complete/skills'
|
|
42
42
|
},
|
|
43
43
|
extraCopy: [
|
|
44
|
-
|
|
44
|
+
...['threat-assessment.mjs', 'threat-assessment'].map(name => ({ from: `tools/security/${name}`, to: `tools/security/${name}` })),
|
|
45
45
|
{ from: 'agentic/code/frameworks/sdlc-complete/flows/flow-architecture-evolution.playbook.yaml', to: 'flows/flow-architecture-evolution.playbook.yaml' },
|
|
46
46
|
{ from: 'agentic/code/frameworks/sdlc-complete/flows/flow-release.playbook.yaml', to: 'flows/flow-release.playbook.yaml' }
|
|
47
47
|
],
|
|
@@ -212,8 +212,8 @@ export class ClaudeAdapter extends ProviderAdapter {
|
|
|
212
212
|
];
|
|
213
213
|
|
|
214
214
|
// Session tracking
|
|
215
|
-
if (options.sessionId) {
|
|
216
|
-
args.push('--session-id', options.sessionId);
|
|
215
|
+
if (options.resumeSession || options.sessionId) {
|
|
216
|
+
args.push(options.resumeSession ? '--resume' : '--session-id', options.resumeSession || options.sessionId);
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
// Model selection (Claude passes through directly)
|
|
@@ -44,7 +44,7 @@ const MODEL_MAP = {
|
|
|
44
44
|
*
|
|
45
45
|
* codex --no-daemon exec --ignore-user-config -m M \
|
|
46
46
|
* -c model_reasoning_effort="E" -s S -C W -c approval_policy="never" \
|
|
47
|
-
* --skip-git-repo-check -o F --json - < briefPath
|
|
47
|
+
* --skip-git-repo-check -o F.last-message --json - < briefPath
|
|
48
48
|
*
|
|
49
49
|
* `--no-daemon` is a root-level flag and `--ignore-user-config` is an
|
|
50
50
|
* `exec`-level flag (root `--help` lists no `--ignore-user-config`;
|
|
@@ -206,7 +206,7 @@ export class CodexAdapter extends ProviderAdapter {
|
|
|
206
206
|
* model, explicit effort as one `-c model_reasoning_effort="E"` token
|
|
207
207
|
* (TOML `"E"` quoting via `JSON.stringify`; there is no shell in argv),
|
|
208
208
|
* explicit sandbox/approval (`never` only), the conductor workdir as
|
|
209
|
-
* `-C`,
|
|
209
|
+
* `-C`, a separate last-message capture as `-o`, the JSONL stream as `--json` and
|
|
210
210
|
* `-` as the prompt argument so the brief arrives on stdin
|
|
211
211
|
* (`stdin: 'brief-text'`). Permission flags from the legacy Ralph loop
|
|
212
212
|
* (`--dangerously-bypass-approvals-and-sandbox`), guessed step caps and
|
|
@@ -260,7 +260,7 @@ export class CodexAdapter extends ProviderAdapter {
|
|
|
260
260
|
'-C', cwd,
|
|
261
261
|
'-c', `approval_policy=${JSON.stringify(approval)}`,
|
|
262
262
|
'--skip-git-repo-check',
|
|
263
|
-
'-o', finalPath
|
|
263
|
+
'-o', `${finalPath}.last-message`,
|
|
264
264
|
'--json',
|
|
265
265
|
'-',
|
|
266
266
|
];
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Validate the parent reservation and disclose one-shot attempt bindings.
|
|
2
|
+
import { importImpl } from '../../_resolve-impl.mjs';
|
|
3
|
+
import { sampleWorkerIdentity } from '../process-monitor.mjs';
|
|
4
|
+
import { changedBindings } from './session-attempt.mjs';
|
|
5
|
+
const { qualificationDigest } = await importImpl(import.meta.url, 'models/qualification-digest.mjs');
|
|
6
|
+
|
|
7
|
+
export function managedAttemptBindings(plan) {
|
|
8
|
+
const invocation = plan.invocation;
|
|
9
|
+
const digests = plan.digests ?? {};
|
|
10
|
+
const selected = invocation?.selected ?? plan.selected;
|
|
11
|
+
return { policyDigest: invocation?.policyDigest ?? (digests.configDigest && digests.resolvedModelsDigest
|
|
12
|
+
? qualificationDigest([digests.configDigest, digests.resolvedModelsDigest]) : null),
|
|
13
|
+
catalogDigest: invocation?.catalogDigest ?? digests.catalogDigest ?? null,
|
|
14
|
+
profileDigest: invocation?.profileDigest ?? digests.profileDigest ?? null,
|
|
15
|
+
selected: selected ? { group: selected.group, member: selected.member, cli: selected.cli,
|
|
16
|
+
inferenceProvider: selected.inferenceProvider, model: selected.model,
|
|
17
|
+
effort: typeof selected.effort === 'object' ? selected.effort?.value ?? null : selected.effort ?? null,
|
|
18
|
+
profileRef: selected.profileRef ?? selected.profile } : null, sessionId: null };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function validateManagedAttempt(options, saved, inputs, now, validate, publish) {
|
|
22
|
+
const attempt = await managedWorkerAttempt(options, saved, inputs, now);
|
|
23
|
+
await publish(attempt);
|
|
24
|
+
try {
|
|
25
|
+
const invocation = await validate(saved, inputs.snapshot, inputs.drivers, now);
|
|
26
|
+
return { invocation, attempt };
|
|
27
|
+
} catch (error) {
|
|
28
|
+
attempt.status = 'blocked'; attempt.errorCode = error.code ?? 'PLAN_REVALIDATION_FAILED';
|
|
29
|
+
await publish(attempt);
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function managedWorkerAttempt(options, saved, inputs, now) {
|
|
35
|
+
const { resolveWorkerInvocation } = await importImpl(import.meta.url, 'serve/worker-invocation.js');
|
|
36
|
+
const current = await resolveWorkerInvocation(inputs.snapshot, saved.request, inputs.drivers, now);
|
|
37
|
+
const original = options.attempt?.originalBindings ?? managedAttemptBindings(saved);
|
|
38
|
+
const bindings = managedAttemptBindings(current);
|
|
39
|
+
return { ...options.attempt, kind: 'fresh', reason: options.attempt?.reason
|
|
40
|
+
?? (options.resumeSession || options.sessionId ? 'one-shot-continuation-unsupported' : 'one-shot-profile'),
|
|
41
|
+
requestedSessionId: options.attempt?.requestedSessionId ?? options.resumeSession ?? options.sessionId ?? null,
|
|
42
|
+
requestId: options.managedAdmission.requestId, originalBindings: original,
|
|
43
|
+
bindings, changes: changedBindings(original, bindings),
|
|
44
|
+
status: current.status === 'eligible' ? 'prepared' : 'blocked',
|
|
45
|
+
diagnostics: current.diagnostics };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Reuse the MC authority and its registered one-shot identity at every spawn boundary. */
|
|
49
|
+
export async function assertManagedWorkerLaunch(options, invocation, inputs, projectRoot = process.cwd()) {
|
|
50
|
+
if (!options.managedAdmission || invocation.mode !== 'managed'
|
|
51
|
+
|| inputs.snapshot.config.parallelism?.worker_mode !== 'managed') throw new Error('Managed worker admission is required');
|
|
52
|
+
const { createMcAdmissionScheduler } = await importImpl(import.meta.url, 'cli/handlers/mc.js');
|
|
53
|
+
const { loadLauncherRegistry } = await importImpl(import.meta.url, 'cli/handlers/ralph-launcher.js');
|
|
54
|
+
const { workerModelKey } = await importImpl(import.meta.url, 'models/provider-policy.js');
|
|
55
|
+
const scheduler = await createMcAdmissionScheduler(projectRoot, inputs.snapshot.config);
|
|
56
|
+
const { requestId, generation } = options.managedAdmission;
|
|
57
|
+
const snapshot = scheduler.snapshot();
|
|
58
|
+
const record = snapshot.records[requestId];
|
|
59
|
+
const deadlineAtMs = Math.min(options.cycleDeadlineAtMs ?? Infinity, ...[record?.accounting?.runKey, record?.accounting?.taskKey]
|
|
60
|
+
.map(key => snapshot.allowances?.[key]?.deadlineAt).filter(Boolean).map(Date.parse));
|
|
61
|
+
const registered = loadLauncherRegistry(projectRoot).loops[options.loopId ?? process.env.RALPH_LOOP_ID];
|
|
62
|
+
const worker = record?.managed;
|
|
63
|
+
const reservation = record?.accounting?.reservation;
|
|
64
|
+
if (!record || record.state !== 'admitted' || record.leaseGeneration !== generation
|
|
65
|
+
|| !record.launchClaimedAt || !record.metadata?.invocationId || Date.parse(record.leaseExpiresAt ?? '') <= Date.now()
|
|
66
|
+
|| deadlineAtMs <= Date.now()
|
|
67
|
+
|| worker?.kind !== 'external' || worker.group !== invocation.selected.group
|
|
68
|
+
|| worker.member !== invocation.selected.member || worker.cli !== invocation.selected.cli
|
|
69
|
+
|| worker.model !== workerModelKey(invocation.selected.inferenceProvider, invocation.selected.model)
|
|
70
|
+
|| reservation?.tokens != null && !(invocation.limits.tokens <= reservation.tokens)
|
|
71
|
+
|| reservation?.costUsd != null && !(invocation.limits.costUsd !== null && invocation.limits.costUsd <= reservation.costUsd)
|
|
72
|
+
|| registered?.managedAdmission?.requestId !== requestId || registered.managedAdmission.generation !== generation
|
|
73
|
+
|| registered.managedWorker?.invocationPath !== options.invocationPath
|
|
74
|
+
|| registered.managedWorker?.runDir !== options.runDir) {
|
|
75
|
+
throw new Error('Managed worker launch differs from the current claimed admission');
|
|
76
|
+
}
|
|
77
|
+
if (record.execution) {
|
|
78
|
+
const identity = sampleWorkerIdentity(process.pid);
|
|
79
|
+
if (record.execution.pid !== process.pid || !identity?.bootId || !identity.startTicks
|
|
80
|
+
|| record.execution.startedAt !== `${identity.bootId}:${identity.startTicks}:${identity.uid}`) {
|
|
81
|
+
throw new Error('Managed worker supervisor identity differs');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return { ...record, deadlineAtMs };
|
|
85
|
+
}
|
|
86
|
+
|