@plumeai/plugin-sdk 0.0.0-stage → 0.1.3

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 (210) hide show
  1. package/LICENSE +21 -0
  2. package/bin/plume-plugin.mjs +70 -0
  3. package/dist/client/styles/src/index.d.ts +123 -0
  4. package/dist/client/ui/src/avatar/AgentAvatar.d.ts +42 -0
  5. package/dist/client/ui/src/avatar/AvatarPile.d.ts +36 -0
  6. package/dist/client/ui/src/avatar/AvatarStack.d.ts +26 -0
  7. package/dist/client/ui/src/avatar/art.d.ts +32 -0
  8. package/dist/client/ui/src/fileIcon.d.ts +33 -0
  9. package/dist/client/ui/src/hooks/useCopied.d.ts +16 -0
  10. package/dist/client/ui/src/hooks/useLiveRefresh.d.ts +22 -0
  11. package/dist/client/ui/src/hooks/useNarrow.d.ts +16 -0
  12. package/dist/client/ui/src/i18n/context.d.ts +17 -0
  13. package/dist/client/ui/src/i18n/index.d.ts +2 -0
  14. package/dist/client/ui/src/i18n/list.d.ts +20 -0
  15. package/dist/client/ui/src/i18n/locales.d.ts +10 -0
  16. package/dist/client/ui/src/i18n/messages/en.d.ts +1418 -0
  17. package/dist/client/ui/src/i18n/messages/index.d.ts +6 -0
  18. package/dist/client/ui/src/i18n/messages/zh-CN.d.ts +1263 -0
  19. package/dist/client/ui/src/i18n/plural.d.ts +16 -0
  20. package/dist/client/ui/src/i18n/translate.d.ts +43 -0
  21. package/dist/client/ui/src/icons/index.d.ts +5 -0
  22. package/dist/client/ui/src/image/AnnotateCanvas.d.ts +29 -0
  23. package/dist/client/ui/src/image/AnnotateToolbar.d.ts +73 -0
  24. package/dist/client/ui/src/image/Annotator.d.ts +154 -0
  25. package/dist/client/ui/src/image/annotate.d.ts +227 -0
  26. package/dist/client/ui/src/image/paint.d.ts +35 -0
  27. package/dist/client/ui/src/image/screenshot-geometry.d.ts +112 -0
  28. package/dist/client/ui/src/image/useBarDrag.d.ts +31 -0
  29. package/dist/client/ui/src/image/useMovableBar.d.ts +26 -0
  30. package/dist/client/ui/src/image/useToolbarPlacement.d.ts +42 -0
  31. package/dist/client/ui/src/index.d.ts +140 -0
  32. package/dist/client/ui/src/inputs/ColorField.d.ts +9 -0
  33. package/dist/client/ui/src/inputs/ComposerToggle.d.ts +23 -0
  34. package/dist/client/ui/src/inputs/DateRangeField.d.ts +25 -0
  35. package/dist/client/ui/src/inputs/NativeField.d.ts +5 -0
  36. package/dist/client/ui/src/inputs/SearchField.d.ts +21 -0
  37. package/dist/client/ui/src/inputs/Segmented.d.ts +23 -0
  38. package/dist/client/ui/src/inputs/Select.d.ts +32 -0
  39. package/dist/client/ui/src/inputs/ShortcutRecorder.d.ts +8 -0
  40. package/dist/client/ui/src/inputs/Slider.d.ts +13 -0
  41. package/dist/client/ui/src/inputs/TextArea.d.ts +30 -0
  42. package/dist/client/ui/src/inputs/Toggle.d.ts +10 -0
  43. package/dist/client/ui/src/inputs/WorktreeToggle.d.ts +19 -0
  44. package/dist/client/ui/src/inputs/day-range.d.ts +57 -0
  45. package/dist/client/ui/src/inputs/pickers.d.ts +40 -0
  46. package/dist/client/ui/src/inputs/useFieldFade.d.ts +1 -0
  47. package/dist/client/ui/src/keyboard.d.ts +21 -0
  48. package/dist/client/ui/src/layout/ApprovalFrame.d.ts +33 -0
  49. package/dist/client/ui/src/layout/Collapse.d.ts +9 -0
  50. package/dist/client/ui/src/layout/CopyMark.d.ts +19 -0
  51. package/dist/client/ui/src/layout/Disclosure.d.ts +66 -0
  52. package/dist/client/ui/src/layout/ErrorBoundary.d.ts +26 -0
  53. package/dist/client/ui/src/layout/PageTopRow.d.ts +19 -0
  54. package/dist/client/ui/src/layout/PanelEmpty.d.ts +17 -0
  55. package/dist/client/ui/src/layout/ResizeHandle.d.ts +24 -0
  56. package/dist/client/ui/src/layout/RetainedViews.d.ts +32 -0
  57. package/dist/client/ui/src/layout/Settings.d.ts +41 -0
  58. package/dist/client/ui/src/layout/Splitter.d.ts +25 -0
  59. package/dist/client/ui/src/lib/agent-avatar.d.ts +58 -0
  60. package/dist/client/ui/src/lib/code/font-stack.d.ts +12 -0
  61. package/dist/client/ui/src/lib/code/themes.d.ts +35 -0
  62. package/dist/client/ui/src/lib/date-format.d.ts +8 -0
  63. package/dist/client/ui/src/lib/dispatches.d.ts +8 -0
  64. package/dist/client/ui/src/lib/hour-style.d.ts +13 -0
  65. package/dist/client/ui/src/lib/number-draft.d.ts +24 -0
  66. package/dist/client/ui/src/lib/relative-time.d.ts +18 -0
  67. package/dist/client/ui/src/lib/tiling.d.ts +89 -0
  68. package/dist/client/ui/src/lib/typing.d.ts +19 -0
  69. package/dist/client/ui/src/motion/RollWords.d.ts +26 -0
  70. package/dist/client/ui/src/motion/RollingText.d.ts +43 -0
  71. package/dist/client/ui/src/motion/TypedText.d.ts +9 -0
  72. package/dist/client/ui/src/motion/freeze.d.ts +22 -0
  73. package/dist/client/ui/src/motion/held.d.ts +17 -0
  74. package/dist/client/ui/src/motion/loaders.d.ts +65 -0
  75. package/dist/client/ui/src/motion/reduced.d.ts +13 -0
  76. package/dist/client/ui/src/motion/ticked.d.ts +24 -0
  77. package/dist/client/ui/src/motion/ticker.d.ts +31 -0
  78. package/dist/client/ui/src/motion/tokens.d.ts +29 -0
  79. package/dist/client/ui/src/motion/useNow.d.ts +6 -0
  80. package/dist/client/ui/src/overlay/Confirm.d.ts +96 -0
  81. package/dist/client/ui/src/overlay/ContextMenu.d.ts +40 -0
  82. package/dist/client/ui/src/overlay/Dialog.d.ts +60 -0
  83. package/dist/client/ui/src/overlay/Menu.d.ts +90 -0
  84. package/dist/client/ui/src/overlay/Overlay.d.ts +17 -0
  85. package/dist/client/ui/src/overlay/Popover.d.ts +127 -0
  86. package/dist/client/ui/src/overlay/Tooltip.d.ts +14 -0
  87. package/dist/client/ui/src/overlay/afterimage.d.ts +20 -0
  88. package/dist/client/ui/src/overlay/chrome-top.d.ts +8 -0
  89. package/dist/client/ui/src/overlay/hover-layers.d.ts +41 -0
  90. package/dist/client/ui/src/overlay/portal.d.ts +21 -0
  91. package/dist/client/ui/src/overlay/scrims.d.ts +20 -0
  92. package/dist/client/ui/src/overlay/tooltip-attribute.d.ts +62 -0
  93. package/dist/client/ui/src/primitives/Button.d.ts +64 -0
  94. package/dist/client/ui/src/primitives/Caret.d.ts +25 -0
  95. package/dist/client/ui/src/primitives/ChoiceMark.d.ts +12 -0
  96. package/dist/client/ui/src/primitives/ClosableTab.d.ts +23 -0
  97. package/dist/client/ui/src/primitives/CountUp.d.ts +21 -0
  98. package/dist/client/ui/src/primitives/GitHubMark.d.ts +12 -0
  99. package/dist/client/ui/src/primitives/IconButton.d.ts +55 -0
  100. package/dist/client/ui/src/primitives/MarkNumber.d.ts +12 -0
  101. package/dist/client/ui/src/primitives/RowDeleteButton.d.ts +6 -0
  102. package/dist/client/ui/src/primitives/Skeleton.d.ts +36 -0
  103. package/dist/client/ui/src/primitives/TabStrip.d.ts +29 -0
  104. package/dist/client/ui/src/primitives/Text.d.ts +341 -0
  105. package/dist/client/ui/src/primitives/TimeAgo.d.ts +13 -0
  106. package/dist/client/ui/src/primitives/useCountUp.d.ts +23 -0
  107. package/dist/client/ui/src/row/FlowRow.d.ts +31 -0
  108. package/dist/client/ui/src/row/HoverRow.d.ts +28 -0
  109. package/dist/client/ui/src/scroll/BackToLatest.d.ts +19 -0
  110. package/dist/client/ui/src/scroll/OverlayScrollbar.d.ts +9 -0
  111. package/dist/client/ui/src/scroll/ScrollText.d.ts +24 -0
  112. package/dist/client/ui/src/scroll/Scroller.d.ts +66 -0
  113. package/dist/client/ui/src/scroll/Sideways.d.ts +30 -0
  114. package/dist/client/ui/src/scroll/fade.d.ts +2 -0
  115. package/dist/client/ui/src/scroll/follow.d.ts +157 -0
  116. package/dist/client/ui/src/scroll/measure-batch.d.ts +13 -0
  117. package/dist/client/ui/src/scroll/memory.d.ts +22 -0
  118. package/dist/client/ui/src/scroll/signature.d.ts +24 -0
  119. package/dist/client/ui/src/scroll/useFollowBottom.d.ts +80 -0
  120. package/dist/client/ui/src/scroll/useSideways.d.ts +29 -0
  121. package/dist/contract/src/app.d.ts +16 -0
  122. package/dist/contract/src/changes.d.ts +50 -0
  123. package/dist/contract/src/diagnostics.d.ts +86 -0
  124. package/dist/contract/src/index.d.ts +18 -0
  125. package/dist/contract/src/plugin.d.ts +1391 -0
  126. package/dist/contract/src/resources.d.ts +79 -0
  127. package/dist/contract/src/service.d.ts +806 -0
  128. package/dist/contract/src/ui.d.ts +1039 -0
  129. package/dist/contract/src/usage.d.ts +102 -0
  130. package/dist/core/llm/ai/anthropic-messages-request.d.ts +82 -0
  131. package/dist/core/llm/ai/anthropic-messages.d.ts +43 -0
  132. package/dist/core/llm/ai/cache-routing.d.ts +53 -0
  133. package/dist/core/llm/ai/chat-delta.d.ts +107 -0
  134. package/dist/core/llm/ai/compat-key.d.ts +19 -0
  135. package/dist/core/llm/ai/endpoint.d.ts +57 -0
  136. package/dist/core/llm/ai/failure.d.ts +181 -0
  137. package/dist/core/llm/ai/index.d.ts +16 -0
  138. package/dist/core/llm/ai/openai-chat-completions-request.d.ts +49 -0
  139. package/dist/core/llm/ai/openai-chat-completions.d.ts +37 -0
  140. package/dist/core/llm/ai/openai-responses-request.d.ts +63 -0
  141. package/dist/core/llm/ai/openai-responses.d.ts +11 -0
  142. package/dist/core/llm/ai/prefix-fingerprint.d.ts +20 -0
  143. package/dist/core/llm/ai/reasoning-compat.d.ts +69 -0
  144. package/dist/core/llm/ai/request-params-compat.d.ts +39 -0
  145. package/dist/core/llm/ai/retry.d.ts +130 -0
  146. package/dist/core/llm/ai/sanitize-history.d.ts +23 -0
  147. package/dist/core/llm/ai/think-tag.d.ts +37 -0
  148. package/dist/core/llm/ai/thinking-options.d.ts +35 -0
  149. package/dist/core/llm/ai/tool-names.d.ts +17 -0
  150. package/dist/core/llm/ai/tool-pairing-compat.d.ts +53 -0
  151. package/dist/core/llm/ai/tool-result-images-compat.d.ts +24 -0
  152. package/dist/core/llm/ai/tool-schema.d.ts +20 -0
  153. package/dist/core/llm/ai/usage-fields.d.ts +30 -0
  154. package/dist/core/llm/ai/vendors/index.d.ts +20 -0
  155. package/dist/core/llm/ai/vendors/thinking.d.ts +39 -0
  156. package/dist/core/llm/ai/vendors/types.d.ts +47 -0
  157. package/dist/core/llm/config/image-models.d.ts +34 -0
  158. package/dist/core/llm/config/retry-policy.d.ts +35 -0
  159. package/dist/core/llm/images/chat-images.d.ts +11 -0
  160. package/dist/core/llm/images/dimensions.d.ts +15 -0
  161. package/dist/core/llm/images/generate.d.ts +15 -0
  162. package/dist/core/llm/images/openai-images.d.ts +11 -0
  163. package/dist/core/llm/images/quota.d.ts +9 -0
  164. package/dist/core/llm/images/shape.d.ts +44 -0
  165. package/dist/core/llm/images/types.d.ts +118 -0
  166. package/dist/core/llm/model-catalog-format.d.ts +50 -0
  167. package/dist/core/llm/model-catalog.d.ts +51 -0
  168. package/dist/core/llm/types/message.d.ts +450 -0
  169. package/dist/core/llm/types/provider.d.ts +169 -0
  170. package/dist/core/llm/types/tool.d.ts +23 -0
  171. package/dist/core/llm/types.d.ts +10 -0
  172. package/dist/core/llm/utils/pricing.d.ts +18 -0
  173. package/dist/core/llm/utils/record.d.ts +2 -0
  174. package/dist/core/llm/utils/sse.d.ts +69 -0
  175. package/dist/infra/rpc/src/endpoint.d.ts +36 -0
  176. package/dist/infra/rpc/src/index.d.ts +2 -0
  177. package/dist/infra/rpc/src/rpc.d.ts +54 -0
  178. package/dist/theme.css +336 -0
  179. package/dist/ui.d.ts +2 -0
  180. package/lib/build.mjs +143 -0
  181. package/lib/check.mjs +23 -0
  182. package/lib/dev.mjs +52 -0
  183. package/lib/icons.mjs +2 -0
  184. package/lib/init.mjs +109 -0
  185. package/lib/install.mjs +80 -0
  186. package/lib/link.mjs +44 -0
  187. package/lib/lint/i18n.mjs +149 -0
  188. package/lib/lint/imports.mjs +83 -0
  189. package/lib/lint/index.mjs +54 -0
  190. package/lib/lint/styles.mjs +160 -0
  191. package/lib/manifest.d.mts +9 -0
  192. package/lib/manifest.mjs +99 -0
  193. package/lib/targets.mjs +60 -0
  194. package/package.json +34 -4
  195. package/skill/SKILL.md +150 -0
  196. package/skill/references/components.md +2830 -0
  197. package/skill/references/logic.md +526 -0
  198. package/skill/references/style-i18n.md +70 -0
  199. package/skill/references/ui.md +532 -0
  200. package/template/AGENTS.md +17 -0
  201. package/template/CLAUDE.md +1 -0
  202. package/template/README.md +12 -0
  203. package/template/gitignore +2 -0
  204. package/template/src/index.ts +28 -0
  205. package/template/src/messages.ts +16 -0
  206. package/template/src/types.ts +8 -0
  207. package/template/src/ui.tsx +30 -0
  208. package/template/tsconfig.json +7 -0
  209. package/tsconfig.json +25 -0
  210. package/README.md +0 -3
@@ -0,0 +1,450 @@
1
+ /**
2
+ * The neutral message shape, and what a reply costs.
3
+ *
4
+ * One shape flows through the whole system. Provider adapters translate it into their wire format
5
+ * on the way out and back on the way in, so the agent loop, the session store and the desktop UI
6
+ * never see provider-specific JSON.
7
+ */
8
+ import type { ApiFormat } from "./provider.ts";
9
+ import type { Failure } from "../ai/failure.ts";
10
+ export interface TextContent {
11
+ type: "text";
12
+ text: string;
13
+ /** Opaque provider handle (Responses item id, etc.) needed to replay this block. */
14
+ signature?: string;
15
+ /**
16
+ * Responses 上 message 项自带的 `phase`:`commentary` 是干活途中的播报,`final_answer` 是收尾的答复。
17
+ *
18
+ * 最终答复按设计要能单独读懂,会把同一次回复里先播报过的话再说一遍——不记下来就分不清哪段是哪段,
19
+ * 界面两段都画、看上去整段重复。回放时也要原样带回去,模型靠它认出自己哪些话是播报。见 `restated`。
20
+ */
21
+ phase?: "commentary" | "final_answer";
22
+ /**
23
+ * 这段正文写完了(适配器发了 `text_end`)。只出现在流式的 `message_update` 里,不进最终存下的消息。
24
+ *
25
+ * 界面靠它分清「写完了、在等这条回复收尾」和「写到一半停住了」:有的网关在最后一个字和收尾事件之间
26
+ * 要空一两秒,两者光看正文长得一样。见 `agent/stream-turn.ts`。
27
+ */
28
+ ended?: true;
29
+ }
30
+ export interface ThinkingContent {
31
+ type: "thinking";
32
+ thinking: string;
33
+ /** Opaque provider handle: Anthropic thinking signature or Responses reasoning item id. */
34
+ signature?: string;
35
+ /** Provider-encrypted reasoning payload, replayed verbatim on the next turn. */
36
+ encrypted?: string;
37
+ /** Safety filters removed the visible text but the encrypted payload is still replayable. */
38
+ redacted?: boolean;
39
+ /**
40
+ * 入站时这段推理挂在哪个键上——原样记下,下一轮用同一个键还回去。
41
+ *
42
+ * Chat Completions 上同一件事有三个字段名:`reasoning_content`(DeepSeek、llama.cpp)、`reasoning`
43
+ * (OpenRouter)、`reasoning_text`。一个只认自己那个键的端点,收到另一个键就当没收到。
44
+ *
45
+ * 只有 Chat Completions 这一条链写和读它;缺省(旧会话、Anthropic 和 Responses 产生的块)一律按
46
+ * `reasoning_content` 处理,也就是这条链从前唯一发过的那个键——所以加这个字段不改变任何既有语义。
47
+ *
48
+ * 值也可能是 `"<think>"`:推理是从正文开头内联的标签里切出来的,回放时要包回 content 原样还回去,
49
+ * 而不是换成某个字段。见 `ai/think-tag.ts`。
50
+ */
51
+ reasoningField?: string;
52
+ /**
53
+ * 这段推理开始的时刻(`Date.now()`),由各适配器在思考块开始时记下,结束时据此算出 `durationMs`。
54
+ * 界面在推理还在写的时候拿它给这一段选一个固定的说法。
55
+ */
56
+ startedAt?: number;
57
+ /**
58
+ * 这段推理从开始到写完用了多久,毫秒,由各适配器在思考块结束时记下。写完之后界面读它,不再拿现在比。
59
+ *
60
+ * 缺省是旧会话、或者推理写到一半就断了(没等到结束事件)——那时说不出一个真的时长,界面不编一个。
61
+ * 一次性把整块推理吐出来的中转上,开始和结束几乎同时到,这里就只有几毫秒:量的是看得见的那一段。
62
+ */
63
+ durationMs?: number;
64
+ }
65
+ export interface ImageContent {
66
+ type: "image";
67
+ /** base64, no data: prefix. Empty on the display path when pixels live in `media`. */
68
+ data: string;
69
+ mimeType: string;
70
+ /**
71
+ * Content-addressed file under `session-media`.
72
+ *
73
+ * The window only ever sees this name. The model gets `data` back at the
74
+ * request boundary, not when a conversation is opened.
75
+ */
76
+ media?: string;
77
+ /**
78
+ * The `session-media` picture this one is a marked-up copy of, when it is one.
79
+ *
80
+ * For the gallery's history: an annotated copy is new bytes, so without this the edit made from it
81
+ * would start a history of its own. Never reaches a provider — the adapters read `data` and `mimeType`.
82
+ */
83
+ from?: string;
84
+ }
85
+ export interface ToolCallContent {
86
+ type: "toolCall";
87
+ id: string;
88
+ name: string;
89
+ arguments: Record<string, unknown>;
90
+ /** Raw argument text as streamed; kept for salvage when JSON is truncated. */
91
+ argumentsText?: string;
92
+ /** Provider item id (Responses `item.id`), distinct from the `call_id` in `id`. */
93
+ signature?: string;
94
+ }
95
+ export type AssistantContent = TextContent | ThinkingContent | ToolCallContent;
96
+ export type UserContent = TextContent | ImageContent;
97
+ export interface Usage {
98
+ input: number;
99
+ output: number;
100
+ cacheRead: number;
101
+ cacheWrite: number;
102
+ reasoning?: number;
103
+ total: number;
104
+ cost: {
105
+ input: number;
106
+ output: number;
107
+ cacheRead: number;
108
+ cacheWrite: number;
109
+ total: number;
110
+ /** How these dollar values were obtained. Absent on logs written before this field existed. */
111
+ source?: "manual" | "catalog" | "provider" | "mixed";
112
+ /** The selected rates are stored so later catalogue updates cannot rewrite history. */
113
+ rates?: {
114
+ input: number;
115
+ output: number;
116
+ cacheRead: number;
117
+ cacheWrite: number;
118
+ };
119
+ };
120
+ }
121
+ export declare function emptyUsage(): Usage;
122
+ export declare function addUsage(a: Usage, b: Usage): Usage;
123
+ /** What the request behind a reply actually sent and received, retries excluded. See `lastAttemptUsage`. */
124
+ export declare function requestUsage(message: Pick<AssistantMessage, "usage" | "lastAttemptUsage">): Usage;
125
+ export type StopReason = "pending" | "stop" | "length" | "toolUse" | "error" | "aborted";
126
+ /**
127
+ * One attached file, as a message records it.
128
+ *
129
+ * A named type rather than the shape written inline, which is what it used to be — in nine places
130
+ * across seven files, because every layer between the composer and the session log restates the
131
+ * option bag it forwards. Two fields were added to the message and reached none of those
132
+ * signatures; the values still flowed (a structural type does not strip anything at runtime), but
133
+ * the next person to write against the type would have dropped them, and the one after that would
134
+ * have had no way to know they existed.
135
+ */
136
+ export interface MessageAttachment {
137
+ name: string;
138
+ /** `fileKind` on the desktop side — image, document, archive, text. Purely for the icon. */
139
+ kind?: string;
140
+ mimeType?: string;
141
+ /**
142
+ * Where the file came from on this machine, when it came from a file at all.
143
+ *
144
+ * What it buys: a sent message can still offer to open the spreadsheet it carried, or show it
145
+ * where it lives. Without it the bubble knows a name and nothing else, so the only honest
146
+ * thing it can do with a file it is displaying is display it.
147
+ *
148
+ * Optional because a good share of attachments never were files. A pasted screenshot, a
149
+ * region grabbed inside the app — those are pixels in memory with a name invented for them,
150
+ * and there is nothing on disk to open. Absent is also what every message written before this
151
+ * field existed looks like, which is the same thing and wants the same treatment: offer
152
+ * nothing rather than offer something that fails.
153
+ *
154
+ * Not a promise that the file is still there. It records where it was at send time; anything
155
+ * acting on it checks first, because the transcript outlives the file by design.
156
+ */
157
+ path?: string;
158
+ /**
159
+ * What it was called on screen — 「图片 1」 for a pasted screenshot whose `name` is the
160
+ * `image.png` the clipboard invented.
161
+ *
162
+ * Kept because the message text refers to it by this name: a draft writes 【图片 1】 where the
163
+ * file was dropped, and the bubble has to find that mark again to draw it as a label rather
164
+ * than as a stray pair of brackets. Absent on messages written before labels existed, where
165
+ * the mark spelled the filename instead — both are still recognised.
166
+ */
167
+ label?: string;
168
+ }
169
+ /** Runtime-owned facts carried beside a lossy summary, never extracted from summary text. */
170
+ export interface CompactionContext {
171
+ originalRequest?: string;
172
+ latestRequest?: string;
173
+ /** Ordered excerpts of real user messages; rendering has its own window budget. */
174
+ requests?: {
175
+ ordinal: number;
176
+ text: string;
177
+ timestamp: number;
178
+ }[];
179
+ todos?: {
180
+ content: string;
181
+ status: "pending" | "in_progress" | "completed";
182
+ }[];
183
+ /** 人批准过的计划;摘要可以忘,它不能。见 `runtime/plan/plan-mode.ts`。 */
184
+ plan?: ApprovedPlan;
185
+ }
186
+ /**
187
+ * 计划模式里交上去、被人批准的那份计划:一段 Markdown。
188
+ *
189
+ * 不拆成字段。拆过一次(现状、方案、改动、验证……),每份计划都被套进同一张表,三行的小改动也得
190
+ * 填满六栏;该写什么由提示词说,怎么写由模型按这件事的大小定。计划也不是清单:怎么拆步是执行时
191
+ * 模型自己用 `todo_write` 记的账。
192
+ */
193
+ export interface ApprovedPlan {
194
+ /** 计划全文,Markdown。 */
195
+ plan: string;
196
+ /** 人批准时附上的注意事项(多是在计划上划线留的评论):不改计划,执行时照着做。 */
197
+ notes?: string;
198
+ /** 批准时写出的计划文件,绝对路径,写完不再改:交给别的会话的就是这一份。宿主没有临时目录时没有。 */
199
+ file?: string;
200
+ /** 人选了「在新会话里执行」:实施在另一个会话里,这个会话不再执行它。 */
201
+ handoff?: true;
202
+ /** 交接时人给那个新会话挑的模型。没挑就没有,宿主用默认模型开。 */
203
+ modelId?: string;
204
+ approvedAt: number;
205
+ }
206
+ export interface UserMessage {
207
+ role: "user";
208
+ content: UserContent[];
209
+ timestamp: number;
210
+ /** Set when the message was injected by the runtime rather than typed by a human. */
211
+ synthetic?: boolean;
212
+ /** Only runtime-generated compaction heads carry this snapshot. */
213
+ compactionContext?: CompactionContext;
214
+ /** File references survive another compaction without parsing model-written summaries. */
215
+ compactionFiles?: {
216
+ read: string[];
217
+ changed: string[];
218
+ };
219
+ /** An explicit runtime control discarded the old plan; it did not mark work completed. */
220
+ clearsTaskPlan?: boolean;
221
+ /**
222
+ * 会话中途改了的 system prompt 段落:段落 id → 新文本,`null` 是这一段没了。
223
+ *
224
+ * 存成数据而不是让下一轮去解析正文:正文是写给模型的,措辞会改;而下一轮
225
+ * 要据此算出「模型此刻以为各段是什么」,重启后也要从日志算出同一个答案。见 `prompt/update.ts`。
226
+ */
227
+ promptUpdate?: {
228
+ section: string;
229
+ text: string | null;
230
+ }[];
231
+ /**
232
+ * Who sent this, when it was not the person looking at the transcript.
233
+ *
234
+ * A task dispatched from the side chat lands in the main conversation as an ordinary user
235
+ * message. Without this you would scroll back and find an instruction you have no memory
236
+ * of writing, in your own voice, with no way to tell where it came from.
237
+ *
238
+ * `parent` 是子智能体转录里「派它出去的那个 Agent 说的话」——开头那份任务、续跑时补的那一句。
239
+ * 面板上它不该画成人发的气泡:那不是看着面板的人说的。
240
+ */
241
+ origin?: "side-chat" | "parent";
242
+ /**
243
+ * 侧边聊天每次提问前附上的主会话进展,读到了哪里。
244
+ *
245
+ * 只附上次之后新增的那一段,所以下一次要知道从哪接着读;`lastAt` 是读到的最后一条的时间戳,
246
+ * 主会话被撤回、同一位置换成了别的消息时对不上,就整段重读。见 `runtime/side-chat/side-session.ts`。
247
+ */
248
+ mainChat?: {
249
+ seen: number;
250
+ lastAt: number;
251
+ };
252
+ /** Clean user input text for UI display, excluding injected skill or session instructions. */
253
+ displayText?: string;
254
+ /** Skill triggered by this prompt, along with its filesystem path for viewing. */
255
+ skillRef?: {
256
+ name: string;
257
+ path?: string;
258
+ pluginId?: string;
259
+ };
260
+ /** Historical sessions referenced by @ in this prompt. */
261
+ sessionRefs?: Array<{
262
+ id: string;
263
+ title: string;
264
+ }>;
265
+ /**
266
+ * 后台子代理送回来的结果、或后台命令结束的消息——这条消息是运行时替它们递过来的,不是人说的话。
267
+ *
268
+ * 人在主会话等子代理的时候插了话,父会话先去回应人,子代理留在后台接着跑;它们跑完之后,结果
269
+ * 作为这样一条消息回到主会话(见 `runtime/agent-session/delivery-message.ts`)。`content` 是给模型读的报告
270
+ * 原文;这一项给界面画成一行「谁的结果到了」,而不是一个人发的气泡——那不是人说的话,而报告
271
+ * 本身在派发卡片和子智能体面板里都看得到。
272
+ */
273
+ delivery?: DeliveredReport[];
274
+ /**
275
+ * 另一个会话的模型发来的消息——不是看着这个面板的人说的话。
276
+ *
277
+ * `content` 是给模型读的那份(外面套着 `<session-message>`);这一项给界面画成「来自 X」,
278
+ * 并让回信的那一方接上同一条链(见 `runtime/agent-session/peer-letters.ts`)。
279
+ */
280
+ peer?: PeerLetter[];
281
+ /**
282
+ * 被正在跑的那次运行接了进去,而不是开了一次新的运行。
283
+ *
284
+ * 循环把它接进去的那一刻打上(`agent/loop.ts` 的 `takeSteering`),因为只有那里知道它到底进没
285
+ * 进去:会话忙时收下的话,循环没来得及取的,会排到下一次运行开头,那时它开的是新的一次。界面
286
+ * 靠它分辨送达是把空闲的会话叫醒了,还是插在一轮中间——后者不是新的一轮。
287
+ */
288
+ steered?: true;
289
+ /**
290
+ * 这句话打断了正在跑的那一轮:会话先停下手上的事,再拿它开新的一轮(`AgentSession.prompt` 的
291
+ * `deliver: "interrupt"`,排队条上的「现在就发」)。
292
+ *
293
+ * 只在真的打断了什么时才打上——会话闲着时它就是一句普通的话。发给模型时据此说清楚它是在模型
294
+ * 工作时说的、上一条回复是被它截断的(`prompt/steering.ts`);不说的话,模型看到的只是自己写到
295
+ * 一半的回复后面跟着一条新消息,分不清那半截是被人停下的还是自己断的。
296
+ */
297
+ interrupting?: true;
298
+ /**
299
+ * The files that were attached, by name and kind — never their contents.
300
+ *
301
+ * A text attachment's body is expanded into the prompt, which is what the model needs and the
302
+ * last thing a reader wants to scroll past: a thousand-line document arrived as a thousand lines
303
+ * inside the message bubble, and getting back above it was a chore. `displayText` keeps the
304
+ * bubble to what was actually typed, and this is what lets it still say which files went with it.
305
+ *
306
+ * Metadata only, deliberately. The contents are already in `content`; a second copy here would
307
+ * double the size of every session log for something no reader ever looks at.
308
+ */
309
+ attachments?: MessageAttachment[];
310
+ }
311
+ /** 送达消息里的一份报告是谁的、怎么收场的。 */
312
+ export interface DeliveredReport {
313
+ /** 登记簿里的 id;后台命令是它的任务 id。 */
314
+ id: string;
315
+ /**
316
+ * 后台命令结束了,而不是子代理跑完了。这时 `agent` 为空,`description` 是模型起任务时写的那句
317
+ * 说明(没写就是命令本身),`command` 是完整命令,`exitCode` 为 null 表示被信号终止。
318
+ */
319
+ kind?: "job" | "session" | "wakeup";
320
+ command?: string;
321
+ exitCode?: number | null;
322
+ agent: string;
323
+ description: string;
324
+ status: "done" | "failed" | "aborted";
325
+ /** 没做完就停下了(检查点、原地打转、上游出错)——报告是阶段性的。 */
326
+ incomplete?: boolean;
327
+ /** 不带 `kind` 时:交回这份的插件,宿主照实填,界面交给这个插件登记的那一行(`ui.agentDelivery`)画。 */
328
+ plugin?: string;
329
+ /**
330
+ * `kind` 为 `job` 时,这不是命令结束,是 `monitor` 送来的一批新输出:`lines` 行,`exitCode` 不设。
331
+ * 达到上限自己停了的,最后一批带着 `stopped`。
332
+ */
333
+ monitor?: {
334
+ lines: number;
335
+ stopped?: "max_events" | "idle";
336
+ };
337
+ }
338
+ /**
339
+ * 另一个会话发来的一条消息。
340
+ *
341
+ * 请求、通知、答复走同一条路:对方在跑就在两步之间读到,闲着就开一轮。问是发出即返回,请求的答复
342
+ * 由收件方用 `reply_session` 交回,作为一封 `answer` 信送到发件方;通知没有答复。
343
+ */
344
+ export interface PeerLetter {
345
+ /** 这一封的 id:发件方据此认出交回的回答、或兜底时它进了哪一次运行,见桌面端的 `session-peers.ts`。旧记录里没有。 */
346
+ id?: string;
347
+ /** 请求:发件方要一个答复。没有就是通知(或答复)。 */
348
+ request?: true;
349
+ /**
350
+ * 这是对我问过的请求的答复:`replied` 是对方用 `reply_session` 交回的;`unreplied` 是对方读到请求的那一轮
351
+ * 收场了却没回,正文是那一轮最后说的话;`dropped` 是请求没被读到就被对方的人停下了;`closed` 是对方会话关了。
352
+ * 后三种由路由器代写,见桌面端的 `session-peers.ts`。
353
+ */
354
+ answer?: "replied" | "unreplied" | "dropped" | "closed";
355
+ /** 答复对着我发过的哪几封:每封开头一截,按发出的顺序。 */
356
+ re?: string[];
357
+ /** 对方会话的 id,界面据此跳过去。 */
358
+ sessionId: string;
359
+ /** 寄出那一刻对方的地址,`名字 [ref]`;要再找它时原样当作 `to`。 */
360
+ from: string;
361
+ text: string;
362
+ /** 对方的工作目录,跨项目时路径的意思会变,模型要知道是从哪儿说的。 */
363
+ cwd?: string;
364
+ /** 这封信所在的往来链,按经手的会话 id 排;跳数上限在桌面端的 `peer-policy.ts`。 */
365
+ chain?: string[];
366
+ }
367
+ export interface RequestPrefix {
368
+ /** 这次请求体切成了几段(工具、系统提示词、逐条消息)。 */
369
+ segments: number;
370
+ /** 第一处和上一次不同的段及其前后长度(字符);没有就是上一次原样是这次的前缀。 */
371
+ change?: {
372
+ segment: string;
373
+ before: number;
374
+ after: number;
375
+ };
376
+ }
377
+ export interface AssistantMessage {
378
+ role: "assistant";
379
+ content: AssistantContent[];
380
+ api: ApiFormat;
381
+ provider: string;
382
+ model: string;
383
+ usage: Usage;
384
+ /**
385
+ * The final attempt's own usage, set only when failed attempts were folded into `usage`.
386
+ *
387
+ * `usage` is the bill, and the bill has to include what abandoned retries cost. The context
388
+ * window, though, held one request, not their sum, so anything asking "how big was the
389
+ * conversation" reads this instead — through `requestUsage`. Absent without retries and on
390
+ * logs written before it existed; `usage` is then that one request.
391
+ */
392
+ lastAttemptUsage?: Usage;
393
+ stopReason: StopReason;
394
+ errorMessage?: string;
395
+ /**
396
+ * Whether what ended this was the connection rather than the request.
397
+ *
398
+ * A dropped socket and a rejected API key both arrive as `stopReason: "error"`, and only one of
399
+ * them is worth going back for. Which it was is known exactly once — where the error is caught,
400
+ * with the cause still attached — and by the time it has been flattened into a message string
401
+ * telling them apart is pattern-matching on prose. So it is written down while it is still a
402
+ * fact.
403
+ */
404
+ errorRetryable?: boolean;
405
+ /**
406
+ * 这次失败是什么——分类的结果,不是一串给人猜的字符串。
407
+ *
408
+ * `errorMessage` 留着是为了读得懂旧会话,但它把一次失败压成了一行字,之后每个想知道「这该不该
409
+ * 重试」「这句话该怎么说给人听」「有没有下一步可给」的地方,都只能对着那行字做模式匹配。结论
410
+ * 在 `failure.ts` 里只产生一次,然后一路带着走。
411
+ */
412
+ failure?: Failure;
413
+ /**
414
+ * 这次请求体和同一会话上一次相比,前缀从哪里开始不同。缓存未命中的归因证据,见
415
+ * `ai/prefix-fingerprint.ts`。没有这个字段是没量(旧日志、第一次请求、替身流)。
416
+ */
417
+ prefix?: RequestPrefix;
418
+ /** Provider response id, used for Responses-API conversation chaining. */
419
+ responseId?: string;
420
+ /** Latency in milliseconds from request start to completion */
421
+ durationMs?: number;
422
+ /** Latency in milliseconds of actual streaming token generation (from first token chunk to completion) */
423
+ sseDurationMs?: number;
424
+ timestamp: number;
425
+ }
426
+ export interface ToolResultMessage {
427
+ /** Actual execution boundary; absent in historical messages. */
428
+ startedAt?: number;
429
+ durationMs?: number;
430
+ role: "toolResult";
431
+ toolCallId: string;
432
+ toolName: string;
433
+ content: UserContent[];
434
+ /** Structured payload for rich UI rendering; never sent to the model. */
435
+ details?: unknown;
436
+ /** 只给人看、不交给模型的图(Plume 的 `ToolOutput.shownImages`):和 `details` 一样不进请求体。 */
437
+ shownImages?: ImageContent[];
438
+ isError: boolean;
439
+ /** See `ToolResult.uneventful`. Carried on the message so compaction can see it. */
440
+ uneventful?: boolean;
441
+ /**
442
+ * See `ToolResult.terminate`. Carried on the message so the loop can see it.
443
+ *
444
+ * 和 `details` 一样,只在本进程里活着——不进 provider 的请求体。写进会话日志是无害的:循环只
445
+ * 看自己这一轮刚拿到的结果,重放历史时没人会再读它。
446
+ */
447
+ terminate?: boolean;
448
+ timestamp: number;
449
+ }
450
+ export type Message = UserMessage | AssistantMessage | ToolResultMessage;
@@ -0,0 +1,169 @@
1
+ import type { RetryPolicySource } from "../config/retry-policy.ts";
2
+ import type { Failure } from "../ai/failure.ts";
3
+ /**
4
+ * Models, providers, and the stream a request comes back as.
5
+ *
6
+ * We speak three wire formats and no others. Everything above this line is expressed in the
7
+ * neutral message shape; everything below it is a provider's own idea of a request.
8
+ */
9
+ import type { AssistantMessage, Message } from "./message.ts";
10
+ import type { ToolSpec } from "./tool.ts";
11
+ /**
12
+ * Wire formats we speak: OpenAI Responses, Anthropic Messages, and OpenAI Chat Completions — the
13
+ * one most OpenAI-compatible services and relays speak. Each has its adapter in `ai/`, and every path
14
+ * that sends a request (the settings page's connection test included) goes through that adapter.
15
+ */
16
+ export type ApiFormat = "openai-responses" | "anthropic-messages" | "openai-chat-completions";
17
+ export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | "ultra" | (string & {});
18
+ export interface ThinkingOption {
19
+ id: ThinkingLevel;
20
+ label: string;
21
+ detail: string;
22
+ isDefault?: boolean;
23
+ /** Required for a nonstandard effort name on the budget-based Anthropic adapter. */
24
+ budgetTokens?: number;
25
+ }
26
+ export interface ModelPricing {
27
+ /** USD per million tokens. */
28
+ input: number;
29
+ output: number;
30
+ cacheRead?: number;
31
+ cacheWrite?: number;
32
+ /** Higher rates selected when one request crosses a context threshold. */
33
+ tiers?: ModelPricingTier[];
34
+ /** Manual values always take precedence over catalogue defaults. */
35
+ source?: "manual" | "catalog";
36
+ }
37
+ export interface ModelPricingTier {
38
+ aboveTokens: number;
39
+ input: number;
40
+ output: number;
41
+ cacheRead?: number;
42
+ cacheWrite?: number;
43
+ }
44
+ export interface ModelConfig {
45
+ /** Stable local id, unique across providers: `${providerId}/${modelId}`. */
46
+ id: string;
47
+ providerId: string;
48
+ /** Id sent to the provider. */
49
+ modelId: string;
50
+ /** Label shown in the UI. */
51
+ name: string;
52
+ contextWindow: number;
53
+ maxOutputTokens: number;
54
+ supportsThinking: boolean;
55
+ supportsImages: boolean;
56
+ supportsTools: boolean;
57
+ pricing?: ModelPricing;
58
+ /**
59
+ * 这个模型可选的思考档位,从模型目录填入或在模型编辑器里勾选;不填用默认四档。
60
+ * 列表里没有 `off` 表示关不掉思考:选「关闭」时落到最浅的一档。
61
+ */
62
+ thinkingOptions?: ThinkingOption[];
63
+ /** Extra sampling parameters merged verbatim into the request body. */
64
+ samplingParams?: Record<string, unknown>;
65
+ /**
66
+ * Whether this model draws rather than converses. Only stored when it disagrees with the name —
67
+ * absent means `isImageModelId` decides (`config/image-models.ts`), so a model nobody overrode
68
+ * keeps following the rule as the rule improves.
69
+ */
70
+ imageOutput?: boolean;
71
+ }
72
+ export interface ProviderConfig {
73
+ id: string;
74
+ name: string;
75
+ baseUrl: string;
76
+ api: ApiFormat;
77
+ apiKey: string;
78
+ enabled: boolean;
79
+ models: ModelConfig[];
80
+ }
81
+ export interface RequestOptions {
82
+ signal?: AbortSignal;
83
+ maxTokens?: number;
84
+ temperature?: number;
85
+ thinking?: ThinkingLevel;
86
+ /** Merged over `ModelConfig.samplingParams`. */
87
+ samplingParams?: Record<string, unknown>;
88
+ fetch?: typeof globalThis.fetch;
89
+ /**
90
+ * 同一条对话前缀的稳定标识,让服务商把这些请求路由到同一处缓存(OpenAI 的 `prompt_cache_key`、
91
+ * OpenRouter 的 `x-session-id` 等,按端点选择见 `ai/cache-routing.ts`)。
92
+ *
93
+ * 前缀不同的对话要用不同的键:主会话传会话 id,子代理传各自区分开的 id——共用一个键会把不相干的
94
+ * 前缀挤到同一台机器上,互相顶掉缓存。不传就什么都不带。
95
+ */
96
+ cacheKey?: string;
97
+ /** Inspect or rewrite the outgoing body — used by the request inspector in the UI. */
98
+ onPayload?: (payload: unknown) => void;
99
+ /**
100
+ * How many times to attempt the request, including the first.
101
+ *
102
+ * Only the connection is retried, never a stream already in flight. 1 disables it.
103
+ */
104
+ retryAttempts?: number;
105
+ retryPolicy?: RetryPolicySource;
106
+ /** Told about each wait, so the UI can say why a turn is taking longer than usual. */
107
+ onRetry?: (info: {
108
+ attempt: number;
109
+ delayMs: number;
110
+ reason: string;
111
+ failure?: Failure;
112
+ }) => void;
113
+ }
114
+ export interface LlmContext {
115
+ systemPrompt: string;
116
+ messages: Message[];
117
+ tools: ToolSpec[];
118
+ }
119
+ export type StreamEvent = {
120
+ type: "start";
121
+ partial: AssistantMessage;
122
+ } | {
123
+ type: "text_start";
124
+ index: number;
125
+ } | {
126
+ type: "text_delta";
127
+ index: number;
128
+ delta: string;
129
+ partial: AssistantMessage;
130
+ } | {
131
+ type: "text_end";
132
+ index: number;
133
+ } | {
134
+ type: "thinking_start";
135
+ index: number;
136
+ } | {
137
+ type: "thinking_delta";
138
+ index: number;
139
+ delta: string;
140
+ partial: AssistantMessage;
141
+ } | {
142
+ type: "thinking_end";
143
+ index: number;
144
+ } | {
145
+ type: "toolcall_start";
146
+ index: number;
147
+ id: string;
148
+ name: string;
149
+ } | {
150
+ type: "toolcall_delta";
151
+ index: number;
152
+ delta: string;
153
+ partial: AssistantMessage;
154
+ } | {
155
+ type: "toolcall_end";
156
+ index: number;
157
+ partial: AssistantMessage;
158
+ } | {
159
+ type: "done";
160
+ message: AssistantMessage;
161
+ } | {
162
+ type: "error";
163
+ error: string;
164
+ message: AssistantMessage;
165
+ };
166
+ export interface Provider {
167
+ readonly api: ApiFormat;
168
+ stream(provider: ProviderConfig, model: ModelConfig, context: LlmContext, options: RequestOptions): AsyncGenerator<StreamEvent, AssistantMessage>;
169
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * 模型调用要用的两个工具类型。
3
+ *
4
+ * 工具的执行上下文(沙箱、MCP 表单、资源路由等)属于工具运行时,
5
+ * 不属于模型调用;Plume 的工具接口由 plume.agent 插件自己定义。
6
+ */
7
+ /** JSON Schema subset accepted by every provider we target. */
8
+ export interface JsonSchema {
9
+ type?: string;
10
+ properties?: Record<string, JsonSchema>;
11
+ items?: JsonSchema | JsonSchema[];
12
+ required?: string[];
13
+ enum?: unknown[];
14
+ description?: string;
15
+ default?: unknown;
16
+ additionalProperties?: boolean | JsonSchema;
17
+ [key: string]: unknown;
18
+ }
19
+ export interface ToolSpec {
20
+ name: string;
21
+ description: string;
22
+ parameters: JsonSchema;
23
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Core type model.
3
+ *
4
+ * Split by what the types are about — a message, a tool, a provider — and re-exported here so the
5
+ * hundred imports of `../types.ts` across the codebase keep meaning what they meant. The three
6
+ * files are the boundary; this one is the door.
7
+ */
8
+ export * from "./types/message.ts";
9
+ export * from "./types/tool.ts";
10
+ export * from "./types/provider.ts";
@@ -0,0 +1,18 @@
1
+ import type { ModelConfig, ModelPricing, Usage } from "../types.ts";
2
+ export interface SelectedPricingRates {
3
+ input: number;
4
+ output: number;
5
+ cacheRead: number;
6
+ cacheWrite: number;
7
+ }
8
+ /** Select the long-context tier for this request, then make every token bucket explicitly priced. */
9
+ export declare function selectPricingRates(usage: Pick<Usage, "input" | "cacheRead" | "cacheWrite">, pricing: ModelPricing): SelectedPricingRates;
10
+ export declare function costAtRates(usage: Pick<Usage, "input" | "output" | "cacheRead" | "cacheWrite">, rates: SelectedPricingRates): {
11
+ input: number;
12
+ output: number;
13
+ cacheRead: number;
14
+ cacheWrite: number;
15
+ total: number;
16
+ };
17
+ /** Fill in the cost breakdown from raw token counts and the model's per-million pricing. */
18
+ export declare function computeCost(usage: Usage, model: ModelConfig): Usage;
@@ -0,0 +1,2 @@
1
+ /** A JSON object: not `null`, not an array. What every reader of parsed config and wire bodies checks first. */
2
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;