Files
stack/packages/mosaic/src/commands/compose-contract.spec.ts
jason.woltje adad486b6f
All checks were successful
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
fix(fleet): enforce exact comms authority (#787)
2026-07-16 00:32:23 +00:00

575 lines
24 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import {
accessSync,
chmodSync,
constants,
mkdtempSync,
mkdirSync,
writeFileSync,
rmSync,
readFileSync,
renameSync,
symlinkSync,
} from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { FileConfigAdapter } from '../config/file-adapter.js';
import { composeContract } from './launch.js';
/**
* Composer unit test (R7/R8/R9): asserts the launcher-composed runtime contract
*
* - includes the per-tier anchors (CONSTITUTION / AGENTS / USER / runtime),
* - keeps the CONSTITUTION block byte-equal to the on-disk file (Tier-3
* byte-equality — the bare-launch fallback read must match what is injected),
* - merges `*.local.md` operator overlays as deltas-by-value, and omits them
* entirely when absent (base-only),
* - selects the correct per-harness RUNTIME.md.
*
* `composeContract` takes `mosaicHome` as a param, so each test runs against an
* isolated fixture home. We also chdir to an empty temp cwd so the cwd-relative
* mission/PRD blocks contribute nothing (deterministic output).
*/
const CONSTITUTION = '# CONSTITUTION\n\nGATE-1: the non-negotiable law.\n';
const AGENTS = '# Mosaic Agent Dispatcher\n\nLoad order + guide router.\n';
const USER = '# operator\n\nName: Test Operator\n';
const TOOLS = '# tools index\n\n<!-- fleet-comms-contract: 1 -->\n';
const FRAMEWORK_SOURCE = fileURLToPath(new URL('../../framework', import.meta.url));
const SOURCE_TOOLS_PATH = join(FRAMEWORK_SOURCE, 'defaults', 'TOOLS.md');
function makeHome(): { home: string; root: string } {
const root = mkdtempSync(join(tmpdir(), 'mosaic-compose-'));
const home = join(root, 'mosaic-home');
for (const h of ['claude', 'codex', 'opencode', 'pi']) {
mkdirSync(join(home, 'runtime', h), { recursive: true });
writeFileSync(join(home, 'runtime', h, 'RUNTIME.md'), `# ${h} runtime contract\n`);
}
mkdirSync(join(home, 'defaults'), { recursive: true });
mkdirSync(join(home, 'tools', 'tmux'), { recursive: true });
writeFileSync(join(home, 'defaults', 'TOOLS.md'), TOOLS);
const helper = join(home, 'tools', 'tmux', 'agent-send.sh');
writeFileSync(helper, '#!/bin/sh\n');
chmodSync(helper, 0o755);
writeFileSync(join(home, 'CONSTITUTION.md'), CONSTITUTION);
writeFileSync(join(home, 'AGENTS.md'), AGENTS);
writeFileSync(join(home, 'USER.md'), USER);
writeFileSync(join(home, 'TOOLS.md'), TOOLS);
return { home, root };
}
describe('composeContract — overlay composer', () => {
let fixture: ReturnType<typeof makeHome>;
let prevCwd: string;
let cwdDir: string;
let prevAgentName: string | undefined;
let prevAgentClass: string | undefined;
let prevAgentToolPolicy: string | undefined;
beforeEach(() => {
fixture = makeHome();
prevCwd = process.cwd();
prevAgentName = process.env['MOSAIC_AGENT_NAME'];
prevAgentClass = process.env['MOSAIC_AGENT_CLASS'];
prevAgentToolPolicy = process.env['MOSAIC_AGENT_TOOL_POLICY'];
delete process.env['MOSAIC_AGENT_NAME'];
delete process.env['MOSAIC_AGENT_CLASS'];
delete process.env['MOSAIC_AGENT_TOOL_POLICY'];
cwdDir = mkdtempSync(join(tmpdir(), 'mosaic-cwd-'));
process.chdir(cwdDir); // neutralize cwd-relative mission/PRD blocks
});
afterEach(() => {
process.chdir(prevCwd);
if (prevAgentName === undefined) delete process.env['MOSAIC_AGENT_NAME'];
else process.env['MOSAIC_AGENT_NAME'] = prevAgentName;
if (prevAgentClass === undefined) delete process.env['MOSAIC_AGENT_CLASS'];
else process.env['MOSAIC_AGENT_CLASS'] = prevAgentClass;
if (prevAgentToolPolicy === undefined) delete process.env['MOSAIC_AGENT_TOOL_POLICY'];
else process.env['MOSAIC_AGENT_TOOL_POLICY'] = prevAgentToolPolicy;
rmSync(fixture.root, { recursive: true, force: true });
rmSync(cwdDir, { recursive: true, force: true });
});
it('injects the fleet comms cheat-sheet for a spawned fleet agent (situational)', () => {
// A spawned agent has MOSAIC_AGENT_NAME set + is a member of the roster.
mkdirSync(join(fixture.home, 'fleet'), { recursive: true });
writeFileSync(
join(fixture.home, 'fleet', 'roster.yaml'),
[
'version: 1',
'transport: tmux',
'tmux:',
' socket_name: mosaic-fleet',
'agents:',
' - name: orchestrator',
' runtime: claude',
' class: orchestrator',
' host: w-jarvis',
' - name: enhancer',
' runtime: claude',
' class: enhancer',
' host: w-jarvis',
' - name: coder0-0',
' runtime: claude',
' class: implementer',
' host: 10.1.10.37',
' ssh: jwoltje@10.1.10.37',
'',
].join('\n'),
);
const prev = process.env['MOSAIC_AGENT_NAME'];
try {
process.env['MOSAIC_AGENT_NAME'] = 'enhancer';
const outputs = (['claude', 'codex', 'opencode', 'pi'] as const).map((runtime) =>
composeContract(runtime, fixture.home),
);
for (const out of outputs) {
expect(out).toContain('# Fleet Comms');
expect(out).toContain('Host: `w-jarvis`');
expect(out).toContain('Agent/session: `enhancer`');
expect(out).toContain('tmux socket: `mosaic-fleet`');
expect(out).toContain(
`Helper: \`${join(fixture.home, 'tools', 'tmux', 'agent-send.sh')}\``,
);
expect(out).toContain('-L mosaic-fleet -s orchestrator -m "…"');
expect(out).toContain('-L mosaic-fleet -H jwoltje@10.1.10.37 -s coder0-0 -m "…"');
expect(out).not.toContain('-H jwoltje@10.1.10.37 -s orchestrator');
}
const commsSection = (out: string): string => out.slice(out.indexOf('# Fleet Comms'));
const authoritative = commsSection(outputs[0]!);
expect(outputs.map(commsSection)).toEqual([
authoritative,
authoritative,
authoritative,
authoritative,
]);
} finally {
if (prev === undefined) delete process.env['MOSAIC_AGENT_NAME'];
else process.env['MOSAIC_AGENT_NAME'] = prev;
}
});
it.each(['claude', 'codex', 'opencode', 'pi'] as const)(
'derives canonical class, persona, tool policy, and comms from one roster member for %s',
(runtime) => {
mkdirSync(join(fixture.home, 'fleet', 'roles'), { recursive: true });
writeFileSync(
join(fixture.home, 'fleet', 'roles', 'code.md'),
'# Code\n\n(`class: code`)\n\nCANONICAL-CODE-MANDATE.\n',
);
writeFileSync(
join(fixture.home, 'fleet', 'roster.yaml'),
[
'version: 1',
'transport: tmux',
'agents:',
' - name: exact-coder',
` runtime: ${runtime}`,
' class: implementer',
' tool_policy: operator-interaction',
'',
].join('\n'),
);
process.env['MOSAIC_AGENT_NAME'] = 'exact-coder';
process.env['MOSAIC_AGENT_CLASS'] = 'implementer';
process.env['MOSAIC_AGENT_TOOL_POLICY'] = 'ambient-policy-must-not-win';
const out = composeContract(runtime, fixture.home);
expect(out).toContain('# Persona Contract (code)');
expect(out).toContain('CANONICAL-CODE-MANDATE');
expect(out).toContain('Role/class: `code`');
expect(out).toContain('# Fleet Tool Policy (operator-interaction)');
expect(out).not.toContain('ambient-policy-must-not-win');
expect(out.indexOf('# Persona Contract')).toBeLessThan(out.indexOf('# Fleet Comms'));
},
);
it.each(['claude', 'codex', 'opencode', 'pi'] as const)(
'does not inherit ambient tool policy when canonical member omits tool_policy for %s',
(runtime) => {
mkdirSync(join(fixture.home, 'fleet', 'roles'), { recursive: true });
writeFileSync(
join(fixture.home, 'fleet', 'roles', 'code.md'),
'# Code\n\n(`class: code`)\n\nCANONICAL-CODE-MANDATE.\n',
);
writeFileSync(
join(fixture.home, 'fleet', 'roster.yaml'),
[
'version: 1',
'transport: tmux',
'agents:',
' - name: exact-coder',
` runtime: ${runtime}`,
' class: implementer',
'',
].join('\n'),
);
process.env['MOSAIC_AGENT_NAME'] = 'exact-coder';
process.env['MOSAIC_AGENT_CLASS'] = 'implementer';
process.env['MOSAIC_AGENT_TOOL_POLICY'] = 'operator-interaction';
const out = composeContract(runtime, fixture.home);
expect(out).toContain('# Persona Contract (code)');
expect(out).toContain('Role/class: `code`');
expect(out).toContain('# Fleet Comms');
expect(out).not.toContain('# Fleet Tool Policy (operator-interaction)');
},
);
it.each(['claude', 'codex', 'opencode', 'pi'] as const)(
'rejects an ambient class that mismatches the canonical roster member for %s',
(runtime) => {
mkdirSync(join(fixture.home, 'fleet'), { recursive: true });
writeFileSync(
join(fixture.home, 'fleet', 'roster.yaml'),
[
'version: 1',
'transport: tmux',
'agents:',
' - name: exact-coder',
` runtime: ${runtime}`,
' class: implementer',
'',
].join('\n'),
);
process.env['MOSAIC_AGENT_NAME'] = 'exact-coder';
process.env['MOSAIC_AGENT_CLASS'] = 'reviewer';
expect(() => composeContract(runtime, fixture.home)).toThrow(
/ambient MOSAIC_AGENT_CLASS.*review.*canonical roster.*code/i,
);
},
);
it('composes solo role mandate and boundaries before explicit no-peer authority', () => {
mkdirSync(join(fixture.home, 'fleet', 'roles'), { recursive: true });
writeFileSync(
join(fixture.home, 'fleet', 'roles', 'orchestrator.md'),
'# Orchestrator\n\n## Mandate\n\nCoordinate exact work.\n\n## Boundaries\n\nDo not infer authority.\n',
);
writeFileSync(
join(fixture.home, 'fleet', 'roster.yaml'),
[
'version: 1',
'transport: tmux',
'agents:',
' - name: solo',
' runtime: claude',
' class: orchestrator',
'',
].join('\n'),
);
process.env['MOSAIC_AGENT_NAME'] = 'solo';
process.env['MOSAIC_AGENT_CLASS'] = 'orchestrator';
const out = composeContract('claude', fixture.home);
expect(out).toContain('## Mandate');
expect(out).toContain('## Boundaries');
expect(out).toContain('Role/class: `orchestrator`');
expect(out).toContain('## Solo authority boundaries');
expect(out.indexOf('## Mandate')).toBeLessThan(out.indexOf('# Fleet Comms'));
expect(out.indexOf('## Boundaries')).toBeLessThan(out.indexOf('# Fleet Comms'));
expect(out).toContain('no peer, orchestrator, or remote communication authority');
});
it('proves real source TOOLS.md through fresh install, executable helper, and final composition', async () => {
const installRoot = mkdtempSync(join(tmpdir(), 'mosaic-real-contract-'));
const installedHome = join(installRoot, 'mosaic-home');
mkdirSync(installedHome, { recursive: true });
const previous = process.env['MOSAIC_AGENT_NAME'];
try {
const adapter = new FileConfigAdapter(installedHome, FRAMEWORK_SOURCE);
await adapter.syncFramework('fresh');
const sourceTools = readFileSync(SOURCE_TOOLS_PATH, 'utf8');
const installedToolsPath = join(installedHome, 'TOOLS.md');
expect(readFileSync(installedToolsPath, 'utf8')).toBe(sourceTools);
expect(sourceTools).toContain('fleet-comms-contract: 1');
expect(sourceTools).not.toMatch(
/<(?:user@host|src_host|src_session|dst_host|dst_session|target-session)>/,
);
const helper = join(installedHome, 'tools', 'tmux', 'agent-send.sh');
expect(() => accessSync(helper, constants.X_OK)).not.toThrow();
mkdirSync(join(installedHome, 'fleet'), { recursive: true });
writeFileSync(
join(installedHome, 'fleet', 'roster.yaml'),
[
'version: 1',
'transport: tmux',
'tmux:',
' socket_name: exact-socket',
'agents:',
' - name: exact-self',
' runtime: pi',
' class: orchestrator',
' host: local-host',
' - name: exact-peer',
' runtime: claude',
' class: implementer',
' host: local-host',
'',
].join('\n'),
);
process.env['MOSAIC_AGENT_NAME'] = 'exact-self';
const composed = composeContract('pi', installedHome);
expect(composed).toContain(sourceTools);
expect(composed).toContain(`Helper: \`${helper}\``);
expect(composed).toContain(`${helper} -L exact-socket -s exact-peer -m "…"`);
expect(composed).not.toContain('# Fleet Comms Installation Status');
} finally {
if (previous === undefined) delete process.env['MOSAIC_AGENT_NAME'];
else process.env['MOSAIC_AGENT_NAME'] = previous;
rmSync(installRoot, { recursive: true, force: true });
}
});
it.each(['claude', 'codex', 'opencode', 'pi'] as const)(
'never injects installed TOOLS.md through a target symlink for %s',
(runtime) => {
const toolsPath = join(fixture.home, 'TOOLS.md');
const external = join(fixture.root, 'attacker-tools.md');
writeFileSync(external, 'UNSAFE-TARGET-SYMLINK-CONTENT\n');
rmSync(toolsPath);
symlinkSync(external, toolsPath);
const out = composeContract(runtime, fixture.home);
expect(out).not.toContain('UNSAFE-TARGET-SYMLINK-CONTENT');
expect(out).toContain('# Fleet Comms Installation Status');
expect(out).toContain('unavailable');
expect(readFileSync(external, 'utf8')).toBe('UNSAFE-TARGET-SYMLINK-CONTENT\n');
},
);
it.each(['claude', 'codex', 'opencode', 'pi'] as const)(
'never injects installed TOOLS.md through an ancestor symlink for %s',
(runtime) => {
const realHome = join(fixture.root, 'real-mosaic-home');
renameSync(fixture.home, realHome);
symlinkSync(realHome, fixture.home, 'dir');
writeFileSync(join(realHome, 'TOOLS.md'), 'UNSAFE-ANCESTOR-SYMLINK-CONTENT\n');
const out = composeContract(runtime, fixture.home);
expect(out).not.toContain('UNSAFE-ANCESTOR-SYMLINK-CONTENT');
expect(out).toContain('# Fleet Comms Installation Status');
expect(out).toContain('unavailable');
expect(readFileSync(join(realHome, 'TOOLS.md'), 'utf8')).toBe(
'UNSAFE-ANCESTOR-SYMLINK-CONTENT\n',
);
},
);
it('reports stale preserved TOOLS.md without rewriting it', () => {
const toolsPath = join(fixture.home, 'TOOLS.md');
const stale = '# user-customized tools without fleet contract marker\n';
writeFileSync(toolsPath, stale);
const out = composeContract('pi', fixture.home);
expect(out).toContain('# Fleet Comms Installation Status');
expect(out).toContain('does not byte-match');
expect(out).toContain('active context was not rewritten');
expect(readFileSync(toolsPath, 'utf8')).toBe(stale);
});
it('does NOT inject fleet comms when MOSAIC_AGENT_NAME is unset (non-fleet launch)', () => {
const prev = process.env['MOSAIC_AGENT_NAME'];
try {
delete process.env['MOSAIC_AGENT_NAME'];
expect(composeContract('claude', fixture.home)).not.toContain('# Fleet Comms');
} finally {
if (prev !== undefined) process.env['MOSAIC_AGENT_NAME'] = prev;
}
});
it('fails closed when an explicitly requested fleet identity is unknown', () => {
mkdirSync(join(fixture.home, 'fleet'), { recursive: true });
writeFileSync(
join(fixture.home, 'fleet', 'roster.yaml'),
['version: 1', 'transport: tmux', 'agents:', ' - name: exact-agent', ' runtime: pi'].join(
'\n',
),
);
const previous = process.env['MOSAIC_AGENT_NAME'];
try {
process.env['MOSAIC_AGENT_NAME'] = 'invented-agent';
expect(() => composeContract('pi', fixture.home)).toThrow(/known exact names: exact-agent/i);
} finally {
if (previous === undefined) delete process.env['MOSAIC_AGENT_NAME'];
else process.env['MOSAIC_AGENT_NAME'] = previous;
}
});
it('includes the per-tier anchors and the selected harness runtime', () => {
const out = composeContract('claude', fixture.home);
expect(out).toContain('GATE-1: the non-negotiable law.'); // L0
expect(out).toContain('Mosaic Agent Dispatcher'); // AGENTS
expect(out).toContain('# User Profile'); // USER header
expect(out).toContain('Name: Test Operator'); // USER body
expect(out).toContain('# Runtime-Specific Contract');
expect(out).toContain('# claude runtime contract');
});
it('keeps the CONSTITUTION block byte-equal to the on-disk file (Tier-3)', () => {
const out = composeContract('pi', fixture.home);
const onDisk = readFileSync(join(fixture.home, 'CONSTITUTION.md'), 'utf-8');
// The injected L0 must be a byte-equal substring of the composed blob, so a
// bare-launch fallback read of CONSTITUTION.md matches what was injected.
expect(out.includes(onDisk)).toBe(true);
});
it('is base-only when no *.local overlays exist', () => {
const out = composeContract('claude', fixture.home);
expect(out).not.toContain('# Operator Overlays');
expect(out).not.toContain('Operator Overlay (USER.local.md)');
expect(out).not.toContain('Persona Overlay');
expect(out).not.toContain('Standards Overlay');
});
it('merges USER.local.md directly under the operator profile', () => {
writeFileSync(join(fixture.home, 'USER.local.md'), 'Prefer terse status updates.\n');
const out = composeContract('claude', fixture.home);
expect(out).toContain('## Operator Overlay (USER.local.md)');
expect(out).toContain('Prefer terse status updates.');
// Overlay appears AFTER its base profile.
expect(out.indexOf('# User Profile')).toBeLessThan(
out.indexOf('## Operator Overlay (USER.local.md)'),
);
});
it('merges SOUL.local.md + STANDARDS.local.md as deltas in the Operator Overlays block', () => {
writeFileSync(join(fixture.home, 'SOUL.local.md'), 'Tone: dry and direct.\n');
writeFileSync(join(fixture.home, 'STANDARDS.local.md'), 'Require 90% coverage on auth code.\n');
const out = composeContract('claude', fixture.home);
expect(out).toContain('# Operator Overlays');
expect(out).toContain('## Persona Overlay (SOUL.local.md)');
expect(out).toContain('Tone: dry and direct.');
expect(out).toContain('## Standards Overlay (STANDARDS.local.md)');
expect(out).toContain('Require 90% coverage on auth code.');
});
it('ignores whitespace-only *.local overlays (no empty overlay section)', () => {
writeFileSync(join(fixture.home, 'SOUL.local.md'), ' \n\n');
const out = composeContract('claude', fixture.home);
expect(out).not.toContain('# Operator Overlays');
});
it('selects a different RUNTIME.md per harness', () => {
expect(composeContract('codex', fixture.home)).toContain('# codex runtime contract');
expect(composeContract('pi', fixture.home)).toContain('# pi runtime contract');
expect(composeContract('codex', fixture.home)).not.toContain('# pi runtime contract');
});
it('injects the configured operator-interaction tool policy into the runtime contract', () => {
const previous = process.env['MOSAIC_AGENT_TOOL_POLICY'];
try {
process.env['MOSAIC_AGENT_TOOL_POLICY'] = 'operator-interaction';
const out = composeContract('pi', fixture.home);
expect(out).toContain('# Fleet Tool Policy (operator-interaction)');
expect(out).toContain('Denied by default: coding/general orchestration claims');
} finally {
if (previous === undefined) delete process.env['MOSAIC_AGENT_TOOL_POLICY'];
else process.env['MOSAIC_AGENT_TOOL_POLICY'] = previous;
}
});
// ── Persona contract injection (A3b) ──────────────────────────────────────
// composeContract reads MOSAIC_AGENT_CLASS and injects the resolved persona
// (override-aware). Save/restore the env so these tests don't leak state.
describe('persona contract (A3b)', () => {
let prevClass: string | undefined;
beforeEach(() => {
prevClass = process.env['MOSAIC_AGENT_CLASS'];
});
afterEach(() => {
if (prevClass === undefined) delete process.env['MOSAIC_AGENT_CLASS'];
else process.env['MOSAIC_AGENT_CLASS'] = prevClass;
});
const seedBaseline = (klass: string, body: string): void => {
mkdirSync(join(fixture.home, 'fleet', 'roles'), { recursive: true });
writeFileSync(join(fixture.home, 'fleet', 'roles', `${klass}.md`), body);
};
const seedOverride = (klass: string, body: string): void => {
mkdirSync(join(fixture.home, 'fleet', 'roles.local'), { recursive: true });
writeFileSync(join(fixture.home, 'fleet', 'roles.local', `${klass}.md`), body);
};
it('injects the baseline persona when MOSAIC_AGENT_CLASS is set and a role file exists', () => {
seedBaseline('coder', '# Coder\n\n(`class: coder`)\n\nBASELINE-MANDATE: ship the lane.\n');
process.env['MOSAIC_AGENT_CLASS'] = 'coder';
const out = composeContract('claude', fixture.home);
expect(out).toContain('# Persona Contract (coder)');
expect(out).toContain('BASELINE-MANDATE');
});
it('OVERRIDE WINS at launch: roles.local persona is injected over baseline (AC-NS-7)', () => {
seedBaseline('coder', '# Coder\n\n(`class: coder`)\n\nBASELINE-MANDATE.\n');
seedOverride('coder', '# Coder (override)\n\n(`class: coder`)\n\nOVERRIDE-MANDATE.\n');
process.env['MOSAIC_AGENT_CLASS'] = 'coder';
const out = composeContract('claude', fixture.home);
expect(out).toContain('# Persona Contract (coder)');
expect(out).toContain('OVERRIDE-MANDATE');
expect(out).not.toContain('BASELINE-MANDATE');
});
it('does NOT inject a persona when MOSAIC_AGENT_CLASS is unset', () => {
seedBaseline('coder', '# Coder\n\n(`class: coder`)\n\nBASELINE-MANDATE.\n');
delete process.env['MOSAIC_AGENT_CLASS'];
const out = composeContract('claude', fixture.home);
expect(out).not.toContain('# Persona Contract');
});
it('does NOT inject (no throw) when MOSAIC_AGENT_CLASS names an unknown class', () => {
seedBaseline('coder', '# Coder\n\n(`class: coder`)\n\nBASELINE-MANDATE.\n');
process.env['MOSAIC_AGENT_CLASS'] = 'nonexistent';
expect(() => composeContract('claude', fixture.home)).not.toThrow();
expect(composeContract('claude', fixture.home)).not.toContain('# Persona Contract');
});
it('places the persona contract BEFORE the fleet comms block (identity, then peers)', () => {
seedBaseline('enhancer', '# Enhancer\n\n(`class: enhancer`)\n\nIMPROVE.\n');
mkdirSync(join(fixture.home, 'fleet'), { recursive: true });
writeFileSync(
join(fixture.home, 'fleet', 'roster.yaml'),
[
'version: 1',
'transport: tmux',
'agents:',
' - name: orchestrator',
' runtime: claude',
' class: orchestrator',
' - name: enhancer',
' runtime: claude',
' class: enhancer',
'',
].join('\n'),
);
const prevName = process.env['MOSAIC_AGENT_NAME'];
try {
process.env['MOSAIC_AGENT_CLASS'] = 'enhancer';
process.env['MOSAIC_AGENT_NAME'] = 'enhancer';
const out = composeContract('claude', fixture.home);
expect(out).toContain('# Persona Contract (enhancer)');
expect(out).toContain('# Fleet Comms');
expect(out.indexOf('# Persona Contract')).toBeLessThan(out.indexOf('# Fleet Comms'));
} finally {
if (prevName === undefined) delete process.env['MOSAIC_AGENT_NAME'];
else process.env['MOSAIC_AGENT_NAME'] = prevName;
}
});
});
});