@keyou007/agent-bot 0.1.18 → 0.1.19-alpha.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/CHANGELOG.md +6 -0
- package/dist/codex/CodexRuntime.js +8 -3
- package/dist/codex/CodexRuntime.js.map +1 -1
- package/dist/codex/ThreadWriterProcess.js +344 -0
- package/dist/codex/ThreadWriterProcess.js.map +1 -0
- package/dist/console/ConsoleTurnPresenter.js +1 -0
- package/dist/console/ConsoleTurnPresenter.js.map +1 -1
- package/dist/feishu/CardRenderer.js +83 -0
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/feishu/FeishuTurnPresenter.js +13 -3
- package/dist/feishu/FeishuTurnPresenter.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/logging/errorLogValue.js +18 -0
- package/dist/logging/errorLogValue.js.map +1 -0
- package/dist/logging/logger.js +4 -0
- package/dist/logging/logger.js.map +1 -1
- package/dist/presentation/OutboundRouter.js +2 -2
- package/dist/presentation/OutboundRouter.js.map +1 -1
- package/dist/proxy/ProxySessionController.js +329 -15
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/supervision/SafeRestartScheduler.js +1 -1
- package/dist/supervision/SafeRestartScheduler.js.map +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/src/codex/CodexRuntime.ts +8 -2
- package/src/codex/ThreadWriterProcess.ts +385 -0
- package/src/console/ConsoleTurnPresenter.ts +2 -1
- package/src/feishu/CardRenderer.ts +109 -0
- package/src/feishu/FeishuTurnPresenter.ts +16 -3
- package/src/index.ts +2 -2
- package/src/logging/errorLogValue.ts +26 -0
- package/src/logging/logger.ts +4 -0
- package/src/presentation/OutboundRouter.ts +15 -3
- package/src/proxy/ProxySessionController.ts +459 -24
- package/src/runtime/types.ts +1 -0
- package/src/supervision/SafeRestartScheduler.ts +1 -1
|
@@ -94,7 +94,7 @@ export class SafeRestartScheduler {
|
|
|
94
94
|
await this.restart(reason, scheduleId, state);
|
|
95
95
|
return;
|
|
96
96
|
}
|
|
97
|
-
const quietPeriodMs = this.options.quietPeriodMs ??
|
|
97
|
+
const quietPeriodMs = this.options.quietPeriodMs ?? 5_000;
|
|
98
98
|
const latestInboundAt = state.latestInboundAt ?? "";
|
|
99
99
|
if (this.idleSince === undefined || this.idleInboundAt !== latestInboundAt) {
|
|
100
100
|
this.idleSince = Date.now();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SafeRestartScheduler.js","sourceRoot":"","sources":["../../src/supervision/SafeRestartScheduler.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"SafeRestartScheduler.js","sourceRoot":"","sources":["../../src/supervision/SafeRestartScheduler.ts"],"names":[],"mappings":"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"}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keyou007/agent-bot",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.19-alpha.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@keyou007/agent-bot",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.19-alpha.0",
|
|
10
10
|
"license": "GPL-3.0-only",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@agentclientprotocol/sdk": "1.2.1",
|
package/package.json
CHANGED
|
@@ -26,6 +26,7 @@ import { AppServerRequestError } from "./AppServerConnection.js";
|
|
|
26
26
|
import { mapCodexNotification } from "./CodexEventMapper.js";
|
|
27
27
|
import { CodexLocalActivityDetector } from "./CodexLocalActivityDetector.js";
|
|
28
28
|
import { detectProjectlessWorkspace } from "./ProjectlessWorkspace.js";
|
|
29
|
+
import { threadWriterLockPath } from "./ThreadWriterProcess.js";
|
|
29
30
|
|
|
30
31
|
const WINDOWS_SCREENSHOT_DEVELOPER_INSTRUCTIONS = [
|
|
31
32
|
"When capturing any screenshot on Windows, use one fresh process and make it Per-Monitor DPI Aware V2 before loading System.Windows.Forms, System.Drawing, or UI Automation, and before calling any screen, window, or bounds API.",
|
|
@@ -88,14 +89,15 @@ export class CodexRuntime implements AgentRuntime {
|
|
|
88
89
|
private readonly unsubscribeDisconnect?: () => void;
|
|
89
90
|
private readonly sessionSyncs = new Map<string, Promise<RuntimeSession>>();
|
|
90
91
|
private readonly localActivityDetector?: CodexLocalActivityDetector;
|
|
92
|
+
private readonly codexHome?: string;
|
|
91
93
|
private threadListSortKey: ThreadListSortKey = "recency_at";
|
|
92
94
|
|
|
93
95
|
constructor(
|
|
94
96
|
private readonly provider: AppServerClientProvider,
|
|
95
97
|
private readonly logger: Logger,
|
|
96
98
|
) {
|
|
97
|
-
|
|
98
|
-
if (codexHome) this.localActivityDetector = new CodexLocalActivityDetector(codexHome);
|
|
99
|
+
this.codexHome = provider.getCodexHome?.();
|
|
100
|
+
if (this.codexHome) this.localActivityDetector = new CodexLocalActivityDetector(this.codexHome);
|
|
99
101
|
this.unsubscribeDisconnect = provider.onDisconnect?.((error) => this.handleDisconnect(error));
|
|
100
102
|
}
|
|
101
103
|
|
|
@@ -107,6 +109,10 @@ export class CodexRuntime implements AgentRuntime {
|
|
|
107
109
|
return this.provider.getProcessInfo?.() ?? {};
|
|
108
110
|
}
|
|
109
111
|
|
|
112
|
+
getThreadWriterLockPath(remoteSessionId: string): string | undefined {
|
|
113
|
+
return this.codexHome ? threadWriterLockPath(this.codexHome, remoteSessionId) : undefined;
|
|
114
|
+
}
|
|
115
|
+
|
|
110
116
|
async createSession(input: CreateRuntimeSessionInput): Promise<RuntimeSession> {
|
|
111
117
|
const client = await this.client();
|
|
112
118
|
const response = await client.request<ThreadResponse>("thread/start", {
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { access, readdir } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
const PROCESS_INSPECTION_TIMEOUT_MS = 10_000;
|
|
6
|
+
const MAX_PROCESS_OUTPUT_BYTES = 1024 * 1024;
|
|
7
|
+
const LOCK_INSPECTION_CONCURRENCY = 4;
|
|
8
|
+
const THREAD_WRITER_LOCK_FILE_PATTERN = /^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.lock$/iu;
|
|
9
|
+
|
|
10
|
+
export interface ThreadWriterProcess {
|
|
11
|
+
writerPid: number;
|
|
12
|
+
writerProcessName: string;
|
|
13
|
+
writerStartedAt?: string;
|
|
14
|
+
applicationPid: number;
|
|
15
|
+
applicationProcessName: string;
|
|
16
|
+
applicationStartedAt?: string;
|
|
17
|
+
displayName: string;
|
|
18
|
+
canClose: boolean;
|
|
19
|
+
commandLine?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ThreadWriterProcessController {
|
|
23
|
+
inspect(lockPath: string): Promise<ThreadWriterProcess[]>;
|
|
24
|
+
inspectApplicationThreadIds?(lockPath: string, owner: ThreadWriterProcess): Promise<string[]>;
|
|
25
|
+
close(process: ThreadWriterProcess, force: boolean): Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class SystemThreadWriterProcessController implements ThreadWriterProcessController {
|
|
29
|
+
async inspect(lockPath: string): Promise<ThreadWriterProcess[]> {
|
|
30
|
+
try {
|
|
31
|
+
await access(lockPath);
|
|
32
|
+
} catch {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
return process.platform === "win32"
|
|
36
|
+
? inspectWindowsLockOwners(lockPath)
|
|
37
|
+
: inspectPosixLockOwners(lockPath);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async inspectApplicationThreadIds(lockPath: string, owner: ThreadWriterProcess): Promise<string[]> {
|
|
41
|
+
if (!owner.applicationStartedAt) return [];
|
|
42
|
+
let entries;
|
|
43
|
+
try {
|
|
44
|
+
entries = await readdir(path.dirname(lockPath), { withFileTypes: true });
|
|
45
|
+
} catch {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
const candidates = entries.flatMap((entry) => {
|
|
49
|
+
if (!entry.isFile()) return [];
|
|
50
|
+
const match = THREAD_WRITER_LOCK_FILE_PATTERN.exec(entry.name);
|
|
51
|
+
return match?.[1] ? [{ threadId: match[1], lockPath: path.join(path.dirname(lockPath), entry.name) }] : [];
|
|
52
|
+
});
|
|
53
|
+
const ownedThreadIds: string[] = [];
|
|
54
|
+
for (let index = 0; index < candidates.length; index += LOCK_INSPECTION_CONCURRENCY) {
|
|
55
|
+
const batch = candidates.slice(index, index + LOCK_INSPECTION_CONCURRENCY);
|
|
56
|
+
const results = await Promise.all(batch.map(async (candidate) => {
|
|
57
|
+
const owners = samePath(candidate.lockPath, lockPath)
|
|
58
|
+
? [owner]
|
|
59
|
+
: await this.inspect(candidate.lockPath);
|
|
60
|
+
return owners.some((candidateOwner) => sameApplication(candidateOwner, owner))
|
|
61
|
+
? candidate.threadId
|
|
62
|
+
: undefined;
|
|
63
|
+
}));
|
|
64
|
+
ownedThreadIds.push(...results.filter((threadId): threadId is string => Boolean(threadId)));
|
|
65
|
+
}
|
|
66
|
+
return ownedThreadIds;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async close(owner: ThreadWriterProcess, force: boolean): Promise<void> {
|
|
70
|
+
const targetPid = owner.applicationPid;
|
|
71
|
+
if (!owner.canClose) throw new Error("The writer process is not recognized as a closable Agent application.");
|
|
72
|
+
if (!owner.applicationStartedAt) throw new Error("The writer process does not have a stable start-time fingerprint.");
|
|
73
|
+
if (!Number.isSafeInteger(targetPid) || targetPid <= 4 || targetPid === process.pid) {
|
|
74
|
+
throw new Error(`Refusing to close unsafe process PID ${targetPid}.`);
|
|
75
|
+
}
|
|
76
|
+
if (process.platform === "win32") {
|
|
77
|
+
await executeWindowsPowerShell(WINDOWS_CLOSE_PROCESS_SCRIPT, {
|
|
78
|
+
AGENT_BOT_CLOSE_PID: String(targetPid),
|
|
79
|
+
AGENT_BOT_CLOSE_STARTED_AT: owner.applicationStartedAt,
|
|
80
|
+
AGENT_BOT_FORCE_CLOSE: force ? "true" : "false",
|
|
81
|
+
AGENT_BOT_WRITER_PID: String(owner.writerPid),
|
|
82
|
+
});
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const current = await readPosixProcess(targetPid);
|
|
86
|
+
if (!current || current.startedAt !== owner.applicationStartedAt) {
|
|
87
|
+
throw new Error(`Process PID ${targetPid} no longer matches the inspected writer application.`);
|
|
88
|
+
}
|
|
89
|
+
process.kill(targetPid, force ? "SIGKILL" : "SIGTERM");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function threadWriterLockPath(codexHome: string, threadId: string): string | undefined {
|
|
94
|
+
if (!THREAD_WRITER_LOCK_FILE_PATTERN.test(`${threadId}.lock`)) {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
return path.join(codexHome, "thread-writer-locks", `${threadId}.lock`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function parseWindowsLockOwners(value: string): ThreadWriterProcess[] {
|
|
101
|
+
const trimmed = value.trim();
|
|
102
|
+
if (!trimmed) return [];
|
|
103
|
+
const parsed: unknown = JSON.parse(trimmed);
|
|
104
|
+
const entries = Array.isArray(parsed) ? parsed : [parsed];
|
|
105
|
+
return entries.flatMap((entry) => {
|
|
106
|
+
if (!entry || typeof entry !== "object") return [];
|
|
107
|
+
const record = entry as Record<string, unknown>;
|
|
108
|
+
const writerPid = positiveInteger(record.writerPid);
|
|
109
|
+
const applicationPid = positiveInteger(record.applicationPid);
|
|
110
|
+
const writerProcessName = stringValue(record.writerProcessName);
|
|
111
|
+
const applicationProcessName = stringValue(record.applicationProcessName);
|
|
112
|
+
if (!writerPid || !applicationPid || !writerProcessName || !applicationProcessName) return [];
|
|
113
|
+
return [{
|
|
114
|
+
writerPid,
|
|
115
|
+
writerProcessName,
|
|
116
|
+
writerStartedAt: stringValue(record.writerStartedAt),
|
|
117
|
+
applicationPid,
|
|
118
|
+
applicationProcessName,
|
|
119
|
+
applicationStartedAt: stringValue(record.applicationStartedAt),
|
|
120
|
+
displayName: stringValue(record.displayName) ?? applicationProcessName,
|
|
121
|
+
canClose: record.canClose === true,
|
|
122
|
+
commandLine: stringValue(record.commandLine),
|
|
123
|
+
}];
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function inspectWindowsLockOwners(lockPath: string): Promise<ThreadWriterProcess[]> {
|
|
128
|
+
const output = await executeWindowsPowerShell(WINDOWS_LOCK_OWNER_SCRIPT, {
|
|
129
|
+
AGENT_BOT_THREAD_LOCK_PATH: lockPath,
|
|
130
|
+
});
|
|
131
|
+
return parseWindowsLockOwners(output);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function inspectPosixLockOwners(lockPath: string): Promise<ThreadWriterProcess[]> {
|
|
135
|
+
const pids = await posixLockOwnerPids(lockPath);
|
|
136
|
+
const owners = await Promise.all(pids.map((pid) => inspectPosixProcess(pid)));
|
|
137
|
+
return owners.filter((owner): owner is ThreadWriterProcess => owner !== undefined);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function posixLockOwnerPids(lockPath: string): Promise<number[]> {
|
|
141
|
+
const lsof = await tryExecuteFile("lsof", ["-t", "--", lockPath]);
|
|
142
|
+
const raw = lsof.ok ? lsof.stdout : (await tryExecuteFile("fuser", [lockPath])).stdout;
|
|
143
|
+
return [...new Set(raw.match(/\d+/gu)?.map(Number).filter((pid) => Number.isSafeInteger(pid) && pid > 0) ?? [])];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function inspectPosixProcess(writerPid: number): Promise<ThreadWriterProcess | undefined> {
|
|
147
|
+
const writer = await readPosixProcess(writerPid);
|
|
148
|
+
if (!writer) return undefined;
|
|
149
|
+
let application = writer;
|
|
150
|
+
let cursor = writer;
|
|
151
|
+
for (let depth = 0; depth < 8 && cursor.parentPid > 1; depth += 1) {
|
|
152
|
+
const parent = await readPosixProcess(cursor.parentPid);
|
|
153
|
+
if (!parent) break;
|
|
154
|
+
if (/\/(?:Codex|ChatGPT)\.app\//iu.test(parent.commandLine)) {
|
|
155
|
+
application = parent;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
cursor = parent;
|
|
159
|
+
}
|
|
160
|
+
const canClose = isRecognizedWriter(writer.name, writer.commandLine);
|
|
161
|
+
return {
|
|
162
|
+
writerPid,
|
|
163
|
+
writerProcessName: writer.name,
|
|
164
|
+
writerStartedAt: writer.startedAt,
|
|
165
|
+
applicationPid: application.pid,
|
|
166
|
+
applicationProcessName: application.name,
|
|
167
|
+
applicationStartedAt: application.startedAt,
|
|
168
|
+
displayName: /\/(?:Codex|ChatGPT)\.app\//iu.test(application.commandLine)
|
|
169
|
+
? "Codex Desktop"
|
|
170
|
+
: writer.name,
|
|
171
|
+
canClose,
|
|
172
|
+
commandLine: writer.commandLine,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
interface PosixProcessInfo {
|
|
177
|
+
pid: number;
|
|
178
|
+
parentPid: number;
|
|
179
|
+
name: string;
|
|
180
|
+
startedAt?: string;
|
|
181
|
+
commandLine: string;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function readPosixProcess(pid: number): Promise<PosixProcessInfo | undefined> {
|
|
185
|
+
const [parent, name, startedAt, commandLine] = await Promise.all([
|
|
186
|
+
tryExecuteFile("ps", ["-p", String(pid), "-o", "ppid="]),
|
|
187
|
+
tryExecuteFile("ps", ["-p", String(pid), "-o", "comm="]),
|
|
188
|
+
tryExecuteFile("ps", ["-p", String(pid), "-o", "lstart="]),
|
|
189
|
+
tryExecuteFile("ps", ["-p", String(pid), "-o", "command="]),
|
|
190
|
+
]);
|
|
191
|
+
const parentPid = Number(parent.stdout.trim());
|
|
192
|
+
const processName = name.stdout.trim();
|
|
193
|
+
if (!parent.ok || !processName || !Number.isSafeInteger(parentPid)) return undefined;
|
|
194
|
+
return {
|
|
195
|
+
pid,
|
|
196
|
+
parentPid,
|
|
197
|
+
name: processName,
|
|
198
|
+
startedAt: startedAt.stdout.trim() || undefined,
|
|
199
|
+
commandLine: commandLine.stdout.trim(),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function isRecognizedWriter(name: string, commandLine: string): boolean {
|
|
204
|
+
const candidate = `${name} ${commandLine}`;
|
|
205
|
+
return /(?:^|[\\/\s])(?:codex|trae(?:x|cli)?)(?:\.exe|\.js)?(?:\s|$)/iu.test(candidate)
|
|
206
|
+
&& /app-server/iu.test(candidate);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async function executeWindowsPowerShell(script: string, extraEnv: Record<string, string>): Promise<string> {
|
|
210
|
+
const encoded = Buffer.from(script, "utf16le").toString("base64");
|
|
211
|
+
return executeFile(
|
|
212
|
+
"powershell.exe",
|
|
213
|
+
["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-EncodedCommand", encoded],
|
|
214
|
+
extraEnv,
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function executeFile(command: string, args: string[], extraEnv: Record<string, string> = {}): Promise<string> {
|
|
219
|
+
return new Promise((resolve, reject) => {
|
|
220
|
+
execFile(command, args, {
|
|
221
|
+
env: { ...process.env, ...extraEnv },
|
|
222
|
+
timeout: PROCESS_INSPECTION_TIMEOUT_MS,
|
|
223
|
+
maxBuffer: MAX_PROCESS_OUTPUT_BYTES,
|
|
224
|
+
windowsHide: true,
|
|
225
|
+
encoding: "utf8",
|
|
226
|
+
}, (error, stdout, stderr) => {
|
|
227
|
+
if (error) {
|
|
228
|
+
reject(new Error(`${command} failed: ${stderr.trim() || error.message}`, { cause: error }));
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
resolve(stdout);
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function tryExecuteFile(command: string, args: string[]): Promise<{ ok: boolean; stdout: string }> {
|
|
237
|
+
return executeFile(command, args).then(
|
|
238
|
+
(stdout) => ({ ok: true, stdout }),
|
|
239
|
+
() => ({ ok: false, stdout: "" }),
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function positiveInteger(value: unknown): number | undefined {
|
|
244
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : undefined;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function stringValue(value: unknown): string | undefined {
|
|
248
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function sameApplication(left: ThreadWriterProcess, right: ThreadWriterProcess): boolean {
|
|
252
|
+
return left.applicationPid === right.applicationPid
|
|
253
|
+
&& Boolean(left.applicationStartedAt)
|
|
254
|
+
&& left.applicationStartedAt === right.applicationStartedAt;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function samePath(left: string, right: string): boolean {
|
|
258
|
+
const normalizedLeft = path.resolve(left);
|
|
259
|
+
const normalizedRight = path.resolve(right);
|
|
260
|
+
return process.platform === "win32"
|
|
261
|
+
? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase()
|
|
262
|
+
: normalizedLeft === normalizedRight;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const WINDOWS_CLOSE_PROCESS_SCRIPT = String.raw`
|
|
266
|
+
$ErrorActionPreference = 'Stop'
|
|
267
|
+
$targetPid = [int]$env:AGENT_BOT_CLOSE_PID
|
|
268
|
+
$writerPid = [int]$env:AGENT_BOT_WRITER_PID
|
|
269
|
+
$expectedStartedAt = $env:AGENT_BOT_CLOSE_STARTED_AT
|
|
270
|
+
$forceClose = $env:AGENT_BOT_FORCE_CLOSE -eq 'true'
|
|
271
|
+
$process = Get-Process -Id $targetPid -ErrorAction Stop
|
|
272
|
+
$actualStartedAt = $process.StartTime.ToUniversalTime().ToString('o')
|
|
273
|
+
if ($actualStartedAt -ne $expectedStartedAt) {
|
|
274
|
+
throw "Process PID $targetPid no longer matches the inspected writer application."
|
|
275
|
+
}
|
|
276
|
+
if ($forceClose) {
|
|
277
|
+
& taskkill.exe /PID $targetPid /T /F | Out-Null
|
|
278
|
+
if ($LASTEXITCODE -ne 0) { throw "taskkill failed with exit code $LASTEXITCODE" }
|
|
279
|
+
} elseif ($targetPid -ne $writerPid -and $process.MainWindowHandle -ne 0) {
|
|
280
|
+
if (-not $process.CloseMainWindow()) {
|
|
281
|
+
throw "The application did not accept a close request."
|
|
282
|
+
}
|
|
283
|
+
} else {
|
|
284
|
+
Stop-Process -Id $targetPid -ErrorAction Stop
|
|
285
|
+
}
|
|
286
|
+
`;
|
|
287
|
+
|
|
288
|
+
const WINDOWS_LOCK_OWNER_SCRIPT = String.raw`
|
|
289
|
+
$ErrorActionPreference = 'Stop'
|
|
290
|
+
Add-Type @'
|
|
291
|
+
using System;
|
|
292
|
+
using System.Runtime.InteropServices;
|
|
293
|
+
using System.Text;
|
|
294
|
+
public static class AgentBotRestartManager {
|
|
295
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
296
|
+
public struct RM_UNIQUE_PROCESS {
|
|
297
|
+
public int dwProcessId;
|
|
298
|
+
public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
|
|
299
|
+
}
|
|
300
|
+
public enum RM_APP_TYPE { UnknownApp=0, MainWindow=1, OtherWindow=2, Service=3, Explorer=4, Console=5, Critical=1000 }
|
|
301
|
+
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
|
|
302
|
+
public struct RM_PROCESS_INFO {
|
|
303
|
+
public RM_UNIQUE_PROCESS Process;
|
|
304
|
+
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=256)] public string strAppName;
|
|
305
|
+
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=64)] public string strServiceShortName;
|
|
306
|
+
public RM_APP_TYPE ApplicationType;
|
|
307
|
+
public uint AppStatus;
|
|
308
|
+
public uint TSSessionId;
|
|
309
|
+
[MarshalAs(UnmanagedType.Bool)] public bool bRestartable;
|
|
310
|
+
}
|
|
311
|
+
[DllImport("rstrtmgr.dll", CharSet=CharSet.Unicode)]
|
|
312
|
+
public static extern int RmStartSession(out uint sessionHandle, int sessionFlags, StringBuilder sessionKey);
|
|
313
|
+
[DllImport("rstrtmgr.dll", CharSet=CharSet.Unicode)]
|
|
314
|
+
public static extern int RmRegisterResources(uint sessionHandle, uint fileCount, string[] files, uint applicationCount, RM_UNIQUE_PROCESS[] applications, uint serviceCount, string[] services);
|
|
315
|
+
[DllImport("rstrtmgr.dll")]
|
|
316
|
+
public static extern int RmGetList(uint sessionHandle, out uint needed, ref uint count, [In, Out] RM_PROCESS_INFO[] processes, ref uint rebootReasons);
|
|
317
|
+
[DllImport("rstrtmgr.dll")]
|
|
318
|
+
public static extern int RmEndSession(uint sessionHandle);
|
|
319
|
+
}
|
|
320
|
+
'@
|
|
321
|
+
|
|
322
|
+
function Get-AgentBotProcess([int]$processId) {
|
|
323
|
+
Get-CimInstance Win32_Process -Filter ("ProcessId=" + $processId) -ErrorAction SilentlyContinue
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function Get-AgentBotStartTime([int]$processId) {
|
|
327
|
+
try { (Get-Process -Id $processId -ErrorAction Stop).StartTime.ToUniversalTime().ToString('o') } catch { $null }
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
$lockPath = $env:AGENT_BOT_THREAD_LOCK_PATH
|
|
331
|
+
$key = New-Object Text.StringBuilder 64
|
|
332
|
+
[uint32]$handle = 0
|
|
333
|
+
$result = [AgentBotRestartManager]::RmStartSession([ref]$handle, 0, $key)
|
|
334
|
+
if ($result -ne 0) { throw "RmStartSession failed: $result" }
|
|
335
|
+
try {
|
|
336
|
+
$result = [AgentBotRestartManager]::RmRegisterResources($handle, 1, [string[]]@($lockPath), 0, $null, 0, $null)
|
|
337
|
+
if ($result -ne 0) { throw "RmRegisterResources failed: $result" }
|
|
338
|
+
[uint32]$needed = 0
|
|
339
|
+
[uint32]$count = 0
|
|
340
|
+
[uint32]$rebootReasons = 0
|
|
341
|
+
$result = [AgentBotRestartManager]::RmGetList($handle, [ref]$needed, [ref]$count, $null, [ref]$rebootReasons)
|
|
342
|
+
if ($result -eq 0) {
|
|
343
|
+
ConvertTo-Json -Compress -InputObject @()
|
|
344
|
+
return
|
|
345
|
+
}
|
|
346
|
+
if ($result -ne 234) { throw "RmGetList failed: $result" }
|
|
347
|
+
$items = New-Object 'AgentBotRestartManager+RM_PROCESS_INFO[]' $needed
|
|
348
|
+
$count = $needed
|
|
349
|
+
$result = [AgentBotRestartManager]::RmGetList($handle, [ref]$needed, [ref]$count, $items, [ref]$rebootReasons)
|
|
350
|
+
if ($result -ne 0) { throw "RmGetList failed: $result" }
|
|
351
|
+
$owners = @()
|
|
352
|
+
for ($index = 0; $index -lt $count; $index++) {
|
|
353
|
+
$writerPid = $items[$index].Process.dwProcessId
|
|
354
|
+
$writer = Get-AgentBotProcess $writerPid
|
|
355
|
+
if ($null -eq $writer) { continue }
|
|
356
|
+
$application = $writer
|
|
357
|
+
$cursor = $writer
|
|
358
|
+
for ($depth = 0; $depth -lt 8 -and $cursor.ParentProcessId -gt 1; $depth++) {
|
|
359
|
+
$parent = Get-AgentBotProcess $cursor.ParentProcessId
|
|
360
|
+
if ($null -eq $parent) { break }
|
|
361
|
+
if ($parent.Name -ieq 'ChatGPT.exe' -and $parent.ExecutablePath -match 'OpenAI[.\\]Codex') {
|
|
362
|
+
$application = $parent
|
|
363
|
+
break
|
|
364
|
+
}
|
|
365
|
+
$cursor = $parent
|
|
366
|
+
}
|
|
367
|
+
$recognized = (($writer.Name -match '^(codex|trae|traex|traecli)(.exe)?$') -or ($writer.CommandLine -match '(codex|trae|traex|traecli).*(app-server)'))
|
|
368
|
+
$desktop = $application.Name -ieq 'ChatGPT.exe' -and $application.ExecutablePath -match 'OpenAI[.\\]Codex'
|
|
369
|
+
$owners += [pscustomobject]@{
|
|
370
|
+
writerPid = $writerPid
|
|
371
|
+
writerProcessName = $writer.Name
|
|
372
|
+
writerStartedAt = Get-AgentBotStartTime $writerPid
|
|
373
|
+
applicationPid = [int]$application.ProcessId
|
|
374
|
+
applicationProcessName = $application.Name
|
|
375
|
+
applicationStartedAt = Get-AgentBotStartTime $application.ProcessId
|
|
376
|
+
displayName = $(if ($desktop) { 'Codex Desktop' } else { $writer.Name })
|
|
377
|
+
canClose = [bool]$recognized
|
|
378
|
+
commandLine = $writer.CommandLine
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
ConvertTo-Json -Compress -InputObject @($owners)
|
|
382
|
+
} finally {
|
|
383
|
+
[void][AgentBotRestartManager]::RmEndSession($handle)
|
|
384
|
+
}
|
|
385
|
+
`;
|
|
@@ -32,9 +32,10 @@ export class ConsoleTurnPresenter implements TurnPresenter {
|
|
|
32
32
|
return undefined;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
async failPendingTurn(sessionId: string, message: string): Promise<
|
|
35
|
+
async failPendingTurn(sessionId: string, message: string): Promise<boolean> {
|
|
36
36
|
const contextKey = this.contexts.get(sessionId);
|
|
37
37
|
if (contextKey) await this.outbound.sendText(contextKey, `${this.agentLabel(sessionId)} failed to start: ${message}`);
|
|
38
|
+
return false;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
async interruptTurnForRecovery(): Promise<void> {}
|
|
@@ -255,6 +255,26 @@ export interface ShellCommandCardView {
|
|
|
255
255
|
outputTruncated: boolean;
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
+
export interface ThreadWriterConflictCardView {
|
|
259
|
+
status: "occupied" | "force_required" | "released";
|
|
260
|
+
contextKey: string;
|
|
261
|
+
sessionId: string;
|
|
262
|
+
threadId: string;
|
|
263
|
+
taskTitle?: string;
|
|
264
|
+
notice?: string;
|
|
265
|
+
applicationIdle?: boolean;
|
|
266
|
+
owner?: {
|
|
267
|
+
displayName: string;
|
|
268
|
+
writerPid: number;
|
|
269
|
+
writerProcessName: string;
|
|
270
|
+
writerStartedAt?: string;
|
|
271
|
+
applicationPid: number;
|
|
272
|
+
applicationProcessName: string;
|
|
273
|
+
applicationStartedAt?: string;
|
|
274
|
+
canClose: boolean;
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
258
278
|
export class CardRenderer {
|
|
259
279
|
private readonly thinkingCardLayout: ThinkingCardLayout;
|
|
260
280
|
|
|
@@ -262,6 +282,95 @@ export class CardRenderer {
|
|
|
262
282
|
this.thinkingCardLayout = options.thinkingCardLayout ?? "grouped";
|
|
263
283
|
}
|
|
264
284
|
|
|
285
|
+
renderThreadWriterConflict(view: ThreadWriterConflictCardView): Record<string, unknown> {
|
|
286
|
+
if (view.status === "released") {
|
|
287
|
+
return sectionCard("任务占用已解除", [markdown([
|
|
288
|
+
view.taskTitle ? `**任务**:${inlineCode(view.taskTitle)}` : undefined,
|
|
289
|
+
view.notice ?? "请重新发送消息。",
|
|
290
|
+
].filter((line): line is string => Boolean(line)).join("\n"))], "green");
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const owner = view.owner;
|
|
294
|
+
const lines = [
|
|
295
|
+
view.taskTitle ? `**任务**:${inlineCode(view.taskTitle)}` : undefined,
|
|
296
|
+
owner ? `**进程**:${inlineCode(`${owner.applicationProcessName} (${owner.applicationPid})`)}` : undefined,
|
|
297
|
+
view.notice,
|
|
298
|
+
owner
|
|
299
|
+
? view.applicationIdle === true
|
|
300
|
+
? "> 无执行中任务,可安全关闭;也可 **Fork** 后继续发送消息。"
|
|
301
|
+
: view.applicationIdle === false
|
|
302
|
+
? "> 有任务执行中,关闭会中断任务;可 **Fork** 后继续发送消息。"
|
|
303
|
+
: "> 可 **Fork** 后继续发送消息;关闭进程后请重发消息。"
|
|
304
|
+
: "> 可 **Fork** 后继续发送消息;或手动关闭占用任务。",
|
|
305
|
+
].filter((line): line is string => Boolean(line));
|
|
306
|
+
const elements: Record<string, unknown>[] = [markdown(lines.join("\n"))];
|
|
307
|
+
const canClose = owner?.canClose
|
|
308
|
+
&& Boolean(owner.writerStartedAt)
|
|
309
|
+
&& Boolean(owner.applicationStartedAt);
|
|
310
|
+
const actions: Record<string, unknown>[] = [{
|
|
311
|
+
tag: "button",
|
|
312
|
+
text: {
|
|
313
|
+
tag: "plain_text",
|
|
314
|
+
content: "Fork",
|
|
315
|
+
},
|
|
316
|
+
type: "default",
|
|
317
|
+
size: "small",
|
|
318
|
+
behaviors: [{
|
|
319
|
+
type: "callback",
|
|
320
|
+
value: {
|
|
321
|
+
action: "thread_writer_fork",
|
|
322
|
+
contextKey: view.contextKey,
|
|
323
|
+
sessionId: view.sessionId,
|
|
324
|
+
threadId: view.threadId,
|
|
325
|
+
},
|
|
326
|
+
}],
|
|
327
|
+
}];
|
|
328
|
+
if (canClose && owner) {
|
|
329
|
+
actions.push({
|
|
330
|
+
tag: "button",
|
|
331
|
+
text: {
|
|
332
|
+
tag: "plain_text",
|
|
333
|
+
content: view.status === "force_required"
|
|
334
|
+
? `Force ${owner.applicationPid}`
|
|
335
|
+
: `Close ${owner.applicationPid}`,
|
|
336
|
+
},
|
|
337
|
+
type: "danger",
|
|
338
|
+
size: "small",
|
|
339
|
+
behaviors: [{
|
|
340
|
+
type: "callback",
|
|
341
|
+
value: {
|
|
342
|
+
action: "thread_writer_close",
|
|
343
|
+
contextKey: view.contextKey,
|
|
344
|
+
sessionId: view.sessionId,
|
|
345
|
+
threadId: view.threadId,
|
|
346
|
+
writerPid: owner.writerPid,
|
|
347
|
+
writerStartedAt: owner.writerStartedAt,
|
|
348
|
+
applicationPid: owner.applicationPid,
|
|
349
|
+
applicationStartedAt: owner.applicationStartedAt,
|
|
350
|
+
force: view.status === "force_required",
|
|
351
|
+
},
|
|
352
|
+
}],
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
elements.push({
|
|
356
|
+
tag: "column_set",
|
|
357
|
+
flex_mode: "none",
|
|
358
|
+
horizontal_spacing: "8px",
|
|
359
|
+
vertical_align: "center",
|
|
360
|
+
columns: actions.map((button) => ({
|
|
361
|
+
tag: "column",
|
|
362
|
+
width: "auto",
|
|
363
|
+
vertical_align: "center",
|
|
364
|
+
elements: [button],
|
|
365
|
+
})),
|
|
366
|
+
});
|
|
367
|
+
return sectionCard(
|
|
368
|
+
view.status === "force_required" ? "仍被占用" : "任务被占用",
|
|
369
|
+
elements,
|
|
370
|
+
view.status === "force_required" ? "red" : "orange",
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
|
|
265
374
|
renderShellCommandCard(view: ShellCommandCardView): Record<string, unknown> {
|
|
266
375
|
const rawOutput = normalizeTerminalOutput(view.output).trimEnd();
|
|
267
376
|
const displayOutput = truncateMiddle(
|
|
@@ -164,9 +164,13 @@ export class FeishuTurnPresenter {
|
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
-
async failPendingTurn(
|
|
167
|
+
async failPendingTurn(
|
|
168
|
+
sessionId: string,
|
|
169
|
+
message: string,
|
|
170
|
+
replacementCard?: Record<string, unknown>,
|
|
171
|
+
): Promise<boolean> {
|
|
168
172
|
const entry = this.pendingEntries.get(sessionId);
|
|
169
|
-
if (!entry) return;
|
|
173
|
+
if (!entry) return false;
|
|
170
174
|
this.pendingEntries.delete(sessionId);
|
|
171
175
|
this.stopElapsedUpdates(entry);
|
|
172
176
|
const completedAt = Date.now();
|
|
@@ -183,9 +187,18 @@ export class FeishuTurnPresenter {
|
|
|
183
187
|
await entry.scheduler?.flush(entry.state);
|
|
184
188
|
} catch (error) {
|
|
185
189
|
this.options.onError?.(error);
|
|
186
|
-
return;
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
if (replacementCard && entry.messageId) {
|
|
193
|
+
try {
|
|
194
|
+
await this.outbound.updateInteractiveCard(entry.messageId, replacementCard);
|
|
195
|
+
} catch (error) {
|
|
196
|
+
this.options.onError?.(error);
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
187
199
|
}
|
|
188
200
|
await this.syncThinkingCardReaction(entry);
|
|
201
|
+
return Boolean(replacementCard && entry.messageId);
|
|
189
202
|
}
|
|
190
203
|
|
|
191
204
|
async interruptTurnForRecovery(
|