@danceiny/gotry 0.0.1-rc.21 → 0.0.1-rc.24

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 (327) hide show
  1. package/README.md +102 -194
  2. package/README.zh-CN.md +115 -210
  3. package/bin/gotry-backend.js +53 -0
  4. package/bin/gotry-bootstrap.js +466 -35
  5. package/bin/gotry-inner.js +255 -36
  6. package/bin/gotry-process-liveness.d.ts +27 -0
  7. package/bin/gotry-process-liveness.js +97 -0
  8. package/bin/gotry-runtime-resolution.d.ts +1 -1
  9. package/bin/gotry-runtime-resolution.js +2 -2
  10. package/client/client.js +174 -0
  11. package/cordis.gotry-patch.yml +70 -93
  12. package/dist/capabilities/agent-reach-deep.js +45 -122
  13. package/dist/capabilities/agent-reach.js +63 -148
  14. package/dist/capabilities/anything.js +89 -75
  15. package/dist/capabilities/artifacts.js +123 -104
  16. package/dist/capabilities/channel-health.js +65 -45
  17. package/dist/capabilities/channel-registry.js +108 -87
  18. package/dist/capabilities/doctor.js +357 -235
  19. package/dist/capabilities/effect.js +223 -309
  20. package/dist/capabilities/fact-log.js +28 -14
  21. package/dist/capabilities/flyai.js +245 -156
  22. package/dist/capabilities/ground-transfer.js +509 -0
  23. package/dist/capabilities/hbcli.js +144 -148
  24. package/dist/capabilities/hotelbyte-transaction.js +194 -0
  25. package/dist/capabilities/incident-log.js +131 -61
  26. package/dist/capabilities/model-override.js +30 -10
  27. package/dist/capabilities/opensky.js +54 -43
  28. package/dist/capabilities/resilience.js +50 -32
  29. package/dist/capabilities/session/action-cache.js +50 -44
  30. package/dist/capabilities/session/adapters/ctrip-flight.js +119 -62
  31. package/dist/capabilities/session/adapters/ctrip-hotel.js +125 -110
  32. package/dist/capabilities/session/adapters/dida-portal.js +206 -0
  33. package/dist/capabilities/session/adapters/meituan-local.js +35 -54
  34. package/dist/capabilities/session/adapters/rail-12306.js +169 -112
  35. package/dist/capabilities/session/benchmark.js +85 -149
  36. package/dist/capabilities/session/extension-bridge.js +320 -259
  37. package/dist/capabilities/session/extension-channel.js +57 -76
  38. package/dist/capabilities/session/extension-distribution.js +115 -119
  39. package/dist/capabilities/session/extract.js +31 -21
  40. package/dist/capabilities/session/golden-score.js +55 -33
  41. package/dist/capabilities/session/health-watch.js +146 -154
  42. package/dist/capabilities/session/read-guard.js +55 -47
  43. package/dist/capabilities/session/static-flight-golden.js +49 -36
  44. package/dist/capabilities/session/transport.js +54 -61
  45. package/dist/capabilities/session/wizard.js +63 -93
  46. package/dist/capabilities/session-consent.js +143 -47
  47. package/dist/capabilities/session-login.js +95 -126
  48. package/dist/capabilities/session-search.js +391 -253
  49. package/dist/capabilities/sponsor-plugin.js +341 -0
  50. package/dist/capabilities/visa-policy.js +87 -73
  51. package/dist/capabilities/weather.js +163 -235
  52. package/dist/scripts/action-cache-tests.js +24 -59
  53. package/dist/scripts/agent-planning-turn-deadline-e2e.js +91 -142
  54. package/dist/scripts/agent-planning-turn-deadline-tests.js +80 -176
  55. package/dist/scripts/agent-reach-deep-tests.js +30 -40
  56. package/dist/scripts/agent-reach-tests.js +26 -18
  57. package/dist/scripts/agent-reach-wrapper-tests.js +46 -50
  58. package/dist/scripts/anything-tests.js +58 -62
  59. package/dist/scripts/artifact-client-contract-tests.js +69 -0
  60. package/dist/scripts/artifacts-capability-tests.js +235 -0
  61. package/dist/scripts/async-collect.js +26 -15
  62. package/dist/scripts/benchmark-environment-bridge-e2e.js +287 -684
  63. package/dist/scripts/benchmark-environment-bridge-tests.js +818 -3123
  64. package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +66 -279
  65. package/dist/scripts/booking-copilot-availability-policy-tests.js +153 -1257
  66. package/dist/scripts/booking-copilot-bin-proof-tests.js +22 -45
  67. package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +64 -76
  68. package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +69 -199
  69. package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +393 -759
  70. package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +33 -75
  71. package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +60 -110
  72. package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +28 -66
  73. package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +72 -271
  74. package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +73 -275
  75. package/dist/scripts/booking-copilot-runtime-proof-tests.js +784 -3398
  76. package/dist/scripts/booking-copilot-server-proof-tests.js +63 -168
  77. package/dist/scripts/booking-copilot-startup-proof-tests.js +38 -124
  78. package/dist/scripts/booking-executor-tests.js +80 -0
  79. package/dist/scripts/booking-recovery-chain-tests.js +182 -0
  80. package/dist/scripts/booking-saga-tests.js +61 -123
  81. package/dist/scripts/booking-surface-contract-proof-tests.js +152 -1167
  82. package/dist/scripts/bootstrap-tests.js +1383 -128
  83. package/dist/scripts/build-changelog.js +89 -130
  84. package/dist/scripts/build-metrics-report.js +135 -143
  85. package/dist/scripts/changelog-tests.js +35 -82
  86. package/dist/scripts/channel-probe-tests.js +44 -116
  87. package/dist/scripts/channel-probe.js +84 -167
  88. package/dist/scripts/channel-registry-tests.js +97 -185
  89. package/dist/scripts/companion-tests.js +25 -70
  90. package/dist/scripts/dida-cookie-probe.js +87 -0
  91. package/dist/scripts/dida-sw-probe.js +114 -0
  92. package/dist/scripts/diff-test.js +17 -12
  93. package/dist/scripts/doctor-tests.js +428 -120
  94. package/dist/scripts/dsh-artifact-e2e.js +150 -0
  95. package/dist/scripts/dsh-artifact-web-e2e.js +443 -0
  96. package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
  97. package/dist/scripts/dsh-runtime-closure-tests.js +110 -153
  98. package/dist/scripts/dsh-runtime-closure.js +75 -63
  99. package/dist/scripts/dsh-session-v3-migration-proof.js +106 -0
  100. package/dist/scripts/dsh-subprocess-local-proof.js +149 -0
  101. package/dist/scripts/dsh-target-closure-proof.js +178 -0
  102. package/dist/scripts/effect-tests.js +165 -552
  103. package/dist/scripts/engine-run.js +1 -3
  104. package/dist/scripts/engine-tests.js +16 -10
  105. package/dist/scripts/evaluation-cadence-tests.js +69 -242
  106. package/dist/scripts/evaluation-contract-tests.js +193 -499
  107. package/dist/scripts/extension-distribution-cli.js +19 -15
  108. package/dist/scripts/extension-distribution-tests.js +98 -211
  109. package/dist/scripts/extension-tests.js +366 -389
  110. package/dist/scripts/fact-gate-tests.js +725 -263
  111. package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
  112. package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
  113. package/dist/scripts/flight-malformed-tests.js +159 -0
  114. package/dist/scripts/flyai-tests.js +325 -68
  115. package/dist/scripts/g5-guard-tests.js +131 -0
  116. package/dist/scripts/g5-guard.js +189 -0
  117. package/dist/scripts/gen-lunar-anchors.js +106 -0
  118. package/dist/scripts/gotry-backend-kernel-tests.js +79 -0
  119. package/dist/scripts/ground-transfer-tests.js +956 -0
  120. package/dist/scripts/hbcli-e2e-tests.js +71 -90
  121. package/dist/scripts/hbcli-live-optin-tests.js +96 -0
  122. package/dist/scripts/hbcli-release-contract-tests.js +122 -0
  123. package/dist/scripts/hbcli-tests.js +41 -115
  124. package/dist/scripts/health-watch-cli.js +42 -28
  125. package/dist/scripts/hotel-date-gate-tests.js +496 -0
  126. package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
  127. package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
  128. package/dist/scripts/i18n-tests.js +16 -9
  129. package/dist/scripts/incident-tests.js +281 -87
  130. package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
  131. package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
  132. package/dist/scripts/issue-271-liveness-tests.js +547 -0
  133. package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
  134. package/dist/scripts/issue-289-model-retry-tests.js +441 -0
  135. package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
  136. package/dist/scripts/issue-338-home-city-e2e.js +577 -0
  137. package/dist/scripts/issue343-real-entry-e2e.js +160 -0
  138. package/dist/scripts/journey-tests.js +22 -27
  139. package/dist/scripts/kernel-manifest-evidence.js +109 -0
  140. package/dist/scripts/kernel-manifest-gate.js +126 -0
  141. package/dist/scripts/kernel-manifest-tests.js +212 -0
  142. package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
  143. package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
  144. package/dist/scripts/kernel-manifest.js +369 -0
  145. package/dist/scripts/ledger-repair-apply-tests.js +316 -0
  146. package/dist/scripts/ledger-repair-plan-tests.js +375 -0
  147. package/dist/scripts/ledger-tests.js +325 -340
  148. package/dist/scripts/ledger-workflow-crash.js +10 -20
  149. package/dist/scripts/map-tools-vendor-package-proof.js +206 -134
  150. package/dist/scripts/memory-capture-tests.js +242 -61
  151. package/dist/scripts/memory-decay-tests.js +18 -52
  152. package/dist/scripts/memory-lifecycle-tests.js +657 -0
  153. package/dist/scripts/memory-lifecycle.js +196 -0
  154. package/dist/scripts/memory-metrics.js +24 -12
  155. package/dist/scripts/memory-value-report.js +474 -222
  156. package/dist/scripts/memory-value-tests.js +328 -0
  157. package/dist/scripts/metrics-report-tests.js +62 -199
  158. package/dist/scripts/model-override-e2e.js +66 -105
  159. package/dist/scripts/nightly-evidence-tests.js +64 -89
  160. package/dist/scripts/nightly-evidence.js +108 -111
  161. package/dist/scripts/nudge-digest.js +39 -28
  162. package/dist/scripts/onboarding-tests.js +63 -88
  163. package/dist/scripts/opensky-check.js +18 -13
  164. package/dist/scripts/opensky-tests.js +28 -28
  165. package/dist/scripts/persona-surface-guard-tests.js +37 -12
  166. package/dist/scripts/pnpm-dsh-closure-proof.js +2 -5
  167. package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
  168. package/dist/scripts/price-drift-tests.js +126 -227
  169. package/dist/scripts/price-drift-watch.js +209 -253
  170. package/dist/scripts/probe-poi-tests.js +52 -45
  171. package/dist/scripts/product-metrics.js +187 -273
  172. package/dist/scripts/publish-preverify.js +47 -41
  173. package/dist/scripts/realtime-pricing-tests.js +37 -76
  174. package/dist/scripts/replay-async.js +20 -18
  175. package/dist/scripts/replay-real.js +16 -18
  176. package/dist/scripts/replay.js +52 -82
  177. package/dist/scripts/session-attach-diagnose.js +16 -12
  178. package/dist/scripts/session-attach-poc.js +34 -37
  179. package/dist/scripts/session-benchmark.js +66 -147
  180. package/dist/scripts/session-dida-live-e2e.js +151 -0
  181. package/dist/scripts/session-extract-tests.js +14 -35
  182. package/dist/scripts/session-login.js +14 -13
  183. package/dist/scripts/session-tests.js +545 -583
  184. package/dist/scripts/sf-live-benchmark.js +92 -110
  185. package/dist/scripts/sf-live-cli-tests.js +2 -9
  186. package/dist/scripts/sf-soft-score-tests.js +19 -60
  187. package/dist/scripts/sf-summary-tests.js +149 -0
  188. package/dist/scripts/sf-summary.js +466 -84
  189. package/dist/scripts/skeleton-check.js +16 -14
  190. package/dist/scripts/skeleton-integration-test.js +11 -10
  191. package/dist/scripts/skills-contract-tests.js +51 -46
  192. package/dist/scripts/smoke-session-gate-tests.js +12 -20
  193. package/dist/scripts/smoke.js +541 -422
  194. package/dist/scripts/sponsor-reuse-tests.js +262 -0
  195. package/dist/scripts/state-cli-tests.js +226 -101
  196. package/dist/scripts/state-cli.js +549 -212
  197. package/dist/scripts/static-golden-tests.js +46 -180
  198. package/dist/scripts/time-eval-tests.js +210 -192
  199. package/dist/scripts/travel-timeline-tests.js +29 -93
  200. package/dist/scripts/turn-handoff-collect-tests.js +46 -93
  201. package/dist/scripts/turn-handoff-collect.js +71 -99
  202. package/dist/scripts/turn-policy-tests.js +28 -29
  203. package/dist/scripts/typed-contract-canary.js +77 -162
  204. package/dist/scripts/tz-resolver-sanity.js +50 -0
  205. package/dist/scripts/tz-resolver-tests.js +584 -0
  206. package/dist/scripts/unified-tests.js +23 -19
  207. package/dist/scripts/visa-policy-tests.js +31 -44
  208. package/dist/scripts/weather-tests.js +121 -543
  209. package/dist/scripts/wish-channel-gate-tests.js +29 -86
  210. package/dist/scripts/write-gate-tests.js +522 -0
  211. package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
  212. package/dist/scripts/z3-lifecycle-tests.js +170 -0
  213. package/dist/scripts/z3-race-repeat-tests.js +39 -0
  214. package/dist/scripts/z3-race-tests.js +18 -12
  215. package/dist/src/artifact-gate.js +652 -221
  216. package/dist/src/backend/kernel.js +91 -0
  217. package/dist/src/backend/modules/booking-copilot.js +55 -0
  218. package/dist/src/backend/modules/booking-executor.js +124 -0
  219. package/dist/src/backend/modules/session-search.js +194 -0
  220. package/dist/src/benchmark-agent-conformance.js +296 -262
  221. package/dist/src/benchmark-environment-bridge.js +298 -413
  222. package/dist/src/benchmark-headless-child-diagnostics.js +93 -100
  223. package/dist/src/benchmark-tool-isolation.js +94 -67
  224. package/dist/src/bookable-facts.js +299 -174
  225. package/dist/src/booking-saga.js +64 -120
  226. package/dist/src/booking-surface/availability-policy.js +443 -576
  227. package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
  228. package/dist/src/booking-surface/contracts.js +24 -80
  229. package/dist/src/booking-surface/dsh-planner.js +510 -281
  230. package/dist/src/booking-surface/dsh-plugin.js +17 -1
  231. package/dist/src/booking-surface/error-codes.js +82 -10
  232. package/dist/src/booking-surface/index.js +0 -3
  233. package/dist/src/booking-surface/profile.js +10 -54
  234. package/dist/src/booking-surface/recovery-chain.js +367 -0
  235. package/dist/src/booking-surface/runtime.js +765 -891
  236. package/dist/src/booking-surface/server.js +200 -251
  237. package/dist/src/booking-surface/startup.js +58 -54
  238. package/dist/src/booking-surface/validation.js +150 -218
  239. package/dist/src/bridge.js +20 -13
  240. package/dist/src/companions.js +48 -74
  241. package/dist/src/contracts.js +21 -43
  242. package/dist/src/dsh-llm.js +82 -120
  243. package/dist/src/engine.js +253 -239
  244. package/dist/src/evaluation-cadence.js +72 -209
  245. package/dist/src/evaluation-contracts.js +220 -843
  246. package/dist/src/flight-pack-adapter.js +54 -0
  247. package/dist/src/flight-pack-contract.js +2 -0
  248. package/dist/src/gotry-backend.js +70 -0
  249. package/dist/src/hotel-date-gate.js +118 -0
  250. package/dist/src/i18n.js +18 -7
  251. package/dist/src/index.js +1425 -1629
  252. package/dist/src/journey.js +132 -81
  253. package/dist/src/ledger-repair-apply.js +538 -0
  254. package/dist/src/ledger-repair-plan.js +463 -0
  255. package/dist/src/loop.js +257 -166
  256. package/dist/src/memory-capture.js +180 -32
  257. package/dist/src/memory-decay.js +30 -14
  258. package/dist/src/memory-lifecycle.js +1310 -0
  259. package/dist/src/memory-utility.js +27 -40
  260. package/dist/src/mock-llm.js +36 -61
  261. package/dist/src/model.js +81 -36
  262. package/dist/src/realtime-pricing.js +63 -72
  263. package/dist/src/slot-spec.js +84 -106
  264. package/dist/src/state-ledger.js +310 -471
  265. package/dist/src/subagent-job-id-guard.js +58 -0
  266. package/dist/src/time-anchor.js +231 -33
  267. package/dist/src/tool-packet.js +20 -8
  268. package/dist/src/travel-slots.js +58 -35
  269. package/dist/src/travel-timeline.js +44 -63
  270. package/dist/src/turn-deadline.js +121 -127
  271. package/dist/src/turn-policy.js +61 -89
  272. package/dist/src/tz-resolver.js +143 -0
  273. package/dist/src/unified.js +504 -381
  274. package/dist/src/wish-pool.js +39 -14
  275. package/dist/src/write-gate.js +495 -0
  276. package/dist/src/z3-shared.js +273 -7
  277. package/extension/README.md +4 -3
  278. package/extension/background.js +125 -14
  279. package/extension/content-bridge.js +23 -1
  280. package/extension/content-main.js +15 -4
  281. package/extension/manifest.json +16 -8
  282. package/package.json +251 -220
  283. package/ts/capabilities/agent-reach.ts +4 -4
  284. package/ts/capabilities/anything.ts +1 -1
  285. package/ts/capabilities/artifacts.ts +46 -9
  286. package/ts/capabilities/flyai.ts +156 -37
  287. package/ts/capabilities/hbcli.ts +1 -1
  288. package/ts/capabilities/incident-log.ts +88 -59
  289. package/ts/capabilities/session/adapters/ctrip-flight.ts +99 -36
  290. package/ts/capabilities/session/adapters/dida-portal.ts +250 -0
  291. package/ts/capabilities/session/adapters/rail-12306.ts +123 -24
  292. package/ts/capabilities/session/extension-bridge.ts +226 -101
  293. package/ts/capabilities/session/extension-channel.ts +22 -9
  294. package/ts/capabilities/session/wizard.ts +2 -2
  295. package/ts/capabilities/session-consent.ts +98 -14
  296. package/ts/capabilities/session-login.ts +1 -0
  297. package/ts/capabilities/session-search.ts +315 -31
  298. package/ts/dsh-runtime/vendor/README.md +8 -4
  299. package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +7 -1
  300. package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +5 -3
  301. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +1 -1
  302. package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +1 -1
  303. package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +6 -6
  304. package/ts/package.json +236 -16
  305. package/ts/scripts/map-tools-vendor-package-proof.ts +152 -31
  306. package/ts/scripts/state-cli.ts +418 -51
  307. package/ts/src/artifact-gate.ts +561 -15
  308. package/ts/src/benchmark-environment-bridge.ts +37 -20
  309. package/ts/src/bookable-facts.ts +183 -7
  310. package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
  311. package/ts/src/booking-surface/dsh-planner.ts +240 -32
  312. package/ts/src/booking-surface/dsh-plugin.js +17 -1
  313. package/ts/src/booking-surface/error-codes.ts +75 -0
  314. package/ts/src/booking-surface/recovery-chain.ts +409 -0
  315. package/ts/src/booking-surface/server.ts +84 -51
  316. package/ts/src/booking-surface/validation.ts +2 -0
  317. package/ts/src/contracts.ts +12 -2
  318. package/ts/src/dsh-llm.ts +4 -8
  319. package/ts/src/index.ts +609 -90
  320. package/ts/src/loop.ts +116 -7
  321. package/ts/src/memory-capture.ts +227 -3
  322. package/ts/src/mock-llm.ts +4 -8
  323. package/ts/src/model.ts +88 -2
  324. package/ts/src/state-ledger.ts +47 -29
  325. package/ts/src/subagent-job-id-guard.ts +72 -0
  326. package/ts/src/time-anchor.ts +217 -13
  327. package/ts/src/unified.ts +316 -54
@@ -1,220 +1,233 @@
1
1
  import { accessSync, constants, lstatSync, readFileSync, statSync } from 'node:fs';
2
2
  import { getuid } from 'node:process';
3
3
  import { isAbsolute } from 'node:path';
4
- import { assertSupportedJsonSchema, validateJsonSchemaValue, ToolArgsError } from '@deepseek-ai/dsh-tools';
5
- import { validateTerminalOutputConfig } from './benchmark-agent-conformance.js';
4
+ import { assertSupportedJsonSchema, validateJsonSchemaValue, ToolArgsError, } from '@deepseek-ai/dsh-tools';
5
+ import { validateTerminalOutputConfig, } from './benchmark-agent-conformance.js';
6
+ // v4(#215 Round 12):terminal_output 携带 closed body schema(结构关键字白名单,
7
+ // 禁数据值注解面);v3 及更早配置缺该面,按契约不兼容 fail-closed。
6
8
  const SCHEMA_VERSION = 'gotry_benchmark_environment_bridge_v4';
7
9
  export const BENCHMARK_TOOL_RESULT_SCHEMA_VERSION = 'gotry_benchmark_tool_result_v1';
8
- export const BENCHMARK_DOMAIN_RECOVERIES = [
9
- 'none',
10
- 'retry_same',
11
- 'revise_arguments',
12
- 'choose_alternative'
13
- ];
10
+ export const BENCHMARK_DOMAIN_RECOVERIES = ['none', 'retry_same', 'revise_arguments', 'choose_alternative'];
14
11
  const IDENTIFIER = /^[A-Za-z][A-Za-z0-9_.-]*$/;
15
12
  const RECOVERIES = new Set(BENCHMARK_DOMAIN_RECOVERIES);
16
- const CONFIG_KEYS = [
17
- 'argv_prefix',
18
- 'cwd',
19
- 'enabled',
20
- 'executable',
21
- 'isolation',
22
- 'max_output_bytes',
23
- 'schema_version',
24
- 'terminal_output',
25
- 'timeout_ms',
26
- 'tools'
27
- ];
13
+ const CONFIG_KEYS = ['argv_prefix', 'cwd', 'enabled', 'executable', 'isolation', 'max_output_bytes', 'schema_version', 'terminal_output', 'timeout_ms', 'tools'];
28
14
  const INPUT_SCHEMA_MAX_BYTES = 16 * 1024;
29
15
  const INPUT_SCHEMA_MAX_DEPTH = 8;
30
16
  const INPUT_SCHEMA_MAX_NODES = 256;
31
17
  const INPUT_SCHEMA_MAX_PROPERTIES = 256;
32
18
  const INPUT_SCHEMA_MAX_ENUM_VALUES = 256;
33
19
  const INPUT_SCHEMA_MAX_DESCRIPTION_LENGTH = 512;
34
- const INPUT_SCHEMA_TYPES = new Set([
35
- 'string',
36
- 'number',
37
- 'integer',
38
- 'boolean',
39
- 'null',
40
- 'array',
41
- 'object'
42
- ]);
20
+ const INPUT_SCHEMA_TYPES = new Set(['string', 'number', 'integer', 'boolean', 'null', 'array', 'object']);
43
21
  function plainObject(value) {
44
22
  return typeof value === 'object' && value !== null && !Array.isArray(value);
45
23
  }
46
24
  function deepFreezeJson(value) {
47
- if (!value || typeof value !== 'object' || Object.isFrozen(value)) return value;
48
- for (const child of Object.values(value))deepFreezeJson(child);
25
+ if (!value || typeof value !== 'object' || Object.isFrozen(value))
26
+ return value;
27
+ for (const child of Object.values(value))
28
+ deepFreezeJson(child);
49
29
  return Object.freeze(value);
50
30
  }
51
31
  function existingExecutable(value) {
52
- if (typeof value !== 'string' || !isAbsolute(value)) return false;
32
+ if (typeof value !== 'string' || !isAbsolute(value))
33
+ return false;
53
34
  try {
54
35
  const stat = statSync(value);
55
36
  accessSync(value, constants.X_OK);
56
37
  return stat.isFile();
57
- } catch {
38
+ }
39
+ catch {
58
40
  return false;
59
41
  }
60
42
  }
61
43
  function existingDirectory(value) {
62
- if (typeof value !== 'string' || !isAbsolute(value)) return false;
44
+ if (typeof value !== 'string' || !isAbsolute(value))
45
+ return false;
63
46
  try {
64
47
  return statSync(value).isDirectory();
65
- } catch {
48
+ }
49
+ catch {
66
50
  return false;
67
51
  }
68
52
  }
69
53
  function identifiers(value, allowEmpty = false) {
70
- return Array.isArray(value) && (allowEmpty || value.length > 0) && value.length <= 64 && new Set(value).size === value.length && value.every((item)=>typeof item === 'string' && item.length > 0 && IDENTIFIER.test(item));
54
+ return Array.isArray(value) && (allowEmpty || value.length > 0) && value.length <= 64 && new Set(value).size === value.length && value.every(item => typeof item === 'string' && item.length > 0 && IDENTIFIER.test(item));
71
55
  }
72
56
  function exactKeys(value, allowed) {
73
- return Object.keys(value).every((key)=>allowed.includes(key));
57
+ return Object.keys(value).every(key => allowed.includes(key));
74
58
  }
75
59
  function validDescription(value, required = false) {
76
- if (value === undefined) return !required;
77
- return typeof value === 'string' && (!required || value.trim().length > 0) && value.length <= INPUT_SCHEMA_MAX_DESCRIPTION_LENGTH && !/[\u0000-\u001f\u007f]/.test(value);
60
+ if (value === undefined)
61
+ return !required;
62
+ return typeof value === 'string'
63
+ && (!required || value.trim().length > 0)
64
+ && value.length <= INPUT_SCHEMA_MAX_DESCRIPTION_LENGTH
65
+ && !/[\u0000-\u001f\u007f]/.test(value);
78
66
  }
79
67
  function scalarMatches(type, value) {
80
- if (type === 'string') return typeof value === 'string' && value.length <= 512;
81
- if (type === 'number') return typeof value === 'number' && Number.isFinite(value);
82
- if (type === 'integer') return typeof value === 'number' && Number.isSafeInteger(value);
83
- if (type === 'boolean') return typeof value === 'boolean';
68
+ if (type === 'string')
69
+ return typeof value === 'string' && value.length <= 512;
70
+ if (type === 'number')
71
+ return typeof value === 'number' && Number.isFinite(value);
72
+ if (type === 'integer')
73
+ return typeof value === 'number' && Number.isSafeInteger(value);
74
+ if (type === 'boolean')
75
+ return typeof value === 'boolean';
84
76
  return type === 'null' && value === null;
85
77
  }
86
78
  function validInputSchema(value) {
87
- if (!plainObject(value)) return false;
79
+ if (!plainObject(value))
80
+ return false;
88
81
  let serialized;
89
82
  try {
90
83
  serialized = JSON.stringify(value);
91
- } catch {
84
+ }
85
+ catch {
92
86
  return false;
93
87
  }
94
- if (Buffer.byteLength(serialized, 'utf8') > INPUT_SCHEMA_MAX_BYTES) return false;
95
- const pending = [
96
- {
97
- node: value,
98
- depth: 0
99
- }
100
- ];
88
+ if (Buffer.byteLength(serialized, 'utf8') > INPUT_SCHEMA_MAX_BYTES)
89
+ return false;
90
+ const pending = [{ node: value, depth: 0 }];
101
91
  const seen = new Set();
102
92
  let nodes = 0;
103
93
  let properties = 0;
104
94
  let enumValues = 0;
105
- while(pending.length > 0){
95
+ while (pending.length > 0) {
106
96
  const { node, depth } = pending.pop();
107
- if (seen.has(node) || ++nodes > INPUT_SCHEMA_MAX_NODES || depth > INPUT_SCHEMA_MAX_DEPTH) return false;
97
+ if (seen.has(node) || ++nodes > INPUT_SCHEMA_MAX_NODES || depth > INPUT_SCHEMA_MAX_DEPTH)
98
+ return false;
108
99
  seen.add(node);
109
- if (!INPUT_SCHEMA_TYPES.has(String(node.type)) || !validDescription(node.description)) return false;
100
+ if (!INPUT_SCHEMA_TYPES.has(String(node.type)) || !validDescription(node.description))
101
+ return false;
110
102
  if (node.type === 'object') {
111
- if (!exactKeys(node, [
112
- 'type',
113
- 'description',
114
- 'properties',
115
- 'required',
116
- 'additionalProperties'
117
- ]) || !plainObject(node.properties) || !Array.isArray(node.required) || node.additionalProperties !== false) return false;
103
+ if (!exactKeys(node, ['type', 'description', 'properties', 'required', 'additionalProperties'])
104
+ || !plainObject(node.properties)
105
+ || !Array.isArray(node.required)
106
+ || node.additionalProperties !== false)
107
+ return false;
118
108
  const propertyEntries = Object.entries(node.properties);
119
109
  properties += propertyEntries.length;
120
- if (properties > INPUT_SCHEMA_MAX_PROPERTIES || propertyEntries.some(([key, child])=>!IDENTIFIER.test(key) || !plainObject(child))) return false;
110
+ if (properties > INPUT_SCHEMA_MAX_PROPERTIES || propertyEntries.some(([key, child]) => !IDENTIFIER.test(key) || !plainObject(child)))
111
+ return false;
121
112
  const required = node.required;
122
- if (required.length > propertyEntries.length || new Set(required).size !== required.length || required.some((key)=>typeof key !== 'string' || !Object.hasOwn(node.properties, key))) return false;
123
- for (const [, child] of propertyEntries)pending.push({
124
- node: child,
125
- depth: depth + 1
126
- });
113
+ if (required.length > propertyEntries.length
114
+ || new Set(required).size !== required.length
115
+ || required.some(key => typeof key !== 'string' || !Object.hasOwn(node.properties, key)))
116
+ return false;
117
+ for (const [, child] of propertyEntries)
118
+ pending.push({ node: child, depth: depth + 1 });
127
119
  continue;
128
120
  }
129
121
  if (node.type === 'array') {
130
- if (!exactKeys(node, [
131
- 'type',
132
- 'description',
133
- 'items'
134
- ]) || !plainObject(node.items)) return false;
135
- pending.push({
136
- node: node.items,
137
- depth: depth + 1
138
- });
122
+ if (!exactKeys(node, ['type', 'description', 'items']) || !plainObject(node.items))
123
+ return false;
124
+ pending.push({ node: node.items, depth: depth + 1 });
139
125
  continue;
140
126
  }
141
- if (!exactKeys(node, [
142
- 'type',
143
- 'description',
144
- 'enum',
145
- 'const'
146
- ]) || Object.hasOwn(node, 'enum') && Object.hasOwn(node, 'const')) return false;
147
- if (Object.hasOwn(node, 'const') && !scalarMatches(String(node.type), node.const)) return false;
127
+ if (!exactKeys(node, ['type', 'description', 'enum', 'const'])
128
+ || (Object.hasOwn(node, 'enum') && Object.hasOwn(node, 'const')))
129
+ return false;
130
+ if (Object.hasOwn(node, 'const') && !scalarMatches(String(node.type), node.const))
131
+ return false;
148
132
  if (Object.hasOwn(node, 'enum')) {
149
- if (!Array.isArray(node.enum) || node.enum.length === 0 || node.enum.length > 64) return false;
133
+ if (!Array.isArray(node.enum) || node.enum.length === 0 || node.enum.length > 64)
134
+ return false;
150
135
  enumValues += node.enum.length;
151
- if (enumValues > INPUT_SCHEMA_MAX_ENUM_VALUES || new Set(node.enum).size !== node.enum.length || node.enum.some((item)=>!scalarMatches(String(node.type), item))) return false;
136
+ if (enumValues > INPUT_SCHEMA_MAX_ENUM_VALUES
137
+ || new Set(node.enum).size !== node.enum.length
138
+ || node.enum.some(item => !scalarMatches(String(node.type), item)))
139
+ return false;
152
140
  }
153
141
  }
154
142
  return value.type === 'object';
155
143
  }
156
144
  function toolDescriptors(value) {
157
- if (!Array.isArray(value) || value.length === 0 || value.length > 64) return false;
145
+ if (!Array.isArray(value) || value.length === 0 || value.length > 64)
146
+ return false;
158
147
  const names = new Set();
159
- return value.every((item)=>{
160
- if (!plainObject(item)) return false;
148
+ return value.every(item => {
149
+ if (!plainObject(item))
150
+ return false;
161
151
  const keys = Object.keys(item).sort();
162
- const required = [
163
- 'description',
164
- 'domain_outcomes',
165
- 'input_schema',
166
- 'name'
167
- ];
168
- if (JSON.stringify(keys) !== JSON.stringify([
169
- ...required,
170
- 'output_keys'
171
- ].sort())) return false;
172
- if (typeof item.name !== 'string' || !IDENTIFIER.test(item.name) || names.has(item.name)) return false;
152
+ const required = ['description', 'domain_outcomes', 'input_schema', 'name'];
153
+ if (JSON.stringify(keys) !== JSON.stringify([...required, 'output_keys'].sort()))
154
+ return false;
155
+ if (typeof item.name !== 'string' || !IDENTIFIER.test(item.name) || names.has(item.name))
156
+ return false;
173
157
  names.add(item.name);
174
- if (!validDescription(item.description, true) || !validInputSchema(item.input_schema)) return false;
175
- if (!Array.isArray(item.domain_outcomes) || item.domain_outcomes.length > 64) return false;
158
+ if (!validDescription(item.description, true) || !validInputSchema(item.input_schema))
159
+ return false;
160
+ if (!Array.isArray(item.domain_outcomes) || item.domain_outcomes.length > 64)
161
+ return false;
176
162
  const outcomeKeys = new Set();
177
- if (!item.domain_outcomes.every((outcome)=>plainObject(outcome) && JSON.stringify(Object.keys(outcome).sort()) === JSON.stringify([
178
- 'code',
179
- 'recovery',
180
- 'status'
181
- ]) && (outcome.status === 'miss' || outcome.status === 'error') && typeof outcome.code === 'string' && IDENTIFIER.test(outcome.code) && typeof outcome.recovery === 'string' && RECOVERIES.has(outcome.recovery) && !outcomeKeys.has(outcome.code) && (outcomeKeys.add(outcome.code), true))) return false;
163
+ if (!item.domain_outcomes.every(outcome => plainObject(outcome)
164
+ && JSON.stringify(Object.keys(outcome).sort()) === JSON.stringify(['code', 'recovery', 'status'])
165
+ && (outcome.status === 'miss' || outcome.status === 'error')
166
+ && typeof outcome.code === 'string' && IDENTIFIER.test(outcome.code)
167
+ && typeof outcome.recovery === 'string' && RECOVERIES.has(outcome.recovery)
168
+ && !outcomeKeys.has(outcome.code)
169
+ && (outcomeKeys.add(outcome.code), true)))
170
+ return false;
182
171
  return identifiers(item.output_keys);
183
172
  });
184
173
  }
174
+ // These are passed as direct argv entries (never through a shell), so options
175
+ // such as "-m" and dotted module names are valid. Keep the prefix bounded and
176
+ // reject line/control separators that could make diagnostics ambiguous.
185
177
  function argvPrefix(value) {
186
- return Array.isArray(value) && value.length > 0 && value.length <= 32 && value.every((item)=>typeof item === 'string' && item.length > 0 && item.length <= 256 && !item.includes('\0') && !item.includes('\r') && !item.includes('\n'));
178
+ return Array.isArray(value) && value.length > 0 && value.length <= 32 && value.every(item => typeof item === 'string'
179
+ && item.length > 0
180
+ && item.length <= 256
181
+ && !item.includes('\0')
182
+ && !item.includes('\r')
183
+ && !item.includes('\n'));
187
184
  }
188
185
  function exactIsolation(value) {
189
- if (!plainObject(value)) return false;
186
+ if (!plainObject(value))
187
+ return false;
190
188
  const keys = Object.keys(value).sort();
191
- return JSON.stringify(keys) === JSON.stringify([
192
- 'mode',
193
- 'network',
194
- 'writes'
195
- ]) && value.mode === 'host-enforced' && value.writes === 'forbidden' && value.network === 'denied';
189
+ return JSON.stringify(keys) === JSON.stringify(['mode', 'network', 'writes'])
190
+ && value.mode === 'host-enforced'
191
+ && value.writes === 'forbidden'
192
+ && value.network === 'denied';
196
193
  }
197
194
  function validBound(value, maximum) {
198
195
  return typeof value === 'number' && Number.isInteger(value) && value >= 1 && value <= maximum;
199
196
  }
200
197
  function validate(value) {
201
198
  const configKeys = plainObject(value) ? Object.keys(value).sort() : [];
202
- const baseKeys = [
203
- ...CONFIG_KEYS
204
- ].sort();
205
- if (!plainObject(value) || JSON.stringify(configKeys) !== JSON.stringify(baseKeys) || value.schema_version !== SCHEMA_VERSION || value.enabled !== true || !existingExecutable(value.executable) || !existingDirectory(value.cwd) || !argvPrefix(value.argv_prefix) || !toolDescriptors(value.tools) || !validBound(value.timeout_ms, 120_000) || !validBound(value.max_output_bytes, 10 * 1024 * 1024) || !validateTerminalOutputConfig(value.terminal_output) || !exactIsolation(value.isolation)) return false;
199
+ const baseKeys = [...CONFIG_KEYS].sort();
200
+ if (!plainObject(value)
201
+ || JSON.stringify(configKeys) !== JSON.stringify(baseKeys)
202
+ || value.schema_version !== SCHEMA_VERSION
203
+ || value.enabled !== true
204
+ || !existingExecutable(value.executable)
205
+ || !existingDirectory(value.cwd)
206
+ || !argvPrefix(value.argv_prefix)
207
+ || !toolDescriptors(value.tools)
208
+ || !validBound(value.timeout_ms, 120_000)
209
+ || !validBound(value.max_output_bytes, 10 * 1024 * 1024)
210
+ || !validateTerminalOutputConfig(value.terminal_output)
211
+ || !exactIsolation(value.isolation))
212
+ return false;
206
213
  return true;
207
214
  }
215
+ /** Read and validate an owner-local bridge config without exposing its contents. */
208
216
  export function loadBenchmarkEnvironmentConfig(path) {
209
217
  try {
210
- if (!isAbsolute(path)) return null;
218
+ if (!isAbsolute(path))
219
+ return null;
211
220
  const metadata = lstatSync(path);
212
- if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.size > 64 * 1024) return null;
213
- if (typeof getuid !== 'function' || metadata.uid !== getuid()) return null;
214
- if ((metadata.mode & 0o022) !== 0) return null;
221
+ if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.size > 64 * 1024)
222
+ return null;
223
+ if (typeof getuid !== 'function' || metadata.uid !== getuid())
224
+ return null;
225
+ if ((metadata.mode & 0o022) !== 0)
226
+ return null;
215
227
  const parsed = JSON.parse(readFileSync(path, 'utf8'));
216
228
  return validate(parsed) ? deepFreezeJson(parsed) : null;
217
- } catch {
229
+ }
230
+ catch {
218
231
  return null;
219
232
  }
220
233
  }
@@ -222,7 +235,8 @@ function structuredObject(text) {
222
235
  try {
223
236
  const parsed = JSON.parse(text.trim());
224
237
  return plainObject(parsed) ? parsed : null;
225
- } catch {
238
+ }
239
+ catch {
226
240
  return null;
227
241
  }
228
242
  }
@@ -230,123 +244,68 @@ function jsonObject(value) {
230
244
  return JSON.parse(JSON.stringify(value));
231
245
  }
232
246
  const FORBIDDEN_OUTPUT_KEY_FRAGMENTS = [
233
- 'answer',
234
- 'expected',
235
- 'gold',
236
- 'groundtruth',
237
- 'hiddenquery',
238
- 'label',
239
- 'loadermetadata',
240
- 'oracle',
241
- 'reference',
242
- 'reward',
243
- 'score'
247
+ 'answer', 'expected', 'gold', 'groundtruth', 'hiddenquery', 'label',
248
+ 'loadermetadata', 'oracle', 'reference', 'reward', 'score',
244
249
  ];
245
250
  export const BRIDGE_ERROR_CONTRACT = Object.freeze({
246
- invalid_action: {
247
- recoverable: true,
248
- remedy: 'action 只允许 tools、call errors'
249
- },
250
- disallowed_tool: {
251
- recoverable: true,
252
- remedy: '先 action=tools 列出 allowed_tools 清单,再从清单内选工具'
253
- },
254
- invalid_arguments: {
255
- recoverable: true,
256
- remedy: 'arguments 必须是可序列化 JSON 对象(≤64KB、深度≤12);收窄后重试'
257
- },
258
- timed_out: {
259
- recoverable: true,
260
- remedy: '上游超时;可原样重试一次或换其他工具'
261
- },
262
- output_truncated: {
263
- recoverable: true,
264
- remedy: '输出超出上限;让被调工具收窄查询范围后重试'
265
- },
266
- invalid_json: {
267
- recoverable: true,
268
- remedy: '上游输出不是合法 JSON;重试一次或换工具'
269
- },
270
- invalid_output: {
271
- recoverable: true,
272
- remedy: '上游输出结构不符;重试或换工具'
273
- },
274
- runner_failed: {
275
- recoverable: true,
276
- remedy: '上游进程非零退出;可重试一次,持续失败换工具'
277
- },
278
- spawn_failed: {
279
- recoverable: false,
280
- remedy: '可执行文件不可用——环境问题,调用方不可恢复'
281
- },
282
- forbidden_output: {
283
- recoverable: false,
284
- remedy: '输出含未授权键(策略边界)——不可恢复,换工具或放弃'
285
- }
251
+ invalid_action: { recoverable: true, remedy: 'action 只允许 tools、call 或 errors' },
252
+ disallowed_tool: { recoverable: true, remedy: '先 action=tools 列出 allowed_tools 清单,再从清单内选工具' },
253
+ invalid_arguments: { recoverable: true, remedy: 'arguments 必须是可序列化 JSON 对象(≤64KB、深度≤12);收窄后重试' },
254
+ timed_out: { recoverable: true, remedy: '上游超时;可原样重试一次或换其他工具' },
255
+ output_truncated: { recoverable: true, remedy: '输出超出上限;让被调工具收窄查询范围后重试' },
256
+ invalid_json: { recoverable: true, remedy: '上游输出不是合法 JSON;重试一次或换工具' },
257
+ invalid_output: { recoverable: true, remedy: '上游输出结构不符;重试或换工具' },
258
+ runner_failed: { recoverable: true, remedy: '上游进程非零退出;可重试一次,持续失败换工具' },
259
+ spawn_failed: { recoverable: false, remedy: '可执行文件不可用——环境问题,调用方不可恢复' },
260
+ forbidden_output: { recoverable: false, remedy: '输出含未授权键(策略边界)——不可恢复,换工具或放弃' },
286
261
  });
287
262
  function inspectOutput(value) {
288
- const pending = [
289
- {
290
- value,
291
- depth: 0
292
- }
293
- ];
263
+ const pending = [{ value, depth: 0 }];
294
264
  let nodes = 0;
295
- while(pending.length > 0){
265
+ while (pending.length > 0) {
296
266
  const current = pending.pop();
297
- if (++nodes > 10_000 || current.depth > 24) return 'structure_limit';
267
+ if (++nodes > 10_000 || current.depth > 24)
268
+ return 'structure_limit';
298
269
  if (Array.isArray(current.value)) {
299
- for (const child of current.value)pending.push({
300
- value: child,
301
- depth: current.depth + 1
302
- });
270
+ for (const child of current.value)
271
+ pending.push({ value: child, depth: current.depth + 1 });
303
272
  continue;
304
273
  }
305
- if (!plainObject(current.value)) continue;
306
- for (const [key, child] of Object.entries(current.value)){
307
- if (!/^[\x20-\x7E]+$/.test(key)) return 'forbidden_key';
274
+ if (!plainObject(current.value))
275
+ continue;
276
+ for (const [key, child] of Object.entries(current.value)) {
277
+ if (!/^[\x20-\x7E]+$/.test(key))
278
+ return 'forbidden_key';
308
279
  const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, '');
309
- if (FORBIDDEN_OUTPUT_KEY_FRAGMENTS.some((fragment)=>normalized.includes(fragment))) return 'forbidden_key';
310
- pending.push({
311
- value: child,
312
- depth: current.depth + 1
313
- });
280
+ if (FORBIDDEN_OUTPUT_KEY_FRAGMENTS.some(fragment => normalized.includes(fragment)))
281
+ return 'forbidden_key';
282
+ pending.push({ value: child, depth: current.depth + 1 });
314
283
  }
315
284
  }
316
285
  return 'ok';
317
286
  }
318
287
  function inspectAllowedOutput(value, allowedKeys) {
319
288
  const allowed = new Set(allowedKeys);
320
- const pending = [
321
- {
322
- value,
323
- depth: 0,
324
- coveredByDeclaredKey: false
325
- }
326
- ];
289
+ const pending = [{ value, depth: 0, coveredByDeclaredKey: false }];
327
290
  let nodes = 0;
328
- while(pending.length > 0){
291
+ while (pending.length > 0) {
329
292
  const current = pending.pop();
330
- if (++nodes > 10_000 || current.depth > 24) return false;
293
+ if (++nodes > 10_000 || current.depth > 24)
294
+ return false;
331
295
  if (Array.isArray(current.value)) {
332
- for (const child of current.value)pending.push({
333
- value: child,
334
- depth: current.depth + 1,
335
- coveredByDeclaredKey: current.coveredByDeclaredKey
336
- });
296
+ for (const child of current.value)
297
+ pending.push({ value: child, depth: current.depth + 1, coveredByDeclaredKey: current.coveredByDeclaredKey });
337
298
  continue;
338
299
  }
339
300
  if (!plainObject(current.value)) {
340
- if (!current.coveredByDeclaredKey) return false;
301
+ if (!current.coveredByDeclaredKey)
302
+ return false;
341
303
  continue;
342
304
  }
343
- for (const [key, child] of Object.entries(current.value)){
344
- if (!allowed.has(key)) return false;
345
- pending.push({
346
- value: child,
347
- depth: current.depth + 1,
348
- coveredByDeclaredKey: true
349
- });
305
+ for (const [key, child] of Object.entries(current.value)) {
306
+ if (!allowed.has(key))
307
+ return false;
308
+ pending.push({ value: child, depth: current.depth + 1, coveredByDeclaredKey: true });
350
309
  }
351
310
  }
352
311
  return true;
@@ -355,68 +314,52 @@ function serializedArguments(value) {
355
314
  const seen = new Set();
356
315
  let nodes = 0;
357
316
  let keys = 0;
358
- const inspect = (current, depth)=>{
359
- if (++nodes > 10_000 || depth > 12) return false;
317
+ const inspect = (current, depth) => {
318
+ if (++nodes > 10_000 || depth > 12)
319
+ return false;
360
320
  if (current && typeof current === 'object') {
361
- if (seen.has(current)) return false;
321
+ if (seen.has(current))
322
+ return false;
362
323
  seen.add(current);
363
- if (Array.isArray(current)) return current.every((item)=>inspect(item, depth + 1));
364
- return Object.entries(current).every(([key, child])=>{
365
- if (++keys > 10_000) return false;
324
+ if (Array.isArray(current))
325
+ return current.every(item => inspect(item, depth + 1));
326
+ return Object.entries(current).every(([key, child]) => {
327
+ if (++keys > 10_000)
328
+ return false;
366
329
  return inspect(key, depth + 1) && inspect(child, depth + 1);
367
330
  });
368
331
  }
369
332
  return true;
370
333
  };
371
- if (!inspect(value, 0)) return {
372
- reason: 'serialization_limit'
373
- };
334
+ if (!inspect(value, 0))
335
+ return { reason: 'serialization_limit' };
374
336
  try {
375
337
  const json = JSON.stringify(value);
376
- if (typeof json !== 'string' || Buffer.byteLength(json, 'utf8') > 65_536) return {
377
- reason: 'serialization_limit'
378
- };
379
- return {
380
- json
381
- };
382
- } catch {
383
- return {
384
- reason: 'serialization_limit'
385
- };
338
+ if (typeof json !== 'string' || Buffer.byteLength(json, 'utf8') > 65_536)
339
+ return { reason: 'serialization_limit' };
340
+ return { json };
341
+ }
342
+ catch {
343
+ return { reason: 'serialization_limit' };
386
344
  }
387
345
  }
346
+ /** Register the opt-in model-facing bridge. */
388
347
  export function registerBenchmarkEnvironmentBridge(path, register, subprocess) {
389
348
  const bridge = loadBenchmarkEnvironmentConfig(path);
390
- if (!bridge) throw new Error('benchmark environment bridge configuration unavailable');
391
- if (!subprocess) throw new Error('benchmark environment bridge subprocess unavailable');
349
+ if (!bridge)
350
+ throw new Error('benchmark environment bridge configuration unavailable');
351
+ if (!subprocess)
352
+ throw new Error('benchmark environment bridge subprocess unavailable');
392
353
  const parameters = deepFreezeJson({
393
354
  type: 'object',
394
355
  description: 'Flat benchmark bridge wire: choose an action, then provide the mapped tool and its arguments.',
395
356
  properties: {
396
- action: {
397
- type: 'string',
398
- enum: [
399
- 'tools',
400
- 'call',
401
- 'errors'
402
- ],
403
- description: 'tools=列出可调工具;call=执行工具;errors=拉取错误契约'
404
- },
405
- tool: {
406
- type: 'string',
407
- enum: bridge.tools.map((item)=>item.name),
408
- description: 'action=call 时选择的冻结工具名'
409
- },
410
- arguments: {
411
- type: 'object',
412
- additionalProperties: true,
413
- description: 'action=call 时传给工具的参数对象'
414
- }
357
+ action: { type: 'string', enum: ['tools', 'call', 'errors'], description: 'tools=列出可调工具;call=执行工具;errors=拉取错误契约' },
358
+ tool: { type: 'string', enum: bridge.tools.map(item => item.name), description: 'action=call 时选择的冻结工具名' },
359
+ arguments: { type: 'object', additionalProperties: true, description: 'action=call 时传给工具的参数对象' },
415
360
  },
416
- required: [
417
- 'action'
418
- ],
419
- additionalProperties: false
361
+ required: ['action'],
362
+ additionalProperties: false,
420
363
  });
421
364
  assertSupportedJsonSchema(parameters);
422
365
  const definition = {
@@ -425,206 +368,148 @@ export function registerBenchmarkEnvironmentBridge(path, register, subprocess) {
425
368
  parameters,
426
369
  output: {
427
370
  schema: {},
428
- render: (_args, value)=>[
429
- {
430
- type: 'text',
431
- text: JSON.stringify(value)
432
- }
433
- ]
371
+ render: (_args, value) => [{ type: 'text', text: JSON.stringify(value) }],
434
372
  },
435
- async execute (args) {
373
+ async execute(args) {
436
374
  const violations = validateJsonSchemaValue(parameters, args, 'arguments');
437
- if (violations.length > 0) throw new ToolArgsError(violations);
375
+ if (violations.length > 0)
376
+ throw new ToolArgsError(violations);
438
377
  const query = args;
439
378
  const tool = query.tool;
440
- const descriptor = bridge.tools.find((item)=>item.name === tool);
379
+ const descriptor = bridge.tools.find(item => item.name === tool);
441
380
  const keys = Object.keys(query);
442
381
  if (query.action === 'tools') {
443
- if (keys.length !== 1) throw new ToolArgsError([
444
- 'arguments: tools action accepts no tool or arguments'
445
- ]);
446
- return jsonObject({
447
- ok: true,
448
- tools: bridge.tools
449
- });
382
+ if (keys.length !== 1)
383
+ throw new ToolArgsError(['arguments: tools action accepts no tool or arguments']);
384
+ return jsonObject({ ok: true, tools: bridge.tools });
450
385
  }
451
386
  if (query.action === 'errors') {
452
- if (keys.length !== 1) throw new ToolArgsError([
453
- 'arguments: errors action accepts no tool or arguments'
454
- ]);
455
- return jsonObject({
456
- ok: true,
457
- errors: BRIDGE_ERROR_CONTRACT
458
- });
387
+ if (keys.length !== 1)
388
+ throw new ToolArgsError(['arguments: errors action accepts no tool or arguments']);
389
+ return jsonObject({ ok: true, errors: BRIDGE_ERROR_CONTRACT });
459
390
  }
460
391
  if (query.action !== 'call') {
461
- return jsonObject({
462
- ok: false,
463
- error: 'invalid_action'
464
- });
392
+ return jsonObject({ ok: false, error: 'invalid_action' });
465
393
  }
466
394
  if (keys.length !== 3 || !Object.hasOwn(query, 'tool') || !Object.hasOwn(query, 'arguments')) {
467
- throw new ToolArgsError([
468
- 'arguments: call action requires exactly tool and arguments'
469
- ]);
395
+ throw new ToolArgsError(['arguments: call action requires exactly tool and arguments']);
470
396
  }
471
397
  if (typeof tool !== 'string' || !descriptor) {
472
- return jsonObject({
473
- ok: false,
474
- error: 'disallowed_tool'
475
- });
398
+ return jsonObject({ ok: false, error: 'disallowed_tool' });
476
399
  }
477
400
  if (!plainObject(query.arguments)) {
478
- return jsonObject({
479
- ok: false,
480
- error: 'invalid_arguments'
481
- });
401
+ return jsonObject({ ok: false, error: 'invalid_arguments' });
482
402
  }
483
403
  const callArguments = query.arguments;
484
404
  const argumentViolations = validateJsonSchemaValue(descriptor.input_schema, callArguments, 'arguments');
485
- if (argumentViolations.length > 0) throw new ToolArgsError(argumentViolations);
405
+ if (argumentViolations.length > 0)
406
+ throw new ToolArgsError(argumentViolations);
486
407
  const serialized = serializedArguments(callArguments);
487
- if (serialized.reason) return jsonObject({
488
- ok: false,
489
- error: 'invalid_arguments',
490
- reason: serialized.reason
491
- });
408
+ if (serialized.reason)
409
+ return jsonObject({ ok: false, error: 'invalid_arguments', reason: serialized.reason });
492
410
  const controller = new AbortController();
493
- const timer = setTimeout(()=>{
411
+ const timer = setTimeout(() => {
494
412
  controller.abort();
495
413
  }, bridge.timeout_ms);
496
414
  let handle;
497
415
  try {
498
- const env = Object.fromEntries(Object.keys(process.env).map((key)=>[
499
- key,
500
- undefined
501
- ]));
502
- for (const key of [
503
- 'PATH',
504
- 'LANG',
505
- 'LC_ALL',
506
- 'LC_CTYPE',
507
- 'TZ'
508
- ]){
509
- if (process.env[key] !== undefined) env[key] = process.env[key];
416
+ const env = Object.fromEntries(Object.keys(process.env).map(key => [key, undefined]));
417
+ for (const key of ['PATH', 'LANG', 'LC_ALL', 'LC_CTYPE', 'TZ']) {
418
+ if (process.env[key] !== undefined)
419
+ env[key] = process.env[key];
510
420
  }
511
421
  env.PYTHONDONTWRITEBYTECODE = '1';
512
422
  env.PYTHONNOUSERSITE = '1';
513
- handle = subprocess.spawn({
514
- argv: [
515
- bridge.executable,
516
- ...bridge.argv_prefix,
517
- 'call',
518
- tool,
519
- serialized.json
520
- ],
521
- cwd: bridge.cwd,
522
- stdio: {
523
- stdin: 'ignore',
524
- stdout: {
525
- maxBytes: bridge.max_output_bytes
423
+ try {
424
+ handle = subprocess.spawn({
425
+ argv: [bridge.executable, ...bridge.argv_prefix, 'call', tool, serialized.json],
426
+ cwd: bridge.cwd,
427
+ stdio: {
428
+ stdin: 'ignore',
429
+ stdout: { maxBytes: bridge.max_output_bytes },
430
+ stderr: { maxBytes: bridge.max_output_bytes },
526
431
  },
527
- stderr: {
528
- maxBytes: bridge.max_output_bytes
529
- }
530
- },
531
- graceMs: Math.min(1_000, bridge.timeout_ms),
532
- env,
533
- signal: controller.signal
534
- });
535
- const status = await handle.done;
536
- if (controller.signal.aborted) return jsonObject({
537
- ok: false,
538
- error: 'timed_out'
539
- });
540
- const stdout = handle.collected.stdout.readFrom(0);
541
- const stderr = handle.collected.stderr.readFrom(0);
542
- if (stdout.lossy || stderr.lossy) return jsonObject({
543
- ok: false,
544
- error: 'output_truncated'
545
- });
432
+ graceMs: Math.min(1_000, bridge.timeout_ms),
433
+ env,
434
+ signal: controller.signal,
435
+ });
436
+ }
437
+ catch {
438
+ if (controller.signal.aborted)
439
+ return jsonObject({ ok: false, error: 'timed_out' });
440
+ return jsonObject({ ok: false, error: 'spawn_failed' });
441
+ }
442
+ let status;
443
+ try {
444
+ status = await handle.done;
445
+ }
446
+ catch {
447
+ if (controller.signal.aborted)
448
+ return jsonObject({ ok: false, error: 'timed_out' });
449
+ return jsonObject({ ok: false, error: 'spawn_failed' });
450
+ }
451
+ if (controller.signal.aborted)
452
+ return jsonObject({ ok: false, error: 'timed_out' });
453
+ let stdout;
454
+ let stderr;
455
+ try {
456
+ stdout = handle.collected.stdout.readFrom(0);
457
+ stderr = handle.collected.stderr.readFrom(0);
458
+ }
459
+ catch {
460
+ return jsonObject({
461
+ ok: false,
462
+ error: 'runner_failed',
463
+ exit_code: status.exitCode,
464
+ signal: status.signal ?? null,
465
+ });
466
+ }
467
+ if (stdout.lossy || stderr.lossy)
468
+ return jsonObject({ ok: false, error: 'output_truncated' });
546
469
  if (status.exitCode !== 0) {
547
470
  return jsonObject({
548
471
  ok: false,
549
472
  error: 'runner_failed',
550
473
  exit_code: status.exitCode,
551
- signal: status.signal ?? null
474
+ signal: status.signal ?? null,
552
475
  });
553
476
  }
554
477
  const parsed = structuredObject(stdout.text);
555
- if (!parsed) return jsonObject({
556
- ok: false,
557
- error: 'invalid_json'
558
- });
478
+ if (!parsed)
479
+ return jsonObject({ ok: false, error: 'invalid_json' });
559
480
  const outputInspection = inspectOutput(parsed);
560
- if (outputInspection === 'forbidden_key') return jsonObject({
561
- ok: false,
562
- error: 'forbidden_output'
563
- });
564
- if (outputInspection === 'structure_limit') return jsonObject({
565
- ok: false,
566
- error: 'invalid_output'
567
- });
481
+ if (outputInspection === 'forbidden_key')
482
+ return jsonObject({ ok: false, error: 'forbidden_output' });
483
+ if (outputInspection === 'structure_limit')
484
+ return jsonObject({ ok: false, error: 'invalid_output' });
568
485
  if (parsed.schema_version !== BENCHMARK_TOOL_RESULT_SCHEMA_VERSION || typeof parsed.status !== 'string') {
569
- return jsonObject({
570
- ok: false,
571
- error: 'invalid_output'
572
- });
486
+ return jsonObject({ ok: false, error: 'invalid_output' });
573
487
  }
574
488
  if (parsed.status === 'miss' || parsed.status === 'error') {
575
- if (typeof parsed.code !== 'string' || typeof parsed.recovery !== 'string' || !descriptor.domain_outcomes.some((outcome)=>outcome.status === parsed.status && outcome.code === parsed.code && outcome.recovery === parsed.recovery)) return jsonObject({
576
- ok: false,
577
- error: 'invalid_output'
578
- });
579
- if (Object.keys(parsed).sort().join(',') !== 'code,recovery,schema_version,status') return jsonObject({
580
- ok: false,
581
- error: 'invalid_output'
582
- });
583
- return jsonObject({
584
- ok: true,
585
- outcome: parsed
586
- });
489
+ if (typeof parsed.code !== 'string' || typeof parsed.recovery !== 'string' || !descriptor.domain_outcomes.some(outcome => outcome.status === parsed.status && outcome.code === parsed.code && outcome.recovery === parsed.recovery))
490
+ return jsonObject({ ok: false, error: 'invalid_output' });
491
+ if (Object.keys(parsed).sort().join(',') !== 'code,recovery,schema_version,status')
492
+ return jsonObject({ ok: false, error: 'invalid_output' });
493
+ return jsonObject({ ok: true, outcome: parsed });
587
494
  }
588
- if (parsed.status !== 'ok' || !Object.hasOwn(parsed, 'result') || Object.keys(parsed).sort().join(',') !== 'result,schema_version,status') return jsonObject({
589
- ok: false,
590
- error: 'invalid_output'
591
- });
495
+ if (parsed.status !== 'ok' || !Object.hasOwn(parsed, 'result') || Object.keys(parsed).sort().join(',') !== 'result,schema_version,status')
496
+ return jsonObject({ ok: false, error: 'invalid_output' });
592
497
  const visibleResult = parsed.result;
593
- if (!plainObject(visibleResult) && !Array.isArray(visibleResult)) return jsonObject({
594
- ok: false,
595
- error: 'invalid_output'
596
- });
597
- if (!inspectAllowedOutput(visibleResult, descriptor.output_keys)) return jsonObject({
598
- ok: false,
599
- error: 'forbidden_output'
600
- });
601
- return jsonObject({
602
- ok: true,
603
- result: visibleResult
604
- });
605
- } catch {
606
- if (controller.signal.aborted) return jsonObject({
607
- ok: false,
608
- error: 'timed_out'
609
- });
610
- return jsonObject({
611
- ok: false,
612
- error: !handle || handle.pid === -1 ? 'spawn_failed' : 'runner_failed'
613
- });
614
- } finally{
498
+ if (!plainObject(visibleResult) && !Array.isArray(visibleResult))
499
+ return jsonObject({ ok: false, error: 'invalid_output' });
500
+ if (!inspectAllowedOutput(visibleResult, descriptor.output_keys))
501
+ return jsonObject({ ok: false, error: 'forbidden_output' });
502
+ return jsonObject({ ok: true, result: visibleResult });
503
+ }
504
+ finally {
615
505
  clearTimeout(timer);
616
506
  }
617
- }
507
+ },
618
508
  };
619
509
  register(definition);
620
510
  return Object.freeze({
621
511
  toolName: 'gotry_benchmark_environment',
622
- allowedTools: Object.freeze(bridge.tools.map((item)=>item.name)),
623
- terminal: Object.freeze({
624
- ...bridge.terminal_output
625
- })
512
+ allowedTools: Object.freeze(bridge.tools.map(item => item.name)),
513
+ terminal: Object.freeze({ ...bridge.terminal_output }),
626
514
  });
627
515
  }
628
-
629
-
630
- //# sourceURL=ts/src/benchmark-environment-bridge.ts