linco-connect 1.1.5 → 1.1.7
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/package.json +1 -1
- package/src/agents/codex.js +120 -51
package/package.json
CHANGED
package/src/agents/codex.js
CHANGED
|
@@ -12,6 +12,8 @@ const {
|
|
|
12
12
|
setPendingPermission,
|
|
13
13
|
} = require('../permissionState');
|
|
14
14
|
|
|
15
|
+
const CODEX_TURN_COMPLETION_FALLBACK_MS = 1000;
|
|
16
|
+
|
|
15
17
|
function execute(input, ws, session, config) {
|
|
16
18
|
const textForCheck = stringifyInput(input);
|
|
17
19
|
if (isDangerousCommand(textForCheck) && session.autoApprove !== true) {
|
|
@@ -52,6 +54,7 @@ function runAppServerTurn(input, ws, session, config) {
|
|
|
52
54
|
session.sawPartialAssistantText = false;
|
|
53
55
|
session.codexAssistantEnded = false;
|
|
54
56
|
session.codexEmittedAgentMessageIds = new Set();
|
|
57
|
+
session.codexToolStates = new Map();
|
|
55
58
|
resetCodexAssistantText(session);
|
|
56
59
|
session._lastWs = ws;
|
|
57
60
|
session._lastConfig = config;
|
|
@@ -286,6 +289,35 @@ function clearTurnState(session) {
|
|
|
286
289
|
}
|
|
287
290
|
}
|
|
288
291
|
|
|
292
|
+
function finishCodexTurn(ws, session, config, reason = 'completed', payload = {}) {
|
|
293
|
+
if (!session.isTurnActive) return;
|
|
294
|
+
|
|
295
|
+
if (reason === 'completed') {
|
|
296
|
+
updateCodexSessionStats(session, payload);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
clearTurnState(session);
|
|
300
|
+
if (session.sawPartialAssistantText) {
|
|
301
|
+
sendCodexAssistantEnd(ws, session);
|
|
302
|
+
} else {
|
|
303
|
+
sendSystem(ws, 'Codex 本次执行没有输出。');
|
|
304
|
+
}
|
|
305
|
+
sendTurnEnd(ws, session, reason, payload);
|
|
306
|
+
if (config) drainQueue(ws, session, config);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function isFinalCodexAssistantItem(params) {
|
|
310
|
+
const item = params.item || {};
|
|
311
|
+
return isCodexAssistantMessageType(item.type) && item.phase === 'final_answer';
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function armCodexTurnCompletionFallback(ws, session, config) {
|
|
315
|
+
if (session.turnCompletedTimerId) clearTimeout(session.turnCompletedTimerId);
|
|
316
|
+
session.turnCompletedTimerId = setTimeout(() => {
|
|
317
|
+
finishCodexTurn(ws, session, config, 'completed');
|
|
318
|
+
}, CODEX_TURN_COMPLETION_FALLBACK_MS);
|
|
319
|
+
}
|
|
320
|
+
|
|
289
321
|
function ensureCodexStreamState(session) {
|
|
290
322
|
if (!session.codexStreamState) {
|
|
291
323
|
session.codexStreamState = createTextStreamBuffer();
|
|
@@ -327,6 +359,68 @@ function hasCodexAgentMessageEmitted(session, itemId) {
|
|
|
327
359
|
return Boolean(id && session.codexEmittedAgentMessageIds?.has(id));
|
|
328
360
|
}
|
|
329
361
|
|
|
362
|
+
function ensureCodexToolStates(session) {
|
|
363
|
+
if (!(session.codexToolStates instanceof Map)) {
|
|
364
|
+
session.codexToolStates = new Map();
|
|
365
|
+
}
|
|
366
|
+
return session.codexToolStates;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function codexToolStateFor(session, id) {
|
|
370
|
+
const toolId = String(id || '').trim();
|
|
371
|
+
if (!toolId) return null;
|
|
372
|
+
const states = ensureCodexToolStates(session);
|
|
373
|
+
return states.get(toolId) || null;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function setCodexToolState(session, id, next) {
|
|
377
|
+
const toolId = String(id || '').trim();
|
|
378
|
+
if (!toolId) return;
|
|
379
|
+
const states = ensureCodexToolStates(session);
|
|
380
|
+
const previous = states.get(toolId) || {};
|
|
381
|
+
states.set(toolId, {
|
|
382
|
+
...previous,
|
|
383
|
+
...next,
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function emitCodexToolCall(ws, session, tool) {
|
|
388
|
+
if (!ws) return false;
|
|
389
|
+
const id = String(tool.id || '').trim();
|
|
390
|
+
const existing = codexToolStateFor(session, id);
|
|
391
|
+
if (existing?.phase === 'completed' || existing?.phase === 'started') {
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
const name = String(tool.name || existing?.name || 'tool').trim() || 'tool';
|
|
395
|
+
const input = tool.input ?? existing?.input ?? '';
|
|
396
|
+
send(ws, 'tool_call', { id, name, input });
|
|
397
|
+
setCodexToolState(session, id, { phase: 'started', name, input });
|
|
398
|
+
return true;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function emitCodexToolResult(ws, session, tool) {
|
|
402
|
+
if (!ws) return false;
|
|
403
|
+
const id = String(tool.id || '').trim();
|
|
404
|
+
const existing = codexToolStateFor(session, id);
|
|
405
|
+
if (existing?.phase === 'completed') return false;
|
|
406
|
+
|
|
407
|
+
const name = String(tool.name || existing?.name || 'tool').trim() || 'tool';
|
|
408
|
+
const input = tool.input ?? existing?.input ?? '';
|
|
409
|
+
if (id && existing?.phase !== 'started') {
|
|
410
|
+
emitCodexToolCall(ws, session, { id, name, input });
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const output = tool.output ?? '';
|
|
414
|
+
send(ws, 'tool_result', { id, output });
|
|
415
|
+
setCodexToolState(session, id, {
|
|
416
|
+
phase: 'completed',
|
|
417
|
+
name,
|
|
418
|
+
input,
|
|
419
|
+
output,
|
|
420
|
+
});
|
|
421
|
+
return true;
|
|
422
|
+
}
|
|
423
|
+
|
|
330
424
|
function shouldAppendCompletedAgentMessage(session, params) {
|
|
331
425
|
const itemId = codexAgentMessageId(params);
|
|
332
426
|
if (itemId) return !hasCodexAgentMessageEmitted(session, itemId);
|
|
@@ -483,14 +577,12 @@ function handleServerRequest(message, session) {
|
|
|
483
577
|
|
|
484
578
|
// Tool call from server request
|
|
485
579
|
if (method === 'item/tool/call') {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
});
|
|
493
|
-
}
|
|
580
|
+
const toolName = params.name || params.tool || '';
|
|
581
|
+
emitCodexToolCall(ws, session, {
|
|
582
|
+
id: String(message.id),
|
|
583
|
+
name: toolName,
|
|
584
|
+
input: JSON.stringify(params.input || {}).slice(0, 300),
|
|
585
|
+
});
|
|
494
586
|
sendJsonRpc(session.codexAppServer, {
|
|
495
587
|
jsonrpc: '2.0',
|
|
496
588
|
id: message.id,
|
|
@@ -574,16 +666,19 @@ function handleAppServerMessage(message, session) {
|
|
|
574
666
|
session._log?.info('codex item completed', { itemType, item: summarizeCodexItemForLog(params.item) });
|
|
575
667
|
if (itemType === 'toolCall' || itemType === 'commandExecution' || itemType === 'webSearch') {
|
|
576
668
|
const itemId = params.item?.id || params.itemId || '';
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
669
|
+
const isCommand = itemType === 'commandExecution';
|
|
670
|
+
const isWebSearch = itemType === 'webSearch';
|
|
671
|
+
const toolName = isCommand ? 'exec' : isWebSearch ? 'webSearch' : (params.item?.name || params.item?.tool || '');
|
|
672
|
+
const toolInput = isCommand
|
|
673
|
+
? (params.item?.command || '')
|
|
674
|
+
: isWebSearch
|
|
675
|
+
? (params.item?.query || params.item?.input || params.item?.arguments || {})
|
|
676
|
+
: (params.item?.input || params.item?.arguments || {});
|
|
584
677
|
const output = params.item?.output || params.item?.result || params.item?.results || params.output || params.result || '';
|
|
585
|
-
|
|
678
|
+
emitCodexToolResult(ws, session, {
|
|
586
679
|
id: itemId,
|
|
680
|
+
name: toolName,
|
|
681
|
+
input: typeof toolInput === 'string' ? toolInput : JSON.stringify(toolInput).slice(0, 300),
|
|
587
682
|
output: typeof output === 'string' ? output : JSON.stringify(output).slice(0, 1000),
|
|
588
683
|
});
|
|
589
684
|
return;
|
|
@@ -597,42 +692,16 @@ function handleAppServerMessage(message, session) {
|
|
|
597
692
|
appendCodexAssistantText(text, ws, session, () => send(ws, 'assistant_start', {}));
|
|
598
693
|
markCodexAgentMessageEmitted(session, agentMessageId);
|
|
599
694
|
}
|
|
600
|
-
// Safety fallback:
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
session.currentInputForNoOutput = null;
|
|
606
|
-
if (session.sawPartialAssistantText) {
|
|
607
|
-
sendCodexAssistantEnd(ws, session);
|
|
608
|
-
} else {
|
|
609
|
-
sendSystem(ws, 'Codex 本次执行没有输出。');
|
|
610
|
-
}
|
|
611
|
-
sendTurnEnd(ws, session, 'timeout');
|
|
612
|
-
const cfg = session._lastConfig;
|
|
613
|
-
if (cfg) drainQueue(ws, session, cfg);
|
|
614
|
-
}
|
|
615
|
-
}, 10000);
|
|
695
|
+
// Safety fallback: some app-server builds emit task completion without a turn/completed notification.
|
|
696
|
+
// Arm it only after the final assistant message, not after user/tool/reasoning items.
|
|
697
|
+
if (isFinalCodexAssistantItem(params)) {
|
|
698
|
+
armCodexTurnCompletionFallback(ws, session, session._lastConfig);
|
|
699
|
+
}
|
|
616
700
|
return;
|
|
617
701
|
}
|
|
618
702
|
|
|
619
703
|
if (method === 'turn/completed' || method === 'turn.completed') {
|
|
620
|
-
|
|
621
|
-
clearTimeout(session.turnCompletedTimerId);
|
|
622
|
-
session.turnCompletedTimerId = null;
|
|
623
|
-
}
|
|
624
|
-
updateCodexSessionStats(session, params);
|
|
625
|
-
clearTurnState(session);
|
|
626
|
-
if (session.sawPartialAssistantText) {
|
|
627
|
-
sendCodexAssistantEnd(ws, session);
|
|
628
|
-
} else {
|
|
629
|
-
sendSystem(ws, 'Codex 本次执行没有输出。');
|
|
630
|
-
}
|
|
631
|
-
sendTurnEnd(ws, session);
|
|
632
|
-
const cfg = session._lastConfig;
|
|
633
|
-
if (cfg) {
|
|
634
|
-
drainQueue(ws, session, cfg);
|
|
635
|
-
}
|
|
704
|
+
finishCodexTurn(ws, session, session._lastConfig, 'completed', params);
|
|
636
705
|
return;
|
|
637
706
|
}
|
|
638
707
|
|
|
@@ -647,7 +716,7 @@ function handleAppServerMessage(message, session) {
|
|
|
647
716
|
if (method === 'tool/start' || method === 'tool_call') {
|
|
648
717
|
const toolName = params.name || params.tool || '';
|
|
649
718
|
if (toolName) {
|
|
650
|
-
|
|
719
|
+
emitCodexToolCall(ws, session, {
|
|
651
720
|
id: params.id || params.toolId || '',
|
|
652
721
|
name: toolName,
|
|
653
722
|
input: params.input || params.arguments || {},
|
|
@@ -657,7 +726,7 @@ function handleAppServerMessage(message, session) {
|
|
|
657
726
|
}
|
|
658
727
|
|
|
659
728
|
if (method === 'tool/completed' || method === 'tool_result') {
|
|
660
|
-
|
|
729
|
+
emitCodexToolResult(ws, session, {
|
|
661
730
|
id: params.id || params.toolId || '',
|
|
662
731
|
output: params.output || params.result || '',
|
|
663
732
|
});
|
|
@@ -678,7 +747,7 @@ function handleAppServerMessage(message, session) {
|
|
|
678
747
|
: (params.item?.input || params.item?.arguments || {});
|
|
679
748
|
const itemId = params.item?.id || params.itemId || '';
|
|
680
749
|
if (toolName) {
|
|
681
|
-
|
|
750
|
+
emitCodexToolCall(ws, session, {
|
|
682
751
|
id: itemId,
|
|
683
752
|
name: toolName,
|
|
684
753
|
input: typeof toolInput === 'string' ? toolInput : JSON.stringify(toolInput).slice(0, 300),
|