@ai-workbench/remote-agent 0.8.18 → 0.8.21

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.
@@ -81,7 +81,7 @@ export function employeeFailure(code, brand = 'codex') {
81
81
  if (code === 'AW_EMPLOYEE_HOST_PROJECT_CONFIG_UNSUPPORTED')
82
82
  return '主机上存在与本机工作区路径重合的项目配置,当前版本无法安全应用,本轮没有执行,请联系管理员。';
83
83
  if (code === 'AW_EMPLOYEE_EXECUTOR_MCP_UNSUPPORTED')
84
- return '本机工作区配置了附加MCP工具,当前版本尚不支持这类工具,本轮没有继续执行。';
84
+ return '本机 HTTP MCP 当前配置策略为拒绝,本轮没有继续执行;管理员可在远程助手参数中调整本机 HTTP MCP 策略,新建会话后生效。';
85
85
  if (['AW_EMPLOYEE_CONFIG_ADMISSION_INVALID', 'AW_EMPLOYEE_CONFIG_ADMISSION_FAILED', 'AW_EMPLOYEE_EXECUTOR_CONFIG_PROTOCOL_INVALID', 'AW_EMPLOYEE_EXECUTOR_CONFIG_READ_FAILED'].includes(code))
86
86
  return '无法核验这次本机工作区的助手配置,本轮没有继续执行,请联系管理员查看技术详情。';
87
87
  if (code === 'AW_EMPLOYEE_MCP_UNSUPPORTED')
@@ -16,6 +16,7 @@ import type { EmployeeProviderOptions } from './employee-provider.ts';
16
16
  import { type WorkspaceSourceOptions, type SourceAdmissionStage } from './workspace-source.ts';
17
17
  import type { LocalUserTurnAcceptance } from '@ai-workbench/connect/api';
18
18
  import type { EmployeeRouting } from './employee-relay.ts';
19
+ import type { HostWorkspacePresentation } from './input.ts';
19
20
  export interface EmployeeExecution {
20
21
  hostCwd: string;
21
22
  routing: EmployeeRouting;
@@ -29,6 +30,7 @@ export interface EmployeeExecution {
29
30
  export interface Runtime {
30
31
  identity: string;
31
32
  cwd: string;
33
+ hostWorkspace?: Readonly<HostWorkspacePresentation>;
32
34
  contextSignal?: AbortSignal;
33
35
  execution?: EmployeeExecution;
34
36
  acpTransport?: {
@@ -63,9 +65,11 @@ export interface Runtime {
63
65
  workspaceId: string;
64
66
  cwd: string;
65
67
  acpMaxMessageBytes: number;
68
+ httpMcpPolicy?: 'reject' | 'ignore';
66
69
  };
67
70
  employeeExecution?: (params: unknown, signal?: AbortSignal) => Promise<Record<string, unknown>>;
68
71
  employeePresentation?: string;
72
+ employeeRemoteCloseReason?: () => string | undefined;
69
73
  closeDiagnostics?: CloseDiagnostics;
70
74
  }
71
75
  export interface RuntimeRequest extends Invocation {
@@ -83,6 +87,7 @@ export interface RuntimeRequest extends Invocation {
83
87
  detailChannel?: DetailChannel;
84
88
  validateContext?: (ignoreCancellation?: boolean) => Promise<void>;
85
89
  verifyRecovery?: () => Promise<boolean>;
90
+ employeeHttpMcpPolicy?: 'reject' | 'ignore';
86
91
  acpMaxMessageBytes?: number;
87
92
  employeeScope?: string;
88
93
  contextSha256?: string;
package/dist/provider.js CHANGED
@@ -305,6 +305,8 @@ export function connectProvider(get, options = {}) {
305
305
  request.timing.bind(detail => details.record(detail));
306
306
  await stable();
307
307
  host?.assertCurrent();
308
+ if (host)
309
+ details?.record({ kind: 'session', severity: 'info', code: 'HOST_EXECUTION_VALIDATION_POLICY', summary: '主机目录核验不可关闭:每轮接纳时由L核验主机身份、登录与物理目录;之后消息、审批回复与检查点只核对内存中的连接、登录与位置,靠已订阅的L连接和位置事件撤销,不重复SSH。' });
308
310
  let workspaceNotice = undefined;
309
311
  const notice = !host && 'notice' in resolved ? resolved.notice : undefined;
310
312
  if (notice) {
@@ -319,8 +321,6 @@ export function connectProvider(get, options = {}) {
319
321
  const validateContext = async (ignoreCancellation = false) => {
320
322
  const check = () => waitForLogin(api, agent.loginProvider, connection, get, ignoreCancellation ? undefined : request.signal);
321
323
  await check();
322
- if (host && !ignoreCancellation)
323
- await host.resolve();
324
324
  if (observedBinding) {
325
325
  const current = api.getWorkspaceBindingState();
326
326
  if (current.instanceId !== observedBinding.instanceId || !current.items.some(item => item.bindingId === resolved.bindingId && item.workspaceId === resolved.workspaceId && item.status === 'ready'))
@@ -342,6 +342,7 @@ export function connectProvider(get, options = {}) {
342
342
  }
343
343
  if (host)
344
344
  retainHostScope(runtime, host);
345
+ runtime.hostWorkspace = host && 'directory' in resolved ? Object.freeze({ cwd: resolved.directory.path }) : undefined;
345
346
  // The transport can outlive a turn; presentation belongs only to this invocation.
346
347
  runtime.workspaceNotice = workspaceNotice || undefined;
347
348
  runtime.details = details ?? runtime.details;
@@ -22,6 +22,10 @@ export declare class StageTiming {
22
22
  private readonly started;
23
23
  private pending;
24
24
  private sink?;
25
+ private confirmations;
26
+ private emit;
27
+ /** Presentation aggregation only: no duration threshold and no new execution gate. */
28
+ summarize(): void;
25
29
  bind(sink: (detail: DetailObservation) => void): void;
26
30
  run<T>(phase: Stage, operation: () => Promise<T>): Promise<T>;
27
31
  }
@@ -11,6 +11,19 @@ export class StageTiming {
11
11
  started = performance.now();
12
12
  pending = [];
13
13
  sink;
14
+ confirmations = { count: 0, total: 0, min: Infinity, max: 0, elapsed: 0 };
15
+ emit(detail) { if (this.sink)
16
+ this.sink(detail);
17
+ else
18
+ this.pending.push(detail); }
19
+ /** Presentation aggregation only: no duration threshold and no new execution gate. */
20
+ summarize() {
21
+ const current = this.confirmations;
22
+ if (!current.count)
23
+ return;
24
+ this.confirmations = { count: 0, total: 0, min: Infinity, max: 0, elapsed: 0 };
25
+ this.emit({ kind: 'session', severity: 'info', code: 'REMOTE_STAGE_TIMING', summary: STAGES['connection-confirm'] + ':' + current.count + '次,共' + current.total.toFixed(1) + '毫秒,最大' + current.max.toFixed(1) + '毫秒', payload: { phase: 'connection-confirm', count: current.count, totalDurationMs: current.total, minDurationMs: current.min, maxDurationMs: current.max, elapsedMs: current.elapsed, outcome: 'ok', clock: 'client-monotonic' } });
26
+ }
14
27
  bind(sink) {
15
28
  this.sink = sink;
16
29
  const pending = this.pending;
@@ -29,11 +42,20 @@ export class StageTiming {
29
42
  finally {
30
43
  const ended = performance.now();
31
44
  const durationMs = ended - started, elapsedMs = ended - this.started;
32
- const detail = { kind: 'session', severity: outcome === 'ok' ? 'info' : 'warning', code: 'REMOTE_STAGE_TIMING', summary: STAGES[phase] + ':' + durationMs.toFixed(1) + '毫秒', payload: { phase, durationMs, elapsedMs, outcome, clock: 'client-monotonic' } };
33
- if (this.sink)
34
- this.sink(detail);
35
- else
36
- this.pending.push(detail);
45
+ if (phase === 'connection-confirm' && outcome === 'ok') {
46
+ const current = this.confirmations;
47
+ current.count++;
48
+ current.total += durationMs;
49
+ current.min = Math.min(current.min, durationMs);
50
+ current.max = Math.max(current.max, durationMs);
51
+ current.elapsed = elapsedMs;
52
+ }
53
+ else {
54
+ if (phase !== 'connection-confirm')
55
+ this.summarize();
56
+ const detail = { kind: 'session', severity: outcome === 'ok' ? 'info' : 'warning', code: 'REMOTE_STAGE_TIMING', summary: STAGES[phase] + ':' + durationMs.toFixed(1) + '毫秒', payload: { phase, durationMs, elapsedMs, outcome, clock: 'client-monotonic' } };
57
+ this.emit(detail);
58
+ }
37
59
  }
38
60
  }
39
61
  }
package/locale/en.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "meta": {
3
3
  "title": "Codex (Remote Host)",
4
- "description": "Run Codex sessions through a remote host. Administrators: use Settings → Open configuration file to edit the existing plugin entry. See README for the six fields and approved defaults."
4
+ "description": "Run Codex sessions through a remote host. Administrators: use Settings → Open configuration file to edit the existing plugin entry. See README for the eight fields and approved defaults."
5
5
  },
6
6
  "configuration": {
7
- "administratorNote": "Settings → Built-in plugins → 远程助手参数. Seven named settings with defaults, scope and effects.",
7
+ "administratorNote": "Settings → Built-in plugins → 远程助手参数. Eight named settings with defaults, scope and effects.",
8
8
  "providerName": "Codex (Remote Host)"
9
9
  }
10
10
  }
package/locale/zh.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "description": "通过远程主机运行Claude与Codex。首版不支持总是允许和歧义审批选项、已有Claude会话中立即生效的系统上下文变更;完整说明见README。参数从设置 → 内置插件 → 远程助手参数编辑。"
5
5
  },
6
6
  "configuration": {
7
- "administratorNote": "管理员:设置 → 内置插件 → 远程助手参数。七项设置含默认值、范围、生效和后果说明。",
7
+ "administratorNote": "管理员:设置 → 内置插件 → 远程助手参数。八项设置含默认值、范围、生效和后果说明。",
8
8
  "providerName": "Codex(远程主机)"
9
9
  }
10
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-workbench/remote-agent",
3
- "version": "0.8.18",
3
+ "version": "0.8.21",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",