@love-moon/ai-sdk 0.2.31 → 0.2.33
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/dist/client.d.ts +5 -0
- package/dist/client.js +31 -0
- package/dist/providers/claude-agent-sdk-session.d.ts +8 -0
- package/dist/providers/claude-agent-sdk-session.js +61 -6
- package/dist/providers/codex-app-server-session.d.ts +9 -0
- package/dist/providers/codex-app-server-session.js +84 -6
- package/dist/providers/kimi-cli-session.d.ts +8 -0
- package/dist/providers/kimi-cli-session.js +80 -7
- package/dist/providers/opencode-sdk-session.d.ts +9 -0
- package/dist/providers/opencode-sdk-session.js +82 -9
- package/package.json +2 -2
- package/dist/resume.d.ts +0 -26
- package/dist/resume.js +0 -380
- package/dist/tui-session.d.ts +0 -153
- package/dist/tui-session.js +0 -941
package/dist/client.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export class RemoteAiSession extends EventEmitter<[never]> {
|
|
|
9
9
|
};
|
|
10
10
|
useSessionFileReplyStreamValue: boolean;
|
|
11
11
|
sessionInfo: any;
|
|
12
|
+
currentTurnStatus: any;
|
|
12
13
|
snapshot: {
|
|
13
14
|
backend: undefined;
|
|
14
15
|
provider: undefined;
|
|
@@ -38,6 +39,7 @@ export class RemoteAiSession extends EventEmitter<[never]> {
|
|
|
38
39
|
model: any;
|
|
39
40
|
};
|
|
40
41
|
getSnapshot(): {
|
|
42
|
+
currentTurnStatus: any;
|
|
41
43
|
sessionInfo: any;
|
|
42
44
|
backend: undefined;
|
|
43
45
|
provider: undefined;
|
|
@@ -47,6 +49,7 @@ export class RemoteAiSession extends EventEmitter<[never]> {
|
|
|
47
49
|
};
|
|
48
50
|
usesSessionFileReplyStream(): boolean;
|
|
49
51
|
getSessionInfo(): any;
|
|
52
|
+
getCurrentTurnStatus(): any;
|
|
50
53
|
setSessionMessageHandler(handler: any): void;
|
|
51
54
|
setWorkingStatusHandler(handler: any): void;
|
|
52
55
|
setSessionReplyTarget(replyTo: any): void;
|
|
@@ -75,6 +78,7 @@ declare class LocalAiSessionProxy extends EventEmitter<[never]> {
|
|
|
75
78
|
};
|
|
76
79
|
useSessionFileReplyStreamValue: boolean;
|
|
77
80
|
sessionInfo: any;
|
|
81
|
+
currentTurnStatus: any;
|
|
78
82
|
session: any;
|
|
79
83
|
closed: boolean;
|
|
80
84
|
sessionMessageHandler: any;
|
|
@@ -94,6 +98,7 @@ declare class LocalAiSessionProxy extends EventEmitter<[never]> {
|
|
|
94
98
|
getSnapshot(): any;
|
|
95
99
|
usesSessionFileReplyStream(): boolean;
|
|
96
100
|
getSessionInfo(): any;
|
|
101
|
+
getCurrentTurnStatus(): any;
|
|
97
102
|
setSessionMessageHandler(handler: any): void;
|
|
98
103
|
setWorkingStatusHandler(handler: any): void;
|
|
99
104
|
setSessionReplyTarget(replyTarget: any): void;
|
package/dist/client.js
CHANGED
|
@@ -39,6 +39,7 @@ export class RemoteAiSession extends EventEmitter {
|
|
|
39
39
|
};
|
|
40
40
|
this.useSessionFileReplyStreamValue = true;
|
|
41
41
|
this.sessionInfo = null;
|
|
42
|
+
this.currentTurnStatus = null;
|
|
42
43
|
this.snapshot = {
|
|
43
44
|
backend: undefined,
|
|
44
45
|
provider: undefined,
|
|
@@ -135,6 +136,7 @@ export class RemoteAiSession extends EventEmitter {
|
|
|
135
136
|
getSnapshot() {
|
|
136
137
|
return {
|
|
137
138
|
...this.snapshot,
|
|
139
|
+
currentTurnStatus: this.getCurrentTurnStatus(),
|
|
138
140
|
sessionInfo: this.sessionInfo ? { ...this.sessionInfo } : null,
|
|
139
141
|
};
|
|
140
142
|
}
|
|
@@ -144,6 +146,9 @@ export class RemoteAiSession extends EventEmitter {
|
|
|
144
146
|
getSessionInfo() {
|
|
145
147
|
return this.sessionInfo ? { ...this.sessionInfo } : null;
|
|
146
148
|
}
|
|
149
|
+
getCurrentTurnStatus() {
|
|
150
|
+
return this.currentTurnStatus ? { ...this.currentTurnStatus } : null;
|
|
151
|
+
}
|
|
147
152
|
setSessionMessageHandler(handler) {
|
|
148
153
|
this.sessionMessageHandler = typeof handler === "function" ? handler : null;
|
|
149
154
|
}
|
|
@@ -304,6 +309,9 @@ export class RemoteAiSession extends EventEmitter {
|
|
|
304
309
|
if (snapshot?.useSessionFileReplyStream !== undefined) {
|
|
305
310
|
this.useSessionFileReplyStreamValue = Boolean(snapshot.useSessionFileReplyStream);
|
|
306
311
|
}
|
|
312
|
+
if (snapshot?.currentTurnStatus && typeof snapshot.currentTurnStatus === "object") {
|
|
313
|
+
this.currentTurnStatus = { ...snapshot.currentTurnStatus };
|
|
314
|
+
}
|
|
307
315
|
}
|
|
308
316
|
handleWorkerResponse(payload) {
|
|
309
317
|
const pending = this.pendingRequests.get(payload.id);
|
|
@@ -331,6 +339,9 @@ export class RemoteAiSession extends EventEmitter {
|
|
|
331
339
|
}
|
|
332
340
|
handleWorkerProgress(payload) {
|
|
333
341
|
const pending = this.pendingRequests.get(payload.requestId);
|
|
342
|
+
if (payload?.payload && typeof payload.payload === "object") {
|
|
343
|
+
this.currentTurnStatus = { ...payload.payload };
|
|
344
|
+
}
|
|
334
345
|
if (!pending || typeof pending.progressHandler !== "function") {
|
|
335
346
|
return;
|
|
336
347
|
}
|
|
@@ -363,6 +374,11 @@ export class RemoteAiSession extends EventEmitter {
|
|
|
363
374
|
if (name === "assistant_message" && typeof this.sessionMessageHandler === "function") {
|
|
364
375
|
await this.sessionMessageHandler(eventPayload);
|
|
365
376
|
}
|
|
377
|
+
if (name === "working_status") {
|
|
378
|
+
if (eventPayload && typeof eventPayload === "object") {
|
|
379
|
+
this.currentTurnStatus = { ...eventPayload };
|
|
380
|
+
}
|
|
381
|
+
}
|
|
366
382
|
if (name === "working_status" && typeof this.workingStatusHandler === "function") {
|
|
367
383
|
await this.workingStatusHandler(eventPayload);
|
|
368
384
|
}
|
|
@@ -395,6 +411,7 @@ class LocalAiSessionProxy extends EventEmitter {
|
|
|
395
411
|
};
|
|
396
412
|
this.useSessionFileReplyStreamValue = true;
|
|
397
413
|
this.sessionInfo = null;
|
|
414
|
+
this.currentTurnStatus = null;
|
|
398
415
|
this.session = null;
|
|
399
416
|
this.closed = false;
|
|
400
417
|
this.sessionMessageHandler = null;
|
|
@@ -428,6 +445,9 @@ class LocalAiSessionProxy extends EventEmitter {
|
|
|
428
445
|
this.threadIdValue = String(payload.sessionId);
|
|
429
446
|
}
|
|
430
447
|
}
|
|
448
|
+
if (eventName === "working_status" && payload && typeof payload === "object") {
|
|
449
|
+
this.currentTurnStatus = { ...payload };
|
|
450
|
+
}
|
|
431
451
|
this.emit(eventName, payload);
|
|
432
452
|
});
|
|
433
453
|
}
|
|
@@ -463,6 +483,7 @@ class LocalAiSessionProxy extends EventEmitter {
|
|
|
463
483
|
backend: this.backend,
|
|
464
484
|
sessionId: this.threadIdValue || undefined,
|
|
465
485
|
sessionInfo: this.sessionInfo,
|
|
486
|
+
currentTurnStatus: typeof session.getCurrentTurnStatus === "function" ? session.getCurrentTurnStatus() : this.currentTurnStatus,
|
|
466
487
|
useSessionFileReplyStream: this.useSessionFileReplyStreamValue,
|
|
467
488
|
workerReady: true,
|
|
468
489
|
};
|
|
@@ -481,6 +502,9 @@ class LocalAiSessionProxy extends EventEmitter {
|
|
|
481
502
|
...this.snapshot,
|
|
482
503
|
...sessionSnapshot,
|
|
483
504
|
backend: sessionSnapshot.backend || this.snapshot.backend,
|
|
505
|
+
currentTurnStatus: typeof this.session?.getCurrentTurnStatus === "function"
|
|
506
|
+
? this.session.getCurrentTurnStatus()
|
|
507
|
+
: sessionSnapshot.currentTurnStatus || this.currentTurnStatus || null,
|
|
484
508
|
sessionId: sessionSnapshot.sessionId || this.threadIdValue || undefined,
|
|
485
509
|
sessionInfo: typeof this.session?.getSessionInfo === "function"
|
|
486
510
|
? this.session.getSessionInfo()
|
|
@@ -489,6 +513,7 @@ class LocalAiSessionProxy extends EventEmitter {
|
|
|
489
513
|
}
|
|
490
514
|
return {
|
|
491
515
|
...this.snapshot,
|
|
516
|
+
currentTurnStatus: this.getCurrentTurnStatus(),
|
|
492
517
|
sessionInfo: this.sessionInfo ? { ...this.sessionInfo } : null,
|
|
493
518
|
};
|
|
494
519
|
}
|
|
@@ -504,6 +529,12 @@ class LocalAiSessionProxy extends EventEmitter {
|
|
|
504
529
|
}
|
|
505
530
|
return this.sessionInfo ? { ...this.sessionInfo } : null;
|
|
506
531
|
}
|
|
532
|
+
getCurrentTurnStatus() {
|
|
533
|
+
if (typeof this.session?.getCurrentTurnStatus === "function") {
|
|
534
|
+
return this.session.getCurrentTurnStatus();
|
|
535
|
+
}
|
|
536
|
+
return this.currentTurnStatus ? { ...this.currentTurnStatus } : null;
|
|
537
|
+
}
|
|
507
538
|
setSessionMessageHandler(handler) {
|
|
508
539
|
this.sessionMessageHandler = typeof handler === "function" ? handler : null;
|
|
509
540
|
if (typeof this.session?.setSessionMessageHandler === "function") {
|
|
@@ -31,6 +31,9 @@ export class ClaudeAgentSdkSession extends EventEmitter<[never]> {
|
|
|
31
31
|
} | null;
|
|
32
32
|
lastResult: any;
|
|
33
33
|
rateLimitInfo: any;
|
|
34
|
+
currentTurnStatus: any;
|
|
35
|
+
currentTurnActivityAt: number;
|
|
36
|
+
now: any;
|
|
34
37
|
turnDeadlineMs: any;
|
|
35
38
|
sdkModulePromise: Promise<any> | Promise<typeof import("@anthropic-ai/claude-agent-sdk")> | null;
|
|
36
39
|
env: any;
|
|
@@ -56,11 +59,13 @@ export class ClaudeAgentSdkSession extends EventEmitter<[never]> {
|
|
|
56
59
|
ready: boolean;
|
|
57
60
|
command: string;
|
|
58
61
|
} | null;
|
|
62
|
+
currentTurnStatus: any;
|
|
59
63
|
};
|
|
60
64
|
getSessionInfo(): {
|
|
61
65
|
backend: string;
|
|
62
66
|
sessionId: any;
|
|
63
67
|
} | null;
|
|
68
|
+
getCurrentTurnStatus(): any;
|
|
64
69
|
ensureSessionInfo(): Promise<{
|
|
65
70
|
backend: string;
|
|
66
71
|
sessionId: any;
|
|
@@ -82,6 +87,9 @@ export class ClaudeAgentSdkSession extends EventEmitter<[never]> {
|
|
|
82
87
|
setWorkingStatusHandler(handler: any): void;
|
|
83
88
|
setSessionReplyTarget(replyTo: any): void;
|
|
84
89
|
getCurrentReplyTarget(): string | undefined;
|
|
90
|
+
touchTurnActivity(): void;
|
|
91
|
+
updateCurrentTurnStatus(payload: any): void;
|
|
92
|
+
markTurnStartedStatus(): void;
|
|
85
93
|
emitWorkingStatus(payload: any, onProgress?: null): Promise<void>;
|
|
86
94
|
emitAssistantMessage(text: any): Promise<void>;
|
|
87
95
|
emitTerminalWorkingStatus(currentTurn: any, payload: any, onProgress?: null): Promise<void>;
|
|
@@ -156,6 +156,9 @@ export class ClaudeAgentSdkSession extends EventEmitter {
|
|
|
156
156
|
this.currentTurn = null;
|
|
157
157
|
this.lastResult = null;
|
|
158
158
|
this.rateLimitInfo = null;
|
|
159
|
+
this.currentTurnStatus = null;
|
|
160
|
+
this.currentTurnActivityAt = 0;
|
|
161
|
+
this.now = typeof options.now === "function" ? options.now : () => Date.now();
|
|
159
162
|
this.turnDeadlineMs = getBoundedEnvInt("CONDUCTOR_TURN_DEADLINE_MS", DEFAULT_TURN_DEADLINE_MS, MIN_TURN_DEADLINE_MS, MAX_TURN_DEADLINE_MS);
|
|
160
163
|
this.sdkModulePromise = null;
|
|
161
164
|
const envConfig = loadEnvConfig(options.configFile);
|
|
@@ -203,11 +206,15 @@ export class ClaudeAgentSdkSession extends EventEmitter {
|
|
|
203
206
|
command: `claude --resume ${this.sessionId}`,
|
|
204
207
|
}
|
|
205
208
|
: null,
|
|
209
|
+
currentTurnStatus: this.getCurrentTurnStatus(),
|
|
206
210
|
};
|
|
207
211
|
}
|
|
208
212
|
getSessionInfo() {
|
|
209
213
|
return this.sessionInfo ? { ...this.sessionInfo } : null;
|
|
210
214
|
}
|
|
215
|
+
getCurrentTurnStatus() {
|
|
216
|
+
return this.currentTurnStatus ? { ...this.currentTurnStatus } : null;
|
|
217
|
+
}
|
|
211
218
|
async ensureSessionInfo() {
|
|
212
219
|
return this.getSessionInfo();
|
|
213
220
|
}
|
|
@@ -248,6 +255,27 @@ export class ClaudeAgentSdkSession extends EventEmitter {
|
|
|
248
255
|
getCurrentReplyTarget() {
|
|
249
256
|
return this.activeReplyTarget || this.lastReplyTarget || undefined;
|
|
250
257
|
}
|
|
258
|
+
touchTurnActivity() {
|
|
259
|
+
this.currentTurnActivityAt = this.now();
|
|
260
|
+
}
|
|
261
|
+
updateCurrentTurnStatus(payload) {
|
|
262
|
+
const updatedAtMs = this.now();
|
|
263
|
+
this.currentTurnActivityAt = updatedAtMs;
|
|
264
|
+
this.currentTurnStatus = {
|
|
265
|
+
...payload,
|
|
266
|
+
updated_at: new Date(updatedAtMs).toISOString(),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
markTurnStartedStatus() {
|
|
270
|
+
this.updateCurrentTurnStatus({
|
|
271
|
+
source: CLAUDE_PROVIDER_VARIANT,
|
|
272
|
+
reply_in_progress: true,
|
|
273
|
+
replyTo: this.getCurrentReplyTarget(),
|
|
274
|
+
phase: "turn_started",
|
|
275
|
+
status_line: "claude is working",
|
|
276
|
+
thread_id: this.sessionId || undefined,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
251
279
|
async emitWorkingStatus(payload, onProgress = null) {
|
|
252
280
|
const normalized = {
|
|
253
281
|
source: CLAUDE_PROVIDER_VARIANT,
|
|
@@ -260,15 +288,18 @@ export class ClaudeAgentSdkSession extends EventEmitter {
|
|
|
260
288
|
reply_preview: payload?.reply_preview,
|
|
261
289
|
thread_id: this.sessionId || undefined,
|
|
262
290
|
};
|
|
291
|
+
this.updateCurrentTurnStatus(normalized);
|
|
292
|
+
const snapshot = this.getCurrentTurnStatus();
|
|
263
293
|
if (typeof onProgress === "function") {
|
|
264
|
-
onProgress(
|
|
294
|
+
onProgress(snapshot);
|
|
265
295
|
}
|
|
266
296
|
if (typeof this.workingStatusHandler === "function") {
|
|
267
|
-
await this.workingStatusHandler(
|
|
297
|
+
await this.workingStatusHandler(snapshot);
|
|
268
298
|
}
|
|
269
|
-
this.emit("working_status",
|
|
299
|
+
this.emit("working_status", snapshot);
|
|
270
300
|
}
|
|
271
301
|
async emitAssistantMessage(text) {
|
|
302
|
+
this.touchTurnActivity();
|
|
272
303
|
const payload = {
|
|
273
304
|
text,
|
|
274
305
|
preserveWhitespace: true,
|
|
@@ -342,8 +373,27 @@ export class ClaudeAgentSdkSession extends EventEmitter {
|
|
|
342
373
|
};
|
|
343
374
|
}
|
|
344
375
|
let timer = null;
|
|
345
|
-
|
|
376
|
+
let settled = false;
|
|
377
|
+
const schedule = (reject) => {
|
|
378
|
+
const now = this.now();
|
|
379
|
+
const lastActivityAt = Number.isFinite(this.currentTurnActivityAt) && this.currentTurnActivityAt > 0
|
|
380
|
+
? this.currentTurnActivityAt
|
|
381
|
+
: now;
|
|
382
|
+
const elapsedMs = Math.max(0, now - lastActivityAt);
|
|
383
|
+
const waitMs = Math.max(1, this.turnDeadlineMs - elapsedMs);
|
|
346
384
|
timer = setTimeout(() => {
|
|
385
|
+
if (settled) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const activityNow = this.now();
|
|
389
|
+
const latestActivityAt = Number.isFinite(this.currentTurnActivityAt) && this.currentTurnActivityAt > 0
|
|
390
|
+
? this.currentTurnActivityAt
|
|
391
|
+
: activityNow;
|
|
392
|
+
if (activityNow - latestActivityAt < this.turnDeadlineMs) {
|
|
393
|
+
schedule(reject);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
settled = true;
|
|
347
397
|
try {
|
|
348
398
|
onTimeout?.();
|
|
349
399
|
}
|
|
@@ -351,14 +401,18 @@ export class ClaudeAgentSdkSession extends EventEmitter {
|
|
|
351
401
|
// best effort
|
|
352
402
|
}
|
|
353
403
|
reject(this.createTurnTimeoutError(this.turnDeadlineMs));
|
|
354
|
-
},
|
|
355
|
-
if (typeof timer
|
|
404
|
+
}, waitMs);
|
|
405
|
+
if (typeof timer?.unref === "function") {
|
|
356
406
|
timer.unref();
|
|
357
407
|
}
|
|
408
|
+
};
|
|
409
|
+
const promise = new Promise((_, reject) => {
|
|
410
|
+
schedule(reject);
|
|
358
411
|
});
|
|
359
412
|
return {
|
|
360
413
|
promise,
|
|
361
414
|
cleanup: () => {
|
|
415
|
+
settled = true;
|
|
362
416
|
if (timer) {
|
|
363
417
|
clearTimeout(timer);
|
|
364
418
|
}
|
|
@@ -681,6 +735,7 @@ export class ClaudeAgentSdkSession extends EventEmitter {
|
|
|
681
735
|
terminalWorkingStatusEmitted: false,
|
|
682
736
|
};
|
|
683
737
|
this.currentTurn = currentTurn;
|
|
738
|
+
this.markTurnStartedStatus();
|
|
684
739
|
const closeGuard = this.createCloseGuard(() => {
|
|
685
740
|
abortController.abort();
|
|
686
741
|
currentTurn.query?.close?.();
|
|
@@ -21,6 +21,9 @@ export class CodexAppServerSession extends EventEmitter<[never]> {
|
|
|
21
21
|
nativeSessionId: string;
|
|
22
22
|
rateLimits: any;
|
|
23
23
|
tokenUsage: any;
|
|
24
|
+
currentTurnStatus: any;
|
|
25
|
+
currentTurnActivityAt: number;
|
|
26
|
+
now: any;
|
|
24
27
|
turnDeadlineMs: any;
|
|
25
28
|
currentTurn: {
|
|
26
29
|
turnId: string;
|
|
@@ -52,9 +55,11 @@ export class CodexAppServerSession extends EventEmitter<[never]> {
|
|
|
52
55
|
ready: boolean;
|
|
53
56
|
command: string;
|
|
54
57
|
} | null;
|
|
58
|
+
currentTurnStatus: any;
|
|
55
59
|
pid: number | undefined;
|
|
56
60
|
};
|
|
57
61
|
getSessionInfo(): any;
|
|
62
|
+
getCurrentTurnStatus(): any;
|
|
58
63
|
ensureSessionInfo(): Promise<any>;
|
|
59
64
|
getSessionUsageSummary(): Promise<{
|
|
60
65
|
sessionId: any;
|
|
@@ -73,6 +78,10 @@ export class CodexAppServerSession extends EventEmitter<[never]> {
|
|
|
73
78
|
setWorkingStatusHandler(handler: any): void;
|
|
74
79
|
setSessionReplyTarget(replyTo: any): void;
|
|
75
80
|
getCurrentReplyTarget(): string | undefined;
|
|
81
|
+
touchTurnActivity(): void;
|
|
82
|
+
updateCurrentTurnStatus(payload: any): void;
|
|
83
|
+
markTurnStartedStatus(): void;
|
|
84
|
+
failPendingTurnStart(error: any): Promise<void>;
|
|
76
85
|
boot(): Promise<void>;
|
|
77
86
|
bootInternal(): Promise<void>;
|
|
78
87
|
applyThreadInfo(payload: any, { resumeReady }?: {
|
|
@@ -186,6 +186,9 @@ export class CodexAppServerSession extends EventEmitter {
|
|
|
186
186
|
this.nativeSessionId = "";
|
|
187
187
|
this.rateLimits = null;
|
|
188
188
|
this.tokenUsage = null;
|
|
189
|
+
this.currentTurnStatus = null;
|
|
190
|
+
this.currentTurnActivityAt = 0;
|
|
191
|
+
this.now = typeof options.now === "function" ? options.now : () => Date.now();
|
|
189
192
|
this.turnDeadlineMs = getBoundedEnvInt("CONDUCTOR_TURN_DEADLINE_MS", DEFAULT_TURN_DEADLINE_MS, MIN_TURN_DEADLINE_MS, MAX_TURN_DEADLINE_MS);
|
|
190
193
|
this.currentTurn = null;
|
|
191
194
|
this.bootPromise = null;
|
|
@@ -244,12 +247,16 @@ export class CodexAppServerSession extends EventEmitter {
|
|
|
244
247
|
command: `codex resume ${this.sessionId}`,
|
|
245
248
|
}
|
|
246
249
|
: null,
|
|
250
|
+
currentTurnStatus: this.getCurrentTurnStatus(),
|
|
247
251
|
pid: this.transport.pid || undefined,
|
|
248
252
|
};
|
|
249
253
|
}
|
|
250
254
|
getSessionInfo() {
|
|
251
255
|
return this.sessionInfo ? { ...this.sessionInfo } : null;
|
|
252
256
|
}
|
|
257
|
+
getCurrentTurnStatus() {
|
|
258
|
+
return this.currentTurnStatus ? { ...this.currentTurnStatus } : null;
|
|
259
|
+
}
|
|
253
260
|
async ensureSessionInfo() {
|
|
254
261
|
await this.boot();
|
|
255
262
|
return this.getSessionInfo();
|
|
@@ -291,6 +298,37 @@ export class CodexAppServerSession extends EventEmitter {
|
|
|
291
298
|
getCurrentReplyTarget() {
|
|
292
299
|
return this.activeReplyTarget || this.lastReplyTarget || undefined;
|
|
293
300
|
}
|
|
301
|
+
touchTurnActivity() {
|
|
302
|
+
this.currentTurnActivityAt = this.now();
|
|
303
|
+
}
|
|
304
|
+
updateCurrentTurnStatus(payload) {
|
|
305
|
+
const updatedAtMs = this.now();
|
|
306
|
+
this.currentTurnActivityAt = updatedAtMs;
|
|
307
|
+
this.currentTurnStatus = {
|
|
308
|
+
...payload,
|
|
309
|
+
updated_at: new Date(updatedAtMs).toISOString(),
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
markTurnStartedStatus() {
|
|
313
|
+
this.updateCurrentTurnStatus({
|
|
314
|
+
source: "codex-app-server",
|
|
315
|
+
reply_in_progress: true,
|
|
316
|
+
replyTo: this.getCurrentReplyTarget(),
|
|
317
|
+
phase: "turn_started",
|
|
318
|
+
status_line: "codex is working",
|
|
319
|
+
thread_id: this.sessionId || undefined,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
async failPendingTurnStart(error) {
|
|
323
|
+
if (this.closeRequested || error?.reason === "session_closed") {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
await this.emitWorkingStatus({
|
|
327
|
+
phase: "turn_failed",
|
|
328
|
+
reply_in_progress: false,
|
|
329
|
+
status_done_line: error instanceof Error ? error.message : String(error),
|
|
330
|
+
});
|
|
331
|
+
}
|
|
294
332
|
async boot() {
|
|
295
333
|
if (this.booted) {
|
|
296
334
|
return;
|
|
@@ -406,12 +444,15 @@ export class CodexAppServerSession extends EventEmitter {
|
|
|
406
444
|
}
|
|
407
445
|
async emitWorkingStatus(payload) {
|
|
408
446
|
const normalized = this.normalizeWorkingStatusPayload(payload);
|
|
447
|
+
this.updateCurrentTurnStatus(normalized);
|
|
448
|
+
const snapshot = this.getCurrentTurnStatus();
|
|
409
449
|
if (typeof this.workingStatusHandler === "function") {
|
|
410
|
-
await this.workingStatusHandler(
|
|
450
|
+
await this.workingStatusHandler(snapshot);
|
|
411
451
|
}
|
|
412
|
-
this.emit("working_status",
|
|
452
|
+
this.emit("working_status", snapshot);
|
|
413
453
|
}
|
|
414
454
|
async emitAssistantMessage(text) {
|
|
455
|
+
this.touchTurnActivity();
|
|
415
456
|
const payload = {
|
|
416
457
|
text,
|
|
417
458
|
preserveWhitespace: true,
|
|
@@ -489,17 +530,40 @@ export class CodexAppServerSession extends EventEmitter {
|
|
|
489
530
|
};
|
|
490
531
|
}
|
|
491
532
|
let timer = null;
|
|
492
|
-
|
|
533
|
+
let settled = false;
|
|
534
|
+
const schedule = (reject) => {
|
|
535
|
+
const now = this.now();
|
|
536
|
+
const lastActivityAt = Number.isFinite(this.currentTurnActivityAt) && this.currentTurnActivityAt > 0
|
|
537
|
+
? this.currentTurnActivityAt
|
|
538
|
+
: now;
|
|
539
|
+
const elapsedMs = Math.max(0, now - lastActivityAt);
|
|
540
|
+
const waitMs = Math.max(1, this.turnDeadlineMs - elapsedMs);
|
|
493
541
|
timer = setTimeout(() => {
|
|
542
|
+
if (settled) {
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
const activityNow = this.now();
|
|
546
|
+
const latestActivityAt = Number.isFinite(this.currentTurnActivityAt) && this.currentTurnActivityAt > 0
|
|
547
|
+
? this.currentTurnActivityAt
|
|
548
|
+
: activityNow;
|
|
549
|
+
if (activityNow - latestActivityAt < this.turnDeadlineMs) {
|
|
550
|
+
schedule(reject);
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
settled = true;
|
|
494
554
|
reject(this.createTurnTimeoutError(this.turnDeadlineMs));
|
|
495
|
-
},
|
|
496
|
-
if (typeof timer
|
|
555
|
+
}, waitMs);
|
|
556
|
+
if (typeof timer?.unref === "function") {
|
|
497
557
|
timer.unref();
|
|
498
558
|
}
|
|
559
|
+
};
|
|
560
|
+
const promise = new Promise((_, reject) => {
|
|
561
|
+
schedule(reject);
|
|
499
562
|
});
|
|
500
563
|
return {
|
|
501
564
|
promise,
|
|
502
565
|
cleanup: () => {
|
|
566
|
+
settled = true;
|
|
503
567
|
if (timer) {
|
|
504
568
|
clearTimeout(timer);
|
|
505
569
|
}
|
|
@@ -803,7 +867,6 @@ export class CodexAppServerSession extends EventEmitter {
|
|
|
803
867
|
if (this.closeRequested) {
|
|
804
868
|
throw this.createSessionClosedError();
|
|
805
869
|
}
|
|
806
|
-
await this.boot();
|
|
807
870
|
const effectivePrompt = this.buildPrompt(promptText, { useInitialImages });
|
|
808
871
|
if (!effectivePrompt) {
|
|
809
872
|
return {
|
|
@@ -818,6 +881,14 @@ export class CodexAppServerSession extends EventEmitter {
|
|
|
818
881
|
reason: "turn_already_running",
|
|
819
882
|
});
|
|
820
883
|
}
|
|
884
|
+
this.markTurnStartedStatus();
|
|
885
|
+
try {
|
|
886
|
+
await this.boot();
|
|
887
|
+
}
|
|
888
|
+
catch (error) {
|
|
889
|
+
await this.failPendingTurnStart(error);
|
|
890
|
+
throw error;
|
|
891
|
+
}
|
|
821
892
|
this.history.push({ role: "user", content: promptText });
|
|
822
893
|
const closeGuard = this.createCloseGuard();
|
|
823
894
|
const turnTimeoutGuard = this.createTurnTimeoutGuard();
|
|
@@ -879,6 +950,13 @@ export class CodexAppServerSession extends EventEmitter {
|
|
|
879
950
|
if (error?.reason === "turn_timeout") {
|
|
880
951
|
await this.interruptCurrentTurn();
|
|
881
952
|
}
|
|
953
|
+
if (!this.closeRequested && error?.reason !== "session_closed") {
|
|
954
|
+
await this.emitWorkingStatus({
|
|
955
|
+
phase: "turn_failed",
|
|
956
|
+
reply_in_progress: false,
|
|
957
|
+
status_done_line: error instanceof Error ? error.message : String(error),
|
|
958
|
+
});
|
|
959
|
+
}
|
|
882
960
|
this.maybeEmitAuthRequired(error);
|
|
883
961
|
throw error;
|
|
884
962
|
}
|
|
@@ -32,6 +32,8 @@ export class KimiCliSession extends EventEmitter<[never]> {
|
|
|
32
32
|
} | null;
|
|
33
33
|
lastTokenUsage: any;
|
|
34
34
|
lastContextUsagePercent: number | undefined;
|
|
35
|
+
currentTurnStatus: any;
|
|
36
|
+
currentTurnActivityAt: number;
|
|
35
37
|
turnDeadlineMs: any;
|
|
36
38
|
workingStatusDedupeMs: any;
|
|
37
39
|
workingStatusThrottleMs: any;
|
|
@@ -77,12 +79,14 @@ export class KimiCliSession extends EventEmitter<[never]> {
|
|
|
77
79
|
ready: boolean;
|
|
78
80
|
command: any;
|
|
79
81
|
} | null;
|
|
82
|
+
currentTurnStatus: any;
|
|
80
83
|
pid: any;
|
|
81
84
|
};
|
|
82
85
|
getSessionInfo(): {
|
|
83
86
|
backend: string;
|
|
84
87
|
sessionId: any;
|
|
85
88
|
} | null;
|
|
89
|
+
getCurrentTurnStatus(): any;
|
|
86
90
|
ensureSessionInfo(): Promise<{
|
|
87
91
|
backend: string;
|
|
88
92
|
sessionId: any;
|
|
@@ -104,6 +108,10 @@ export class KimiCliSession extends EventEmitter<[never]> {
|
|
|
104
108
|
setWorkingStatusHandler(handler: any): void;
|
|
105
109
|
setSessionReplyTarget(replyTo: any): void;
|
|
106
110
|
getCurrentReplyTarget(): string | undefined;
|
|
111
|
+
touchTurnActivity(): void;
|
|
112
|
+
updateCurrentTurnStatus(payload: any): void;
|
|
113
|
+
markTurnStartedStatus(): void;
|
|
114
|
+
failPendingTurnStart(error: any, onProgress?: null): Promise<void>;
|
|
107
115
|
buildWorkingStatusFingerprint(payload: any): string;
|
|
108
116
|
shouldSuppressWorkingStatus(payload: any): boolean;
|
|
109
117
|
recordWorkingStatusEmission(payload: any): void;
|