@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.
Files changed (37) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/codex/CodexRuntime.js +8 -3
  3. package/dist/codex/CodexRuntime.js.map +1 -1
  4. package/dist/codex/ThreadWriterProcess.js +344 -0
  5. package/dist/codex/ThreadWriterProcess.js.map +1 -0
  6. package/dist/console/ConsoleTurnPresenter.js +1 -0
  7. package/dist/console/ConsoleTurnPresenter.js.map +1 -1
  8. package/dist/feishu/CardRenderer.js +83 -0
  9. package/dist/feishu/CardRenderer.js.map +1 -1
  10. package/dist/feishu/FeishuTurnPresenter.js +13 -3
  11. package/dist/feishu/FeishuTurnPresenter.js.map +1 -1
  12. package/dist/index.js +2 -2
  13. package/dist/index.js.map +1 -1
  14. package/dist/logging/errorLogValue.js +18 -0
  15. package/dist/logging/errorLogValue.js.map +1 -0
  16. package/dist/logging/logger.js +4 -0
  17. package/dist/logging/logger.js.map +1 -1
  18. package/dist/presentation/OutboundRouter.js +2 -2
  19. package/dist/presentation/OutboundRouter.js.map +1 -1
  20. package/dist/proxy/ProxySessionController.js +329 -15
  21. package/dist/proxy/ProxySessionController.js.map +1 -1
  22. package/dist/supervision/SafeRestartScheduler.js +1 -1
  23. package/dist/supervision/SafeRestartScheduler.js.map +1 -1
  24. package/npm-shrinkwrap.json +2 -2
  25. package/package.json +1 -1
  26. package/src/codex/CodexRuntime.ts +8 -2
  27. package/src/codex/ThreadWriterProcess.ts +385 -0
  28. package/src/console/ConsoleTurnPresenter.ts +2 -1
  29. package/src/feishu/CardRenderer.ts +109 -0
  30. package/src/feishu/FeishuTurnPresenter.ts +16 -3
  31. package/src/index.ts +2 -2
  32. package/src/logging/errorLogValue.ts +26 -0
  33. package/src/logging/logger.ts +4 -0
  34. package/src/presentation/OutboundRouter.ts +15 -3
  35. package/src/proxy/ProxySessionController.ts +459 -24
  36. package/src/runtime/types.ts +1 -0
  37. 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 ?? 15_000;
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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"}
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@keyou007/agent-bot",
3
- "version": "0.1.18",
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.18",
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keyou007/agent-bot",
3
- "version": "0.1.18",
3
+ "version": "0.1.19-alpha.0",
4
4
  "type": "module",
5
5
  "description": "Use Codex, TraeX, and compatible coding agents from Lark/Feishu.",
6
6
  "license": "GPL-3.0-only",
@@ -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
- const codexHome = provider.getCodexHome?.();
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<void> {
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(sessionId: string, message: string): Promise<void> {
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(