@x-otto/cli 0.0.1-alpha.8 → 0.1.0-alpha.9

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 (145) hide show
  1. package/README.md +72 -65
  2. package/bin/otto.js +6 -0
  3. package/dist/{auth-command-CXAB3_hs.js → auth-command-WddVZWgz.js} +3 -3
  4. package/dist/auth-command-WddVZWgz.js.map +1 -0
  5. package/dist/backend-handler-DUafuNWw.js +3 -0
  6. package/dist/backend-handler-DUafuNWw.js.map +1 -0
  7. package/dist/build-id-CGJ2iv_o.js +3 -0
  8. package/dist/build-id-CGJ2iv_o.js.map +1 -0
  9. package/dist/bundled-extensions/plugin-otto-wire-protocols/plugin-dist/meta.json +1 -1
  10. package/dist/bundled-extensions/plugin-otto-wire-protocols/plugin-dist/plugin.cjs +1056 -43191
  11. package/dist/bundled-extensions/plugin-otto-wire-protocols/plugin.ts +4 -0
  12. package/dist/bundled-extensions/plugin-otto-wire-protocols/src/anthropic-messages.ts +14 -24
  13. package/dist/bundled-extensions/plugin-otto-wire-protocols/src/openai-completions.ts +27 -10
  14. package/dist/bundled-extensions/plugin-otto-wire-protocols/src/openai-responses.ts +20 -17
  15. package/dist/bundled-extensions-C3n8jg20.js +3 -0
  16. package/dist/bundled-extensions-C3n8jg20.js.map +1 -0
  17. package/dist/command-dispatcher-Du_Sk33M.js +18 -0
  18. package/dist/command-dispatcher-Du_Sk33M.js.map +1 -0
  19. package/dist/config-migration-DatflIxs.js.map +1 -1
  20. package/dist/create-host-app-DwlqiuPB.js +3 -0
  21. package/dist/create-host-app-DwlqiuPB.js.map +1 -0
  22. package/dist/{daemon-base-YOWgYYN7.js → daemon-base-D015ew5y.js} +1 -1
  23. package/dist/{daemon-base-YOWgYYN7.js.map → daemon-base-D015ew5y.js.map} +1 -1
  24. package/dist/discover-with-bundled-B5d4q___.js +2 -0
  25. package/dist/discover-with-bundled-CF8qFP41.js +3 -0
  26. package/dist/discover-with-bundled-CF8qFP41.js.map +1 -0
  27. package/dist/doctor-CPIS47uZ.js +36 -0
  28. package/dist/doctor-CPIS47uZ.js.map +1 -0
  29. package/dist/{doctor-attach-_zeIJewx.js → doctor-attach-DEoE7eFx.js} +2 -2
  30. package/dist/{doctor-attach-_zeIJewx.js.map → doctor-attach-DEoE7eFx.js.map} +1 -1
  31. package/dist/{doctor-cdp-K_mIFRfA.js → doctor-cdp-Cj9IFpEP.js} +1 -1
  32. package/dist/{doctor-cdp-K_mIFRfA.js.map → doctor-cdp-Cj9IFpEP.js.map} +1 -1
  33. package/dist/{doctor-heap-Rr2nNvcw.js → doctor-heap-SIxiRvL8.js} +2 -2
  34. package/dist/{doctor-heap-Rr2nNvcw.js.map → doctor-heap-SIxiRvL8.js.map} +1 -1
  35. package/dist/{doctor-ps-OL3FGQYu.js → doctor-ps-DNVOF6yY.js} +2 -2
  36. package/dist/{doctor-ps-OL3FGQYu.js.map → doctor-ps-DNVOF6yY.js.map} +1 -1
  37. package/dist/export-trajectory-D-BcH0Wt.js +14 -0
  38. package/dist/export-trajectory-D-BcH0Wt.js.map +1 -0
  39. package/dist/extension-CoJqqktf.js +2 -0
  40. package/dist/extension-Dpn8gltG.js +179 -0
  41. package/dist/extension-Dpn8gltG.js.map +1 -0
  42. package/dist/feedback-BivcU-y_.js +30 -0
  43. package/dist/feedback-BivcU-y_.js.map +1 -0
  44. package/dist/feedback-backend-methods-DkhBM2l6.js +3 -0
  45. package/dist/feedback-backend-methods-DkhBM2l6.js.map +1 -0
  46. package/dist/{feishu-setup-D6vnuNLw.js → feishu-setup-DbAq_BRH.js} +2 -2
  47. package/dist/feishu-setup-DbAq_BRH.js.map +1 -0
  48. package/dist/ftue-doctor-recovery-CKTMnxM8.js +3 -0
  49. package/dist/ftue-doctor-recovery-CKTMnxM8.js.map +1 -0
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +11 -13
  52. package/dist/index.js.map +1 -1
  53. package/dist/install-CEPczuet.js +11 -0
  54. package/dist/install-CEPczuet.js.map +1 -0
  55. package/dist/install-CG6b6fox.js +2 -0
  56. package/dist/interactive-schedule-DVJjhqnt.js +3 -0
  57. package/dist/interactive-schedule-DVJjhqnt.js.map +1 -0
  58. package/dist/{mcp-DbTYT7hB.js → mcp-DjReRjuu.js} +1 -1
  59. package/dist/{mcp-DbTYT7hB.js.map → mcp-DjReRjuu.js.map} +1 -1
  60. package/dist/mcp-server-Dq72PTeW.js +5 -0
  61. package/dist/mcp-server-Dq72PTeW.js.map +1 -0
  62. package/dist/{migrate-boDXhEyV.js → migrate-BBi3Txu2.js} +1 -1
  63. package/dist/{migrate-boDXhEyV.js.map → migrate-BBi3Txu2.js.map} +1 -1
  64. package/dist/observe-BqLCpmFD.js +83 -0
  65. package/dist/observe-BqLCpmFD.js.map +1 -0
  66. package/dist/parse-cli-Cp5a7taK.js +3 -0
  67. package/dist/parse-cli-Cp5a7taK.js.map +1 -0
  68. package/dist/persistence-BvLRnpfi.js +85 -0
  69. package/dist/persistence-BvLRnpfi.js.map +1 -0
  70. package/dist/plugin-assembly-check-BntT5W6H.js +3 -0
  71. package/dist/plugin-assembly-check-BntT5W6H.js.map +1 -0
  72. package/dist/{proxy-evg7ZvoD.js → proxy-CFZDVIwI.js} +1 -1
  73. package/dist/proxy-CFZDVIwI.js.map +1 -0
  74. package/dist/proxy-CkSf8ziQ.js +2 -0
  75. package/dist/{ps-DKrluEuy.js → ps-BVKv6rAo.js} +1 -1
  76. package/dist/{ps-DKrluEuy.js.map → ps-BVKv6rAo.js.map} +1 -1
  77. package/dist/{registry-D0NONjLv.js → registry-BUvu-es1.js} +4 -4
  78. package/dist/registry-BUvu-es1.js.map +1 -0
  79. package/dist/registry-DYHR-ONu.js +2 -0
  80. package/dist/{run-attach-Ci5jYyCa.js → run-attach-X35puJ_n.js} +2 -2
  81. package/dist/{run-attach-Ci5jYyCa.js.map → run-attach-X35puJ_n.js.map} +1 -1
  82. package/dist/schedule-C_uHI2M8.js +5 -0
  83. package/dist/schedule-C_uHI2M8.js.map +1 -0
  84. package/dist/serve-a4W5vFXL.js +21 -0
  85. package/dist/serve-a4W5vFXL.js.map +1 -0
  86. package/dist/{service-client-BLNeugM9.js → service-client-cfUUP4EE.js} +1 -1
  87. package/dist/{service-client-BLNeugM9.js.map → service-client-cfUUP4EE.js.map} +1 -1
  88. package/dist/{version-DaMJKLwj.js → version-D1Hmbcos.js} +2 -2
  89. package/dist/{version-DaMJKLwj.js.map → version-D1Hmbcos.js.map} +1 -1
  90. package/package.json +28 -25
  91. package/dist/auth-command-CXAB3_hs.js.map +0 -1
  92. package/dist/build-id-DMyumG97.js +0 -3
  93. package/dist/build-id-DMyumG97.js.map +0 -1
  94. package/dist/bundled-extensions/plugin-skill-inductor/otto-plugin.json +0 -8
  95. package/dist/bundled-extensions/plugin-skill-inductor/plugin-dist/meta.json +0 -1
  96. package/dist/bundled-extensions/plugin-skill-inductor/plugin-dist/plugin.cjs +0 -52781
  97. package/dist/bundled-extensions/plugin-skill-inductor/plugin.ts +0 -77
  98. package/dist/bundled-extensions/plugin-skill-inductor/src/candidate-detector.ts +0 -210
  99. package/dist/bundled-extensions/plugin-skill-inductor/src/candidate-digest.ts +0 -146
  100. package/dist/bundled-extensions/plugin-skill-inductor/src/candidate-store.ts +0 -87
  101. package/dist/bundled-extensions/plugin-skill-inductor/src/episode-store.ts +0 -122
  102. package/dist/bundled-extensions/plugin-skill-inductor/src/episode.ts +0 -125
  103. package/dist/bundled-extensions/plugin-skill-inductor/src/extraction-prompt.ts +0 -97
  104. package/dist/bundled-extensions/plugin-skill-inductor/src/inductor-service.ts +0 -183
  105. package/dist/bundled-extensions/plugin-skill-inductor/src/similarity.ts +0 -58
  106. package/dist/command-dispatcher-CR7RCzUl.js +0 -13
  107. package/dist/command-dispatcher-CR7RCzUl.js.map +0 -1
  108. package/dist/debug-CiosFwme.js +0 -34
  109. package/dist/debug-CiosFwme.js.map +0 -1
  110. package/dist/discover-with-bundled-CznLjkkw.js +0 -3
  111. package/dist/discover-with-bundled-CznLjkkw.js.map +0 -1
  112. package/dist/doctor-C84-hhEr.js +0 -30
  113. package/dist/doctor-C84-hhEr.js.map +0 -1
  114. package/dist/extension-BKXIr86F.js +0 -170
  115. package/dist/extension-BKXIr86F.js.map +0 -1
  116. package/dist/extension-CnON6Xum.js +0 -2
  117. package/dist/extension-plugin-B6WF8HzF.js +0 -2
  118. package/dist/extension-plugin-DH-4D0tt.js +0 -7
  119. package/dist/extension-plugin-DH-4D0tt.js.map +0 -1
  120. package/dist/feedback-7qQsiabD.js +0 -21
  121. package/dist/feedback-7qQsiabD.js.map +0 -1
  122. package/dist/feedback-Cc1eBsqy.js +0 -2
  123. package/dist/feishu-setup-D6vnuNLw.js.map +0 -1
  124. package/dist/install-DlVJRHRT.js +0 -11
  125. package/dist/install-DlVJRHRT.js.map +0 -1
  126. package/dist/install-WCcQ9k9M.js +0 -2
  127. package/dist/model-auth-7WCx1j-B.js +0 -2
  128. package/dist/model-auth-ylinoVLo.js +0 -3
  129. package/dist/model-auth-ylinoVLo.js.map +0 -1
  130. package/dist/observe-CVg-MRM2.js +0 -32
  131. package/dist/observe-CVg-MRM2.js.map +0 -1
  132. package/dist/parse-cli-2Cs-ZcSC.js +0 -3
  133. package/dist/parse-cli-2Cs-ZcSC.js.map +0 -1
  134. package/dist/persistenced-CCwHu1Ww.js +0 -36
  135. package/dist/persistenced-CCwHu1Ww.js.map +0 -1
  136. package/dist/proxy-BvdBsB1O.js +0 -2
  137. package/dist/proxy-evg7ZvoD.js.map +0 -1
  138. package/dist/registry-B3nfN62q.js +0 -2
  139. package/dist/registry-D0NONjLv.js.map +0 -1
  140. package/dist/remote-persistence-server-B_hONoVR.js +0 -35
  141. package/dist/remote-persistence-server-B_hONoVR.js.map +0 -1
  142. package/dist/serve-pVR6UIT9.js +0 -21
  143. package/dist/serve-pVR6UIT9.js.map +0 -1
  144. package/dist/time-travel-ZozuPPI-.js +0 -21
  145. package/dist/time-travel-ZozuPPI-.js.map +0 -1
@@ -19,6 +19,8 @@ import type { WireProtocolFactoryInput } from '@x-otto/provider'
19
19
  function adaptToOpenAICompletions(input: WireProtocolFactoryInput) {
20
20
  return createOpenAICompletionsProvider({
21
21
  resolveKey: () => input.resolveAuth().then((a) => a?.token),
22
+ resolveBaseUrl: input.resolveBaseUrl,
23
+ preferResolvedBaseUrl: input.preferResolvedBaseUrl,
22
24
  headers: input.headers,
23
25
  tokenDerivedHeaders: input.tokenDerivedHeaders,
24
26
  logger: input.logger,
@@ -32,6 +34,7 @@ function adaptToOpenAIResponses(input: WireProtocolFactoryInput) {
32
34
  return createOpenAIResponsesProvider({
33
35
  resolveKey: () => input.resolveAuth().then((a) => a?.token),
34
36
  resolveBaseUrl: input.resolveBaseUrl,
37
+ preferResolvedBaseUrl: input.preferResolvedBaseUrl,
35
38
  tokenDerivedHeaders: input.tokenDerivedHeaders,
36
39
  headers: input.headers,
37
40
  logger: input.logger,
@@ -45,6 +48,7 @@ function adaptToAnthropicMessages(input: WireProtocolFactoryInput) {
45
48
  return createAnthropicProvider({
46
49
  resolveAuth: () => input.resolveAuth(),
47
50
  resolveBaseUrl: input.resolveBaseUrl,
51
+ preferResolvedBaseUrl: input.preferResolvedBaseUrl,
48
52
  customizeRequest: input.customizeRequest,
49
53
  logger: input.logger,
50
54
  // RFC-270 缺陷 1(P0):此前这两行缺失——另两个 adapter 都透传了 headers,唯独本
@@ -41,30 +41,12 @@ import type {
41
41
  * (红线:绝不 fallback 到 createLogger)。
42
42
  */
43
43
  import type { WireProtocolLogger } from '@x-otto/provider'
44
-
45
- const noopLogger: WireProtocolLogger = { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} }
44
+ import { noopWireProtocolLogger, resolveProviderBaseUrl } from '@x-otto/provider'
46
45
 
47
46
  export type AnthropicCredentialResolver = () => Promise<string | undefined>
48
47
  export type AnthropicAuthResolver = () => Promise<ResolvedAuth | undefined>
49
48
  export type AnthropicBaseUrlResolver = () => Promise<string | undefined>
50
49
 
51
- async function resolveBaseUrl(
52
- modelBaseUrl: string,
53
- resolver?: AnthropicBaseUrlResolver,
54
- ): Promise<string> {
55
- const envUrl = process.env['ANTHROPIC_BASE_URL']
56
- if (envUrl) {
57
- return envUrl
58
- }
59
- if (resolver) {
60
- const authUrl = await resolver()
61
- if (authUrl) {
62
- return authUrl
63
- }
64
- }
65
- return modelBaseUrl
66
- }
67
-
68
50
  export function toBudgetTokens(level: string, maxOutputTokens: number): number {
69
51
  // RFC-235:Anthropic 是 token 预算型(非 effort 关键字)。otto 档位 → maxOutput 百分比预算。
70
52
  // 词表对齐 Claude Code(去 minimal 加 max):low 25% / medium 50% / high 75% / xhigh 90% / max 95%。
@@ -623,6 +605,7 @@ export interface AnthropicProviderOptions {
623
605
  resolveKey?: AnthropicCredentialResolver
624
606
  resolveAuth?: AnthropicAuthResolver
625
607
  resolveBaseUrl?: AnthropicBaseUrlResolver
608
+ preferResolvedBaseUrl?: boolean
626
609
  /**
627
610
  * 请求定制点(RFC-140 D-copilot-spoof-split,取代此前的 `enableClaudeCodeSpoof` 布尔开关):
628
611
  * `createAnthropicProvider` 是**任意**声明 `wireApi: 'anthropic-messages'` 的插件共用的
@@ -638,8 +621,8 @@ export interface AnthropicProviderOptions {
638
621
  */
639
622
  customizeRequest?: (input: AnthropicRequestCustomizeInput) => AnthropicRequestCustomization
640
623
  /**
641
- * RFC-270 缺陷 1(P0):静态额外 headers(如 manifest 声明的 `x-llm-appcode` 企业网关
642
- * 路由标识)。此前本 Options **完全没有这个字段**——`plugin.ts` 的 adapter 也因此未透传
624
+ * RFC-270 缺陷 1(P0):静态额外 headers(如 manifest 声明的企业网关路由标识头)。
625
+ * 此前本 Options **完全没有这个字段**——`plugin.ts` 的 adapter 也因此未透传
643
626
  * `input.headers`,声明式 provider 给本协议写的 headers 被静默丢弃且不报错。该缺陷长期
644
627
  * 未被发现,是因为唯一的真实用例 `anthropic-version` 恰好被 @anthropic-ai/sdk 自身硬编码
645
628
  * 补上(`client.js` buildRequest),掩盖了丢弃。
@@ -649,7 +632,7 @@ export interface AnthropicProviderOptions {
649
632
  tokenDerivedHeaders?: TokenDerivedHeader[]
650
633
  /** RFC-270 D3:manifest 显式声明后,允许自定义头覆盖鉴权头(企业网关换鉴权)。 */
651
634
  allowAuthHeaderOverride?: boolean
652
- /** RFC-185 D3:宿主注入的日志端口,缺省 no-op(见文件头 noopLogger 注释)。 */
635
+ /** RFC-185 D3:宿主注入的日志端口,缺省 no-op(`noopWireProtocolLogger`,@x-otto/provider 单例)。 */
653
636
  logger?: WireProtocolLogger
654
637
  }
655
638
 
@@ -666,10 +649,11 @@ class AnthropicStream implements ProviderStream {
666
649
  constructor(
667
650
  private readonly resolveCredential?: AnthropicCredentialResolver,
668
651
  private readonly baseUrlResolver?: AnthropicBaseUrlResolver,
652
+ private readonly preferResolvedBaseUrl = false,
669
653
  private readonly authResolver?: AnthropicAuthResolver,
670
654
  /** 见 `AnthropicProviderOptions.customizeRequest` 注释。缺省 = 零定制。 */
671
655
  private readonly customizeRequest?: (input: AnthropicRequestCustomizeInput) => AnthropicRequestCustomization,
672
- private readonly logger: WireProtocolLogger = noopLogger,
656
+ private readonly logger: WireProtocolLogger = noopWireProtocolLogger,
673
657
  /** 见 `AnthropicProviderOptions.headers`(RFC-270 缺陷 1 修复)。 */
674
658
  private readonly staticHeaders?: Record<string, string>,
675
659
  private readonly tokenDerivedHeaders?: TokenDerivedHeader[],
@@ -711,7 +695,12 @@ class AnthropicStream implements ProviderStream {
711
695
  context: StreamContext,
712
696
  auth: ResolvedAuth,
713
697
  ): Promise<{ client: Anthropic; requestParams: Anthropic.MessageStreamParams; baseURL: string }> {
714
- const baseURL = await resolveBaseUrl(model.baseUrl, this.baseUrlResolver)
698
+ const baseURL = await resolveProviderBaseUrl(
699
+ 'ANTHROPIC_BASE_URL',
700
+ this.baseUrlResolver,
701
+ model.baseUrl,
702
+ this.preferResolvedBaseUrl,
703
+ )
715
704
  // RFC-140 D-copilot-spoof-split:本文件不再判断"是否 oauth 才伪装"——那是具体厂商
716
705
  // (如 plugin-anthropic 的 Claude Code 伪装)的业务逻辑,交给 customizeRequest 定制器
717
706
  // 自行按 input.auth.mode 判断要不要返回定制内容。未提供定制器 = 零调用零开销。
@@ -906,6 +895,7 @@ export function createAnthropicProvider(options: AnthropicProviderOptions = {}):
906
895
  return new AnthropicStream(
907
896
  options.resolveKey,
908
897
  options.resolveBaseUrl,
898
+ options.preferResolvedBaseUrl,
909
899
  options.resolveAuth,
910
900
  options.customizeRequest,
911
901
  options.logger,
@@ -20,9 +20,7 @@ import type {
20
20
  WireRequestCustomization,
21
21
  WireRequestCustomizeInput,
22
22
  } from '@x-otto/provider'
23
-
24
- /** RFC-185 D3:日志走宿主注入端口,缺省 no-op——插件源码禁 createLogger(防第二单例直写 stderr)。 */
25
- const noopLogger: WireProtocolLogger = { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} }
23
+ import { noopWireProtocolLogger, resolveProviderBaseUrl } from '@x-otto/provider'
26
24
 
27
25
  /**
28
26
  * 通用 OpenAI Chat Completions provider(对标 opencode/pi 的 openai-compatible 基座)。
@@ -38,6 +36,7 @@ const noopLogger: WireProtocolLogger = { debug: () => {}, info: () => {}, warn:
38
36
  * RFC-122 D3:支持声明式 `tokenDerivedHeaders`(插件 provider 轴),JWT claim 值提取后注入请求头。
39
37
  */
40
38
  export type OpenAICompletionsCredentialResolver = (provider: string) => Promise<string | undefined>
39
+ export type OpenAICompletionsBaseUrlResolver = () => Promise<string | undefined>
41
40
 
42
41
  // RFC-122 D3:TokenDerivedHeader 定义单一真源在 @x-otto/provider(协议中立层,见该包
43
42
  // types.ts 头注释)——此前本文件与 openai-responses.ts 各自重复定义一份相同形状,
@@ -45,6 +44,8 @@ export type OpenAICompletionsCredentialResolver = (provider: string) => Promise<
45
44
 
46
45
  export interface OpenAICompletionsProviderOptions {
47
46
  resolveKey?: OpenAICompletionsCredentialResolver
47
+ resolveBaseUrl?: OpenAICompletionsBaseUrlResolver
48
+ preferResolvedBaseUrl?: boolean
48
49
  /** RFC-122 D3:静态额外 headers(如 vendor 自定义头)。 */
49
50
  headers?: Record<string, string>
50
51
  /** RFC-122 D3:JWT claim 派生 headers。 */
@@ -73,9 +74,11 @@ class OpenAICompletionsStream implements ProviderStream {
73
74
 
74
75
  constructor(
75
76
  private readonly resolveCredential?: OpenAICompletionsCredentialResolver,
77
+ private readonly baseUrlResolver?: OpenAICompletionsBaseUrlResolver,
78
+ private readonly preferResolvedBaseUrl = false,
76
79
  private readonly extraHeaders?: Record<string, string>,
77
80
  private readonly tokenDerivedHeaders?: TokenDerivedHeader[],
78
- private readonly logger: WireProtocolLogger = noopLogger,
81
+ private readonly logger: WireProtocolLogger = noopWireProtocolLogger,
79
82
  /** 见 `OpenAICompletionsProviderOptions.customizeRequest`。缺省 = 零定制。 */
80
83
  private readonly customizeRequest?: (input: WireRequestCustomizeInput) => WireRequestCustomization,
81
84
  private readonly allowAuthHeaderOverride?: boolean,
@@ -127,17 +130,29 @@ class OpenAICompletionsStream implements ProviderStream {
127
130
  })
128
131
 
129
132
  yield* streamChatCompletions({
130
- url: `${model.baseUrl}/chat/completions`,
133
+ url: `${await resolveProviderBaseUrl(
134
+ model.provider === 'deepseek' ? 'DEEPSEEK_BASE_URL' : 'OPENAI_BASE_URL',
135
+ this.baseUrlResolver,
136
+ model.baseUrl,
137
+ this.preferResolvedBaseUrl,
138
+ )}/chat/completions`,
131
139
  headers,
132
140
  body: buildChatCompletionsBody(model, context),
133
141
  identity: { api: model.api, provider: model.provider, model: model.id },
134
142
  signal,
135
143
  timeout: options.timeout,
136
- // RFC-XX reasoning-thinking:reasoning 模型的 thinking 阶段可能长达分钟级零
137
- // visible token——中间无任何 SSE chunk,默认 120s idle 会无差别单边坑所有 reasoning
138
- // 模型(实测 turn 3 触发:thinking_start 零输出 → stream.retry×2 → 崩)。
139
- // 此处给 reasoning 模型显式加大 idle 苟(300s),非 reasoning 模型保持默认 120s。
140
- idleTimeout: model.reasoning ? 300_000 : undefined,
144
+ // RFC-361:**不再**给 reasoning 模型统一放大 idleTimeout。
145
+ //
146
+ // 旧做法(`idleTimeout: model.reasoning ? 300_000 : undefined`)是"把阈值往上堆"的
147
+ // 治标手段:它同时放宽了两个语义完全不同的阶段——"首 token 前的长思考"(正常)与
148
+ // "已开始吐字后突然静默"(真卡死)。后果有二:①300s deepseek-v4-flash·max
149
+ // thinking 仍然不够,误杀照旧(真机实测 4 个长推理任务因此失败);②真卡死的检测被
150
+ // 无谓延迟到 5 分钟。
151
+ //
152
+ // 新机制由 `@x-otto/shared` 的 `stream()` 分段承担:首字节前默认 900s(放行长思考),
153
+ // 首字节后回到 120s(保持对流中断的灵敏度)。故此处不再需要按 `model.reasoning`
154
+ // 区分——分段判据比"模型是否声明 reasoning"更准(同一个 reasoning 模型的不同 turn,
155
+ // 思考时长差异极大,用模型属性预测本就是弱相关变量)。
141
156
  // RFC-232(终局修正):跨 wire 协议救回违约响应,见 @x-otto/provider stream-state.ts。
142
157
  registeredToolNames: registeredToolNames(context.tools),
143
158
  })
@@ -149,6 +164,8 @@ export function createOpenAICompletionsProvider(
149
164
  ): ProviderStream {
150
165
  return new OpenAICompletionsStream(
151
166
  options.resolveKey,
167
+ options.resolveBaseUrl,
168
+ options.preferResolvedBaseUrl,
152
169
  options.headers,
153
170
  options.tokenDerivedHeaders,
154
171
  options.logger,
@@ -34,9 +34,7 @@ import type {
34
34
  WireRequestCustomization,
35
35
  WireRequestCustomizeInput,
36
36
  } from '@x-otto/provider'
37
-
38
- /** RFC-185 D3:日志走宿主注入端口,缺省 no-op——插件源码禁 createLogger(防第二单例直写 stderr)。 */
39
- const noopLogger: WireProtocolLogger = { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} }
37
+ import { noopWireProtocolLogger, resolveProviderBaseUrl } from '@x-otto/provider'
40
38
 
41
39
  export type OpenAIResponsesCredentialResolver = () => Promise<string | undefined>
42
40
  export type OpenAIResponsesBaseUrlResolver = () => Promise<string | undefined>
@@ -318,7 +316,11 @@ function dispatchResponsesSSEEvent(
318
316
  }
319
317
  const hasTool = [...state.blocks.values()].some((b) => b.kind === 'tool_call')
320
318
  if (type === 'response.incomplete') {
321
- state.stopReason = 'max_tokens'
319
+ // 08-14 review A1:不再无条件映射 max_tokens——读 incomplete_details.reason 区分截断根因。
320
+ // OpenAI Responses 的 incomplete 可能因 max_output_tokens(真截断,走引擎续写/升档路径)
321
+ // 或 content_filter(内容过滤,应映射 refusal 让引擎干净终止,而非误走"触顶重试"白烧一轮)。
322
+ const reason = (response?.incomplete_details as Record<string, unknown> | undefined)?.reason
323
+ state.stopReason = reason === 'content_filter' ? 'refusal' : 'max_tokens'
322
324
  } else {
323
325
  state.stopReason = hasTool ? 'tool_use' : 'end_turn'
324
326
  }
@@ -359,6 +361,7 @@ class OpenAIResponsesStream implements ProviderStream {
359
361
  constructor(
360
362
  private readonly resolveCredential?: OpenAIResponsesCredentialResolver,
361
363
  private readonly baseUrlResolver?: OpenAIResponsesBaseUrlResolver,
364
+ private readonly preferResolvedBaseUrl = false,
362
365
  private readonly tokenDerivedHeaders?: TokenDerivedHeader[],
363
366
  /**
364
367
  * 终局 review 2026-07-10 P1-7:静态额外 headers(如 vendor 自定义头),对齐
@@ -369,7 +372,7 @@ class OpenAIResponsesStream implements ProviderStream {
369
372
  * 补齐此字段后硬编码分支已冗余,随本次一并删除(见下方 buildHeaders)。
370
373
  */
371
374
  private readonly staticHeaders?: Record<string, string>,
372
- private readonly logger: WireProtocolLogger = noopLogger,
375
+ private readonly logger: WireProtocolLogger = noopWireProtocolLogger,
373
376
  /** 见 `OpenAIResponsesProviderOptions.customizeRequest`。缺省 = 零定制。 */
374
377
  private readonly customizeRequest?: (input: WireRequestCustomizeInput) => WireRequestCustomization,
375
378
  private readonly allowAuthHeaderOverride?: boolean,
@@ -401,17 +404,12 @@ class OpenAIResponsesStream implements ProviderStream {
401
404
  }
402
405
 
403
406
  private async resolveBaseUrl(model: Model): Promise<string> {
404
- const envUrl = process.env['OPENAI_BASE_URL']
405
- if (envUrl) {
406
- return envUrl
407
- }
408
- if (this.baseUrlResolver) {
409
- const url = await this.baseUrlResolver()
410
- if (url) {
411
- return url
412
- }
413
- }
414
- return model.baseUrl
407
+ return resolveProviderBaseUrl(
408
+ 'OPENAI_BASE_URL',
409
+ this.baseUrlResolver,
410
+ model.baseUrl,
411
+ this.preferResolvedBaseUrl,
412
+ )
415
413
  }
416
414
 
417
415
  /**
@@ -464,7 +462,10 @@ class OpenAIResponsesStream implements ProviderStream {
464
462
  body,
465
463
  headers: this.buildHeaders(auth, model.provider),
466
464
  signal,
467
- timeout: options.timeout ?? 300000,
465
+ // RFC-361 D5:不再兜底 300s——那会让墙钟先于首字节空闲阈值(默认 900s)触发,
466
+ // 使分段计时成为死代码。`stream()` 会按 `max(requested, firstTokenIdle + 余量)`
467
+ // 统一解析,保证墙钟总是大于首字节阈值。o3/o1 等 reasoning 模型的长思考同样受益。
468
+ timeout: options.timeout,
468
469
  onResponse: (h) => {
469
470
  responseHeaders = h
470
471
  },
@@ -541,6 +542,7 @@ class OpenAIResponsesStream implements ProviderStream {
541
542
  export interface OpenAIResponsesProviderOptions {
542
543
  resolveKey?: OpenAIResponsesCredentialResolver
543
544
  resolveBaseUrl?: OpenAIResponsesBaseUrlResolver
545
+ preferResolvedBaseUrl?: boolean
544
546
  /** RFC-122 D3:插件声明式 token 派生 header(provider-loader 装载时注入) */
545
547
  tokenDerivedHeaders?: TokenDerivedHeader[]
546
548
  /**
@@ -566,6 +568,7 @@ export function createOpenAIResponsesProvider(
566
568
  return new OpenAIResponsesStream(
567
569
  options.resolveKey,
568
570
  options.resolveBaseUrl,
571
+ options.preferResolvedBaseUrl,
569
572
  options.tokenDerivedHeaders,
570
573
  options.headers,
571
574
  options.logger,
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import{dirname as e,join as t}from"node:path";import{existsSync as n}from"node:fs";import{fileURLToPath as r}from"node:url";function i(){let i=t(e(r(import.meta.url)),`bundled-extensions`);return n(i)?i:``}export{i as t};
3
+ //# sourceMappingURL=bundled-extensions-C3n8jg20.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundled-extensions-C3n8jg20.js","names":[],"sources":["../src/bundled-extensions.ts"],"sourcesContent":["/**\n * bundled-extensions.ts —— 随 otto 分发的内置扩展目录解析。\n *\n * cli 构建时(scripts/bundle-builtin-extensions.mjs)把内置扩展的编译产物拷进\n * `dist/bundled-extensions/<id>/`。本模块解析该目录,供 `discoverPlugins` 作为\n * 最高优先扫描层传入。\n *\n * ## 为什么在 cli 层解析而不是 plugin 层\n * `@x-otto/plugin` 是库,不该知道 cli 的 dist 布局(反向依赖)。目录位置只有 cli\n * 自己知道(它拥有 `import.meta.url` → dist 的路径)。\n *\n * ## 分布式的可移植性\n * 发布物 = `dist/`(package.json files:[\"dist\"])。本目录与 dist 同级,\n * 打包后相对路径关系保持不变——`import.meta.url` 解析到 dist 内的一个 js,\n * `../bundled-extensions` 即发布物内的扩展目录。\n */\nimport { existsSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\n/** cli 发布物内内置扩展目录(不存在 = 该发布物没带内置扩展,返回空)。 */\nexport function bundledExtensionsDir(): string {\n // dist 里的这个 js 文件 → dist/bundled-extensions(同目录,非父级——这是 cli 构建\n // 时拷进去的发布物内目录,见 scripts/bundle-builtin-extensions.mjs)。\n const dir = join(dirname(fileURLToPath(import.meta.url)), 'bundled-extensions')\n return existsSync(dir) ? dir : ''\n}\n"],"mappings":";4HAqBA,SAAgB,GAA+B,CAG7C,IAAM,EAAM,EAAK,EAAQ,EAAc,OAAO,KAAK,IAAI,CAAC,CAAE,qBAAqB,CAC/E,OAAO,EAAW,EAAI,CAAG,EAAM"}