@ai-setting/roy-agent-core 1.7.53 → 1.7.56

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 (124) hide show
  1. package/dist/config/index.js +7 -7
  2. package/dist/env/agent/agent-component.d.ts +30 -0
  3. package/dist/env/agent/index.js +13 -12
  4. package/dist/env/commands/index.js +7 -7
  5. package/dist/env/context/index.js +3 -3
  6. package/dist/env/debug/index.js +7 -7
  7. package/dist/env/environment.d.ts +7 -0
  8. package/dist/env/error-reply.d.ts +184 -0
  9. package/dist/env/event-source/event-source-handlers.d.ts +9 -23
  10. package/dist/env/event-source/index.js +17 -17
  11. package/dist/env/hook/index.js +5 -5
  12. package/dist/env/index.js +41 -40
  13. package/dist/env/llm/adapter-ai-sdk.d.ts +6 -0
  14. package/dist/env/llm/adapter-pi-ai.d.ts +152 -2
  15. package/dist/env/llm/adapter.d.ts +21 -1
  16. package/dist/env/llm/index.js +10 -10
  17. package/dist/env/llm/invoke.d.ts +74 -2
  18. package/dist/env/llm/llm.d.ts +38 -0
  19. package/dist/env/llm/outbound-defense.d.ts +140 -0
  20. package/dist/env/llm/reasoning-defense.d.ts +104 -0
  21. package/dist/env/llm/types.d.ts +26 -0
  22. package/dist/env/log-trace/decorator.d.ts +46 -3
  23. package/dist/env/log-trace/index.d.ts +1 -0
  24. package/dist/env/log-trace/index.js +33 -7
  25. package/dist/env/log-trace/trace-filters.d.ts +127 -0
  26. package/dist/env/mcp/index.js +7 -7
  27. package/dist/env/memory/index.js +7 -7
  28. package/dist/env/plugin/index.js +7 -7
  29. package/dist/env/prompt/index.js +8 -8
  30. package/dist/env/prompt/prompts-index.d.ts +1 -1
  31. package/dist/env/session/index.js +11 -10
  32. package/dist/env/session/session-component.d.ts +4 -0
  33. package/dist/env/session/storage/index.js +5 -3
  34. package/dist/env/skill/index.js +7 -7
  35. package/dist/env/task/backend/index.js +0 -2
  36. package/dist/env/task/backend/types.d.ts +0 -11
  37. package/dist/env/task/delegate/index.js +8 -8
  38. package/dist/env/task/index.js +13 -13
  39. package/dist/env/task/plugins/card-chat-routing.d.ts +42 -89
  40. package/dist/env/task/plugins/index.js +10 -10
  41. package/dist/env/task/plugins/lark-card-patch-notifier.d.ts +0 -51
  42. package/dist/env/task/plugins/lark-cli-task-notify-plugin.d.ts +14 -31
  43. package/dist/env/task/plugins/per-task-group-chat-plugin.d.ts +4 -4
  44. package/dist/env/task/storage/index.js +2 -2
  45. package/dist/env/task/tools/index.js +3 -3
  46. package/dist/env/tool/built-in/index.js +3 -3
  47. package/dist/env/tool/index.js +9 -9
  48. package/dist/env/workflow/engine/engine.d.ts +65 -0
  49. package/dist/env/workflow/engine/index.js +12 -12
  50. package/dist/env/workflow/hooks/index.js +7 -7
  51. package/dist/env/workflow/index.js +23 -22
  52. package/dist/env/workflow/nodes/index.js +2 -2
  53. package/dist/env/workflow/nodes/tool-node.d.ts +6 -0
  54. package/dist/env/workflow/plugins/index.js +8 -8
  55. package/dist/env/workflow/service/index.js +2 -2
  56. package/dist/env/workflow/storage/index.js +3 -3
  57. package/dist/env/workflow/tools/index.js +15 -14
  58. package/dist/env/workflow/tools/workflow-validate-tool.d.ts +11 -2
  59. package/dist/env/workflow/types/run.d.ts +19 -0
  60. package/dist/env/workflow/utils/agent-session-metadata.d.ts +13 -0
  61. package/dist/env/workflow/utils/index.js +5 -5
  62. package/dist/env/workflow/utils/redact-command-secrets.d.ts +70 -0
  63. package/dist/env/workflow/utils/shell-dialect-lint.d.ts +75 -0
  64. package/dist/env/workflow/utils/validate-workflow-definition.d.ts +22 -0
  65. package/dist/index.d.ts +1 -1
  66. package/dist/index.js +82 -56
  67. package/dist/shared/@ai-setting/{roy-agent-core-k25gv86p.js → roy-agent-core-0b3fdnn3.js} +1 -1
  68. package/dist/shared/@ai-setting/{roy-agent-core-vvxb9522.js → roy-agent-core-0bmk44ye.js} +71 -10
  69. package/dist/shared/@ai-setting/{roy-agent-core-k8b440n3.js → roy-agent-core-16t0zfbk.js} +1 -1
  70. package/dist/shared/@ai-setting/{roy-agent-core-4z3rq5gw.js → roy-agent-core-1pnfkspm.js} +2 -2
  71. package/dist/shared/@ai-setting/{roy-agent-core-na73mym1.js → roy-agent-core-202v00x4.js} +213 -2
  72. package/dist/shared/@ai-setting/{roy-agent-core-ph5qbqcx.js → roy-agent-core-2044fbsd.js} +471 -134
  73. package/dist/shared/@ai-setting/{roy-agent-core-fs36xgcs.js → roy-agent-core-3dkzqqsd.js} +1 -1
  74. package/dist/shared/@ai-setting/{roy-agent-core-qm9jda8p.js → roy-agent-core-3kz6934b.js} +1 -1
  75. package/dist/shared/@ai-setting/{roy-agent-core-tjg71zqp.js → roy-agent-core-3mc6vmvj.js} +1 -1
  76. package/dist/shared/@ai-setting/{roy-agent-core-tdbv7ff4.js → roy-agent-core-40xzbjtm.js} +2 -2
  77. package/dist/shared/@ai-setting/{roy-agent-core-athdr2m0.js → roy-agent-core-4zy2v3br.js} +10 -2
  78. package/dist/shared/@ai-setting/{roy-agent-core-t37wbysj.js → roy-agent-core-5gw6w424.js} +4 -4
  79. package/dist/shared/@ai-setting/{roy-agent-core-978b6wfx.js → roy-agent-core-5psjwd08.js} +1 -1
  80. package/dist/shared/@ai-setting/{roy-agent-core-7evvpfwc.js → roy-agent-core-5rxs0ra3.js} +1 -1
  81. package/dist/shared/@ai-setting/{roy-agent-core-gdsqp6j8.js → roy-agent-core-6masakz4.js} +45 -0
  82. package/dist/shared/@ai-setting/{roy-agent-core-stnddvx9.js → roy-agent-core-6qgq7h1g.js} +4 -4
  83. package/dist/shared/@ai-setting/{roy-agent-core-6167phd4.js → roy-agent-core-833t16n1.js} +3 -3
  84. package/dist/shared/@ai-setting/{roy-agent-core-2k4xffaa.js → roy-agent-core-8cgtwytv.js} +3 -3
  85. package/dist/shared/@ai-setting/{roy-agent-core-rx16h2m1.js → roy-agent-core-97eyghsp.js} +2 -2
  86. package/dist/shared/@ai-setting/{roy-agent-core-fvs48d38.js → roy-agent-core-98aa9r0x.js} +12 -7
  87. package/dist/shared/@ai-setting/{roy-agent-core-3tv1q6f2.js → roy-agent-core-9nv586rw.js} +57 -19
  88. package/dist/shared/@ai-setting/roy-agent-core-b599jgyd.js +578 -0
  89. package/dist/shared/@ai-setting/{roy-agent-core-yd21n6c7.js → roy-agent-core-bwzy00t2.js} +12 -5
  90. package/dist/shared/@ai-setting/{roy-agent-core-dwx9hjwx.js → roy-agent-core-c6at31ra.js} +1 -1
  91. package/dist/shared/@ai-setting/{roy-agent-core-1cv9j4dg.js → roy-agent-core-d14h726p.js} +3 -3
  92. package/dist/shared/@ai-setting/{roy-agent-core-4sx6sdb2.js → roy-agent-core-d4jpn06f.js} +22 -9
  93. package/dist/shared/@ai-setting/{roy-agent-core-39xejr39.js → roy-agent-core-e0wbq0qq.js} +7 -7
  94. package/dist/shared/@ai-setting/{roy-agent-core-vrsxhw9e.js → roy-agent-core-e677am8e.js} +1 -1
  95. package/dist/shared/@ai-setting/{roy-agent-core-hch2geta.js → roy-agent-core-ezsyyjxw.js} +21 -190
  96. package/dist/shared/@ai-setting/{roy-agent-core-ne2t1pay.js → roy-agent-core-f8sp1txg.js} +2 -2
  97. package/dist/shared/@ai-setting/{roy-agent-core-ak4dc3kg.js → roy-agent-core-gn8f4rhs.js} +1 -1
  98. package/dist/shared/@ai-setting/{roy-agent-core-qcspfszy.js → roy-agent-core-gngjkw3b.js} +1 -1
  99. package/dist/shared/@ai-setting/{roy-agent-core-9jzysq50.js → roy-agent-core-heft3j4c.js} +2 -2
  100. package/dist/shared/@ai-setting/{roy-agent-core-5apmk4qw.js → roy-agent-core-jszgaevt.js} +1 -1
  101. package/dist/shared/@ai-setting/{roy-agent-core-nde5gzta.js → roy-agent-core-m8n0dffe.js} +1 -1
  102. package/dist/shared/@ai-setting/{roy-agent-core-x8n1r7h4.js → roy-agent-core-n3b7ztms.js} +189 -8
  103. package/dist/shared/@ai-setting/{roy-agent-core-4vpk40ss.js → roy-agent-core-nhk0dsxr.js} +13 -13
  104. package/dist/shared/@ai-setting/{roy-agent-core-j8e92gj8.js → roy-agent-core-nntphgww.js} +1 -60
  105. package/dist/shared/@ai-setting/{roy-agent-core-8s9jd2qy.js → roy-agent-core-nra8vfrs.js} +1 -1
  106. package/dist/shared/@ai-setting/{roy-agent-core-pk1b3kvf.js → roy-agent-core-ns3pbyz4.js} +1 -1
  107. package/dist/shared/@ai-setting/{roy-agent-core-7g03zkkb.js → roy-agent-core-pemxr5n4.js} +1 -1
  108. package/dist/shared/@ai-setting/{roy-agent-core-skzjs973.js → roy-agent-core-pjxa78mp.js} +5 -5
  109. package/dist/shared/@ai-setting/{roy-agent-core-p8ak03s0.js → roy-agent-core-q7zksbw0.js} +117 -315
  110. package/dist/shared/@ai-setting/{roy-agent-core-zd3ddm4k.js → roy-agent-core-qjjf4tjb.js} +1 -1
  111. package/dist/shared/@ai-setting/{roy-agent-core-qamktze3.js → roy-agent-core-qyqnmz8c.js} +1 -1
  112. package/dist/shared/@ai-setting/{roy-agent-core-9csa89jx.js → roy-agent-core-r9qe0ghe.js} +2 -2
  113. package/dist/shared/@ai-setting/{roy-agent-core-h93v6p6y.js → roy-agent-core-tc7gw38m.js} +2 -2
  114. package/dist/shared/@ai-setting/{roy-agent-core-35s1w7tc.js → roy-agent-core-v2188rnk.js} +157 -85
  115. package/dist/shared/@ai-setting/{roy-agent-core-p2r27era.js → roy-agent-core-vmg30n5x.js} +8 -8
  116. package/dist/shared/@ai-setting/{roy-agent-core-rnwr39b0.js → roy-agent-core-w7hnjhvx.js} +5 -3
  117. package/dist/shared/@ai-setting/{roy-agent-core-dv714bqh.js → roy-agent-core-wafk25ff.js} +1 -1
  118. package/dist/shared/@ai-setting/{roy-agent-core-brwzs8wf.js → roy-agent-core-xcq04fq0.js} +5 -5
  119. package/dist/shared/@ai-setting/{roy-agent-core-2gay3sjt.js → roy-agent-core-yg247mk4.js} +1 -1
  120. package/dist/shared/@ai-setting/{roy-agent-core-zy3hqfwg.js → roy-agent-core-yv9x4wmg.js} +631 -35
  121. package/dist/shared/@ai-setting/{roy-agent-core-yhsssn86.js → roy-agent-core-zmeka63k.js} +1 -1
  122. package/dist/test-utils/child-process-mock.d.ts +73 -0
  123. package/package.json +2 -2
  124. package/dist/shared/@ai-setting/roy-agent-core-dsevjbck.js +0 -245
package/dist/env/index.js CHANGED
@@ -1,21 +1,21 @@
1
1
  import {
2
2
  BaseEnvironment
3
- } from "../shared/@ai-setting/roy-agent-core-4sx6sdb2.js";
3
+ } from "../shared/@ai-setting/roy-agent-core-d4jpn06f.js";
4
4
  import {
5
5
  TaskComponent
6
- } from "../shared/@ai-setting/roy-agent-core-39xejr39.js";
6
+ } from "../shared/@ai-setting/roy-agent-core-e0wbq0qq.js";
7
7
  import"../shared/@ai-setting/roy-agent-core-amkqnyzy.js";
8
- import"../shared/@ai-setting/roy-agent-core-qm9jda8p.js";
9
- import"../shared/@ai-setting/roy-agent-core-7g03zkkb.js";
8
+ import"../shared/@ai-setting/roy-agent-core-3kz6934b.js";
9
+ import"../shared/@ai-setting/roy-agent-core-pemxr5n4.js";
10
10
  import"../shared/@ai-setting/roy-agent-core-rn57psp0.js";
11
11
  import"../shared/@ai-setting/roy-agent-core-8gxth0eh.js";
12
- import"../shared/@ai-setting/roy-agent-core-p2r27era.js";
12
+ import"../shared/@ai-setting/roy-agent-core-vmg30n5x.js";
13
13
  import"../shared/@ai-setting/roy-agent-core-tbrn9wdm.js";
14
14
  import {
15
15
  EventSourceComponent,
16
16
  builtInHandlers,
17
17
  larkCliHandler
18
- } from "../shared/@ai-setting/roy-agent-core-ph5qbqcx.js";
18
+ } from "../shared/@ai-setting/roy-agent-core-2044fbsd.js";
19
19
  import {
20
20
  EventSourceInitHooks,
21
21
  getDefaultConfigForType,
@@ -23,78 +23,79 @@ import {
23
23
  isValidEventSourceType,
24
24
  validateEventSourceConfig
25
25
  } from "../shared/@ai-setting/roy-agent-core-ck60j1q9.js";
26
- import"../shared/@ai-setting/roy-agent-core-rnwr39b0.js";
27
- import"../shared/@ai-setting/roy-agent-core-hch2geta.js";
28
- import"../shared/@ai-setting/roy-agent-core-yhsssn86.js";
26
+ import"../shared/@ai-setting/roy-agent-core-w7hnjhvx.js";
27
+ import"../shared/@ai-setting/roy-agent-core-ezsyyjxw.js";
28
+ import"../shared/@ai-setting/roy-agent-core-zmeka63k.js";
29
29
  import"../shared/@ai-setting/roy-agent-core-1pg0fepg.js";
30
- import"../shared/@ai-setting/roy-agent-core-gdsqp6j8.js";
31
- import"../shared/@ai-setting/roy-agent-core-brwzs8wf.js";
32
- import"../shared/@ai-setting/roy-agent-core-j8e92gj8.js";
30
+ import"../shared/@ai-setting/roy-agent-core-6masakz4.js";
31
+ import"../shared/@ai-setting/roy-agent-core-xcq04fq0.js";
32
+ import"../shared/@ai-setting/roy-agent-core-nntphgww.js";
33
33
  import {
34
34
  WorkflowComponent
35
- } from "../shared/@ai-setting/roy-agent-core-4vpk40ss.js";
36
- import"../shared/@ai-setting/roy-agent-core-vrsxhw9e.js";
35
+ } from "../shared/@ai-setting/roy-agent-core-nhk0dsxr.js";
36
+ import"../shared/@ai-setting/roy-agent-core-e677am8e.js";
37
37
  import"../shared/@ai-setting/roy-agent-core-h0x19xgn.js";
38
38
  import"../shared/@ai-setting/roy-agent-core-1ce3fqrk.js";
39
- import"../shared/@ai-setting/roy-agent-core-vvxb9522.js";
40
- import"../shared/@ai-setting/roy-agent-core-yd21n6c7.js";
41
- import"../shared/@ai-setting/roy-agent-core-h93v6p6y.js";
39
+ import"../shared/@ai-setting/roy-agent-core-0bmk44ye.js";
40
+ import"../shared/@ai-setting/roy-agent-core-bwzy00t2.js";
41
+ import"../shared/@ai-setting/roy-agent-core-tc7gw38m.js";
42
42
  import"../shared/@ai-setting/roy-agent-core-hhrg314p.js";
43
- import"../shared/@ai-setting/roy-agent-core-7evvpfwc.js";
44
- import"../shared/@ai-setting/roy-agent-core-35s1w7tc.js";
43
+ import"../shared/@ai-setting/roy-agent-core-5rxs0ra3.js";
44
+ import"../shared/@ai-setting/roy-agent-core-v2188rnk.js";
45
45
  import"../shared/@ai-setting/roy-agent-core-6atd905e.js";
46
- import"../shared/@ai-setting/roy-agent-core-6167phd4.js";
47
- import"../shared/@ai-setting/roy-agent-core-x8n1r7h4.js";
46
+ import"../shared/@ai-setting/roy-agent-core-833t16n1.js";
47
+ import"../shared/@ai-setting/roy-agent-core-n3b7ztms.js";
48
48
  import"../shared/@ai-setting/roy-agent-core-1db4vpc6.js";
49
49
  import"../shared/@ai-setting/roy-agent-core-ze068vc9.js";
50
- import"../shared/@ai-setting/roy-agent-core-fs36xgcs.js";
51
- import"../shared/@ai-setting/roy-agent-core-9csa89jx.js";
50
+ import"../shared/@ai-setting/roy-agent-core-3dkzqqsd.js";
51
+ import"../shared/@ai-setting/roy-agent-core-r9qe0ghe.js";
52
52
  import"../shared/@ai-setting/roy-agent-core-7exqwax1.js";
53
- import"../shared/@ai-setting/roy-agent-core-na73mym1.js";
54
- import"../shared/@ai-setting/roy-agent-core-tjg71zqp.js";
53
+ import"../shared/@ai-setting/roy-agent-core-202v00x4.js";
54
+ import"../shared/@ai-setting/roy-agent-core-3mc6vmvj.js";
55
55
  import"../shared/@ai-setting/roy-agent-core-ddwa0n6h.js";
56
- import"../shared/@ai-setting/roy-agent-core-8s9jd2qy.js";
56
+ import"../shared/@ai-setting/roy-agent-core-nra8vfrs.js";
57
57
  import"../shared/@ai-setting/roy-agent-core-jrj30vq3.js";
58
58
  import"../shared/@ai-setting/roy-agent-core-rvxg1wps.js";
59
59
  import {
60
60
  AgentComponent,
61
61
  AgentComponentConfigSchema
62
- } from "../shared/@ai-setting/roy-agent-core-3tv1q6f2.js";
63
- import"../shared/@ai-setting/roy-agent-core-fvs48d38.js";
62
+ } from "../shared/@ai-setting/roy-agent-core-9nv586rw.js";
63
+ import"../shared/@ai-setting/roy-agent-core-98aa9r0x.js";
64
64
  import"../shared/@ai-setting/roy-agent-core-kzg1jwt8.js";
65
- import"../shared/@ai-setting/roy-agent-core-rx16h2m1.js";
65
+ import"../shared/@ai-setting/roy-agent-core-97eyghsp.js";
66
66
  import {
67
67
  XDG_PATHS,
68
68
  getXDGPath,
69
69
  getXDGPaths
70
70
  } from "../shared/@ai-setting/roy-agent-core-qxnbvgwe.js";
71
- import"../shared/@ai-setting/roy-agent-core-dv714bqh.js";
72
- import"../shared/@ai-setting/roy-agent-core-pk1b3kvf.js";
73
- import"../shared/@ai-setting/roy-agent-core-zy3hqfwg.js";
71
+ import"../shared/@ai-setting/roy-agent-core-wafk25ff.js";
72
+ import"../shared/@ai-setting/roy-agent-core-ns3pbyz4.js";
73
+ import"../shared/@ai-setting/roy-agent-core-yv9x4wmg.js";
74
+ import"../shared/@ai-setting/roy-agent-core-zjdzk1gg.js";
74
75
  import"../shared/@ai-setting/roy-agent-core-c1v263jn.js";
75
76
  import"../shared/@ai-setting/roy-agent-core-e25xkv53.js";
76
77
  import {
77
78
  PluginComponent
78
- } from "../shared/@ai-setting/roy-agent-core-t37wbysj.js";
79
+ } from "../shared/@ai-setting/roy-agent-core-5gw6w424.js";
79
80
  import"../shared/@ai-setting/roy-agent-core-64f22h1g.js";
80
81
  import"../shared/@ai-setting/roy-agent-core-az13yzmc.js";
81
82
  import {
82
83
  McpComponent
83
- } from "../shared/@ai-setting/roy-agent-core-dwx9hjwx.js";
84
+ } from "../shared/@ai-setting/roy-agent-core-c6at31ra.js";
84
85
  import"../shared/@ai-setting/roy-agent-core-8t7wny6v.js";
85
86
  import"../shared/@ai-setting/roy-agent-core-ctdhjv68.js";
86
87
  import"../shared/@ai-setting/roy-agent-core-7hh0brvs.js";
87
88
  import {
88
89
  BaseComponent
89
- } from "../shared/@ai-setting/roy-agent-core-tdbv7ff4.js";
90
+ } from "../shared/@ai-setting/roy-agent-core-40xzbjtm.js";
90
91
  import {
91
92
  createHook,
92
93
  createPriorityHook
93
- } from "../shared/@ai-setting/roy-agent-core-qcspfszy.js";
94
+ } from "../shared/@ai-setting/roy-agent-core-gngjkw3b.js";
94
95
  import {
95
96
  HookManager
96
- } from "../shared/@ai-setting/roy-agent-core-4z3rq5gw.js";
97
- import"../shared/@ai-setting/roy-agent-core-k8b440n3.js";
97
+ } from "../shared/@ai-setting/roy-agent-core-1pnfkspm.js";
98
+ import"../shared/@ai-setting/roy-agent-core-16t0zfbk.js";
98
99
  import {
99
100
  createEnvContext,
100
101
  getEnvContext,
@@ -103,8 +104,8 @@ import {
103
104
  mergeEnvContext,
104
105
  runWithEnvContext,
105
106
  runWithEnvContextAsync
106
- } from "../shared/@ai-setting/roy-agent-core-5apmk4qw.js";
107
- import"../shared/@ai-setting/roy-agent-core-dsevjbck.js";
107
+ } from "../shared/@ai-setting/roy-agent-core-jszgaevt.js";
108
+ import"../shared/@ai-setting/roy-agent-core-b599jgyd.js";
108
109
  import"../shared/@ai-setting/roy-agent-core-8seq65bp.js";
109
110
  import"../shared/@ai-setting/roy-agent-core-jnszp0jq.js";
110
111
  import"../shared/@ai-setting/roy-agent-core-fnv3ev18.js";
@@ -3,6 +3,12 @@ export declare class AiSdkAdapter implements LLMAdapter {
3
3
  readonly name: "ai-sdk";
4
4
  chat(req: ChatRequest): Promise<ChatResponse>;
5
5
  stream(req: ChatRequest): Promise<StreamHandle>;
6
+ /**
7
+ * ★ 2026-10-22:**只存摘要**(Task #4479)。embed 的 `input` 是大字符串数组,
8
+ * 返回值 embedding 向量更大 —— 正常路径由 filter 存「条数/维度」摘要。
9
+ * 这里额外保留 `recordResult: false` 作为兜底:万一 filter 失效,
10
+ * 宁可没有 result 列,也不能把整个向量数组写进 DB。
11
+ */
6
12
  embed(req: EmbedRequest): Promise<EmbedResponse>;
7
13
  resolveProvider(provider: string, modelId: string): Promise<ResolvedProvider>;
8
14
  private toInvokeConfig;
@@ -11,16 +11,161 @@
11
11
  * - AssistantMessage.stopReason → ChatResponse.finishReason
12
12
  * - usage {input, output, totalTokens} → UsageInfo
13
13
  */
14
- import { type Model, type Context } from "@earendil-works/pi-ai";
14
+ import { type Model, type Context, type CacheRetention } from "@earendil-works/pi-ai";
15
15
  import type { LLMAdapter, ChatRequest, ChatResponse, StreamHandle, ResolvedProvider, EmbedRequest, EmbedResponse } from "./adapter";
16
16
  import type { LLMMessage, ToolInfo } from "./types";
17
+ /**
18
+ * Known provider list — 驱动 resolveProvider 的硬编码能力映射
19
+ *
20
+ * `apiProtocol`(Task #4383 P1)是**显式**协议声明,优先于任何 baseURL 猜测。
21
+ * 历史背景:`api` 字段曾被 `resolvePiModel()` 里的字面量特判
22
+ * (`baseURL?.includes("anthropic")`)绕过 —— minimax 因为真实 baseURL 恰好是
23
+ * `https://api.minimaxi.com/anthropic` 才「误打误撞」走对 anthropic-messages,
24
+ * 从而成为三家 provider 里唯一挂上显式 `cache_control` 的那个(#4381 实测 99.86%)。
25
+ * 现在把它变成声明式的,避免「换个 baseURL 就悄悄改了协议」。
26
+ *
27
+ * `api` 保留为**向后兼容**的遗留字段(只在既无 `apiProtocol` 也无 baseURL 线索时兜底)。
28
+ */
29
+ export declare const KNOWN_PROVIDER_APIS: Record<string, {
30
+ api: string;
31
+ apiProtocol?: string;
32
+ contextLimit: number;
33
+ maxTokensLimit: number;
34
+ }>;
35
+ /**
36
+ * Resolve the pi-ai API protocol for a provider (Task #4383 P1).
37
+ *
38
+ * Resolution order (first match wins):
39
+ * 1. `explicitProtocol` — caller/config pins it, highest priority
40
+ * 2. `PROVIDER_API_OVERRIDES` — deliberate id-variant pins
41
+ * 3. `PROVIDER_ENDPOINT_APIS[provider]` — declared per-endpoint mapping
42
+ * (only for vendors that genuinely ship two protocols)
43
+ * 4. `KNOWN_PROVIDER_APIS[provider].apiProtocol` — declarative vendor default
44
+ * 5. generic `baseURL` heuristics — backward compat for user-defined proxies
45
+ * (`/responses` → openai-responses, `anthropic` → anthropic-messages).
46
+ * This path logs a warning so we notice configs that should be declared.
47
+ * 6. `KNOWN_PROVIDER_APIS[provider].api` — legacy field
48
+ * 7. `openai-completions` — final fallback
49
+ *
50
+ * @param provider roy-agent provider id from config (e.g. "minimax")
51
+ * @param baseURL resolved provider base URL (may be undefined)
52
+ * @param explicitProtocol optional explicit protocol from caller/config
53
+ */
54
+ export declare function resolveApiProtocol(provider: string, baseURL?: string, explicitProtocol?: string): string;
55
+ /**
56
+ * pi-ai `Model.compat` overrides that make `reasoning: true` a **byte-level
57
+ * no-op** for the openai-completions request body (Task #4403).
58
+ *
59
+ * Both entries are load-bearing and were derived empirically by capturing the
60
+ * real payload through pi-ai's public `onPayload` hook and diffing it against
61
+ * the `reasoning: false` baseline:
62
+ *
63
+ * - `supportsDeveloperRole: false` — without it, `openai-completions.js:591`
64
+ * computes `useDeveloperRole = model.reasoning && compat.supportsDeveloperRole`
65
+ * and rewrites the first message's role from `system` to `developer`.
66
+ * That difference lands at **JSON offset ~40** (the very start of the body),
67
+ * so prompt caching — which is a prefix match — would miss on every turn.
68
+ * - `thinkingFormat: "openai"` — without it, `openai-completions.js:433-475`
69
+ * picks the vendor's native thinking branch and, because roy-agent never
70
+ * passes `options.reasoningEffort`, emits an explicit
71
+ * `thinking: {type: "disabled"}` (deepseek) / `enable_thinking: false`
72
+ * (zai/qwen). That is a new top-level field the provider never saw before.
73
+ *
74
+ * With all three pins the payload is byte-identical to the `reasoning: false`
75
+ * baseline for every CUSTOM provider in the real config (verified for
76
+ * opencodego / glm / minimax / ali / volcengine / tong / opencode, plus
77
+ * deepseek once the Task #4398 `reasoning_content` hook is attached), so
78
+ * turning reasoning on can never regress the cache.
79
+ *
80
+ * Scope caveat: this guarantee covers the **fallback** (custom-provider)
81
+ * branch of `resolvePiModel()`. pi-ai built-ins keep their own hand-tuned
82
+ * `compat`/`reasoning` in the descriptor and are deliberately left alone.
83
+ */
84
+ export declare const REASONING_COMPAT_PIN: {
85
+ readonly supportsDeveloperRole: false;
86
+ readonly thinkingFormat: "openai";
87
+ readonly supportsReasoningEffort: false;
88
+ };
89
+ /**
90
+ * Protocols where {@link REASONING_COMPAT_PIN} can actually hold the payload
91
+ * byte-stable.
92
+ *
93
+ * `openai-responses` is deliberately excluded: pi-ai flips the system prompt
94
+ * role there **unconditionally** —
95
+ * `openai-responses-shared.js:66: const role = model.reasoning ? "developer" : "system"`
96
+ * — with no `compat` escape hatch (its `getCompat()` only exposes
97
+ * `sendSessionIdHeader` / `supportsLongCacheRetention`). So on that path
98
+ * `reasoning: true` is a guaranteed prefix break, and we keep it off.
99
+ */
100
+ export declare const REASONING_SAFE_PROTOCOLS: ReadonlySet<string>;
101
+ /**
102
+ * Decide the `reasoning` flag for a pi-ai model descriptor (Task #4403).
103
+ *
104
+ * This replaces the hardcoded `reasoning: false` that `resolvePiModel()` used
105
+ * to emit for every non-built-in (i.e. every custom) provider — the root
106
+ * problem flagged by Task #4398, because that literal silently disabled every
107
+ * `model.reasoning`-gated pi-ai capability.
108
+ *
109
+ * Resolution order:
110
+ * 1. protocol safety guard — see {@link REASONING_SAFE_PROTOCOLS}. Even an
111
+ * explicit `capabilities.reasoning: true` cannot win here, because on
112
+ * `openai-responses` the request prefix is guaranteed to change.
113
+ * 2. explicit config `capabilities.reasoning` (either `true` or `false`).
114
+ * 3. conservative default `false`.
115
+ *
116
+ * Deliberately NO model-name pattern matching. Guessing from names (e.g.
117
+ * "looks like a reasoner") risks emitting provider-specific thinking fields
118
+ * that the backend rejects (400), makes calls slower, and bills thinking
119
+ * tokens as output. Config is the only source of truth; absence means off.
120
+ *
121
+ * @param provider roy-agent provider id (may be a gateway)
122
+ * @param modelId model id requested from the provider
123
+ * @param capabilities `llm.providers.<id>.capabilities` from config
124
+ * @param apiProtocol resolved pi-ai API protocol for this target
125
+ */
126
+ export declare function resolveModelReasoning(provider: string, modelId: string, capabilities?: {
127
+ reasoning?: boolean;
128
+ }, apiProtocol?: string): boolean;
129
+ /**
130
+ * Build the pi-ai cache-related request options (Task #4383 P0).
131
+ *
132
+ * Why this exists (#4381 root cause): pi-ai only emits an explicit cache
133
+ * marker when it is handed the right options. Two independent code paths:
134
+ *
135
+ * - **openai-completions** (`openai-completions.js:394-398`): `prompt_cache_key`
136
+ * is only set when `baseUrl` contains `api.openai.com` **or**
137
+ * `cacheRetention === "long" && compat.supportsLongCacheRetention`.
138
+ * deepseek / glm are neither `api.openai.com` nor passed any retention, so
139
+ * `prompt_cache_key` was permanently `undefined` → they fell back to implicit
140
+ * caching. Passing `cacheRetention: "long"` also sets
141
+ * `prompt_cache_retention: "24h"`.
142
+ * - **anthropic-messages** (`anthropic.js:25-36, 667-707`): `cache_control`
143
+ * markers are attached whenever `cacheRetention !== "none"`, and pi-ai
144
+ * already defaults to `"short"` (ephemeral, no ttl). That default is exactly
145
+ * what makes minimax hit 99.86%, so we must NOT override it — passing
146
+ * `"long"` would add `ttl: "1h"` and silently change current behaviour.
147
+ *
148
+ * Hence: openai-completions defaults to `"long"`, everything else keeps pi-ai's
149
+ * own default unless the caller explicitly asks for a retention.
150
+ *
151
+ * @returns options fragment to spread into `complete()` / `stream()` options.
152
+ * Only keys that should actually be sent are present (no `undefined`
153
+ * values), so `"sessionId" in opts` stays a meaningful assertion.
154
+ */
155
+ export declare function buildPiCacheOptions(args: {
156
+ apiProtocol: string;
157
+ sessionId?: string;
158
+ cacheRetention?: CacheRetention;
159
+ }): Record<string, unknown>;
17
160
  /**
18
161
  * Resolve built-in models and models declared by roy-agent configuration.
19
162
  * pi-ai's generated registry only contains its bundled provider/model pairs;
20
163
  * custom provider ids (for example minimax-openai) need a complete Model
21
164
  * descriptor before complete()/stream() can dispatch by API.
22
165
  */
23
- export declare function resolvePiModel(provider: string, modelId: string, baseURL?: string): Model<any>;
166
+ export declare function resolvePiModel(provider: string, modelId: string, baseURL?: string, capabilities?: {
167
+ reasoning?: boolean;
168
+ }): Model<any>;
24
169
  /**
25
170
  * 将 LLMMessage 转换为 pi-ai 的消息结构
26
171
  */
@@ -34,6 +179,11 @@ export declare class PiAiAdapter implements LLMAdapter {
34
179
  readonly name: "pi-ai";
35
180
  chat(req: ChatRequest): Promise<ChatResponse>;
36
181
  stream(req: ChatRequest): Promise<StreamHandle>;
182
+ /**
183
+ * ★ 2026-10-22:**只存摘要**(Task #4479)。embed 的 `texts` 是大字符串数组,
184
+ * 绝不能入库;返回值向量同理。正常路径由 filter 存摘要,
185
+ * `recordResult: false` 只是 filter 失效时的兜底(见 ai_sdk.embed 的说明)。
186
+ */
37
187
  embed(_req: EmbedRequest): Promise<EmbedResponse>;
38
188
  resolveProvider(provider: string, modelId: string): Promise<ResolvedProvider>;
39
189
  }
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * 通过 {@link createLLMAdapter} 选择,由环境变量 `ROY_LLM_PROVIDER` 控制。
10
10
  */
11
- import type { LLMMessage, ToolInfo, UsageInfo } from "./types";
11
+ import type { LLMMessage, ToolInfo, UsageInfo, ProviderCapabilities } from "./types";
12
12
  /** Adapter 名称(联合类型 + 运行时数组) */
13
13
  export type AdapterName = "pi-ai" | "ai-sdk";
14
14
  /** 运行时可用的 adapter 列表 */
@@ -40,6 +40,26 @@ export interface ChatRequest {
40
40
  * `x-opencode-session` header (see opencode-session.ts).
41
41
  */
42
42
  sessionId?: string;
43
+ /**
44
+ * Prompt-cache retention preference (Task #4383 P0).
45
+ *
46
+ * Forwarded to pi-ai's `ProviderStreamOptions.cacheRetention`. When omitted,
47
+ * `buildPiCacheOptions()` applies a protocol-aware default:
48
+ * - `openai-completions` → `"long"` (needed for `prompt_cache_key`)
49
+ * - anything else → pi-ai's own default (`anthropic` uses `"short"` =
50
+ * ephemeral `cache_control`, which is the behaviour minimax already relies on)
51
+ */
52
+ cacheRetention?: "none" | "short" | "long";
53
+ /**
54
+ * Provider capabilities from config (`llm.providers.<id>.capabilities`).
55
+ *
56
+ * Task #4403: this is the data path that was missing. `resolvePiModel()` used
57
+ * to hardcode `reasoning: false` for every custom provider, so
58
+ * `capabilities.reasoning` in config never influenced the pi-ai model
59
+ * descriptor at all. Threading it through here lets the descriptor reflect
60
+ * what the user actually configured.
61
+ */
62
+ capabilities?: ProviderCapabilities;
43
63
  }
44
64
  /** Chat 响应(统一形状) */
45
65
  export interface ChatResponse {
@@ -7,17 +7,16 @@ import {
7
7
  LLMHooks,
8
8
  LLMTransform,
9
9
  ModelLimitsSchema,
10
- PiAiAdapter,
11
10
  ProviderCapabilitiesSchema,
12
11
  ProviderConfigSchema,
13
12
  ProviderManager,
14
13
  createLLMAdapter,
15
14
  getLLMProviderOverride,
16
15
  setLLMProvider
17
- } from "../../shared/@ai-setting/roy-agent-core-p8ak03s0.js";
16
+ } from "../../shared/@ai-setting/roy-agent-core-q7zksbw0.js";
18
17
  import"../../shared/@ai-setting/roy-agent-core-adg9ghbt.js";
19
- import"../../shared/@ai-setting/roy-agent-core-zjdzk1gg.js";
20
18
  import {
19
+ PiAiAdapter,
21
20
  buildOpencodeHeaders,
22
21
  convertToSDKMessages,
23
22
  createInvokeConfig,
@@ -28,15 +27,16 @@ import {
28
27
  isOpencodeEndpoint,
29
28
  isOpencodeProvider,
30
29
  parseModelString
31
- } from "../../shared/@ai-setting/roy-agent-core-zy3hqfwg.js";
30
+ } from "../../shared/@ai-setting/roy-agent-core-yv9x4wmg.js";
31
+ import"../../shared/@ai-setting/roy-agent-core-zjdzk1gg.js";
32
32
  import"../../shared/@ai-setting/roy-agent-core-ctdhjv68.js";
33
33
  import"../../shared/@ai-setting/roy-agent-core-7hh0brvs.js";
34
- import"../../shared/@ai-setting/roy-agent-core-tdbv7ff4.js";
35
- import"../../shared/@ai-setting/roy-agent-core-qcspfszy.js";
36
- import"../../shared/@ai-setting/roy-agent-core-4z3rq5gw.js";
37
- import"../../shared/@ai-setting/roy-agent-core-k8b440n3.js";
38
- import"../../shared/@ai-setting/roy-agent-core-5apmk4qw.js";
39
- import"../../shared/@ai-setting/roy-agent-core-dsevjbck.js";
34
+ import"../../shared/@ai-setting/roy-agent-core-40xzbjtm.js";
35
+ import"../../shared/@ai-setting/roy-agent-core-gngjkw3b.js";
36
+ import"../../shared/@ai-setting/roy-agent-core-1pnfkspm.js";
37
+ import"../../shared/@ai-setting/roy-agent-core-16t0zfbk.js";
38
+ import"../../shared/@ai-setting/roy-agent-core-jszgaevt.js";
39
+ import"../../shared/@ai-setting/roy-agent-core-b599jgyd.js";
40
40
  import"../../shared/@ai-setting/roy-agent-core-8seq65bp.js";
41
41
  import"../../shared/@ai-setting/roy-agent-core-jnszp0jq.js";
42
42
  import"../../shared/@ai-setting/roy-agent-core-fs0mn2jk.js";
@@ -5,7 +5,7 @@
5
5
  * Stream events are published via Environment.pushEnvEvent().
6
6
  */
7
7
  import { type ModelMessage } from "ai";
8
- import type { ToolInfo, ToolResult, LLMOutput, LLMMessage } from "./types";
8
+ import type { ToolInfo, ToolResult, LLMOutput, LLMMessage, ProviderCapabilities } from "./types";
9
9
  import type { Environment } from "../interface";
10
10
  import { __clearOpencodeSessionIdCache } from "./opencode-session";
11
11
  /**
@@ -24,6 +24,15 @@ export interface InvokeConfig {
24
24
  baseURL?: string;
25
25
  /** API Key */
26
26
  apiKey: string;
27
+ /**
28
+ * Provider capabilities from config (`llm.providers.<id>.capabilities`).
29
+ *
30
+ * Task #4403: declared here so `LLMComponent.invoke()` can forward it to
31
+ * `ChatRequest.capabilities` without an `as any` cast. Typed as the full
32
+ * `ProviderCapabilities` shape (not just `{ reasoning?: boolean }`) because
33
+ * the runtime value is the whole object from config.
34
+ */
35
+ capabilities?: ProviderCapabilities;
27
36
  /** 额外选项 */
28
37
  options?: {
29
38
  /**
@@ -79,6 +88,18 @@ export interface InvokeOptions {
79
88
  name: string;
80
89
  };
81
90
  };
91
+ /**
92
+ * Prompt-cache retention preference (Task #4465).
93
+ *
94
+ * path A already honours this through `buildPiCacheOptions()`; path B (this
95
+ * AI SDK fallback) ignored it entirely, so every fallback request paid full
96
+ * input price. Forwarded to the outbound defence middleware, which emits
97
+ * `prompt_cache_key` / `prompt_cache_retention` on OpenAI-family protocols.
98
+ *
99
+ * Omitted → the middleware defaults to `"long"` for OpenAI-family protocols,
100
+ * matching path A's default for `openai-completions`.
101
+ */
102
+ cacheRetention?: "none" | "short" | "long";
82
103
  }
83
104
  /**
84
105
  * LLM Tool Context - Context for tool calls during LLM invocation
@@ -187,6 +208,37 @@ export declare function parseModelString(model?: string): {
187
208
  * `undefined` means the provider did not report the field at all.
188
209
  */
189
210
  export declare function extractUsageInfo(usage: AISDKUsage | undefined): ConvertedUsageInfo | undefined;
211
+ /**
212
+ * 计算 prompt cache 命中率(0..1)。
213
+ *
214
+ * Task #4373:这是把「roy-agent 自己的缓存命中」变成可观测量的关键 helper。
215
+ *
216
+ * ⚠️ 语义陷阱(上一轮 #4369 已经踩过一次):
217
+ *
218
+ * pi-ai 上报的 `promptTokens` 是 **已扣除缓存后的新增量**:
219
+ * `input = max(0, prompt_tokens - cacheRead - cacheWrite)`
220
+ * `totalTokens = input + output + cacheRead + cacheWrite`
221
+ * (见 `pi-ai/dist/providers/openai-completions.js::parseChunkUsage`、
222
+ * 以及 `anthropic.js` 的 message_start 分支——同样把 cacheRead /
223
+ * cacheWrite 单独累加进 totalTokens)
224
+ *
225
+ * 所以 `cacheRead / promptTokens` 在真实暖流数据下会算出 20,960%
226
+ * 这种荒谬值。正确的分母是「模型实际处理过的全部 input token」:
227
+ *
228
+ * promptTokensTotal = promptTokens + cacheRead + cacheWrite
229
+ * hitRate = cacheRead / promptTokensTotal
230
+ *
231
+ * @param usage - 至少包含 promptTokens;cache 字段可选
232
+ * @returns 0..1 之间的命中率。无缓存信息 / 无 input token 时返回 0(不是 NaN)
233
+ */
234
+ export declare function computeCacheHitRate(usage: {
235
+ promptTokens: number;
236
+ /** Cache-aware denominator; when present it wins over the derivation below. */
237
+ promptTokensTotal?: number;
238
+ cacheRead?: number;
239
+ cacheWrite?: number;
240
+ cacheMiss?: number;
241
+ }): number;
190
242
  /**
191
243
  * Recursively sort object keys for deterministic byte serialization.
192
244
  *
@@ -242,8 +294,28 @@ export declare function invoke(config: InvokeConfig, options: InvokeOptions, ctx
242
294
  /**
243
295
  * 创建 Provider 实例
244
296
  * 根据 providerId 创建对应的 AI SDK Provider
297
+ *
298
+ * Task #4465: every branch that can actually reach the wire installs an
299
+ * outbound defence `fetch` middleware (see outbound-defense.ts). The `google`
300
+ * branch is deliberately excluded: `createGoogleGenerativeAI` exposes no
301
+ * `fetch` option, and Google needs neither the DeepSeek-family
302
+ * `reasoning_content` field nor OpenAI-style prompt-cache keys. Rationale:
303
+ *
304
+ * - path A (`adapter-pi-ai.ts`) has carried the `reasoning_content` defence
305
+ * (#4398/#4403) and the explicit prompt-cache markers (#4383) for a while;
306
+ * path B — this AI SDK fallback — had neither, so a single transient 429 on
307
+ * path A turned into a hard 400 here.
308
+ * - The middleware patches the serialised HTTP body, which is the only place
309
+ * that is protocol-agnostic: `createOpenAI().languageModel()` speaks the
310
+ * **responses** protocol (body key `input`), while the compatible branch
311
+ * speaks **chat/completions** (body key `messages`).
312
+ * - `fetch` is the only option all three provider factories share
313
+ * (`transformRequestBody` exists on `openai-compatible` alone).
314
+ *
315
+ * The middleware is fail-open: anything it cannot parse is forwarded untouched,
316
+ * because path B only runs once we are already in a degraded state.
245
317
  */
246
- declare function createProviderInstance(providerId: string, apiKey: string, baseURL?: string, conversationId?: string): any;
318
+ declare function createProviderInstance(providerId: string, apiKey: string, baseURL?: string, conversationId?: string, cacheRetention?: "none" | "short" | "long", modelId?: string): any;
247
319
  /**
248
320
  * Test-only re-export of createProviderInstance so that unit tests can verify
249
321
  * header injection behavior without booting the full invoke() stack.
@@ -1,5 +1,6 @@
1
1
  import { BaseComponent, type ComponentConfig } from "../component";
2
2
  import { type LLMConfig, type LLMInvokeRequest, type LLMInvokeResult, type ModelLimits } from "./types";
3
+ import type { LLMAdapter } from "./adapter";
3
4
  import type { ConfigComponent } from "../../config/config-component";
4
5
  import { ContextError } from "../errors";
5
6
  /**
@@ -90,6 +91,14 @@ export declare class LLMComponent extends BaseComponent {
90
91
  private cachedAdapter?;
91
92
  /** The provider name that was used to create the cached adapter */
92
93
  private cachedAdapterProvider?;
94
+ /**
95
+ * Test-only adapter override (see `__setAdapterForTest`).
96
+ *
97
+ * Kept as an instance field rather than a module mock so the override is
98
+ * scoped to a single LLMComponent instance and cannot leak into other test
99
+ * files sharing the same Bun process.
100
+ */
101
+ private adapterOverride?;
93
102
  constructor();
94
103
  /**
95
104
  * 初始化组件
@@ -164,6 +173,14 @@ export declare class LLMComponent extends BaseComponent {
164
173
  * a rebuild on the next invoke().
165
174
  */
166
175
  private getAdapter;
176
+ /**
177
+ * Test-only: inject a fake adapter for this component instance.
178
+ *
179
+ * Preferred over `mock.module("./factory")` because it does not leak across
180
+ * test files in the same Bun process. Call `__setAdapterForTest(undefined)`
181
+ * to restore normal factory behaviour.
182
+ */
183
+ __setAdapterForTest(adapter: LLMAdapter | undefined): void;
167
184
  /**
168
185
  * P1-3: Invalidate the cached adapter.
169
186
  *
@@ -171,6 +188,19 @@ export declare class LLMComponent extends BaseComponent {
171
188
  * the adapter to be recreated on the next invoke().
172
189
  */
173
190
  invalidateAdapterCache(): void;
191
+ /**
192
+ * ★ 2026-10-22:**入参/返回值只存摘要**(Task #4479)。
193
+ *
194
+ * 改造前本 span 平均 473KB —— 因为 `request.messages`(全量 system prompt +
195
+ * 对话历史)被原样序列化进 DB attributes,完整模型响应又存了两遍
196
+ * (`attributes.result` + `result` 列)。
197
+ *
198
+ * 现在只存摘要:模型名 / provider / 消息条数 / 总字符数 / 工具数 /
199
+ * finishReason / output 长度 / usage —— 排障时最常问的问题的答案,
200
+ * 体积 < 1KB。
201
+ *
202
+ * 需要临时全量排查时:`ROY_TRACE_FULL=1` 重启即可恢复全量(不改代码)。
203
+ */
174
204
  invoke(request: LLMInvokeRequest): Promise<LLMInvokeResult>;
175
205
  /**
176
206
  * Task #2719: 消费 adapter.stream() 返回的事件流,把 pi-ai AssistantMessageEvent
@@ -190,6 +220,14 @@ export declare class LLMComponent extends BaseComponent {
190
220
  * 公共收尾逻辑:threshold check + return LLMInvokeResult
191
221
  */
192
222
  private finalizeOutput;
223
+ /**
224
+ * Task #4396: publish the `llm.completed` env event.
225
+ *
226
+ * Payload shape must match what `StreamOutputService.handleCompleted()`
227
+ * reads (`payload.metadata.usage` + `payload.metadata.model`), otherwise the
228
+ * TUI footer silently degrades to nothing.
229
+ */
230
+ private emitCompletedEvent;
193
231
  /**
194
232
  * 获取 Provider API Key
195
233
  */