@dharma-ai-labs/agent-fabric 0.2.15 → 0.2.17

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.
@@ -7,7 +7,7 @@ import { join } from 'node:path';
7
7
  import test from 'node:test';
8
8
  import { fileURLToPath, pathToFileURL } from 'node:url';
9
9
  import { promisify } from 'node:util';
10
- import { activateAgyPlugin, actionDecisionCapabilityFreshUntil, canonicalFilesystemPath, controlAgentDecisionUrl, installAvailableNativeAgentFabricBootstraps, assertCapsuleAuthorizedByCurrentPolicy, assertRecoveredTaskWorkspacePolicy, assertTaskWorkspacePolicy, assertTaskSkillPin, installNativeAgentFabricBootstrap, installRepositoryAgentFabricSkill, isDirectExecution, materializeWorkspacePolicy, applyServerEvidencePolicy, materializeInlineSkillFiles, nativeSkillDirectory, normalizeGitRemoteIdentity, postTaskOutcome, parseCliOptions, parseSelectedProviderIds, pathExistsOrThrow, portalUrl, rawLocalRetentionDays, recoverLegacySkillBundleIdAfterAuthorizationFailure, installedBundleIdForSkillPollAfterAuthorizationFailure, recoveredTaskPolicyWasSuperseded, relayProcessState, releaseDailyContentUpload, reserveDailyContentUpload, run, runAssistantCommand, sourceRepositoryFingerprint, responseTextFromEvent, taskResponsePreview, taskReceiptSession, taskSkillPinFailureCode, verifyAgentFabricSkillInstallation, withWorkspacePolicyRefreshLock, withWorkspaceSkillActivationLock, } from './index.js';
10
+ import { activateAgyPlugin, attestAgySkillActivation, attestHermesSkillBundleActivation, actionDecisionCapabilityFreshUntil, canonicalFilesystemPath, controlAgentDecisionUrl, installAvailableNativeAgentFabricBootstraps, assertCapsuleAuthorizedByCurrentPolicy, assertRecoveredTaskWorkspacePolicy, assertTaskWorkspacePolicy, assertTaskSkillPin, installNativeAgentFabricBootstrap, installRepositoryAgentFabricSkill, isDirectExecution, materializeWorkspacePolicy, applyServerEvidencePolicy, materializeInlineSkillFiles, nativeSkillDirectory, normalizeGitRemoteIdentity, postTaskOutcome, parseCliOptions, parseSelectedProviderIds, pathExistsOrThrow, portalUrl, rawLocalRetentionDays, recoverLegacySkillBundleIdAfterAuthorizationFailure, installedBundleIdForSkillPollAfterAuthorizationFailure, recoveredTaskPolicyWasSuperseded, relayProcessState, releaseDailyContentUpload, reserveDailyContentUpload, run, runAssistantCommand, sourceRepositoryFingerprint, ensureAgyReadOnlyAttestationPermission, attestAgySkillBundleActivation, responseTextFromEvent, taskResponsePreview, taskReceiptSession, taskSkillPinFailureCode, verifyAgentFabricSkillInstallation, withWorkspacePolicyRefreshLock, withWorkspaceSkillActivationLock, } from './index.js';
11
11
  import { canonicalize, signCanonicalObject } from '@dharma-ai-labs/agent-fabric-contracts';
12
12
  import { buildTrajectoryCapsule } from '@dharma-ai-labs/agent-fabric-evidence-reduction';
13
13
  import { CLI_USAGE } from './usage.js';
@@ -401,7 +401,8 @@ test('CLI parser preserves repeated repository, key, and provider selections', (
401
401
  assert.equal(parsed.flags.get('non-interactive'), true);
402
402
  assert.deepEqual(parseSelectedProviderIds(parsed.repeated.get('provider') || []), ['codex', 'claude']);
403
403
  assert.deepEqual(parseSelectedProviderIds(['agy,codex', 'agy']), ['agy', 'codex']);
404
- assert.throws(() => parseSelectedProviderIds(['unsupported']), /codex, claude, or agy/);
404
+ assert.deepEqual(parseSelectedProviderIds(['hermes,codex', 'hermes']), ['hermes', 'codex']);
405
+ assert.throws(() => parseSelectedProviderIds(['unsupported']), /codex, claude, agy, or hermes/);
405
406
  });
406
407
  test('workspace registration reuses a normalized repository identity across local paths', async () => {
407
408
  const root = await mkdtemp(join(tmpdir(), 'dharma-repository-identity-'));
@@ -590,12 +591,15 @@ test('workspace policy refreshes serialize before requesting a new signed author
590
591
  process.env.DHARMA_HOME = home;
591
592
  const events = [];
592
593
  try {
594
+ let firstEntered;
595
+ const firstStarted = new Promise((resolve) => { firstEntered = resolve; });
593
596
  const first = withWorkspacePolicyRefreshLock('workspace-concurrent', async () => {
594
597
  events.push('first-start');
598
+ firstEntered();
595
599
  await new Promise((accept) => setTimeout(accept, 40));
596
600
  events.push('first-end');
597
601
  });
598
- await new Promise((accept) => setTimeout(accept, 5));
602
+ await firstStarted;
599
603
  const second = withWorkspacePolicyRefreshLock('workspace-concurrent', async () => {
600
604
  events.push('second-start');
601
605
  events.push('second-end');
@@ -616,12 +620,15 @@ test('task execution and skill synchronization share one workspace-provider acti
616
620
  process.env.DHARMA_HOME = home;
617
621
  const events = [];
618
622
  try {
623
+ let taskEntered;
624
+ const taskStarted = new Promise((resolve) => { taskEntered = resolve; });
619
625
  const task = withWorkspaceSkillActivationLock('workspace-concurrent', 'codex', async () => {
620
626
  events.push('task-start');
627
+ taskEntered();
621
628
  await new Promise((accept) => setTimeout(accept, 40));
622
629
  events.push('task-end');
623
630
  });
624
- await new Promise((accept) => setTimeout(accept, 5));
631
+ await taskStarted;
625
632
  const sync = withWorkspaceSkillActivationLock('workspace-concurrent', 'codex', async () => {
626
633
  events.push('sync-start');
627
634
  events.push('sync-end');
@@ -1132,9 +1139,73 @@ test('provider skill roots map to each host native discovery directory', async (
1132
1139
  assert.equal(nativeSkillDirectory('codex', {}, home), join(home, '.codex', 'skills'));
1133
1140
  assert.equal(nativeSkillDirectory('claude', {}, home), join(home, '.claude', 'skills'));
1134
1141
  assert.equal(nativeSkillDirectory('agy', {}, home), join(home, '.gemini', 'antigravity-cli', 'plugins', 'dharma-agent-fabric', 'skills'));
1142
+ assert.equal(nativeSkillDirectory('hermes', {}, home), join(home, '.hermes', 'skills'));
1135
1143
  assert.equal(nativeSkillDirectory('codex', { CODEX_HOME: join(home, 'custom-codex') }, home), join(home, 'custom-codex', 'skills'));
1136
1144
  assert.equal(nativeSkillDirectory('claude', { CLAUDE_CONFIG_DIR: join(home, 'custom-claude') }, home), join(home, 'custom-claude', 'skills'));
1137
1145
  assert.equal(nativeSkillDirectory('agy', { AGY_CONFIG_DIR: join(home, 'custom-agy') }, home), join(home, 'custom-agy', 'plugins', 'dharma-agent-fabric', 'skills'));
1146
+ assert.equal(nativeSkillDirectory('hermes', { HERMES_HOME: join(home, 'custom-hermes') }, home), join(home, 'custom-hermes', 'skills'));
1147
+ });
1148
+ test('Hermes bootstrap trusts the repository and verifies native discovery', async () => {
1149
+ const root = await mkdtemp(join(tmpdir(), 'dharma-hermes-skill-'));
1150
+ const home = join(root, 'home');
1151
+ const workspace = join(root, 'repo');
1152
+ const calls = [];
1153
+ await mkdir(workspace, { recursive: true });
1154
+ const canonicalWorkspace = await realpath(workspace);
1155
+ await installRepositoryAgentFabricSkill({
1156
+ workspace,
1157
+ hqUrl: 'https://www.dharma-ai.io',
1158
+ organizationId: 'org_test',
1159
+ workspaceId: 'workspace_test',
1160
+ policyRevision: 'agent-fabric-policy-v1',
1161
+ });
1162
+ const installed = await installNativeAgentFabricBootstrap({
1163
+ provider: 'hermes', workspace, workspaceId: 'workspace_test', organizationId: 'org_test',
1164
+ hqUrl: 'https://www.dharma-ai.io', home, env: { HOME: home, PATH: '/usr/bin:/bin' },
1165
+ executeHermes: async (executable, argv, options) => {
1166
+ calls.push({ executable, argv, cwd: options.cwd });
1167
+ return { stdout: '' };
1168
+ },
1169
+ });
1170
+ assert.equal(installed.verified, true);
1171
+ assert.deepEqual(calls[0], {
1172
+ executable: 'hermes',
1173
+ argv: ['skills', 'trust', canonicalWorkspace],
1174
+ cwd: canonicalWorkspace,
1175
+ });
1176
+ const verified = await verifyAgentFabricSkillInstallation({
1177
+ provider: 'hermes', workspace, home, env: { HOME: home, PATH: '/usr/bin:/bin' },
1178
+ listHermesSkills: async () => ({ stdout: 'dharma-agent-fabric local trusted enabled' }),
1179
+ });
1180
+ assert.equal(verified.ready, true);
1181
+ assert.equal(verified.nativeDiscovered, true);
1182
+ assert.equal(verified.activationAttested, false);
1183
+ assert.match(verified.nextAction, /signed remediation bundle/);
1184
+ });
1185
+ test('Hermes signed activation requires every installed bundle skill to be discovered', async () => {
1186
+ const workspace = await mkdtemp(join(tmpdir(), 'dharma-hermes-activation-'));
1187
+ const canonicalWorkspace = await realpath(workspace);
1188
+ const bundle = {
1189
+ operation: 'install',
1190
+ skills: [{ skillId: 'price-boundary' }, { skillId: 'handoff-authority' }],
1191
+ };
1192
+ const calls = [];
1193
+ const passed = await attestHermesSkillBundleActivation({
1194
+ bundle, workspace,
1195
+ execute: async (_executable, argv) => {
1196
+ calls.push(argv);
1197
+ return { stdout: argv[1] === 'list' ? 'price-boundary\nhandoff-authority\n' : '' };
1198
+ },
1199
+ });
1200
+ assert.equal(passed.status, 'pass');
1201
+ assert.deepEqual(calls[0], ['skills', 'trust', canonicalWorkspace]);
1202
+ assert.deepEqual(calls[1], ['skills', 'list', '--source', 'local', '--enabled-only']);
1203
+ const failed = await attestHermesSkillBundleActivation({
1204
+ bundle, workspace,
1205
+ execute: async (_executable, argv) => ({ stdout: argv[1] === 'list' ? 'price-boundary\n' : '' }),
1206
+ });
1207
+ assert.equal(failed.status, 'fail');
1208
+ assert.match(failed.details, /handoff-authority/);
1138
1209
  });
1139
1210
  test('native bootstrap installation makes the repository skill verifiable by Codex', async () => {
1140
1211
  const root = await mkdtemp(join(tmpdir(), 'dharma-native-skill-'));
@@ -1207,7 +1278,7 @@ test('Agy skill verification requires the native plugin to be discoverable by th
1207
1278
  assert.equal(discovered.signedLifecycleReady, false);
1208
1279
  assert.equal(discovered.ready, true);
1209
1280
  assert.equal(discovered.verificationScope, 'generic_bootstrap');
1210
- assert.match(discovered.nextAction, /signed activation and rollback remain unavailable/);
1281
+ assert.match(discovered.nextAction, /content-bound activation challenge/);
1211
1282
  });
1212
1283
  test('native bootstrap installation refuses to overwrite an unmanaged provider skill', async () => {
1213
1284
  const root = await mkdtemp(join(tmpdir(), 'dharma-native-skill-unmanaged-'));
@@ -1242,6 +1313,105 @@ test('Agy activation validates and registers the generated plugin before enablin
1242
1313
  { executable: 'agy', argv: ['plugin', 'enable', 'dharma-agent-fabric'] },
1243
1314
  ]);
1244
1315
  });
1316
+ test('Agy attestation permission adds only the supported read-only Git preflight', async () => {
1317
+ const home = await mkdtemp(join(tmpdir(), 'dharma-agy-permission-'));
1318
+ const root = join(home, '.gemini', 'antigravity-cli');
1319
+ await mkdir(root, { recursive: true });
1320
+ await writeFile(join(root, 'settings.json'), `${JSON.stringify({
1321
+ enableTelemetry: false,
1322
+ permissions: { allow: ['command(ls)'] },
1323
+ }, null, 2)}\n`);
1324
+ const first = await ensureAgyReadOnlyAttestationPermission({ home, env: {} });
1325
+ const second = await ensureAgyReadOnlyAttestationPermission({ home, env: {} });
1326
+ const settings = JSON.parse(await readFile(join(root, 'settings.json'), 'utf8'));
1327
+ assert.equal(first.changed, true);
1328
+ assert.equal(second.changed, false);
1329
+ assert.deepEqual(settings.permissions.allow, ['command(ls)', 'command(git status)']);
1330
+ assert.equal(settings.enableTelemetry, false);
1331
+ });
1332
+ test('Agy activation attestation proves the invoked skill token without unsafe permission bypasses', async () => {
1333
+ const root = await mkdtemp(join(tmpdir(), 'dharma-agy-attestation-'));
1334
+ const workspace = join(root, 'repo');
1335
+ const skillPath = join(root, 'SKILL.md');
1336
+ await mkdir(workspace, { recursive: true });
1337
+ await writeFile(skillPath, [
1338
+ '# Signed remediation',
1339
+ '',
1340
+ 'Dharma activation token: `sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`',
1341
+ '',
1342
+ ].join('\n'));
1343
+ let observed = {};
1344
+ const result = await attestAgySkillActivation({
1345
+ skillId: 'dharma-remediation',
1346
+ skillPath,
1347
+ workspace,
1348
+ challenge: 'agy-test-challenge',
1349
+ execute: async (executable, argv, options) => {
1350
+ observed = { executable, argv, cwd: options.cwd };
1351
+ return { stdout: JSON.stringify({
1352
+ status: 'SUCCESS',
1353
+ response: '```json\\n{"challenge":"agy-test-challenge","activationToken":"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}\\n```',
1354
+ }) };
1355
+ },
1356
+ });
1357
+ assert.equal(result.status, 'pass');
1358
+ assert.equal(observed.executable, 'agy');
1359
+ assert.equal(observed.cwd, await realpath(workspace));
1360
+ assert.ok(observed.argv?.includes('--sandbox'));
1361
+ assert.ok(observed.argv?.includes('plan'));
1362
+ assert.ok(observed.argv?.includes('gemini-3.7-flash-low'));
1363
+ assert.equal(observed.argv?.includes('--dangerously-skip-permissions'), false);
1364
+ assert.match(String(observed.argv?.[2]), /^\/dharma-remediation/);
1365
+ assert.doesNotMatch(String(observed.argv?.[2]), /sha256:aaaaaaaa/);
1366
+ });
1367
+ test('Agy activation attestation fails closed on a mismatched content token', async () => {
1368
+ const root = await mkdtemp(join(tmpdir(), 'dharma-agy-attestation-mismatch-'));
1369
+ const skillPath = join(root, 'SKILL.md');
1370
+ await writeFile(skillPath, 'Dharma activation token: `sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`\n');
1371
+ const result = await attestAgySkillActivation({
1372
+ skillId: 'dharma-remediation',
1373
+ skillPath,
1374
+ workspace: root,
1375
+ challenge: 'agy-test-challenge',
1376
+ execute: async () => ({ stdout: JSON.stringify({
1377
+ status: 'SUCCESS',
1378
+ response: '{"challenge":"agy-test-challenge","activationToken":"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}',
1379
+ }) }),
1380
+ });
1381
+ assert.equal(result.status, 'fail');
1382
+ assert.equal(result.details, 'Agy returned an activation token that does not match the installed signed skill.');
1383
+ });
1384
+ test('Agy does not report content attestation for an empty clear bundle', async () => {
1385
+ const root = await mkdtemp(join(tmpdir(), 'dharma-agy-empty-attestation-'));
1386
+ const result = await attestAgySkillBundleActivation({
1387
+ bundle: {
1388
+ schema: 'dharma.skill-bundle/v2',
1389
+ bundleId: 'bundle_clear',
1390
+ organizationId: 'org_test',
1391
+ version: '0.0.0',
1392
+ operation: 'clear',
1393
+ riskClass: 'R0',
1394
+ targetSelectors: {
1395
+ organizationAgentIds: [],
1396
+ deviceIds: [],
1397
+ workspaceIds: [],
1398
+ providers: ['agy'],
1399
+ },
1400
+ activationPolicy: 'next_session',
1401
+ rollbackBundleId: null,
1402
+ evaluationReceiptId: 'evaluation_clear',
1403
+ bundleHash: 'unused-by-attestation',
1404
+ createdAt: new Date().toISOString(),
1405
+ expiresAt: new Date(Date.now() + 60_000).toISOString(),
1406
+ skills: [],
1407
+ signature: 'unused-by-attestation',
1408
+ },
1409
+ nativeSkillDirectory: root,
1410
+ workspace: root,
1411
+ });
1412
+ assert.equal(result.status, 'unavailable');
1413
+ assert.equal(result.details, 'The signed bundle contains no Agy skill content to attest.');
1414
+ });
1245
1415
  test('native bootstrap installation isolates provider failures during onboarding', async () => {
1246
1416
  const result = await installAvailableNativeAgentFabricBootstraps({
1247
1417
  providers: [