mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
fix(codex): expose HAPI threads to Codex Desktop (#1022)
* test(codex): reproduce desktop thread classification gap * fix(codex): classify HAPI threads as user sessions * test(codex): cover thread source creation paths
This commit is contained in:
@@ -93,6 +93,8 @@ export interface ThreadStartParams {
|
||||
baseInstructions?: string;
|
||||
developerInstructions?: string;
|
||||
personality?: string;
|
||||
/** Client-supplied analytics classification persisted with the thread. */
|
||||
threadSource?: string;
|
||||
ephemeral?: boolean;
|
||||
experimentalRawEvents?: boolean;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ const harness = vi.hoisted(() => ({
|
||||
collaborationModeResponse: { data: [{ mode: 'default' }, { mode: 'plan' }] } as unknown,
|
||||
failListCollaborationModes: false,
|
||||
startThreadIds: [] as string[],
|
||||
startThreadParams: [] as Array<Record<string, unknown>>,
|
||||
resumeThreadIds: [] as string[],
|
||||
resumeThreadParams: [] as Array<Record<string, unknown>>,
|
||||
startTurnThreadIds: [] as string[],
|
||||
startTurnParams: [] as Array<Record<string, unknown>>,
|
||||
startTurnErrors: [] as Error[],
|
||||
@@ -110,15 +112,17 @@ vi.mock('./codexAppServerClient', () => {
|
||||
harness.requestHandlers.set(method, handler);
|
||||
}
|
||||
|
||||
async startThread(): Promise<{ thread: { id: string }; model: string }> {
|
||||
async startThread(params?: Record<string, unknown>): Promise<{ thread: { id: string }; model: string }> {
|
||||
const id = `thread-${harness.startThreadIds.length + 1}`;
|
||||
harness.startThreadIds.push(id);
|
||||
harness.startThreadParams.push(params ?? {});
|
||||
return { thread: { id }, model: 'gpt-5.4' };
|
||||
}
|
||||
|
||||
async resumeThread(params?: { threadId?: string }): Promise<{ thread: { id: string }; model: string }> {
|
||||
const id = params?.threadId ?? 'thread-resumed';
|
||||
async resumeThread(params?: Record<string, unknown>): Promise<{ thread: { id: string }; model: string }> {
|
||||
const id = typeof params?.threadId === 'string' ? params.threadId : 'thread-resumed';
|
||||
harness.resumeThreadIds.push(id);
|
||||
harness.resumeThreadParams.push(params ?? {});
|
||||
if (harness.failResumeThreadIds.includes(id)) {
|
||||
throw new Error('resume failed');
|
||||
}
|
||||
@@ -1051,7 +1055,9 @@ describe('codexRemoteLauncher', () => {
|
||||
harness.collaborationModeResponse = { data: [{ mode: 'default' }, { mode: 'plan' }] };
|
||||
harness.failListCollaborationModes = false;
|
||||
harness.startThreadIds = [];
|
||||
harness.startThreadParams = [];
|
||||
harness.resumeThreadIds = [];
|
||||
harness.resumeThreadParams = [];
|
||||
harness.startTurnThreadIds = [];
|
||||
harness.startTurnParams = [];
|
||||
harness.startTurnErrors = [];
|
||||
@@ -1119,6 +1125,8 @@ describe('codexRemoteLauncher', () => {
|
||||
expect(exitReason).toBe('exit');
|
||||
expect(foundSessionIds).toContain('thread-1');
|
||||
expect(getModel()).toBe('gpt-5.4');
|
||||
expect(harness.startThreadParams).toHaveLength(1);
|
||||
expect(harness.startThreadParams[0]?.threadSource).toBe('user');
|
||||
expect(harness.initializeCalls).toEqual([{
|
||||
clientInfo: {
|
||||
name: 'hapi-codex-client',
|
||||
@@ -1344,6 +1352,8 @@ describe('codexRemoteLauncher', () => {
|
||||
|
||||
expect(exitReason).toBe('exit');
|
||||
expect(foundSessionIds).toEqual(['thread-1']);
|
||||
expect(harness.startThreadParams).toHaveLength(1);
|
||||
expect(harness.startThreadParams[0]?.threadSource).toBe('user');
|
||||
expect(harness.startTurnParams).toHaveLength(0);
|
||||
expect(harness.goalSetCalls).toEqual([{
|
||||
threadId: 'thread-1',
|
||||
@@ -1967,6 +1977,8 @@ describe('codexRemoteLauncher', () => {
|
||||
|
||||
expect(exitReason).toBe('exit');
|
||||
expect(harness.resumeThreadIds).toEqual(['thread-old']);
|
||||
expect(harness.resumeThreadParams).toHaveLength(1);
|
||||
expect(harness.resumeThreadParams[0]?.threadSource).toBeUndefined();
|
||||
expect(harness.startThreadIds).toEqual([]);
|
||||
expect(harness.startTurnThreadIds).toEqual([]);
|
||||
expect(session.sessionId).toBe('thread-old');
|
||||
|
||||
@@ -98,6 +98,7 @@ const TRUSTED_ACCESS_FOR_CYBER_URL = 'https://chatgpt.com/cyber';
|
||||
const CYBER_POLICY_TRUSTED_ACCESS_URL = 'https://openai.com/form/enterprise-trusted-access-for-cyber/';
|
||||
const CODEX_GOALS_UNSUPPORTED_MESSAGE = 'Codex goals are not supported by this Codex runtime. Upgrade Codex or enable features.goals.';
|
||||
const MAX_CODEX_GOAL_OBJECTIVE_CHARS = 4_000;
|
||||
const HAPI_TOP_LEVEL_THREAD_SOURCE = 'user';
|
||||
|
||||
type GoalForwardSignature = {
|
||||
objective: string | null;
|
||||
@@ -3338,7 +3339,10 @@ class CodexRemoteLauncher extends RemoteLauncherBase {
|
||||
mcpServers,
|
||||
cliOverrides: session.codexCliOverrides
|
||||
});
|
||||
const threadResponse = await appServerClient.startThread(threadParams, {
|
||||
const threadResponse = await appServerClient.startThread({
|
||||
...threadParams,
|
||||
threadSource: HAPI_TOP_LEVEL_THREAD_SOURCE
|
||||
}, {
|
||||
signal: this.abortController.signal
|
||||
});
|
||||
const threadRecord = asRecord(threadResponse);
|
||||
@@ -3597,7 +3601,10 @@ class CodexRemoteLauncher extends RemoteLauncherBase {
|
||||
}
|
||||
|
||||
if (!threadId) {
|
||||
const threadResponse = await appServerClient.startThread(threadParams, {
|
||||
const threadResponse = await appServerClient.startThread({
|
||||
...threadParams,
|
||||
threadSource: HAPI_TOP_LEVEL_THREAD_SOURCE
|
||||
}, {
|
||||
signal: this.abortController.signal
|
||||
});
|
||||
const threadRecord = asRecord(threadResponse);
|
||||
|
||||
Reference in New Issue
Block a user