remote-codex 0.1.10 → 0.11.0
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/apps/supervisor-api/dist/index.js +11159 -27875
- package/apps/supervisor-web/dist/assets/{highlighted-body-OFNGDK62-CyMcatlD.js → highlighted-body-OFNGDK62-ChrwAL9u.js} +1 -1
- package/apps/supervisor-web/dist/assets/index-DHf2HOXx.js +381 -0
- package/apps/supervisor-web/dist/assets/index-DpWxXCgt.css +32 -0
- package/apps/supervisor-web/dist/assets/{xterm-DbYWMNQ0.js → xterm-D4sevve4.js} +1 -1
- package/apps/supervisor-web/dist/index.html +2 -2
- package/package.json +2 -3
- package/packages/agent-runtime/src/index.ts +4 -0
- package/packages/agent-runtime/src/management-errors.ts +11 -0
- package/packages/agent-runtime/src/model-pricing.ts +312 -0
- package/packages/agent-runtime/src/registry.ts +19 -4
- package/packages/agent-runtime/src/runtime-errors.ts +97 -0
- package/packages/agent-runtime/src/types.ts +36 -3
- package/packages/agent-runtime/src/unavailable-runtime.ts +169 -0
- package/packages/claude/src/runtimeAdapter.test.ts +95 -6
- package/packages/claude/src/runtimeAdapter.ts +421 -65
- package/packages/codex/src/historyItems.test.ts +110 -0
- package/packages/codex/src/historyItems.ts +96 -15
- package/packages/codex/src/hookHistory.test.ts +59 -0
- package/packages/codex/src/index.ts +7 -0
- package/packages/codex/src/local-session-store.ts +390 -0
- package/packages/codex/src/management/codex-management-service.ts +454 -0
- package/packages/codex/src/management/codexHostConfig.test.ts +88 -0
- package/packages/codex/src/management/codexHostConfig.ts +188 -0
- package/packages/codex/src/management/errors.ts +20 -0
- package/packages/codex/src/modelPricing.test.ts +184 -0
- package/packages/codex/src/modelPricing.ts +9 -0
- package/packages/codex/src/runtime-errors.test.ts +72 -0
- package/packages/codex/src/runtime-errors.ts +37 -0
- package/packages/codex/src/runtimeAdapter.ts +15 -0
- package/packages/codex/src/thread-title.ts +1 -0
- package/packages/opencode/src/historyItems.test.ts +504 -0
- package/packages/opencode/src/historyItems.ts +896 -0
- package/packages/opencode/src/index.ts +2 -0
- package/packages/opencode/src/runtimeAdapter.test.ts +1355 -0
- package/packages/opencode/src/runtimeAdapter.ts +1469 -0
- package/packages/shared/src/agent-providers.ts +56 -0
- package/packages/shared/src/index.ts +170 -35
- package/apps/supervisor-web/dist/assets/index-BlAhoIuq.js +0 -379
- package/apps/supervisor-web/dist/assets/index-DI0NRNgr.css +0 -32
|
@@ -1,29 +1,11 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { EventEmitter } from 'node:events';
|
|
3
|
+
import { execFile } from 'node:child_process';
|
|
3
4
|
import fs from 'node:fs/promises';
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
4
6
|
import path from 'node:path';
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
getSessionInfo as sdkGetSessionInfo,
|
|
8
|
-
getSessionMessages as sdkGetSessionMessages,
|
|
9
|
-
listSessions as sdkListSessions,
|
|
10
|
-
query as sdkQuery,
|
|
11
|
-
} from '@anthropic-ai/claude-agent-sdk';
|
|
12
|
-
import type {
|
|
13
|
-
GetSessionInfoOptions,
|
|
14
|
-
GetSessionMessagesOptions,
|
|
15
|
-
ListSessionsOptions,
|
|
16
|
-
McpServerStatus,
|
|
17
|
-
ModelInfo,
|
|
18
|
-
Options as ClaudeQueryOptions,
|
|
19
|
-
PermissionMode,
|
|
20
|
-
Query,
|
|
21
|
-
SandboxSettings,
|
|
22
|
-
SDKMessage,
|
|
23
|
-
SDKUserMessage,
|
|
24
|
-
SDKSessionInfo,
|
|
25
|
-
SessionMessage,
|
|
26
|
-
} from '@anthropic-ai/claude-agent-sdk';
|
|
7
|
+
import { pathToFileURL } from 'node:url';
|
|
8
|
+
import { promisify } from 'node:util';
|
|
27
9
|
|
|
28
10
|
import type {
|
|
29
11
|
AgentActionQuestion,
|
|
@@ -43,12 +25,19 @@ import type {
|
|
|
43
25
|
AgentSessionSummary,
|
|
44
26
|
AgentTurn,
|
|
45
27
|
InterruptAgentTurnInput,
|
|
28
|
+
ReadAgentSessionOptions,
|
|
46
29
|
ResumeAgentSessionInput,
|
|
47
30
|
StartAgentSessionInput,
|
|
48
31
|
StartAgentSessionResult,
|
|
49
32
|
StartAgentTurnInput,
|
|
50
33
|
} from '../../agent-runtime/src/index';
|
|
51
|
-
import {
|
|
34
|
+
import type {
|
|
35
|
+
AgentBackendInstallationDto,
|
|
36
|
+
} from '../../shared/src/index';
|
|
37
|
+
import {
|
|
38
|
+
AgentRuntimeError,
|
|
39
|
+
markTransientAgentHistoryItem,
|
|
40
|
+
} from '../../agent-runtime/src/index';
|
|
52
41
|
import {
|
|
53
42
|
assistantMessageToHistoryItems,
|
|
54
43
|
buildAgentTurn,
|
|
@@ -67,14 +56,135 @@ import {
|
|
|
67
56
|
userMessageToHistoryItem,
|
|
68
57
|
} from './historyItems';
|
|
69
58
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
type
|
|
73
|
-
type
|
|
74
|
-
|
|
75
|
-
|
|
59
|
+
const execFileAsync = promisify(execFile);
|
|
60
|
+
|
|
61
|
+
type ClaudePromptInput = string | AsyncIterable<SDKUserMessage>;
|
|
62
|
+
type ClaudeMessageContent =
|
|
63
|
+
| string
|
|
64
|
+
| Array<{
|
|
65
|
+
type: 'text';
|
|
66
|
+
text: string;
|
|
67
|
+
} | {
|
|
68
|
+
type: 'image';
|
|
69
|
+
source: {
|
|
70
|
+
type: 'base64';
|
|
71
|
+
media_type: 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp';
|
|
72
|
+
data: string;
|
|
73
|
+
};
|
|
74
|
+
}>;
|
|
76
75
|
type ClaudeMessageContentBlock = Exclude<ClaudeMessageContent, string>[number];
|
|
77
|
-
|
|
76
|
+
type ClaudeQueryFunction = (input: {
|
|
77
|
+
prompt: ClaudePromptInput;
|
|
78
|
+
options: ClaudeQueryOptions;
|
|
79
|
+
}) => Query;
|
|
80
|
+
type ClaudeListSessionsFunction = (_options: ListSessionsOptions) => Promise<SDKSessionInfo[]>;
|
|
81
|
+
type ClaudeGetSessionMessagesFunction = (
|
|
82
|
+
sessionId: string,
|
|
83
|
+
options: GetSessionMessagesOptions,
|
|
84
|
+
) => Promise<SessionMessage[]>;
|
|
85
|
+
type ClaudeGetSessionInfoFunction = (
|
|
86
|
+
sessionId: string,
|
|
87
|
+
options: GetSessionInfoOptions,
|
|
88
|
+
) => Promise<SDKSessionInfo | null>;
|
|
89
|
+
interface ClaudeSdkModule {
|
|
90
|
+
query: ClaudeQueryFunction;
|
|
91
|
+
listSessions: ClaudeListSessionsFunction;
|
|
92
|
+
getSessionMessages: ClaudeGetSessionMessagesFunction;
|
|
93
|
+
getSessionInfo: ClaudeGetSessionInfoFunction;
|
|
94
|
+
}
|
|
95
|
+
interface Query extends AsyncIterable<SDKMessage> {
|
|
96
|
+
close(): void;
|
|
97
|
+
interrupt(): Promise<void>;
|
|
98
|
+
supportedModels(): Promise<ModelInfo[]>;
|
|
99
|
+
mcpServerStatus(): Promise<McpServerStatus[]>;
|
|
100
|
+
}
|
|
101
|
+
interface SDKMessage {
|
|
102
|
+
type: string;
|
|
103
|
+
subtype?: string;
|
|
104
|
+
session_id?: string;
|
|
105
|
+
cwd?: string;
|
|
106
|
+
model?: string;
|
|
107
|
+
uuid?: string;
|
|
108
|
+
message?: unknown;
|
|
109
|
+
event?: unknown;
|
|
110
|
+
parent_tool_use_id?: string | null;
|
|
111
|
+
tool_use_result?: unknown;
|
|
112
|
+
tool_use_id?: string;
|
|
113
|
+
tool_name?: string;
|
|
114
|
+
elapsed_time_seconds?: number;
|
|
115
|
+
usage?: unknown;
|
|
116
|
+
modelUsage?: unknown;
|
|
117
|
+
errors?: string[];
|
|
118
|
+
stop_reason?: string;
|
|
119
|
+
}
|
|
120
|
+
interface SDKUserMessage {
|
|
121
|
+
type: 'user';
|
|
122
|
+
message: {
|
|
123
|
+
role: 'user';
|
|
124
|
+
content: ClaudeMessageContent;
|
|
125
|
+
};
|
|
126
|
+
parent_tool_use_id: string | null;
|
|
127
|
+
}
|
|
128
|
+
interface SDKSessionInfo {
|
|
129
|
+
sessionId: string;
|
|
130
|
+
cwd?: string;
|
|
131
|
+
firstPrompt?: string | null;
|
|
132
|
+
summary?: string | null;
|
|
133
|
+
customTitle?: string | null;
|
|
134
|
+
createdAt?: number | null;
|
|
135
|
+
lastModified?: number | null;
|
|
136
|
+
}
|
|
137
|
+
type SessionMessage = SDKMessage;
|
|
138
|
+
interface ModelInfo {
|
|
139
|
+
value: string;
|
|
140
|
+
displayName: string;
|
|
141
|
+
description: string;
|
|
142
|
+
supportedEffortLevels?: string[];
|
|
143
|
+
supportsEffort?: boolean;
|
|
144
|
+
}
|
|
145
|
+
type PermissionMode = 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan';
|
|
146
|
+
interface SandboxSettings {
|
|
147
|
+
enabled: boolean;
|
|
148
|
+
autoAllowBashIfSandboxed?: boolean;
|
|
149
|
+
allowUnsandboxedCommands?: boolean;
|
|
150
|
+
filesystem?: {
|
|
151
|
+
allowWrite?: string[];
|
|
152
|
+
denyWrite?: string[];
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
interface ClaudeQueryOptions {
|
|
156
|
+
includeHookEvents?: boolean;
|
|
157
|
+
permissionMode?: PermissionMode;
|
|
158
|
+
thinking?: {
|
|
159
|
+
type: string;
|
|
160
|
+
display: string;
|
|
161
|
+
};
|
|
162
|
+
env?: Record<string, string | undefined>;
|
|
163
|
+
cwd?: string;
|
|
164
|
+
sandbox?: SandboxSettings;
|
|
165
|
+
model?: string;
|
|
166
|
+
betas?: string[];
|
|
167
|
+
effort?: 'low' | 'medium' | 'high' | 'xhigh' | 'max';
|
|
168
|
+
resume?: string;
|
|
169
|
+
includePartialMessages?: boolean;
|
|
170
|
+
maxTurns?: number;
|
|
171
|
+
tools?: unknown;
|
|
172
|
+
allowDangerouslySkipPermissions?: boolean;
|
|
173
|
+
pathToClaudeCodeExecutable?: string;
|
|
174
|
+
}
|
|
175
|
+
interface GetSessionInfoOptions {}
|
|
176
|
+
interface GetSessionMessagesOptions {
|
|
177
|
+
includeSystemMessages?: boolean;
|
|
178
|
+
}
|
|
179
|
+
interface ListSessionsOptions {}
|
|
180
|
+
interface McpServerStatus {
|
|
181
|
+
name: string;
|
|
182
|
+
status: string;
|
|
183
|
+
tools?: Array<{
|
|
184
|
+
name: string;
|
|
185
|
+
description?: string | null;
|
|
186
|
+
}>;
|
|
187
|
+
}
|
|
78
188
|
export interface ClaudeRuntimeAdapterOptions {
|
|
79
189
|
home: string;
|
|
80
190
|
command?: string;
|
|
@@ -87,6 +197,7 @@ export interface ClaudeRuntimeAdapterOptions {
|
|
|
87
197
|
listSessions?: ClaudeListSessionsFunction;
|
|
88
198
|
getSessionMessages?: ClaudeGetSessionMessagesFunction;
|
|
89
199
|
getSessionInfo?: ClaudeGetSessionInfoFunction;
|
|
200
|
+
sdk?: ClaudeSdkModule;
|
|
90
201
|
}
|
|
91
202
|
|
|
92
203
|
interface ActiveClaudeTurn {
|
|
@@ -125,6 +236,22 @@ function mimeTypeForImagePath(filePath: string) {
|
|
|
125
236
|
}
|
|
126
237
|
}
|
|
127
238
|
|
|
239
|
+
function extensionForImageMediaType(mediaType: string | null | undefined) {
|
|
240
|
+
switch (mediaType?.toLowerCase()) {
|
|
241
|
+
case 'image/jpeg':
|
|
242
|
+
case 'image/jpg':
|
|
243
|
+
return 'jpg';
|
|
244
|
+
case 'image/png':
|
|
245
|
+
return 'png';
|
|
246
|
+
case 'image/gif':
|
|
247
|
+
return 'gif';
|
|
248
|
+
case 'image/webp':
|
|
249
|
+
return 'webp';
|
|
250
|
+
default:
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
128
255
|
function resolvePromptAssetPath(assetPath: string, cwd: string | null | undefined) {
|
|
129
256
|
if (!cwd) {
|
|
130
257
|
return null;
|
|
@@ -139,6 +266,10 @@ function resolvePromptAssetPath(assetPath: string, cwd: string | null | undefine
|
|
|
139
266
|
return null;
|
|
140
267
|
}
|
|
141
268
|
|
|
269
|
+
function safeAssetFilePart(value: string) {
|
|
270
|
+
return value.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '') || randomUUID();
|
|
271
|
+
}
|
|
272
|
+
|
|
142
273
|
async function* singleUserMessage(content: ClaudeMessageContent): AsyncIterable<SDKUserMessage> {
|
|
143
274
|
yield {
|
|
144
275
|
type: 'user',
|
|
@@ -865,7 +996,7 @@ function queryOptionsForRuntime(
|
|
|
865
996
|
if (input.maxTurns !== undefined) {
|
|
866
997
|
options.maxTurns = input.maxTurns;
|
|
867
998
|
}
|
|
868
|
-
if (input.tools !== undefined) {
|
|
999
|
+
if (input.tools !== undefined && (!Array.isArray(input.tools) || input.tools.length > 0)) {
|
|
869
1000
|
options.tools = input.tools;
|
|
870
1001
|
}
|
|
871
1002
|
if (permission.allowDangerouslySkipPermissions) {
|
|
@@ -877,9 +1008,19 @@ function queryOptionsForRuntime(
|
|
|
877
1008
|
|
|
878
1009
|
export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
879
1010
|
readonly provider = 'claude' as const;
|
|
880
|
-
readonly displayName = 'Claude';
|
|
1011
|
+
readonly displayName = 'Claude Code';
|
|
881
1012
|
readonly description = 'Local Claude Code Agent SDK runtime.';
|
|
882
1013
|
readonly capabilities = claudeCapabilities;
|
|
1014
|
+
readonly installation: AgentBackendInstallationDto = {
|
|
1015
|
+
packageName: '@anthropic-ai/claude-agent-sdk',
|
|
1016
|
+
installed: false,
|
|
1017
|
+
installedVersion: null,
|
|
1018
|
+
latestVersion: null,
|
|
1019
|
+
installCommand: 'npm install -g @anthropic-ai/claude-code @anthropic-ai/claude-agent-sdk',
|
|
1020
|
+
updateCommand: 'npm install -g @anthropic-ai/claude-code@latest @anthropic-ai/claude-agent-sdk@latest',
|
|
1021
|
+
busy: false,
|
|
1022
|
+
lastError: null,
|
|
1023
|
+
};
|
|
883
1024
|
readonly managementSchema: AgentRuntimeManagementSchema = {
|
|
884
1025
|
hostConfigFiles: [],
|
|
885
1026
|
toolboxItems: [
|
|
@@ -899,10 +1040,10 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
899
1040
|
lastError: null,
|
|
900
1041
|
restartCount: 0,
|
|
901
1042
|
};
|
|
902
|
-
private
|
|
903
|
-
private
|
|
904
|
-
private
|
|
905
|
-
private
|
|
1043
|
+
private queryFactory: ClaudeQueryFunction;
|
|
1044
|
+
private listSessionsFn: ClaudeListSessionsFunction;
|
|
1045
|
+
private getSessionMessagesFn: ClaudeGetSessionMessagesFunction;
|
|
1046
|
+
private getSessionInfoFn: ClaudeGetSessionInfoFunction;
|
|
906
1047
|
private readonly activeTurns = new Map<string, ActiveClaudeTurn>();
|
|
907
1048
|
private readonly knownSessionIds = new Set<string>();
|
|
908
1049
|
private readonly sessionCwds = new Map<string, string>();
|
|
@@ -910,13 +1051,15 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
910
1051
|
private readonly sessionApprovalModes = new Map<string, StartAgentSessionInput['approvalMode']>();
|
|
911
1052
|
private readonly liveUserPrompts = new Map<string, Map<string, string>>();
|
|
912
1053
|
private readonly clientApp: string;
|
|
1054
|
+
private sdkLoadError: string | null = null;
|
|
913
1055
|
|
|
914
1056
|
constructor(private readonly options: ClaudeRuntimeAdapterOptions) {
|
|
915
1057
|
super();
|
|
916
|
-
|
|
917
|
-
this.
|
|
918
|
-
this.
|
|
919
|
-
this.
|
|
1058
|
+
const sdk = options.sdk;
|
|
1059
|
+
this.queryFactory = options.query ?? sdk?.query ?? this.unavailableQueryFactory.bind(this);
|
|
1060
|
+
this.listSessionsFn = options.listSessions ?? sdk?.listSessions ?? this.unavailableListSessions.bind(this);
|
|
1061
|
+
this.getSessionMessagesFn = options.getSessionMessages ?? sdk?.getSessionMessages ?? this.unavailableGetSessionMessages.bind(this);
|
|
1062
|
+
this.getSessionInfoFn = options.getSessionInfo ?? sdk?.getSessionInfo ?? this.unavailableGetSessionInfo.bind(this);
|
|
920
1063
|
this.clientApp = [
|
|
921
1064
|
options.clientInfo?.name ?? 'remote-codex-supervisor',
|
|
922
1065
|
options.clientInfo?.version,
|
|
@@ -929,6 +1072,29 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
929
1072
|
|
|
930
1073
|
async start() {
|
|
931
1074
|
await fs.mkdir(this.options.home, { recursive: true });
|
|
1075
|
+
if (!this.options.query && !this.options.sdk) {
|
|
1076
|
+
try {
|
|
1077
|
+
const sdk = await this.loadSdk();
|
|
1078
|
+
this.queryFactory = sdk.query;
|
|
1079
|
+
this.listSessionsFn = sdk.listSessions;
|
|
1080
|
+
this.getSessionMessagesFn = sdk.getSessionMessages;
|
|
1081
|
+
this.getSessionInfoFn = sdk.getSessionInfo;
|
|
1082
|
+
this.sdkLoadError = null;
|
|
1083
|
+
this.installation.installed = true;
|
|
1084
|
+
this.installation.lastError = null;
|
|
1085
|
+
} catch (error) {
|
|
1086
|
+
this.sdkLoadError = errorMessage(error);
|
|
1087
|
+
this.installation.installed = false;
|
|
1088
|
+
this.installation.lastError = this.sdkLoadError;
|
|
1089
|
+
this.status = {
|
|
1090
|
+
...this.status,
|
|
1091
|
+
state: 'stopped',
|
|
1092
|
+
lastError: `Claude Code SDK is not installed or could not be loaded. ${this.sdkLoadError}`,
|
|
1093
|
+
};
|
|
1094
|
+
this.emit('status', this.getStatus());
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
932
1098
|
this.status = {
|
|
933
1099
|
...this.status,
|
|
934
1100
|
state: 'ready',
|
|
@@ -992,7 +1158,7 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
992
1158
|
|
|
993
1159
|
async readSession(
|
|
994
1160
|
providerSessionId: string,
|
|
995
|
-
|
|
1161
|
+
options: ReadAgentSessionOptions = {},
|
|
996
1162
|
): Promise<AgentSessionDetail> {
|
|
997
1163
|
const [info, messages] = await this.withClaudeConfigEnv(async () => Promise.all([
|
|
998
1164
|
this.getSessionInfoFn(providerSessionId, {} as GetSessionInfoOptions),
|
|
@@ -1015,14 +1181,19 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
1015
1181
|
rawSession: null,
|
|
1016
1182
|
};
|
|
1017
1183
|
|
|
1018
|
-
const
|
|
1184
|
+
const cwd = summary.cwd || this.sessionCwds.get(providerSessionId) || '';
|
|
1185
|
+
const historyAssetContext = {
|
|
1186
|
+
workspacePath: options.workspacePath || cwd,
|
|
1187
|
+
...(options.localThreadId ? { localThreadId: options.localThreadId } : {}),
|
|
1188
|
+
};
|
|
1189
|
+
const turns = await this.sessionMessagesToTurns(messages, historyAssetContext);
|
|
1019
1190
|
const activeTurn = [...this.activeTurns.values()].find(
|
|
1020
1191
|
(turn) => turn.providerSessionId === providerSessionId,
|
|
1021
1192
|
);
|
|
1022
1193
|
|
|
1023
1194
|
return {
|
|
1024
1195
|
...summary,
|
|
1025
|
-
cwd
|
|
1196
|
+
cwd,
|
|
1026
1197
|
turns: this.reconcileActiveTranscriptTurn(providerSessionId, turns, activeTurn),
|
|
1027
1198
|
};
|
|
1028
1199
|
}
|
|
@@ -1052,9 +1223,12 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
1052
1223
|
for await (const message of query) {
|
|
1053
1224
|
rawMessages.push(message);
|
|
1054
1225
|
if (message.type === 'system' && message.subtype === 'init') {
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1226
|
+
const sessionId = message.session_id;
|
|
1227
|
+
if (sessionId) {
|
|
1228
|
+
providerSessionId = sessionId;
|
|
1229
|
+
model = displayClaudeModel(input.model, message.model ?? model);
|
|
1230
|
+
this.sessionCwds.set(sessionId, message.cwd ?? input.cwd);
|
|
1231
|
+
}
|
|
1058
1232
|
} else if ('session_id' in message && typeof message.session_id === 'string') {
|
|
1059
1233
|
providerSessionId ??= message.session_id;
|
|
1060
1234
|
}
|
|
@@ -1382,8 +1556,10 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
1382
1556
|
this.captureUsage(state, message);
|
|
1383
1557
|
|
|
1384
1558
|
if (message.type === 'system' && message.subtype === 'init') {
|
|
1385
|
-
|
|
1386
|
-
|
|
1559
|
+
if (message.session_id) {
|
|
1560
|
+
this.sessionCwds.set(message.session_id, message.cwd ?? '');
|
|
1561
|
+
this.sessionModels.set(message.session_id, message.model ?? null);
|
|
1562
|
+
}
|
|
1387
1563
|
return;
|
|
1388
1564
|
}
|
|
1389
1565
|
|
|
@@ -1430,15 +1606,15 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
1430
1606
|
if (delta) {
|
|
1431
1607
|
const existing = state.items.get(delta.itemId);
|
|
1432
1608
|
const nextItem: AgentHistoryItem = existing?.kind === 'agentMessage'
|
|
1433
|
-
? {
|
|
1609
|
+
? markTransientAgentHistoryItem({
|
|
1434
1610
|
...existing,
|
|
1435
1611
|
text: `${existing.text}${delta.delta}`,
|
|
1436
|
-
}
|
|
1437
|
-
: {
|
|
1612
|
+
})
|
|
1613
|
+
: markTransientAgentHistoryItem({
|
|
1438
1614
|
id: delta.itemId,
|
|
1439
1615
|
kind: 'agentMessage',
|
|
1440
1616
|
text: delta.delta,
|
|
1441
|
-
};
|
|
1617
|
+
});
|
|
1442
1618
|
addOrUpdateItem(state, nextItem);
|
|
1443
1619
|
this.emitRuntimeEvent({
|
|
1444
1620
|
type: 'output.delta',
|
|
@@ -1523,10 +1699,15 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
1523
1699
|
}
|
|
1524
1700
|
|
|
1525
1701
|
if (message.type === 'tool_progress') {
|
|
1526
|
-
|
|
1702
|
+
const toolUseId = message.tool_use_id;
|
|
1703
|
+
const toolName = message.tool_name;
|
|
1704
|
+
if (!toolUseId || !toolName) {
|
|
1705
|
+
return;
|
|
1706
|
+
}
|
|
1707
|
+
if (!state.items.has(toolUseId)) {
|
|
1527
1708
|
const item = toolUseToHistoryItem({
|
|
1528
|
-
id:
|
|
1529
|
-
name:
|
|
1709
|
+
id: toolUseId,
|
|
1710
|
+
name: toolName,
|
|
1530
1711
|
toolInput: {
|
|
1531
1712
|
elapsed_time_seconds: message.elapsed_time_seconds,
|
|
1532
1713
|
},
|
|
@@ -1536,14 +1717,18 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
1536
1717
|
addOrUpdateItem(state, item);
|
|
1537
1718
|
this.emitItem(state, item, 'item.started');
|
|
1538
1719
|
} else {
|
|
1539
|
-
state.suppressedToolUseIds.add(
|
|
1720
|
+
state.suppressedToolUseIds.add(toolUseId);
|
|
1540
1721
|
}
|
|
1541
1722
|
}
|
|
1542
1723
|
return;
|
|
1543
1724
|
}
|
|
1544
1725
|
|
|
1545
1726
|
if (message.type === 'system' && message.subtype === 'permission_denied') {
|
|
1546
|
-
const
|
|
1727
|
+
const toolUseId = message.tool_use_id;
|
|
1728
|
+
if (!toolUseId) {
|
|
1729
|
+
return;
|
|
1730
|
+
}
|
|
1731
|
+
const previous = state.items.get(toolUseId);
|
|
1547
1732
|
const item: AgentHistoryItem = previous
|
|
1548
1733
|
? {
|
|
1549
1734
|
...previous,
|
|
@@ -1551,10 +1736,10 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
1551
1736
|
detailText: [previous.detailText ?? previous.text, '', message.message].join('\n'),
|
|
1552
1737
|
}
|
|
1553
1738
|
: {
|
|
1554
|
-
id:
|
|
1739
|
+
id: toolUseId,
|
|
1555
1740
|
kind: 'toolCall',
|
|
1556
1741
|
text: `${message.tool_name} denied`,
|
|
1557
|
-
detailText: message.message,
|
|
1742
|
+
detailText: typeof message.message === 'string' ? message.message : null,
|
|
1558
1743
|
status: 'denied',
|
|
1559
1744
|
};
|
|
1560
1745
|
addOrUpdateItem(state, item);
|
|
@@ -1620,6 +1805,44 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
1620
1805
|
this.emit('event', event);
|
|
1621
1806
|
}
|
|
1622
1807
|
|
|
1808
|
+
private async loadSdk(): Promise<ClaudeSdkModule> {
|
|
1809
|
+
try {
|
|
1810
|
+
return await importOptionalPackage('@anthropic-ai/claude-agent-sdk') as unknown as ClaudeSdkModule;
|
|
1811
|
+
} catch (error) {
|
|
1812
|
+
throw new AgentRuntimeError(
|
|
1813
|
+
'Install Claude Code support with npm install -g @anthropic-ai/claude-agent-sdk, or add @anthropic-ai/claude-agent-sdk to this checkout.',
|
|
1814
|
+
'claude',
|
|
1815
|
+
'provider_unavailable',
|
|
1816
|
+
undefined,
|
|
1817
|
+
error,
|
|
1818
|
+
);
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
private unavailableError() {
|
|
1823
|
+
return new AgentRuntimeError(
|
|
1824
|
+
this.sdkLoadError ?? 'Claude Code SDK is not installed.',
|
|
1825
|
+
'claude',
|
|
1826
|
+
'provider_unavailable',
|
|
1827
|
+
);
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
private unavailableQueryFactory(): Query {
|
|
1831
|
+
throw this.unavailableError();
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
private async unavailableListSessions(): Promise<SDKSessionInfo[]> {
|
|
1835
|
+
throw this.unavailableError();
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
private async unavailableGetSessionMessages(): Promise<SessionMessage[]> {
|
|
1839
|
+
throw this.unavailableError();
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
private async unavailableGetSessionInfo(): Promise<SDKSessionInfo | null> {
|
|
1843
|
+
throw this.unavailableError();
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1623
1846
|
private mapMcpServer(server: McpServerStatus): AgentMcpServer {
|
|
1624
1847
|
return {
|
|
1625
1848
|
name: server.name,
|
|
@@ -1634,7 +1857,103 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
1634
1857
|
};
|
|
1635
1858
|
}
|
|
1636
1859
|
|
|
1637
|
-
private
|
|
1860
|
+
private async userMessageToHistoryItem(
|
|
1861
|
+
id: string,
|
|
1862
|
+
message: unknown,
|
|
1863
|
+
context: {
|
|
1864
|
+
localThreadId?: string;
|
|
1865
|
+
workspacePath?: string;
|
|
1866
|
+
},
|
|
1867
|
+
): Promise<AgentHistoryItem> {
|
|
1868
|
+
if (!isRecord(message) || !Array.isArray(message.content)) {
|
|
1869
|
+
return userMessageToHistoryItem(id, message);
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
const parts: string[] = [];
|
|
1873
|
+
for (let index = 0; index < message.content.length; index += 1) {
|
|
1874
|
+
const block = message.content[index];
|
|
1875
|
+
if (!isRecord(block)) {
|
|
1876
|
+
continue;
|
|
1877
|
+
}
|
|
1878
|
+
if (block.type === 'image') {
|
|
1879
|
+
const photoToken = await this.persistHistoryImageBlock({
|
|
1880
|
+
messageId: id,
|
|
1881
|
+
blockIndex: index,
|
|
1882
|
+
block,
|
|
1883
|
+
...context,
|
|
1884
|
+
});
|
|
1885
|
+
if (photoToken) {
|
|
1886
|
+
parts.push(photoToken);
|
|
1887
|
+
}
|
|
1888
|
+
continue;
|
|
1889
|
+
}
|
|
1890
|
+
if (typeof block.text === 'string') {
|
|
1891
|
+
parts.push(block.text);
|
|
1892
|
+
continue;
|
|
1893
|
+
}
|
|
1894
|
+
if (typeof block.content === 'string') {
|
|
1895
|
+
parts.push(block.content);
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
return userMessageHistoryItem(id, parts.filter(Boolean).join('\n'));
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
private async persistHistoryImageBlock(input: {
|
|
1903
|
+
messageId: string;
|
|
1904
|
+
blockIndex: number;
|
|
1905
|
+
block: Record<string, unknown>;
|
|
1906
|
+
localThreadId?: string;
|
|
1907
|
+
workspacePath?: string;
|
|
1908
|
+
}): Promise<string | null> {
|
|
1909
|
+
if (!input.localThreadId || !input.workspacePath) {
|
|
1910
|
+
return null;
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
const source = isRecord(input.block.source) ? input.block.source : null;
|
|
1914
|
+
if (!source || source.type !== 'base64') {
|
|
1915
|
+
return null;
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
const data = typeof source.data === 'string' ? source.data : null;
|
|
1919
|
+
if (!data) {
|
|
1920
|
+
return null;
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
const extension = extensionForImageMediaType(
|
|
1924
|
+
typeof source.media_type === 'string' ? source.media_type : null,
|
|
1925
|
+
);
|
|
1926
|
+
if (!extension) {
|
|
1927
|
+
return null;
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
const relativePath = `./.temp/threads/${input.localThreadId}/claude-history-${safeAssetFilePart(input.messageId)}-${input.blockIndex}.${extension}`;
|
|
1931
|
+
const targetPath = path.resolve(input.workspacePath, relativePath);
|
|
1932
|
+
const relativeToWorkspace = path.relative(input.workspacePath, targetPath);
|
|
1933
|
+
if (
|
|
1934
|
+
relativeToWorkspace === '' ||
|
|
1935
|
+
relativeToWorkspace.startsWith('..') ||
|
|
1936
|
+
path.isAbsolute(relativeToWorkspace)
|
|
1937
|
+
) {
|
|
1938
|
+
return null;
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
try {
|
|
1942
|
+
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
|
1943
|
+
await fs.writeFile(targetPath, Buffer.from(data, 'base64'));
|
|
1944
|
+
return `[PHOTO ${relativePath}]`;
|
|
1945
|
+
} catch {
|
|
1946
|
+
return null;
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
private async sessionMessagesToTurns(
|
|
1951
|
+
messages: SessionMessage[],
|
|
1952
|
+
context: {
|
|
1953
|
+
localThreadId?: string;
|
|
1954
|
+
workspacePath?: string;
|
|
1955
|
+
} = {},
|
|
1956
|
+
): Promise<AgentTurn[]> {
|
|
1638
1957
|
const turns: AgentTurn[] = [];
|
|
1639
1958
|
let current: {
|
|
1640
1959
|
providerTurnId: string;
|
|
@@ -1703,12 +2022,17 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
1703
2022
|
items: current.items,
|
|
1704
2023
|
}));
|
|
1705
2024
|
}
|
|
1706
|
-
const
|
|
2025
|
+
const messageUuid = message.uuid ?? randomUUID();
|
|
2026
|
+
const userItem = await this.userMessageToHistoryItem(
|
|
2027
|
+
messageUuid,
|
|
2028
|
+
message.message,
|
|
2029
|
+
context,
|
|
2030
|
+
);
|
|
1707
2031
|
current = {
|
|
1708
|
-
providerTurnId: `claude-turn-${
|
|
1709
|
-
startedAt: isoFromUuidV7(
|
|
2032
|
+
providerTurnId: `claude-turn-${messageUuid}`,
|
|
2033
|
+
startedAt: isoFromUuidV7(messageUuid),
|
|
1710
2034
|
items: [userItem],
|
|
1711
|
-
itemsById: new Map([[
|
|
2035
|
+
itemsById: new Map([[messageUuid, userItem]]),
|
|
1712
2036
|
};
|
|
1713
2037
|
continue;
|
|
1714
2038
|
}
|
|
@@ -1723,7 +2047,7 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
1723
2047
|
current?.itemsById.delete(toolUseId);
|
|
1724
2048
|
}
|
|
1725
2049
|
for (const item of assistantMessageToHistoryItems({
|
|
1726
|
-
messageId: message.uuid,
|
|
2050
|
+
messageId: message.uuid ?? randomUUID(),
|
|
1727
2051
|
message: message.message,
|
|
1728
2052
|
})) {
|
|
1729
2053
|
upsertCurrentItem(item);
|
|
@@ -1787,3 +2111,35 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
|
|
|
1787
2111
|
this.emit('status', this.getStatus());
|
|
1788
2112
|
}
|
|
1789
2113
|
}
|
|
2114
|
+
|
|
2115
|
+
async function importOptionalPackage(specifier: string) {
|
|
2116
|
+
const dynamicImport = new Function('specifier', 'return import(specifier);') as (
|
|
2117
|
+
specifier: string,
|
|
2118
|
+
) => Promise<unknown>;
|
|
2119
|
+
try {
|
|
2120
|
+
return await dynamicImport(specifier);
|
|
2121
|
+
} catch (localError) {
|
|
2122
|
+
const globalRoot = await npmGlobalRoot();
|
|
2123
|
+
if (!globalRoot) {
|
|
2124
|
+
throw localError;
|
|
2125
|
+
}
|
|
2126
|
+
try {
|
|
2127
|
+
const requireFromGlobal = createRequire(path.join(globalRoot, 'remote-codex-global.cjs'));
|
|
2128
|
+
const resolved = requireFromGlobal.resolve(specifier);
|
|
2129
|
+
return await dynamicImport(pathToFileURL(resolved).href);
|
|
2130
|
+
} catch {
|
|
2131
|
+
throw localError;
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
async function npmGlobalRoot() {
|
|
2137
|
+
try {
|
|
2138
|
+
const { stdout } = await execFileAsync('npm', ['root', '-g'], {
|
|
2139
|
+
timeout: 3_000,
|
|
2140
|
+
});
|
|
2141
|
+
return stdout.trim() || null;
|
|
2142
|
+
} catch {
|
|
2143
|
+
return null;
|
|
2144
|
+
}
|
|
2145
|
+
}
|