linco-connect 1.0.3 → 1.0.4
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/public/index.html +10 -1
- package/src/agents/codex.js +86 -9
- package/src/lincoProtocol.js +3 -3
package/package.json
CHANGED
package/public/index.html
CHANGED
|
@@ -931,12 +931,21 @@
|
|
|
931
931
|
}
|
|
932
932
|
|
|
933
933
|
function addPermissionConfirm(data) {
|
|
934
|
+
const requestId = data.requestId || '';
|
|
935
|
+
const selector = requestId ? `.permission-confirm[data-request-id="${cssEscape(requestId)}"]` : '';
|
|
936
|
+
const existing = selector ? messagesEl.querySelector(selector) : null;
|
|
937
|
+
if (existing) {
|
|
938
|
+
scrollToBottom();
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
|
|
934
942
|
const container = document.createElement('div');
|
|
935
943
|
container.className = 'danger-confirm permission-confirm';
|
|
944
|
+
container.dataset.requestId = requestId;
|
|
936
945
|
|
|
937
946
|
const title = document.createElement('div');
|
|
938
947
|
title.className = 'danger-text';
|
|
939
|
-
title.textContent =
|
|
948
|
+
title.textContent = `${agentLabel(currentAgentType)} 请求使用工具:${data.toolName || 'tool'}`;
|
|
940
949
|
container.appendChild(title);
|
|
941
950
|
|
|
942
951
|
if (data.input) {
|
package/src/agents/codex.js
CHANGED
|
@@ -319,12 +319,27 @@ function handleServerRequest(message, session) {
|
|
|
319
319
|
|
|
320
320
|
// File change approval — auto-approve but notify user
|
|
321
321
|
if (method === 'item/fileChange/requestApproval') {
|
|
322
|
+
const toolId = String(message.id);
|
|
323
|
+
const input = summarizeCodexParams(params);
|
|
322
324
|
session._log?.info('codex auto-approving file change');
|
|
325
|
+
if (ws) {
|
|
326
|
+
send(ws, 'tool_call', {
|
|
327
|
+
id: toolId,
|
|
328
|
+
name: 'fileChange',
|
|
329
|
+
input,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
323
332
|
sendJsonRpc(session.codexAppServer, {
|
|
324
333
|
jsonrpc: '2.0',
|
|
325
334
|
id: message.id,
|
|
326
335
|
result: { decision: 'accept' },
|
|
327
336
|
});
|
|
337
|
+
if (ws) {
|
|
338
|
+
send(ws, 'tool_result', {
|
|
339
|
+
id: toolId,
|
|
340
|
+
output: 'approved',
|
|
341
|
+
});
|
|
342
|
+
}
|
|
328
343
|
return;
|
|
329
344
|
}
|
|
330
345
|
|
|
@@ -333,8 +348,11 @@ function handleServerRequest(message, session) {
|
|
|
333
348
|
const cmd = params.command || params.tool || '';
|
|
334
349
|
session._log?.info('codex command execution approval requested', { method, command: cmd });
|
|
335
350
|
|
|
351
|
+
if (session.pendingPermission?.requestId === String(message.id)) return;
|
|
352
|
+
|
|
336
353
|
session.pendingPermission = {
|
|
337
354
|
provider: 'codex',
|
|
355
|
+
requestId: String(message.id),
|
|
338
356
|
toolName: 'exec',
|
|
339
357
|
input: cmd,
|
|
340
358
|
_codexMethod: method,
|
|
@@ -342,11 +360,6 @@ function handleServerRequest(message, session) {
|
|
|
342
360
|
};
|
|
343
361
|
|
|
344
362
|
if (ws) {
|
|
345
|
-
send(ws, 'tool_call', {
|
|
346
|
-
id: String(message.id),
|
|
347
|
-
name: 'exec',
|
|
348
|
-
input: cmd,
|
|
349
|
-
});
|
|
350
363
|
send(ws, 'permission_request', {
|
|
351
364
|
requestId: String(message.id),
|
|
352
365
|
toolName: 'exec',
|
|
@@ -369,12 +382,27 @@ function handleServerRequest(message, session) {
|
|
|
369
382
|
|
|
370
383
|
// Apply patch approval — auto-approve
|
|
371
384
|
if (method === 'applyPatchApproval') {
|
|
385
|
+
const toolId = String(message.id);
|
|
386
|
+
const input = summarizeCodexParams(params);
|
|
372
387
|
session._log?.info('codex auto-approving patch');
|
|
388
|
+
if (ws) {
|
|
389
|
+
send(ws, 'tool_call', {
|
|
390
|
+
id: toolId,
|
|
391
|
+
name: 'applyPatch',
|
|
392
|
+
input,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
373
395
|
sendJsonRpc(session.codexAppServer, {
|
|
374
396
|
jsonrpc: '2.0',
|
|
375
397
|
id: message.id,
|
|
376
398
|
result: { decision: 'approved' },
|
|
377
399
|
});
|
|
400
|
+
if (ws) {
|
|
401
|
+
send(ws, 'tool_result', {
|
|
402
|
+
id: toolId,
|
|
403
|
+
output: 'approved',
|
|
404
|
+
});
|
|
405
|
+
}
|
|
378
406
|
return;
|
|
379
407
|
}
|
|
380
408
|
|
|
@@ -477,6 +505,25 @@ function handleAppServerMessage(message, session) {
|
|
|
477
505
|
}
|
|
478
506
|
|
|
479
507
|
if (method === 'item/completed') {
|
|
508
|
+
const itemType = params.item?.type || '';
|
|
509
|
+
session._log?.info('codex item completed', { itemType, item: summarizeCodexItemForLog(params.item) });
|
|
510
|
+
if (itemType === 'toolCall' || itemType === 'commandExecution' || itemType === 'webSearch') {
|
|
511
|
+
const itemId = params.item?.id || params.itemId || '';
|
|
512
|
+
if (itemType === 'webSearch' && params.item?.query) {
|
|
513
|
+
send(ws, 'tool_call', {
|
|
514
|
+
id: itemId,
|
|
515
|
+
name: 'webSearch',
|
|
516
|
+
input: params.item.query,
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
const output = params.item?.output || params.item?.result || params.item?.results || params.output || params.result || '';
|
|
520
|
+
send(ws, 'tool_result', {
|
|
521
|
+
id: itemId,
|
|
522
|
+
output: typeof output === 'string' ? output : JSON.stringify(output).slice(0, 1000),
|
|
523
|
+
});
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
|
|
480
527
|
// final content fallback if no deltas were emitted
|
|
481
528
|
const text = extractFinalText(params);
|
|
482
529
|
if (text && !session.sawPartialAssistantText) {
|
|
@@ -549,10 +596,16 @@ function handleAppServerMessage(message, session) {
|
|
|
549
596
|
|
|
550
597
|
if (method === 'item/started') {
|
|
551
598
|
const itemType = params.item?.type || '';
|
|
552
|
-
session._log?.info('codex item started', { itemType });
|
|
553
|
-
if (itemType === 'toolCall' || itemType === 'commandExecution') {
|
|
554
|
-
const
|
|
555
|
-
const
|
|
599
|
+
session._log?.info('codex item started', { itemType, item: summarizeCodexItemForLog(params.item) });
|
|
600
|
+
if (itemType === 'toolCall' || itemType === 'commandExecution' || itemType === 'webSearch') {
|
|
601
|
+
const isCommand = itemType === 'commandExecution';
|
|
602
|
+
const isWebSearch = itemType === 'webSearch';
|
|
603
|
+
const toolName = isCommand ? 'exec' : isWebSearch ? 'webSearch' : (params.item?.name || params.item?.tool || '');
|
|
604
|
+
const toolInput = isCommand
|
|
605
|
+
? (params.item?.command || '')
|
|
606
|
+
: isWebSearch
|
|
607
|
+
? (params.item?.query || params.item?.input || params.item?.arguments || {})
|
|
608
|
+
: (params.item?.input || params.item?.arguments || {});
|
|
556
609
|
const itemId = params.item?.id || params.itemId || '';
|
|
557
610
|
if (toolName) {
|
|
558
611
|
send(ws, 'tool_call', {
|
|
@@ -595,6 +648,30 @@ function extractFinalText(params) {
|
|
|
595
648
|
return '';
|
|
596
649
|
}
|
|
597
650
|
|
|
651
|
+
function summarizeCodexItemForLog(item) {
|
|
652
|
+
if (!item || typeof item !== 'object') return {};
|
|
653
|
+
const summary = {};
|
|
654
|
+
for (const key of Object.keys(item)) {
|
|
655
|
+
const value = item[key];
|
|
656
|
+
if (typeof value === 'string') {
|
|
657
|
+
summary[key] = value.slice(0, 200);
|
|
658
|
+
} else if (Array.isArray(value)) {
|
|
659
|
+
summary[key] = `[array:${value.length}]`;
|
|
660
|
+
} else if (value && typeof value === 'object') {
|
|
661
|
+
summary[key] = `[object:${Object.keys(value).slice(0, 10).join(',')}]`;
|
|
662
|
+
} else {
|
|
663
|
+
summary[key] = value;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
return summary;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
function summarizeCodexParams(params) {
|
|
670
|
+
if (!params || typeof params !== 'object') return '';
|
|
671
|
+
const input = params.path || params.file || params.filePath || params.command || params.patch || params.diff || params.input || params;
|
|
672
|
+
return typeof input === 'string' ? input.slice(0, 1000) : JSON.stringify(input).slice(0, 1000);
|
|
673
|
+
}
|
|
674
|
+
|
|
598
675
|
function buildCodexInput(input, workspace) {
|
|
599
676
|
if (Array.isArray(input)) {
|
|
600
677
|
const result = [];
|
package/src/lincoProtocol.js
CHANGED
|
@@ -36,7 +36,7 @@ function createLincoAdapter(rawWs, session, config) {
|
|
|
36
36
|
}
|
|
37
37
|
const payload = mapLocalEventToLinco(event, session, config, linco);
|
|
38
38
|
if (!payload || closed.current) return;
|
|
39
|
-
const wrapped = wrapLincoEnvelope(payload, config);
|
|
39
|
+
const wrapped = wrapLincoEnvelope(payload, config, session);
|
|
40
40
|
if (Array.isArray(wrapped)) {
|
|
41
41
|
for (const item of wrapped) rawWs.send(JSON.stringify(item));
|
|
42
42
|
return;
|
|
@@ -55,11 +55,11 @@ function createLincoAdapter(rawWs, session, config) {
|
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
function wrapLincoEnvelope(payload, config) {
|
|
58
|
+
function wrapLincoEnvelope(payload, config, session) {
|
|
59
59
|
const meta = lincoMetaDefaults(config, {});
|
|
60
60
|
return pruneUndefined({
|
|
61
61
|
...payload,
|
|
62
|
-
from: payload.from || (config.agents ? Object.keys(config.agents)[0] : 'claude'),
|
|
62
|
+
from: payload.from || session?.agentType || (config.agents ? Object.keys(config.agents)[0] : 'claude'),
|
|
63
63
|
to: payload.to || 'robot',
|
|
64
64
|
source: payload.source || 'ws',
|
|
65
65
|
ts: payload.ts || Date.now(),
|