@ai-workbench/remote-agent 0.8.2 → 0.8.10
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 +42 -7
- package/dist/acp.d.ts +46 -1
- package/dist/acp.js +296 -36
- package/dist/approval-label.d.ts +6 -0
- package/dist/approval-label.js +57 -0
- package/dist/broker-runtime.d.ts +10 -0
- package/dist/broker-runtime.js +334 -0
- package/dist/broker.d.ts +29 -6
- package/dist/broker.js +146 -142
- package/dist/claude-local.d.ts +37 -0
- package/dist/claude-local.js +207 -0
- package/dist/client.js +71 -2
- package/dist/conversation-details.d.ts +1 -1
- package/dist/conversation-details.js +88 -8
- package/dist/employee-endpoint.d.ts +91 -0
- package/dist/employee-endpoint.js +141 -0
- package/dist/employee-exec.d.ts +53 -0
- package/dist/employee-exec.js +423 -0
- package/dist/employee-location.d.ts +66 -0
- package/dist/employee-location.js +51 -0
- package/dist/employee-mcp.d.ts +42 -0
- package/dist/employee-mcp.js +181 -0
- package/dist/employee-native.d.ts +18 -0
- package/dist/employee-native.js +38 -0
- package/dist/employee-provider.d.ts +42 -0
- package/dist/employee-provider.js +326 -0
- package/dist/employee-relay.d.ts +18 -0
- package/dist/employee-relay.js +49 -0
- package/dist/employee-shell.d.ts +26 -0
- package/dist/employee-shell.js +89 -0
- package/dist/employee-ssh.d.ts +22 -0
- package/dist/employee-ssh.js +123 -0
- package/dist/employee-tools.d.ts +117 -0
- package/dist/employee-tools.js +292 -0
- package/dist/employee-workspace.d.ts +26 -0
- package/dist/employee-workspace.js +61 -0
- package/dist/index.d.ts +31 -14
- package/dist/index.js +335 -42
- package/dist/input.d.ts +15 -0
- package/dist/input.js +65 -24
- package/dist/local-user-turn.d.ts +24 -0
- package/dist/local-user-turn.js +103 -0
- package/dist/model-catalog.d.ts +55 -0
- package/dist/model-catalog.js +254 -0
- package/dist/model-configuration.d.ts +51 -0
- package/dist/model-configuration.js +212 -0
- package/dist/presentation.d.ts +1 -1
- package/dist/presentation.js +50 -2
- package/dist/protocol-boundary.d.ts +13 -0
- package/dist/protocol-boundary.js +49 -0
- package/dist/provider.d.ts +54 -2
- package/dist/provider.js +92 -29
- package/dist/readiness.js +1 -1
- package/dist/recovery-checkpoint.d.ts +6 -0
- package/dist/recovery-checkpoint.js +71 -0
- package/dist/recovery.d.ts +47 -0
- package/dist/recovery.js +61 -0
- package/dist/remote-details.d.ts +10 -0
- package/dist/remote-details.js +14 -4
- package/dist/rpc-namespace.d.ts +3 -0
- package/dist/rpc-namespace.js +50 -0
- package/dist/stage-timing.d.ts +27 -0
- package/dist/stage-timing.js +39 -0
- package/helpers/ripgrep/LICENSE-vscode +13 -0
- package/helpers/ripgrep/PROVENANCE.md +9 -0
- package/helpers/ripgrep/darwin-arm64/rg +0 -0
- package/helpers/ripgrep/darwin-x64/rg +0 -0
- package/helpers/ripgrep/manifest.json +56 -0
- package/helpers/ripgrep/notices/COPYING-ripgrep +3 -0
- package/helpers/ripgrep/notices/DEPENDENCIES.json +946 -0
- package/helpers/ripgrep/notices/LICENCE-PCRE2.md +103 -0
- package/helpers/ripgrep/notices/LICENSE-MIT-ripgrep +21 -0
- package/helpers/ripgrep/notices/UNLICENSE-ripgrep +24 -0
- package/helpers/ripgrep/notices/aho-corasick-1.1.3/COPYING +3 -0
- package/helpers/ripgrep/notices/aho-corasick-1.1.3/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/aho-corasick-1.1.3/UNLICENSE +24 -0
- package/helpers/ripgrep/notices/anyhow-1.0.100/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/anyhow-1.0.100/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/arbitrary-1.4.2/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/arbitrary-1.4.2/LICENSE-MIT +27 -0
- package/helpers/ripgrep/notices/bstr-1.12.0/COPYING +8 -0
- package/helpers/ripgrep/notices/bstr-1.12.0/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/bstr-1.12.0/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/cc-1.2.41/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/cc-1.2.41/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/cfg-if-1.0.4/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/cfg-if-1.0.4/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/crossbeam-channel-0.5.15/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/crossbeam-channel-0.5.15/LICENSE-MIT +27 -0
- package/helpers/ripgrep/notices/crossbeam-channel-0.5.15/LICENSE-THIRD-PARTY +593 -0
- package/helpers/ripgrep/notices/crossbeam-deque-0.8.6/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/crossbeam-deque-0.8.6/LICENSE-MIT +27 -0
- package/helpers/ripgrep/notices/crossbeam-epoch-0.9.18/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/crossbeam-epoch-0.9.18/LICENSE-MIT +27 -0
- package/helpers/ripgrep/notices/crossbeam-utils-0.8.21/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/crossbeam-utils-0.8.21/LICENSE-MIT +27 -0
- package/helpers/ripgrep/notices/derive_arbitrary-1.4.2/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/derive_arbitrary-1.4.2/LICENSE-MIT +27 -0
- package/helpers/ripgrep/notices/encoding_rs-0.8.35/COPYRIGHT +17 -0
- package/helpers/ripgrep/notices/encoding_rs-0.8.35/LICENSE-APACHE +202 -0
- package/helpers/ripgrep/notices/encoding_rs-0.8.35/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/encoding_rs-0.8.35/LICENSE-WHATWG +26 -0
- package/helpers/ripgrep/notices/encoding_rs_io-0.1.7/COPYING +8 -0
- package/helpers/ripgrep/notices/encoding_rs_io-0.1.7/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/encoding_rs_io-0.1.7/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/find-msvc-tools-0.1.4/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/find-msvc-tools-0.1.4/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/getrandom-0.3.4/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/getrandom-0.3.4/LICENSE-MIT +26 -0
- package/helpers/ripgrep/notices/glob-0.3.3/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/glob-0.3.3/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/itoa-1.0.15/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/itoa-1.0.15/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/jobserver-0.1.34/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/jobserver-0.1.34/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/lexopt-0.3.1/LICENSE +19 -0
- package/helpers/ripgrep/notices/libc-0.2.177/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/libc-0.2.177/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/log-0.4.28/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/log-0.4.28/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/memchr-2.7.6/COPYING +3 -0
- package/helpers/ripgrep/notices/memchr-2.7.6/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/memchr-2.7.6/UNLICENSE +24 -0
- package/helpers/ripgrep/notices/memmap2-0.9.8/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/memmap2-0.9.8/LICENSE-MIT +26 -0
- package/helpers/ripgrep/notices/msvc_spectre_libs-0.1.3/LICENSE +21 -0
- package/helpers/ripgrep/notices/pcre2-0.2.11/COPYING +3 -0
- package/helpers/ripgrep/notices/pcre2-0.2.11/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/pcre2-0.2.11/UNLICENSE +24 -0
- package/helpers/ripgrep/notices/pcre2-sys-0.2.10/COPYING +3 -0
- package/helpers/ripgrep/notices/pcre2-sys-0.2.10/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/pcre2-sys-0.2.10/UNLICENSE +24 -0
- package/helpers/ripgrep/notices/pkg-config-0.3.32/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/pkg-config-0.3.32/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/proc-macro2-1.0.101/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/proc-macro2-1.0.101/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/quote-1.0.41/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/quote-1.0.41/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/r-efi-5.3.0/AUTHORS +74 -0
- package/helpers/ripgrep/notices/regex-1.12.2/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/regex-1.12.2/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/regex-automata-0.4.13/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/regex-automata-0.4.13/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/regex-syntax-0.8.8/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/regex-syntax-0.8.8/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/regex-syntax-0.8.8/src/unicode_tables/LICENSE-UNICODE +57 -0
- package/helpers/ripgrep/notices/ryu-1.0.20/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/ryu-1.0.20/LICENSE-BOOST +23 -0
- package/helpers/ripgrep/notices/same-file-1.0.6/COPYING +3 -0
- package/helpers/ripgrep/notices/same-file-1.0.6/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/same-file-1.0.6/UNLICENSE +24 -0
- package/helpers/ripgrep/notices/serde-1.0.228/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/serde-1.0.228/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/serde_core-1.0.228/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/serde_core-1.0.228/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/serde_derive-1.0.228/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/serde_derive-1.0.228/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/serde_json-1.0.145/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/serde_json-1.0.145/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/shlex-1.3.0/LICENSE-APACHE +13 -0
- package/helpers/ripgrep/notices/shlex-1.3.0/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/syn-2.0.106/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/syn-2.0.106/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/termcolor-1.4.1/COPYING +3 -0
- package/helpers/ripgrep/notices/termcolor-1.4.1/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/termcolor-1.4.1/UNLICENSE +24 -0
- package/helpers/ripgrep/notices/textwrap-0.16.2/LICENSE +21 -0
- package/helpers/ripgrep/notices/tikv-jemalloc-sys-0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/tikv-jemalloc-sys-0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/tikv-jemalloc-sys-0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7/jemalloc/COPYING +27 -0
- package/helpers/ripgrep/notices/tikv-jemallocator-0.6.0/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/tikv-jemallocator-0.6.0/LICENSE-MIT +25 -0
- package/helpers/ripgrep/notices/unicode-ident-1.0.19/LICENSE-APACHE +176 -0
- package/helpers/ripgrep/notices/unicode-ident-1.0.19/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/unicode-ident-1.0.19/LICENSE-UNICODE +39 -0
- package/helpers/ripgrep/notices/walkdir-2.5.0/COPYING +3 -0
- package/helpers/ripgrep/notices/walkdir-2.5.0/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/walkdir-2.5.0/UNLICENSE +24 -0
- package/helpers/ripgrep/notices/wasip2-1.0.1+wasi-0.2.4/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/wasip2-1.0.1+wasi-0.2.4/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
- package/helpers/ripgrep/notices/wasip2-1.0.1+wasi-0.2.4/LICENSE-MIT +23 -0
- package/helpers/ripgrep/notices/winapi-util-0.1.11/COPYING +3 -0
- package/helpers/ripgrep/notices/winapi-util-0.1.11/LICENSE-MIT +21 -0
- package/helpers/ripgrep/notices/winapi-util-0.1.11/UNLICENSE +24 -0
- package/helpers/ripgrep/notices/windows-link-0.2.1/license-apache-2.0 +201 -0
- package/helpers/ripgrep/notices/windows-link-0.2.1/license-mit +21 -0
- package/helpers/ripgrep/notices/windows-sys-0.61.2/license-apache-2.0 +201 -0
- package/helpers/ripgrep/notices/windows-sys-0.61.2/license-mit +21 -0
- package/helpers/ripgrep/notices/wit-bindgen-0.46.0/LICENSE-APACHE +201 -0
- package/helpers/ripgrep/notices/wit-bindgen-0.46.0/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
- package/helpers/ripgrep/notices/wit-bindgen-0.46.0/LICENSE-MIT +23 -0
- package/locale/en.json +1 -1
- package/locale/zh.json +2 -2
- package/package.json +30 -12
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
10
|
+
import { openEmployeeLease } from "./employee-endpoint.js";
|
|
11
|
+
import { openEmployeeMcp } from "./employee-mcp.js";
|
|
12
|
+
import { createEmployeeShell } from "./employee-shell.js";
|
|
13
|
+
import { sameTarget, unwrap } from "./provider.js";
|
|
14
|
+
import { sshIo } from "./broker.js";
|
|
15
|
+
import { NeedError, shellQuote } from "./remote.js";
|
|
16
|
+
import { awaitL } from "./provider.js";
|
|
17
|
+
const failure = (code) => new NeedError(code, '当前本机执行上下文无法确认,本轮已停止;没有改在主机执行。', 'L当前本地绑定、N员工端点与S同代桥');
|
|
18
|
+
const NOTICE = '本轮六类文件与命令工具在你的电脑执行,使用你选择的文件夹;Git也使用本机仓库。子代理和内部直接调用尚不支持本机,可能仍在主机;已识别的此类审批不会执行。PDF等二进制读取、后台命令暂不支持。';
|
|
19
|
+
/** Local source only. Host and legacy-host remain with the original provider. */
|
|
20
|
+
export function createClaudeLocalProvider(options) {
|
|
21
|
+
const entries = new Map();
|
|
22
|
+
const admitted = new WeakMap();
|
|
23
|
+
const admit = async (request, api, connection, target) => {
|
|
24
|
+
if (admitted.has(request))
|
|
25
|
+
return;
|
|
26
|
+
try {
|
|
27
|
+
if (api.getContractInfo?.().workspaceLocations !== 1)
|
|
28
|
+
return;
|
|
29
|
+
const local = api;
|
|
30
|
+
if (typeof local.getWorkspaceLocationState !== 'function')
|
|
31
|
+
throw failure('EMPLOYEE_CONTEXT_UNAVAILABLE');
|
|
32
|
+
const workspace = await options.readWorkspace(request.sessionId), state = local.getWorkspaceLocationState(), matches = state.items.filter(row => row.workspaceId === workspace.workspaceId);
|
|
33
|
+
if (matches.length !== 1 || state.phase !== 'ready' || state.instanceId !== connection.instanceId || state.epoch !== connection.epoch)
|
|
34
|
+
throw failure('EMPLOYEE_CONTEXT_CHANGED');
|
|
35
|
+
const item = matches[0];
|
|
36
|
+
if (item.source !== 'local')
|
|
37
|
+
return;
|
|
38
|
+
if (item.status !== 'selected' || !item.bindingId || api.getContractInfo?.().localUserTurnAcceptance !== 1 || typeof api.acceptLocalWorkspaceUserTurn !== 'function' || typeof local.getLocalWorkspaceContext !== 'function')
|
|
39
|
+
throw failure('EMPLOYEE_CONTEXT_UNAVAILABLE');
|
|
40
|
+
const context = unwrap(await awaitL(() => local.getLocalWorkspaceContext({ instanceId: connection.instanceId, epoch: connection.epoch, workspaceId: workspace.workspaceId, expectedBindingId: item.bindingId }), request.signal));
|
|
41
|
+
if (context.instanceId !== connection.instanceId || context.epoch !== connection.epoch || context.workspaceId !== workspace.workspaceId || context.bindingId !== item.bindingId || context.source !== 'local' || !isDeepStrictEqual(context.directory, workspace.directory))
|
|
42
|
+
throw failure('EMPLOYEE_CONTEXT_CHANGED');
|
|
43
|
+
const proof = await options.readUserTurn(request);
|
|
44
|
+
proof.assertCurrent();
|
|
45
|
+
const accepted = unwrap(await awaitL(() => api.acceptLocalWorkspaceUserTurn({ instanceId: connection.instanceId, epoch: connection.epoch, sessionId: request.sessionId, provider: 'claude', purpose: 'user-turn', workspaceId: workspace.workspaceId, expectedBindingId: item.bindingId, expectedDirectory: { device: workspace.directory.device, inode: workspace.directory.inode }, expectedUserTurn: { ...proof.expectedUserTurn } }), request.signal));
|
|
46
|
+
proof.assertCurrent();
|
|
47
|
+
request.signal?.throwIfAborted();
|
|
48
|
+
const now = api.getConnectionState();
|
|
49
|
+
if (options.getConnect() !== api || now.phase !== 'connected' || now.instanceId !== connection.instanceId || now.epoch !== connection.epoch)
|
|
50
|
+
throw failure('CONNECTION_LOST');
|
|
51
|
+
if (!accepted || accepted.version !== 1 || typeof accepted.acceptanceId !== 'string' || !accepted.acceptanceId.trim() || accepted.instanceId !== connection.instanceId || accepted.epoch !== connection.epoch || accepted.sessionId !== request.sessionId || accepted.provider !== 'claude' || accepted.workspaceId !== workspace.workspaceId || accepted.bindingId !== item.bindingId || accepted.source !== 'local' || !isDeepStrictEqual(accepted.directory, workspace.directory) || accepted.userTurn?.turn !== proof.expectedUserTurn.turn || accepted.userTurn.userMessageId !== proof.expectedUserTurn.userMessageId || accepted.userTurn.startSequence !== proof.startSequence || accepted.userTurn.userMessageSequence !== proof.userMessageSequence)
|
|
52
|
+
throw failure('REMOTE_DETAIL_RESPONSE_INVALID');
|
|
53
|
+
Object.defineProperty(request, 'localAcceptanceId', { value: accepted.acceptanceId, enumerable: true });
|
|
54
|
+
request.detailChannel = await options.details(request, api, connection, target);
|
|
55
|
+
admitted.set(request, { api, workspace: structuredClone(workspace), bindingId: item.bindingId, connection: structuredClone(connection) });
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
const code = error instanceof NeedError ? error.code : 'LOCAL_ADMISSION_FAILED';
|
|
59
|
+
try {
|
|
60
|
+
await options.reportAdmissionFailure(request, code);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
throw new NeedError(code, '本轮输入无法核对,已停止本次处理;诊断保存也未完成。', '原原因 ' + code + ';私有诊断 REMOTE_DETAIL_STORAGE_FAILED');
|
|
64
|
+
}
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const resolve = async (request, api, connection, target) => {
|
|
69
|
+
if (api.getContractInfo?.().workspaceLocations !== 1)
|
|
70
|
+
return;
|
|
71
|
+
const local = api;
|
|
72
|
+
if (typeof local.getWorkspaceLocationState !== 'function')
|
|
73
|
+
throw failure('EMPLOYEE_CONTEXT_UNAVAILABLE');
|
|
74
|
+
const workspace = await options.readWorkspace(request.sessionId), state = local.getWorkspaceLocationState(), matches = state.items.filter(row => row.workspaceId === workspace.workspaceId);
|
|
75
|
+
if (state.phase !== 'ready' || state.instanceId !== connection.instanceId || state.epoch !== connection.epoch || matches.length !== 1)
|
|
76
|
+
throw failure('EMPLOYEE_CONTEXT_CHANGED');
|
|
77
|
+
const item = matches[0];
|
|
78
|
+
if (item.source !== 'local')
|
|
79
|
+
return;
|
|
80
|
+
if (!admitted.has(request))
|
|
81
|
+
await admit(request, api, connection, target);
|
|
82
|
+
const acceptance = admitted.get(request);
|
|
83
|
+
if (!acceptance || acceptance.api !== api || acceptance.bindingId !== item.bindingId || acceptance.connection.instanceId !== connection.instanceId || acceptance.connection.epoch !== connection.epoch || !isDeepStrictEqual(acceptance.workspace, workspace))
|
|
84
|
+
throw failure('EMPLOYEE_CONTEXT_CHANGED');
|
|
85
|
+
if (item.status !== 'selected' || !item.bindingId || local.getContractInfo().localWorkspaceContext !== 1)
|
|
86
|
+
throw failure('EMPLOYEE_CONTEXT_UNAVAILABLE');
|
|
87
|
+
if (request.cwd && request.cwd !== workspace.directory.path || request.bindingId && request.bindingId !== item.bindingId || request.workspaceId && request.workspaceId !== workspace.workspaceId)
|
|
88
|
+
throw failure('SESSION_CONTEXT_MISMATCH');
|
|
89
|
+
const configuration = options.configuration(), maximum = request.acpMaxMessageBytes ?? configuration.acpMaxMessageBytes, control = request.brokerSettings?.brokerControlMaxBytes ?? configuration.brokerControlMaxBytes, grace = configuration.employeeShellKillGraceMs;
|
|
90
|
+
if (![maximum, control, grace].every(n => Number.isSafeInteger(n) && n >= 0) || !maximum || !control)
|
|
91
|
+
throw failure('SETTINGS_REQUIRED');
|
|
92
|
+
const scope = { target, workspace, bindingId: item.bindingId, instanceId: connection.instanceId, epoch: connection.epoch, maximum, control, grace };
|
|
93
|
+
let entry = entries.get(request.sessionId);
|
|
94
|
+
if (entry && (entry.closing || !isDeepStrictEqual(entry.scope, scope) || entry.lease.signal.aborted))
|
|
95
|
+
throw failure('EMPLOYEE_CONTEXT_CHANGED');
|
|
96
|
+
const details = request.detailChannel;
|
|
97
|
+
request.timing?.bind(observation => details.record(observation));
|
|
98
|
+
const trusted = async (ignoreCancellation = false) => {
|
|
99
|
+
if (!ignoreCancellation)
|
|
100
|
+
request.signal?.throwIfAborted();
|
|
101
|
+
if (options.getConnect() !== api)
|
|
102
|
+
throw failure('CONNECTION_LOST');
|
|
103
|
+
const now = api.getConnectionState();
|
|
104
|
+
if (now.phase !== 'connected' || now.instanceId !== connection.instanceId || now.epoch !== connection.epoch || !sameTarget(unwrap(await api.getSshTarget()), target))
|
|
105
|
+
throw failure('CONNECTION_LOST');
|
|
106
|
+
};
|
|
107
|
+
const openRequest = (row, cold = false) => ({ ...request, detailChannel: details, bindingId: item.bindingId, workspaceId: workspace.workspaceId, bindingRevision: state.revision, ...cold ? { recoveryCheckpointId: undefined } : {}, validateContext: async (cleanup = false) => { await trusted(cleanup); if (!cleanup)
|
|
108
|
+
await row.lease.validate(request.signal); } });
|
|
109
|
+
if (entry) {
|
|
110
|
+
entry.details = details;
|
|
111
|
+
entry.request = request;
|
|
112
|
+
await entry.lease.validate(request.signal);
|
|
113
|
+
const base = await options.open(target, { name: workspace.workspaceId, path: target.session.root }, openRequest(entry));
|
|
114
|
+
if (base.acpTransport !== entry.runtime.acpTransport)
|
|
115
|
+
throw failure('SESSION_TRANSPORT_MISMATCH');
|
|
116
|
+
entry.runtime.details = details;
|
|
117
|
+
entry.runtime.restartNotice = base.restartNotice;
|
|
118
|
+
entry.runtime.restartInputIds = base.restartInputIds;
|
|
119
|
+
return entry.runtime;
|
|
120
|
+
}
|
|
121
|
+
let io;
|
|
122
|
+
const transport = async () => { if (!io) {
|
|
123
|
+
const name = '@ai-workbench/connect';
|
|
124
|
+
const l = await import(__rewriteRelativeImportExtension(name));
|
|
125
|
+
if (typeof l.sshArgs !== 'function')
|
|
126
|
+
throw failure('DEPENDENCY_REQUIRED');
|
|
127
|
+
io = sshIo(target, l.sshArgs, control);
|
|
128
|
+
} return io; };
|
|
129
|
+
let lease, mcp, base;
|
|
130
|
+
let privateFailure, privateTail = Promise.resolve();
|
|
131
|
+
try {
|
|
132
|
+
const unscoped = (observation) => { if (privateFailure)
|
|
133
|
+
throw privateFailure; privateTail = privateTail.then(() => options.recordUnscoped(request, observation)).catch(error => { privateFailure ??= error; void lease?.close().catch(failure => { privateFailure ??= failure; }); }); };
|
|
134
|
+
lease = await (options.ports?.lease ?? openEmployeeLease)({ getConnect: () => options.getConnect(), getNetwork: options.getNetwork, readWorkspace: () => options.readWorkspace(request.sessionId), host: target, sessionId: request.sessionId, control: async (_target, command, signal) => (await transport()).run(command, signal), detail: unscoped }, request.signal);
|
|
135
|
+
const shell = (options.ports?.shell ?? createEmployeeShell)({ target: lease.target, maxBytes: maximum, killGraceMs: grace, attach: async (spec) => (await transport()).attach([spec.command, ...spec.args].map(shellQuote).join(' ')) });
|
|
136
|
+
mcp = await (options.ports?.mcp ?? openEmployeeMcp)({ lease, maxBytes: maximum, killGraceMs: grace, runShell: shell, activeSignal: () => entry?.active?.signal, captureDetail: () => { const sink = entry?.active?.details; if (!sink)
|
|
137
|
+
throw failure('EMPLOYEE_NO_ACTIVE_TURN'); return observation => sink.record(observation); }, detail: unscoped });
|
|
138
|
+
entry = { runtime: undefined, scope, lease, details, request };
|
|
139
|
+
const row = entry;
|
|
140
|
+
base = await options.open(target, { name: workspace.workspaceId, path: target.session.root }, openRequest(row, true));
|
|
141
|
+
const underlying = base, endpoint = mcp;
|
|
142
|
+
const close = (detach) => row.closing ??= Promise.resolve().then(async () => {
|
|
143
|
+
row.active = undefined;
|
|
144
|
+
const errors = [];
|
|
145
|
+
try {
|
|
146
|
+
await endpoint.close();
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
errors.push(error);
|
|
150
|
+
}
|
|
151
|
+
await privateTail;
|
|
152
|
+
if (privateFailure)
|
|
153
|
+
errors.push(privateFailure);
|
|
154
|
+
try {
|
|
155
|
+
await (detach ? underlying.detach?.() : underlying.dispose?.());
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
errors.push(error);
|
|
159
|
+
}
|
|
160
|
+
if (errors.length)
|
|
161
|
+
throw new AggregateError(errors, 'EMPLOYEE_CLEANUP_UNCONFIRMED');
|
|
162
|
+
if (entries.get(request.sessionId) === row)
|
|
163
|
+
entries.delete(request.sessionId);
|
|
164
|
+
});
|
|
165
|
+
const executionSignal = AbortSignal.any([lease.signal, ...mcp.signal ? [mcp.signal] : []]);
|
|
166
|
+
row.runtime = { ...underlying, cwd: workspace.directory.path, details, connection: { ...connection }, binding: { bindingId: item.bindingId, workspaceId: workspace.workspaceId, revision: state.revision }, execution: { hostCwd: target.session.root, routing: mcp.routing, notice: NOTICE, signal: executionSignal, scope: { source: 'local', cwd: workspace.directory.path, hostCwd: target.session.root, uid: lease.target.endpoint.remoteUid, claudeAcpVersion: '0.94.0', sdkVersion: '0.3.303', unsupported: ['subagents', 'internal-direct-calls', 'binary-read', 'background-shell'] }, validate: async (signal) => { executionSignal.throwIfAborted(); await row.lease.validate(signal); }, beginTurn: async (signal) => { executionSignal.throwIfAborted(); if (row.closing || row.active)
|
|
167
|
+
throw failure('TURN_BUSY'); await row.lease.validate(signal); executionSignal.throwIfAborted(); row.active = { signal, details: row.details }; }, endTurn: () => { row.active = undefined; } }, dispose: () => close(false), detach: () => close(true) };
|
|
168
|
+
entries.set(request.sessionId, row);
|
|
169
|
+
lease.signal.addEventListener('abort', () => { void close(false).catch(() => { }); }, { once: true });
|
|
170
|
+
if (lease.signal.aborted) {
|
|
171
|
+
await close(false);
|
|
172
|
+
throw failure('EMPLOYEE_CONTEXT_CHANGED');
|
|
173
|
+
}
|
|
174
|
+
return row.runtime;
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
const cleanup = [];
|
|
178
|
+
try {
|
|
179
|
+
await mcp?.close();
|
|
180
|
+
await lease?.close();
|
|
181
|
+
}
|
|
182
|
+
catch (e) {
|
|
183
|
+
cleanup.push(e);
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
await base?.dispose?.();
|
|
187
|
+
}
|
|
188
|
+
catch (e) {
|
|
189
|
+
cleanup.push(e);
|
|
190
|
+
}
|
|
191
|
+
await privateTail;
|
|
192
|
+
if (privateFailure)
|
|
193
|
+
cleanup.push(privateFailure);
|
|
194
|
+
try {
|
|
195
|
+
await details.flush();
|
|
196
|
+
}
|
|
197
|
+
catch (e) {
|
|
198
|
+
cleanup.push(e);
|
|
199
|
+
}
|
|
200
|
+
if (cleanup.length)
|
|
201
|
+
throw new AggregateError([error, ...cleanup], 'EMPLOYEE_OPEN_CLEANUP_UNCONFIRMED');
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
resolve.admit = admit;
|
|
206
|
+
return resolve;
|
|
207
|
+
}
|
package/dist/client.js
CHANGED
|
@@ -1,7 +1,76 @@
|
|
|
1
1
|
/* Official locale and provider-registration updates only; no custom UI or refresh. */
|
|
2
|
-
window.__ModuleLoader__.load({id:'@ai-workbench/remote-agent',factory(){
|
|
3
|
-
const messages={"en":{"administratorNote":"
|
|
2
|
+
window.__ModuleLoader__.load({id:'@ai-workbench/remote-agent',factory(require){
|
|
3
|
+
const messages={"en":{"administratorNote":"Settings → Built-in plugins → 远程助手参数. Seven named settings with defaults, scope and effects.","providerName":"Codex (Remote Host)"},"zh":{"administratorNote":"管理员:设置 → 内置插件 → 远程助手参数。七项设置含默认值、范围、生效和后果说明。","providerName":"Codex(远程主机)"}};
|
|
4
|
+
const settingsFields=[{"key":"mode","name":"Codex 运行方式","kind":"select","options":[{"value":"session","name":"会话模式"},{"value":"one-shot","name":"单次模式(当前版本不可运行)"}],"range":"session / one-shot","scope":"仅 Codex;Claude 始终使用会话模式。","when":"当前轮不变;更改后原会话下一轮会拒绝继续,需新建会话。","consequence":"会话模式保留连续对话;当前单次模式会明确拒绝运行,不会发给模型。不提供关闭会话身份核对的选项。","defaultValue":"session"},{"key":"acpMaxMessageBytes","name":"完整消息与工具输出容量(字节)","kind":"number","min":1,"max":9007199254740991,"range":"1~9007199254740991 的整数;0不能关闭上限","scope":"两位远程助手的完整会话消息;也用于模型目录、本机 MCP、员工执行 RPC 和工具输出容量。","when":"当轮快照不变;下一次独立目录查询读取新值;已有会话需新建后使用。","consequence":"调大可接收更大消息、占用更多内存;调小更早拒绝超限。超限明确停止,不截断、不自动重试;不能用0取消完整消息保护。","defaultValue":4194304},{"key":"brokerMaxFrameBytes","name":"主机会话回传单段容量(字节)","kind":"number","min":1,"max":9007199254740991,"range":"1~9007199254740991 的整数;0不能关闭上限","scope":"本插件接收主机会话单段输出的容量;不会替主机修改它自己的输出策略。","when":"当轮快照不变;更改后已有会话需新建。","consequence":"调大允许更大回传段、占用更多内存;调小可能拒绝主机合法输出。超限停止,不截断;不能关闭协议帧核验。","defaultValue":2097152},{"key":"brokerControlMaxBytes","name":"主机会话管理回复容量(字节)","kind":"number","min":1,"max":9007199254740991,"range":"1~9007199254740991 的整数;0不能关闭上限","scope":"主机会话管理命令的标准输出、标准错误各自容量;也用于本机执行桥的管理回复。","when":"当轮快照不变;更改后已有会话需新建。","consequence":"调大允许更长管理回复、占用更多内存;调小可能无法核实状态。超限明确停止,不能把不完整回复当成功;0不表示无限。","defaultValue":1048576},{"key":"oneShotSandbox","name":"Codex 单次任务权限模式","kind":"select","options":[{"value":"read-only","name":"只读"},{"value":"workspace-write","name":"工作区内可写"},{"value":"danger-full-access","name":"完全访问(高权限)"}],"range":"read-only / workspace-write / danger-full-access","scope":"仅 Codex 单次通道;不改变会话模式、本机工具或员工账号的系统权限。","when":"仅单次任务启动时适用;当前版本单次任务被拒绝,因此目前不生效。","consequence":"只读不允许单次任务写入;工作区内可写允许该目录写入;完全访问放宽单次沙箱。此项不是员工目录的权限开关,也不会解锁当前不可用的单次模式。","defaultValue":"workspace-write"},{"key":"oneShotApproval","name":"Codex 单次任务审批方式","kind":"fixed","readOnly":true,"range":"固定 never;不能切换","scope":"仅 Codex 单次通道;不改变会话模式的员工审批。","when":"仅单次启动参数;当前单次模式不可运行。","consequence":"单次通道没有中途交互审批能力,所以必须固定为无中途交互;不表示自动批准或允许所有操作。不能切换为自动回答旧审批。","defaultValue":"never"},{"key":"employeeShellKillGraceMs","name":"本机命令外层连接清理宽限(毫秒)","kind":"number","min":0,"max":2147483647,"range":"0~2147483647 的整数","scope":"仅 Claude 本机 Bash 取消后,本插件自己的外层 SSH 进程清理;不控制员工命令或 Codex 执行器。","when":"当轮快照不变;更改后已有会话需新建。","consequence":"大于0时先请求外层连接退出,再等待所设毫秒后强制清理;0表示不留宽限、立即强制清理外层连接,不是关闭取消。没有员工端退出确认时结果仍未知,不能据此自动重放或声称命令已停止。","defaultValue":1000}];
|
|
5
|
+
/* Public settings.plugins.tab + shared ConfigForms. No private client services. */
|
|
6
|
+
function mountRuntimeSettings(ctx,React,fields){
|
|
7
|
+
const h=React.createElement;
|
|
8
|
+
const invariants=[
|
|
9
|
+
['员工、主机和文件夹核对','始终开启,不可关闭','每次派发前核对当前连接、员工与工作区,避免操作到别人的文件或旧位置;核对失败即停止。'],
|
|
10
|
+
['本轮输入与技术详情归属','始终开启,不可关闭','真实输入须先接纳,技术详情须确认保存;迟到结果仍属于原轮次,不能借下一轮的记录继续执行。'],
|
|
11
|
+
['模型选择确认','始终开启,不可关闭','主机须完整确认所选模型和推理等级,不能静默换模型继续。'],
|
|
12
|
+
['完整消息与精确整数','始终开启,不可关闭','容量不能用0关闭,超限明确停止、不截断;整数必须能精确表示,防止消息缺失或容量计算失真。'],
|
|
13
|
+
['未知结果与旧审批保护','始终开启,不可关闭','断线或取消不等于员工命令已结束;不自动重放未知操作,不代答旧审批,清理未确认时不擅自重开。'],
|
|
14
|
+
['本机执行位置','始终开启,不可关闭','已提供的本机工具通道不悄悄回退主机;文件夹不是权限隔离根,也不新增目录白名单。Claude子代理及内部直接调用尚未完整覆盖,可能仍在主机,每轮另有范围提示。']
|
|
15
|
+
];
|
|
16
|
+
function Panel({form}){
|
|
17
|
+
const snapshot=React.useSyncExternalStore(fn=>form.subscribe(fn),()=>form.getSnapshot(),()=>form.getSnapshot());
|
|
18
|
+
const [draft,setDraft]=React.useState({}),[busy,setBusy]=React.useState(false),[notice,setNotice]=React.useState('');
|
|
19
|
+
const revision=React.useRef(undefined),saving=React.useRef(false),alive=React.useRef(true),prefix=React.useId();
|
|
20
|
+
React.useEffect(()=>{alive.current=true;return()=>{alive.current=false;};},[]);
|
|
21
|
+
const ready=snapshot.status==='ready',writable=ready&&snapshot.writable===true&&snapshot.mode==='host'&&snapshot.revision!==undefined;
|
|
22
|
+
const changed=Object.keys(draft).length>0,stale=changed&&revision.current!==snapshot.revision;
|
|
23
|
+
const edit=(key,value)=>{if(saving.current||!writable)return;if(!changed)revision.current=snapshot.revision;setDraft(previous=>({...previous,[key]:value}));setNotice('修改尚未保存。');};
|
|
24
|
+
const discard=()=>{if(saving.current)return;setDraft({});revision.current=undefined;setNotice('已放弃未保存修改,显示当前配置。');};
|
|
25
|
+
const save=async()=>{
|
|
26
|
+
if(saving.current||!writable||!changed||stale)return;
|
|
27
|
+
const ops=[];
|
|
28
|
+
for(const field of fields){const entry=draft[field.key];if(!entry)continue;
|
|
29
|
+
if(field.readOnly){setNotice('固定约束不能修改。');return;}
|
|
30
|
+
if(entry.op==='unset'){ops.push({op:'unset',path:[field.key]});continue;}
|
|
31
|
+
const raw=entry.value,value=field.kind==='number'?Number(raw):raw;
|
|
32
|
+
if(field.kind==='number'&&(String(raw).trim()===''||!Number.isSafeInteger(value)||value<field.min||value>field.max)||field.kind==='select'&&!field.options.some(option=>option.value===value)){setNotice(field.name+'的值不在所列范围内,未保存。');return;}
|
|
33
|
+
ops.push({op:'set',path:[field.key],value});
|
|
34
|
+
}
|
|
35
|
+
saving.current=true;setBusy(true);setNotice('正在保存,请稍候。');
|
|
36
|
+
try{
|
|
37
|
+
const accepted=await form.mutate(ops,revision.current);
|
|
38
|
+
if(!alive.current)return;
|
|
39
|
+
if(accepted===true){setDraft({});revision.current=undefined;setNotice('已保存。运行中的这一轮不变;请按各项生效说明使用新会话。');}
|
|
40
|
+
else setNotice('未保存:配置已变化或官方设置服务拒绝了修改。草稿已保留;核对当前值后可放弃草稿重新修改,不会自动重试。');
|
|
41
|
+
}catch{if(alive.current)setNotice('保存失败,草稿已保留。请检查连接及官方配置状态;不会自动重试。');}
|
|
42
|
+
finally{saving.current=false;if(alive.current)setBusy(false);}
|
|
43
|
+
};
|
|
44
|
+
const stateMessage=snapshot.mode!=='host'?'当前连接不提供持久配置写入,本页不能保存。':!ready?'尚未取得官方配置,当前不能修改。':!snapshot.writable?'当前配置由更高层覆盖或不可写,本页不能修改。':stale?'其他地方已更新配置;本页草稿保留。请放弃草稿后按最新值重新修改。':'';
|
|
45
|
+
return h('section',{'aria-label':'远程助手参数',style:{maxWidth:850}},
|
|
46
|
+
h('h2',null,'远程助手参数'),
|
|
47
|
+
h('p',null,'这些参数用于本客户端当前配置下的 Claude(远程主机)和 Codex(远程主机),不改变其他模型或主机账号权限。没有总开关;关闭整个插件请使用官方插件管理。'),
|
|
48
|
+
stateMessage&&h('p',{role:'status'},stateMessage),
|
|
49
|
+
h('form',{onSubmit:event=>{event.preventDefault();void save();}},
|
|
50
|
+
...fields.map(field=>{
|
|
51
|
+
const entry=draft[field.key],accepted=ready?snapshot.value?.[field.key]:undefined;
|
|
52
|
+
const base=snapshot.base,hasBase=base!==null&&typeof base==='object'&&Object.hasOwn(base,field.key)&&base[field.key]!==undefined;
|
|
53
|
+
const value=entry?.op==='set'?entry.value:entry?.op==='unset'&&hasBase?base[field.key]:accepted;
|
|
54
|
+
const id=prefix+'-'+field.key,description=id+'-description';
|
|
55
|
+
const control=field.readOnly?h('p',{'aria-label':field.name+'当前值'},String(value??'尚未读取')+'(固定,不可切换)'):field.kind==='select'?h('select',{id,value:value??'',disabled:!writable||busy,'aria-describedby':description,onChange:event=>edit(field.key,{op:'set',value:event.target.value})},value===undefined&&h('option',{value:'',disabled:true},'尚未读取'),...field.options.map(option=>h('option',{key:option.value,value:option.value},option.name))):h('input',{id,type:'number',step:1,min:field.min,max:field.max,value:value??'',disabled:!writable||busy,'aria-describedby':description,onChange:event=>edit(field.key,{op:'set',value:event.target.value})});
|
|
56
|
+
return h('fieldset',{key:field.key,style:{marginBottom:18,padding:14,border:'1px solid currentColor',borderRadius:6}},
|
|
57
|
+
h('legend',null,field.name),h('label',{htmlFor:field.readOnly?undefined:id},field.name+'('+field.key+')'),
|
|
58
|
+
h('div',null,control),h('div',{id:description},
|
|
59
|
+
h('p',null,'默认值:'+field.defaultValue+';范围:'+field.range),
|
|
60
|
+
h('p',null,'生效范围:'+field.scope),h('p',null,'生效时间:'+field.when),h('p',null,'调整或关闭的后果:'+field.consequence)),
|
|
61
|
+
entry?.op==='unset'&&h('p',null,hasBase?'将移除本项覆盖,恢复所示继承值;尚未保存。':'将移除本项覆盖;继承层未设此项,保存后由官方配置确定(默认值:'+field.defaultValue+')。输入框仍显示保存前值,尚未保存。'),
|
|
62
|
+
!field.readOnly&&h('button',{type:'button',disabled:!writable||busy,onClick:()=>edit(field.key,{op:'unset'})},'恢复继承值:'+field.name));
|
|
63
|
+
}),
|
|
64
|
+
h('div',null,h('button',{type:'submit',disabled:!writable||!changed||busy||stale},busy?'正在保存…':'保存修改'),h('button',{type:'button',disabled:!changed||busy,onClick:discard},'放弃未保存修改'))),
|
|
65
|
+
h('p',{role:'status','aria-live':'polite'},notice),h('h3',null,'必须保持的正确性约束'),
|
|
66
|
+
h('p',null,'以下保护默认开启且不能关闭。关闭会使输入、审批、执行位置或记录失去可信归属,并非可以放宽的偏好。'),
|
|
67
|
+
h('dl',null,...invariants.flatMap(([name,state,why])=>[h('dt',{key:name},name+':'+state),h('dd',{key:name+'-why'},why)])));
|
|
68
|
+
}
|
|
69
|
+
ctx.effect(()=>ctx.configForms.whileServed(['ai-workbench-codex'],()=>ctx.slots.inject('settings.plugins.tab',()=>ctx.slots.register({name:'settings.plugins.tab',id:'ai-workbench-remote-parameters',order:90,label:()=> '远程助手参数',inject:()=>({form:ctx.configForms.get('ai-workbench-codex')})},Panel))));
|
|
70
|
+
}
|
|
71
|
+
|
|
4
72
|
return{inject:['locale','connection'],apply(ctx){
|
|
73
|
+
ctx.inject(['slots','configForms'],settings=>mountRuntimeSettings(settings,require('react'),settingsFields));
|
|
5
74
|
ctx.effect(()=>ctx.locale.register('ai-workbench-codex',messages),'Codex official translations');
|
|
6
75
|
ctx.effect(()=>{
|
|
7
76
|
let disposed=false,connected=false,previous,generation=0,request=new AbortController(),queue=Promise.resolve();
|
|
@@ -31,7 +31,7 @@ export type DetailSink = {
|
|
|
31
31
|
};
|
|
32
32
|
/** Only objects created by this module are trusted as redaction markers. */
|
|
33
33
|
export declare function isSanitizedRedaction(value: unknown): boolean;
|
|
34
|
-
|
|
34
|
+
export declare function sanitizeDisplayLabel(value: string): string;
|
|
35
35
|
export declare function sanitizeObservation(observation: DetailObservation, protectModelMaterial?: boolean): Readonly<SanitizedObservation>;
|
|
36
36
|
/** Internal diagnostic recorder; a sink is not an assertion that an L wire API exists. */
|
|
37
37
|
export declare class ConversationDetails {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { STAGES } from "./stage-timing.js";
|
|
2
3
|
const redactionMarkers = new WeakSet();
|
|
3
4
|
function redaction(fields) {
|
|
4
5
|
const marker = { redacted: true, ...fields };
|
|
@@ -92,7 +93,7 @@ function safeText(value) {
|
|
|
92
93
|
const url = cleanUrls(value);
|
|
93
94
|
return url.changed ? redaction({ reason: 'URL敏感部分', value: url.value }) : value;
|
|
94
95
|
}
|
|
95
|
-
function
|
|
96
|
+
export function sanitizeDisplayLabel(value) {
|
|
96
97
|
if (typeof value !== 'string')
|
|
97
98
|
throw new TypeError('Detail observation labels must be strings');
|
|
98
99
|
// Inspect credentials before removing control characters, so a bearer newline cannot evade detection.
|
|
@@ -254,19 +255,98 @@ function usageStructure(value, key = '') {
|
|
|
254
255
|
return value;
|
|
255
256
|
return opaqueStructure(value);
|
|
256
257
|
}
|
|
258
|
+
/** Only fixed local stage names and finite nonnegative millisecond observations. */
|
|
259
|
+
function timingStructure(value) {
|
|
260
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
261
|
+
return opaqueStructure(value);
|
|
262
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => {
|
|
263
|
+
if (key === 'phase' && typeof child === 'string' && Object.hasOwn(STAGES, child))
|
|
264
|
+
return [key, child];
|
|
265
|
+
if (key === 'clock' && child === 'client-monotonic' || key === 'outcome' && (child === 'ok' || child === 'failed'))
|
|
266
|
+
return [key, child];
|
|
267
|
+
if (['durationMs', 'elapsedMs'].includes(key) && typeof child === 'number' && Number.isFinite(child) && child >= 0)
|
|
268
|
+
return [key, child];
|
|
269
|
+
return [key, opaqueStructure(child)];
|
|
270
|
+
}));
|
|
271
|
+
}
|
|
272
|
+
/** Only our two model-configuration observations approve these exact diagnostic
|
|
273
|
+
* paths. Directory source, config metadata and arbitrary subtrees stay opaque. */
|
|
274
|
+
function modelConfigurationStructure(value, level = 'root') {
|
|
275
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
276
|
+
return opaqueStructure(value);
|
|
277
|
+
if (isSanitizedRedaction(value))
|
|
278
|
+
return value;
|
|
279
|
+
const strings = {
|
|
280
|
+
root: ['resolvedModel', 'requestedEffort', 'reason'], selected: ['id', 'value', 'name', 'resolvedModel', 'effort'],
|
|
281
|
+
actual: ['model', 'effort', 'modelConfigId', 'effortConfigId'], reasoning: ['defaultValue'], effort: ['value', 'name'],
|
|
282
|
+
};
|
|
283
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => {
|
|
284
|
+
if (typeof child === 'string' && strings[level].includes(key))
|
|
285
|
+
return [key, child];
|
|
286
|
+
if (level === 'root' && key === 'status' && child === 'remote-configured')
|
|
287
|
+
return [key, child];
|
|
288
|
+
if (level === 'actual' && typeof child === 'boolean' && ['confirmed', 'effortAvailable'].includes(key))
|
|
289
|
+
return [key, child];
|
|
290
|
+
if (level === 'reasoning' && key === 'defaultKnown' && typeof child === 'boolean')
|
|
291
|
+
return [key, child];
|
|
292
|
+
if (child === null && ((level === 'root' && ['actual', 'requestedEffort'].includes(key)) || (level === 'selected' && key === 'resolvedModel') || (level === 'reasoning' && key === 'defaultValue')))
|
|
293
|
+
return [key, null];
|
|
294
|
+
if (level === 'root' && key === 'selected')
|
|
295
|
+
return [key, modelConfigurationStructure(child, 'selected')];
|
|
296
|
+
if (level === 'root' && ['configured', 'actual'].includes(key))
|
|
297
|
+
return [key, modelConfigurationStructure(child, 'actual')];
|
|
298
|
+
if (level === 'selected' && key === 'reasoning')
|
|
299
|
+
return [key, modelConfigurationStructure(child, 'reasoning')];
|
|
300
|
+
if (level === 'reasoning' && key === 'options' && Array.isArray(child))
|
|
301
|
+
return [key, child.map(option => modelConfigurationStructure(option, 'effort'))];
|
|
302
|
+
return [key, opaqueStructure(child)];
|
|
303
|
+
}));
|
|
304
|
+
}
|
|
257
305
|
/** Sanitize an internal observation without attaching any L wire semantics. */
|
|
306
|
+
function contextTransitionStructure(value, history = false) {
|
|
307
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value) || isSanitizedRedaction(value))
|
|
308
|
+
return opaqueStructure(value);
|
|
309
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => {
|
|
310
|
+
if (!history && key === 'history')
|
|
311
|
+
return [key, contextTransitionStructure(child, true)];
|
|
312
|
+
if (!history && typeof child === 'string' && (['oldSessionId', 'newSessionId'].includes(key) || (['oldContextSha256', 'newContextSha256'].includes(key) && /^[a-fA-F0-9]{64}$/u.test(child)) || (key === 'reason' && child === 'system-context-changed')))
|
|
313
|
+
return [key, child];
|
|
314
|
+
if (history && ((key === 'scope' && child === 'visible-user-assistant-text-and-images') || (key === 'reasoningOmissionReason' && child === 'dsh-internal-reasoning') || (['textBlocks', 'imageBlocks', 'offloadedImagePlaceholders', 'omittedReasoningBlocks'].includes(key) && typeof child === 'number' && Number.isSafeInteger(child) && child >= 0)))
|
|
315
|
+
return [key, child];
|
|
316
|
+
return [key, opaqueStructure(child)];
|
|
317
|
+
}));
|
|
318
|
+
}
|
|
319
|
+
function employeeStructure(value) {
|
|
320
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
321
|
+
return opaqueStructure(value);
|
|
322
|
+
const strings = new Set(['executionId', 'workspaceId', 'bindingId', 'instanceId', 'deviceId', 'endpointInstanceId', 'executionContextId', 'bridgeInstanceId', 'cwd', 'hostCwd', 'remoteUser', 'operation', 'status', 'reason', 'source', 'claudeAcpVersion', 'sdkVersion']);
|
|
323
|
+
const numbers = new Set(['uid', 'pid', 'childPid', 'generation', 'epoch', 'device', 'inode', 'killGraceMs', 'maxBytes']);
|
|
324
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => {
|
|
325
|
+
if (strings.has(key) && typeof child === 'string')
|
|
326
|
+
return [key, child];
|
|
327
|
+
if (numbers.has(key) && typeof child === 'number' && Number.isSafeInteger(child) && child >= 0)
|
|
328
|
+
return [key, child];
|
|
329
|
+
if (['supported', 'unsupported'].includes(key) && Array.isArray(child))
|
|
330
|
+
return [key, child.map(x => typeof x === 'string' ? x : opaqueStructure(x))];
|
|
331
|
+
return [key, opaqueStructure(child)];
|
|
332
|
+
}));
|
|
333
|
+
}
|
|
258
334
|
export function sanitizeObservation(observation, protectModelMaterial = false) {
|
|
259
335
|
const payload = snapshotPayload(observation.payload, new Set(), protectModelMaterial && observation.kind !== 'commands');
|
|
260
336
|
return freezeDeep({
|
|
261
337
|
kind: observation.kind, severity: observation.severity,
|
|
262
|
-
code:
|
|
263
|
-
...(observation.method === undefined ? {} : { method:
|
|
338
|
+
code: sanitizeDisplayLabel(observation.code), summary: sanitizeDisplayLabel(observation.summary),
|
|
339
|
+
...(observation.method === undefined ? {} : { method: sanitizeDisplayLabel(observation.method) }),
|
|
264
340
|
payload: !protectModelMaterial ? payload
|
|
265
|
-
: observation.kind === '
|
|
266
|
-
: observation.kind === '
|
|
267
|
-
: observation.kind === '
|
|
268
|
-
: observation.kind === '
|
|
269
|
-
:
|
|
341
|
+
: observation.kind === 'session' && observation.code === 'CODEX_CONTEXT_SESSION_REPLACED' ? contextTransitionStructure(payload)
|
|
342
|
+
: observation.kind === 'session' && ['MODEL_CONFIGURATION_CONFIRMED', 'MODEL_CONFIGURATION_FAILED'].includes(observation.code) ? modelConfigurationStructure(payload)
|
|
343
|
+
: observation.kind === 'commands' ? catalogStructure(payload)
|
|
344
|
+
: observation.kind === 'usage' ? usageStructure(payload)
|
|
345
|
+
: observation.kind === 'session' && observation.code === 'REMOTE_STAGE_TIMING' ? timingStructure(payload)
|
|
346
|
+
: observation.kind === 'session' && ['CLAUDE_EMPLOYEE_OPERATION', 'CLAUDE_EMPLOYEE_PROCESS', 'CLAUDE_EMPLOYEE_REVOKED', 'CLAUDE_EMPLOYEE_SCOPE'].includes(observation.code) ? employeeStructure(payload)
|
|
347
|
+
: observation.kind === 'need' ? payload
|
|
348
|
+
: observation.kind === 'tool' || ['SDK_ITEM', 'SDK_TOOL', 'ACP_SESSION_UPDATE', 'ACP_IDLE_UPDATE'].includes(observation.code) ? toolStructure(payload)
|
|
349
|
+
: opaqueStructure(payload),
|
|
270
350
|
});
|
|
271
351
|
}
|
|
272
352
|
function validatedScope(scope) {
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { ConnectApi as LApi, LocalWorkspaceContextRequest } from '@ai-workbench/connect/api';
|
|
2
|
+
import type { ConnectTarget } from './provider.ts';
|
|
3
|
+
import type { DetailObservation } from './conversation-details.ts';
|
|
4
|
+
/** Exact consumer subset of N0.4.4 employee-endpoint v1; no new provider fields. */
|
|
5
|
+
export interface EmployeeEndpointTarget {
|
|
6
|
+
version: 1;
|
|
7
|
+
local: {
|
|
8
|
+
instanceId: string;
|
|
9
|
+
epoch: number;
|
|
10
|
+
workspaceId: string;
|
|
11
|
+
bindingId: string;
|
|
12
|
+
directory: {
|
|
13
|
+
path: string;
|
|
14
|
+
device: number;
|
|
15
|
+
inode: number;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
endpoint: {
|
|
19
|
+
deviceId: string;
|
|
20
|
+
endpointInstanceId: string;
|
|
21
|
+
executionContextId: string;
|
|
22
|
+
remoteUser: string;
|
|
23
|
+
remoteUid: number;
|
|
24
|
+
remoteHome: string;
|
|
25
|
+
endpointCwd: string;
|
|
26
|
+
endpointShell: string;
|
|
27
|
+
};
|
|
28
|
+
bridge: {
|
|
29
|
+
protocolVersion: 1;
|
|
30
|
+
command: '/usr/local/aw-host/bin/aw-employee-bridge';
|
|
31
|
+
settingsPath: string;
|
|
32
|
+
bridgeInstanceId: string;
|
|
33
|
+
generation: number;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export interface EmployeeNetworkApi {
|
|
37
|
+
prepareEmployeeEndpoint(request: LocalWorkspaceContextRequest, signal: AbortSignal): Promise<{
|
|
38
|
+
ok: true;
|
|
39
|
+
value: EmployeeEndpointTarget;
|
|
40
|
+
} | {
|
|
41
|
+
ok: false;
|
|
42
|
+
error: {
|
|
43
|
+
code: string;
|
|
44
|
+
needs: string;
|
|
45
|
+
message: string;
|
|
46
|
+
actor: string;
|
|
47
|
+
};
|
|
48
|
+
}>;
|
|
49
|
+
getState(): {
|
|
50
|
+
phase: 'starting' | 'online' | 'blocked' | 'stopped';
|
|
51
|
+
};
|
|
52
|
+
onState(listener: (event: {
|
|
53
|
+
kind: 'lost' | 'restored';
|
|
54
|
+
state: {
|
|
55
|
+
phase: 'starting' | 'online' | 'blocked' | 'stopped';
|
|
56
|
+
};
|
|
57
|
+
}) => void): () => void;
|
|
58
|
+
}
|
|
59
|
+
export type EmployeeConnectApi = Pick<LApi, 'getContractInfo' | 'getConnectionState' | 'onConnectionState' | 'getSshTarget' | 'getLocalWorkspaceContext' | 'getWorkspaceLocationState' | 'onWorkspaceLocationState'>;
|
|
60
|
+
export interface EmployeeWorkspace {
|
|
61
|
+
workspaceId: string;
|
|
62
|
+
directory: {
|
|
63
|
+
path: string;
|
|
64
|
+
device: number;
|
|
65
|
+
inode: number;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export interface EmployeeLeaseOptions {
|
|
69
|
+
getConnect: () => EmployeeConnectApi | undefined;
|
|
70
|
+
getNetwork: () => EmployeeNetworkApi | undefined;
|
|
71
|
+
readWorkspace: () => Promise<EmployeeWorkspace>;
|
|
72
|
+
host: ConnectTarget;
|
|
73
|
+
sessionId: string;
|
|
74
|
+
control: (host: ConnectTarget, command: string, signal?: AbortSignal) => Promise<string>;
|
|
75
|
+
detail: (detail: DetailObservation) => void;
|
|
76
|
+
}
|
|
77
|
+
export declare function endpointArguments(target: EmployeeEndpointTarget): string[];
|
|
78
|
+
export declare function endpointCommand(target: EmployeeEndpointTarget): string;
|
|
79
|
+
export declare function verifyEmployeeTarget(target: EmployeeEndpointTarget, request: LocalWorkspaceContextRequest, directory: EmployeeWorkspace['directory'], host: ConnectTarget): void;
|
|
80
|
+
export declare function verifyBridgeTarget(value: unknown, expected: EmployeeEndpointTarget): {
|
|
81
|
+
sshArgs: string[];
|
|
82
|
+
};
|
|
83
|
+
/** Holds only this workspace's dispatch fence. N continues to own the shared device service. */
|
|
84
|
+
export interface EmployeeLease {
|
|
85
|
+
target: EmployeeEndpointTarget;
|
|
86
|
+
request: Readonly<LocalWorkspaceContextRequest>;
|
|
87
|
+
signal: AbortSignal;
|
|
88
|
+
validate(callerSignal?: AbortSignal): Promise<EmployeeEndpointTarget>;
|
|
89
|
+
close(): Promise<void>;
|
|
90
|
+
}
|
|
91
|
+
export declare function openEmployeeLease(options: EmployeeLeaseOptions, initialSignal?: AbortSignal): Promise<EmployeeLease>;
|