@dharma-ai-labs/agent-fabric 0.2.183 → 0.2.185

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.
Files changed (36) hide show
  1. package/dist/codexSetupChildProvider.test.js +33 -2
  2. package/dist/codexSetupChildProvider.test.js.map +1 -1
  3. package/dist/codexSetupDiagnostic.d.ts.map +1 -1
  4. package/dist/codexSetupDiagnostic.js +3 -1
  5. package/dist/codexSetupDiagnostic.js.map +1 -1
  6. package/dist/codexSetupDiagnostic.test.js +16 -1
  7. package/dist/codexSetupDiagnostic.test.js.map +1 -1
  8. package/dist/codexSetupOwnedConnection.js +2 -2
  9. package/dist/codexSetupOwnedConnection.js.map +1 -1
  10. package/dist/codexSetupOwnedConnection.test.js +4 -2
  11. package/dist/codexSetupOwnedConnection.test.js.map +1 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +114 -94
  14. package/dist/index.js.map +1 -1
  15. package/dist/namedCodexFilesystem.d.ts +2 -0
  16. package/dist/namedCodexFilesystem.d.ts.map +1 -1
  17. package/dist/namedCodexFilesystem.js +8 -1
  18. package/dist/namedCodexFilesystem.js.map +1 -1
  19. package/dist/namedCodexFilesystem.test.js +46 -0
  20. package/dist/namedCodexFilesystem.test.js.map +1 -1
  21. package/dist/namedSessionStartupChild.d.ts +2 -10
  22. package/dist/namedSessionStartupChild.d.ts.map +1 -1
  23. package/dist/namedSessionStartupChild.js +4 -9
  24. package/dist/namedSessionStartupChild.js.map +1 -1
  25. package/dist/namedSessionStartupChild.test.js +15 -0
  26. package/dist/namedSessionStartupChild.test.js.map +1 -1
  27. package/dist/namedSessionStartupDiagnostic.d.ts +18 -0
  28. package/dist/namedSessionStartupDiagnostic.d.ts.map +1 -0
  29. package/dist/namedSessionStartupDiagnostic.js +45 -0
  30. package/dist/namedSessionStartupDiagnostic.js.map +1 -0
  31. package/dist/namedSessionStartupDiagnostic.test.d.ts +2 -0
  32. package/dist/namedSessionStartupDiagnostic.test.d.ts.map +1 -0
  33. package/dist/namedSessionStartupDiagnostic.test.js +43 -0
  34. package/dist/namedSessionStartupDiagnostic.test.js.map +1 -0
  35. package/dist/schemas/codex-setup-failure-diagnostic.schema.json +4 -1
  36. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -71,6 +71,7 @@ import { namedSessionPaths, namedSessionRequest, readNamedSession, saveNamedSess
71
71
  import { queueNamedSessionEvidence } from './namedSessionEvidence.js';
72
72
  import { waitForNamedSessionStartup } from './namedSessionStartup.js';
73
73
  import { observeNamedSessionStartupChild } from './namedSessionStartupChild.js';
74
+ import { namedSessionStartupPhaseError } from './namedSessionStartupDiagnostic.js';
74
75
  import { verifiedCliProcessArguments } from './cliProcessIdentity.js';
75
76
  import { retainNamedSessionRepositoryState } from './namedSessionRepositoryState.js';
76
77
  import { syncNamedSessionTaskExports } from './namedSessionTaskExportSync.js';
@@ -98,7 +99,7 @@ import { assertCodexSetupExecutionLease } from './codexSetupAdmission.js';
98
99
  import { parseLocalCodexSetupReadiness } from '@dharma-ai-labs/agent-fabric-local-vault/setup-readiness';
99
100
  import { writeBootstrapHostJson, writeBootstrapHostText } from './bootstrapHostFiles.js';
100
101
  export { openCooperativeInboxSession } from './cooperativeInboxSession.js';
101
- const VERSION = '0.2.183';
102
+ const VERSION = '0.2.185';
102
103
  const USAGE = CLI_USAGE;
103
104
  const execFileAsync = promisify(execFile);
104
105
  const LOCAL_PROVIDER_IDS = ['codex', 'claude', 'agy', 'hermes'];
@@ -5077,7 +5078,8 @@ async function namedSessionCommand(action, flags) {
5077
5078
  }
5078
5079
  }
5079
5080
  await mkdir(paths.root, { recursive: true, mode: 0o700 });
5080
- const vault = await openBootstrapVault({ root: resolve(dharmaHome(), 'vault') });
5081
+ const vault = await openBootstrapVault({ root: resolve(dharmaHome(), 'vault') })
5082
+ .catch(error => { throw namedSessionStartupPhaseError('vault', error); });
5081
5083
  let releaseLock;
5082
5084
  const controller = new AbortController(), stop = () => controller.abort();
5083
5085
  process.once('SIGTERM', stop);
@@ -5086,100 +5088,117 @@ async function namedSessionCommand(action, flags) {
5086
5088
  process.once('disconnect', stop);
5087
5089
  let transport;
5088
5090
  try {
5089
- releaseLock = await acquirePidLock(resolve(paths.root, 'service.lock'), 250, 'named_session_already_running');
5091
+ releaseLock = await acquirePidLock(resolve(paths.root, 'service.lock'), 250, 'named_session_already_running')
5092
+ .catch(error => { throw namedSessionStartupPhaseError('lock', error); });
5090
5093
  const startProvider = async (childScope) => {
5091
- const startupStep = (operation) => childScope ? childScope.step(operation) : operation();
5092
- if (childScope && existing && existing.identity.membershipId !== childScope.request.membershipId) {
5093
- throw new Error('setup_child_unavailable');
5094
- }
5095
- const fabric = await startupStep(() => AgentFabricClient.open({ configPath: configPath(), statePath: resolve(paths.root, 'protocol-state.json'),
5096
- hostScope: currentBootstrapHostScope() }));
5097
- if (fabric.config.organizationId !== config.organizationId || fabric.config.deviceId !== config.deviceId
5098
- || fabric.config.publicKeyEd25519 !== config.publicKeyEd25519)
5099
- throw new Error('named_session_trust_scope_mismatch');
5100
- const trust = createNamedSessionTrust({ configPath: configPath(), identity: fabric.config });
5101
- await startupStep(() => trust.refresh());
5102
- await startupStep(() => fabric.openSession(VERSION));
5103
- const policyPath = resolve(item.path, '.dharma', 'approved-policy.json');
5104
- const policy = await startupStep(() => refreshVerifiedWorkspacePolicyForTransmission(policyPath, workspaceId, fabric));
5105
- const writeRoots = policy.tasks.writePaths.filter(path => /^[a-zA-Z0-9_-]+\/\*\*$/.test(path))
5106
- .map(path => path.slice(0, -3));
5107
- if (!writeRoots.length)
5108
- throw new Error('named_session_workspace_write_not_authorized');
5109
- let registration = existing, pending;
5110
- let ownerReceipt;
5111
- // Resolve authenticated ownership before opening any provider transport.
5112
- if (registration) {
5113
- const response = await startupStep(() => fabric.signedPost('/agent-fabric/provider-sessions', {
5114
- schema: 'dharma.provider-session-registration/v1', action: 'inspect', provider: 'codex', mode: 'bridge_owned',
5115
- workspaceId, endpointId: scope.endpointId, repositoryBindingId: scope.repositoryBindingId,
5116
- bindingId: registration.bindingId, expectedRevision: 0, leaseSeconds: 60,
5117
- }));
5118
- if (!isNamedSessionReconnectReceipt(response, registration.bindingId, registration.identity)) {
5119
- throw new Error('named_session_registration_invalid');
5094
+ let startupPhase = 'client';
5095
+ try {
5096
+ const startupStep = (operation) => childScope ? childScope.step(operation) : operation();
5097
+ if (childScope && existing && existing.identity.membershipId !== childScope.request.membershipId) {
5098
+ throw new Error('setup_child_unavailable');
5120
5099
  }
5121
- ownerReceipt = response;
5122
- }
5123
- else {
5124
- const maximumCostCents = boundedInteger(flags.get('session-budget-cents'), 1000, 1, 10000, '--session-budget-cents');
5125
- const maximumTurnCostCents = boundedInteger(flags.get('turn-budget-cents'), 25, 1, maximumCostCents, '--turn-budget-cents');
5126
- const bindingId = randomUUID();
5127
- const response = await startupStep(() => fabric.signedPost('/agent-fabric/provider-sessions', {
5128
- schema: 'dharma.provider-session-registration/v1', action: 'attach', provider: 'codex', mode: 'bridge_owned',
5129
- workspaceId, endpointId: scope.endpointId, repositoryBindingId: scope.repositoryBindingId,
5130
- bindingId, expectedRevision: 0, leaseSeconds: 60,
5131
- }));
5132
- const remote = response.registration;
5133
- if (!remote || !UUID_PATTERN.test(String(remote.membershipId))
5134
- || childScope && remote.membershipId !== childScope.request.membershipId || remote.revision !== 1) {
5135
- throw new Error('named_session_registration_invalid');
5100
+ const fabric = await startupStep(() => AgentFabricClient.open({ configPath: configPath(), statePath: resolve(paths.root, 'protocol-state.json'),
5101
+ hostScope: currentBootstrapHostScope() }));
5102
+ if (fabric.config.organizationId !== config.organizationId || fabric.config.deviceId !== config.deviceId
5103
+ || fabric.config.publicKeyEd25519 !== config.publicKeyEd25519)
5104
+ throw new Error('named_session_trust_scope_mismatch');
5105
+ startupPhase = 'trust';
5106
+ const trust = createNamedSessionTrust({ configPath: configPath(), identity: fabric.config });
5107
+ await startupStep(() => trust.refresh());
5108
+ startupPhase = 'session';
5109
+ await startupStep(() => fabric.openSession(VERSION));
5110
+ startupPhase = 'policy';
5111
+ const policyPath = resolve(item.path, '.dharma', 'approved-policy.json');
5112
+ const policy = await startupStep(() => refreshVerifiedWorkspacePolicyForTransmission(policyPath, workspaceId, fabric));
5113
+ const writeRoots = policy.tasks.writePaths.filter(path => /^[a-zA-Z0-9_-]+\/\*\*$/.test(path))
5114
+ .map(path => path.slice(0, -3));
5115
+ if (!writeRoots.length)
5116
+ throw new Error('named_session_workspace_write_not_authorized');
5117
+ startupPhase = 'registration';
5118
+ let registration = existing, pending;
5119
+ let ownerReceipt;
5120
+ // Resolve authenticated ownership before opening any provider transport.
5121
+ if (registration) {
5122
+ const response = await startupStep(() => fabric.signedPost('/agent-fabric/provider-sessions', {
5123
+ schema: 'dharma.provider-session-registration/v1', action: 'inspect', provider: 'codex', mode: 'bridge_owned',
5124
+ workspaceId, endpointId: scope.endpointId, repositoryBindingId: scope.repositoryBindingId,
5125
+ bindingId: registration.bindingId, expectedRevision: 0, leaseSeconds: 60,
5126
+ }));
5127
+ if (!isNamedSessionReconnectReceipt(response, registration.bindingId, registration.identity)) {
5128
+ throw new Error('named_session_registration_invalid');
5129
+ }
5130
+ ownerReceipt = response;
5136
5131
  }
5137
- const identity = { organizationId: config.organizationId, repositoryBindingId: scope.repositoryBindingId,
5138
- workspaceId, endpointId: scope.endpointId, membershipId: String(remote.membershipId), deviceId: config.deviceId,
5139
- provider: 'codex' };
5140
- if (!isNamedSessionOwnerReceipt(response, bindingId, identity))
5141
- throw new Error('named_session_registration_invalid');
5142
- ownerReceipt = response;
5143
- pending = { schema: 'dharma.named-session/v1', name, bindingId, identity,
5144
- maximumCostCents, maximumTurnCostCents, enabled: true };
5145
- }
5146
- const environment = namedCodexEnvironment(process.env);
5147
- const privateRoots = [dharmaHome(), resolve(environment.CODEX_HOME || resolve(homedir(), '.codex'))];
5148
- if (environment.XDG_RUNTIME_DIR)
5149
- privateRoots.push(environment.XDG_RUNTIME_DIR);
5150
- if (environment.DBUS_SESSION_BUS_ADDRESS)
5151
- privateRoots.push(environment.DBUS_SESSION_BUS_ADDRESS.slice('unix:path='.length));
5152
- const filesystem = await startupStep(() => namedCodexFilesystem({ environment, workspace: item.path, privateRoots, writeRoots }));
5153
- await startupStep(async () => {
5154
- const expectedOwner = registration ?? pending;
5155
- const ownerConfirmed = registration
5156
- ? isNamedSessionReconnectReceipt(ownerReceipt, expectedOwner.bindingId, expectedOwner.identity)
5157
- : isNamedSessionOwnerReceipt(ownerReceipt, expectedOwner.bindingId, expectedOwner.identity);
5158
- if (!ownerConfirmed) {
5159
- throw new Error('named_session_registration_invalid');
5132
+ else {
5133
+ const maximumCostCents = boundedInteger(flags.get('session-budget-cents'), 1000, 1, 10000, '--session-budget-cents');
5134
+ const maximumTurnCostCents = boundedInteger(flags.get('turn-budget-cents'), 25, 1, maximumCostCents, '--turn-budget-cents');
5135
+ const bindingId = randomUUID();
5136
+ const response = await startupStep(() => fabric.signedPost('/agent-fabric/provider-sessions', {
5137
+ schema: 'dharma.provider-session-registration/v1', action: 'attach', provider: 'codex', mode: 'bridge_owned',
5138
+ workspaceId, endpointId: scope.endpointId, repositoryBindingId: scope.repositoryBindingId,
5139
+ bindingId, expectedRevision: 0, leaseSeconds: 60,
5140
+ }));
5141
+ const remote = response.registration;
5142
+ if (!remote || !UUID_PATTERN.test(String(remote.membershipId))
5143
+ || childScope && remote.membershipId !== childScope.request.membershipId || remote.revision !== 1) {
5144
+ throw new Error('named_session_registration_invalid');
5145
+ }
5146
+ const identity = { organizationId: config.organizationId, repositoryBindingId: scope.repositoryBindingId,
5147
+ workspaceId, endpointId: scope.endpointId, membershipId: String(remote.membershipId), deviceId: config.deviceId,
5148
+ provider: 'codex' };
5149
+ if (!isNamedSessionOwnerReceipt(response, bindingId, identity))
5150
+ throw new Error('named_session_registration_invalid');
5151
+ ownerReceipt = response;
5152
+ pending = { schema: 'dharma.named-session/v1', name, bindingId, identity,
5153
+ maximumCostCents, maximumTurnCostCents, enabled: true };
5160
5154
  }
5161
- transport = await openCodexAppServerTransport({ command: 'codex', cwd: item.path,
5162
- environment, experimentalApi: true, toolCallTimeoutMs: 60_000,
5163
- argv: [...namedCodexNodeOutputArguments(), '-c', 'allow_login_shell=false', '-c', 'default_permissions="dharma_bridge"',
5164
- '-c', `permissions.dharma_bridge.filesystem=${filesystem.peer}`,
5165
- '-c', 'permissions.dharma_bridge.network={enabled=false}',
5166
- '-c', `permissions.dharma_work.filesystem=${filesystem.work}`,
5167
- '-c', 'permissions.dharma_work.network={enabled=false}', 'app-server'] });
5168
- return transport;
5169
- });
5170
- const account = await startupStep(() => transport.request('account/read', { refreshToken: false }));
5171
- if (!account.account)
5172
- throw new Error('named_session_provider_authentication_required');
5173
- if (pending) {
5174
- const threadId = await startupStep(() => startNamedCodexThread(transport, item.path, name));
5175
- await startupStep(async () => vault.saveProviderSessionBinding({ schema: 'dharma.local-provider-session-binding/v1', ...pending.identity,
5176
- bindingId: pending.bindingId, owner: 'dharma_bridge', sessionId: threadId, workspaceRoot: item.path,
5177
- createdAt: new Date().toISOString(), expiresAt: new Date(Date.now() + 30 * 86400000).toISOString(),
5178
- maximumProviderCostCents: pending.maximumTurnCostCents }));
5179
- registration = pending;
5180
- await startupStep(async () => saveNamedSession(dharmaHome(), registration));
5181
- }
5182
- return { registration: registration, filesystem, fabric, trust, policyPath, writeRoots };
5155
+ startupPhase = 'environment';
5156
+ const environment = namedCodexEnvironment(process.env);
5157
+ const privateRoots = [dharmaHome(), resolve(environment.CODEX_HOME || resolve(homedir(), '.codex'))];
5158
+ if (environment.XDG_RUNTIME_DIR)
5159
+ privateRoots.push(environment.XDG_RUNTIME_DIR);
5160
+ if (environment.DBUS_SESSION_BUS_ADDRESS)
5161
+ privateRoots.push(environment.DBUS_SESSION_BUS_ADDRESS.slice('unix:path='.length));
5162
+ startupPhase = 'filesystem';
5163
+ const filesystem = await startupStep(() => namedCodexFilesystem({ environment, workspace: item.path, privateRoots, writeRoots }));
5164
+ startupPhase = 'transport';
5165
+ await startupStep(async () => {
5166
+ const expectedOwner = registration ?? pending;
5167
+ const ownerConfirmed = registration
5168
+ ? isNamedSessionReconnectReceipt(ownerReceipt, expectedOwner.bindingId, expectedOwner.identity)
5169
+ : isNamedSessionOwnerReceipt(ownerReceipt, expectedOwner.bindingId, expectedOwner.identity);
5170
+ if (!ownerConfirmed) {
5171
+ throw new Error('named_session_registration_invalid');
5172
+ }
5173
+ transport = await openCodexAppServerTransport({ command: filesystem.providerExecutable, cwd: item.path,
5174
+ environment: filesystem.environment, experimentalApi: true, toolCallTimeoutMs: 60_000,
5175
+ argv: [...namedCodexNodeOutputArguments(), '-c', 'allow_login_shell=false', '-c', 'default_permissions="dharma_bridge"',
5176
+ '-c', `permissions.dharma_bridge.filesystem=${filesystem.peer}`,
5177
+ '-c', 'permissions.dharma_bridge.network={enabled=false}',
5178
+ '-c', `permissions.dharma_work.filesystem=${filesystem.work}`,
5179
+ '-c', 'permissions.dharma_work.network={enabled=false}', 'app-server'] });
5180
+ return transport;
5181
+ });
5182
+ startupPhase = 'account';
5183
+ const account = await startupStep(() => transport.request('account/read', { refreshToken: false }));
5184
+ if (!account.account)
5185
+ throw new Error('named_session_provider_authentication_required');
5186
+ if (pending) {
5187
+ startupPhase = 'thread';
5188
+ const threadId = await startupStep(() => startNamedCodexThread(transport, item.path, name));
5189
+ startupPhase = 'binding';
5190
+ await startupStep(async () => vault.saveProviderSessionBinding({ schema: 'dharma.local-provider-session-binding/v1', ...pending.identity,
5191
+ bindingId: pending.bindingId, owner: 'dharma_bridge', sessionId: threadId, workspaceRoot: item.path,
5192
+ createdAt: new Date().toISOString(), expiresAt: new Date(Date.now() + 30 * 86400000).toISOString(),
5193
+ maximumProviderCostCents: pending.maximumTurnCostCents }));
5194
+ registration = pending;
5195
+ await startupStep(async () => saveNamedSession(dharmaHome(), registration));
5196
+ }
5197
+ return { registration: registration, filesystem, fabric, trust, policyPath, writeRoots };
5198
+ }
5199
+ catch (error) {
5200
+ throw namedSessionStartupPhaseError(startupPhase, error);
5201
+ }
5183
5202
  };
5184
5203
  const { registration, filesystem, fabric, trust, policyPath, writeRoots } = childMessage
5185
5204
  ? await runCodexSetupChildStartup({ vault: vault, message: childMessage, name, workspaceId,
@@ -5198,7 +5217,7 @@ async function namedSessionCommand(action, flags) {
5198
5217
  };
5199
5218
  const refreshLifetime = () => renewNamedSessionLifetime({ vault, bindingId: registration.bindingId,
5200
5219
  identity: registration.identity, trust, authorize: authorizeLifetime });
5201
- await refreshLifetime();
5220
+ await refreshLifetime().catch(error => { throw namedSessionStartupPhaseError('lifetime', error); });
5202
5221
  const channelTransport = { async signedPost(route, body) {
5203
5222
  await trust.refresh();
5204
5223
  const response = await fabric.signedPost(route, body);
@@ -5323,7 +5342,8 @@ async function namedSessionCommand(action, flags) {
5323
5342
  return false; consumed.add(id); return true; } },
5324
5343
  authorizeContent: createNamedPeerContentAuthorization({ scope: repositoryRoleScope(item), diagnostics: true,
5325
5344
  loadCurrentPolicy: () => refreshVerifiedWorkspacePolicyForTransmission(policyPath, workspaceId, fabric),
5326
- loadCurrentSourceAuthorization: () => fetchRepositorySourceAuthorization(fabric, repositoryRoleScope(item)) }) });
5345
+ loadCurrentSourceAuthorization: () => fetchRepositorySourceAuthorization(fabric, repositoryRoleScope(item)) }) })
5346
+ .catch(error => { throw namedSessionStartupPhaseError('service', error); });
5327
5347
  }
5328
5348
  finally {
5329
5349
  try {