@keyou007/agent-bot 0.1.20-alpha.0 → 0.1.20
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/CHANGELOG.md +17 -0
- package/README.md +18 -1
- package/README.zh.md +9 -0
- package/dist/cli/AgentPrerequisites.js +11 -47
- package/dist/cli/AgentPrerequisites.js.map +1 -1
- package/dist/cli/DefaultAgentSelection.js +1 -1
- package/dist/cli/DefaultAgentSelection.js.map +1 -1
- package/dist/cli.js +15 -0
- package/dist/cli.js.map +1 -1
- package/dist/codex/CodexLocalActivityDetector.js +32 -10
- package/dist/codex/CodexLocalActivityDetector.js.map +1 -1
- package/dist/codex/CodexProcessManager.js +53 -7
- package/dist/codex/CodexProcessManager.js.map +1 -1
- package/dist/codex/CodexRuntime.js +225 -164
- package/dist/codex/CodexRuntime.js.map +1 -1
- package/dist/codex/CodexVersion.js +18 -0
- package/dist/codex/CodexVersion.js.map +1 -0
- package/dist/codex/ProviderSwitch.js +32 -0
- package/dist/codex/ProviderSwitch.js.map +1 -0
- package/dist/config/AgentExecutionDefaultsStore.js +1 -1
- package/dist/config/AgentExecutionDefaultsStore.js.map +1 -1
- package/dist/feishu/CardRenderer.js +36 -11
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/feishu/FeishuConnector.js +17 -5
- package/dist/feishu/FeishuConnector.js.map +1 -1
- package/dist/feishu/InboundText.js +11 -0
- package/dist/feishu/InboundText.js.map +1 -1
- package/dist/feishu/MergedForwardMessage.js +17 -5
- package/dist/feishu/MergedForwardMessage.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/local-files/LocalFileViewerServer.js +25 -7
- package/dist/local-files/LocalFileViewerServer.js.map +1 -1
- package/dist/local-files/MarkdownFileLinks.js +65 -0
- package/dist/local-files/MarkdownFileLinks.js.map +1 -0
- package/dist/proxy/ProxySessionController.js +400 -259
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/state/StateStore.js +99 -65
- package/dist/state/StateStore.js.map +1 -1
- package/dist/utils/semanticVersion.js +39 -0
- package/dist/utils/semanticVersion.js.map +1 -0
- package/docs/technical-reference.md +5 -3
- package/docs/technical-reference.zh.md +5 -3
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/skills/agent-bot/SKILL.md +7 -1
- package/src/cli/AgentPrerequisites.ts +14 -40
- package/src/cli/DefaultAgentSelection.ts +1 -1
- package/src/cli.ts +13 -0
- package/src/codex/CodexLocalActivityDetector.ts +41 -9
- package/src/codex/CodexProcessManager.ts +44 -7
- package/src/codex/CodexRuntime.ts +244 -195
- package/src/codex/CodexVersion.ts +18 -0
- package/src/codex/ProviderSwitch.ts +36 -0
- package/src/config/AgentExecutionDefaultsStore.ts +1 -1
- package/src/feishu/CardRenderer.ts +37 -27
- package/src/feishu/FeishuConnector.ts +15 -5
- package/src/feishu/InboundText.ts +11 -0
- package/src/feishu/MergedForwardMessage.ts +15 -5
- package/src/index.ts +1 -1
- package/src/local-files/LocalFileViewerServer.ts +26 -7
- package/src/local-files/MarkdownFileLinks.ts +64 -0
- package/src/proxy/ProxySessionController.ts +370 -240
- package/src/runtime/types.ts +15 -2
- package/src/state/StateStore.ts +117 -90
- package/src/utils/semanticVersion.ts +28 -0
|
@@ -96,6 +96,8 @@ agentbot task forkgroup [title]
|
|
|
96
96
|
|
|
97
97
|
Choose `new` or `newgroup` for fresh context. Choose `fork` or `forkgroup` when the new task must retain conversation history through the latest completed Turn. Forking must not interrupt an active source turn.
|
|
98
98
|
|
|
99
|
+
Fork creation stores source-task and branch-Turn references without synchronizing the full local Turn list. The Feishu Turn card reuses local records and loads only the summary pages needed for the requested page, not the entire history. `task turns` loads the first page if necessary and returns available local records. This does not change the Agent's inherited context. Retry listing Turns after a temporary history-read failure; unsupported summary pagination never falls back to downloading full history.
|
|
100
|
+
|
|
99
101
|
## Change Settings
|
|
100
102
|
|
|
101
103
|
```powershell
|
|
@@ -108,6 +110,8 @@ agentbot task permissions [auto|confirm]
|
|
|
108
110
|
|
|
109
111
|
Omit the value to inspect the current setting and available choices. `agent` changes the default Agent for future tasks in that conversation. The other settings affect the specified task from its next request and become the saved defaults for that Agent.
|
|
110
112
|
|
|
113
|
+
Provider changes require an idle task and verify the actual Provider and model before saving. Wait for active work to finish; do not stop it without the user's request. Switching unloads only the selected thread, not the shared App Server. On failure, previous settings are retained and recovery is attempted. If recovery fails, resolve the reported configuration or ownership issue and retry the Provider switch before sending another prompt. Never replace a forked task with an empty task to work around a switch failure.
|
|
114
|
+
|
|
111
115
|
## Goals And Turns
|
|
112
116
|
|
|
113
117
|
```powershell
|
|
@@ -119,7 +123,7 @@ agentbot task turns
|
|
|
119
123
|
agentbot task reset <turn-id>
|
|
120
124
|
```
|
|
121
125
|
|
|
122
|
-
Use `turns` to obtain a real Turn ID before `reset`. Reset changes conversation context only; it does not revert local files.
|
|
126
|
+
Use `turns` to obtain a real Turn ID before `reset`. Reset changes conversation context only; it does not revert local files. Agent Bot announces an interactive Reset when it starts and queues every new message after its Reaction until the replacement thread is ready.
|
|
123
127
|
|
|
124
128
|
## Files And Local Commands
|
|
125
129
|
|
|
@@ -168,6 +172,8 @@ agentbot --profile ~/.agent-bot-rescue server status
|
|
|
168
172
|
|
|
169
173
|
Initialization prepares configuration, checks supported Agents, configures Feishu, and starts the server. On first initialization and `init --reset`, let the user choose whether all group messages are accepted or an explicit @ mention is required; only the first choice requests the additional all-group-message permission. Relay authorization links and wait for the user; never choose a response mode, skip authorization, or choose maintenance actions without permission.
|
|
170
174
|
|
|
175
|
+
Codex requires version 0.153.4 or later. Init checks this locally, and runtime startup validates the actual App Server version. For an unsupported version, show the upgrade instruction (`codex update` or `npm install -g @openai/codex@latest`); do not work around it by reading full history through legacy APIs. Safely restart Agent Bot after an authorized upgrade. This version requirement does not apply to TraeX or ACP Agents.
|
|
176
|
+
|
|
171
177
|
Use `init --reset` only for an explicitly requested full reset. Without `--profile` it resets the default Profile; pass `--profile <directory>` to reset another Profile. It preserves backups under `.reset-backups`.
|
|
172
178
|
|
|
173
179
|
## After Code Changes
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { codexVersionIssue, MINIMUM_CODEX_VERSION } from "../codex/CodexVersion.js";
|
|
3
|
+
import { compareSemanticVersions } from "../utils/semanticVersion.js";
|
|
4
|
+
export { compareSemanticVersions } from "../utils/semanticVersion.js";
|
|
2
5
|
|
|
3
6
|
export type SupportedAgentId = "codex" | "traex";
|
|
4
7
|
export type SupportedAgentState = "missing" | "outdated" | "ready";
|
|
@@ -29,6 +32,8 @@ export interface SupportedAgentInspection {
|
|
|
29
32
|
installedVersion?: string;
|
|
30
33
|
latestVersion?: string;
|
|
31
34
|
latestCheckFailed?: boolean;
|
|
35
|
+
minimumVersion?: string;
|
|
36
|
+
compatibilityIssue?: string;
|
|
32
37
|
action?: {
|
|
33
38
|
kind: AgentMaintenanceKind;
|
|
34
39
|
command: string;
|
|
@@ -100,6 +105,15 @@ async function inspectCodex(
|
|
|
100
105
|
const installedVersion = commandVersion(installed);
|
|
101
106
|
if (!installedVersion) return missingInspection("codex", "Codex", platform, env);
|
|
102
107
|
|
|
108
|
+
const compatibilityIssue = codexVersionIssue(installedVersion);
|
|
109
|
+
if (compatibilityIssue) {
|
|
110
|
+
return {
|
|
111
|
+
id: "codex", name: "Codex", state: "outdated", installedVersion,
|
|
112
|
+
minimumVersion: MINIMUM_CODEX_VERSION, compatibilityIssue,
|
|
113
|
+
action: maintenanceAction("codex", "upgrade", platform, env),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
103
117
|
const latest = await run(capturedCommand(
|
|
104
118
|
"npm",
|
|
105
119
|
["view", CODEX_PACKAGE, "version", "--json"],
|
|
@@ -294,46 +308,6 @@ function versionInText(value: string): string | undefined {
|
|
|
294
308
|
return /\b(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)\b/u.exec(value)?.[1];
|
|
295
309
|
}
|
|
296
310
|
|
|
297
|
-
export function compareSemanticVersions(left: string, right: string): number {
|
|
298
|
-
const leftVersion = parseSemanticVersion(left);
|
|
299
|
-
const rightVersion = parseSemanticVersion(right);
|
|
300
|
-
if (!leftVersion || !rightVersion) return 0;
|
|
301
|
-
for (let index = 0; index < 3; index += 1) {
|
|
302
|
-
const difference = leftVersion.core[index]! - rightVersion.core[index]!;
|
|
303
|
-
if (difference !== 0) return difference;
|
|
304
|
-
}
|
|
305
|
-
if (!leftVersion.prerelease && !rightVersion.prerelease) return 0;
|
|
306
|
-
if (!leftVersion.prerelease) return 1;
|
|
307
|
-
if (!rightVersion.prerelease) return -1;
|
|
308
|
-
const length = Math.max(leftVersion.prerelease.length, rightVersion.prerelease.length);
|
|
309
|
-
for (let index = 0; index < length; index += 1) {
|
|
310
|
-
const leftPart = leftVersion.prerelease[index];
|
|
311
|
-
const rightPart = rightVersion.prerelease[index];
|
|
312
|
-
if (leftPart === undefined) return -1;
|
|
313
|
-
if (rightPart === undefined) return 1;
|
|
314
|
-
if (leftPart === rightPart) continue;
|
|
315
|
-
const leftNumber = /^\d+$/u.test(leftPart) ? Number(leftPart) : undefined;
|
|
316
|
-
const rightNumber = /^\d+$/u.test(rightPart) ? Number(rightPart) : undefined;
|
|
317
|
-
if (leftNumber !== undefined && rightNumber !== undefined) return leftNumber - rightNumber;
|
|
318
|
-
if (leftNumber !== undefined) return -1;
|
|
319
|
-
if (rightNumber !== undefined) return 1;
|
|
320
|
-
return leftPart.localeCompare(rightPart);
|
|
321
|
-
}
|
|
322
|
-
return 0;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
function parseSemanticVersion(value: string): {
|
|
326
|
-
core: [number, number, number];
|
|
327
|
-
prerelease?: string[];
|
|
328
|
-
} | undefined {
|
|
329
|
-
const match = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/u.exec(value);
|
|
330
|
-
if (!match) return undefined;
|
|
331
|
-
return {
|
|
332
|
-
core: [Number(match[1]), Number(match[2]), Number(match[3])],
|
|
333
|
-
...(match[4] ? { prerelease: match[4].split(".") } : {}),
|
|
334
|
-
};
|
|
335
|
-
}
|
|
336
|
-
|
|
337
311
|
export function runAgentCommand(command: AgentCommandSpec): Promise<AgentCommandResult> {
|
|
338
312
|
return new Promise((resolve) => {
|
|
339
313
|
const invocation = resolveInvocation(command);
|
|
@@ -12,7 +12,7 @@ export function selectableDefaultAgents(
|
|
|
12
12
|
const supported = new Map(inspections.map((inspection) => [inspection.id, inspection]));
|
|
13
13
|
return configured.flatMap((agent) => {
|
|
14
14
|
const inspection = supported.get(agent.name as SupportedAgentInspection["id"]);
|
|
15
|
-
if (!inspection?.installedVersion) return [];
|
|
15
|
+
if (!inspection?.installedVersion || inspection.compatibilityIssue) return [];
|
|
16
16
|
return [{
|
|
17
17
|
...agent,
|
|
18
18
|
installedVersion: inspection.installedVersion,
|
package/src/cli.ts
CHANGED
|
@@ -338,6 +338,9 @@ async function initCommand(
|
|
|
338
338
|
);
|
|
339
339
|
const configuredAgents = readConfiguredAgentSelection(paths.config.path).agents.map((agent) => agent.name);
|
|
340
340
|
const configuredAgentNames = new Set(configuredAgents);
|
|
341
|
+
for (const agent of inspectedAgents) {
|
|
342
|
+
if (configuredAgentNames.has(agent.id) && agent.compatibilityIssue) throw new Error(agent.compatibilityIssue);
|
|
343
|
+
}
|
|
341
344
|
const agents = inspectedAgents.map((agent) => ({
|
|
342
345
|
...agent,
|
|
343
346
|
configured: configuredAgentNames.has(agent.id),
|
|
@@ -554,6 +557,8 @@ async function configureAgentsAndDefault(
|
|
|
554
557
|
|
|
555
558
|
const choices = selectableDefaultAgents(configured.agents, inspections);
|
|
556
559
|
if (choices.length === 0) {
|
|
560
|
+
const issue = inspections.find((inspection) => inspection.compatibilityIssue)?.compatibilityIssue;
|
|
561
|
+
if (issue) throw new Error(issue);
|
|
557
562
|
throw new Error(cliText(
|
|
558
563
|
"No installed supported Agent was detected. Install Codex or TraeX, then run agentbot init again.",
|
|
559
564
|
"没有检测到已安装且受支持的 Agent。请安装 Codex 或 TraeX,然后重新运行 agentbot init。",
|
|
@@ -679,6 +684,10 @@ function printInteractiveAgentInspection(
|
|
|
679
684
|
inspection: SupportedAgentInspection,
|
|
680
685
|
ui: InitUi,
|
|
681
686
|
): void {
|
|
687
|
+
if (inspection.compatibilityIssue) {
|
|
688
|
+
ui.warn(inspection.compatibilityIssue);
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
682
691
|
if (inspection.state === "missing") {
|
|
683
692
|
ui.info(cliText(
|
|
684
693
|
`${inspection.name}: not installed`,
|
|
@@ -710,6 +719,10 @@ function printAgentInspection(
|
|
|
710
719
|
inspection: SupportedAgentInspection,
|
|
711
720
|
output: NodeJS.WriteStream,
|
|
712
721
|
): void {
|
|
722
|
+
if (inspection.compatibilityIssue) {
|
|
723
|
+
output.write(` ${inspection.compatibilityIssue}\n`);
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
713
726
|
if (inspection.state === "missing") {
|
|
714
727
|
output.write(cliText(
|
|
715
728
|
` ${inspection.name}: not installed\n`,
|
|
@@ -9,6 +9,16 @@ const TASK_COMPLETE = Buffer.from('"type":"task_complete"');
|
|
|
9
9
|
const TURN_ABORTED = Buffer.from('"type":"turn_aborted"');
|
|
10
10
|
const PATTERN_OVERLAP = Math.max(TASK_STARTED.length, TASK_COMPLETE.length, TURN_ABORTED.length) - 1;
|
|
11
11
|
|
|
12
|
+
interface RolloutState {
|
|
13
|
+
size: number;
|
|
14
|
+
mtimeMs: number;
|
|
15
|
+
ctimeMs: number;
|
|
16
|
+
ino: number;
|
|
17
|
+
dev: number;
|
|
18
|
+
tail: Buffer;
|
|
19
|
+
task: { active: boolean; turnId?: string };
|
|
20
|
+
}
|
|
21
|
+
|
|
12
22
|
interface ThreadPathRow {
|
|
13
23
|
id: string;
|
|
14
24
|
rollout_path: string;
|
|
@@ -35,6 +45,7 @@ export interface CodexLocalThreadSettings {
|
|
|
35
45
|
* even while Codex Desktop is actively appending events to its rollout.
|
|
36
46
|
*/
|
|
37
47
|
export class CodexLocalActivityDetector {
|
|
48
|
+
private readonly rollouts = new Map<string, RolloutState>();
|
|
38
49
|
constructor(private readonly codexHome: string) {}
|
|
39
50
|
|
|
40
51
|
async activeThreadIds(threadIds: string[]): Promise<Set<string>> {
|
|
@@ -64,7 +75,7 @@ export class CodexLocalActivityDetector {
|
|
|
64
75
|
|
|
65
76
|
const active = new Map<string, string | undefined>();
|
|
66
77
|
for (const row of rows) {
|
|
67
|
-
const task = await latestTask(row.rollout_path);
|
|
78
|
+
const task = await latestTask(row.rollout_path, this.rollouts);
|
|
68
79
|
if (task.active) active.set(row.id, task.turnId);
|
|
69
80
|
}
|
|
70
81
|
return active;
|
|
@@ -104,16 +115,31 @@ export class CodexLocalActivityDetector {
|
|
|
104
115
|
}
|
|
105
116
|
}
|
|
106
117
|
|
|
107
|
-
async function latestTask(rolloutPath: string): Promise<{ active: boolean; turnId?: string }> {
|
|
118
|
+
async function latestTask(rolloutPath: string, cache: Map<string, RolloutState>): Promise<{ active: boolean; turnId?: string }> {
|
|
108
119
|
let file;
|
|
109
120
|
try {
|
|
110
121
|
file = await open(rolloutPath, "r");
|
|
111
|
-
const size =
|
|
122
|
+
const { size, mtimeMs, ctimeMs, ino, dev } = await file.stat();
|
|
123
|
+
const previous = cache.get(rolloutPath);
|
|
124
|
+
const sameFile = previous?.ino === ino && previous.dev === dev;
|
|
125
|
+
if (sameFile && previous.size === size && previous.mtimeMs === mtimeMs && previous.ctimeMs === ctimeMs) {
|
|
126
|
+
return previous.task;
|
|
127
|
+
}
|
|
128
|
+
let stopAt = 0;
|
|
129
|
+
let task: RolloutState["task"] = { active: false };
|
|
130
|
+
if (sameFile && size > previous.size) {
|
|
131
|
+
const boundary = Buffer.alloc(previous.tail.length);
|
|
132
|
+
await file.read(boundary, 0, boundary.length, previous.size - boundary.length);
|
|
133
|
+
if (boundary.equals(previous.tail)) {
|
|
134
|
+
stopAt = Math.max(0, previous.size - PATTERN_OVERLAP);
|
|
135
|
+
task = previous.task;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
112
138
|
let position = size;
|
|
113
139
|
let laterPrefix = Buffer.alloc(0);
|
|
114
140
|
|
|
115
|
-
while (position >
|
|
116
|
-
const length = Math.min(READ_CHUNK_SIZE, position);
|
|
141
|
+
while (position > stopAt) {
|
|
142
|
+
const length = Math.min(READ_CHUNK_SIZE, position - stopAt);
|
|
117
143
|
position -= length;
|
|
118
144
|
const chunk = Buffer.allocUnsafe(length);
|
|
119
145
|
await file.read(chunk, 0, length, position);
|
|
@@ -123,14 +149,20 @@ async function latestTask(rolloutPath: string): Promise<{ active: boolean; turnI
|
|
|
123
149
|
const abortedAt = searchable.lastIndexOf(TURN_ABORTED);
|
|
124
150
|
const terminalAt = Math.max(completedAt, abortedAt);
|
|
125
151
|
if (startedAt >= 0 || terminalAt >= 0) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
152
|
+
task = startedAt <= terminalAt ? { active: false }
|
|
153
|
+
: { active: true, turnId: await readTurnId(file, position + startedAt, size) };
|
|
154
|
+
break;
|
|
129
155
|
}
|
|
130
156
|
laterPrefix = chunk.subarray(0, Math.min(PATTERN_OVERLAP, chunk.length));
|
|
131
157
|
}
|
|
132
|
-
|
|
158
|
+
const tail = Buffer.alloc(Math.min(1_024, size));
|
|
159
|
+
await file.read(tail, 0, tail.length, size - tail.length);
|
|
160
|
+
cache.delete(rolloutPath);
|
|
161
|
+
cache.set(rolloutPath, { size, mtimeMs, ctimeMs, ino, dev, tail, task });
|
|
162
|
+
if (cache.size > 512) cache.delete(cache.keys().next().value!);
|
|
163
|
+
return task;
|
|
133
164
|
} catch {
|
|
165
|
+
cache.delete(rolloutPath);
|
|
134
166
|
return { active: false };
|
|
135
167
|
} finally {
|
|
136
168
|
await file?.close();
|
|
@@ -11,12 +11,15 @@ import {
|
|
|
11
11
|
} from "../runtime/agentEnvironment.js";
|
|
12
12
|
import type { AgentProcessInfo } from "../runtime/types.js";
|
|
13
13
|
import { spawnStdioCommand } from "../utils/spawnCommand.js";
|
|
14
|
+
import { assertSupportedCodexVersion } from "./CodexVersion.js";
|
|
14
15
|
|
|
15
16
|
export class CodexProcessManager implements AppServerClientProvider {
|
|
16
17
|
private static readonly RELEASE_TIMEOUT_MS = 5_000;
|
|
17
18
|
private client?: AppServerConnection;
|
|
18
19
|
private child?: ChildProcessWithoutNullStreams;
|
|
19
20
|
private version?: string;
|
|
21
|
+
private agentFamily?: "codex" | "traex";
|
|
22
|
+
private initializing?: Promise<AppServerClient>;
|
|
20
23
|
private readonly disconnectListeners = new Set<(error: Error) => void>();
|
|
21
24
|
|
|
22
25
|
constructor(
|
|
@@ -28,7 +31,18 @@ export class CodexProcessManager implements AppServerClientProvider {
|
|
|
28
31
|
) {}
|
|
29
32
|
|
|
30
33
|
async getClient(): Promise<AppServerClient> {
|
|
34
|
+
if (this.initializing) return this.initializing;
|
|
31
35
|
if (this.client) return this.client;
|
|
36
|
+
const initialization = this.startClient();
|
|
37
|
+
this.initializing = initialization;
|
|
38
|
+
try {
|
|
39
|
+
return await initialization;
|
|
40
|
+
} finally {
|
|
41
|
+
if (this.initializing === initialization) this.initializing = undefined;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private async startClient(): Promise<AppServerClient> {
|
|
32
46
|
this.version = undefined;
|
|
33
47
|
const environmentContext = this.environmentContext();
|
|
34
48
|
const child = spawnStdioCommand(
|
|
@@ -52,13 +66,25 @@ export class CodexProcessManager implements AppServerClientProvider {
|
|
|
52
66
|
const error = new Error(`App Server exited (code=${code ?? "null"}, signal=${signal ?? "null"}).`);
|
|
53
67
|
for (const listener of this.disconnectListeners) listener(error);
|
|
54
68
|
});
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
69
|
+
try {
|
|
70
|
+
const initializeResult = await client.request("initialize", {
|
|
71
|
+
clientInfo: { name: "agent-bot", title: "Agent Bot", version: "0.1.0" },
|
|
72
|
+
capabilities: { experimentalApi: true },
|
|
73
|
+
});
|
|
74
|
+
this.version = initializedAgentVersion(initializeResult);
|
|
75
|
+
const userAgent = initializeResult && typeof initializeResult === "object"
|
|
76
|
+
? (initializeResult as Record<string, unknown>).userAgent : undefined;
|
|
77
|
+
if (!this.getAgentFamily() && typeof userAgent === "string") {
|
|
78
|
+
if (/^codex(?:-cli)?[ /]/iu.test(userAgent)) this.agentFamily = "codex";
|
|
79
|
+
else if (/^trae(?:cli|x)?[ /]/iu.test(userAgent)) this.agentFamily = "traex";
|
|
80
|
+
}
|
|
81
|
+
if (this.getAgentFamily() === "codex") assertSupportedCodexVersion(this.version);
|
|
82
|
+
client.notify("initialized", {});
|
|
83
|
+
return client;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
if (this.client === client) this.close();
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
62
88
|
}
|
|
63
89
|
|
|
64
90
|
getProcessInfo(): AgentProcessInfo {
|
|
@@ -70,6 +96,17 @@ export class CodexProcessManager implements AppServerClientProvider {
|
|
|
70
96
|
};
|
|
71
97
|
}
|
|
72
98
|
|
|
99
|
+
getAgentFamily(): "codex" | "traex" | undefined {
|
|
100
|
+
if (this.agentFamily) return this.agentFamily;
|
|
101
|
+
const candidates = [this.command, this.args[0], this.environmentContext().agentName];
|
|
102
|
+
for (const value of candidates) {
|
|
103
|
+
const executable = value?.replaceAll("\\", "/").split("/").at(-1) ?? "";
|
|
104
|
+
if (/^codex(?:\.(?:exe|cmd|bat|m?js))?$/iu.test(executable)) return "codex";
|
|
105
|
+
if (/^(?:traex|traecli)(?:\.(?:exe|cmd|bat|m?js))?$/iu.test(executable)) return "traex";
|
|
106
|
+
}
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
|
|
73
110
|
getCodexHome(): string {
|
|
74
111
|
return this.env.CODEX_HOME ?? process.env.CODEX_HOME ?? path.join(os.homedir(), ".codex");
|
|
75
112
|
}
|