@ai-workbench/remote-agent 0.8.28 → 0.9.0-toolbridge.2
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/README.md +13 -3
- package/dist/acp.d.ts +3 -0
- package/dist/acp.js +35 -7
- package/dist/agents.js +1 -1
- package/dist/broker-ownership.d.ts +10 -0
- package/dist/broker-ownership.js +59 -0
- package/dist/broker-runtime.js +5 -2
- package/dist/claude-local.js +2 -1
- package/dist/employee-mcp.d.ts +2 -0
- package/dist/employee-mcp.js +3 -1
- package/dist/index.d.ts +8 -3
- package/dist/index.js +108 -16
- package/dist/presentation.js +3 -0
- package/dist/provider.d.ts +4 -0
- package/dist/toolbridge-loop.d.ts +19 -0
- package/dist/toolbridge-loop.js +251 -0
- package/dist/toolbridge-table.d.ts +10 -0
- package/dist/toolbridge-table.js +27 -0
- package/dist/toolbridge.d.ts +114 -0
- package/dist/toolbridge.js +205 -0
- package/package.json +6 -5
package/dist/index.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { ToolBridgeLoop } from "./toolbridge-loop.js";
|
|
2
|
+
import { openToolBridgeMcp, TOOLBRIDGE_NOTICE } from "./toolbridge.js";
|
|
1
3
|
import { connectNeedMessage } from "./connect-need.js";
|
|
2
4
|
import { CloseDiagnostics, withFirstFailureEvidence, failureEvidenceSummary, closeCode } from "./close-diagnostics.js";
|
|
3
5
|
import { withRemoteFailureEvidence } from "./failure-evidence.js";
|
|
4
6
|
import { SessionId } from '@deepseek-ai/dsh-session';
|
|
7
|
+
import { createAssistantMessage } from '@deepseek-ai/dsh-llm';
|
|
5
8
|
import Schema from '@deepseek-ai/schemastery';
|
|
6
9
|
import { LlmAdapter, LlmError } from '@deepseek-ai/dsh-llm';
|
|
7
10
|
import { createHash } from 'node:crypto';
|
|
@@ -91,6 +94,7 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
91
94
|
directory;
|
|
92
95
|
preflightDiagnostics;
|
|
93
96
|
permissions;
|
|
97
|
+
bridgeLoop;
|
|
94
98
|
config;
|
|
95
99
|
sessions = new Map();
|
|
96
100
|
turns = new Map();
|
|
@@ -99,7 +103,8 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
99
103
|
detached = new Set();
|
|
100
104
|
modelPresentation = new ModelPresentation();
|
|
101
105
|
employeeNoticeSessions = new Set();
|
|
102
|
-
|
|
106
|
+
pendingEmployeeNotices = new Set();
|
|
107
|
+
constructor(config, resolveRuntime, approve, providerName = () => CODEX_AGENT.name.zh, agent = CODEX_AGENT, readImage, reportLoginFailure, directory, preflightDiagnostics, permissions, bridgeStopped) {
|
|
103
108
|
super();
|
|
104
109
|
this.resolveRuntime = resolveRuntime;
|
|
105
110
|
this.approve = approve;
|
|
@@ -110,6 +115,7 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
110
115
|
this.directory = directory;
|
|
111
116
|
this.preflightDiagnostics = preflightDiagnostics;
|
|
112
117
|
this.permissions = permissions;
|
|
118
|
+
this.bridgeLoop = new ToolBridgeLoop(readImage, bridgeStopped);
|
|
113
119
|
this.config = typeof config === 'function' ? config : () => config;
|
|
114
120
|
}
|
|
115
121
|
providerInfo(provider) { return { id: provider, name: this.providerName() }; }
|
|
@@ -156,7 +162,10 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
156
162
|
void Promise.resolve().then(() => runtime.dispose?.(cause)).catch(() => console.warn('[remote-agent] 旧主机会话暂未清理,将由主机自动回收'));
|
|
157
163
|
return Promise.resolve();
|
|
158
164
|
}
|
|
165
|
+
completeDshTurn(sessionId, messages) { this.sessions.get(sessionId)?.runtime.execution?.endTurn(); return this.bridgeLoop.conclude(sessionId, messages); }
|
|
166
|
+
hasBridgeSession(sessionId) { return this.bridgeLoop.active(sessionId); }
|
|
159
167
|
async disposeSession(sessionId) {
|
|
168
|
+
await this.bridgeLoop.cancel(sessionId);
|
|
160
169
|
this.turns.get(sessionId)?.abort();
|
|
161
170
|
const current = this.sessions.get(sessionId);
|
|
162
171
|
if (!current)
|
|
@@ -166,6 +175,7 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
166
175
|
await this.cleanupRuntime(current.runtime);
|
|
167
176
|
}
|
|
168
177
|
async detachSession(sessionId) {
|
|
178
|
+
await this.bridgeLoop.cancel(sessionId);
|
|
169
179
|
const current = this.sessions.get(sessionId), turn = this.turns.get(sessionId);
|
|
170
180
|
if (!current && !turn)
|
|
171
181
|
return;
|
|
@@ -188,8 +198,10 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
188
198
|
for (const live of this.sessions.values())
|
|
189
199
|
live.runtime.closeDiagnostics?.record({ layer: 'adapter', initiator: 'adapter.dispose', reason: 'PLUGIN_DISPOSED', action: 'dispose', changes: [{ field: 'adapter.shutting-down', before: this.shuttingDown, after: true }] });
|
|
190
200
|
this.shuttingDown = true;
|
|
201
|
+
await this.bridgeLoop.dispose();
|
|
191
202
|
this.modelPresentation.clear();
|
|
192
203
|
this.employeeNoticeSessions.clear();
|
|
204
|
+
this.pendingEmployeeNotices.clear();
|
|
193
205
|
const results = await Promise.allSettled([...new Set([...this.sessions.keys(), ...this.turns.keys()])].map(id => this.detachSession(id)));
|
|
194
206
|
this.lifetime.abort();
|
|
195
207
|
const failure = results.find(result => result.status === 'rejected');
|
|
@@ -197,9 +209,15 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
197
209
|
throw failure.reason;
|
|
198
210
|
}
|
|
199
211
|
async *stream(options) {
|
|
200
|
-
let promptDispatched =
|
|
212
|
+
let promptDispatched = !!options.sessionId && (this.sessions.get(options.sessionId)?.session.promptDispatchCount ?? 0) > 0;
|
|
201
213
|
try {
|
|
202
|
-
|
|
214
|
+
for await (const chunk of this.bridgeLoop.stream(options, (o, b) => this.generate(o, () => { promptDispatched = true; }, b))) {
|
|
215
|
+
if (options.sessionId && this.pendingEmployeeNotices.has(options.sessionId) && chunk.type === 'text-delta' && chunk.text.startsWith(EMPLOYEE_EXECUTION_NOTICE)) {
|
|
216
|
+
this.employeeNoticeSessions.add(options.sessionId);
|
|
217
|
+
this.pendingEmployeeNotices.delete(options.sessionId);
|
|
218
|
+
}
|
|
219
|
+
yield chunk;
|
|
220
|
+
}
|
|
203
221
|
}
|
|
204
222
|
catch (error) {
|
|
205
223
|
if (error instanceof ModelConfigurationError)
|
|
@@ -223,8 +241,12 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
223
241
|
}
|
|
224
242
|
throw new LlmError(employeeFailure('TURN_FAILED', this.agent.loginProvider, promptDispatched), 'TURN_FAILED', { cause: error });
|
|
225
243
|
}
|
|
244
|
+
finally {
|
|
245
|
+
if (options.sessionId)
|
|
246
|
+
this.pendingEmployeeNotices.delete(options.sessionId);
|
|
247
|
+
}
|
|
226
248
|
}
|
|
227
|
-
async *generate(original, onPromptDispatched) {
|
|
249
|
+
async *generate(original, onPromptDispatched, bridge) {
|
|
228
250
|
if (original.purpose)
|
|
229
251
|
throw new NeedError('PURPOSE_UNSUPPORTED', '此远程模型不处理后台标题或上下文整理请求,未发送模型请求', '独立的后台请求能力');
|
|
230
252
|
if (this.lifetime.signal.aborted)
|
|
@@ -238,6 +260,7 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
238
260
|
const signal = AbortSignal.any([this.lifetime.signal, turn.signal, ...(original.signal ? [original.signal] : [])]);
|
|
239
261
|
const options = { ...original, signal };
|
|
240
262
|
const abortOffs = [];
|
|
263
|
+
let bridgeMcp;
|
|
241
264
|
let request;
|
|
242
265
|
let earlyStage = 'input-prepare';
|
|
243
266
|
let runtime;
|
|
@@ -261,7 +284,7 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
261
284
|
runtime.details.record(entry); };
|
|
262
285
|
const config = this.config();
|
|
263
286
|
const mode = 'session';
|
|
264
|
-
request = { sessionId: options.sessionId, mode: mode ?? 'session', signal };
|
|
287
|
+
request = { sessionId: options.sessionId, mode: mode ?? 'session', signal, toolBridge: bridge };
|
|
265
288
|
const observeAbort = (source, initiator, reason) => { if (!source)
|
|
266
289
|
return; const record = () => { (request?.closeDiagnostics ?? runtime?.closeDiagnostics)?.record({ layer: 'adapter', initiator, reason, action: 'abort', changes: [{ field: 'adapter.turn-aborted', before: false, after: source.aborted }] }); }; source.addEventListener('abort', record, { once: true }); abortOffs.push(() => source.removeEventListener('abort', record)); };
|
|
267
290
|
observeAbort(original.signal, 'signal.caller', 'CALLER_ABORTED');
|
|
@@ -329,6 +352,10 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
329
352
|
if (runtime.hostWorkspace && mode === 'session' && Buffer.byteLength(JSON.stringify({ prompt: input.prompt, systemPrompt: input.systemPrompt }), 'utf8') > config.acpMaxMessageBytes)
|
|
330
353
|
throw new NeedError('ACP_MESSAGE_TOO_LARGE', '本轮输入超过已设置的会话消息容量,未截断或发送', '管理员调整acpMaxMessageBytes');
|
|
331
354
|
const details = runtime.details ?? await createRemoteDetails({ warn: line => console.warn(line) }).open({ sessionId: options.sessionId ?? 'unscoped', provider: this.agent.loginProvider, identity: runtime.identity, connection: runtime.connection });
|
|
355
|
+
if (bridge && runtime.employeeExecutionMeta && !runtime.hostWorkspace && bridge.table.size) {
|
|
356
|
+
bridge.enable(event => details.record(event));
|
|
357
|
+
bridgeMcp = await openToolBridgeMcp(bridge, config.acpMaxMessageBytes);
|
|
358
|
+
}
|
|
332
359
|
recordInformation();
|
|
333
360
|
request.recordEarlyFailure = undefined;
|
|
334
361
|
if (input.historyTransfer)
|
|
@@ -339,16 +366,22 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
339
366
|
activeMapper = mapper;
|
|
340
367
|
let id;
|
|
341
368
|
let stop;
|
|
369
|
+
const pointerRuntime = runtime;
|
|
370
|
+
const makePointer = (nativeId) => ({ ...(pointerRuntime.hostSource ? { hostSource: pointerRuntime.hostSource } : {}), ...(modelConfiguration ? { modelConfiguration } : {}), version: 2, agent: this.agent.id, contextSha256, ...(mode === 'session' ? { usageSnapshot: mapper.usageSnapshot } : {}), ...(options.sessionId ? { dshSessionId: options.sessionId } : {}), identity: pointerRuntime.identity, cwd: pointerRuntime.cwd, sessionId: nativeId, ...(pointerRuntime.binding ? { bindingId: pointerRuntime.binding.bindingId, workspaceId: pointerRuntime.binding.workspaceId } : {}) });
|
|
342
371
|
if (runtime.employeePresentation)
|
|
343
372
|
details.record({ kind: 'session', severity: 'warning', code: 'EMPLOYEE_EXECUTION_SCOPE', summary: runtime.employeePresentation, payload: { source: 'local' } });
|
|
344
373
|
if (runtime.execution)
|
|
345
374
|
details.record({ kind: 'session', severity: 'warning', code: 'CLAUDE_EMPLOYEE_SCOPE', summary: runtime.execution.notice, payload: runtime.execution.scope });
|
|
346
375
|
if ((runtime.employeePresentation || runtime.execution) && !employeeNoticeShown) {
|
|
347
|
-
for (const chunk of mapper.text(EMPLOYEE_EXECUTION_NOTICE + '\n\n')) {
|
|
376
|
+
for (const chunk of mapper.text(EMPLOYEE_EXECUTION_NOTICE + (bridge?.enabled ? '\n\n' + TOOLBRIDGE_NOTICE : '') + '\n\n')) {
|
|
348
377
|
if (chunk.type === 'text-delta') {
|
|
349
378
|
employeeNoticeShown = true;
|
|
350
|
-
if (key)
|
|
351
|
-
|
|
379
|
+
if (key) {
|
|
380
|
+
if (bridge)
|
|
381
|
+
this.pendingEmployeeNotices.add(key);
|
|
382
|
+
else
|
|
383
|
+
this.employeeNoticeSessions.add(key);
|
|
384
|
+
}
|
|
352
385
|
}
|
|
353
386
|
yield chunk;
|
|
354
387
|
}
|
|
@@ -363,7 +396,8 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
363
396
|
let failure;
|
|
364
397
|
const push = (chunks) => { queue.push(...chunks); wake?.(); wake = undefined; };
|
|
365
398
|
let promptPermit;
|
|
366
|
-
|
|
399
|
+
let confirmedNativeMode;
|
|
400
|
+
const hooks = { closeDiagnostics: runtime.closeDiagnostics, broker: runtime.broker, sessionOwnership: runtime.sessionOwnership, employeeExecution: runtime.employeeExecution, ...this.permissions ? { promptGuard: () => {
|
|
367
401
|
if (!promptPermit)
|
|
368
402
|
throw new NeedError('PERMISSION_POLICY_CHANGED', '当前权限没有保持确认,本次没有继续派发。', '当前权限代次和同值回执');
|
|
369
403
|
return promptPermit;
|
|
@@ -373,6 +407,17 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
373
407
|
} } : {}, approve: async (permission, approvalSignal) => {
|
|
374
408
|
const execution = runtime.execution;
|
|
375
409
|
const toolName = permission.toolCall.name ?? permission.toolCall._meta?.claudeCode?.toolName;
|
|
410
|
+
if (execution && bridge?.enabled && bridge.isBridge(toolName)) {
|
|
411
|
+
const current = this.permissions ? permissionMode('claude', this.permissions.read(options.sessionId)) : undefined;
|
|
412
|
+
if (current === 'plan') {
|
|
413
|
+
details.record({ kind: 'tool', severity: 'info', code: 'TOOLBRIDGE_PLAN_PERMISSION_REJECTED', summary: '本轮为plan档,桥接原生权限请求未放行。' });
|
|
414
|
+
return 'rejected';
|
|
415
|
+
}
|
|
416
|
+
if (current === 'default' && confirmedNativeMode === 'default') {
|
|
417
|
+
details.record({ kind: 'tool', severity: 'info', code: 'TOOLBRIDGE_NATIVE_PERMISSION_ANSWERED', summary: '已确认桥接工具仅在DSH审批,原生请求自动答复一次。', payload: { tool: toolName } });
|
|
418
|
+
return 'allowed-once';
|
|
419
|
+
}
|
|
420
|
+
}
|
|
376
421
|
if (execution) {
|
|
377
422
|
if (toolName && ['Agent', 'Task', 'TaskOutput', 'TaskStop', 'Read', 'Write', 'Edit', 'Glob', 'Grep', 'Bash'].includes(toolName)) {
|
|
378
423
|
details.record({ kind: 'need', severity: 'warning', code: 'CLAUDE_HOST_TOOL_UNSUPPORTED', summary: '这类原生调用没有经过员工端别名,本机来源不支持;本次审批已取消', payload: { operation: toolName } });
|
|
@@ -383,6 +428,7 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
383
428
|
const answer = await this.approve?.(permission, approvalSignal, options, execution && toolName?.startsWith('mcp__employee__') ? 'employee-local' : undefined, runtime.hostWorkspace) ?? 'unavailable';
|
|
384
429
|
return answer;
|
|
385
430
|
}, onEmployeeNotice: notice => push(mapper.text(notice + '\n\n')), onUpdate: notification => { try {
|
|
431
|
+
bridge?.observe(notification.update);
|
|
386
432
|
push(mapper.acp(notification.update));
|
|
387
433
|
}
|
|
388
434
|
catch (error) {
|
|
@@ -412,6 +458,7 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
412
458
|
const remoteMode = permissionMode(this.agent.loginProvider, policy);
|
|
413
459
|
details.record({ kind: 'session', severity: 'info', code: 'DSH_PERMISSION_POLICY', summary: '按DSH当前会话权限同步远程工具;不会把关闭审批提示解释为自动批准。', payload: { ...policy, remoteMode } });
|
|
414
460
|
await session.selectPermissionMode(remoteMode, modelSignal, this.agent.loginProvider);
|
|
461
|
+
confirmedNativeMode = remoteMode;
|
|
415
462
|
permissionKey = next;
|
|
416
463
|
});
|
|
417
464
|
};
|
|
@@ -437,7 +484,7 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
437
484
|
const task = (async () => {
|
|
438
485
|
remoteAttempted = true;
|
|
439
486
|
try {
|
|
440
|
-
await session.start(runtime.execution?.hostCwd ?? runtime.cwd, previous?.sessionId, modelSignal, { systemPrompt: input.systemPrompt, ...(runtime.execution ? { employeeRouting: runtime.execution.routing } : {}), ...(codex ? { codexNewSessionOnly: true } : {}), ...(runtime.employeeExecutionMeta ? { employeeExecution: runtime.employeeExecutionMeta } : {}) }, input.prompt);
|
|
487
|
+
await session.start(runtime.execution?.hostCwd ?? runtime.cwd, previous?.sessionId, modelSignal, { systemPrompt: input.systemPrompt, ...(bridgeMcp ? { toolBridgeServers: bridgeMcp.mcpServers } : {}), ...(runtime.execution ? { employeeRouting: runtime.execution.routing } : {}), ...(codex ? { codexNewSessionOnly: true } : {}), ...(runtime.employeeExecutionMeta ? { employeeExecution: runtime.employeeExecutionMeta } : {}) }, input.prompt);
|
|
441
488
|
}
|
|
442
489
|
catch (error) {
|
|
443
490
|
details.record({ kind: 'session', severity: 'action-required', code: 'SESSION_START_FAILED', summary: '远端会话建立或恢复失败,本轮用户输入未发送。', payload: { operation: previous ? 'resume-or-load' : 'new', reason: closeCode(error, 'ACP_FAILED'), promptDispatched: false } });
|
|
@@ -488,9 +535,14 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
488
535
|
recordInformation();
|
|
489
536
|
details.beginTurn(runtime.detailContext?.() ?? { turn: null, step: null });
|
|
490
537
|
await runtime.execution?.beginTurn(modelSignal);
|
|
538
|
+
if (bridge?.enabled)
|
|
539
|
+
bridge.replayState = { response: makePointer(session.id) };
|
|
540
|
+
bridge?.begin(modelSignal);
|
|
491
541
|
id = session.id;
|
|
492
542
|
try {
|
|
493
543
|
const result = await dispatchPrompt();
|
|
544
|
+
if (bridge?.unsettled)
|
|
545
|
+
bridge.abandon('HOST_PROMPT_ENDED_WITH_PENDING_CALL');
|
|
494
546
|
permissionOff?.();
|
|
495
547
|
permissionOff = undefined;
|
|
496
548
|
await permissionTail;
|
|
@@ -499,6 +551,7 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
499
551
|
finally {
|
|
500
552
|
permissionOff?.();
|
|
501
553
|
permissionOff = undefined;
|
|
554
|
+
bridge?.end();
|
|
502
555
|
runtime.execution?.endTurn();
|
|
503
556
|
details.endTurn();
|
|
504
557
|
}
|
|
@@ -533,13 +586,15 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
533
586
|
}
|
|
534
587
|
}
|
|
535
588
|
yield* mapper.flush();
|
|
536
|
-
const pointer =
|
|
589
|
+
const pointer = makePointer(id);
|
|
537
590
|
succeeded = true;
|
|
538
591
|
yield { type: 'finish', reason: { kind: stop === 'max_tokens' || stop === 'max_turn_requests' ? 'max-tokens' : 'stop' }, replayState: { response: pointer } };
|
|
539
592
|
}
|
|
540
593
|
catch (error) {
|
|
541
594
|
if (session && session.promptDispatchCount > promptBaseline)
|
|
542
595
|
onPromptDispatched();
|
|
596
|
+
if (bridge?.unsettled && !signal.aborted)
|
|
597
|
+
bridge.abandon('HOST_PROMPT_FAILED');
|
|
543
598
|
turnFailure = error;
|
|
544
599
|
runtime?.closeDiagnostics?.record({ layer: 'adapter', initiator: 'adapter.turn-failure', reason: closeCode(error, signal.aborted ? 'ABORTED' : 'TURN_FAILED'), action: 'failure' });
|
|
545
600
|
recordInformation();
|
|
@@ -559,6 +614,8 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
559
614
|
for (const off of abortOffs)
|
|
560
615
|
off();
|
|
561
616
|
runtime?.execution?.endTurn();
|
|
617
|
+
if (bridgeMcp)
|
|
618
|
+
await bridgeMcp.close();
|
|
562
619
|
if (runtime) {
|
|
563
620
|
session?.close({ layer: 'adapter', initiator: 'adapter.turn-finally', reason: succeeded ? 'TURN_FINISHED' : 'TURN_FAILED', action: 'close' });
|
|
564
621
|
if (key && this.sessions.get(key) === live)
|
|
@@ -575,16 +632,25 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
575
632
|
/** One official registration owns both routes, preserving opaque replay ownership. */
|
|
576
633
|
export class RemoteAgentsAdapter extends LlmAdapter {
|
|
577
634
|
adapters = new Map();
|
|
578
|
-
constructor(config, makeRuntime, approve, nameFor, readImage, reportLoginFailure, directory, preflightDiagnostics, permissions) { super(); for (const agent of AGENTS)
|
|
579
|
-
this.adapters.set(agent.id, new CodexRemoteAdapter(config, makeRuntime(agent), approve, () => nameFor(agent), agent, readImage, reportLoginFailure, directory, preflightDiagnostics, permissions)); }
|
|
635
|
+
constructor(config, makeRuntime, approve, nameFor, readImage, reportLoginFailure, directory, preflightDiagnostics, permissions, bridgeStopped) { super(); for (const agent of AGENTS)
|
|
636
|
+
this.adapters.set(agent.id, new CodexRemoteAdapter(config, makeRuntime(agent), approve, () => nameFor(agent), agent, readImage, reportLoginFailure, directory, preflightDiagnostics, permissions, bridgeStopped)); }
|
|
580
637
|
adapter(provider) { const adapter = this.adapters.get(provider); if (!adapter)
|
|
581
638
|
throw new LlmError('远程模型未注册', 'PROVIDER_NOT_FOUND'); return adapter; }
|
|
582
639
|
providerInfo(provider) { return this.adapter(provider).providerInfo(provider); }
|
|
583
640
|
listModels(provider) { return this.adapter(provider).listModels(provider); }
|
|
584
641
|
resolveModel(provider, model, signal) { return this.adapter(provider).resolveModel(provider, model, signal); }
|
|
585
|
-
prepareCall(provider, model, signal) {
|
|
642
|
+
async prepareCall(provider, model, signal) { const prepared = await this.adapter(provider).prepareCall(provider, model, signal); return { ...prepared, stream: (options) => this.stream(options) }; }
|
|
586
643
|
providerRetryPolicy() { return { mode: 'normal', maxRetries: 0, retryableCodes: [], initialDelayMs: 0, maxDelayMs: 0, jitterRatio: 0 }; }
|
|
587
|
-
stream(options) {
|
|
644
|
+
stream(options) {
|
|
645
|
+
if (options.sessionId)
|
|
646
|
+
for (const [provider, adapter] of this.adapters)
|
|
647
|
+
if (provider !== options.provider && adapter.hasBridgeSession(options.sessionId)) {
|
|
648
|
+
void adapter.disposeSession(options.sessionId);
|
|
649
|
+
throw new LlmError('原主机还在等待DSH工具结果,切换助手已停止原轮次;请核对已执行的工具后重新发送,结果不会重放。', 'TOOLBRIDGE_ROUTE_CHANGED');
|
|
650
|
+
}
|
|
651
|
+
return this.adapter(options.provider).stream(options);
|
|
652
|
+
}
|
|
653
|
+
async completeDshTurn(sessionId, messages) { await Promise.all([...this.adapters.values()].map(adapter => adapter.completeDshTurn(sessionId, messages))); }
|
|
588
654
|
async disposeSession(sessionId) { await Promise.all([...this.adapters.values()].map(adapter => adapter.disposeSession(sessionId))); }
|
|
589
655
|
async detachSession(sessionId) { await Promise.all([...this.adapters.values()].map(adapter => adapter.detachSession(sessionId))); }
|
|
590
656
|
async dispose() { await Promise.all([...this.adapters.values()].map(adapter => adapter.dispose())); }
|
|
@@ -688,7 +754,33 @@ export function apply(ctx, live) {
|
|
|
688
754
|
step = null;
|
|
689
755
|
}
|
|
690
756
|
return managerForProfile().recordLoginFailure({ sessionId, code, diagnostic, context: { turn, step } });
|
|
691
|
-
}, modelDirectory, preflightDiagnostics, dshPermissionSource(ctx))
|
|
757
|
+
}, modelDirectory, preflightDiagnostics, dshPermissionSource(ctx), (sessionId, notice) => {
|
|
758
|
+
const agent = ctx.get('agents')?.get(SessionId(sessionId));
|
|
759
|
+
if (!agent)
|
|
760
|
+
return;
|
|
761
|
+
let turn = null, step = null;
|
|
762
|
+
for (const e of agent.session.snapshotEvents()) {
|
|
763
|
+
if (e.type === 'turn/start') {
|
|
764
|
+
turn = e.data.turn;
|
|
765
|
+
step = null;
|
|
766
|
+
}
|
|
767
|
+
else if (e.type === 'turn/end') {
|
|
768
|
+
turn = null;
|
|
769
|
+
step = null;
|
|
770
|
+
}
|
|
771
|
+
else if (e.type === 'step/start')
|
|
772
|
+
step = e.data.step;
|
|
773
|
+
else if (e.type === 'step/end')
|
|
774
|
+
step = null;
|
|
775
|
+
}
|
|
776
|
+
if (turn !== null && step !== null)
|
|
777
|
+
agent.session.append('assistant/message', { turn, step, message: createAssistantMessage({ content: [{ type: 'text', text: notice }], source: { provider: 'ai-workbench-toolbridge', model: 'notice' } }), stream: [] }, { surfaceOp: 'append' });
|
|
778
|
+
agent.cancel({ kind: 'hook', reason: 'DSH_BRIDGE_CALL_ABANDONED_BY_HOST' }, { keepInbox: true });
|
|
779
|
+
});
|
|
780
|
+
ctx.on('session/event', (session, event) => { if (event.type === 'turn/end') {
|
|
781
|
+
const operation = event.data.reason.kind === 'completed' ? adapter.completeDshTurn(session.id, session.deriveMessages()) : adapter.disposeSession(session.id);
|
|
782
|
+
void operation.catch(() => ctx.logger('remote-agent').warn('TOOLBRIDGE_RESULT_DELIVERY_UNCONFIRMED'));
|
|
783
|
+
} });
|
|
692
784
|
const ids = AGENTS.map(agent => agent.id);
|
|
693
785
|
const entries = () => AGENTS.map(agent => ({ provider: agent.id, displayName: agent.name[language], settingsNs: actualSettingsNamespace ?? settingsNamespace, settingsPath: [] }));
|
|
694
786
|
const directory = ctx.llm.registerConfigurableProviders(entries());
|
package/dist/presentation.js
CHANGED
|
@@ -42,6 +42,9 @@ export function employeeFailure(code, brand = 'codex', promptDispatched = true)
|
|
|
42
42
|
POLICY_DENIED: '这个账号暂时不能使用,请联系公司账号管理员。',
|
|
43
43
|
SESSION_CONTEXT_MISMATCH: '远程主机连接已改变,请重新开始本次检查。',
|
|
44
44
|
REMOTE_SESSION_BUSY: '原会话仍被主机上的上一轮占用,' + (promptDispatched ? '本轮请求未能完成' : '本轮输入未发送') + ';请稍后在原会话重新发送。若持续占用,请联系管理员检查上一轮主机会话。',
|
|
45
|
+
BROKER_OWNERSHIP_UNAVAILABLE: '主机会话组件缺少安全续接能力,本轮输入未发送;请联系管理员更新主机组件。',
|
|
46
|
+
BROKER_OWNERSHIP_UNCONFIRMED: '主机尚未确认这条会话的归属,本轮输入未发送;请检查远程主机连接后在原会话重试。',
|
|
47
|
+
BROKER_OWNERSHIP_INVALID: '远端会话编号无法确认,本轮输入未发送;请联系管理员查看技术详情。',
|
|
45
48
|
ABORTED: promptDispatched ? '本轮已停止。操作可能已部分执行,请先确认文件;恢复连接后可在原会话继续。' : '本轮已停止,本轮输入未发送;恢复连接后可在原会话重新发送。',
|
|
46
49
|
CONFIRMATION_REQUIRED: '请先选择是否打开授权窗口。',
|
|
47
50
|
CONTEXT_UNSUPPORTED: '远程 Codex 暂不支持这类会话内容,请联系管理员。',
|
package/dist/provider.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ToolBridge } from './toolbridge.ts';
|
|
1
2
|
import { type SessionSource } from './host-workspace.ts';
|
|
2
3
|
import type { CloseCause, CloseDiagnostics } from './close-diagnostics.ts';
|
|
3
4
|
import type { AgentApp } from '@agentclientprotocol/sdk';
|
|
@@ -14,6 +15,7 @@ import type { SourceAdmissionStage } from './workspace-source.ts';
|
|
|
14
15
|
import type { LocalWorkspaceContext } from '@ai-workbench/connect/api';
|
|
15
16
|
import type { EmployeeRouting } from './employee-relay.ts';
|
|
16
17
|
import type { HostWorkspacePresentation } from './input.ts';
|
|
18
|
+
import type { SessionOwnership } from './broker-ownership.ts';
|
|
17
19
|
export interface EmployeeExecution {
|
|
18
20
|
hostCwd: string;
|
|
19
21
|
routing: EmployeeRouting;
|
|
@@ -37,6 +39,7 @@ export interface Runtime {
|
|
|
37
39
|
readable: ReadableStream<Uint8Array>;
|
|
38
40
|
} | AgentApp | ExtendedAgentApp;
|
|
39
41
|
broker?: BrokerTransport;
|
|
42
|
+
sessionOwnership?: SessionOwnership;
|
|
40
43
|
loginRequired?: () => Promise<void>;
|
|
41
44
|
dispose?: (cause?: CloseCause) => Promise<void>;
|
|
42
45
|
binding?: {
|
|
@@ -64,6 +67,7 @@ export interface Runtime {
|
|
|
64
67
|
closeDiagnostics?: CloseDiagnostics;
|
|
65
68
|
}
|
|
66
69
|
export interface RuntimeRequest extends Invocation {
|
|
70
|
+
toolBridge?: ToolBridge;
|
|
67
71
|
workspaceSource?: SessionSource;
|
|
68
72
|
hostSource?: Extract<SessionSource, {
|
|
69
73
|
source: 'host';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type GenerateOptions, type StreamChunk } from '@deepseek-ai/dsh-llm';
|
|
2
|
+
import { ToolBridge } from './toolbridge.ts';
|
|
3
|
+
import type { ImageReader } from './input.ts';
|
|
4
|
+
type Producer = (options: GenerateOptions, bridge: ToolBridge) => AsyncGenerator<StreamChunk>;
|
|
5
|
+
/** Owns the original prompt across official generate/tool/generate steps. No tool execution. */
|
|
6
|
+
export declare class ToolBridgeLoop {
|
|
7
|
+
private readImage?;
|
|
8
|
+
private stopped?;
|
|
9
|
+
private runs;
|
|
10
|
+
private concluding;
|
|
11
|
+
constructor(readImage?: ImageReader | undefined, stopped?: ((sessionId: string, notice: string) => void) | undefined);
|
|
12
|
+
private launch;
|
|
13
|
+
conclude(id: string, messages: GenerateOptions['messages']): Promise<void>;
|
|
14
|
+
active(id: string): boolean;
|
|
15
|
+
cancel(id: string): void;
|
|
16
|
+
dispose(): Promise<void>;
|
|
17
|
+
stream(options: GenerateOptions, producer: Producer): AsyncGenerator<StreamChunk>;
|
|
18
|
+
}
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { createAssistantMessage } from '@deepseek-ai/dsh-llm';
|
|
2
|
+
import { ToolBridge } from "./toolbridge.js";
|
|
3
|
+
import { NeedError } from "./remote.js";
|
|
4
|
+
/** Rebase native text blocks at each DSH step; a parked stream must end all open blocks. */
|
|
5
|
+
class Projection {
|
|
6
|
+
blocks = new Map();
|
|
7
|
+
next = 0;
|
|
8
|
+
chunks(chunk) {
|
|
9
|
+
if (chunk.type === 'block-start') {
|
|
10
|
+
if (chunk.blockType !== 'text' && chunk.blockType !== 'reasoning')
|
|
11
|
+
throw Error('TOOLBRIDGE_NATIVE_BLOCK_UNSUPPORTED');
|
|
12
|
+
this.blocks.set(chunk.index, { type: chunk.blockType, text: '' });
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
if (chunk.type === 'text-delta' || chunk.type === 'reasoning-delta') {
|
|
16
|
+
const block = this.blocks.get(chunk.index);
|
|
17
|
+
if (!block)
|
|
18
|
+
throw Error('TOOLBRIDGE_NATIVE_BLOCK_MISSING');
|
|
19
|
+
const result = [];
|
|
20
|
+
if (block.index === undefined) {
|
|
21
|
+
block.index = this.next++;
|
|
22
|
+
result.push({ type: 'block-start', index: block.index, blockType: block.type });
|
|
23
|
+
}
|
|
24
|
+
const text = chunk.type === 'text-delta' ? chunk.text : chunk.text;
|
|
25
|
+
block.text += text;
|
|
26
|
+
result.push({ ...chunk, index: block.index });
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
if (chunk.type === 'block-end') {
|
|
30
|
+
const block = this.blocks.get(chunk.index);
|
|
31
|
+
this.blocks.delete(chunk.index);
|
|
32
|
+
return block?.index === undefined ? [] : [{ type: 'block-end', index: block.index, block: { type: block.type, text: block.text } }];
|
|
33
|
+
}
|
|
34
|
+
return [chunk];
|
|
35
|
+
}
|
|
36
|
+
park() { const chunks = []; for (const block of this.blocks.values())
|
|
37
|
+
if (block.index !== undefined) {
|
|
38
|
+
chunks.push({ type: 'block-end', index: block.index, block: { type: block.type, text: block.text } });
|
|
39
|
+
block.index = undefined;
|
|
40
|
+
block.text = '';
|
|
41
|
+
} return chunks; }
|
|
42
|
+
}
|
|
43
|
+
/** Owns the original prompt across official generate/tool/generate steps. No tool execution. */
|
|
44
|
+
export class ToolBridgeLoop {
|
|
45
|
+
readImage;
|
|
46
|
+
stopped;
|
|
47
|
+
runs = new Map();
|
|
48
|
+
concluding = new Map();
|
|
49
|
+
constructor(readImage, stopped) {
|
|
50
|
+
this.readImage = readImage;
|
|
51
|
+
this.stopped = stopped;
|
|
52
|
+
}
|
|
53
|
+
launch(options, producer) {
|
|
54
|
+
const controller = new AbortController(), bridge = new ToolBridge(options, this.readImage);
|
|
55
|
+
const run = { bridge, controller, options, latest: options, queue: [], wake: Promise.withResolvers(), done: false, task: undefined, seen: new Set(options.messages.flatMap(m => 'id' in m && typeof m.id === 'string' ? [m.id] : [])), deferred: [], text: '', reading: false, projection: new Projection(), off: () => { } };
|
|
56
|
+
const signal = AbortSignal.any([controller.signal, ...options.signal ? [options.signal] : []]);
|
|
57
|
+
const changed = () => { run.wake.resolve(); };
|
|
58
|
+
bridge.changed = changed;
|
|
59
|
+
bridge.onAbandon = notice => { controller.abort(); if (options.sessionId)
|
|
60
|
+
this.stopped?.(options.sessionId, notice); };
|
|
61
|
+
const abort = () => { bridge.close(); changed(); };
|
|
62
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
63
|
+
run.off = () => signal.removeEventListener('abort', abort);
|
|
64
|
+
run.task = (async () => {
|
|
65
|
+
try {
|
|
66
|
+
for await (const chunk of producer({ ...options, signal }, bridge)) {
|
|
67
|
+
if (chunk.type === 'finish') {
|
|
68
|
+
run.finish = chunk;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (chunk.type === 'text-delta')
|
|
72
|
+
run.text += chunk.text;
|
|
73
|
+
run.queue.push(chunk);
|
|
74
|
+
changed();
|
|
75
|
+
}
|
|
76
|
+
if (bridge.unsettled)
|
|
77
|
+
bridge.abandon('HOST_PROMPT_ENDED_WITH_PENDING_CALL');
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
run.error = error;
|
|
81
|
+
if (bridge.unsettled)
|
|
82
|
+
bridge.abandon('HOST_PROMPT_FAILED');
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
run.done = true;
|
|
86
|
+
changed();
|
|
87
|
+
}
|
|
88
|
+
})();
|
|
89
|
+
return run;
|
|
90
|
+
}
|
|
91
|
+
async conclude(id, messages) {
|
|
92
|
+
const existing = this.concluding.get(id);
|
|
93
|
+
if (existing)
|
|
94
|
+
return existing.flight;
|
|
95
|
+
const run = this.runs.get(id);
|
|
96
|
+
if (!run || !run.bridge.enabled || !run.bridge.unsettled) {
|
|
97
|
+
this.cancel(id);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
this.runs.delete(id);
|
|
101
|
+
run.bridge.beginConclude();
|
|
102
|
+
run.bridge.onConcludeAcknowledged = () => run.controller.abort();
|
|
103
|
+
const flight = (async () => {
|
|
104
|
+
try {
|
|
105
|
+
await run.bridge.results({ ...run.latest, messages, signal: run.controller.signal });
|
|
106
|
+
await run.task;
|
|
107
|
+
if (run.error && !run.bridge.concludeAcknowledged)
|
|
108
|
+
throw run.error;
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
run.bridge.close();
|
|
112
|
+
run.controller.abort();
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
run.off();
|
|
117
|
+
}
|
|
118
|
+
})();
|
|
119
|
+
this.concluding.set(id, { run, flight });
|
|
120
|
+
void flight.then(() => { if (this.concluding.get(id)?.flight === flight)
|
|
121
|
+
this.concluding.delete(id); }, () => { });
|
|
122
|
+
await flight;
|
|
123
|
+
}
|
|
124
|
+
active(id) { return (this.runs.get(id)?.bridge.enabled ?? false) || this.concluding.has(id); }
|
|
125
|
+
cancel(id) {
|
|
126
|
+
const closing = this.concluding.get(id);
|
|
127
|
+
if (closing) {
|
|
128
|
+
this.concluding.delete(id);
|
|
129
|
+
closing.run.bridge.close();
|
|
130
|
+
closing.run.controller.abort();
|
|
131
|
+
closing.run.off();
|
|
132
|
+
}
|
|
133
|
+
const run = this.runs.get(id);
|
|
134
|
+
if (!run)
|
|
135
|
+
return;
|
|
136
|
+
this.runs.delete(id);
|
|
137
|
+
run.bridge.close();
|
|
138
|
+
run.controller.abort();
|
|
139
|
+
run.off();
|
|
140
|
+
}
|
|
141
|
+
async dispose() { for (const id of new Set([...this.runs.keys(), ...this.concluding.keys()]))
|
|
142
|
+
this.cancel(id); }
|
|
143
|
+
async *stream(options, producer) {
|
|
144
|
+
const key = options.sessionId ?? '';
|
|
145
|
+
const closing = this.concluding.get(key);
|
|
146
|
+
if (closing) {
|
|
147
|
+
let aborted;
|
|
148
|
+
try {
|
|
149
|
+
await Promise.race([closing.flight, ...(options.signal ? [new Promise((_r, reject) => { aborted = () => reject(new NeedError('ABORTED', '本轮等待已取消,尚未发送新输入。', '原主机prompt收尾')); options.signal.addEventListener('abort', aborted, { once: true }); if (options.signal.aborted)
|
|
150
|
+
aborted(); })] : [])]);
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
if (this.concluding.get(key) === closing)
|
|
154
|
+
this.cancel(key);
|
|
155
|
+
else {
|
|
156
|
+
closing.run.bridge.close();
|
|
157
|
+
closing.run.controller.abort();
|
|
158
|
+
closing.run.off();
|
|
159
|
+
}
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
162
|
+
finally {
|
|
163
|
+
if (aborted)
|
|
164
|
+
options.signal?.removeEventListener('abort', aborted);
|
|
165
|
+
if (this.concluding.get(key) === closing)
|
|
166
|
+
this.concluding.delete(key);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
let run = this.runs.get(key), boundary = false;
|
|
170
|
+
if (run?.reading)
|
|
171
|
+
throw new NeedError('TURN_BUSY', '工具桥仍在处理当前生成步骤。', '等待当前步骤结束');
|
|
172
|
+
if (!run) {
|
|
173
|
+
run = this.launch(options, producer);
|
|
174
|
+
if (key)
|
|
175
|
+
this.runs.set(key, run);
|
|
176
|
+
}
|
|
177
|
+
run.reading = true;
|
|
178
|
+
run.projection.next = 0;
|
|
179
|
+
try {
|
|
180
|
+
if (options.signal?.aborted)
|
|
181
|
+
throw new NeedError('ABORTED', '本轮已取消,工具结果没有重放。', '确认主机取消状态');
|
|
182
|
+
if (run.latest !== options) {
|
|
183
|
+
for (const m of options.messages)
|
|
184
|
+
if (m.role === 'user' && 'source' in m && m.source?.kind === 'user' && 'id' in m && typeof m.id === 'string' && !run.seen.has(m.id)) {
|
|
185
|
+
run.seen.add(m.id);
|
|
186
|
+
run.deferred.push(m);
|
|
187
|
+
}
|
|
188
|
+
run.latest = options;
|
|
189
|
+
await run.bridge.results(options);
|
|
190
|
+
}
|
|
191
|
+
for (;;) {
|
|
192
|
+
const wake = run.wake;
|
|
193
|
+
run.wake = Promise.withResolvers();
|
|
194
|
+
while (run.queue.length)
|
|
195
|
+
yield* run.projection.chunks(run.queue.shift());
|
|
196
|
+
if (run.error)
|
|
197
|
+
throw run.error;
|
|
198
|
+
if (run.bridge.pending.length && !run.done) {
|
|
199
|
+
yield* run.projection.park();
|
|
200
|
+
for (const p of run.bridge.pending) {
|
|
201
|
+
p.exported = true;
|
|
202
|
+
const index = run.projection.next++;
|
|
203
|
+
yield { type: 'block-start', index, blockType: 'tool-call' };
|
|
204
|
+
yield { type: 'tool-call-delta', index, id: p.block.id, name: p.block.name, argumentsDelta: p.block.arguments };
|
|
205
|
+
yield { type: 'block-end', index, block: p.block };
|
|
206
|
+
}
|
|
207
|
+
boundary = true;
|
|
208
|
+
yield { type: 'finish', reason: { kind: 'tool-calls' }, ...(run.bridge.replayState ? { replayState: run.bridge.replayState } : {}) };
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (run.done) {
|
|
212
|
+
yield* run.projection.park();
|
|
213
|
+
run.off();
|
|
214
|
+
if (run.deferred.length) {
|
|
215
|
+
const deferred = run.deferred, ids = new Set(deferred.flatMap(m => 'id' in m ? [m.id] : []));
|
|
216
|
+
const last = createAssistantMessage({ content: run.text ? [{ type: 'text', text: run.text }] : [], source: { provider: run.options.provider, model: run.options.model, ...(run.finish?.replayState ? { replayState: { response: run.finish.replayState.response } } : {}) } });
|
|
217
|
+
const next = { ...options, messages: [...run.latest.messages.filter(m => !('id' in m && ids.has(m.id))), last, ...deferred] };
|
|
218
|
+
const nextIndex = run.projection.next;
|
|
219
|
+
run.bridge.close();
|
|
220
|
+
run = this.launch(next, producer);
|
|
221
|
+
run.projection.next = nextIndex;
|
|
222
|
+
run.reading = true;
|
|
223
|
+
if (key)
|
|
224
|
+
this.runs.set(key, run);
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (key)
|
|
228
|
+
this.runs.delete(key);
|
|
229
|
+
run.bridge.close();
|
|
230
|
+
boundary = true;
|
|
231
|
+
yield run.finish ?? { type: 'finish', reason: { kind: 'stop' } };
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (options.signal?.aborted)
|
|
235
|
+
throw new NeedError('ABORTED', '本轮已取消,工具结果没有重放。', '确认主机取消状态');
|
|
236
|
+
await Promise.race([wake.promise, run.wake.promise]);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
finally {
|
|
240
|
+
run.reading = false;
|
|
241
|
+
if (!boundary || options.signal?.aborted) {
|
|
242
|
+
run.bridge.close();
|
|
243
|
+
run.controller.abort();
|
|
244
|
+
run.off();
|
|
245
|
+
if (key && this.runs.get(key) === run)
|
|
246
|
+
this.runs.delete(key);
|
|
247
|
+
await run.task;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ToolSchema } from '@deepseek-ai/dsh-llm';
|
|
2
|
+
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
/** Fixed official tool names; no description heuristics or plugin-name branches. */
|
|
4
|
+
export declare const TOOLBRIDGE_EXCLUDED: readonly ["read", "write", "edit", "read_image", "glob", "grep", "bash", "pwsh", "str_replace_editor", "job_output", "job_list", "job_kill", "load_workspace_dependencies", "terminal_open", "terminal_send", "terminal_read", "terminal_signal", "terminal_close", "terminal_list", "subagent", "list_subagent_models", "send_message", "interrupt_agent", "spawn_teammate", "list_agents", "wait_agent", "team_task_create", "team_task_list", "team_task_get", "team_task_update", "run_code"];
|
|
5
|
+
export interface BridgeTool {
|
|
6
|
+
alias: string;
|
|
7
|
+
schema: ToolSchema;
|
|
8
|
+
wire: Tool;
|
|
9
|
+
}
|
|
10
|
+
export declare function bridgeTable(tools?: readonly ToolSchema[]): ReadonlyMap<string, BridgeTool>;
|