@dharma-ai-labs/agent-fabric 0.2.110 → 0.2.112
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/AGENT_FABRIC_ONBOARDING.md +13 -0
- package/dist/codexBoundSession.d.ts +30 -5
- package/dist/codexBoundSession.d.ts.map +1 -1
- package/dist/codexBoundSession.js +49 -2
- package/dist/codexBoundSession.js.map +1 -1
- package/dist/codexBoundSession.test.js +17 -0
- package/dist/codexBoundSession.test.js.map +1 -1
- package/dist/codexInboxSession.d.ts +50 -0
- package/dist/codexInboxSession.d.ts.map +1 -1
- package/dist/codexInboxSession.js +32 -3
- package/dist/codexInboxSession.js.map +1 -1
- package/dist/codexPeerTools.d.ts +62 -0
- package/dist/codexPeerTools.d.ts.map +1 -0
- package/dist/codexPeerTools.js +53 -0
- package/dist/codexPeerTools.js.map +1 -0
- package/dist/codexPeerTools.test.d.ts +2 -0
- package/dist/codexPeerTools.test.d.ts.map +1 -0
- package/dist/codexPeerTools.test.js +77 -0
- package/dist/codexPeerTools.test.js.map +1 -0
- package/dist/demoDeviceTransport.d.ts +33 -0
- package/dist/demoDeviceTransport.d.ts.map +1 -0
- package/dist/demoDeviceTransport.js +52 -0
- package/dist/demoDeviceTransport.js.map +1 -0
- package/dist/demoDeviceTransport.test.d.ts +2 -0
- package/dist/demoDeviceTransport.test.d.ts.map +1 -0
- package/dist/demoDeviceTransport.test.js +178 -0
- package/dist/demoDeviceTransport.test.js.map +1 -0
- package/dist/demoTransportCommand.test.d.ts +2 -0
- package/dist/demoTransportCommand.test.d.ts.map +1 -0
- package/dist/demoTransportCommand.test.js +28 -0
- package/dist/demoTransportCommand.test.js.map +1 -0
- package/dist/demoTransportContinuity.d.ts +39 -0
- package/dist/demoTransportContinuity.d.ts.map +1 -0
- package/dist/demoTransportContinuity.js +82 -0
- package/dist/demoTransportContinuity.js.map +1 -0
- package/dist/demoTransportContinuity.test.d.ts +2 -0
- package/dist/demoTransportContinuity.test.d.ts.map +1 -0
- package/dist/demoTransportContinuity.test.js +145 -0
- package/dist/demoTransportContinuity.test.js.map +1 -0
- package/dist/demoTransportFetch.d.ts +12 -0
- package/dist/demoTransportFetch.d.ts.map +1 -0
- package/dist/demoTransportFetch.js +109 -0
- package/dist/demoTransportFetch.js.map +1 -0
- package/dist/demoTransportFetch.test.d.ts +2 -0
- package/dist/demoTransportFetch.test.d.ts.map +1 -0
- package/dist/demoTransportFetch.test.js +150 -0
- package/dist/demoTransportFetch.test.js.map +1 -0
- package/dist/demoTransportRequest.d.ts +35 -0
- package/dist/demoTransportRequest.d.ts.map +1 -0
- package/dist/demoTransportRequest.js +193 -0
- package/dist/demoTransportRequest.js.map +1 -0
- package/dist/demoTransportRequest.test.d.ts +2 -0
- package/dist/demoTransportRequest.test.d.ts.map +1 -0
- package/dist/demoTransportRequest.test.js +203 -0
- package/dist/demoTransportRequest.test.js.map +1 -0
- package/dist/demoTransportState.d.ts +31 -0
- package/dist/demoTransportState.d.ts.map +1 -0
- package/dist/demoTransportState.js +204 -0
- package/dist/demoTransportState.js.map +1 -0
- package/dist/demoTransportState.test.d.ts +2 -0
- package/dist/demoTransportState.test.d.ts.map +1 -0
- package/dist/demoTransportState.test.js +192 -0
- package/dist/demoTransportState.test.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +299 -17
- package/dist/index.js.map +1 -1
- package/dist/namedCodexThread.d.ts +3 -0
- package/dist/namedCodexThread.d.ts.map +1 -0
- package/dist/namedCodexThread.js +22 -0
- package/dist/namedCodexThread.js.map +1 -0
- package/dist/namedCodexThread.test.d.ts +2 -0
- package/dist/namedCodexThread.test.d.ts.map +1 -0
- package/dist/namedCodexThread.test.js +63 -0
- package/dist/namedCodexThread.test.js.map +1 -0
- package/dist/namedSessionBudget.d.ts +21 -0
- package/dist/namedSessionBudget.d.ts.map +1 -0
- package/dist/namedSessionBudget.js +70 -0
- package/dist/namedSessionBudget.js.map +1 -0
- package/dist/namedSessionBudget.test.d.ts +2 -0
- package/dist/namedSessionBudget.test.d.ts.map +1 -0
- package/dist/namedSessionBudget.test.js +40 -0
- package/dist/namedSessionBudget.test.js.map +1 -0
- package/dist/namedSessionService.d.ts +39 -0
- package/dist/namedSessionService.d.ts.map +1 -0
- package/dist/namedSessionService.js +269 -0
- package/dist/namedSessionService.js.map +1 -0
- package/dist/namedSessionService.test.d.ts +2 -0
- package/dist/namedSessionService.test.d.ts.map +1 -0
- package/dist/namedSessionService.test.js +158 -0
- package/dist/namedSessionService.test.js.map +1 -0
- package/dist/namedSessionTrust.d.ts +21 -0
- package/dist/namedSessionTrust.d.ts.map +1 -0
- package/dist/namedSessionTrust.js +64 -0
- package/dist/namedSessionTrust.js.map +1 -0
- package/dist/namedSessionTrust.test.d.ts +2 -0
- package/dist/namedSessionTrust.test.d.ts.map +1 -0
- package/dist/namedSessionTrust.test.js +178 -0
- package/dist/namedSessionTrust.test.js.map +1 -0
- package/dist/onboardingStage.d.ts +1 -1
- package/dist/onboardingStage.d.ts.map +1 -1
- package/dist/onboardingStage.js.map +1 -1
- package/dist/providerSessionChannel.d.ts +1 -0
- package/dist/providerSessionChannel.d.ts.map +1 -1
- package/dist/providerSessionChannel.js +5 -1
- package/dist/providerSessionChannel.js.map +1 -1
- package/dist/providerSessionChannel.test.js +10 -3
- package/dist/providerSessionChannel.test.js.map +1 -1
- package/dist/relaySupervisor.test.js +3 -1
- package/dist/relaySupervisor.test.js.map +1 -1
- package/dist/repositoryOnboardingCaller.test.js +19 -0
- package/dist/repositoryOnboardingCaller.test.js.map +1 -1
- package/dist/schemas/demo-transport-continuity.schema.json +27 -0
- package/dist/schemas/demo-transport-journal.schema.json +13 -0
- package/dist/schemas/demo-transport-request.schema.json +27 -0
- package/dist/schemas/demo-transport-state.schema.json +13 -0
- package/dist/usage.d.ts +1 -1
- package/dist/usage.d.ts.map +1 -1
- package/dist/usage.js +1 -1
- package/dist/usage.js.map +1 -1
- package/dist/usage.test.d.ts +2 -0
- package/dist/usage.test.d.ts.map +1 -0
- package/dist/usage.test.js +18 -0
- package/dist/usage.test.js.map +1 -0
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -41,13 +41,18 @@ import { demoRepositoryPackage } from './demoPackage.js';
|
|
|
41
41
|
import { demoDeviceAndPackageStatus } from './demoStatus.js';
|
|
42
42
|
import { prepareDemoSigningUpgradeProof, readDemoSigningUpgradeContext } from './demoSigningUpgradeProof.js';
|
|
43
43
|
import { submitDemoSigningUpgradeProof } from './demoSigningUpgradeSubmission.js';
|
|
44
|
+
import { createDemoDeviceTransport } from './demoDeviceTransport.js';
|
|
44
45
|
import { runDemoWatch } from './demoWatch.js';
|
|
45
46
|
import { runDemoSupervisor } from './demoSupervisor.js';
|
|
46
47
|
import { demoWatchRegistrationKey, listDemoWatchRegistrations } from './demoWatchRegistry.js';
|
|
47
48
|
import { createDemoWatchHealthRecorder } from './demoWatchHealth.js';
|
|
48
49
|
import { demoWatchStatus, disableDemoWatch, enableDemoWatch, inspectDemoWatchSupervisor } from './demoWatchControl.js';
|
|
50
|
+
import { namedSessionPaths, namedSessionRequest, readNamedSession, saveNamedSession, runNamedSessionService } from './namedSessionService.js';
|
|
51
|
+
import { openCodexAppServerTransport } from '@dharma-ai-labs/agent-fabric-provider-adapters/experimental/codex-transport';
|
|
52
|
+
import { createNamedSessionTrust, isNamedSessionOwnerReceipt, renewNamedSessionLifetime } from './namedSessionTrust.js';
|
|
53
|
+
import { startNamedCodexThread } from './namedCodexThread.js';
|
|
49
54
|
export { openCooperativeInboxSession } from './cooperativeInboxSession.js';
|
|
50
|
-
const VERSION = '0.2.
|
|
55
|
+
const VERSION = '0.2.112';
|
|
51
56
|
const USAGE = CLI_USAGE;
|
|
52
57
|
const execFileAsync = promisify(execFile);
|
|
53
58
|
const LOCAL_PROVIDER_IDS = ['codex', 'claude', 'agy', 'hermes'];
|
|
@@ -1606,14 +1611,15 @@ async function supervisedDemoCycle(registration, signal) {
|
|
|
1606
1611
|
const nativeDirectory = registration.provider === 'codex' ? resolve(workspace, '.agents', 'skills')
|
|
1607
1612
|
: registration.provider === 'claude' ? resolve(workspace, '.claude', 'skills')
|
|
1608
1613
|
: nativeSkillDirectory(registration.provider);
|
|
1609
|
-
return withDemoDeviceLock(scope, () => {
|
|
1614
|
+
return withDemoDeviceLock(scope, async () => {
|
|
1610
1615
|
signal.throwIfAborted();
|
|
1611
|
-
|
|
1612
|
-
nativeSkillDirectory: nativeDirectory }, { fetcher: (url, options) => {
|
|
1616
|
+
const deps = await createDemoDeviceTransport(scope, { fetcher: (url, options) => {
|
|
1613
1617
|
signal.throwIfAborted();
|
|
1614
1618
|
return fetch(url, { ...options,
|
|
1615
1619
|
signal: options?.signal ? AbortSignal.any([options.signal, signal]) : signal });
|
|
1616
1620
|
} });
|
|
1621
|
+
return demoRepositoryPackage({ scope, workspace, provider: registration.provider,
|
|
1622
|
+
nativeSkillDirectory: nativeDirectory }, deps);
|
|
1617
1623
|
});
|
|
1618
1624
|
}
|
|
1619
1625
|
async function relaySupervise(flags) {
|
|
@@ -1665,7 +1671,9 @@ async function relaySupervise(flags) {
|
|
|
1665
1671
|
}),
|
|
1666
1672
|
}) : Promise.resolve({ restarts: 0 });
|
|
1667
1673
|
services.push(standard);
|
|
1668
|
-
const
|
|
1674
|
+
const sessions = policyPath ? superviseNamedSessions(controller.signal) : Promise.resolve();
|
|
1675
|
+
services.push(sessions);
|
|
1676
|
+
const [demoResult, standardResult] = await Promise.all([demo, standard, sessions]);
|
|
1669
1677
|
return { ok: true, stopped: true, ...standardResult, demo: demoResult };
|
|
1670
1678
|
}
|
|
1671
1679
|
finally {
|
|
@@ -1681,6 +1689,42 @@ async function relaySupervise(flags) {
|
|
|
1681
1689
|
await releaseLease();
|
|
1682
1690
|
}
|
|
1683
1691
|
}
|
|
1692
|
+
async function superviseNamedSessions(signal) {
|
|
1693
|
+
if (process.platform !== 'linux')
|
|
1694
|
+
return;
|
|
1695
|
+
while (!signal.aborted) {
|
|
1696
|
+
const root = resolve(dharmaHome(), 'sessions');
|
|
1697
|
+
const entries = await readdir(root, { withFileTypes: true }).catch(error => {
|
|
1698
|
+
if (error.code === 'ENOENT')
|
|
1699
|
+
return [];
|
|
1700
|
+
throw error;
|
|
1701
|
+
});
|
|
1702
|
+
for (const entry of entries.slice(0, 50)) {
|
|
1703
|
+
if (signal.aborted)
|
|
1704
|
+
break;
|
|
1705
|
+
if (!entry.isDirectory() || !/^[a-z][a-z0-9-]{0,47}$/.test(entry.name))
|
|
1706
|
+
continue;
|
|
1707
|
+
try {
|
|
1708
|
+
const registration = await readNamedSession(dharmaHome(), entry.name);
|
|
1709
|
+
if (!registration?.enabled)
|
|
1710
|
+
continue;
|
|
1711
|
+
await namedSessionCommand('start', new Map([
|
|
1712
|
+
['name', registration.name], ['workspace-id', registration.identity.workspaceId], ['apply', true],
|
|
1713
|
+
]));
|
|
1714
|
+
}
|
|
1715
|
+
catch {
|
|
1716
|
+
process.stderr.write(`${JSON.stringify({ event: 'named_session_reconnect_pending', name: entry.name })}\n`);
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
await new Promise(resolveWait => {
|
|
1720
|
+
const finish = () => { clearTimeout(timer); signal.removeEventListener('abort', finish); resolveWait(); };
|
|
1721
|
+
const timer = setTimeout(finish, 10_000);
|
|
1722
|
+
signal.addEventListener('abort', finish, { once: true });
|
|
1723
|
+
if (signal.aborted)
|
|
1724
|
+
finish();
|
|
1725
|
+
});
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1684
1728
|
async function relayStop() {
|
|
1685
1729
|
const home = dharmaHome();
|
|
1686
1730
|
const supervisor = await relaySupervisorProcessState(home);
|
|
@@ -2033,12 +2077,22 @@ async function bootstrap(flags) {
|
|
|
2033
2077
|
usage,
|
|
2034
2078
|
});
|
|
2035
2079
|
const operatingContract = await loadAgentFabricOnboardingContract();
|
|
2080
|
+
const namedSession = provider === 'codex' && process.platform === 'linux' && sharedRepositoryReady
|
|
2081
|
+
? await withOnboardingStage('named_session', String(onboarded.workspaceId), `dharma bootstrap --resume --complete --portal-url ${hqUrl} --organization-id ${organizationId} --workspace . --policy-revision ${policyRevision}`, () => namedSessionCommand('start', new Map([
|
|
2082
|
+
['name', String(flags.get('session-name') || `codex-${String(onboarded.workspaceId).slice(0, 8)}`)],
|
|
2083
|
+
['workspace-id', String(onboarded.workspaceId)], ['apply', true],
|
|
2084
|
+
...(typeof flags.get('session-budget-cents') === 'string'
|
|
2085
|
+
? [['session-budget-cents', String(flags.get('session-budget-cents'))]] : []),
|
|
2086
|
+
])))
|
|
2087
|
+
: null;
|
|
2036
2088
|
const repositoryReceipt = onboarded;
|
|
2089
|
+
const namedSessionReady = provider !== 'codex' || process.platform !== 'linux'
|
|
2090
|
+
|| Boolean(namedSession?.ok === true && ['running', 'executing'].includes(String(namedSession.state)));
|
|
2037
2091
|
const firstLearning = repositoryReceipt.firstLearningEvidence;
|
|
2038
2092
|
const role = repositoryReceipt.repositoryRole;
|
|
2039
2093
|
return {
|
|
2040
|
-
ok: repositoryReadiness?.outcome !== 'blocked',
|
|
2041
|
-
stage: sharedRepositoryReady ? 'complete'
|
|
2094
|
+
ok: repositoryReadiness?.outcome !== 'blocked' && namedSessionReady,
|
|
2095
|
+
stage: sharedRepositoryReady ? namedSessionReady ? 'complete' : 'named_session_pending'
|
|
2042
2096
|
: repositoryReadiness?.outcome === 'blocked' ? 'shared_repository_blocked' : 'shared_repository_pending',
|
|
2043
2097
|
localStage: 'complete',
|
|
2044
2098
|
sharedRepositoryReady,
|
|
@@ -2063,6 +2117,7 @@ async function bootstrap(flags) {
|
|
|
2063
2117
|
},
|
|
2064
2118
|
relayProbe: relay.probe,
|
|
2065
2119
|
relay,
|
|
2120
|
+
namedSession,
|
|
2066
2121
|
repositoryReadiness,
|
|
2067
2122
|
organizationApi,
|
|
2068
2123
|
operatingContract: {
|
|
@@ -2077,6 +2132,8 @@ async function bootstrap(flags) {
|
|
|
2077
2132
|
? Number(firstLearning.discovered || 0) > 0 ? 'synchronized' : 'no_eligible_history'
|
|
2078
2133
|
: String(firstLearning?.state || 'pending'),
|
|
2079
2134
|
synchronization: relay.state === 'running' ? 'running' : 'pending',
|
|
2135
|
+
namedSession: namedSession && namedSessionReady ? 'ready'
|
|
2136
|
+
: provider === 'codex' && process.platform === 'linux' ? 'pending' : 'not_supported',
|
|
2080
2137
|
},
|
|
2081
2138
|
};
|
|
2082
2139
|
}
|
|
@@ -3159,6 +3216,17 @@ async function repositoryRoleCommand(action, flags) {
|
|
|
3159
3216
|
if (!item)
|
|
3160
3217
|
throw new Error('Repository role workspace is not registered locally.');
|
|
3161
3218
|
const scope = repositoryRoleScope(item), transport = await client();
|
|
3219
|
+
if (['ask', 'reply'].includes(action) && flags.has('session-name')) {
|
|
3220
|
+
const name = required(flags, 'session-name');
|
|
3221
|
+
const registered = await readNamedSession(dharmaHome(), name);
|
|
3222
|
+
if (!registered || registered.identity.workspaceId !== workspaceId)
|
|
3223
|
+
throw new Error('named_session_scope_mismatch');
|
|
3224
|
+
return namedSessionRequest(dharmaHome(), name, action === 'ask' ? {
|
|
3225
|
+
action: 'ask', targetBindingId: required(flags, 'target-session-binding-id'),
|
|
3226
|
+
taskId: required(flags, 'task-id'), category: required(flags, 'category'), question: required(flags, 'question'),
|
|
3227
|
+
} : { action: 'read', questionId: required(flags, 'question-id'),
|
|
3228
|
+
taskId: required(flags, 'task-id'), targetBindingId: required(flags, 'target-session-binding-id') });
|
|
3229
|
+
}
|
|
3162
3230
|
if (action === 'register') {
|
|
3163
3231
|
const categories = required(flags, 'question-categories').split(',').map(value => value.trim()).filter(Boolean);
|
|
3164
3232
|
return registerRepositoryRoleMetadata(transport, scope, { expectedRevision: Number(flags.get('expected-revision') || 0),
|
|
@@ -3175,6 +3243,201 @@ async function repositoryRoleCommand(action, flags) {
|
|
|
3175
3243
|
targetEndpointId: typeof flags.get('target-endpoint-id') === 'string' ? String(flags.get('target-endpoint-id')) : undefined });
|
|
3176
3244
|
return readRepositoryRoleReply({ transport, scope, questionId: required(flags, 'question-id') });
|
|
3177
3245
|
}
|
|
3246
|
+
async function namedSessionCommand(action, flags) {
|
|
3247
|
+
const name = String(flags.get('name') || 'codex');
|
|
3248
|
+
const paths = namedSessionPaths(dharmaHome(), name);
|
|
3249
|
+
if (process.platform !== 'linux')
|
|
3250
|
+
throw new Error('codex_session_sandbox_unqualified');
|
|
3251
|
+
const workspaceId = required(flags, 'workspace-id');
|
|
3252
|
+
const item = (await registry()).find(row => row.workspaceId === workspaceId);
|
|
3253
|
+
const config = await readDeviceConfig();
|
|
3254
|
+
if (!item || !config || item.organizationId !== config.organizationId)
|
|
3255
|
+
throw new Error('named_session_scope_mismatch');
|
|
3256
|
+
const scope = repositoryRoleScope(item);
|
|
3257
|
+
const existing = await readNamedSession(dharmaHome(), name);
|
|
3258
|
+
if (existing && (existing.identity.workspaceId !== item.workspaceId
|
|
3259
|
+
|| existing.identity.organizationId !== config.organizationId || existing.identity.deviceId !== config.deviceId
|
|
3260
|
+
|| existing.identity.endpointId !== scope.endpointId || existing.identity.repositoryBindingId !== scope.repositoryBindingId)) {
|
|
3261
|
+
throw new Error('named_session_scope_mismatch');
|
|
3262
|
+
}
|
|
3263
|
+
if (action === 'status') {
|
|
3264
|
+
try {
|
|
3265
|
+
return await namedSessionRequest(dharmaHome(), name, { action: 'status' });
|
|
3266
|
+
}
|
|
3267
|
+
catch {
|
|
3268
|
+
return { ok: true, name, state: 'stopped', registered: Boolean(existing), enabled: existing?.enabled ?? false,
|
|
3269
|
+
bindingId: existing?.bindingId ?? null };
|
|
3270
|
+
}
|
|
3271
|
+
}
|
|
3272
|
+
if (action === 'work')
|
|
3273
|
+
return namedSessionRequest(dharmaHome(), name, { action: 'work',
|
|
3274
|
+
workId: String(flags.get('work-id') || randomUUID()), prompt: required(flags, 'prompt') });
|
|
3275
|
+
if (action === 'stop') {
|
|
3276
|
+
if (!flags.has('apply'))
|
|
3277
|
+
return { ok: true, planned: true, name, action: 'stop' };
|
|
3278
|
+
if (existing)
|
|
3279
|
+
await saveNamedSession(dharmaHome(), { ...existing, enabled: false });
|
|
3280
|
+
try {
|
|
3281
|
+
return await namedSessionRequest(dharmaHome(), name, { action: 'stop' });
|
|
3282
|
+
}
|
|
3283
|
+
catch {
|
|
3284
|
+
return { ok: true, name, state: 'stopped', enabled: false };
|
|
3285
|
+
}
|
|
3286
|
+
}
|
|
3287
|
+
if (!['start', 'serve'].includes(action))
|
|
3288
|
+
throw new Error('named_session_action_invalid');
|
|
3289
|
+
if (!flags.has('apply'))
|
|
3290
|
+
return { ok: true, planned: true, name, workspaceId,
|
|
3291
|
+
provider: 'codex', localWork: 'workspace_write', peerQuestions: 'read_only', network: 'deny' };
|
|
3292
|
+
if (!await repositorySharedReady(item))
|
|
3293
|
+
throw new Error('named_session_repository_package_pending');
|
|
3294
|
+
if (action === 'start') {
|
|
3295
|
+
if (existing && !existing.enabled)
|
|
3296
|
+
await saveNamedSession(dharmaHome(), { ...existing, enabled: true });
|
|
3297
|
+
try {
|
|
3298
|
+
return await namedSessionRequest(dharmaHome(), name, { action: 'status' });
|
|
3299
|
+
}
|
|
3300
|
+
catch { /* Start an owned worker below. */ }
|
|
3301
|
+
const args = [fileURLToPath(import.meta.url), 'sessions', 'serve', '--name', name,
|
|
3302
|
+
'--workspace-id', workspaceId, '--apply'];
|
|
3303
|
+
for (const key of ['session-budget-cents', 'turn-budget-cents']) {
|
|
3304
|
+
if (flags.has(key))
|
|
3305
|
+
args.push(`--${key}`, required(flags, key));
|
|
3306
|
+
}
|
|
3307
|
+
const child = spawn(process.execPath, args, { cwd: item.path, detached: true, stdio: 'ignore', env: process.env });
|
|
3308
|
+
child.on('error', () => { });
|
|
3309
|
+
child.unref();
|
|
3310
|
+
for (let attempt = 0; attempt < 80; attempt++) {
|
|
3311
|
+
await new Promise(resolveWait => setTimeout(resolveWait, 250));
|
|
3312
|
+
try {
|
|
3313
|
+
return await namedSessionRequest(dharmaHome(), name, { action: 'status' });
|
|
3314
|
+
}
|
|
3315
|
+
catch { /* Await service initialization. */ }
|
|
3316
|
+
}
|
|
3317
|
+
throw new Error('named_session_startup_failed');
|
|
3318
|
+
}
|
|
3319
|
+
await mkdir(paths.root, { recursive: true, mode: 0o700 });
|
|
3320
|
+
const releaseLock = await acquirePidLock(resolve(paths.root, 'service.lock'), 250, 'named_session_already_running');
|
|
3321
|
+
const { LocalVault, loadOrCreateVaultMasterKey } = await loadVaultModule();
|
|
3322
|
+
const vault = await LocalVault.open({ root: resolve(dharmaHome(), 'vault'), masterKey: await loadOrCreateVaultMasterKey() });
|
|
3323
|
+
const controller = new AbortController(), stop = () => controller.abort();
|
|
3324
|
+
process.once('SIGTERM', stop);
|
|
3325
|
+
process.once('SIGINT', stop);
|
|
3326
|
+
let transport;
|
|
3327
|
+
try {
|
|
3328
|
+
const fabric = await AgentFabricClient.open({ configPath: configPath(), statePath: resolve(paths.root, 'protocol-state.json') });
|
|
3329
|
+
if (fabric.config.organizationId !== config.organizationId || fabric.config.deviceId !== config.deviceId
|
|
3330
|
+
|| fabric.config.publicKeyEd25519 !== config.publicKeyEd25519)
|
|
3331
|
+
throw new Error('named_session_trust_scope_mismatch');
|
|
3332
|
+
const trust = createNamedSessionTrust({ configPath: configPath(), identity: fabric.config });
|
|
3333
|
+
await trust.refresh();
|
|
3334
|
+
await fabric.openSession(VERSION);
|
|
3335
|
+
const policyPath = resolve(item.path, '.dharma', 'approved-policy.json');
|
|
3336
|
+
const policy = await refreshVerifiedWorkspacePolicyForTransmission(policyPath, workspaceId, fabric);
|
|
3337
|
+
const writeRoots = policy.tasks.writePaths.filter(path => /^[a-zA-Z0-9_-]+\/\*\*$/.test(path))
|
|
3338
|
+
.map(path => path.slice(0, -3));
|
|
3339
|
+
if (!writeRoots.length)
|
|
3340
|
+
throw new Error('named_session_workspace_write_not_authorized');
|
|
3341
|
+
const workRoots = ['"."="read"', ...writeRoots.map(root => `${JSON.stringify(root)}="write"`)].join(',');
|
|
3342
|
+
transport = await openCodexAppServerTransport({ command: 'codex', cwd: item.path,
|
|
3343
|
+
environment: providerProcessEnvironment(process.env), experimentalApi: true,
|
|
3344
|
+
argv: ['-c', 'default_permissions="dharma_bridge"',
|
|
3345
|
+
'-c', 'permissions.dharma_bridge.filesystem={":minimal"="read",":workspace_roots"={"."="read"}}',
|
|
3346
|
+
'-c', 'permissions.dharma_bridge.network={enabled=false}',
|
|
3347
|
+
'-c', `permissions.dharma_work.filesystem={":minimal"="read",":workspace_roots"={${workRoots}}}`,
|
|
3348
|
+
'-c', 'permissions.dharma_work.network={enabled=false}', 'app-server'] });
|
|
3349
|
+
const account = await transport.request('account/read', { refreshToken: false });
|
|
3350
|
+
if (!account.account)
|
|
3351
|
+
throw new Error('named_session_provider_authentication_required');
|
|
3352
|
+
let registration = existing;
|
|
3353
|
+
if (!registration) {
|
|
3354
|
+
const maximumCostCents = boundedInteger(flags.get('session-budget-cents'), 1000, 1, 10000, '--session-budget-cents');
|
|
3355
|
+
const maximumTurnCostCents = boundedInteger(flags.get('turn-budget-cents'), 25, 1, maximumCostCents, '--turn-budget-cents');
|
|
3356
|
+
const threadId = await startNamedCodexThread(transport, item.path, name);
|
|
3357
|
+
const bindingId = randomUUID();
|
|
3358
|
+
const response = await fabric.signedPost('/agent-fabric/provider-sessions', {
|
|
3359
|
+
schema: 'dharma.provider-session-registration/v1', action: 'attach', provider: 'codex', mode: 'bridge_owned',
|
|
3360
|
+
workspaceId, endpointId: scope.endpointId, repositoryBindingId: scope.repositoryBindingId,
|
|
3361
|
+
bindingId, expectedRevision: 0, leaseSeconds: 60,
|
|
3362
|
+
});
|
|
3363
|
+
const remote = response.registration;
|
|
3364
|
+
if (response.ok !== true || response.organizationId !== config.organizationId || !remote
|
|
3365
|
+
|| remote.bindingId !== bindingId || remote.workspaceId !== workspaceId || remote.endpointId !== scope.endpointId
|
|
3366
|
+
|| remote.repositoryBindingId !== scope.repositoryBindingId || remote.deviceId !== config.deviceId
|
|
3367
|
+
|| !UUID_PATTERN.test(String(remote.membershipId)) || remote.provider !== 'codex'
|
|
3368
|
+
|| remote.mode !== 'bridge_owned' || remote.state !== 'attached' || remote.revision !== 1) {
|
|
3369
|
+
throw new Error('named_session_registration_invalid');
|
|
3370
|
+
}
|
|
3371
|
+
const identity = { organizationId: config.organizationId, repositoryBindingId: scope.repositoryBindingId,
|
|
3372
|
+
workspaceId, endpointId: scope.endpointId, membershipId: String(remote.membershipId), deviceId: config.deviceId,
|
|
3373
|
+
provider: 'codex' };
|
|
3374
|
+
vault.saveProviderSessionBinding({ schema: 'dharma.local-provider-session-binding/v1', ...identity,
|
|
3375
|
+
bindingId, owner: 'dharma_bridge', sessionId: threadId, workspaceRoot: item.path,
|
|
3376
|
+
createdAt: new Date().toISOString(), expiresAt: new Date(Date.now() + 30 * 86400000).toISOString(),
|
|
3377
|
+
maximumProviderCostCents: maximumTurnCostCents });
|
|
3378
|
+
registration = { schema: 'dharma.named-session/v1', name, bindingId, identity,
|
|
3379
|
+
maximumCostCents, maximumTurnCostCents, enabled: true };
|
|
3380
|
+
await saveNamedSession(dharmaHome(), registration);
|
|
3381
|
+
}
|
|
3382
|
+
const authorizeLifetime = async () => {
|
|
3383
|
+
const current = await refreshVerifiedWorkspacePolicyForTransmission(policyPath, workspaceId, fabric);
|
|
3384
|
+
if (!writeRoots.every(root => current.tasks.writePaths.includes(`${root}/**`)))
|
|
3385
|
+
return false;
|
|
3386
|
+
const response = await fabric.signedPost('/agent-fabric/provider-sessions', {
|
|
3387
|
+
schema: 'dharma.provider-session-registration/v1', action: 'inspect', provider: 'codex', mode: 'bridge_owned',
|
|
3388
|
+
workspaceId, endpointId: scope.endpointId, repositoryBindingId: scope.repositoryBindingId,
|
|
3389
|
+
bindingId: registration.bindingId, expectedRevision: 0, leaseSeconds: 60,
|
|
3390
|
+
});
|
|
3391
|
+
return isNamedSessionOwnerReceipt(response, registration.bindingId, registration.identity);
|
|
3392
|
+
};
|
|
3393
|
+
const refreshLifetime = () => renewNamedSessionLifetime({ vault, bindingId: registration.bindingId,
|
|
3394
|
+
identity: registration.identity, trust, authorize: authorizeLifetime });
|
|
3395
|
+
await refreshLifetime();
|
|
3396
|
+
const channelTransport = { async signedPost(route, body) {
|
|
3397
|
+
await trust.refresh();
|
|
3398
|
+
const response = await fabric.signedPost(route, body);
|
|
3399
|
+
await trust.refresh();
|
|
3400
|
+
return response;
|
|
3401
|
+
} };
|
|
3402
|
+
// The durable reservation already suppresses duplicate provider execution.
|
|
3403
|
+
const consumed = new Set();
|
|
3404
|
+
return await runNamedSessionService({ home: dharmaHome(), registration, vault, signal: controller.signal,
|
|
3405
|
+
localWriteRoots: writeRoots,
|
|
3406
|
+
withActivationBoundary: operation => withWorkspaceSkillActivationLock(workspaceId, 'codex', async () => {
|
|
3407
|
+
await refreshLifetime();
|
|
3408
|
+
const skill = await verifyAgentFabricSkillInstallation({ provider: 'codex', workspace: item.path });
|
|
3409
|
+
if (!skill.ready || !await repositorySharedReady(item))
|
|
3410
|
+
throw new Error('named_session_repository_package_pending');
|
|
3411
|
+
return operation();
|
|
3412
|
+
}),
|
|
3413
|
+
authorizeLocalWork: async () => {
|
|
3414
|
+
const current = await refreshVerifiedWorkspacePolicyForTransmission(policyPath, workspaceId, fabric);
|
|
3415
|
+
return writeRoots.every(root => current.tasks.writePaths.includes(`${root}/**`));
|
|
3416
|
+
},
|
|
3417
|
+
openTransport: async () => transport, channelTransport,
|
|
3418
|
+
verifier: { resolvePublicKey: trust.resolvePublicKey, consume: async (id) => { if (consumed.has(id))
|
|
3419
|
+
return false; consumed.add(id); return true; } },
|
|
3420
|
+
authorizeContent: async (content) => {
|
|
3421
|
+
const current = await refreshVerifiedWorkspacePolicyForTransmission(policyPath, workspaceId, fabric);
|
|
3422
|
+
return current.evidence.automaticDisclosure?.mode === 'customer_authorized_content'
|
|
3423
|
+
&& !containsDisallowedLocalPath(content)
|
|
3424
|
+
&& canonicalize(redactValue(content, { classes: new Set(), redactedValues: 0,
|
|
3425
|
+
excludedPaths: 0, inputBytes: 0, outputBytes: 0 })) === canonicalize(content);
|
|
3426
|
+
} });
|
|
3427
|
+
}
|
|
3428
|
+
finally {
|
|
3429
|
+
try {
|
|
3430
|
+
if (transport)
|
|
3431
|
+
await transport.close();
|
|
3432
|
+
}
|
|
3433
|
+
finally {
|
|
3434
|
+
vault.close();
|
|
3435
|
+
await releaseLock();
|
|
3436
|
+
process.removeListener('SIGTERM', stop);
|
|
3437
|
+
process.removeListener('SIGINT', stop);
|
|
3438
|
+
}
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3178
3441
|
async function workspaceSync(flags, positional) {
|
|
3179
3442
|
const workspaceId = positional[0] || required(flags, 'workspace-id');
|
|
3180
3443
|
const policyRevision = required(flags, 'policy-revision');
|
|
@@ -5899,7 +6162,7 @@ export async function run(argv) {
|
|
|
5899
6162
|
return { version: VERSION };
|
|
5900
6163
|
if (command === 'demo' && ['connect', 'status', 'role', 'peers', 'ask', 'reply', 'inbox', 'ack', 'resume',
|
|
5901
6164
|
'package', 'package-status', 'watch', 'watch-enable', 'watch-status', 'watch-disable',
|
|
5902
|
-
'signing-client-proof', 'signing-owner-proof']
|
|
6165
|
+
'signing-client-proof', 'signing-owner-proof', 'transport-connect']
|
|
5903
6166
|
.includes(String(subcommand))) {
|
|
5904
6167
|
const hqUrl = normalizeHqUrl(portalUrl(flags));
|
|
5905
6168
|
const organizationId = required(flags, 'organization-id');
|
|
@@ -5921,11 +6184,23 @@ export async function run(argv) {
|
|
|
5921
6184
|
if (watchControl && await realpath(root) !== workspace) {
|
|
5922
6185
|
throw new Error('Demo watch setup must select the exact Git repository root.');
|
|
5923
6186
|
}
|
|
6187
|
+
let transportOrigin = null;
|
|
6188
|
+
if (subcommand === 'transport-connect') {
|
|
6189
|
+
transportOrigin = required(flags, 'transport-origin');
|
|
6190
|
+
try {
|
|
6191
|
+
const target = new URL(transportOrigin);
|
|
6192
|
+
if (transportOrigin.length > 2048 || target.protocol !== 'https:' || target.origin !== transportOrigin || transportOrigin === hqUrl)
|
|
6193
|
+
throw new Error();
|
|
6194
|
+
}
|
|
6195
|
+
catch {
|
|
6196
|
+
throw new Error('Demo transport recovery target_invalid. Use the exact reviewed HTTPS origin.');
|
|
6197
|
+
}
|
|
6198
|
+
}
|
|
5924
6199
|
if (flags.has('dry-run'))
|
|
5925
|
-
return { ok: true, stage: signingProof ? 'demo_signing_proof_plan'
|
|
6200
|
+
return { ok: true, stage: transportOrigin ? 'demo_transport_plan' : signingProof ? 'demo_signing_proof_plan'
|
|
5926
6201
|
: watchControl ? 'demo_watch_plan' : 'demo_device_plan',
|
|
5927
6202
|
organizationId, repositoryId, normalizedRepository, workspaceVerified: true,
|
|
5928
|
-
repositoryPackageState: 'not_connected' };
|
|
6203
|
+
repositoryPackageState: 'not_connected', ...(transportOrigin ? { transportOrigin } : {}) };
|
|
5929
6204
|
const grant = subcommand === 'connect' ? required(flags, 'grant') : null;
|
|
5930
6205
|
const scope = {
|
|
5931
6206
|
hqUrl, organizationId, repositoryId, normalizedRepository,
|
|
@@ -5934,7 +6209,7 @@ export async function run(argv) {
|
|
|
5934
6209
|
if (signingProof) {
|
|
5935
6210
|
const context = await readDemoSigningUpgradeContext(resolve(required(flags, 'review-context')));
|
|
5936
6211
|
if (flags.has('submit'))
|
|
5937
|
-
return withDemoDeviceLock(scope, () => submitDemoSigningUpgradeProof(scope, context));
|
|
6212
|
+
return withDemoDeviceLock(scope, async () => submitDemoSigningUpgradeProof(scope, context, await createDemoDeviceTransport(scope)));
|
|
5938
6213
|
return withDemoDeviceLock(scope, () => prepareDemoSigningUpgradeProof(scope, context, subcommand === 'signing-owner-proof' ? 'owner' : 'client'));
|
|
5939
6214
|
}
|
|
5940
6215
|
if (watchControl) {
|
|
@@ -5949,7 +6224,9 @@ export async function run(argv) {
|
|
|
5949
6224
|
hqUrl, organizationId, repositoryId, normalizedRepository, provider, workspace };
|
|
5950
6225
|
const input = { home: dharmaHome(), registration, version: VERSION };
|
|
5951
6226
|
const deps = {
|
|
5952
|
-
verify: async () => {
|
|
6227
|
+
verify: async () => {
|
|
6228
|
+
await withDemoDeviceLock(scope, async () => verifyDemoDevice(scope, await createDemoDeviceTransport(scope)));
|
|
6229
|
+
},
|
|
5953
6230
|
exclusive: operation => withRelayStartupMutation(operation),
|
|
5954
6231
|
prepareStartup: async () => {
|
|
5955
6232
|
const launcher = await installStableRepositoryLauncher(workspace);
|
|
@@ -5977,9 +6254,9 @@ export async function run(argv) {
|
|
|
5977
6254
|
const demoNativeSkillDirectory = provider === 'codex' ? resolve(workspace, '.agents', 'skills')
|
|
5978
6255
|
: provider === 'claude' ? resolve(workspace, '.claude', 'skills')
|
|
5979
6256
|
: nativeSkillDirectory(provider);
|
|
5980
|
-
const cycle = () => withDemoDeviceLock(scope, () => demoRepositoryPackage({ scope, workspace,
|
|
6257
|
+
const cycle = () => withDemoDeviceLock(scope, async () => demoRepositoryPackage({ scope, workspace,
|
|
5981
6258
|
statusOnly: subcommand === 'package-status', provider,
|
|
5982
|
-
nativeSkillDirectory: demoNativeSkillDirectory }));
|
|
6259
|
+
nativeSkillDirectory: demoNativeSkillDirectory }, await createDemoDeviceTransport(scope)));
|
|
5983
6260
|
if (subcommand !== 'watch')
|
|
5984
6261
|
return cycle();
|
|
5985
6262
|
const rawInterval = flags.get('interval-ms');
|
|
@@ -6002,8 +6279,6 @@ export async function run(argv) {
|
|
|
6002
6279
|
}
|
|
6003
6280
|
}
|
|
6004
6281
|
return withDemoDeviceLock(scope, async () => {
|
|
6005
|
-
if (subcommand === 'status')
|
|
6006
|
-
return demoDeviceAndPackageStatus(scope, workspace);
|
|
6007
6282
|
if (subcommand === 'connect') {
|
|
6008
6283
|
const connected = await connectDemoDevice({
|
|
6009
6284
|
...scope, grant: grant,
|
|
@@ -6017,6 +6292,11 @@ export async function run(argv) {
|
|
|
6017
6292
|
const { configPath: _configPath, ...receipt } = connected;
|
|
6018
6293
|
return receipt;
|
|
6019
6294
|
}
|
|
6295
|
+
const deps = await createDemoDeviceTransport(scope);
|
|
6296
|
+
if (transportOrigin)
|
|
6297
|
+
return deps.connectTransport(transportOrigin);
|
|
6298
|
+
if (subcommand === 'status')
|
|
6299
|
+
return demoDeviceAndPackageStatus(scope, workspace, deps);
|
|
6020
6300
|
let action = null;
|
|
6021
6301
|
if (subcommand === 'role') {
|
|
6022
6302
|
const categories = repeated.get('category') || [];
|
|
@@ -6040,11 +6320,13 @@ export async function run(argv) {
|
|
|
6040
6320
|
action = { kind: 'inbox' };
|
|
6041
6321
|
if (subcommand === 'ack')
|
|
6042
6322
|
action = { kind: 'ack', messageId: required(flags, 'message-id') };
|
|
6043
|
-
return performDemoPeerAction(scope, action);
|
|
6323
|
+
return performDemoPeerAction(scope, action, deps);
|
|
6044
6324
|
});
|
|
6045
6325
|
}
|
|
6046
6326
|
if (command === 'bootstrap')
|
|
6047
6327
|
return bootstrap(flags);
|
|
6328
|
+
if (command === 'sessions')
|
|
6329
|
+
return namedSessionCommand(String(subcommand), flags);
|
|
6048
6330
|
if (command === 'onboard')
|
|
6049
6331
|
return onboard(flags);
|
|
6050
6332
|
if (command === 'login')
|