@jsonstudio/rcc 0.89.942 → 0.89.1083

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 (91) hide show
  1. package/README.md +1 -42
  2. package/dist/build-info.js +2 -2
  3. package/dist/build-info.js.map +1 -1
  4. package/dist/cli.js +106 -10
  5. package/dist/cli.js.map +1 -1
  6. package/dist/commands/quota-daemon.d.ts +2 -0
  7. package/dist/commands/quota-daemon.js +89 -0
  8. package/dist/commands/quota-daemon.js.map +1 -0
  9. package/dist/docs/daemon-admin-ui.html +958 -0
  10. package/dist/index.js +5 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/manager/modules/quota/index.d.ts +34 -0
  13. package/dist/manager/modules/quota/index.js +291 -0
  14. package/dist/manager/modules/quota/index.js.map +1 -1
  15. package/dist/manager/modules/token/index.js +13 -2
  16. package/dist/manager/modules/token/index.js.map +1 -1
  17. package/dist/manager/quota/provider-quota-center.d.ts +48 -0
  18. package/dist/manager/quota/provider-quota-center.js +239 -0
  19. package/dist/manager/quota/provider-quota-center.js.map +1 -0
  20. package/dist/manager/quota/provider-quota-store.d.ts +17 -0
  21. package/dist/manager/quota/provider-quota-store.js +88 -0
  22. package/dist/manager/quota/provider-quota-store.js.map +1 -0
  23. package/dist/providers/auth/token-scanner/index.js +11 -3
  24. package/dist/providers/auth/token-scanner/index.js.map +1 -1
  25. package/dist/providers/core/runtime/http-request-executor.js +24 -7
  26. package/dist/providers/core/runtime/http-request-executor.js.map +1 -1
  27. package/dist/providers/core/runtime/http-transport-provider.js +11 -3
  28. package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
  29. package/dist/providers/core/runtime/responses-provider.js +9 -3
  30. package/dist/providers/core/runtime/responses-provider.js.map +1 -1
  31. package/dist/providers/core/utils/http-client.d.ts +1 -0
  32. package/dist/providers/core/utils/http-client.js +139 -4
  33. package/dist/providers/core/utils/http-client.js.map +1 -1
  34. package/dist/providers/core/utils/snapshot-writer.d.ts +12 -0
  35. package/dist/providers/core/utils/snapshot-writer.js +99 -18
  36. package/dist/providers/core/utils/snapshot-writer.js.map +1 -1
  37. package/dist/providers/mock/mock-provider-runtime.d.ts +3 -0
  38. package/dist/providers/mock/mock-provider-runtime.js +176 -4
  39. package/dist/providers/mock/mock-provider-runtime.js.map +1 -1
  40. package/dist/server/handlers/chat-handler.js +13 -1
  41. package/dist/server/handlers/chat-handler.js.map +1 -1
  42. package/dist/server/handlers/handler-utils.js +5 -0
  43. package/dist/server/handlers/handler-utils.js.map +1 -1
  44. package/dist/server/handlers/messages-handler.js +13 -1
  45. package/dist/server/handlers/messages-handler.js.map +1 -1
  46. package/dist/server/handlers/responses-handler.js +73 -1
  47. package/dist/server/handlers/responses-handler.js.map +1 -1
  48. package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js +174 -2
  49. package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js.map +1 -1
  50. package/dist/server/runtime/http-server/daemon-admin/providers-handler.js +519 -0
  51. package/dist/server/runtime/http-server/daemon-admin/providers-handler.js.map +1 -1
  52. package/dist/server/runtime/http-server/executor-response.js +6 -0
  53. package/dist/server/runtime/http-server/executor-response.js.map +1 -1
  54. package/dist/server/runtime/http-server/index.d.ts +5 -0
  55. package/dist/server/runtime/http-server/index.js +205 -4
  56. package/dist/server/runtime/http-server/index.js.map +1 -1
  57. package/dist/server/runtime/http-server/middleware.d.ts +2 -0
  58. package/dist/server/runtime/http-server/middleware.js +63 -0
  59. package/dist/server/runtime/http-server/middleware.js.map +1 -1
  60. package/dist/server/runtime/http-server/request-executor.d.ts +2 -0
  61. package/dist/server/runtime/http-server/request-executor.js +57 -10
  62. package/dist/server/runtime/http-server/request-executor.js.map +1 -1
  63. package/dist/server/runtime/http-server/routes.js +38 -1
  64. package/dist/server/runtime/http-server/routes.js.map +1 -1
  65. package/dist/server/runtime/http-server/stats-manager.d.ts +55 -0
  66. package/dist/server/runtime/http-server/stats-manager.js +462 -4
  67. package/dist/server/runtime/http-server/stats-manager.js.map +1 -1
  68. package/dist/server/runtime/http-server/types.d.ts +1 -0
  69. package/dist/token-daemon/token-daemon.js +70 -25
  70. package/dist/token-daemon/token-daemon.js.map +1 -1
  71. package/dist/token-daemon/token-utils.d.ts +1 -0
  72. package/dist/token-daemon/token-utils.js +9 -1
  73. package/dist/token-daemon/token-utils.js.map +1 -1
  74. package/dist/tools/semantic-replay.js +29 -0
  75. package/dist/tools/semantic-replay.js.map +1 -1
  76. package/dist/utils/snapshot-writer.d.ts +2 -0
  77. package/dist/utils/snapshot-writer.js +47 -4
  78. package/dist/utils/snapshot-writer.js.map +1 -1
  79. package/package.json +2 -3
  80. package/scripts/analyze-codex-error-failures.mjs +24 -14
  81. package/scripts/classify-codex-samples.mjs +0 -35
  82. package/scripts/copy-modules-config.mjs +17 -1
  83. package/scripts/generate-snapshot-data.mjs +41 -11
  84. package/scripts/mock-provider/extract.mjs +239 -21
  85. package/scripts/mock-provider/run-regressions.mjs +79 -16
  86. package/scripts/quota-dryrun.mjs +124 -0
  87. package/scripts/tests/apply-patch-loop.mjs +5 -1
  88. package/scripts/tests/exec-command-loop.mjs +16 -19
  89. package/scripts/verify-apply-patch.mjs +335 -5
  90. package/scripts/verify-e2e-toolcall.mjs +49 -10
  91. package/scripts/toon-suite.mjs +0 -141
@@ -37,13 +37,14 @@ function parseEntryFilter() {
37
37
 
38
38
  async function ensureCliAvailable() {
39
39
  const cliPath = path.join(PROJECT_ROOT, 'dist', 'cli.js');
40
- if (await fileExists(cliPath)) {
40
+ const serverPath = path.join(PROJECT_ROOT, 'dist', 'index.js');
41
+ if ((await fileExists(cliPath)) && (await fileExists(serverPath))) {
41
42
  return;
42
43
  }
43
- console.warn('[mock:regressions] dist/cli.js missing, running "npm run build:min" automatically...');
44
+ console.warn('[mock:regressions] dist artifacts missing (cli.js/index.js), running "npm run build:min" automatically...');
44
45
  await runBuildForMockRegressions();
45
- if (!(await fileExists(cliPath))) {
46
- throw new Error('dist/cli.js missing after automatic build. Please run "npm run build:dev" manually.');
46
+ if (!(await fileExists(cliPath)) || !(await fileExists(serverPath))) {
47
+ throw new Error('dist artifacts missing after automatic build. Please run "npm run build:dev" manually.');
47
48
  }
48
49
  }
49
50
 
@@ -187,7 +188,7 @@ async function writeTempConfig(sample, port) {
187
188
  return { dir, file };
188
189
  }
189
190
 
190
- function createServer(configPath, port) {
191
+ function createServer(configPath, port, snapshotRoot) {
191
192
  const env = {
192
193
  ...process.env,
193
194
  ROUTECODEX_USE_MOCK: '1',
@@ -196,7 +197,14 @@ function createServer(configPath, port) {
196
197
  ROUTECODEX_MOCK_VALIDATE_NAMES: '1',
197
198
  ROUTECODEX_STAGE_LOG: process.env.ROUTECODEX_STAGE_LOG ?? '0',
198
199
  ROUTECODEX_PORT: String(port),
199
- ROUTECODEX_CONFIG_PATH: configPath
200
+ ROUTECODEX_CONFIG_PATH: configPath,
201
+ // 将快照写入临时目录,避免污染全局 ~/.routecodex/codex-samples 样本
202
+ ...(snapshotRoot
203
+ ? {
204
+ ROUTECODEX_SNAPSHOT_DIR: snapshotRoot,
205
+ RCC_SNAPSHOT_DIR: snapshotRoot
206
+ }
207
+ : {})
200
208
  };
201
209
  const entry = path.join(PROJECT_ROOT, 'dist', 'index.js');
202
210
  const child = spawn(process.execPath, [entry], {
@@ -305,10 +313,9 @@ function validateToolCallIds(payload, sample, tagSet) {
305
313
  if (!payload || typeof payload !== 'object') {
306
314
  return errors;
307
315
  }
308
- const wantsFcIds =
309
- tagSet.has('require_fc_call_ids') ||
310
- tagSet.has('missing_tool_call_id') ||
311
- tagSet.has('regression');
316
+ const enforceCallIdFormat = tagSet.has('require_fc_call_ids');
317
+ const isValidCallId = (value) =>
318
+ /^call_[A-Za-z0-9]+$/.test(value) || /^fc[_-][A-Za-z0-9-]+$/.test(value);
312
319
 
313
320
  const allToolCallIds = new Set();
314
321
  if (Array.isArray(payload.output)) {
@@ -323,7 +330,7 @@ function validateToolCallIds(payload, sample, tagSet) {
323
330
  return;
324
331
  }
325
332
  allToolCallIds.add(rawId);
326
- if (wantsFcIds && !/^call_[A-Za-z0-9]+$/.test(rawId)) {
333
+ if (enforceCallIdFormat && !isValidCallId(rawId)) {
327
334
  errors.push(`output[${oi}].tool_calls[${ti}].id has invalid format: ${rawId}`);
328
335
  }
329
336
  });
@@ -340,12 +347,16 @@ function validateToolCallIds(payload, sample, tagSet) {
340
347
  if (Array.isArray(submitCalls)) {
341
348
  submitCalls.forEach((tc, i) => {
342
349
  if (!tc || typeof tc !== 'object') return;
343
- const rawId = typeof tc.tool_call_id === 'string' ? tc.tool_call_id.trim() : '';
350
+ const rawId = typeof tc.tool_call_id === 'string'
351
+ ? tc.tool_call_id.trim()
352
+ : typeof tc.id === 'string'
353
+ ? tc.id.trim()
354
+ : '';
344
355
  if (!rawId) {
345
356
  errors.push(`required_action.submit_tool_outputs.tool_calls[${i}].tool_call_id missing`);
346
357
  return;
347
358
  }
348
- if (wantsFcIds && !/^call_[A-Za-z0-9]+$/.test(rawId)) {
359
+ if (enforceCallIdFormat && !isValidCallId(rawId)) {
349
360
  errors.push(
350
361
  `required_action.submit_tool_outputs.tool_calls[${i}].tool_call_id has invalid format: ${rawId}`
351
362
  );
@@ -395,6 +406,10 @@ function resolveRequestUrl(sample, requestDoc, port) {
395
406
  async function sendRequest(sample, requestDoc, port) {
396
407
  const url = resolveRequestUrl(sample, requestDoc, port);
397
408
  const payload = extractRequestBody(requestDoc);
409
+ if (payload && typeof payload === 'object') {
410
+ const meta = payload.metadata && typeof payload.metadata === 'object' ? payload.metadata : {};
411
+ payload.metadata = { ...meta, mockSampleReqId: sample.reqId };
412
+ }
398
413
  const headers = { 'content-type': 'application/json' };
399
414
  const wantsStream =
400
415
  payload?.stream === true ||
@@ -424,15 +439,64 @@ async function sendRequest(sample, requestDoc, port) {
424
439
  }
425
440
  }
426
441
 
442
+ function looksLikeSseErrorStream(text) {
443
+ if (typeof text !== 'string') {
444
+ return false;
445
+ }
446
+ const trimmed = text.trim();
447
+ if (!trimmed) {
448
+ return false;
449
+ }
450
+ // 简单判定:包含 SSE error 事件头和 JSON error 负载。
451
+ if (trimmed.includes('event: error') && trimmed.includes('data:')) {
452
+ return true;
453
+ }
454
+ if (trimmed.includes('"type":"error"') || trimmed.includes('"status":502')) {
455
+ return true;
456
+ }
457
+ return false;
458
+ }
459
+
427
460
  async function runSample(sample, index) {
428
461
  const clientDoc = await loadSampleDocument(sample, { fileName: 'client-request.json', optional: true });
429
462
  const requestDoc = clientDoc || (await loadSampleDocument(sample));
463
+ const responseDoc = await loadSampleDocument(sample, { fileName: 'response.json', optional: true });
430
464
  const port = 5800 + index;
431
465
  const { dir, file } = await writeTempConfig(sample, port);
432
- const server = createServer(file, port);
466
+ // 为当前样本创建独立的临时快照根目录,并在完成后整体删除
467
+ const snapshotRoot = path.join(dir, 'codex-samples');
468
+ const server = createServer(file, port, snapshotRoot);
469
+ const tags = new Set(Array.isArray(sample.tags) ? sample.tags : []);
470
+ const expectSseTerminationError = tags.has('responses_sse_terminated');
471
+ const allowSampleError =
472
+ responseDoc && typeof responseDoc.status === 'number' && responseDoc.status >= 400;
433
473
  try {
434
474
  await waitForHealth(port, server.process);
435
- const responseText = await sendRequest(sample, requestDoc, port);
475
+ let responseText;
476
+ try {
477
+ responseText = await sendRequest(sample, requestDoc, port);
478
+ if (expectSseTerminationError) {
479
+ if (!looksLikeSseErrorStream(responseText)) {
480
+ throw new Error(
481
+ 'expected SSE termination to surface as HTTP error or SSE error event, but got successful non-error payload'
482
+ );
483
+ }
484
+ // 对于 SSE 终止样本,只要以 SSE error 事件形式返回即可视为通过。
485
+ return;
486
+ }
487
+ } catch (sendError) {
488
+ if (expectSseTerminationError || allowSampleError) {
489
+ const msg = sendError instanceof Error ? sendError.message : String(sendError);
490
+ if (!/HTTP\s+4\d\d|HTTP\s+5\d\d/i.test(msg)) {
491
+ throw new Error(
492
+ `expected HTTP 4xx/5xx error, but got: ${msg}`
493
+ );
494
+ }
495
+ // 对于错误类样本,只要成功以 HTTP 错误形式透出即可。
496
+ return;
497
+ }
498
+ throw sendError;
499
+ }
436
500
  const body = (() => {
437
501
  try {
438
502
  return JSON.parse(responseText);
@@ -440,7 +504,6 @@ async function runSample(sample, index) {
440
504
  return undefined;
441
505
  }
442
506
  })();
443
- const tags = new Set(Array.isArray(sample.tags) ? sample.tags : []);
444
507
  if (body && Array.isArray(body.output)) {
445
508
  const invalid = collectInvalidNames(body);
446
509
  if (invalid.length) {
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Quota dry-run helper:
5
+ * 从简单的 JSON 事件数组读取错误/成功/usage 事件,驱动 provider-quota-center,
6
+ * 并将结果写入 ~/.routecodex/quota/provider-quota.json,方便人工检查。
7
+ *
8
+ * 用法:
9
+ * node scripts/quota-dryrun.mjs path/to/events.json
10
+ *
11
+ * 事件格式示例:
12
+ * [
13
+ * { "type": "error", "providerKey": "antigravity.alias1.gemini-3-pro-high", "httpStatus": 429 },
14
+ * { "type": "success", "providerKey": "antigravity.alias1.gemini-3-pro-high", "usedTokens": 120 },
15
+ * { "type": "usage", "providerKey": "antigravity.alias1.gemini-3-pro-high", "requestedTokens": 80 }
16
+ * ]
17
+ */
18
+
19
+ import fs from 'node:fs/promises';
20
+ import path from 'node:path';
21
+ import {
22
+ applyErrorEvent,
23
+ applySuccessEvent,
24
+ applyUsageEvent,
25
+ createInitialQuotaState
26
+ } from '../src/manager/quota/provider-quota-center.js';
27
+ import {
28
+ saveProviderQuotaSnapshot
29
+ } from '../src/manager/quota/provider-quota-store.js';
30
+
31
+ async function main() {
32
+ const fileArg = process.argv[2];
33
+ if (!fileArg) {
34
+ // eslint-disable-next-line no-console
35
+ console.error('Usage: node scripts/quota-dryrun.mjs path/to/events.json');
36
+ process.exitCode = 1;
37
+ return;
38
+ }
39
+ const filePath = path.resolve(process.cwd(), fileArg);
40
+ const raw = await fs.readFile(filePath, 'utf8');
41
+ const events = JSON.parse(raw);
42
+ if (!Array.isArray(events)) {
43
+ throw new Error('events file must contain a JSON array');
44
+ }
45
+
46
+ const states = new Map();
47
+ const nowMs = Date.now();
48
+
49
+ for (const entry of events) {
50
+ if (!entry || typeof entry !== 'object') {
51
+ // eslint-disable-next-line no-console
52
+ console.warn('[quota-dryrun] skip non-object event', entry);
53
+ continue;
54
+ }
55
+ const record = entry;
56
+ const providerKey = typeof record.providerKey === 'string' ? record.providerKey.trim() : '';
57
+ if (!providerKey) {
58
+ // eslint-disable-next-line no-console
59
+ console.warn('[quota-dryrun] event missing providerKey', record);
60
+ continue;
61
+ }
62
+ const type = typeof record.type === 'string' ? record.type.trim().toLowerCase() : '';
63
+ if (!type) {
64
+ // eslint-disable-next-line no-console
65
+ console.warn('[quota-dryrun] event missing type', record);
66
+ continue;
67
+ }
68
+ const existing = states.get(providerKey) as any | undefined;
69
+ const baseState =
70
+ existing ??
71
+ createInitialQuotaState(providerKey, undefined, nowMs);
72
+ let nextState = baseState;
73
+
74
+ if (type === 'error') {
75
+ nextState = applyErrorEvent(
76
+ baseState,
77
+ {
78
+ providerKey,
79
+ code: record.code,
80
+ httpStatus: record.httpStatus,
81
+ fatal: record.fatal === true
82
+ },
83
+ nowMs
84
+ );
85
+ } else if (type === 'success') {
86
+ nextState = applySuccessEvent(
87
+ baseState,
88
+ {
89
+ providerKey,
90
+ usedTokens: record.usedTokens
91
+ },
92
+ nowMs
93
+ );
94
+ } else if (type === 'usage') {
95
+ nextState = applyUsageEvent(
96
+ baseState,
97
+ {
98
+ providerKey,
99
+ requestedTokens: record.requestedTokens
100
+ },
101
+ nowMs
102
+ );
103
+ } else {
104
+ // eslint-disable-next-line no-console
105
+ console.warn('[quota-dryrun] unknown event type', type);
106
+ continue;
107
+ }
108
+ states.set(providerKey, nextState);
109
+ }
110
+
111
+ const snapshot = Object.fromEntries(states.entries());
112
+ await saveProviderQuotaSnapshot(snapshot, new Date());
113
+ // eslint-disable-next-line no-console
114
+ console.log(
115
+ `[quota-dryrun] wrote snapshot for ${states.size} provider(s) to ~/.routecodex/quota/provider-quota.json`
116
+ );
117
+ }
118
+
119
+ main().catch((error) => {
120
+ // eslint-disable-next-line no-console
121
+ console.error('[quota-dryrun] failed:', error);
122
+ process.exitCode = 1;
123
+ });
124
+
@@ -8,6 +8,7 @@ import { Readable } from 'node:stream';
8
8
  import http from 'node:http';
9
9
  import { setTimeout as delay } from 'node:timers/promises';
10
10
  import { spawnSync } from 'node:child_process';
11
+ import chalk from 'chalk';
11
12
  import { createTempConfig, startServer, stopServer } from '../lib/routecodex-runner.mjs';
12
13
  import { GeminiSemanticMapper } from '../../sharedmodule/llmswitch-core/dist/conversion/hub/semantic-mappers/gemini-mapper.js';
13
14
 
@@ -30,6 +31,8 @@ const STAGE_SUFFIX = '_req_outbound_stage2_format_build.json';
30
31
  const STAGE1_SUFFIX = '_req_outbound_stage1_semantic_map.json';
31
32
  const MOCK_PROVIDER_ID = 'mock.apply_patch.toolloop';
32
33
 
34
+ const chalkError = typeof chalk?.redBright === 'function' ? chalk.redBright : (value) => value;
35
+
33
36
  function listProcessesOnPort(port) {
34
37
  try {
35
38
  const res = spawnSync('lsof', ['-ti', `tcp:${port}`], { encoding: 'utf-8' });
@@ -702,6 +705,7 @@ async function main() {
702
705
  }
703
706
 
704
707
  main().catch((error) => {
705
- console.error(`[tool-loop] FAILED: ${error.message}`);
708
+ const msg = error instanceof Error ? (error.stack || error.message) : String(error ?? '');
709
+ console.error(chalkError(`[tool-loop] FAILED: ${msg}`));
706
710
  process.exit(1);
707
711
  });
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * exec_command TOON → JSON 回环验证(模拟 Responses 客户端)。
3
+ * exec_command JSON 形态回环验证(模拟 Responses 客户端)。
4
4
  *
5
5
  * 目标:
6
- * - 构造一条带 exec_command TOON arguments 的 chat 响应;
7
- * - 通过 llmswitch-core 的 response 工具过滤管线(ResponseToolArgumentsToonDecodeFilter)做解码;
8
- * - 使用 codex 的工具注册表 validateToolCall 校验最终 JSON 形状(必须包含 cmd,且不再暴露 toon);
9
- * - 只从“客户端视角”观察:发送/接收的都是 JSON,TOON 对客户端完全透明。
6
+ * - 构造一条带 exec_command JSON arguments 的 chat 响应;
7
+ * - 通过 llmswitch-core 的 response 工具过滤管线做统一治理;
8
+ * - 校验最终 JSON 形状(必须包含 cmd,且不暴露 toon);
9
+ * - 再通过 Responses 映射验证 /v1/responses 视图同样保持 JSON 语义。
10
10
  */
11
11
 
12
12
  import path from 'node:path';
@@ -25,9 +25,9 @@ async function main() {
25
25
  'conversion/responses/responses-openai-bridge'
26
26
  );
27
27
 
28
- // 构造一条模拟的 chat 响应,其中 exec_command 使用 TOON 编码参数。
28
+ // 构造一条模拟的 chat 响应,其中 exec_command 直接使用 JSON 编码参数。
29
29
  const chatPayload = {
30
- id: 'chatcmpl_exec_toon',
30
+ id: 'chatcmpl_exec_args',
31
31
  object: 'chat.completion',
32
32
  created: Math.floor(Date.now() / 1000),
33
33
  model: 'gpt-5.2-codex',
@@ -39,18 +39,15 @@ async function main() {
39
39
  content: null,
40
40
  tool_calls: [
41
41
  {
42
- id: 'call_exec_toon',
42
+ id: 'call_exec_args',
43
43
  type: 'function',
44
44
  function: {
45
45
  name: 'exec_command',
46
46
  arguments: JSON.stringify({
47
- toon: [
48
- 'cmd: echo 1',
49
- 'yield_time_ms: 500',
50
- 'max_output_tokens: 128',
51
- 'shell: /bin/bash',
52
- 'login: false'
53
- ].join('\n')
47
+ cmd: 'echo 1',
48
+ workdir: '.',
49
+ yield_time_ms: 500,
50
+ max_output_tokens: 128
54
51
  })
55
52
  }
56
53
  }
@@ -61,10 +58,10 @@ async function main() {
61
58
  ]
62
59
  };
63
60
 
64
- // 通过 response 工具管线运行,触发 TOON → JSON 解码。
61
+ // 通过 response 工具管线运行,触发统一的工具参数治理/归一化。
65
62
  const filtered = await runChatResponseToolFilters(chatPayload, {
66
63
  entryEndpoint: '/v1/chat/completions',
67
- requestId: 'req_exec_toon',
64
+ requestId: 'req_exec_args',
68
65
  profile: 'openai-chat'
69
66
  });
70
67
 
@@ -116,7 +113,7 @@ async function main() {
116
113
  // 延伸验证:基于 chat 结果构建 Responses payload,确保 /v1/responses 视图中的
117
114
  // function_call.arguments 同样保持 exec_command JSON 语义,而不会重新出现 toon。
118
115
  const responsesPayload = buildResponsesPayloadFromChat(filtered, {
119
- requestId: 'verify_exec_command_toon'
116
+ requestId: 'verify_exec_command_args'
120
117
  });
121
118
  const outputItems = Array.isArray(responsesPayload?.output) ? responsesPayload.output : [];
122
119
  const fnCall = outputItems.find(
@@ -153,7 +150,7 @@ async function main() {
153
150
  console.log(
154
151
  `[exec-command-loop] decoded cmd="${args.cmd}" yield_time_ms=${args.yield_time_ms ?? 'n/a'} max_output_tokens=${args.max_output_tokens ?? 'n/a'}`
155
152
  );
156
- console.log('✅ exec_command TOON decode passed (chat + responses views are JSON-only)');
153
+ console.log('✅ exec_command arguments normalization passed (chat + responses views are JSON-only)');
157
154
  }
158
155
 
159
156
  main().catch((error) => {