@ai-workbench/remote-agent 0.8.10 → 0.8.12
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 +29 -6
- package/dist/acp.js +1 -1
- package/dist/catalog-diagnostics.d.ts +16 -0
- package/dist/catalog-diagnostics.js +108 -0
- package/dist/catalog-ssh.d.ts +51 -0
- package/dist/catalog-ssh.js +165 -0
- package/dist/client.js +41 -1
- package/dist/employee-mcp.js +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +133 -27
- package/dist/model-catalog.d.ts +24 -5
- package/dist/model-catalog.js +174 -132
- package/dist/model-defaults.d.ts +29 -0
- package/dist/model-defaults.js +62 -0
- package/dist/model-input-retention.d.ts +22 -0
- package/dist/model-input-retention.js +137 -0
- package/dist/preflight-diagnostic.d.ts +22 -0
- package/dist/preflight-diagnostic.js +112 -0
- package/dist/preflight-payload.d.ts +11 -0
- package/dist/preflight-payload.js +11 -0
- package/dist/presentation.js +6 -0
- package/dist/provider.d.ts +5 -1
- package/dist/provider.js +48 -21
- package/dist/remote-details.d.ts +18 -0
- package/dist/remote-details.js +140 -125
- package/dist/workspace-source-confirmation.d.ts +4 -0
- package/dist/workspace-source-confirmation.js +12 -0
- package/dist/workspace-source.d.ts +25 -0
- package/dist/workspace-source.js +136 -0
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { runCatalog } from "./catalog-ssh.js";
|
|
2
|
+
import { createCatalogDiagnostics, CatalogDiagnosticsRemote } from "./catalog-diagnostics.js";
|
|
1
3
|
import { SessionId } from '@deepseek-ai/dsh-session';
|
|
2
4
|
import Schema from '@deepseek-ai/schemastery';
|
|
3
5
|
import { LlmAdapter, LlmError } from '@deepseek-ai/dsh-llm';
|
|
@@ -24,11 +26,15 @@ import { verifyRecoveryCheckpoint, currentInputIds } from "./recovery-checkpoint
|
|
|
24
26
|
import { ModelCatalog, modelInfo } from "./model-catalog.js";
|
|
25
27
|
import { ModelConfigurationError } from "./model-configuration.js";
|
|
26
28
|
import { officialEmployeeWorkspace } from "./employee-workspace.js";
|
|
29
|
+
import { nativeWorkspaceSourceConfirmation } from "./workspace-source-confirmation.js";
|
|
27
30
|
import { resolveEmployeeNative } from "./employee-native.js";
|
|
28
31
|
import { createLocalUserTurnObserver } from "./local-user-turn.js";
|
|
29
32
|
import { createClaudeLocalProvider } from "./claude-local.js";
|
|
30
33
|
import { readLocalUserTurn } from "./local-user-turn.js";
|
|
31
34
|
import { readEmployeeWorkspace } from "./employee-workspace.js";
|
|
35
|
+
import { createModelDefaults, createDefaultSelectionWriter } from "./model-defaults.js";
|
|
36
|
+
import { createPreflightDiagnostics } from "./preflight-diagnostic.js";
|
|
37
|
+
import { installModelInputRetention, markModelPreparationFailure } from "./model-input-retention.js";
|
|
32
38
|
export const name = 'ai-workbench-remote-agent';
|
|
33
39
|
export const settingsNamespace = 'ai-workbench-codex';
|
|
34
40
|
// L can be installed/enabled after X; keeping X mounted lets it explain the prerequisite.
|
|
@@ -65,6 +71,23 @@ function readPointer(options, agent) {
|
|
|
65
71
|
throw new NeedError('SESSION_CONTEXT_DIGEST_INVALID', '原Codex会话的初建上下文摘要未知或无效,不能猜测是否相同,本轮未发送', '原会话合法的64位十六进制初建上下文摘要');
|
|
66
72
|
return pointer;
|
|
67
73
|
}
|
|
74
|
+
// A reporting failure can wrap the original admission error. Preserve its
|
|
75
|
+
// already-safe login snapshot even when a newer preflight delivery is unknown.
|
|
76
|
+
function originalLoginFailure(error) {
|
|
77
|
+
const pending = [error], seen = new Set();
|
|
78
|
+
while (pending.length) {
|
|
79
|
+
const current = pending.pop();
|
|
80
|
+
if (!current || typeof current !== 'object' || seen.has(current))
|
|
81
|
+
continue;
|
|
82
|
+
seen.add(current);
|
|
83
|
+
if (current instanceof NeedError && current.technical)
|
|
84
|
+
return current;
|
|
85
|
+
if (current instanceof AggregateError)
|
|
86
|
+
pending.push(...current.errors);
|
|
87
|
+
if (current instanceof Error && current.cause !== undefined)
|
|
88
|
+
pending.push(current.cause);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
68
91
|
export class CodexRemoteAdapter extends LlmAdapter {
|
|
69
92
|
resolveRuntime;
|
|
70
93
|
approve;
|
|
@@ -73,6 +96,7 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
73
96
|
readImage;
|
|
74
97
|
reportLoginFailure;
|
|
75
98
|
directory;
|
|
99
|
+
preflightDiagnostics;
|
|
76
100
|
config;
|
|
77
101
|
sessions = new Map();
|
|
78
102
|
turns = new Map();
|
|
@@ -80,7 +104,7 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
80
104
|
shuttingDown = false;
|
|
81
105
|
detached = new Set();
|
|
82
106
|
cleanups = new WeakMap();
|
|
83
|
-
constructor(config, resolveRuntime, approve, providerName = () => PROVIDER_NAMES.en, agent = CODEX_AGENT, readImage, reportLoginFailure, directory) {
|
|
107
|
+
constructor(config, resolveRuntime, approve, providerName = () => PROVIDER_NAMES.en, agent = CODEX_AGENT, readImage, reportLoginFailure, directory, preflightDiagnostics) {
|
|
84
108
|
super();
|
|
85
109
|
this.resolveRuntime = resolveRuntime;
|
|
86
110
|
this.approve = approve;
|
|
@@ -89,6 +113,7 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
89
113
|
this.readImage = readImage;
|
|
90
114
|
this.reportLoginFailure = reportLoginFailure;
|
|
91
115
|
this.directory = directory;
|
|
116
|
+
this.preflightDiagnostics = preflightDiagnostics;
|
|
92
117
|
this.config = typeof config === 'function' ? config : () => config;
|
|
93
118
|
}
|
|
94
119
|
providerInfo(provider) { return { id: provider, name: this.providerName() }; }
|
|
@@ -100,14 +125,21 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
100
125
|
finally {
|
|
101
126
|
catalog.release();
|
|
102
127
|
} }
|
|
103
|
-
async resolveModel(provider, id, signal) {
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
128
|
+
async resolveModel(provider, id, signal) { try {
|
|
129
|
+
const catalog = await this.catalog(signal);
|
|
130
|
+
try {
|
|
131
|
+
const model = catalog.models.find(model => model.id === id);
|
|
132
|
+
if (!model)
|
|
133
|
+
throw new LlmError(id === 'remote' ? '无法获取模型列表:这个会话保存的是旧版模型选择,请打开本会话的模型菜单,重新选择模型后再发送。' : '所选模型已不在当前主机目录中,请打开本会话的模型菜单重新选择模型。', 'MODEL_CATALOG_UNAVAILABLE');
|
|
134
|
+
return modelInfo(provider, model);
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
catalog.release();
|
|
138
|
+
}
|
|
108
139
|
}
|
|
109
|
-
|
|
110
|
-
|
|
140
|
+
catch (error) {
|
|
141
|
+
markModelPreparationFailure(provider, error, signal);
|
|
142
|
+
throw error;
|
|
111
143
|
} }
|
|
112
144
|
async invalidateModelState() { for (const turn of this.turns.values())
|
|
113
145
|
turn.abort(); await Promise.all([...this.sessions.keys()].map(id => this.disposeSession(id))); }
|
|
@@ -177,10 +209,11 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
177
209
|
if (error instanceof NeedError) {
|
|
178
210
|
const rpc = /RPC 错误码 (-?\d+)/u.exec(error.requires)?.[1];
|
|
179
211
|
const message = employeeFailure(error.code, this.agent.loginProvider) + (rpc ? `(技术详情:RPC 错误码 ${rpc})` : '');
|
|
180
|
-
|
|
181
|
-
|
|
212
|
+
const login = originalLoginFailure(error);
|
|
213
|
+
let diagnostic = login?.technical ? JSON.stringify(login.technical) : '';
|
|
214
|
+
if (login?.technical && this.reportLoginFailure) {
|
|
182
215
|
try {
|
|
183
|
-
const receipt = await this.reportLoginFailure(options.sessionId,
|
|
216
|
+
const receipt = await this.reportLoginFailure(options.sessionId, login.code, login.technical);
|
|
184
217
|
diagnostic += ';诊断 eventId=' + receipt.eventId;
|
|
185
218
|
}
|
|
186
219
|
catch (failure) {
|
|
@@ -206,6 +239,8 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
206
239
|
const signal = AbortSignal.any([this.lifetime.signal, turn.signal, ...(original.signal ? [original.signal] : [])]);
|
|
207
240
|
const options = { ...original, signal };
|
|
208
241
|
const timing = new StageTiming();
|
|
242
|
+
let request;
|
|
243
|
+
let earlyStage = 'input-prepare';
|
|
209
244
|
let runtime;
|
|
210
245
|
let session;
|
|
211
246
|
let live;
|
|
@@ -218,6 +253,10 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
218
253
|
let modelConfiguration;
|
|
219
254
|
const config = this.config();
|
|
220
255
|
const mode = this.agent.oneShot ? config.mode : 'session';
|
|
256
|
+
request = { sessionId: options.sessionId, mode: mode ?? 'session', timing, signal };
|
|
257
|
+
this.preflightDiagnostics?.begin(request, this.agent.loginProvider);
|
|
258
|
+
if (this.preflightDiagnostics)
|
|
259
|
+
request.recordEarlyFailure = (error, stage) => this.preflightDiagnostics.failure(request, this.agent.loginProvider, error, stage);
|
|
221
260
|
try {
|
|
222
261
|
if (!mode || (mode === 'session' && (!Number.isSafeInteger(config.acpMaxMessageBytes) || config.acpMaxMessageBytes < 1)))
|
|
223
262
|
throw new NeedError('SETTINGS_REQUIRED', '远程模型连接设置无效,请联系管理员检查', '有效的mode/acpMaxMessageBytes');
|
|
@@ -236,11 +275,13 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
236
275
|
const configuration = JSON.stringify([mode, config.acpMaxMessageBytes, config.brokerMaxFrameBytes, config.brokerControlMaxBytes, config.employeeShellKillGraceMs]);
|
|
237
276
|
if (live && live.configuration !== configuration)
|
|
238
277
|
throw new NeedError('SESSION_CONTEXT_MISMATCH', '会话运行设置已改变,请新建会话', '原会话运行设置');
|
|
239
|
-
|
|
278
|
+
Object.assign(request, { timing, acpMaxMessageBytes: config.acpMaxMessageBytes, resumeSessionId: previous?.sessionId, recoveryCheckpointId: previous?.recoveryCheckpointId, sessionId: options.sessionId, mode, cwd: previous?.cwd, identity: previous?.identity, bindingId: previous?.bindingId, workspaceId: previous?.workspaceId, agent: this.agent, retainProcess: !!key && mode === 'session', signal, brokerSettings: Object.freeze({ brokerMaxFrameBytes: config.brokerMaxFrameBytes, brokerControlMaxBytes: config.brokerControlMaxBytes }) });
|
|
240
279
|
const after = options.messages.findLastIndex(message => message.role === 'assistant' && !isWorkspaceNoticeMessage(message));
|
|
241
280
|
request.userInputIds = Object.freeze(options.messages.slice(after + 1).flatMap(message => message.role === 'user' && message.source?.kind === 'user' && typeof message.id === 'string' ? [message.id] : []));
|
|
242
281
|
request.userMessageIds = request.userInputIds;
|
|
282
|
+
earlyStage = 'source-context';
|
|
243
283
|
await this.resolveRuntime.preflight?.(request);
|
|
284
|
+
earlyStage = 'input-prepare';
|
|
244
285
|
let input = await timing.run('input-prepare', () => prepareInput(options, this.readImage));
|
|
245
286
|
const contextSha256 = createHash('sha256').update(input.systemPrompt ?? '').digest('hex');
|
|
246
287
|
request.contextSha256 = contextSha256;
|
|
@@ -270,6 +311,7 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
270
311
|
if (mode === 'one-shot')
|
|
271
312
|
throw new NeedError('MODEL_CONFIGURATION_UNSUPPORTED', '单次模式不支持确认当前模型与推理等级,请改用会话模式', '支持模型配置确认的会话模式');
|
|
272
313
|
request.localAdmissionCheck?.();
|
|
314
|
+
earlyStage = 'model-catalog';
|
|
273
315
|
catalog = await this.catalog(signal);
|
|
274
316
|
selected = catalog.models.find(model => model.id === options.model);
|
|
275
317
|
if (!selected)
|
|
@@ -279,6 +321,7 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
279
321
|
let modelSignal = AbortSignal.any([signal, catalog.signal]);
|
|
280
322
|
options.signal = modelSignal;
|
|
281
323
|
request.signal = modelSignal;
|
|
324
|
+
earlyStage = 'runtime-resolve';
|
|
282
325
|
runtime = await this.resolveRuntime(request);
|
|
283
326
|
signal.throwIfAborted();
|
|
284
327
|
if (runtime.execution?.signal) {
|
|
@@ -315,6 +358,7 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
315
358
|
if (!details)
|
|
316
359
|
throw new NeedError('TECHNICAL_DETAILS_UNAVAILABLE', '技术详情暂不可用,请更新远程主机连接组件。', 'L技术详情持久接收能力');
|
|
317
360
|
timing.bind(detail => details.record(detail));
|
|
361
|
+
request.recordEarlyFailure = undefined;
|
|
318
362
|
const mapper = new StreamMapper(mode === 'session' && !contextChanged ? (runtime.restartNotice ? undefined : live ? live.usageSnapshot : previous ? (previous.usageSnapshot ?? null) : undefined) : undefined, observation => details.record(observation));
|
|
319
363
|
activeMapper = mapper;
|
|
320
364
|
let id;
|
|
@@ -505,11 +549,29 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
505
549
|
if (live && (remoteAttempted || runtime === live.runtime))
|
|
506
550
|
live.failure = { cause: error };
|
|
507
551
|
let reportingFailure;
|
|
552
|
+
if (request?.recordEarlyFailure) {
|
|
553
|
+
const record = request.recordEarlyFailure;
|
|
554
|
+
request.recordEarlyFailure = undefined;
|
|
555
|
+
const code = error instanceof NeedError || error instanceof ModelConfigurationError ? error.code : error instanceof LlmError ? error.failure.code : signal.aborted ? 'ABORTED' : 'WORKSPACE_SOURCE_CONFIRMATION_FAILED';
|
|
556
|
+
try {
|
|
557
|
+
await record(Object.assign(new NeedError(code, '本轮在执行前失败。', '白名单阶段与错误代码'), { cause: error }), earlyStage);
|
|
558
|
+
}
|
|
559
|
+
catch (failure) {
|
|
560
|
+
reportingFailure = { cause: failure };
|
|
561
|
+
}
|
|
562
|
+
}
|
|
508
563
|
try {
|
|
509
564
|
await runtime?.details?.flush();
|
|
510
565
|
}
|
|
511
566
|
catch (failure) {
|
|
512
|
-
reportingFailure
|
|
567
|
+
if (reportingFailure === undefined)
|
|
568
|
+
reportingFailure = { cause: failure };
|
|
569
|
+
else {
|
|
570
|
+
const first = reportingFailure.cause;
|
|
571
|
+
const originalCode = error instanceof NeedError || error instanceof ModelConfigurationError ? error.code : error instanceof LlmError ? error.failure.code : 'WORKSPACE_SOURCE_CONFIRMATION_FAILED';
|
|
572
|
+
const flushCode = failure instanceof NeedError || failure instanceof ModelConfigurationError ? failure.code : failure instanceof LlmError ? failure.failure.code : 'CONVERSATION_DETAILS_FAILED';
|
|
573
|
+
reportingFailure = { cause: Object.assign(new NeedError('CONVERSATION_DETAILS_FAILED', '本轮诊断保存及详情收尾失败,已停止。', '原错误代码 ' + originalCode + ';' + (first instanceof NeedError ? first.requires : '早期诊断保存失败') + ';详情收尾代码 ' + flushCode), { cause: new AggregateError([error, first, failure], 'ADMISSION_DIAGNOSTIC_AND_FLUSH_FAILED') }) };
|
|
574
|
+
}
|
|
513
575
|
}
|
|
514
576
|
if (activeMapper)
|
|
515
577
|
yield* activeMapper.flush();
|
|
@@ -543,8 +605,8 @@ export class CodexRemoteAdapter extends LlmAdapter {
|
|
|
543
605
|
/** One official registration owns both routes, preserving opaque replay ownership. */
|
|
544
606
|
export class RemoteAgentsAdapter extends LlmAdapter {
|
|
545
607
|
adapters = new Map();
|
|
546
|
-
constructor(config, makeRuntime, approve, nameFor, readImage, reportLoginFailure, directory) { super(); for (const agent of AGENTS)
|
|
547
|
-
this.adapters.set(agent.id, new CodexRemoteAdapter(config, makeRuntime(agent), approve, () => nameFor(agent), agent, readImage, reportLoginFailure, directory)); }
|
|
608
|
+
constructor(config, makeRuntime, approve, nameFor, readImage, reportLoginFailure, directory, preflightDiagnostics) { super(); for (const agent of AGENTS)
|
|
609
|
+
this.adapters.set(agent.id, new CodexRemoteAdapter(config, makeRuntime(agent), approve, () => nameFor(agent), agent, readImage, reportLoginFailure, directory, preflightDiagnostics)); }
|
|
548
610
|
adapter(provider) { const adapter = this.adapters.get(provider); if (!adapter)
|
|
549
611
|
throw new LlmError('远程模型未注册', 'PROVIDER_NOT_FOUND'); return adapter; }
|
|
550
612
|
providerInfo(provider) { return this.adapter(provider).providerInfo(provider); }
|
|
@@ -582,8 +644,30 @@ export function apply(ctx, live) {
|
|
|
582
644
|
}
|
|
583
645
|
return manager;
|
|
584
646
|
};
|
|
647
|
+
const preflightDiagnostics = createPreflightDiagnostics({ ctx, getApi: get, manager: managerForProfile });
|
|
585
648
|
let changed = () => { };
|
|
586
|
-
const
|
|
649
|
+
const diagnostics = new Map();
|
|
650
|
+
const diagnostic = (scope, provider, message) => {
|
|
651
|
+
let entry = diagnostics.get(provider);
|
|
652
|
+
if (!entry) {
|
|
653
|
+
entry = new Map();
|
|
654
|
+
diagnostics.set(provider, entry);
|
|
655
|
+
}
|
|
656
|
+
if (entry.get(scope) === message)
|
|
657
|
+
return;
|
|
658
|
+
if (message === undefined)
|
|
659
|
+
entry.delete(scope);
|
|
660
|
+
else
|
|
661
|
+
entry.set(scope, message);
|
|
662
|
+
};
|
|
663
|
+
let defaults;
|
|
664
|
+
const catalogDiagnostics = createCatalogDiagnostics(() => { const profile = ctx.get('profileContext'); return profile?.dir; });
|
|
665
|
+
// Public Loader Entry.options.id; optional when mounted without Loader.
|
|
666
|
+
const actualSettingsNamespace = ctx.fiber.entry?.options.id;
|
|
667
|
+
new CatalogDiagnosticsRemote(ctx, async () => ({ ...await catalogDiagnostics.snapshot(), settingsNamespace: actualSettingsNamespace ?? null, providers: AGENTS.map(agent => ({ provider: agent.id, name: agent.name[language], messages: [...(diagnostics.get(agent.id)?.values() ?? [])] })) }));
|
|
668
|
+
const modelDirectory = new ModelCatalog(get, () => config().acpMaxMessageBytes, (...args) => { const state = get()?.getConnectionState(); const connection = state ? { instanceId: state.instanceId, epoch: state.epoch, phase: state.phase } : null; return runCatalog(...args, event => catalogDiagnostics.record({ ...event, connection })); }, () => changed(), (brand, message) => {
|
|
669
|
+
diagnostic('catalog', AGENTS.find(agent => agent.loginProvider === brand).id, message);
|
|
670
|
+
}, (agent, models, assertCurrent) => defaults?.observe(agent, models, assertCurrent));
|
|
587
671
|
const adapter = new RemoteAgentsAdapter(config, agent => {
|
|
588
672
|
const details = async (request, api, connection, target) => {
|
|
589
673
|
return managerForProfile().open({ sessionId: request.sessionId, provider: agent.loginProvider, identity: `${target.username}@${target.host}:${target.port}`, connection, localAcceptanceId: request.localAcceptanceId ?? request.localAcceptance?.acceptanceId });
|
|
@@ -617,16 +701,20 @@ export function apply(ctx, live) {
|
|
|
617
701
|
};
|
|
618
702
|
return runtime;
|
|
619
703
|
};
|
|
620
|
-
const local = agent.loginProvider === 'claude' ? createClaudeLocalProvider({ getConnect: get, getNetwork: () => ctx.get('aiWorkbenchNetwork'), readWorkspace: id => readEmployeeWorkspace(ctx, id), readUserTurn: request => readLocalUserTurn(ctx, { sessionId: request.sessionId, route: agent.id, userMessageIds: request.userMessageIds ?? [] }), reportAdmissionFailure: (request, code) =>
|
|
621
|
-
|
|
704
|
+
const local = agent.loginProvider === 'claude' ? createClaudeLocalProvider({ getConnect: get, getNetwork: () => ctx.get('aiWorkbenchNetwork'), readWorkspace: id => readEmployeeWorkspace(ctx, id), readUserTurn: request => readLocalUserTurn(ctx, { sessionId: request.sessionId, route: agent.id, userMessageIds: request.userMessageIds ?? [] }), reportAdmissionFailure: (request, code) => preflightDiagnostics.failure(request, 'claude', code, 'local-admission'), recordUnscoped: (request, observation) => managerForProfile().recordLocalFailure({ sessionId: request.sessionId, provider: 'claude', code: observation.code, observation }), configuration: config, details, open }) : undefined;
|
|
705
|
+
const workspace = async (request) => {
|
|
706
|
+
const services = ctx;
|
|
707
|
+
const registry = services.get('workspaceRegistry');
|
|
708
|
+
const sessions = services.get('sessions');
|
|
709
|
+
return officialEmployeeWorkspace(request.sessionId, registry, sessions?.get(request.sessionId)?.header);
|
|
710
|
+
};
|
|
711
|
+
return connectProvider(get, { agent, workspaceSource: {
|
|
712
|
+
workspace, currentUserTurn: request => localTurns.current(request), confirm: nativeWorkspaceSourceConfirmation(ctx),
|
|
713
|
+
record: (request, observation) => preflightDiagnostics.record(request, agent.loginProvider, observation),
|
|
714
|
+
}, ...(agent.loginProvider === 'codex' ? { employee: {
|
|
622
715
|
currentUserTurn: request => localTurns.current(request),
|
|
623
716
|
getNetwork: () => ctx.get('aiWorkbenchNetwork'),
|
|
624
|
-
workspace:
|
|
625
|
-
const services = ctx;
|
|
626
|
-
const registry = services.get('workspaceRegistry');
|
|
627
|
-
const sessions = services.get('sessions');
|
|
628
|
-
return officialEmployeeWorkspace(request.sessionId, registry, sessions?.get(request.sessionId)?.header);
|
|
629
|
-
}, native: () => resolveEmployeeNative(),
|
|
717
|
+
workspace, native: () => resolveEmployeeNative(),
|
|
630
718
|
} } : {}), notice: createWorkspaceNoticeConsumer(ctx, agent), details, open, local });
|
|
631
719
|
}, dshApproval(ctx), agent => agent.name[language], readImage, (sessionId, code, diagnostic) => {
|
|
632
720
|
const events = sessionId ? ctx.get('agents')?.get(SessionId(sessionId))?.session.snapshotEvents() : undefined;
|
|
@@ -646,13 +734,31 @@ export function apply(ctx, live) {
|
|
|
646
734
|
step = null;
|
|
647
735
|
}
|
|
648
736
|
return managerForProfile().recordLoginFailure({ sessionId, code, diagnostic, context: { turn, step } });
|
|
649
|
-
}, modelDirectory);
|
|
737
|
+
}, modelDirectory, preflightDiagnostics);
|
|
650
738
|
const ids = AGENTS.map(agent => agent.id);
|
|
651
|
-
const entries = () => AGENTS.map(agent => ({ provider: agent.id, displayName: agent.name[language], settingsNs: settingsNamespace, settingsPath: [] }));
|
|
739
|
+
const entries = () => AGENTS.map(agent => ({ provider: agent.id, displayName: agent.name[language], settingsNs: actualSettingsNamespace ?? settingsNamespace, settingsPath: [] }));
|
|
652
740
|
const directory = ctx.llm.registerConfigurableProviders(entries());
|
|
653
741
|
const registration = ctx.llm.registerAdapter(ids, adapter);
|
|
654
|
-
|
|
742
|
+
installModelInputRetention(ctx, { isOurProvider: provider => ids.includes(provider), diagnosticForAttempt: position => preflightDiagnostics.forPreparation(position.sessionId, position), preflightDiagnostic: (provider, message) => diagnostic('preflight', provider, message), diagnostic: (provider, message) => diagnostic('input', provider, message) });
|
|
655
743
|
const serviceValue = (name) => ctx.get(name);
|
|
744
|
+
const refreshDefault = () => {
|
|
745
|
+
const service = serviceValue('agentDefaultModel');
|
|
746
|
+
const selected = service?.currentSelection();
|
|
747
|
+
const agent = AGENTS.find(agent => agent.id === selected?.provider);
|
|
748
|
+
if (!defaults || !agent)
|
|
749
|
+
return;
|
|
750
|
+
// Read through the same trusted, fresh catalog. Failures are already recorded there.
|
|
751
|
+
void modelDirectory.read(agent).then(lease => lease.release(), () => { });
|
|
752
|
+
};
|
|
753
|
+
ctx.inject(['agentDefaultModel'], child => {
|
|
754
|
+
const service = child.get('agentDefaultModel');
|
|
755
|
+
const current = createModelDefaults(service, (provider, message) => diagnostic('default', provider, message), createDefaultSelectionWriter(service, () => serviceValue('configEditor')));
|
|
756
|
+
defaults = current;
|
|
757
|
+
refreshDefault();
|
|
758
|
+
child.effect(() => () => { current.dispose(); if (defaults === current)
|
|
759
|
+
defaults = undefined; });
|
|
760
|
+
});
|
|
761
|
+
changed = () => { registration.replace(ids); refreshDefault(); void adapter.invalidateModelState().catch(() => { ctx.logger('remote-agent').warn('原会话清理结果未确认,已阻断继续发送;请联系管理员核对。'); }); };
|
|
656
762
|
let employeeConnect = serviceValue('aiWorkbenchConnect'), employeeNetwork = serviceValue('aiWorkbenchNetwork');
|
|
657
763
|
ctx.on('internal/service', service => {
|
|
658
764
|
if (service === 'aiWorkbenchConnect')
|
package/dist/model-catalog.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type LlmResolvedModelInfo } from '@deepseek-ai/dsh-llm';
|
|
|
2
2
|
import type { ModelCatalogTarget } from '@ai-workbench/connect/api';
|
|
3
3
|
import type { AgentDefinition } from './agents.ts';
|
|
4
4
|
import { type ConnectApi, type ConnectTarget } from './provider.ts';
|
|
5
|
+
export { runCatalog } from './catalog-ssh.ts';
|
|
5
6
|
export interface HostModel {
|
|
6
7
|
id: string;
|
|
7
8
|
value: string;
|
|
@@ -28,15 +29,27 @@ export interface ModelDirectory {
|
|
|
28
29
|
read(agent: AgentDefinition, signal?: AbortSignal): Promise<CatalogLease>;
|
|
29
30
|
}
|
|
30
31
|
export type CatalogRunner = (target: ConnectTarget, command: string, signal: AbortSignal, maxBytes: number) => Promise<string>;
|
|
31
|
-
export
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
export interface CatalogIssue {
|
|
33
|
+
readonly modelName: string;
|
|
34
|
+
readonly reason: string;
|
|
35
|
+
readonly code: string;
|
|
36
|
+
}
|
|
37
|
+
export interface ParsedCatalog {
|
|
38
|
+
readonly models: readonly HostModel[];
|
|
39
|
+
readonly issues: readonly CatalogIssue[];
|
|
40
|
+
}
|
|
41
|
+
export declare function catalogDiagnostic(issues: readonly CatalogIssue[]): string | undefined;
|
|
42
|
+
/** Defaults are provider declarations, never catalog order or display names. */
|
|
43
|
+
export declare function declaredDefault(agent: AgentDefinition, models: readonly HostModel[]): HostModel | undefined;
|
|
44
|
+
export declare function parseHostCatalog(input: string, provider: string, username: string): ParsedCatalog;
|
|
45
|
+
/** Share only pending work; settled results are never cached. */
|
|
35
46
|
export declare class ModelCatalog implements ModelDirectory {
|
|
36
47
|
private get;
|
|
37
48
|
private maxBytes;
|
|
38
49
|
private run;
|
|
39
50
|
private changed;
|
|
51
|
+
private diagnostic;
|
|
52
|
+
private loaded;
|
|
40
53
|
private api?;
|
|
41
54
|
private unsubscribe?;
|
|
42
55
|
private signature?;
|
|
@@ -44,9 +57,15 @@ export declare class ModelCatalog implements ModelDirectory {
|
|
|
44
57
|
private disposed;
|
|
45
58
|
private initialized;
|
|
46
59
|
private active;
|
|
47
|
-
|
|
60
|
+
private flights;
|
|
61
|
+
private serial;
|
|
62
|
+
private latest;
|
|
63
|
+
constructor(get: () => ConnectApi | undefined, maxBytes: () => number | undefined, run?: CatalogRunner, changed?: () => void, diagnostic?: (provider: 'claude' | 'codex', message: string | undefined) => void, loaded?: (agent: AgentDefinition, models: readonly HostModel[], assertCurrent: () => void) => void | Promise<void>);
|
|
48
64
|
refresh(): void;
|
|
49
65
|
private trusted;
|
|
66
|
+
private assertFlight;
|
|
67
|
+
private failure;
|
|
68
|
+
private start;
|
|
50
69
|
read(agent: AgentDefinition, signal?: AbortSignal): Promise<CatalogLease>;
|
|
51
70
|
dispose(): void;
|
|
52
71
|
}
|