@tencent-ai/agent-sdk 0.3.229 → 0.3.230-dev.80d16dd.202607272156

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.
Files changed (72) hide show
  1. package/cli/CHANGELOG.md +10 -0
  2. package/cli/dist/codebuddy-headless.js +309 -291
  3. package/cli/package.json +3 -2
  4. package/cli/product.cloudhosted.json +2 -2
  5. package/cli/product.internal.json +2 -2
  6. package/cli/product.ioa.json +2 -2
  7. package/cli/product.json +2 -2
  8. package/cli/product.selfhosted.json +2 -2
  9. package/lib/_internal/query-controller.d.ts +48 -2
  10. package/lib/_internal/query-controller.d.ts.map +1 -1
  11. package/lib/_internal/query-controller.js +335 -2
  12. package/lib/_internal/query-controller.js.map +1 -1
  13. package/lib/_internal/remote-extensions-options.d.ts +3 -0
  14. package/lib/_internal/remote-extensions-options.d.ts.map +1 -0
  15. package/lib/_internal/remote-extensions-options.js +37 -0
  16. package/lib/_internal/remote-extensions-options.js.map +1 -0
  17. package/lib/_internal/session-mirror.d.ts +53 -0
  18. package/lib/_internal/session-mirror.d.ts.map +1 -0
  19. package/lib/_internal/session-mirror.js +156 -0
  20. package/lib/_internal/session-mirror.js.map +1 -0
  21. package/lib/_internal/session-store-runtime.d.ts +52 -0
  22. package/lib/_internal/session-store-runtime.d.ts.map +1 -0
  23. package/lib/_internal/session-store-runtime.js +283 -0
  24. package/lib/_internal/session-store-runtime.js.map +1 -0
  25. package/lib/in-memory-session-store.d.ts +31 -0
  26. package/lib/in-memory-session-store.d.ts.map +1 -0
  27. package/lib/in-memory-session-store.js +127 -0
  28. package/lib/in-memory-session-store.js.map +1 -0
  29. package/lib/index.d.ts +13 -1
  30. package/lib/index.d.ts.map +1 -1
  31. package/lib/index.js +16 -1
  32. package/lib/index.js.map +1 -1
  33. package/lib/mcp/delegated-redaction.d.ts +47 -0
  34. package/lib/mcp/delegated-redaction.d.ts.map +1 -0
  35. package/lib/mcp/delegated-redaction.js +362 -0
  36. package/lib/mcp/delegated-redaction.js.map +1 -0
  37. package/lib/mcp/index.d.ts +4 -0
  38. package/lib/mcp/index.d.ts.map +1 -1
  39. package/lib/mcp/index.js +8 -1
  40. package/lib/mcp/index.js.map +1 -1
  41. package/lib/mcp/managed-remote-mcp-bridge.d.ts +49 -0
  42. package/lib/mcp/managed-remote-mcp-bridge.d.ts.map +1 -0
  43. package/lib/mcp/managed-remote-mcp-bridge.js +303 -0
  44. package/lib/mcp/managed-remote-mcp-bridge.js.map +1 -0
  45. package/lib/query.d.ts +14 -1
  46. package/lib/query.d.ts.map +1 -1
  47. package/lib/query.js +320 -28
  48. package/lib/query.js.map +1 -1
  49. package/lib/remote-error-codes.d.ts +8 -0
  50. package/lib/remote-error-codes.d.ts.map +1 -0
  51. package/lib/remote-error-codes.js +23 -0
  52. package/lib/remote-error-codes.js.map +1 -0
  53. package/lib/session-store-protocol.d.ts +19 -0
  54. package/lib/session-store-protocol.d.ts.map +1 -0
  55. package/lib/session-store-protocol.js +125 -0
  56. package/lib/session-store-protocol.js.map +1 -0
  57. package/lib/session.d.ts +8 -1
  58. package/lib/session.d.ts.map +1 -1
  59. package/lib/session.js +132 -11
  60. package/lib/session.js.map +1 -1
  61. package/lib/transport/index.d.ts +53 -3
  62. package/lib/transport/index.d.ts.map +1 -1
  63. package/lib/transport/index.js.map +1 -1
  64. package/lib/transport/process-transport.d.ts +81 -4
  65. package/lib/transport/process-transport.d.ts.map +1 -1
  66. package/lib/transport/process-transport.js +295 -15
  67. package/lib/transport/process-transport.js.map +1 -1
  68. package/lib/types.d.ts +629 -6
  69. package/lib/types.d.ts.map +1 -1
  70. package/lib/types.js +14 -1
  71. package/lib/types.js.map +1 -1
  72. package/package.json +3 -2
@@ -3,8 +3,32 @@
3
3
  *
4
4
  * Transport implementation that spawns CLI process and communicates via stdin/stdout.
5
5
  */
6
+ import { type DelegatedServerDescriptorHost, type RealMcpTransport } from '../mcp/managed-remote-mcp-bridge';
6
7
  import type { ControlRequest, ControlRequestPayload, Message, SubscriptionChannel, UserMessage } from '../types';
7
8
  import type { NotificationHandler, ProcessTransportOptions, Transport } from './index';
9
+ /**
10
+ * 委托 server 注册描述(宿主声明,不含 url / token)。
11
+ * serverName 只在当前 control connection / session scope 内唯一,真实 endpoint/token 由
12
+ * 受管层在 createTransport 内解析,不经过 CLI。
13
+ */
14
+ export interface DelegatedServerRegistration {
15
+ serverName: string;
16
+ transport: 'http' | 'sse' | 'remote';
17
+ capabilityId?: string;
18
+ }
19
+ /**
20
+ * 受管层提供的委托 transport 工厂:按 scopedKey + descriptor 解析真实 endpoint/token 并建 transport。
21
+ * 部署方注入,测试用 mock 替代;这里不实现真实 endpoint / OAuth 解析。
22
+ */
23
+ export type CreateDelegatedMcpTransport = (scopedKey: string, descriptor: DelegatedServerDescriptorHost) => Promise<RealMcpTransport>;
24
+ /**
25
+ * transport 关闭的终止原因分类,用于决定是否需要合成一条补偿 error 帧。
26
+ *
27
+ * 只有异常类原因(进程非零退出、退出前未输出 result、stdout 意外关闭、spawn 失败)
28
+ * 才会触发合成;已经观察到 result / error 帧或上层主动关闭的场景下不合成,
29
+ * 避免将正常结束误报为失败。
30
+ */
31
+ export type TerminalCause = 'sawResult' | 'sawError' | 'explicitClose' | 'processExitNonZero' | 'processExitZeroBeforeResult' | 'stdoutClosedUnexpectedly' | 'spawnError';
8
32
  /**
9
33
  * ProcessTransport - communicates with CLI via spawned process.
10
34
  */
@@ -21,12 +45,19 @@ export declare class ProcessTransport implements Transport {
21
45
  private _stderrBuffer;
22
46
  private readyPromise;
23
47
  private _isCliReady;
48
+ private _sessionId?;
49
+ private _currentRunId?;
50
+ private _sawRunStarted;
51
+ private _sawResult;
52
+ private _sawError;
24
53
  private _writeQueue;
25
54
  private _draining;
26
55
  private sdkMcpTransports;
27
56
  private sdkMcpServerInstances;
28
57
  private pendingMcpResponses;
29
58
  private _sdkMcpServerNames;
59
+ private managedRemoteMcpBridge?;
60
+ private registeredDelegatedScopedKeys;
30
61
  private _cliVersion;
31
62
  private notificationHandlers;
32
63
  constructor(options?: ProcessTransportOptions);
@@ -109,7 +140,7 @@ export declare class ProcessTransport implements Transport {
109
140
  /**
110
141
  * Send a user message to CLI.
111
142
  */
112
- sendUserMessage(content: string | UserMessage, traceId?: string, parentSpanId?: string): void;
143
+ sendUserMessage(content: string | UserMessage, traceId?: string, parentSpanId?: string, remoteContext?: import('../types').RemoteOperationContext): void;
113
144
  /**
114
145
  * Send a control request and wait for response.
115
146
  */
@@ -126,7 +157,7 @@ export declare class ProcessTransport implements Transport {
126
157
  /**
127
158
  * Send a control error response.
128
159
  */
129
- sendControlErrorResponse(requestId: string, error: string): void;
160
+ sendControlErrorResponse(requestId: string, error: string, code?: string): void;
130
161
  /**
131
162
  * Check if transport is ready.
132
163
  * Returns true only when CLI has produced its first output.
@@ -136,14 +167,31 @@ export declare class ProcessTransport implements Transport {
136
167
  * Register a handler for control notifications from a specific channel.
137
168
  */
138
169
  onNotification(channel: SubscriptionChannel, handler: NotificationHandler): void;
170
+ /**
171
+ * 向消息流注入一条 SDK 侧合成的消息(如 mirror_error 帧)。
172
+ * 流未建立或已结束时静默丢弃——此时没有消费者,合成帧无处投递。
173
+ */
174
+ enqueueMessage(message: Message): void;
139
175
  /**
140
176
  * Remove a notification handler for a specific channel.
141
177
  */
142
178
  offNotification(channel: SubscriptionChannel, handler: NotificationHandler): void;
143
179
  /**
144
- * Close the transport.
180
+ * 关闭 transport。
181
+ *
182
+ * cause 标明终止原因:仅在异常类原因(进程非零退出、退出前未输出 result、
183
+ * stdout 意外关闭、spawn 失败)下才合成一条 error 帧;正常结束与主动关闭均不合成,
184
+ * 避免将成功的 run 误报为失败。
185
+ *
186
+ * 已有调用点(例如上层 Session.close())不传 cause 时按主动关闭处理,
187
+ * 与"手动关闭不产生虚假 error"的既有行为保持一致。
145
188
  */
146
- close(): void;
189
+ close(cause?: TerminalCause): void;
190
+ /**
191
+ * 为合成帧生成 uuid。旧 Node 运行时可能未内置 WebCrypto,
192
+ * crypto.randomUUID 不可用时降级为"时间戳 + 随机串"的形式。
193
+ */
194
+ private randomUuid;
147
195
  private handleLine;
148
196
  private handleControlNotification;
149
197
  private handleControlResponse;
@@ -171,6 +219,35 @@ export declare class ProcessTransport implements Transport {
171
219
  * Handle MCP message control request from CLI.
172
220
  */
173
221
  private handleMcpMessageRequest;
222
+ /**
223
+ * 注册远程委托 server(宿主驱动)。
224
+ *
225
+ * 首次调用时懒构造 ManagedRemoteMcpBridge:createTransport 由受管层注入(解析真实
226
+ * endpoint/token),redactResponse 接线为 redactDelegatedResponse(响应离开宿主回到 CLI
227
+ * 之前先脱敏)。随后对每个 descriptor 用与路由一致的 resolveDelegatedScopedKey 生成 scoped
228
+ * key 并注册,key 记入集合供 close 统一注销。
229
+ *
230
+ * 接线现状:ProcessTransport 构造期在 `delegatedMcpServers` + `mcpTransportFactory` 齐备时
231
+ * 自动调用本方法(host-driven v1 必做)。`unregisterAllDelegatedServers` 已接入
232
+ * close 兜底,control connection 关闭时统一释放 transport 与 pending。
233
+ */
234
+ registerDelegatedServers(descriptors: DelegatedServerRegistration[], createTransport: CreateDelegatedMcpTransport): void;
235
+ /**
236
+ * 注销所有已注册的委托 server(reject 在途 pending、关闭真实 transport)。
237
+ * 由 close 调用;单个注销失败不阻塞其他。
238
+ */
239
+ private unregisterAllDelegatedServers;
240
+ /**
241
+ * 组合远程委托 server 的 scoped key。
242
+ *
243
+ * 完整 scoped key 语义是 control connection / tenant / user / session / run + serverName。
244
+ * 本 transport 从 ProcessTransportOptions 拿到 controlConnectionId /
245
+ * tenantId / userId / sessionId / runId,按字符集校验后用分隔符 `::` 拼接,缺失维度直接跳过
246
+ * (不写空段,避免 `::` 退化成空字符串导致跨 scope 撞 key)。所有维度都缺时才退化为裸
247
+ * serverName——这是最后兜底,部署方应至少注入 controlConnectionId / sessionId 之一。
248
+ * 注册与路由必须复用这同一组合逻辑,两侧的 key 才能一致。
249
+ */
250
+ private resolveDelegatedScopedKey;
174
251
  /**
175
252
  * Handle MCP request and wait for response from MCP server.
176
253
  */
@@ -1 +1 @@
1
- {"version":3,"file":"process-transport.d.ts","sourceRoot":"","sources":["../../src/transport/process-transport.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"process-transport.d.ts","sourceRoot":"","sources":["../../src/transport/process-transport.ts"],"names":[],"mappings":"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"}
@@ -41,7 +41,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
41
41
  exports.ProcessTransport = void 0;
42
42
  const node_child_process_1 = require("node:child_process");
43
43
  const readline = __importStar(require("node:readline"));
44
+ const delegated_redaction_1 = require("../mcp/delegated-redaction");
45
+ const managed_remote_mcp_bridge_1 = require("../mcp/managed-remote-mcp-bridge");
44
46
  const sdk_control_server_transport_1 = require("../mcp/sdk-control-server-transport");
47
+ const remote_error_codes_1 = require("../remote-error-codes");
45
48
  const utils_1 = require("../utils");
46
49
  const version_1 = require("../version");
47
50
  function createDeferred() {
@@ -53,6 +56,12 @@ function createDeferred() {
53
56
  });
54
57
  return { promise, resolve, reject };
55
58
  }
59
+ const ERROR_TERMINAL_CAUSES = new Set([
60
+ 'processExitNonZero',
61
+ 'processExitZeroBeforeResult',
62
+ 'stdoutClosedUnexpectedly',
63
+ 'spawnError',
64
+ ]);
56
65
  /**
57
66
  * Resolve the runtime configuration for spawning CLI process.
58
67
  *
@@ -121,6 +130,9 @@ class ProcessTransport {
121
130
  this._stderrBuffer = '';
122
131
  this.readyPromise = null;
123
132
  this._isCliReady = false;
133
+ this._sawRunStarted = false;
134
+ this._sawResult = false;
135
+ this._sawError = false;
124
136
  // Write queue for backpressure handling
125
137
  this._writeQueue = [];
126
138
  this._draining = false;
@@ -129,6 +141,8 @@ class ProcessTransport {
129
141
  this.sdkMcpServerInstances = new Map();
130
142
  this.pendingMcpResponses = new Map();
131
143
  this._sdkMcpServerNames = [];
144
+ // 已注册的委托 server scoped key 集合,供 close 时统一注销,避免残留 pending / transport。
145
+ this.registeredDelegatedScopedKeys = new Set();
132
146
  // Cached CLI version for UserAgent
133
147
  this._cliVersion = null;
134
148
  // Notification handlers by channel
@@ -150,6 +164,17 @@ class ProcessTransport {
150
164
  this.connectSdkMcpServer(name, server);
151
165
  }
152
166
  }
167
+ // 接线 host-driven 委托 bridge:descriptor + transport 工厂齐备时,构造期即注册
168
+ // scoped key → descriptor 到 ManagedRemoteMcpBridge。bridge 不会主动建 transport,
169
+ // 真实 transport 在第一次 mcp_message 到达时按需懒建。
170
+ // 缺 mcpTransportFactory 时只跳过注册(CLI 会因 bridge 未命中显式 fail-closed)。
171
+ if (this.options.delegatedMcpServers && this.options.delegatedMcpServers.length > 0 && this.options.mcpTransportFactory) {
172
+ this.registerDelegatedServers(this.options.delegatedMcpServers.map(d => ({
173
+ serverName: d.serverName,
174
+ transport: d.transport,
175
+ capabilityId: d.capabilityId,
176
+ })), this.options.mcpTransportFactory);
177
+ }
153
178
  }
154
179
  /**
155
180
  * Get the list of SDK MCP server names.
@@ -345,6 +370,9 @@ class ProcessTransport {
345
370
  if (opts.forkSession) {
346
371
  args.push('--fork-session');
347
372
  }
373
+ if (opts.sessionStoreMode) {
374
+ args.push('--session-store-mode', opts.sessionStoreMode);
375
+ }
348
376
  // MCP options
349
377
  if (opts.mcpServers) {
350
378
  if (typeof opts.mcpServers === 'string') {
@@ -358,6 +386,32 @@ class ProcessTransport {
358
386
  if (opts.strictMcpConfig) {
359
387
  args.push('--strict-mcp-config');
360
388
  }
389
+ // FileSystem replacement: serialize explicit modules selection (handler stays in-process)
390
+ if (opts.fileSystem) {
391
+ args.push('--file-system-config', JSON.stringify(opts.fileSystem));
392
+ }
393
+ // Channel replacement: pass empty descriptor to opt in; handler stays in-process
394
+ if (opts.channel) {
395
+ args.push('--channel-config', JSON.stringify({}));
396
+ }
397
+ // Exec replacement: serialize maxOutputBytes / capabilityId / remoteExecutionContextId
398
+ // (handler stays in-process). Presence of --exec-config enables remote exec in cbc.
399
+ if (opts.exec) {
400
+ args.push('--exec-config', JSON.stringify({
401
+ ...(opts.exec.maxOutputBytes ? { maxOutputBytes: opts.exec.maxOutputBytes } : {}),
402
+ ...(opts.exec.capabilityId ? { capabilityId: opts.exec.capabilityId } : {}),
403
+ ...(opts.exec.remoteExecutionContextId ? { remoteExecutionContextId: opts.exec.remoteExecutionContextId } : {}),
404
+ }));
405
+ }
406
+ if (opts.remoteExtensions) {
407
+ args.push('--remote-extensions');
408
+ }
409
+ // Local-execution deny list. Only emit when it actually denies something, so
410
+ // that "unset" and "[]" (empty deny list) produce byte-identical argv/logs.
411
+ const led = opts.localExecutionDeny;
412
+ if (led === 'all' || (Array.isArray(led) && led.length > 0)) {
413
+ args.push('--local-execution-deny', JSON.stringify(led));
414
+ }
361
415
  // Settings
362
416
  // SDK default: don't load any filesystem settings for clean environment isolation
363
417
  // When settingSources is explicitly provided (including empty array), use it
@@ -453,6 +507,9 @@ class ProcessTransport {
453
507
  if (!env.CODEBUDDY_DISABLE_AUTO_MEMORY) {
454
508
  env.CODEBUDDY_DISABLE_AUTO_MEMORY = '1';
455
509
  }
510
+ if (this.options.remoteExtensions) {
511
+ env.CODEBUDDY_DISABLE_PRODUCT_CACHE = '1';
512
+ }
456
513
  // Thinking configuration → MAX_THINKING_TOKENS env var
457
514
  this.applyThinkingEnv(env);
458
515
  // Build SDK UserAgent and inject via CODEBUDDY_CUSTOM_HEADERS
@@ -551,7 +608,7 @@ class ProcessTransport {
551
608
  /**
552
609
  * Send a user message to CLI.
553
610
  */
554
- sendUserMessage(content, traceId, parentSpanId) {
611
+ sendUserMessage(content, traceId, parentSpanId, remoteContext) {
555
612
  // Ensure transport is started
556
613
  this.start();
557
614
  let message;
@@ -566,16 +623,21 @@ class ProcessTransport {
566
623
  parent_tool_use_id: null,
567
624
  ...(traceId ? { trace_id: traceId } : {}),
568
625
  ...(parentSpanId ? { parent_span_id: parentSpanId } : {}),
626
+ ...(remoteContext ? { remoteContext } : {}),
569
627
  };
570
628
  }
571
629
  else {
572
- message = content;
573
- if (traceId && !message.trace_id) {
574
- message.trace_id = traceId;
575
- }
576
- if (parentSpanId && !message.parent_span_id) {
577
- message.parent_span_id = parentSpanId;
630
+ const carrier = content;
631
+ if (remoteContext && carrier.remoteContext?.contextId !== undefined
632
+ && carrier.remoteContext.contextId !== remoteContext.contextId) {
633
+ throw new remote_error_codes_1.RemoteProtocolError('REMOTE_CONTEXT_MISMATCH', 'user message remote context does not match this query');
578
634
  }
635
+ message = {
636
+ ...content,
637
+ ...(traceId && !content.trace_id ? { trace_id: traceId } : {}),
638
+ ...(parentSpanId && !content.parent_span_id ? { parent_span_id: parentSpanId } : {}),
639
+ ...(remoteContext ? { remoteContext } : {}),
640
+ };
579
641
  }
580
642
  this.writeLine(message);
581
643
  }
@@ -641,13 +703,14 @@ class ProcessTransport {
641
703
  /**
642
704
  * Send a control error response.
643
705
  */
644
- sendControlErrorResponse(requestId, error) {
706
+ sendControlErrorResponse(requestId, error, code) {
645
707
  const controlResponse = {
646
708
  type: 'control_response',
647
709
  response: {
648
710
  subtype: 'error',
649
711
  request_id: requestId,
650
712
  error,
713
+ ...(code ? { code } : {}),
651
714
  },
652
715
  };
653
716
  this.writeLine(controlResponse);
@@ -670,6 +733,15 @@ class ProcessTransport {
670
733
  }
671
734
  handlers.add(handler);
672
735
  }
736
+ /**
737
+ * 向消息流注入一条 SDK 侧合成的消息(如 mirror_error 帧)。
738
+ * 流未建立或已结束时静默丢弃——此时没有消费者,合成帧无处投递。
739
+ */
740
+ enqueueMessage(message) {
741
+ if (this.messageStream && !this.messageStream.isFinished) {
742
+ this.messageStream.enqueue(message);
743
+ }
744
+ }
673
745
  /**
674
746
  * Remove a notification handler for a specific channel.
675
747
  */
@@ -683,9 +755,16 @@ class ProcessTransport {
683
755
  }
684
756
  }
685
757
  /**
686
- * Close the transport.
758
+ * 关闭 transport。
759
+ *
760
+ * cause 标明终止原因:仅在异常类原因(进程非零退出、退出前未输出 result、
761
+ * stdout 意外关闭、spawn 失败)下才合成一条 error 帧;正常结束与主动关闭均不合成,
762
+ * 避免将成功的 run 误报为失败。
763
+ *
764
+ * 已有调用点(例如上层 Session.close())不传 cause 时按主动关闭处理,
765
+ * 与"手动关闭不产生虚假 error"的既有行为保持一致。
687
766
  */
688
- close() {
767
+ close(cause = 'explicitClose') {
689
768
  if (this.closed) {
690
769
  return;
691
770
  }
@@ -731,11 +810,47 @@ class ProcessTransport {
731
810
  }
732
811
  this.sdkMcpTransports.clear();
733
812
  this.sdkMcpServerInstances.clear();
813
+ // 注销远程委托 server:reject 在途 pending 并关闭真实 transport
814
+ this.unregisterAllDelegatedServers();
815
+ // 仅在 CLI 未输出终止信号时才合成 error 帧:
816
+ // sawResult / sawError 表示 consumer 已经收到权威的终止信号,
817
+ // explicitClose 属于用户主动关闭,均不应被视为失败。
818
+ const shouldSynthesize = this.messageStream !== null
819
+ && !this.messageStream.isFinished
820
+ && !this._sawResult
821
+ && !this._sawError
822
+ && ERROR_TERMINAL_CAUSES.has(cause);
823
+ if (shouldSynthesize) {
824
+ const syntheticError = {
825
+ type: 'error',
826
+ error: this._closeReason ?? `Transport closed: ${cause}`,
827
+ ...(this._sessionId ? { session_id: this._sessionId } : {}),
828
+ ...(this._currentRunId ? { run_id: this._currentRunId } : {}),
829
+ uuid: this.randomUuid(),
830
+ };
831
+ this.messageStream.enqueue(syntheticError);
832
+ }
734
833
  // Signal stream completion
735
834
  if (this.messageStream) {
736
835
  this.messageStream.done();
737
836
  }
738
837
  }
838
+ /**
839
+ * 为合成帧生成 uuid。旧 Node 运行时可能未内置 WebCrypto,
840
+ * crypto.randomUUID 不可用时降级为"时间戳 + 随机串"的形式。
841
+ */
842
+ randomUuid() {
843
+ try {
844
+ const c = globalThis.crypto;
845
+ if (c?.randomUUID) {
846
+ return c.randomUUID();
847
+ }
848
+ }
849
+ catch {
850
+ // fall through
851
+ }
852
+ return `syn-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
853
+ }
739
854
  // ============= Private Methods =============
740
855
  handleLine(line) {
741
856
  const trimmed = line.trim();
@@ -767,6 +882,43 @@ class ProcessTransport {
767
882
  }
768
883
  return;
769
884
  }
885
+ // 更新终止态追踪字段:session_id / run_id 用于合成 error 帧时填充上下文,
886
+ // sawResult / sawError 用于 close() 判断是否需要合成补偿帧。
887
+ // 必须在 enqueue 之前更新,否则 consumer 收到首帧后立即退出循环时字段仍为空。
888
+ const framed = message;
889
+ if (framed.session_id && !this._sessionId) {
890
+ this._sessionId = framed.session_id;
891
+ }
892
+ const isRunStartedFrame = framed.type === 'system' && framed.subtype === 'run_started';
893
+ if (isRunStartedFrame && framed.run_id) {
894
+ // run_started 是权威的 run 起点:明确切换到新 run,并重置本 run 的累计状态。
895
+ // 这样即便未来在同一 transport 内跑多个 run,每次 run 的终止态判断都不会串扰。
896
+ if (this._currentRunId !== framed.run_id) {
897
+ this._sawResult = false;
898
+ this._sawError = false;
899
+ }
900
+ this._currentRunId = framed.run_id;
901
+ this._sawRunStarted = true;
902
+ }
903
+ else if (framed.run_id && !this._sawRunStarted) {
904
+ // 未启用 --emit-run-lifecycle 时的回退路径:从任意帧的 run_id 推断当前 run。
905
+ // 一旦见过 run_started,就以其为准,不再被其他帧的 run_id 覆写。
906
+ //
907
+ // 检测到 run_id 变化时,同步重置本 run 的累计终止态(sawResult / sawError),
908
+ // 与 run_started 分支的处理对齐,避免旧 run 的终止信号污染新 run 的判定,
909
+ // 导致 close(cause) 误认为新 run 已正常结束而不合成补偿 error。
910
+ if (this._currentRunId && this._currentRunId !== framed.run_id) {
911
+ this._sawResult = false;
912
+ this._sawError = false;
913
+ }
914
+ this._currentRunId = framed.run_id;
915
+ }
916
+ if (framed.type === 'result') {
917
+ this._sawResult = true;
918
+ }
919
+ else if (framed.type === 'error') {
920
+ this._sawError = true;
921
+ }
770
922
  // Only regular messages are enqueued for consumer iteration
771
923
  if (this.messageStream) {
772
924
  this.messageStream.enqueue(message);
@@ -797,7 +949,10 @@ class ProcessTransport {
797
949
  }
798
950
  this.pendingRequests.delete(response.request_id);
799
951
  if (response.subtype === 'error') {
800
- pending.reject(new Error(response.error));
952
+ const error = (0, remote_error_codes_1.isRemoteErrorCode)(response.code)
953
+ ? new remote_error_codes_1.RemoteProtocolError(response.code, response.error)
954
+ : Object.assign(new Error(response.error), response.code ? { code: response.code } : {});
955
+ pending.reject(error);
801
956
  }
802
957
  else {
803
958
  pending.resolve(response.response ?? {});
@@ -808,21 +963,27 @@ class ProcessTransport {
808
963
  // Otherwise use a generic message — no setTimeout needed since
809
964
  // exit fires synchronously before close in Node.js child_process.
810
965
  this.setCloseReason('CLI process stdout closed unexpectedly');
811
- this.close();
966
+ this.close('stdoutClosedUnexpectedly');
812
967
  }
813
968
  handleError(error) {
814
969
  this.setCloseReason(`CLI I/O error: ${error.message}`);
815
- this.close();
970
+ this.close('stdoutClosedUnexpectedly');
816
971
  }
817
972
  handleProcessError(error) {
818
973
  this.setCloseReason(`CLI process spawn error: ${error.message}`);
819
- this.close();
974
+ this.close('spawnError');
820
975
  }
821
976
  handleProcessExit(code) {
822
977
  const stderr = this._stderrBuffer.trim();
823
978
  const stderrSuffix = stderr ? ` | stderr: ${stderr.slice(-500)}` : '';
824
979
  this.setCloseReason(`CLI process exited with code ${code}${stderrSuffix}`);
825
- this.close();
980
+ // 退出码为 0 且已观察到 result → 正常结束;
981
+ // 退出码为 0 但未观察到 result → CLI 在输出终止信号前提前退出,按异常处理以便下游归因;
982
+ // 退出码非 0 → 一律按异常处理。
983
+ const cause = code === 0
984
+ ? (this._sawResult ? 'sawResult' : 'processExitZeroBeforeResult')
985
+ : 'processExitNonZero';
986
+ this.close(cause);
826
987
  }
827
988
  setCloseReason(reason) {
828
989
  if (!this._closeReason) {
@@ -952,6 +1113,28 @@ class ProcessTransport {
952
1113
  const { server_name, message } = mcpRequest;
953
1114
  const transport = this.sdkMcpTransports.get(server_name);
954
1115
  if (!transport) {
1116
+ // in-process 未命中:尝试远程委托 bridge 回退(control ack 仍在本方法包装,不进 bridge)
1117
+ const scopedKey = this.resolveDelegatedScopedKey(server_name);
1118
+ if (this.managedRemoteMcpBridge && scopedKey && this.managedRemoteMcpBridge.isRegistered(scopedKey)) {
1119
+ try {
1120
+ const anyMsg = message;
1121
+ const isRequest = 'id' in anyMsg && anyMsg.id !== null && anyMsg.id !== undefined;
1122
+ const result = await this.managedRemoteMcpBridge.handleMcpMessage(scopedKey, message);
1123
+ if (isRequest) {
1124
+ this.sendControlResponse(request.request_id, { mcp_response: result });
1125
+ }
1126
+ else {
1127
+ // notification:回 id-less sentinel 释放 CLI control pending
1128
+ this.sendControlResponse(request.request_id, {
1129
+ mcp_response: { jsonrpc: '2.0', result: {} },
1130
+ });
1131
+ }
1132
+ }
1133
+ catch (error) {
1134
+ this.sendControlErrorResponse(request.request_id, error instanceof Error ? error.message : String(error));
1135
+ }
1136
+ return;
1137
+ }
955
1138
  this.sendControlErrorResponse(request.request_id, `SDK MCP server not found: ${server_name}`);
956
1139
  return;
957
1140
  }
@@ -978,6 +1161,103 @@ class ProcessTransport {
978
1161
  this.sendControlErrorResponse(request.request_id, error instanceof Error ? error.message : String(error));
979
1162
  }
980
1163
  }
1164
+ /**
1165
+ * 注册远程委托 server(宿主驱动)。
1166
+ *
1167
+ * 首次调用时懒构造 ManagedRemoteMcpBridge:createTransport 由受管层注入(解析真实
1168
+ * endpoint/token),redactResponse 接线为 redactDelegatedResponse(响应离开宿主回到 CLI
1169
+ * 之前先脱敏)。随后对每个 descriptor 用与路由一致的 resolveDelegatedScopedKey 生成 scoped
1170
+ * key 并注册,key 记入集合供 close 统一注销。
1171
+ *
1172
+ * 接线现状:ProcessTransport 构造期在 `delegatedMcpServers` + `mcpTransportFactory` 齐备时
1173
+ * 自动调用本方法(host-driven v1 必做)。`unregisterAllDelegatedServers` 已接入
1174
+ * close 兜底,control connection 关闭时统一释放 transport 与 pending。
1175
+ */
1176
+ registerDelegatedServers(descriptors, createTransport) {
1177
+ if (!this.managedRemoteMcpBridge) {
1178
+ // 审计出口:有 stderr 回调则写入(仅非 secret 的结构化事件),否则忽略。
1179
+ const onAudit = (event) => {
1180
+ this.options.stderr?.(`[mcp-delegation-audit] ${JSON.stringify(event)}\n`);
1181
+ };
1182
+ this.managedRemoteMcpBridge = new managed_remote_mcp_bridge_1.ManagedRemoteMcpBridge({
1183
+ createTransport,
1184
+ requestTimeoutMs: this.options.requestTimeoutMs ?? 30000,
1185
+ // 组合脱敏:先做 schema fail-close 筛查(剔除含凭证形参的 tool),再做值级脱敏;
1186
+ // 被拒 tool 经 onAudit 上报,不抛异常。
1187
+ redactResponse: (scopedKey, message) => {
1188
+ const { message: safe, rejected } = (0, delegated_redaction_1.redactAndScreenDelegatedResponse)(message);
1189
+ if (rejected.length > 0) {
1190
+ onAudit({ type: 'delegated_tool_schema_rejected', scopedKey, rejected });
1191
+ }
1192
+ return safe;
1193
+ },
1194
+ onAudit,
1195
+ });
1196
+ }
1197
+ for (const descriptor of descriptors) {
1198
+ const scopedKey = this.resolveDelegatedScopedKey(descriptor.serverName);
1199
+ if (!scopedKey) {
1200
+ continue;
1201
+ }
1202
+ this.managedRemoteMcpBridge.registerDelegatedServer(scopedKey, {
1203
+ transport: descriptor.transport,
1204
+ capabilityId: descriptor.capabilityId,
1205
+ });
1206
+ this.registeredDelegatedScopedKeys.add(scopedKey);
1207
+ }
1208
+ }
1209
+ /**
1210
+ * 注销所有已注册的委托 server(reject 在途 pending、关闭真实 transport)。
1211
+ * 由 close 调用;单个注销失败不阻塞其他。
1212
+ */
1213
+ unregisterAllDelegatedServers() {
1214
+ if (!this.managedRemoteMcpBridge) {
1215
+ return;
1216
+ }
1217
+ for (const scopedKey of this.registeredDelegatedScopedKeys) {
1218
+ this.managedRemoteMcpBridge.unregisterDelegatedServer(scopedKey).catch(() => {
1219
+ // 清理阶段忽略单个注销错误
1220
+ });
1221
+ }
1222
+ this.registeredDelegatedScopedKeys.clear();
1223
+ }
1224
+ /**
1225
+ * 组合远程委托 server 的 scoped key。
1226
+ *
1227
+ * 完整 scoped key 语义是 control connection / tenant / user / session / run + serverName。
1228
+ * 本 transport 从 ProcessTransportOptions 拿到 controlConnectionId /
1229
+ * tenantId / userId / sessionId / runId,按字符集校验后用分隔符 `::` 拼接,缺失维度直接跳过
1230
+ * (不写空段,避免 `::` 退化成空字符串导致跨 scope 撞 key)。所有维度都缺时才退化为裸
1231
+ * serverName——这是最后兜底,部署方应至少注入 controlConnectionId / sessionId 之一。
1232
+ * 注册与路由必须复用这同一组合逻辑,两侧的 key 才能一致。
1233
+ */
1234
+ resolveDelegatedScopedKey(serverName) {
1235
+ const segments = [];
1236
+ const push = (v, label) => {
1237
+ if (typeof v === 'string' && v.length > 0) {
1238
+ if (!/^[A-Za-z0-9_.:-]+$/.test(v)) {
1239
+ // 非法字符:跳过该维度并记审计,避免拼出含分隔符冲突的 scoped key。
1240
+ this.options.stderr?.(`[mcp-delegation-audit] scoped key segment "${label}" has invalid chars, skipped\n`);
1241
+ return;
1242
+ }
1243
+ segments.push(v);
1244
+ }
1245
+ };
1246
+ push(this.options.controlConnectionId, 'controlConnectionId');
1247
+ push(this.options.tenantId, 'tenantId');
1248
+ push(this.options.userId, 'userId');
1249
+ push(this.options.sessionId, 'sessionId');
1250
+ push(this.options.runId, 'runId');
1251
+ push(serverName, 'serverName');
1252
+ if (segments.length === 0) {
1253
+ return undefined;
1254
+ }
1255
+ if (segments.length === 1) {
1256
+ // 仅 serverName 可用:兜底返回裸 serverName(部署方未注入 scope 维度的退化场景)。
1257
+ return serverName;
1258
+ }
1259
+ return segments.join('::');
1260
+ }
981
1261
  /**
982
1262
  * Handle MCP request and wait for response from MCP server.
983
1263
  */