amsd-pipeline-shared 0.5.45 → 0.5.46
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.
|
@@ -44,13 +44,32 @@ function renderPrompt(vars, profile) {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/** The command, prepared and not started: the CLI in print mode on the profile's family, in the work tree, memory-bounded when the host has the wrapper. */
|
|
47
|
+
/** THE RUNNER'S OWN ISOLATION FOR THE DOCTOR'S CALL (2026-10-25: the doctor's session loaded the host's MCP servers and skills — it is an
|
|
48
|
+
* agent like every other and gets the same declaration). The claude runner's alwaysFlags and env from the stack's declaration the
|
|
49
|
+
* profile names (config/llm-defaults.<set>.json runners.claude), read as the hub's resolver reads them; none when the file has none. */
|
|
50
|
+
function runnerIsolation(install, profile) {
|
|
51
|
+
try {
|
|
52
|
+
const file = String(profile.model.config || '').replace('{install}', install);
|
|
53
|
+
const settings = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
54
|
+
const r = settings.runners || {};
|
|
55
|
+
const d = r.claude || Object.values(r)[0] || {};
|
|
56
|
+
const flags = Array.isArray(d.alwaysFlags) ? d.alwaysFlags.filter((f) => f !== '--safe-mode') : [];
|
|
57
|
+
const env = {};
|
|
58
|
+
for (const [k, name] of Object.entries(d.env || {})) { const v = settings[name]; if (v !== undefined && v !== null && String(v) !== '') env[k] = String(v); }
|
|
59
|
+
return { flags, env };
|
|
60
|
+
} catch { return { flags: [], env: {} }; }
|
|
61
|
+
}
|
|
62
|
+
|
|
47
63
|
function buildCommand({ install, profile, prompt, cwd, claudeBin, budgetUsd, model: rung }) {
|
|
48
64
|
// a rung the failed-call step moved to is taken only within the profile's family; otherwise the family's own model
|
|
49
65
|
const model = rung && rung.toLowerCase().includes(profile.model.family.toLowerCase()) ? rung : modelFor(install, profile);
|
|
66
|
+
const iso = runnerIsolation(install, profile);
|
|
67
|
+
// the declaration's switches (--setting-sources "", --strict-mcp-config, …) replace the former `--setting-sources project`; the
|
|
68
|
+
// doctor edits files, so its permission mode stays bypass
|
|
50
69
|
const argv = [...hostWrap(), claudeBin || process.env.CLAUDE_BIN || 'claude', '-p', prompt, '--model', model, '--output-format', 'json',
|
|
51
|
-
'--setting-sources', 'project', '--permission-mode', 'bypassPermissions',
|
|
70
|
+
...(iso.flags.length ? iso.flags : ['--setting-sources', 'project']), '--permission-mode', 'bypassPermissions',
|
|
52
71
|
...(budgetUsd ? ['--max-budget-usd', String(budgetUsd)] : [])];
|
|
53
|
-
return { cwd, argv, model };
|
|
72
|
+
return { cwd, argv, model, env: iso.env };
|
|
54
73
|
}
|
|
55
74
|
|
|
56
75
|
/** The answer's shape: repaired / cannot with the fields — anything else is no answer. */
|
|
@@ -80,7 +99,7 @@ function readAnswer(text) {
|
|
|
80
99
|
/** Runs the session (the only model call the doctor makes). Returns the answer, the session's cost record and any model of another family it used. */
|
|
81
100
|
function runAgent({ install, profile, prompt, cwd, sessionDir, env, claudeBin, budgetUsd, model }) {
|
|
82
101
|
const cmd = buildCommand({ install, profile, prompt, cwd, claudeBin, budgetUsd, model });
|
|
83
|
-
const r = spawnSync(cmd.argv[0], cmd.argv.slice(1), { cwd: cmd.cwd, stdio: ['ignore', 'pipe', 'pipe'], env: { CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS: '0', ...process.env, ...(env || {}) }, encoding: 'utf8', maxBuffer: 256 * 1024 * 1024 });
|
|
102
|
+
const r = spawnSync(cmd.argv[0], cmd.argv.slice(1), { cwd: cmd.cwd, stdio: ['ignore', 'pipe', 'pipe'], env: { CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS: '0', ...process.env, ...cmd.env, ...(env || {}) }, encoding: 'utf8', maxBuffer: 256 * 1024 * 1024 });
|
|
84
103
|
try { fs.mkdirSync(sessionDir, { recursive: true }); fs.writeFileSync(path.join(sessionDir, 'agent.stderr'), String(r.stderr || '')); fs.writeFileSync(path.join(sessionDir, 'agent.stdout'), String(r.stdout || '')); } catch { /* evidence only */ }
|
|
85
104
|
let out = null; try { out = JSON.parse(r.stdout); } catch { out = null; }
|
|
86
105
|
const session = out && out.session_id ? { sessionId: out.session_id, modelUsage: out.modelUsage || {}, totalCostUsd: out.total_cost_usd || 0 } : null;
|
|
@@ -9,8 +9,12 @@
|
|
|
9
9
|
// agent is granted them by name (--tools mcp__<server>__<tool>), and a tool whose permission is 'safe' is annotated read-only,
|
|
10
10
|
// which is what lets it run in plan mode (proven 2026-10-24 on Claude Code 2.1.310: without the annotation plan mode refuses it).
|
|
11
11
|
//
|
|
12
|
+
// node pipeline-tools-mcp.js --codeline <dir> serve the plugins THAT CODELINE provisions (.epam/settings.json tools — the
|
|
13
|
+
// pipeline's own grant rule, lib/agent-tools.js pluginToolsFor), run inside it
|
|
12
14
|
// node pipeline-tools-mcp.js <runner-name> serve the runner's hostedToolPlugins (declaration read from the active set)
|
|
13
15
|
// node pipeline-tools-mcp.js --modules a.js,b.js serve these plugin modules (tests)
|
|
16
|
+
// ONE SERVER FOR BOTH RUNTIMES (2026-10-25): the Claude workflows had their own (claude-workflows/mcp/pipeline-tools.js, per
|
|
17
|
+
// codeline) and the hub got this one (per runner) — duplicated code; the workflows now start this file in --codeline mode.
|
|
14
18
|
//
|
|
15
19
|
// Protocol: JSON-RPC 2.0, one message per line, MCP 2024-11-05 — initialize, tools/list, tools/call; nothing else.
|
|
16
20
|
const fs = require('fs');
|
|
@@ -27,6 +31,13 @@ function hostedModulesFor(runnerName) {
|
|
|
27
31
|
return ((runner && runner.hostedToolPlugins) || []).map((m) => path.resolve(ORCH, m));
|
|
28
32
|
}
|
|
29
33
|
|
|
34
|
+
/** The plugin modules a codeline provisions (.epam/settings.json tools), as the pipeline's grant rule reads them. */
|
|
35
|
+
function codelineModulesOf(codeline) {
|
|
36
|
+
let settings;
|
|
37
|
+
try { settings = JSON.parse(fs.readFileSync(path.join(codeline, '.epam', 'settings.json'), 'utf8')); } catch { return []; }
|
|
38
|
+
return (Array.isArray(settings.tools) ? settings.tools : []).filter((e) => typeof e === 'string' && e);
|
|
39
|
+
}
|
|
40
|
+
|
|
30
41
|
/** Every tool the modules export, as MCP lists them: safe → read-only; the plugin's own input schema and description. */
|
|
31
42
|
function toolsOf(modules) {
|
|
32
43
|
const out = [];
|
|
@@ -35,7 +46,7 @@ function toolsOf(modules) {
|
|
|
35
46
|
const mod = require(m);
|
|
36
47
|
for (const t of ((mod && mod.tools) || [])) {
|
|
37
48
|
const name = t.name || (t.definition && t.definition.name);
|
|
38
|
-
if (!name) continue;
|
|
49
|
+
if (!name || typeof t.execute !== 'function') continue;
|
|
39
50
|
const def = t.definition || {};
|
|
40
51
|
out.push({
|
|
41
52
|
name,
|
|
@@ -94,9 +105,12 @@ function mcpConfig(runnerName, nodeBin = process.execPath) {
|
|
|
94
105
|
|
|
95
106
|
if (require.main === module) {
|
|
96
107
|
const a = process.argv.slice(2);
|
|
97
|
-
|
|
98
|
-
if (
|
|
108
|
+
let modules;
|
|
109
|
+
if (a[0] === '--modules') modules = String(a[1] || '').split(',').filter(Boolean).map((m) => path.resolve(m));
|
|
110
|
+
else if (a[0] === '--codeline') { const cl = path.resolve(a[1] || process.cwd()); process.chdir(cl); modules = codelineModulesOf(cl); } // plugins work on the folder they run in
|
|
111
|
+
else modules = hostedModulesFor(a[0]);
|
|
112
|
+
if (!modules.length) { process.stderr.write(`[pipeline-tools-mcp] no tool plugins for ${a[0] === '--codeline' ? `codeline ${a[1]}` : `runner '${a[0] || ''}'`}\n`); process.exit(2); }
|
|
99
113
|
serve(modules);
|
|
100
114
|
}
|
|
101
115
|
|
|
102
|
-
module.exports = { hostedModulesFor, toolsOf, serve, mcpConfig, asMcpResult };
|
|
116
|
+
module.exports = { hostedModulesFor, codelineModulesOf, toolsOf, serve, mcpConfig, asMcpResult };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amsd-pipeline-shared",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.46",
|
|
4
4
|
"description": "The AMSD pipeline's shared components, one source for the bash pipeline and its Claude workflows: the model-call gateway (llm-handler.sh — retry, ladder, self-heal, cost) and the libraries it runs.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"bin": {
|