@ai-setting/roy-agent-core 1.7.62 → 1.7.63

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 (40) hide show
  1. package/dist/env/agent/agent-component.d.ts +33 -0
  2. package/dist/env/agent/agent-component.js +4 -3
  3. package/dist/env/agent/index.js +4 -3
  4. package/dist/env/agent/types.d.ts +17 -0
  5. package/dist/env/error-reply.d.ts +53 -1
  6. package/dist/env/error-reply.js +8 -1
  7. package/dist/env/event-source/event-source-handlers.js +3 -2
  8. package/dist/env/event-source/index.js +4 -3
  9. package/dist/env/index.js +14 -13
  10. package/dist/env/llm/adapter-pi-ai.d.ts +47 -0
  11. package/dist/env/llm/index.js +3 -2
  12. package/dist/env/llm/model-fallback.d.ts +54 -0
  13. package/dist/env/llm/redact.d.ts +39 -0
  14. package/dist/env/llm/types.d.ts +13 -0
  15. package/dist/env/session/index.js +5 -4
  16. package/dist/env/session/storage/index.js +4 -3
  17. package/dist/env/task/delegate/delegate-tool.d.ts +27 -1
  18. package/dist/env/task/delegate/delegate-tool.js +1 -1
  19. package/dist/env/task/delegate/index.js +1 -1
  20. package/dist/env/task/index.js +2 -2
  21. package/dist/env/tool/types.d.ts +13 -0
  22. package/dist/env/workflow/index.js +7 -6
  23. package/dist/env/workflow/tools/index.js +6 -5
  24. package/dist/index.js +15 -14
  25. package/dist/shared/@ai-setting/{roy-agent-core-9qgrc9dy.js → roy-agent-core-03k5drfp.js} +54 -14
  26. package/dist/shared/@ai-setting/{roy-agent-core-2veqhrxa.js → roy-agent-core-1dnsk6ht.js} +2 -2
  27. package/dist/shared/@ai-setting/{roy-agent-core-vvb4cyyy.js → roy-agent-core-2cw72f16.js} +7 -2
  28. package/dist/shared/@ai-setting/{roy-agent-core-cqpd41gf.js → roy-agent-core-5hq4v41q.js} +1 -1
  29. package/dist/shared/@ai-setting/{roy-agent-core-fr296a53.js → roy-agent-core-60f7h284.js} +1 -1
  30. package/dist/shared/@ai-setting/{roy-agent-core-gz2jnaan.js → roy-agent-core-b4zdm2n4.js} +1 -1
  31. package/dist/shared/@ai-setting/{roy-agent-core-48h4zh60.js → roy-agent-core-bm3gav5d.js} +1 -1
  32. package/dist/shared/@ai-setting/{roy-agent-core-h3fwjbhp.js → roy-agent-core-ebws32tv.js} +1 -1
  33. package/dist/shared/@ai-setting/{roy-agent-core-5bj0xfjk.js → roy-agent-core-g23yp3t1.js} +298 -34
  34. package/dist/shared/@ai-setting/{roy-agent-core-w2h6xt6k.js → roy-agent-core-g6pw4esq.js} +1 -1
  35. package/dist/shared/@ai-setting/{roy-agent-core-fqandv0a.js → roy-agent-core-gnt23yx0.js} +21 -11
  36. package/dist/shared/@ai-setting/roy-agent-core-ntn32q7j.js +72 -0
  37. package/dist/shared/@ai-setting/{roy-agent-core-x1d6edkh.js → roy-agent-core-rfhxj8v2.js} +1 -1
  38. package/dist/shared/@ai-setting/{roy-agent-core-nmn853z0.js → roy-agent-core-t6nx0rsz.js} +1 -1
  39. package/dist/shared/@ai-setting/{roy-agent-core-9nvyffxg.js → roy-agent-core-va4m04e8.js} +117 -7
  40. package/package.json +1 -1
@@ -231,6 +231,19 @@ export declare class AgentComponent extends BaseComponent {
231
231
  private abortControllers;
232
232
  private defaultTools;
233
233
  private doomLoopCaches;
234
+ /**
235
+ * Task #4623 — per-run fallback state.
236
+ *
237
+ * Keyed by runId. The value is the model that the run has *already* been
238
+ * downgraded to (or the primary model, once a downgrade happened).
239
+ *
240
+ * ⚠️ Why per-run and not global: a quota exhaustion is a durable condition
241
+ * for the *current* run, but the next run may legitimately want the primary
242
+ * model again (e.g. the user fixed the key). Scoping the sticky state to a
243
+ * single run keeps the behaviour predictable and prevents a permanently
244
+ * downgraded agent.
245
+ */
246
+ private fallbackState;
234
247
  /** ConfigComponent 用于配置管理 */
235
248
  private configComponent?;
236
249
  /** 配置变更 watcher 清理函数 */
@@ -531,6 +544,26 @@ export declare class AgentComponent extends BaseComponent {
531
544
  * —— 入参 ctx 带全量消息,返回值是完整模型输出。
532
545
  */
533
546
  private invokeLLM;
547
+ /**
548
+ * Task #4623 — 解析最终的 fallback model。
549
+ *
550
+ * 优先级:AgentContext.fallbackModel(CLI `--fallback-model`)> 配置 `llm.fallbackModel`。
551
+ * 若结果只有 modelId(无 `provider/` 前缀)且配置了 `llm.fallbackProvider`,
552
+ * 则补成 `providerId/modelId`(与 `--model` 同语义)。
553
+ */
554
+ private resolveFallbackModel;
555
+ /**
556
+ * review P2-3 — 判断两个 model 标识是否"同一个模型"。
557
+ *
558
+ * `"GLM-5.3-Flash"` 与 `"tong/GLM-5.3-Flash"` 指向同一模型,仅前缀不同;
559
+ * 若按字符串严格比较会误判为"换了模型",白跑一次注定失败的 primary。
560
+ */
561
+ private static sameModel;
562
+ /**
563
+ * Task #4623 — 归一化 LLM 返回值为内部 AgentLLMOutput。
564
+ * 抽成方法以便 primary / fallback 两条路径共用同一套映射。
565
+ */
566
+ private normalizeLLMOutput;
534
567
  /**
535
568
  * Task #2723: 归一化上游 tool-call shape → 内部 `Record<string, unknown>`。
536
569
  *
@@ -7,16 +7,17 @@ import {
7
7
  filterToolsByAgent,
8
8
  parseToolList,
9
9
  toLLMMessage
10
- } from "../../shared/@ai-setting/roy-agent-core-5bj0xfjk.js";
10
+ } from "../../shared/@ai-setting/roy-agent-core-g23yp3t1.js";
11
11
  import"../../shared/@ai-setting/roy-agent-core-w2ev3z5q.js";
12
12
  import"../../shared/@ai-setting/roy-agent-core-wzp4tbsn.js";
13
13
  import"../../shared/@ai-setting/roy-agent-core-06jakebr.js";
14
14
  import"../../shared/@ai-setting/roy-agent-core-qxnbvgwe.js";
15
15
  import"../../shared/@ai-setting/roy-agent-core-p8rf4z89.js";
16
- import"../../shared/@ai-setting/roy-agent-core-x1d6edkh.js";
17
- import"../../shared/@ai-setting/roy-agent-core-9qgrc9dy.js";
16
+ import"../../shared/@ai-setting/roy-agent-core-rfhxj8v2.js";
17
+ import"../../shared/@ai-setting/roy-agent-core-03k5drfp.js";
18
18
  import"../../shared/@ai-setting/roy-agent-core-zjdzk1gg.js";
19
19
  import"../../shared/@ai-setting/roy-agent-core-c1v263jn.js";
20
+ import"../../shared/@ai-setting/roy-agent-core-ntn32q7j.js";
20
21
  import"../../shared/@ai-setting/roy-agent-core-e25xkv53.js";
21
22
  import"../../shared/@ai-setting/roy-agent-core-ctdhjv68.js";
22
23
  import"../../shared/@ai-setting/roy-agent-core-7hh0brvs.js";
@@ -4,16 +4,17 @@ import {
4
4
  import {
5
5
  AgentComponent,
6
6
  AgentComponentConfigSchema
7
- } from "../../shared/@ai-setting/roy-agent-core-5bj0xfjk.js";
7
+ } from "../../shared/@ai-setting/roy-agent-core-g23yp3t1.js";
8
8
  import"../../shared/@ai-setting/roy-agent-core-w2ev3z5q.js";
9
9
  import"../../shared/@ai-setting/roy-agent-core-wzp4tbsn.js";
10
10
  import"../../shared/@ai-setting/roy-agent-core-06jakebr.js";
11
11
  import"../../shared/@ai-setting/roy-agent-core-qxnbvgwe.js";
12
12
  import"../../shared/@ai-setting/roy-agent-core-p8rf4z89.js";
13
- import"../../shared/@ai-setting/roy-agent-core-x1d6edkh.js";
14
- import"../../shared/@ai-setting/roy-agent-core-9qgrc9dy.js";
13
+ import"../../shared/@ai-setting/roy-agent-core-rfhxj8v2.js";
14
+ import"../../shared/@ai-setting/roy-agent-core-03k5drfp.js";
15
15
  import"../../shared/@ai-setting/roy-agent-core-zjdzk1gg.js";
16
16
  import"../../shared/@ai-setting/roy-agent-core-c1v263jn.js";
17
+ import"../../shared/@ai-setting/roy-agent-core-ntn32q7j.js";
17
18
  import"../../shared/@ai-setting/roy-agent-core-e25xkv53.js";
18
19
  import"../../shared/@ai-setting/roy-agent-core-ctdhjv68.js";
19
20
  import"../../shared/@ai-setting/roy-agent-core-7hh0brvs.js";
@@ -41,6 +41,16 @@ export interface AgentContext {
41
41
  agentType?: string;
42
42
  /** 使用的模型 */
43
43
  model?: string;
44
+ /**
45
+ * Task #4623 — 兜底模型(`providerId/modelId`)。
46
+ *
47
+ * 主模型调用失败(404 / 429 / 配额耗尽 / 5xx / 超时 / 重试耗尽)时,
48
+ * 本次 run 自动切换到该模型继续执行。优先级:
49
+ * CLI `--fallback-model` > AgentContext.fallbackModel > 配置 `llm.fallbackModel`
50
+ *
51
+ * 认证失败(401/403)、请求错误(400)、主动取消、上下文超限 **不** 触发降级。
52
+ */
53
+ fallbackModel?: string;
44
54
  /** 允许的工具列表(上下文级别,会覆盖 agent 配置) */
45
55
  allowedTools?: string[];
46
56
  /** 拒绝的工具列表(上下文级别,会覆盖 agent 配置) */
@@ -224,6 +234,13 @@ export interface HookContext {
224
234
  };
225
235
  /** 执行上下文(可直接修改属性) */
226
236
  context: AgentContext;
237
+ /**
238
+ * Task #4623 — 本次 run 的唯一 ID(internal use)。
239
+ *
240
+ * 仅用于把「本次 run 已降级到 fallback model」的粘性状态 key 到 run 维度,
241
+ * 避免跨 run 串味(上一轮额度耗尽不代表下一轮还失效)。
242
+ */
243
+ _runId?: string;
227
244
  /** 是否已停止 */
228
245
  _stopped?: boolean;
229
246
  /** 停止原因 */
@@ -72,7 +72,7 @@
72
72
  *
73
73
  * 顺序即优先级:越具体的越先匹配。
74
74
  */
75
- export type ReactFailureKind = "ask_user" | "upstream_retry" | "timeout" | "upstream_aborted" | "aborted" | "context_overflow" | "unknown";
75
+ export type ReactFailureKind = "ask_user" | "quota_exhausted" | "upstream_retry" | "timeout" | "upstream_aborted" | "aborted" | "context_overflow" | "unknown";
76
76
  export interface ReactFailureInfo {
77
77
  kind: ReactFailureKind;
78
78
  /** 命中的规则 id(可观测性 / 测试锚点) */
@@ -92,6 +92,16 @@ export interface ReactFailureInfo {
92
92
  notify: boolean;
93
93
  /** 技术细节(截断,供定位;不直接展示给用户) */
94
94
  technical?: string;
95
+ /** 上游声明的恢复窗口(秒),来自 `Retry-After`。无法判定时 undefined */
96
+ retryAfterSeconds?: number;
97
+ /**
98
+ * 「你可以怎么办」的替代建议。
99
+ *
100
+ * 默认由 `retryable` 推导(true → 重发;false → 压缩/新开会话),但有些
101
+ * 失败两者都不对 —— 例如额度用尽:重发无用,压缩会话也无用,唯一出路
102
+ * 是换模型 / 充值 / 等。规则可以在这里覆盖默认文案。
103
+ */
104
+ suggestion?: string;
95
105
  }
96
106
  /** replyChannel 的最小形态(同时兼容 EnvReplyChannel / event-source ReplyChannel) */
97
107
  export interface FailureReplyChannelLike {
@@ -140,6 +150,26 @@ export interface NotifyReactFailureResult {
140
150
  /** 投递错误(sent=false 且确实尝试过时有值) */
141
151
  error?: string;
142
152
  }
153
+ /**
154
+ * 从任意错误对象里挖出 `Retry-After`(秒)。
155
+ *
156
+ * 上游把窗口放在哪一层并不统一:
157
+ * - `error.headers["retry-after"]` (openai SDK 挂在 error 上)
158
+ * - `error.responseHeaders["retry-after"]` (AI SDK APICallError)
159
+ * - `error.data.error.retry_after` (自定义 body)
160
+ * - `error.retryAfter` / `error.retry_after` (扁平形态)
161
+ *
162
+ * 也会处理 HTTP-date 形态(`Wed, 21 Oct 2026 07:28:00 GMT`),因为它合法。
163
+ * 返回值单位是秒,无法判定时返回 undefined(**绝不猜**)。
164
+ */
165
+ export declare function extractRetryAfterSeconds(error: unknown, depth?: number): number | undefined;
166
+ /**
167
+ * 把秒数说成人话("约 22 小时后恢复")。
168
+ *
169
+ * 只在**粗粒度**上承诺时间:22.008h 说成 "约 22 小时",避免给出虚假精度。
170
+ * < 90s 说明「很快」,分钟级/小时级分别取整。
171
+ */
172
+ export declare function humanizeRetryAfter(seconds: number | undefined): string | undefined;
143
173
  /** 当前生效的规则 id 列表(供可观测性 / 文档 / 测试锚定顺序)。 */
144
174
  export declare function getFailureRuleIds(): string[];
145
175
  /**
@@ -162,6 +192,28 @@ export declare function buildFailureReplyMarkdown(info: ReactFailureInfo, opts?:
162
192
  query?: string;
163
193
  sessionId?: string;
164
194
  }): string;
195
+ /**
196
+ * Task #4625:把任意失败压成**一行**给 CLI 用户看的说明。
197
+ *
198
+ * ## 为什么 CLI 需要这个(而飞书不需要)
199
+ *
200
+ * 飞书路径走 `buildFailureReplyMarkdown`,正文可以有标题/建议/技术细节。
201
+ * CLI 路径(`act.ts` catch)只有一行 `output.error("执行失败: " + message)`,
202
+ * 而 `message` 是上游原文 + AI SDK 的 `RetryError` 前缀:
203
+ *
204
+ * ✗ 执行失败: Failed after 4 attempts. Last error: Free usage limit
205
+ * reached. Free models work in OpenCode (https://opencode.ai); ...
206
+ *
207
+ * 对额度用尽这种"重试无用"的失败,这句话既没说清是什么问题,也没说
208
+ * 多久能恢复 —— 用户唯一能做的就是反复重试。
209
+ *
210
+ * ## 产出契约
211
+ *
212
+ * - **单行**、无 `\n`、长度 < 300(不进 stack)
213
+ * - 额度类失败必须带恢复窗口("约 22 小时后")
214
+ * - 其他失败退回分类规则的 `title`,保证**任何**错误都不空串
215
+ */
216
+ export declare function formatCliFailureLine(error: unknown): string;
165
217
  /** 测试用:清空去重状态 */
166
218
  export declare function __resetFailureReplyDedupForTest(): void;
167
219
  /**
@@ -2,16 +2,23 @@ import {
2
2
  __resetFailureReplyDedupForTest,
3
3
  buildFailureReplyMarkdown,
4
4
  classifyReactFailure,
5
+ extractRetryAfterSeconds,
6
+ formatCliFailureLine,
5
7
  getFailureRuleIds,
8
+ humanizeRetryAfter,
6
9
  notifyReactFailure
7
- } from "../shared/@ai-setting/roy-agent-core-9nvyffxg.js";
10
+ } from "../shared/@ai-setting/roy-agent-core-va4m04e8.js";
11
+ import"../shared/@ai-setting/roy-agent-core-ntn32q7j.js";
8
12
  import"../shared/@ai-setting/roy-agent-core-m880qz3y.js";
9
13
  import"../shared/@ai-setting/roy-agent-core-6awgfh3x.js";
10
14
  import"../shared/@ai-setting/roy-agent-core-jnszp0jq.js";
11
15
  import"../shared/@ai-setting/roy-agent-core-fs0mn2jk.js";
12
16
  export {
13
17
  notifyReactFailure,
18
+ humanizeRetryAfter,
14
19
  getFailureRuleIds,
20
+ formatCliFailureLine,
21
+ extractRetryAfterSeconds,
15
22
  classifyReactFailure,
16
23
  buildFailureReplyMarkdown,
17
24
  __resetFailureReplyDedupForTest
@@ -10,9 +10,9 @@ import {
10
10
  patchCardByLarkCli,
11
11
  renderRuleNoticeSection,
12
12
  timerHandler
13
- } from "../../shared/@ai-setting/roy-agent-core-nmn853z0.js";
13
+ } from "../../shared/@ai-setting/roy-agent-core-t6nx0rsz.js";
14
14
  import"../../shared/@ai-setting/roy-agent-core-bxkjk7zx.js";
15
- import"../../shared/@ai-setting/roy-agent-core-9nvyffxg.js";
15
+ import"../../shared/@ai-setting/roy-agent-core-va4m04e8.js";
16
16
  import {
17
17
  cliCommandHandler
18
18
  } from "../../shared/@ai-setting/roy-agent-core-p37kh0kt.js";
@@ -29,6 +29,7 @@ import"../../shared/@ai-setting/roy-agent-core-kmvw6b4v.js";
29
29
  import"../../shared/@ai-setting/roy-agent-core-p6a4v4fq.js";
30
30
  import"../../shared/@ai-setting/roy-agent-core-jrj30vq3.js";
31
31
  import"../../shared/@ai-setting/roy-agent-core-p8rf4z89.js";
32
+ import"../../shared/@ai-setting/roy-agent-core-ntn32q7j.js";
32
33
  import"../../shared/@ai-setting/roy-agent-core-e25xkv53.js";
33
34
  import"../../shared/@ai-setting/roy-agent-core-m880qz3y.js";
34
35
  import"../../shared/@ai-setting/roy-agent-core-az13yzmc.js";
@@ -1,7 +1,7 @@
1
1
  import"../../shared/@ai-setting/roy-agent-core-5enxfga2.js";
2
2
  import {
3
3
  EventSourceComponent
4
- } from "../../shared/@ai-setting/roy-agent-core-w2h6xt6k.js";
4
+ } from "../../shared/@ai-setting/roy-agent-core-g6pw4esq.js";
5
5
  import {
6
6
  BountyIMInstance,
7
7
  TimerInstance,
@@ -13,7 +13,7 @@ import {
13
13
  larkCliHandler,
14
14
  resolveBountyIMSystemPrompt,
15
15
  timerHandler
16
- } from "../../shared/@ai-setting/roy-agent-core-nmn853z0.js";
16
+ } from "../../shared/@ai-setting/roy-agent-core-t6nx0rsz.js";
17
17
  import {
18
18
  BUILT_IN_EVENT_SOURCE_TYPES,
19
19
  BUILT_IN_EVENT_SOURCE_TYPE_LIST,
@@ -23,7 +23,7 @@ import {
23
23
  isValidEventSourceType,
24
24
  validateEventSourceConfig
25
25
  } from "../../shared/@ai-setting/roy-agent-core-bxkjk7zx.js";
26
- import"../../shared/@ai-setting/roy-agent-core-9nvyffxg.js";
26
+ import"../../shared/@ai-setting/roy-agent-core-va4m04e8.js";
27
27
  import {
28
28
  CLI_COMMAND_ES_TYPE,
29
29
  CLI_SESSION_CHAT_ID_PREFIX,
@@ -49,6 +49,7 @@ import"../../shared/@ai-setting/roy-agent-core-kmvw6b4v.js";
49
49
  import"../../shared/@ai-setting/roy-agent-core-p6a4v4fq.js";
50
50
  import"../../shared/@ai-setting/roy-agent-core-jrj30vq3.js";
51
51
  import"../../shared/@ai-setting/roy-agent-core-p8rf4z89.js";
52
+ import"../../shared/@ai-setting/roy-agent-core-ntn32q7j.js";
52
53
  import"../../shared/@ai-setting/roy-agent-core-e25xkv53.js";
53
54
  import"../../shared/@ai-setting/roy-agent-core-m880qz3y.js";
54
55
  import"../../shared/@ai-setting/roy-agent-core-az13yzmc.js";
package/dist/env/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  BaseEnvironment
3
- } from "../shared/@ai-setting/roy-agent-core-gz2jnaan.js";
3
+ } from "../shared/@ai-setting/roy-agent-core-b4zdm2n4.js";
4
4
  import {
5
5
  TaskComponent
6
- } from "../shared/@ai-setting/roy-agent-core-cqpd41gf.js";
6
+ } from "../shared/@ai-setting/roy-agent-core-5hq4v41q.js";
7
7
  import"../shared/@ai-setting/roy-agent-core-amkqnyzy.js";
8
8
  import"../shared/@ai-setting/roy-agent-core-jm136jzh.js";
9
- import"../shared/@ai-setting/roy-agent-core-fqandv0a.js";
9
+ import"../shared/@ai-setting/roy-agent-core-gnt23yx0.js";
10
10
  import"../shared/@ai-setting/roy-agent-core-jw63js86.js";
11
11
  import"../shared/@ai-setting/roy-agent-core-5mrynf9g.js";
12
12
  import"../shared/@ai-setting/roy-agent-core-8gxth0eh.js";
@@ -14,11 +14,11 @@ import"../shared/@ai-setting/roy-agent-core-as0dd70m.js";
14
14
  import"../shared/@ai-setting/roy-agent-core-rn57psp0.js";
15
15
  import {
16
16
  EventSourceComponent
17
- } from "../shared/@ai-setting/roy-agent-core-w2h6xt6k.js";
17
+ } from "../shared/@ai-setting/roy-agent-core-g6pw4esq.js";
18
18
  import {
19
19
  builtInHandlers,
20
20
  larkCliHandler
21
- } from "../shared/@ai-setting/roy-agent-core-nmn853z0.js";
21
+ } from "../shared/@ai-setting/roy-agent-core-t6nx0rsz.js";
22
22
  import {
23
23
  EventSourceInitHooks,
24
24
  getDefaultConfigForType,
@@ -26,7 +26,7 @@ import {
26
26
  isValidEventSourceType,
27
27
  validateEventSourceConfig
28
28
  } from "../shared/@ai-setting/roy-agent-core-bxkjk7zx.js";
29
- import"../shared/@ai-setting/roy-agent-core-9nvyffxg.js";
29
+ import"../shared/@ai-setting/roy-agent-core-va4m04e8.js";
30
30
  import"../shared/@ai-setting/roy-agent-core-p37kh0kt.js";
31
31
  import"../shared/@ai-setting/roy-agent-core-anb53t3v.js";
32
32
  import"../shared/@ai-setting/roy-agent-core-ye4bfzqg.js";
@@ -38,13 +38,13 @@ import"../shared/@ai-setting/roy-agent-core-cxpgd415.js";
38
38
  import"../shared/@ai-setting/roy-agent-core-m3w3ytxt.js";
39
39
  import {
40
40
  WorkflowComponent
41
- } from "../shared/@ai-setting/roy-agent-core-h3fwjbhp.js";
41
+ } from "../shared/@ai-setting/roy-agent-core-ebws32tv.js";
42
42
  import"../shared/@ai-setting/roy-agent-core-dfqhc9ey.js";
43
43
  import"../shared/@ai-setting/roy-agent-core-h0x19xgn.js";
44
44
  import"../shared/@ai-setting/roy-agent-core-1ce3fqrk.js";
45
- import"../shared/@ai-setting/roy-agent-core-fr296a53.js";
46
- import"../shared/@ai-setting/roy-agent-core-48h4zh60.js";
47
- import"../shared/@ai-setting/roy-agent-core-2veqhrxa.js";
45
+ import"../shared/@ai-setting/roy-agent-core-60f7h284.js";
46
+ import"../shared/@ai-setting/roy-agent-core-bm3gav5d.js";
47
+ import"../shared/@ai-setting/roy-agent-core-1dnsk6ht.js";
48
48
  import"../shared/@ai-setting/roy-agent-core-hhrg314p.js";
49
49
  import"../shared/@ai-setting/roy-agent-core-6wxww10t.js";
50
50
  import"../shared/@ai-setting/roy-agent-core-bpvz1dqr.js";
@@ -66,7 +66,7 @@ import"../shared/@ai-setting/roy-agent-core-jagkferh.js";
66
66
  import {
67
67
  AgentComponent,
68
68
  AgentComponentConfigSchema
69
- } from "../shared/@ai-setting/roy-agent-core-5bj0xfjk.js";
69
+ } from "../shared/@ai-setting/roy-agent-core-g23yp3t1.js";
70
70
  import"../shared/@ai-setting/roy-agent-core-w2ev3z5q.js";
71
71
  import"../shared/@ai-setting/roy-agent-core-wzp4tbsn.js";
72
72
  import"../shared/@ai-setting/roy-agent-core-06jakebr.js";
@@ -76,10 +76,11 @@ import {
76
76
  getXDGPaths
77
77
  } from "../shared/@ai-setting/roy-agent-core-qxnbvgwe.js";
78
78
  import"../shared/@ai-setting/roy-agent-core-p8rf4z89.js";
79
- import"../shared/@ai-setting/roy-agent-core-x1d6edkh.js";
80
- import"../shared/@ai-setting/roy-agent-core-9qgrc9dy.js";
79
+ import"../shared/@ai-setting/roy-agent-core-rfhxj8v2.js";
80
+ import"../shared/@ai-setting/roy-agent-core-03k5drfp.js";
81
81
  import"../shared/@ai-setting/roy-agent-core-zjdzk1gg.js";
82
82
  import"../shared/@ai-setting/roy-agent-core-c1v263jn.js";
83
+ import"../shared/@ai-setting/roy-agent-core-ntn32q7j.js";
83
84
  import"../shared/@ai-setting/roy-agent-core-e25xkv53.js";
84
85
  import {
85
86
  PluginComponent
@@ -166,6 +166,53 @@ export declare function buildPiCacheOptions(args: {
166
166
  export declare function resolvePiModel(provider: string, modelId: string, baseURL?: string, capabilities?: {
167
167
  reasoning?: boolean;
168
168
  }): Model<any>;
169
+ /**
170
+ * Task #4625 — capture the upstream HTTP response so `Retry-After` survives.
171
+ *
172
+ * ## Why a hook is the only way
173
+ *
174
+ * pi-ai flattens every provider failure into `AssistantMessage.errorMessage`,
175
+ * a plain string (`openai-completions.js:314-327` does exactly
176
+ * `output.errorMessage = error.message`). `responseHeaders` are dropped on the
177
+ * floor. So by the time roy-agent sees `stopReason === "error"`, the only
178
+ * remaining trace of "how long until the quota resets" is what we saved
179
+ * ourselves.
180
+ *
181
+ * `onResponse` is pi-ai's public hook for that moment — it fires right after
182
+ * `.withResponse()` resolves, *before* the body is consumed
183
+ * (`openai-completions.js:90`), with `{status, headers}`.
184
+ *
185
+ * ## Why it captures unconditionally
186
+ *
187
+ * The hook cannot know in advance whether this response will become an error
188
+ * (a 200 can still fail later while streaming). Capturing every response costs
189
+ * one object allocation per request and lets `enrichProviderError()` decide
190
+ * afterwards. Skipping non-error statuses would risk losing the very headers we
191
+ * need when a `2xx` starts streaming and then dies.
192
+ */
193
+ export interface CapturedProviderResponse {
194
+ status: number;
195
+ headers: Record<string, string>;
196
+ }
197
+ export declare function createResponseCapture(): {
198
+ onResponse: (response: CapturedProviderResponse) => void;
199
+ captured: () => CapturedProviderResponse | undefined;
200
+ };
201
+ /**
202
+ * Task #4625 — attach the captured HTTP response to a failed pi-ai result.
203
+ *
204
+ * pi-ai reports failures as `{stopReason:"error", errorMessage}` rather than by
205
+ * throwing, so roy-agent has to turn that into a real `Error` for the caller to
206
+ * fall back on. Throwing a *bare* `new Error(errorMessage)` was how the
207
+ * `Retry-After` got lost: the message string cannot carry headers.
208
+ *
209
+ * The captured response is attached twice on purpose:
210
+ * - as `responseHeaders`, the shape `@ai-sdk/provider`'s `APICallError` uses, so
211
+ * `extractRetryAfterSeconds()` finds it on the first branch it tries;
212
+ * - as `cause`, so anything that only walks `cause` (including
213
+ * `collectErrorText()`, which builds the user-facing text) also sees it.
214
+ */
215
+ export declare function buildProviderError(message: string, captured: CapturedProviderResponse | undefined, knownSecrets?: Array<string | undefined>): Error;
169
216
  /**
170
217
  * 将 LLMMessage 转换为 pi-ai 的消息结构
171
218
  */
@@ -13,7 +13,7 @@ import {
13
13
  createLLMAdapter,
14
14
  getLLMProviderOverride,
15
15
  setLLMProvider
16
- } from "../../shared/@ai-setting/roy-agent-core-vvb4cyyy.js";
16
+ } from "../../shared/@ai-setting/roy-agent-core-2cw72f16.js";
17
17
  import"../../shared/@ai-setting/roy-agent-core-adg9ghbt.js";
18
18
  import {
19
19
  PiAiAdapter,
@@ -27,8 +27,9 @@ import {
27
27
  isOpencodeEndpoint,
28
28
  isOpencodeProvider,
29
29
  parseModelString
30
- } from "../../shared/@ai-setting/roy-agent-core-9qgrc9dy.js";
30
+ } from "../../shared/@ai-setting/roy-agent-core-03k5drfp.js";
31
31
  import"../../shared/@ai-setting/roy-agent-core-zjdzk1gg.js";
32
+ import"../../shared/@ai-setting/roy-agent-core-ntn32q7j.js";
32
33
  import"../../shared/@ai-setting/roy-agent-core-ctdhjv68.js";
33
34
  import"../../shared/@ai-setting/roy-agent-core-7hh0brvs.js";
34
35
  import"../../shared/@ai-setting/roy-agent-core-gtdtzbs7.js";
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @fileoverview Model fallback eligibility detection (Task #4623).
3
+ *
4
+ * Background
5
+ * ----------
6
+ * The user reported that `roy-agent act ... --model <provider/model>` works for
7
+ * the default agent, but sub-agents spawned via `delegate_task` silently fall
8
+ * back to the configured default model. On top of that, when the primary model
9
+ * runs out of quota the whole run dies with `Error | 404 Page not found` and
10
+ * there is no way to keep going.
11
+ *
12
+ * This module owns two pure concerns:
13
+ *
14
+ * 1. `isFallbackEligibleError()` — decide whether a given failure should
15
+ * trigger a model downgrade.
16
+ * 2. `parseFallbackModel()` — normalise a `providerId/modelId` string.
17
+ *
18
+ * ⚠️ WHY TEXT MATCHING (and not `instanceof`)
19
+ *
20
+ * The production error chain destroys the error *type* before it ever reaches
21
+ * us:
22
+ *
23
+ * - `llm.ts:873` → `throw new Error(result.result)` (AI SDK stringifies
24
+ * the error, `statusCode` is lost)
25
+ * - `environment.ts:handle_query` → `throw new Error(result.error)`
26
+ * - `error-reply.ts` → `classifyReactFailure()` matches on **text**
27
+ * (its own comment says "不能依赖 instanceof RetryError")
28
+ *
29
+ * So the only signal that survives to the fallback layer is the message text
30
+ * (plus, when the provider happens to expose it, a `statusCode` property).
31
+ * Both are inspected here.
32
+ */
33
+ /**
34
+ * Decide whether a failure should trigger a model fallback.
35
+ *
36
+ * @param error The thrown value (may be an Error, a plain object, or a string).
37
+ * @param hasFallback Whether a fallback model is actually configured. When
38
+ * `false` this always returns `false` so callers can use it
39
+ * as a single guard.
40
+ */
41
+ export declare function isFallbackEligibleError(error: unknown, hasFallback?: boolean): boolean;
42
+ /**
43
+ * Normalise a fallback model string.
44
+ *
45
+ * Accepted formats:
46
+ * - `"providerId/modelId"` → `{ providerId, modelId }`
47
+ * - `"modelId"` → `{ providerId: undefined, modelId }`
48
+ *
49
+ * @returns `undefined` when the input is empty / whitespace-only.
50
+ */
51
+ export declare function parseFallbackModel(fallbackModel: string | undefined): {
52
+ providerId?: string;
53
+ modelId: string;
54
+ } | undefined;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Secret redaction for upstream error text.
3
+ *
4
+ * ## Why this is a shared module and not a private helper
5
+ *
6
+ * Task #4465 established the rule: never trust an upstream error message to be
7
+ * secret-free — AI SDK / fetch errors routinely embed the request URL, the
8
+ * `Authorization` header, or a provider payload, any of which can contain the
9
+ * API key. `invoke.ts` therefore redacts before putting the message in
10
+ * `result.result`.
11
+ *
12
+ * Task #4625 added a second channel for the *same* error object: the raw
13
+ * `Error` is attached as `cause` so that `Retry-After` (the "when does the
14
+ * quota reset" header) survives the stringification boundaries, because a bare
15
+ * message string cannot carry headers.
16
+ *
17
+ * That created a hole. `error-reply.ts` walks `cause` while building the
18
+ * `technical` line, and that line is rendered into the user-facing failure
19
+ * reply (Feishu + CLI). So the raw, un-redacted upstream text — the very thing
20
+ * `invoke.ts` had just stripped out — came back in through the side door.
21
+ *
22
+ * Redaction therefore has to happen at **both** ends, which means it cannot stay
23
+ * private to `invoke.ts`. Kept dependency-free on purpose so both the LLM layer
24
+ * and the error-reporting layer can import it without a cycle.
25
+ */
26
+ /**
27
+ * Redact credentials from free-form text before logging or showing it.
28
+ *
29
+ * Two passes:
30
+ * 1. exact known secrets (caller-supplied, e.g. the configured `apiKey`) — this
31
+ * is defense in depth for formats the patterns below do not know about;
32
+ * 2. well-known key shapes (`sk-…`, `oc_sk_…`, `Bearer …`).
33
+ *
34
+ * @param text free-form text that may embed a credential
35
+ * @param knownSecrets exact values to scrub; entries shorter than 8 chars are
36
+ * ignored so a trivial value cannot wreck the message
37
+ */
38
+ export declare function redactSecrets(text: string, knownSecrets?: Array<string | undefined>): string;
39
+ export declare function redactErrorForTransport(error: unknown, knownSecrets?: Array<string | undefined>, depth?: number): unknown;
@@ -65,6 +65,19 @@ export interface ToolResult {
65
65
  result: string;
66
66
  /** 是否为错误 */
67
67
  isError?: boolean;
68
+ /**
69
+ * Task #4625:保留原始错误对象(**已脱敏的结构副本**)。
70
+ *
71
+ * `result` 是字符串,装不下 `APICallError.responseHeaders` 之类结构化信息,
72
+ * 而 `Retry-After`(额度多久恢复)只存在那里。上层构造 `Error` 时把它挂到
73
+ * `cause` 上,`extractRetryAfterSeconds()` 才能一路挖到。
74
+ *
75
+ * ⚠️ **不要用 `instanceof Error` 判断它。** 为了脱敏,`invoke.ts` 交给这里的
76
+ * 是 `redactErrorForTransport()` 产出的**普通对象副本**(不是 Error 实例),
77
+ * 这样才能按值擦掉 apiKey。下游一律按结构化字段访问
78
+ * (`name` / `message` / `statusCode` / `responseHeaders` / `cause`)。
79
+ */
80
+ originalError?: Error | Record<string, unknown>;
68
81
  }
69
82
  /**
70
83
  * Usage Information
@@ -3,17 +3,18 @@ import {
3
3
  MIN_SESSION_ID_LENGTH,
4
4
  SessionComponent,
5
5
  isValidSessionId
6
- } from "../../shared/@ai-setting/roy-agent-core-48h4zh60.js";
6
+ } from "../../shared/@ai-setting/roy-agent-core-bm3gav5d.js";
7
7
  import {
8
8
  MemorySessionStore
9
- } from "../../shared/@ai-setting/roy-agent-core-2veqhrxa.js";
9
+ } from "../../shared/@ai-setting/roy-agent-core-1dnsk6ht.js";
10
10
  import"../../shared/@ai-setting/roy-agent-core-ze068vc9.js";
11
11
  import"../../shared/@ai-setting/roy-agent-core-rvxg1wps.js";
12
12
  import {
13
13
  SessionMessageConverter
14
- } from "../../shared/@ai-setting/roy-agent-core-x1d6edkh.js";
15
- import"../../shared/@ai-setting/roy-agent-core-9qgrc9dy.js";
14
+ } from "../../shared/@ai-setting/roy-agent-core-rfhxj8v2.js";
15
+ import"../../shared/@ai-setting/roy-agent-core-03k5drfp.js";
16
16
  import"../../shared/@ai-setting/roy-agent-core-zjdzk1gg.js";
17
+ import"../../shared/@ai-setting/roy-agent-core-ntn32q7j.js";
17
18
  import"../../shared/@ai-setting/roy-agent-core-7hh0brvs.js";
18
19
  import"../../shared/@ai-setting/roy-agent-core-gtdtzbs7.js";
19
20
  import"../../shared/@ai-setting/roy-agent-core-7tce05jt.js";
@@ -3,10 +3,11 @@ import {
3
3
  SQLiteSessionStore,
4
4
  getDefaultDataDir,
5
5
  getDefaultSessionDbPath
6
- } from "../../../shared/@ai-setting/roy-agent-core-2veqhrxa.js";
7
- import"../../../shared/@ai-setting/roy-agent-core-x1d6edkh.js";
8
- import"../../../shared/@ai-setting/roy-agent-core-9qgrc9dy.js";
6
+ } from "../../../shared/@ai-setting/roy-agent-core-1dnsk6ht.js";
7
+ import"../../../shared/@ai-setting/roy-agent-core-rfhxj8v2.js";
8
+ import"../../../shared/@ai-setting/roy-agent-core-03k5drfp.js";
9
9
  import"../../../shared/@ai-setting/roy-agent-core-zjdzk1gg.js";
10
+ import"../../../shared/@ai-setting/roy-agent-core-ntn32q7j.js";
10
11
  import"../../../shared/@ai-setting/roy-agent-core-386wa4wy.js";
11
12
  import"../../../shared/@ai-setting/roy-agent-core-6awgfh3x.js";
12
13
  import"../../../shared/@ai-setting/roy-agent-core-jnszp0jq.js";
@@ -209,6 +209,20 @@ export interface BackgroundTask {
209
209
  /** Timestamp of the latest lifecycle state change or progress notification. */
210
210
  lastUpdatedAt?: number;
211
211
  startedAt?: number;
212
+ /**
213
+ * Task #4623 — 父 agent 本次 run 使用的模型(`providerId/modelId`)。
214
+ *
215
+ * `roy-agent act "..." --model X` 时,父 agent 的 AgentContext.model 会经
216
+ * ToolContext 透传到 delegate_task,最终写在这里,并在
217
+ * `executeWithAbort` 里作为 sub-agent 的 `AgentContext.model` 传入。
218
+ *
219
+ * 注意:**不写进 sub-agent 的注册配置**(`ensureSubAgentRegistered`)。
220
+ * 注册只在首次发生(已存在则跳过),写注册期 model 会导致「第一次对、
221
+ * 第二次旧」的状态漂移;per-run 透传天然每次都是最新值。
222
+ */
223
+ model?: string;
224
+ /** Task #4623 — 父 agent 的兜底模型,同样透传给 sub-agent。 */
225
+ fallbackModel?: string;
212
226
  /**
213
227
  * ★ Task #4552 (A1):本任务被允许的运行时长(毫秒,createTask 时确定)。
214
228
  *
@@ -289,6 +303,13 @@ export interface CreateBackgroundTaskOptions {
289
303
  * 2. 透传到后续所有 task.background.* event metadata(让 CLI event-handler 反查 EventStore)
290
304
  */
291
305
  triggerEventId?: string;
306
+ /**
307
+ * Task #4623 — 父 agent 本次 run 的模型(`providerId/modelId`)。
308
+ * 透传给 sub-agent,使 `--model` 对 delegate 出来的子 agent 同样生效。
309
+ */
310
+ model?: string;
311
+ /** Task #4623 — 父 agent 的兜底模型,透传给 sub-agent。 */
312
+ fallbackModel?: string;
292
313
  }
293
314
  /**
294
315
  * 构建 entry-agent 调度的 prompt 包装 (Task #2164 beta-compare divergence #2)
@@ -703,5 +724,10 @@ existingSubSessionId?: string,
703
724
  * 传入时透传给 createTask → publishBackgroundEvent,写进 task.background.*
704
725
  * 事件 metadata,供主 agent 反查原始 chatId。
705
726
  */
706
- triggerEventId?: string): Promise<ToolResult>;
727
+ triggerEventId?: string,
728
+ /**
729
+ * Task #4623 — 父 agent 本次 run 的 model / fallbackModel。
730
+ * 透传给 createTask → sub-agent 的 AgentContext。
731
+ */
732
+ model?: string, fallbackModel?: string): Promise<ToolResult>;
707
733
  export { handleBackgroundTask };