@dharma-ai-labs/agent-fabric 0.2.124 → 0.2.126

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/dist/index.js CHANGED
@@ -30,6 +30,7 @@ import { advanceRepositorySourceBaseline, BlockedRepositorySourceRetry, fetchRep
30
30
  import { assertRepositoryInstallerOwnership, writeRepositoryInstallerFile } from './repositoryInstallerFiles.js';
31
31
  import { recoverLegacyRepositoryInstaller, selectLegacyInstallerRecoveryWorkspace } from './legacyInstallerRecovery.js';
32
32
  import { onboardingResumeCommand, selectDeviceWorkspace, workspaceIdForDevice } from './onboardingWorkspace.js';
33
+ import { resolveBootstrapRepositoryWorkspace } from './bootstrapRepositorySelection.js';
33
34
  import { receiveRepositoryPackageDelivery } from './repositoryPackageDelivery.js';
34
35
  import { selectInstalledRepositoryKnowledge } from './repositoryInstalledKnowledge.js';
35
36
  import { prepareProvidersIndependently, startSkillPreparationPump } from './skillPreparationPump.js';
@@ -60,7 +61,7 @@ import { openCodexAppServerTransport } from '@dharma-ai-labs/agent-fabric-provid
60
61
  import { createNamedSessionTrust, isNamedSessionOwnerReceipt, renewNamedSessionLifetime } from './namedSessionTrust.js';
61
62
  import { startNamedCodexThread } from './namedCodexThread.js';
62
63
  export { openCooperativeInboxSession } from './cooperativeInboxSession.js';
63
- const VERSION = '0.2.124';
64
+ const VERSION = '0.2.126';
64
65
  const USAGE = CLI_USAGE;
65
66
  const execFileAsync = promisify(execFile);
66
67
  const LOCAL_PROVIDER_IDS = ['codex', 'claude', 'agy', 'hermes'];
@@ -116,6 +117,17 @@ export function portalUrl(flags, fallback = 'https://www.dharma-ai.io') {
116
117
  function print(value) {
117
118
  process.stdout.write(typeof value === 'string' ? `${value}\n` : `${JSON.stringify(value, null, 2)}\n`);
118
119
  }
120
+ export function commandExitCode(argv, value) {
121
+ const { positional, flags } = parseCliOptions(argv);
122
+ if (positional[0] !== 'bootstrap' || flags.has('help') || flags.has('version'))
123
+ return 0;
124
+ if (!value || typeof value !== 'object' || Array.isArray(value))
125
+ return 1;
126
+ const receipt = value;
127
+ if (receipt.ok !== true)
128
+ return 1;
129
+ return flags.has('complete') && (receipt.stage !== 'complete' || receipt.sharedRepositoryReady !== true) ? 1 : 0;
130
+ }
119
131
  async function loadVaultModule() {
120
132
  const original = process.emitWarning;
121
133
  process.emitWarning = ((warning, ...args) => {
@@ -350,8 +362,16 @@ export async function syncPendingRetentionCapsules(vault, fabric, policy, worksp
350
362
  : 'An unsent oversized trajectory capsule was retired from the upload queue. Encrypted raw evidence remains local and eligible sessions can be recaptured under the current policy.\n');
351
363
  continue;
352
364
  }
353
- await reserveDailyContentUpload(item.capsule, policy);
354
- await fabric.syncTrajectory(item.capsule);
365
+ try {
366
+ await reserveDailyContentUpload(item.capsule, policy);
367
+ await fabric.syncTrajectory(item.capsule);
368
+ }
369
+ catch (error) {
370
+ if (!isDefinitiveSecretDisclosureRejection(error))
371
+ throw error;
372
+ vault.discardPendingCapsuleSync(item.trajectoryId, item.revision, 'secret_disclosure_forbidden');
373
+ continue;
374
+ }
355
375
  vault.markCapsuleSynced(item.trajectoryId, item.revision);
356
376
  synced += 1;
357
377
  }
@@ -1398,10 +1418,30 @@ export async function retryBootstrapOnboarding(operation, options = {}) {
1398
1418
  }
1399
1419
  }
1400
1420
  function bootstrapEvidenceErrorCode(error) {
1421
+ if (isDefinitiveSecretDisclosureRejection(error))
1422
+ return 'secret_disclosure_forbidden';
1401
1423
  const message = String(error?.message || error || '');
1402
1424
  const candidate = message.match(/(?:^|\n)([a-z][a-z0-9_]{2,80})(?=:)/)?.[1];
1403
1425
  return candidate || 'evidence_capture_failed';
1404
1426
  }
1427
+ function isDefinitiveSecretDisclosureRejection(error) {
1428
+ return isDefinitiveAgentFabricRejection(error)
1429
+ && /^policy_boundary:\s*secret_disclosure_forbidden(?:\s|\.|$)/.test(String(error.message));
1430
+ }
1431
+ export function publishedPackageCanDeferSecretEvidence(evidence, repositoryPackage, sourcePolicyGenerationId) {
1432
+ return evidence.state === 'deferred' && evidence.errorCode === 'secret_disclosure_forbidden'
1433
+ && repositoryPackage.state === 'published' && Boolean(repositoryPackage.releaseId)
1434
+ && repositoryPackage.sourcePolicyGenerationId === sourcePolicyGenerationId;
1435
+ }
1436
+ export function initialBootstrapEvidenceDisposition(discovered, disclosureReady) {
1437
+ if (!Number.isSafeInteger(discovered) || discovered < 0) {
1438
+ throw new Error('Evidence preview returned an invalid trajectory count.');
1439
+ }
1440
+ return discovered > 0 && !disclosureReady
1441
+ ? { state: 'denied_disclosure', captured: 0, synced: 0,
1442
+ reason: 'policy_boundary', errorCode: 'automatic_disclosure_not_ready' }
1443
+ : { state: 'synchronized', captured: 0, synced: 0 };
1444
+ }
1405
1445
  export async function synchronizeBootstrapEvidence(operation) {
1406
1446
  try {
1407
1447
  const result = await operation();
@@ -1413,8 +1453,8 @@ export async function synchronizeBootstrapEvidence(operation) {
1413
1453
  const retentionNotReady = errorCode === 'agent_fabric_retention_not_ready';
1414
1454
  return {
1415
1455
  state: 'deferred',
1416
- captured: 0,
1417
- synced: 0,
1456
+ captured: null,
1457
+ synced: null,
1418
1458
  reason: retentionNotReady ? 'retention_not_ready' : policyBoundary ? 'policy_boundary' : 'capture_unavailable',
1419
1459
  errorCode,
1420
1460
  };
@@ -2006,7 +2046,38 @@ async function bootstrap(flags) {
2006
2046
  const organizationId = required(flags, 'organization-id');
2007
2047
  const resuming = flags.has('resume');
2008
2048
  const bootstrapToken = resuming ? null : required(flags, 'grant');
2009
- const workspace = await realpath(String(flags.get('workspace') || '.'));
2049
+ const selectedRemote = flags.get('repository-url-base64url');
2050
+ let repositorySelection;
2051
+ try {
2052
+ if (flags.has('repository-url-base64url') && typeof selectedRemote !== 'string') {
2053
+ throw new Error('repository_selection_invalid_url: selected repository URL is missing.');
2054
+ }
2055
+ if (selectedRemote && flags.has('repository-key')) {
2056
+ throw new Error('repository_selection_invalid_url: selected remote cannot be combined with a repository key.');
2057
+ }
2058
+ repositorySelection = typeof selectedRemote === 'string'
2059
+ ? await resolveBootstrapRepositoryWorkspace({
2060
+ workspace: String(flags.get('workspace') || '.'),
2061
+ selectedRemoteBase64url: selectedRemote,
2062
+ organizationId,
2063
+ home: dharmaHome(),
2064
+ normalizeRemote: normalizeGitRemoteIdentity,
2065
+ withLock: (path) => acquirePidLock(path, 30_000, 'repository_checkout_busy: another setup is selecting this repository; retry after it finishes.'),
2066
+ })
2067
+ : { workspace: await realpath(String(flags.get('workspace') || '.')), selection: 'provided' };
2068
+ }
2069
+ catch (error) {
2070
+ const message = error && typeof error === 'object' && 'message' in error && typeof error.message === 'string'
2071
+ ? error.message : '';
2072
+ const code = /^repository_[a-z_]+:/.exec(message)?.[0].slice(0, -1) || 'repository_selection_failed';
2073
+ return { ok: false, stage: 'repository_selection', code, grantRedeemed: false,
2074
+ message: code === 'repository_checkout_failed'
2075
+ ? 'Git could not clone the selected repository with this device\'s existing access. Confirm repository access or use a matching checkout; the grant was not redeemed.'
2076
+ : message.startsWith('repository_') ? message : 'Selected repository could not be verified; the grant was not redeemed.' };
2077
+ }
2078
+ const workspace = repositorySelection.workspace;
2079
+ if (selectedRemote)
2080
+ process.stderr.write(`Agent Fabric repository checkout: ${workspace}\n`);
2010
2081
  const policyRevision = required(flags, 'policy-revision');
2011
2082
  const repositoryIdentity = await preflightBootstrapWorkspaceIdentity(workspace, typeof flags.get('repository-key') === 'string' ? String(flags.get('repository-key')) : null);
2012
2083
  const requestedProvider = String(flags.get('provider') || 'auto').trim().toLowerCase();
@@ -2116,6 +2187,7 @@ async function bootstrap(flags) {
2116
2187
  return {
2117
2188
  ok: false,
2118
2189
  stage: 'organization_api_credentials',
2190
+ repositorySelection,
2119
2191
  code: credentialFailure,
2120
2192
  message: 'The enrolled device is preserved, but its organization API credential is unavailable. Contact Dharma support for device-bound credential recovery; do not repeat enrollment or use another member\'s token.',
2121
2193
  sharedRepositoryReady: false,
@@ -2137,6 +2209,7 @@ async function bootstrap(flags) {
2137
2209
  return {
2138
2210
  ok: onboarded.ok === true,
2139
2211
  stage: onboarded.stage,
2212
+ repositorySelection,
2140
2213
  code: onboarded.code,
2141
2214
  sharedRepositoryReady: false,
2142
2215
  enrollment: {
@@ -2190,6 +2263,7 @@ async function bootstrap(flags) {
2190
2263
  return {
2191
2264
  ok: true,
2192
2265
  stage: sharedRepositoryReady ? onboarded.stage : 'shared_repository_pending',
2266
+ repositorySelection,
2193
2267
  localStage: onboarded.localStage ?? onboarded.stage,
2194
2268
  sharedRepositoryReady,
2195
2269
  enrollment: {
@@ -2210,16 +2284,8 @@ async function bootstrap(flags) {
2210
2284
  if (!skill.ready)
2211
2285
  throw new Error(`Provider skill did not become ready: ${JSON.stringify(skill)}`);
2212
2286
  const policyPath = resolve(workspace, '.dharma', 'approved-policy.json');
2213
- const evidenceFlags = new Map([
2214
- ['workspace', workspace], ['provider', provider], ['policy', policyPath], ['maximum-sessions', '20'],
2215
- ]);
2216
- const preview = await evidencePreview(evidenceFlags);
2217
- let synchronized = { state: 'synchronized', captured: 0, synced: 0 };
2218
- if (Number(preview.trajectoryCount || 0) > 0
2219
- && preview.automaticDisclosure?.ready === true) {
2220
- evidenceFlags.set('sync', true);
2221
- synchronized = await synchronizeBootstrapEvidence(async () => await retryBootstrapOnboarding(async () => await capture(evidenceFlags, true)));
2222
- }
2287
+ const firstLearning = onboarded.firstLearningEvidence;
2288
+ const firstLearningReady = firstLearning?.state === 'synchronized' || firstLearning?.state === 'denied_disclosure';
2223
2289
  const relay = flags.has('no-relay-daemon')
2224
2290
  ? { started: false, ...(await waitForRelayReadiness()) }
2225
2291
  : await startRelayDaemon(policyPath);
@@ -2252,7 +2318,6 @@ async function bootstrap(flags) {
2252
2318
  runOrganizationCommand('skills', 'list', apiFlags),
2253
2319
  runOrganizationCommand('usage', 'list', apiFlags),
2254
2320
  ]);
2255
- requireCompletedBootstrapEvidence(synchronized, Number(preview.trajectoryCount || 0));
2256
2321
  const organizationApi = summarizeBootstrapOrganizationApi({
2257
2322
  organizationId,
2258
2323
  organization,
@@ -2275,12 +2340,12 @@ async function bootstrap(flags) {
2275
2340
  const repositoryReceipt = onboarded;
2276
2341
  const namedSessionReady = provider !== 'codex' || process.platform !== 'linux'
2277
2342
  || Boolean(namedSession?.ok === true && ['running', 'executing'].includes(String(namedSession.state)));
2278
- const firstLearning = repositoryReceipt.firstLearningEvidence;
2279
2343
  const role = repositoryReceipt.repositoryRole;
2280
2344
  return {
2281
- ok: repositoryReadiness?.outcome !== 'blocked' && namedSessionReady,
2282
- stage: sharedRepositoryReady ? namedSessionReady ? 'complete' : 'named_session_pending'
2345
+ ok: sharedRepositoryReady && namedSessionReady && firstLearningReady,
2346
+ stage: sharedRepositoryReady ? namedSessionReady ? firstLearningReady ? 'complete' : 'first_learning_pending' : 'named_session_pending'
2283
2347
  : repositoryReadiness?.outcome === 'blocked' ? 'shared_repository_blocked' : 'shared_repository_pending',
2348
+ repositorySelection,
2284
2349
  localStage: 'complete',
2285
2350
  sharedRepositoryReady,
2286
2351
  enrollment: {
@@ -2298,9 +2363,9 @@ async function bootstrap(flags) {
2298
2363
  provider,
2299
2364
  skill,
2300
2365
  evidence: {
2301
- discovered: Number(preview.trajectoryCount || 0),
2302
- synchronized,
2303
- automaticDisclosure: preview.automaticDisclosure,
2366
+ discovered: Number(firstLearning?.discovered || 0),
2367
+ synchronized: firstLearning,
2368
+ automaticDisclosure: { ready: firstLearning?.disclosureReady === true },
2304
2369
  },
2305
2370
  relayProbe: relay.probe,
2306
2371
  relay,
@@ -2880,10 +2945,18 @@ async function capture(flags, batch = false) {
2880
2945
  capsules.push(capsule);
2881
2946
  if (fabric) {
2882
2947
  vault.queueCapsuleSync(capsule.trajectoryId, capsule.revision);
2883
- await reserveDailyContentUpload(capsule, policy);
2884
- // An unknown delivery outcome retains the local advisory reservation.
2885
- // HQ performs the authoritative, idempotent organization/day charge.
2886
- syncResults.push(await fabric.syncTrajectory(capsule));
2948
+ try {
2949
+ await reserveDailyContentUpload(capsule, policy);
2950
+ // An unknown delivery outcome retains the local advisory reservation.
2951
+ // HQ performs the authoritative, idempotent organization/day charge.
2952
+ syncResults.push(await fabric.syncTrajectory(capsule));
2953
+ }
2954
+ catch (error) {
2955
+ if (isDefinitiveSecretDisclosureRejection(error)) {
2956
+ vault.discardPendingCapsuleSync(capsule.trajectoryId, capsule.revision, 'secret_disclosure_forbidden');
2957
+ }
2958
+ throw error;
2959
+ }
2887
2960
  vault.markCapsuleSynced(capsule.trajectoryId, capsule.revision);
2888
2961
  }
2889
2962
  }
@@ -4179,19 +4252,24 @@ async function onboard(flags) {
4179
4252
  controlBranch: boundRepository.controlBranch,
4180
4253
  policyRevision: authoritativeRevision,
4181
4254
  }));
4255
+ const canonicalPackage = registered.repositoryPackage;
4182
4256
  const onboardingProvider = providerIds?.[0] || 'codex';
4183
4257
  const onboardingEvidenceFlags = new Map([
4184
4258
  ['workspace', workspace], ['provider', onboardingProvider],
4185
4259
  ['policy', resolve(workspace, '.dharma', 'approved-policy.json')], ['maximum-sessions', '20'],
4186
4260
  ]);
4187
4261
  const onboardingEvidencePreview = await staged('first_learning_preview', async () => evidencePreview(onboardingEvidenceFlags));
4188
- let onboardingEvidence = { state: 'synchronized', captured: 0, synced: 0 };
4189
- if (Number(onboardingEvidencePreview.trajectoryCount || 0) > 0
4190
- && onboardingEvidencePreview.automaticDisclosure?.ready === true) {
4262
+ const discoveredTrajectories = Number(onboardingEvidencePreview.trajectoryCount || 0);
4263
+ const disclosureReady = onboardingEvidencePreview.automaticDisclosure?.ready === true;
4264
+ let onboardingEvidence = initialBootstrapEvidenceDisposition(discoveredTrajectories, disclosureReady);
4265
+ if (discoveredTrajectories > 0 && disclosureReady) {
4191
4266
  onboardingEvidenceFlags.set('sync', true);
4192
4267
  onboardingEvidence = await staged('first_learning_sync', async () => {
4193
4268
  const result = await synchronizeBootstrapEvidence(async () => await retryBootstrapOnboarding(async () => await capture(onboardingEvidenceFlags, true)));
4194
- requireCompletedBootstrapEvidence(result, Number(onboardingEvidencePreview.trajectoryCount || 0));
4269
+ if (publishedPackageCanDeferSecretEvidence(result, canonicalPackage, sourceAuthorization.generationId)) {
4270
+ return { ...result, state: 'denied_disclosure' };
4271
+ }
4272
+ requireCompletedBootstrapEvidence(result, discoveredTrajectories);
4195
4273
  return result;
4196
4274
  });
4197
4275
  }
@@ -4203,7 +4281,6 @@ async function onboard(flags) {
4203
4281
  const candidateScope = { organizationId, workspaceId: registered.workspaceId,
4204
4282
  repositoryBindingId: registered.repositoryBindingId, repositoryAgentId: registered.repositoryAgentId };
4205
4283
  const outboxRoot = resolve(dharmaHome(), 'relay', 'repository-candidates');
4206
- const canonicalPackage = registered.repositoryPackage;
4207
4284
  const refreshPolicy = repositoryPackageNeedsPolicyRefresh(canonicalPackage, sourceAuthorization.generationId);
4208
4285
  const retryBlockedInitial = canonicalPackage.state === 'blocked' && canonicalPackage.generation === 0
4209
4286
  && canonicalPackage.consolidationMode === 'initial_repository';
@@ -4309,8 +4386,8 @@ async function onboard(flags) {
4309
4386
  repositorySkill: installed,
4310
4387
  repositoryCandidate: candidate,
4311
4388
  firstLearningEvidence: {
4312
- discovered: Number(onboardingEvidencePreview.trajectoryCount || 0),
4313
- disclosureReady: onboardingEvidencePreview.automaticDisclosure?.ready === true,
4389
+ discovered: discoveredTrajectories,
4390
+ disclosureReady,
4314
4391
  ...onboardingEvidence,
4315
4392
  },
4316
4393
  repositoryRole: role,
@@ -6961,7 +7038,11 @@ export async function run(argv) {
6961
7038
  throw new Error(USAGE);
6962
7039
  }
6963
7040
  if (isDirectExecution(process.argv[1], import.meta.url)) {
6964
- run(process.argv.slice(2)).then(print).catch((error) => {
7041
+ const argv = process.argv.slice(2);
7042
+ run(argv).then((value) => {
7043
+ print(value);
7044
+ process.exitCode = commandExitCode(argv, value);
7045
+ }).catch((error) => {
6965
7046
  const message = error instanceof Error ? error.message : String(error);
6966
7047
  process.stderr.write(`${message}\n`);
6967
7048
  process.exitCode = 2;