dsh-tap 0.20.0

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 (121) hide show
  1. package/AGENTS.md +99 -0
  2. package/CHANGELOG.md +751 -0
  3. package/LICENSE +21 -0
  4. package/README.en.md +59 -0
  5. package/README.md +256 -0
  6. package/cordis.patch.yml +385 -0
  7. package/core/bridge.js +698 -0
  8. package/core/json-store.js +91 -0
  9. package/core/rotation.js +108 -0
  10. package/core/usage-meter.js +176 -0
  11. package/docs/diagnosis-cache-quota.md +181 -0
  12. package/docs/diagnosis-qoder-flash.md +67 -0
  13. package/docs/diagnosis-trae-3003.md +302 -0
  14. package/docs/goals/bridge-port-host-split.md +91 -0
  15. package/docs/goals/desktop-adaptation.md +106 -0
  16. package/docs/goals/qoder-cn-provider-design.md +308 -0
  17. package/docs/goals/settings-card-ux-redesign-plan.md +1680 -0
  18. package/docs/goals/settings-card-ux-redesign.md +162 -0
  19. package/docs/goals/trae-agent-v3.md +41 -0
  20. package/docs/goals/trae-work-cn-repair.md +44 -0
  21. package/docs/goals/v0.8-/351/242/235/345/272/246/345/217/257/350/247/201-/346/250/241/345/236/213/345/212/250/346/200/201/345/214/226-/345/244/232/346/234/215/345/212/241/345/225/206.md +85 -0
  22. package/docs/pitfalls.md +105 -0
  23. package/docs/reverse/trae-cloud-api.md +218 -0
  24. package/docs/reverse/trae-model-catalog.md +129 -0
  25. package/docs/reverse/traework-cn.md +520 -0
  26. package/docs/rules/STATE.md +198 -0
  27. package/docs/rules/content-moderation.md +54 -0
  28. package/docs/rules/dev-role-boundary.md +94 -0
  29. package/docs/rules/extra-providers.md +42 -0
  30. package/docs/rules/gateway-facts.md +91 -0
  31. package/docs/rules/oauth-handshake.md +76 -0
  32. package/docs/rules/prompt-cache.md +93 -0
  33. package/docs/rules/quota-signals.md +125 -0
  34. package/docs/rules/routing.md +84 -0
  35. package/docs/rules/templates/oauth-reverse-checklist.md +42 -0
  36. package/docs/rules/trae-surface.md +242 -0
  37. package/docs/rules/ua-validation.md +81 -0
  38. package/host-config.js +282 -0
  39. package/index.js +2306 -0
  40. package/lib/client.js +2893 -0
  41. package/local-scan.js +104 -0
  42. package/package.json +82 -0
  43. package/providers/ark/index.js +11 -0
  44. package/providers/bailian/index.js +10 -0
  45. package/providers/bigmodel/index.js +11 -0
  46. package/providers/codebuddy/agenttool.js +122 -0
  47. package/providers/codebuddy/catalog.js +227 -0
  48. package/providers/codebuddy/errors.js +44 -0
  49. package/providers/codebuddy/headers.js +36 -0
  50. package/providers/codebuddy/images.js +125 -0
  51. package/providers/codebuddy/index.js +123 -0
  52. package/providers/codebuddy/oauth.js +279 -0
  53. package/providers/deepseek/index.js +11 -0
  54. package/providers/moonshot/index.js +11 -0
  55. package/providers/openai-compat.js +177 -0
  56. package/providers/openrouter/index.js +27 -0
  57. package/providers/qoder/catalog.js +145 -0
  58. package/providers/qoder/cosy.js +419 -0
  59. package/providers/qoder/gateway.js +563 -0
  60. package/providers/qoder/index.js +116 -0
  61. package/providers/qoder/oauth.js +364 -0
  62. package/providers/qoder/qoder_auth.wasm +0 -0
  63. package/providers/qoder/quota.js +56 -0
  64. package/providers/qwen/index.js +15 -0
  65. package/providers/tool-pairing.js +129 -0
  66. package/providers/trae/catalog.js +103 -0
  67. package/providers/trae/errors.js +85 -0
  68. package/providers/trae/gateway.js +853 -0
  69. package/providers/trae/index.js +126 -0
  70. package/providers/trae/oauth.js +443 -0
  71. package/providers/trae/quota.js +75 -0
  72. package/providers/trae/remote.js +365 -0
  73. package/scripts/capture-cache.mjs +65 -0
  74. package/scripts/capture-traffic.mjs +83 -0
  75. package/scripts/hermes-probe-dev-role.mjs +263 -0
  76. package/scripts/measure-latency.mjs +253 -0
  77. package/scripts/probe-ark-thinking.mjs +298 -0
  78. package/scripts/probe-cache-decline.mjs +292 -0
  79. package/scripts/probe-cache-ttl.mjs +221 -0
  80. package/scripts/probe-cache.mjs +156 -0
  81. package/scripts/probe-codebuddy-efforts.mjs +355 -0
  82. package/scripts/probe-codebuddy-tier-wiring.mjs +244 -0
  83. package/scripts/probe-effort-gaps.mjs +133 -0
  84. package/scripts/probe-media.mjs +115 -0
  85. package/scripts/probe-moderation.mjs +159 -0
  86. package/scripts/probe-oauth.mjs +617 -0
  87. package/scripts/probe-qoder-attribution-arm8.mjs +92 -0
  88. package/scripts/probe-qoder-attribution-arm9.mjs +102 -0
  89. package/scripts/probe-qoder-attribution.mjs +266 -0
  90. package/scripts/probe-qoder-flash-confirm.mjs +94 -0
  91. package/scripts/probe-qoder-live.mjs +344 -0
  92. package/scripts/probe-qoder-matrix.mjs +274 -0
  93. package/scripts/probe-qoder-null-content.mjs +153 -0
  94. package/scripts/probe-qoder-pairing.mjs +308 -0
  95. package/scripts/probe-qoder-quota.mjs +162 -0
  96. package/scripts/probe-qoder-thinking-config.mjs +52 -0
  97. package/scripts/probe-qoder-thinking-efforts.mjs +269 -0
  98. package/scripts/probe-quota.mjs +136 -0
  99. package/scripts/probe-quota2.mjs +144 -0
  100. package/scripts/probe-routing.mjs +226 -0
  101. package/scripts/probe-trae-3003-diagnosis.mjs +139 -0
  102. package/scripts/probe-trae-agent-v3.mjs +399 -0
  103. package/scripts/probe-trae-efforts.mjs +149 -0
  104. package/scripts/probe-trae-live.mjs +147 -0
  105. package/scripts/probe-trae-max-effort.mjs +179 -0
  106. package/scripts/probe-trae-model-routing.mjs +381 -0
  107. package/scripts/probe-trae-thinking-scene.mjs +238 -0
  108. package/scripts/probe-trae-transport-outage.mjs +176 -0
  109. package/scripts/probe-ua.mjs +508 -0
  110. package/scripts/trae-model-catalog.mjs +632 -0
  111. package/scripts/verify-agents-md.mjs +45 -0
  112. package/scripts/verify-bridge.mjs +1041 -0
  113. package/scripts/verify-core-generic.mjs +302 -0
  114. package/scripts/verify-desktop-acceptance.mjs +184 -0
  115. package/scripts/verify-host-config.mjs +265 -0
  116. package/scripts/verify-models.mjs +390 -0
  117. package/scripts/verify-providers.mjs +255 -0
  118. package/scripts/verify-qoder-provider.mjs +1020 -0
  119. package/scripts/verify-rotation.mjs +308 -0
  120. package/scripts/verify-trae-model-catalog.mjs +284 -0
  121. package/scripts/verify-trae-provider.mjs +1451 -0
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Live integration probe for the Trae channel (REAL network, own account).
4
+ *
5
+ * The offline suite (verify:trae-provider) locks translation logic against
6
+ * mocks; this script is the one manual calibration step that needs a real
7
+ * login — the request envelope / SSE grammar of the Trae cloud gateway is
8
+ * extracted from binary strings and must be confirmed against the live wire.
9
+ *
10
+ * node scripts/probe-trae-live.mjs --login
11
+ * Real OAuth device flow against api.trae.cn: prints the authorization
12
+ * URL (open it, log in), waits for the 127.0.0.1 callback, then
13
+ * immediately exercises a DeviceProof refresh to prove the self-held
14
+ * keypair works online. Tokens never print (masked only).
15
+ *
16
+ * node scripts/probe-trae-live.mjs --chat "你好"
17
+ * One-shot real chat via the gateway's request builder; dumps the RAW
18
+ * response (status/headers/body) to docs/probes/trae-chat-live-*.json
19
+ * for schema correction (no credentials in the dump).
20
+ *
21
+ * node scripts/probe-trae-live.mjs --sig raw # flip DeviceProof encoding
22
+ *
23
+ * Uses the SAME auth store as the plugin (~/.dsh/trae-plugin-auth.json,
24
+ * DSH_HOME respected) — one login serves both.
25
+ */
26
+
27
+ import { writeFileSync, mkdirSync } from 'node:fs'
28
+ import { homedir } from 'node:os'
29
+ import { join } from 'node:path'
30
+
31
+ import { createTraeOAuth } from '../providers/trae/oauth.js'
32
+ import { buildChatRequest, TRAE_APP_ID, TRAE_IDE_VERSION, TRAE_IDE_VERSION_CODE } from '../providers/trae/gateway.js'
33
+
34
+ const DSH_HOME = process.env.DSH_HOME ?? join(homedir(), '.dsh')
35
+ const AUTH_PATH = join(DSH_HOME, 'trae-plugin-auth.json')
36
+
37
+ const SETTINGS = {
38
+ traeAuthBaseURL: process.env.TRAE_AUTH_BASE ?? 'https://api.trae.cn',
39
+ traeChatBaseURL: process.env.TRAE_CHAT_BASE ?? 'https://trae-api-cn.mchost.guru',
40
+ traeLoginHost: process.env.TRAE_LOGIN_HOST ?? 'https://www.trae.cn',
41
+ }
42
+
43
+ function mask(t) {
44
+ return typeof t === 'string' && t.length > 12 ? `${t.slice(0, 6)}…${t.slice(-4)}(${t.length} 字符)` : '(none)'
45
+ }
46
+
47
+ const { readJson, writeJson } = await import('../core/json-store.js')
48
+ const oauth = createTraeOAuth({ readAuth: () => readJson(AUTH_PATH), writeAuth: (v) => writeJson(AUTH_PATH, v) })
49
+
50
+ const args = process.argv.slice(2)
51
+ const mode = args.includes('--login') ? 'login' : args.includes('--chat') ? 'chat' : args.includes('--sig') ? 'sig' : null
52
+ if (!mode) {
53
+ console.log('用法:--login(真实登录+刷新自证)| --chat "文本"(真实对话,证据落盘)| --sig der|raw(切换签名编码)')
54
+ process.exit(1)
55
+ }
56
+
57
+ if (mode === 'sig') {
58
+ const fmt = args[args.indexOf('--sig') + 1]
59
+ console.log('signatureFormat →', oauth.setSignatureFormat(fmt))
60
+ process.exit(0)
61
+ }
62
+
63
+ if (mode === 'login') {
64
+ const status0 = oauth.oauthStatus()
65
+ if (status0.signedIn) {
66
+ console.log('已有登录态:', status0.account ?? '', '令牌至', new Date(status0.accessTokenExpiresAt ?? 0).toISOString())
67
+ }
68
+ const { authUrl } = await oauth.startOAuth(SETTINGS)
69
+ console.log('\n在浏览器打开并完成登录(10 分钟内有效):\n')
70
+ console.log(authUrl, '\n')
71
+ process.stdout.write('等待回调')
72
+ for (let i = 0; i < 600; i++) {
73
+ await new Promise((r) => setTimeout(r, 1000))
74
+ process.stdout.write('.')
75
+ const s = oauth.oauthStatus()
76
+ if (s.signedIn) {
77
+ console.log('\n\n登录成功 ✓ 账号:', s.account ?? '(GetUserInfo 未返回昵称)')
78
+ console.log('accessToken:', mask(readJson(AUTH_PATH).auth?.accessToken))
79
+ console.log('refreshToken:', mask(readJson(AUTH_PATH).auth?.refreshToken))
80
+ // 立即自证刷新链路(DeviceProof 线上验签)。
81
+ const store = readJson(AUTH_PATH)
82
+ store.auth.expiresAt = Date.now() - 1000 // 强制走刷新
83
+ writeJson(AUTH_PATH, store)
84
+ const cred = await oauth.resolveTraeCredential(SETTINGS)
85
+ if (cred && readJson(AUTH_PATH).auth?.expiresAt > Date.now() + 60_000) {
86
+ console.log('DeviceProof 刷新成功 ✓(自持设备密钥线上可用)新令牌:', mask(readJson(AUTH_PATH).auth.accessToken))
87
+ } else {
88
+ console.log('DeviceProof 刷新失败 ✗ —— 尝试 `node scripts/probe-trae-live.mjs --sig raw` 后重新 --login')
89
+ }
90
+ process.exit(0)
91
+ }
92
+ if (!s.pending && s.error) {
93
+ console.log('\n\n登录失败:', s.error)
94
+ process.exit(1)
95
+ }
96
+ }
97
+ console.log('\n\n超时(10 分钟未完成回调)')
98
+ process.exit(1)
99
+ }
100
+
101
+ // mode === 'chat'
102
+ const text = args[args.indexOf('--chat') + 1] ?? '你好'
103
+ const cred = await oauth.resolveTraeCredential(SETTINGS)
104
+ if (!cred) {
105
+ console.error('未登录(先跑 --login)。')
106
+ process.exit(1)
107
+ }
108
+ const payload = { model: process.env.TRAE_MODEL ?? 'glm-5.3', stream: true, messages: [{ role: 'user', content: text }] }
109
+ const { body: reqBody, requestId } = buildChatRequest(payload, 'probe-live')
110
+ const token = String(cred.authorization).replace(/^Cloud-IDE-JWT\s+/, '')
111
+ const authStore = readJson(AUTH_PATH)
112
+ const res = await fetch(`${SETTINGS.traeChatBaseURL}/api/agent/v3/llm_utils_chat`, {
113
+ method: 'POST',
114
+ headers: {
115
+ 'Content-Type': 'application/json',
116
+ Accept: 'text/event-stream',
117
+ Authorization: `Cloud-IDE-JWT ${token}`,
118
+ 'X-Cloudide-Token': token,
119
+ 'x-ide-token': token,
120
+ 'x-app-id': TRAE_APP_ID,
121
+ 'x-ide-version': TRAE_IDE_VERSION,
122
+ 'x-ide-version-code': TRAE_IDE_VERSION_CODE,
123
+ 'x-request-id': requestId,
124
+ ...(authStore.account?.uid ? { 'x-uid': String(authStore.account.uid) } : {}),
125
+ ...(authStore.device?.deviceId ? { 'x-device-id': authStore.device.deviceId } : {}),
126
+ ...(authStore.device?.machineId ? { 'x-machine-id': authStore.device.machineId } : {}),
127
+ },
128
+ body: JSON.stringify(reqBody),
129
+ })
130
+ const raw = await res.text()
131
+ const outPath = `docs/probes/trae-chat-live-${Date.now()}.json`
132
+ mkdirSync('docs/probes', { recursive: true })
133
+ writeFileSync(outPath, JSON.stringify({
134
+ at: new Date().toISOString(),
135
+ request: { url: `${SETTINGS.traeChatBaseURL}/api/agent/v3/llm_utils_chat`, body: reqBody },
136
+ response: {
137
+ status: res.status,
138
+ contentType: res.headers.get('content-type'),
139
+ headers: Object.fromEntries([...res.headers].filter(([k]) => !k.includes('token') && !k.includes('authorization'))),
140
+ bodyHead: raw.slice(0, 8000),
141
+ bodyBytes: raw.length,
142
+ },
143
+ }, null, 2))
144
+ console.log(`HTTP ${res.status}(${res.headers.get('content-type')}),body ${raw.length} 字节`)
145
+ console.log('证据落盘 →', outPath)
146
+ console.log('\n--- body 前 1200 字节 ---')
147
+ console.log(raw.slice(0, 1200))
@@ -0,0 +1,179 @@
1
+ /**
2
+ * 探测 Trae remote 通道是否接受 reasoning_effort=max 拼写。
3
+ *
4
+ * 目录只声明了 light/high/extra_high,但上游(trae-api-cn.mchost.guru)可能
5
+ * 也接受 max(CodeBuddy 侧的拼写)。本脚本用本地存储的 Trae OAuth token,
6
+ * 向 remote 会话端点发 reasoning_effort=max 的创建请求,看是否 200 + 正常事件流。
7
+ *
8
+ * 用法:node scripts/probe-trae-max-effort.mjs
9
+ * --model deepseek-v4.1-flash (默认)
10
+ * --base https://trae-api-cn.mchost.guru
11
+ * --effort max (可重复,默认 max + extra_high 对照)
12
+ */
13
+ import { readFileSync } from 'node:fs'
14
+ import { homedir } from 'node:os'
15
+ import { join } from 'node:path'
16
+
17
+ const args = Object.fromEntries(process.argv.slice(2).reduce((acc, a, i, arr) => {
18
+ if (a.startsWith('--')) {
19
+ const k = a.slice(2)
20
+ const v = arr[i + 1]
21
+ if (v && !v.startsWith('--')) { acc.push([k, v]); arr.splice(i + 1, 1) }
22
+ else acc.push([k, 'true'])
23
+ }
24
+ return acc
25
+ }, []))
26
+
27
+ const MODEL = args.model || 'deepseek-v4.1-flash'
28
+ const BASE = args.base || 'https://trae-api-cn.mchost.guru'
29
+ const EFFORTS = args.effort
30
+ ? [args.effort].flat()
31
+ : ['max', 'extra_high'] // max = 待测;extra_high = 对照
32
+
33
+ const authPath = join(homedir(), '.dsh', 'trae-plugin-auth.json')
34
+ const authStore = JSON.parse(readFileSync(authPath, 'utf8'))
35
+ const token = authStore.auth?.accessToken
36
+ if (!token) { console.error('NO_TOKEN'); process.exit(1) }
37
+
38
+ const WEB_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36'
39
+ const headers = (stream) => ({
40
+ Authorization: `Cloud-IDE-JWT ${token}`,
41
+ 'Content-Type': 'application/json',
42
+ 'X-Trae-Client-Type': 'web',
43
+ 'X-Preferenced-Language': 'zh-CN',
44
+ 'x-user-region': 'CN',
45
+ Origin: 'https://solo.trae.cn',
46
+ Referer: 'https://solo.trae.cn/',
47
+ 'User-Agent': WEB_UA,
48
+ Accept: stream ? 'text/event-stream' : 'application/json',
49
+ })
50
+
51
+ const commonParams = JSON.stringify({
52
+ language: 'zh-cn', app_language: 'zh-CN', quality: 'stable',
53
+ app_version: '1.0.0.1229', web_id: '', user_identity: 'Free',
54
+ is_freshman: '0', biz_user_id: '', user_unique_id: '',
55
+ scope: 'marscode-cn', tenant: 'marscode', region: 'cn', aiRegion: 'cn',
56
+ is_privacy_mode: 0, privacy_mode: 'off', solo_chat_mode: 'code',
57
+ })
58
+
59
+ function buildBody(model, effort) {
60
+ const im = {
61
+ chat_session_id: '', content: [],
62
+ query: JSON.stringify([{ type: 'text', data: { content: '一个笼子里有鸡和兔共 10 只,脚共 28 只。鸡兔各几只?只给最终数字。' } }]),
63
+ model_name: model, agent_type: 'solo_agent_remote',
64
+ model_selection_strategy: 'manual', common_params: commonParams,
65
+ }
66
+ if (effort) {
67
+ im.custom_model = {
68
+ model_name: model, config_name: model, config_source: 1,
69
+ is_preset: true, use_remote_service: true, multimodal: false,
70
+ reasoning_effort: effort,
71
+ }
72
+ }
73
+ return { mode: 'code', environment_id: 'default', initial_message: im, env: 'remote', auto_create_project: false, origin: 'web' }
74
+ }
75
+
76
+ async function probe(effort) {
77
+ const label = effort || 'none(omit)'
78
+ const t0 = Date.now()
79
+ try {
80
+ // 1. create session
81
+ const createResp = await fetch(`${BASE}/api/remote/v1/chat_sessions`, {
82
+ method: 'POST', headers: headers(false),
83
+ body: JSON.stringify(buildBody(MODEL, effort)),
84
+ signal: AbortSignal.timeout(20_000),
85
+ })
86
+ const createText = await createResp.text()
87
+ if (createResp.status >= 400) {
88
+ return { effort: label, createHttp: createResp.status, createBody: createText.slice(0, 300), ms: Date.now() - t0 }
89
+ }
90
+ let data
91
+ try { data = JSON.parse(createText) } catch {
92
+ return { effort: label, createHttp: createResp.status, error: 'non-json create', ms: Date.now() - t0 }
93
+ }
94
+ if (data.code != null && data.code !== 0 && !data.data) {
95
+ return { effort: label, createHttp: createResp.status, bizCode: data.code, bizMsg: data.message, ms: Date.now() - t0 }
96
+ }
97
+ const payload = data.data ?? data
98
+ const sessionId = String(payload.chat_session_id ?? '')
99
+ const messageId = String(payload.message_id ?? '')
100
+ if (!sessionId || !messageId) {
101
+ return { effort: label, createHttp: createResp.status, error: 'missing session/message id', ms: Date.now() - t0 }
102
+ }
103
+
104
+ // 2. open events, read entire stream to EOF (like probe-trae-thinking-scene)
105
+ const eventsResp = await fetch(
106
+ `${BASE}/api/remote/v1/chat_sessions/${sessionId}/events?reply_to_message_id=${messageId}`,
107
+ { headers: headers(true) },
108
+ )
109
+ if (eventsResp.status >= 400) {
110
+ const evText = await eventsResp.text().catch(() => '')
111
+ return { effort: label, createHttp: 200, eventsHttp: eventsResp.status, eventsBody: evText.slice(0, 300), ms: Date.now() - t0 }
112
+ }
113
+ const raw = await eventsResp.text()
114
+ let reasoningChars = 0, textChars = 0, actualModel = null, finish = null, bytes = raw.length
115
+ const eventTypes = {}
116
+ let eventSamples = null
117
+ let lastEventName = null
118
+ // also capture raw SSE lines for debugging
119
+ let rawLines = []
120
+ for (const line of raw.split('\n')) {
121
+ const s = line.trim()
122
+ if (!s) { lastEventName = null; continue }
123
+ if (s.startsWith('event:')) { lastEventName = s.slice(6).trim(); continue }
124
+ if (s.startsWith('id:') || s.startsWith(':')) continue
125
+ if (!s.startsWith('data:')) continue
126
+ const dataStr = s.slice(5).trim()
127
+ if (!dataStr) continue
128
+ if (dataStr === '[DONE]') { finish = finish || 'done'; continue }
129
+ let ev
130
+ try { ev = JSON.parse(dataStr) } catch { continue }
131
+ // use .event field (production parser way) or SSE event: line
132
+ const eventName = (typeof ev.event === 'string' && ev.event) ? ev.event : lastEventName
133
+ const et = eventName || (ev.model_config ? 'model_config'
134
+ : ev.plan_item ? 'plan_item'
135
+ : ev.done ? 'done'
136
+ : ev.error ? 'error'
137
+ : 'other')
138
+ eventTypes[et] = (eventTypes[et] || 0) + 1
139
+ if (!eventSamples) eventSamples = []
140
+ if (eventSamples.length < 8) eventSamples.push({ event: eventName || null, data: dataStr.slice(0, 250) })
141
+ if (rawLines.length < 30) rawLines.push(s.slice(0, 200))
142
+ if (eventName === 'model_config' || ev.model_config) {
143
+ if (typeof ev.config_name === 'string') actualModel = ev.config_name
144
+ }
145
+ if (eventName === 'plan_item' || ev.plan_item) {
146
+ const pi = ev.plan_item || ev
147
+ if (typeof pi.reasoning_content === 'string') reasoningChars += pi.reasoning_content.length
148
+ if (typeof pi.thought === 'string') textChars += pi.thought.length
149
+ if (pi.tool_call_info?.name === 'finish') {
150
+ finish = 'finish'
151
+ const sp = pi.tool_call_info?.params?.summary
152
+ if (typeof sp === 'string') textChars += sp.length
153
+ }
154
+ }
155
+ if (eventName === 'done' || ev.done) finish = finish || 'done'
156
+ if (eventName === 'error' || ev.error) { return { effort: label, createHttp: 200, eventsHttp: 200, streamError: ev.error || ev, eventTypes, eventSamples, rawLines, ms: Date.now() - t0 } }
157
+ }
158
+ return { effort: label, createHttp: 200, eventsHttp: 200, reasoningChars, textChars, actualModel, finish, bytes, eventTypes, eventSamples, rawLines, ms: Date.now() - t0 }
159
+ } catch (e) {
160
+ return { effort: label, error: e?.message ?? String(e), ms: Date.now() - t0 }
161
+ }
162
+ }
163
+
164
+ console.log(`Trae max-effort probe: model=${MODEL} base=${BASE} efforts=${EFFORTS.join(',')}`)
165
+ const results = []
166
+ for (let i = 0; i < EFFORTS.length; i++) {
167
+ if (i > 0) { console.log(' (waiting 10s for session slot...)'); await new Promise(r => setTimeout(r, 10_000)) }
168
+ const r = await probe(EFFORTS[i])
169
+ results.push(r)
170
+ console.log(JSON.stringify(r))
171
+ }
172
+ console.log('\n--- summary ---')
173
+ for (const r of results) {
174
+ const ok = r.createHttp === 200 && r.eventsHttp === 200 && !r.streamError && !r.error
175
+ const verdict = ok
176
+ ? (r.reasoningChars > 0 ? `ACCEPTED (reasoning ${r.reasoningChars} chars)` : 'ACCEPTED (no reasoning)')
177
+ : `REJECTED (${r.error || r.bizCode || r.createHttp})`
178
+ console.log(` ${r.effort}: ${verdict}`)
179
+ }