@solazah/solazah-runtime 0.1.162 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solazah/solazah-runtime",
3
- "version": "0.1.162",
3
+ "version": "0.2.9",
4
4
  "main": "index.cjs",
5
5
  "module": "index.js",
6
6
  "types": "types/index.d.ts",
@@ -41,12 +41,38 @@ export interface CopilotChatOptions extends CopilotBaseOptions {
41
41
  * 流式调用在 catch 分支抛错时由 streaming 层调用此回调,让上层有机会回滚预占的配额。
42
42
  */
43
43
  onStreamError?: () => Promise<void> | void;
44
+ /**
45
+ * 内部字段:main 进程构造,仅用于本进程内回调(不跨 IPC)。
46
+ * 本轮**真正结束**时调用(成功、失败、中止都算),发起方据此销掉这一轮的记账。
47
+ *
48
+ * 为什么不用 port 关闭代替:正常跑完的那一轮,渲染端的 for-await 自然退出,
49
+ * 按 JS 语义不会调迭代器的 return(),port 于是从不关闭。把结束这件事挂在别人的
50
+ * 生命周期上,就会得到一个「永远没结束」的运行中状态。
51
+ */
52
+ onRunFinished?: () => void;
53
+ }
54
+ /** 随用户一句话附上的一张图(data URL)。 */
55
+ export interface CopilotInputImage {
56
+ url: string;
57
+ mime?: string;
58
+ }
59
+ /** 待投递给主 agent 的一条内容:notice = 后台任务完成通知,interject = 用户在生成途中插的话。 */
60
+ export interface CopilotPendingInjection {
61
+ kind: "notice" | "interject";
62
+ /** 注入时用作消息 id(落库切片按 id 去重),同时是撤回的凭据。 */
63
+ id: string;
64
+ /** 投给模型看的整段文案。两条投递路径共用同一份,保证模型在轮中和轮间看到的内容一致。 */
65
+ text: string;
66
+ /** 随插话附上的图片;通知不带。 */
67
+ images?: CopilotInputImage[];
44
68
  }
45
69
  export interface CopilotOptions extends CopilotChatOptions {
46
70
  mcp?: string[];
47
71
  projectDir?: string;
48
72
  recursionLimit?: number;
49
73
  thread_id?: string;
74
+ /** 经哪个桥发起(如 wechat)。这一轮新建对话时写进对话行的 bridge。 */
75
+ bridge?: string;
50
76
  run_id?: string;
51
77
  /**
52
78
  * 计费用的模型名(LiteLLM 的 model_name,如 deepseek-v4-pro)。
@@ -89,14 +115,10 @@ export interface CopilotOptions extends CopilotChatOptions {
89
115
  */
90
116
  persistFromToolCallId?: string;
91
117
  /**
92
- * 视觉模型的图片输入。每条 image_url 直接作为 content part 拼到本轮 HumanMessage 上,
93
- * 与 input 文本同一条消息。仅 run() 这条路径用得到;completion/chat 不接。
94
- * 渲染层负责判定模型是否支持视觉再传,这里不再二次过滤。
118
+ * 本轮用户消息附上的图片。视觉模型拼成 image_url 内容块,纯文本模型丢弃(见 CopilotHumanContent)。
119
+ * 仅 run() 这条路径用得到;completion/chat 不接。
95
120
  */
96
- images?: {
97
- url: string;
98
- mime?: string;
99
- }[];
121
+ images?: CopilotInputImage[];
100
122
  }
101
123
  export type CopilotRunOptions = CopilotOptions;
102
124
  /** 一行 = 某个模型某一天的 token 统计。model_name 是模型 id,显示名要另去模型清单里换。 */
@@ -125,7 +147,32 @@ export default class Copilot {
125
147
  */
126
148
  completion: (input: string, options: any) => Promise<any>;
127
149
  chat: (input: string, options: CopilotRunOptions) => Promise<AsyncIterableIterator<any>>;
128
- run: (input: string, options: CopilotRunOptions) => Promise<AsyncIterableIterator<any>>;
150
+ /**
151
+ * 跑一轮。返回本轮的 requestId,**事件不在返回值里** —— 用 `onRunEvent` 订阅。
152
+ *
153
+ * 以前返回一条只有发起方拿得到的流,于是别人开的那一轮谁也看不见:
154
+ * 微信桥、定时任务在后台跑的轮次,本机界面只能等落库刷出结果。
155
+ * 订阅必须在发起**之前**建立,否则会漏掉开头那几条。
156
+ */
157
+ run: (input: string, options: CopilotRunOptions) => Promise<string>;
158
+ /** 轮次事件在本进程里的全部订阅者。端口只开一条,见 onRunEvent。 */
159
+ private readonly runEventSubscribers;
160
+ private runPortRequested;
161
+ /**
162
+ * 订阅轮次事件。**所有轮次都从这一条端口来**,按 threadId / requestId 自己认领;**多个订阅者互不影响**。
163
+ *
164
+ * 第一次订阅时向主进程报到,主进程开一条 MessagePort 送过来,之后每一轮的事件都从这条端口进来,
165
+ * 不经 ipcRenderer 的通道,没报到的渲染进程一条都收不到。端口只开一条:同一渲染进程里谁要谁挂进
166
+ * 订阅者集合。报到与开跑都是本进程发出的 IPC,先后有序:先报到再 run,开头那几条不会漏。
167
+ *
168
+ * @returns 退订函数
169
+ */
170
+ onRunEvent: (onEvent: (e: {
171
+ requestId: string;
172
+ threadId?: string;
173
+ event: string;
174
+ data?: any;
175
+ }) => void) => (() => void);
129
176
  /**
130
177
  * 按 requestId 中止正在进行的 chat/run/resume 流式执行:
131
178
  * 触发对应 AbortController 并向 provider 发出中止信号。
@@ -142,8 +189,9 @@ export default class Copilot {
142
189
  * @param threadId 会话 ID
143
190
  * @param id 消息 id,由渲染层生成;撤回与去重都按它
144
191
  * @param text 插话正文
192
+ * @param images 随插话附上的图片
145
193
  */
146
- interjectPush: (threadId: string, id: string, text: string) => Promise<any>;
194
+ interjectPush: (threadId: string, id: string, text: string, images?: CopilotInputImage[]) => Promise<any>;
147
195
  /**
148
196
  * 撤回一条还没被领走的插话。
149
197
  *
@@ -159,6 +207,7 @@ export default class Copilot {
159
207
  threadId: string;
160
208
  ids: string[];
161
209
  }) => void) => (() => void);
162
- resume: (response: any, options: CopilotRunOptions) => Promise<AsyncIterableIterator<any>>;
210
+ /** 续一轮。与 run 同理:返回 requestId,事件用 `onRunEvent` 订阅。 */
211
+ resume: (response: any, options: CopilotRunOptions) => Promise<string>;
163
212
  private invokeStream;
164
213
  }
@@ -1,3 +1,4 @@
1
+ import { CopilotPendingInjection } from './Copilot';
1
2
  export interface CopilotSubAgentRunDto {
2
3
  id: string;
3
4
  type: string;
@@ -150,11 +151,11 @@ export default class CopilotSubAgent {
150
151
  threadId: string;
151
152
  }) => void) => (() => void);
152
153
  /**
153
- * 取走某会话待注入的文案(取走即清空):后台通知与用户插话共用同一个队列。
154
+ * 取走某会话待注入的内容(取走即清空):后台通知与用户插话共用同一个队列。
154
155
  * 与主进程「轮中注入」共用同一个队列,同一条通知只会被其中一方领到。
155
156
  *
156
157
  * @param threadId 会话 ID
157
- * @returns 待投递文案数组;没有则空数组
158
+ * @returns 待投递内容;没有则空数组
158
159
  */
159
- takePendingInjections: (threadId: string) => Promise<string[]>;
160
+ takePendingInjections: (threadId: string) => Promise<CopilotPendingInjection[]>;
160
161
  }
package/types/Plugin.d.ts CHANGED
@@ -8,9 +8,14 @@ export interface PluginToolReply {
8
8
  /** 本机绝对路径,png / jpg / webp / gif,最多 4 张 */
9
9
  images?: string[];
10
10
  }
11
+ /** 这次工具调用发生在哪。 */
12
+ export interface PluginToolContext {
13
+ /** 发起调用的那条对话;不在对话里调用(如命令行直接驱动工具)时没有 */
14
+ threadId?: string;
15
+ }
11
16
  export interface PluginTool {
12
17
  name: string;
13
- cb: (args: any) => Promise<string | PluginToolReply> | string | PluginToolReply;
18
+ cb: (args: any, context: PluginToolContext) => Promise<string | PluginToolReply> | string | PluginToolReply;
14
19
  description: string;
15
20
  schema: object | null;
16
21
  timeoutMs?: number;
@@ -44,11 +49,12 @@ export default class Plugin {
44
49
  /**
45
50
  * 给 Otta 注册 AI 工具。
46
51
  * schema 传 JSON Schema 对象(如 z.toJSONSchema(...) 的返回值),无参工具传 null;
47
- * cb 收到已按 schema 拆包的参数对象(无参工具收到 {}),返回字符串给 LLM(返回对象会被 JSON.stringify)。
52
+ * cb 收到已按 schema 拆包的参数对象(无参工具收到 {}),返回字符串给 LLM(返回对象会被 JSON.stringify);
53
+ * 第二个参数是调用上下文(见 PluginToolContext)。
48
54
  * options.timeoutMs 是宿主等待回执的上限,执行耗时长的工具要显式给。
49
55
  * 同名重复注册为覆盖(HMR 安全)。
50
56
  */
51
- tool: (name: string, description: string, schema: object | null, cb: (args: any) => Promise<string> | string, options?: PluginToolOptions) => (args: any) => Promise<string> | string;
57
+ tool: (name: string, description: string, schema: object | null, cb: (args: any, context: PluginToolContext) => Promise<string> | string, options?: PluginToolOptions) => (args: any, context: PluginToolContext) => Promise<string> | string;
52
58
  onSearch: (commandName: string, handler: any) => Promise<void>;
53
59
  onAction: (commandName: string, action: string, handler: any) => Promise<void>;
54
60
  /**
@@ -65,6 +71,14 @@ export default class Plugin {
65
71
  * @returns 无返回。窗口不存在时静默忽略。
66
72
  */
67
73
  setHeight: (height: number) => Promise<any>;
74
+ /**
75
+ * 把发起方插件窗口的宽高一起设为指定像素值。调用前会清掉最小尺寸限制,免得被 minWidth/minHeight 钳住。
76
+ *
77
+ * @param width 目标宽度(像素)。
78
+ * @param height 目标高度(像素)。
79
+ * @returns 无返回。窗口不存在时静默忽略。
80
+ */
81
+ setSize: (width: number, height: number) => Promise<any>;
68
82
  /**
69
83
  * 读发起方插件窗口当前的位置与尺寸。
70
84
  * 用 -webkit-app-region 拖动时页面收不到任何鼠标事件,窗口位置只有主进程知道 ——
@@ -130,6 +144,35 @@ export default class Plugin {
130
144
  * @returns 无返回。
131
145
  */
132
146
  hide: () => Promise<any>;
147
+ /**
148
+ * 关掉发起方插件窗口:内容视图一并卸载,渲染进程立刻释放。发起方在主窗口里时退回启动器主页,不关主窗口。
149
+ *
150
+ * 与 hide 的分野是复用:hide 把窗口与视图整套留着换零延迟唤起,之后由巡检按收起时长回收。
151
+ * 每次都按新参数重建、没有可复用内容的窗口(采集边框、采集控制条这类)用 close,别占着一个渲染进程等巡检。
152
+ * 反过来,热路径上反复唤起的浮层(截图浮层这类)该留给 hide —— close 掉就等于每次都付一次冷启动。
153
+ *
154
+ * 调用方自己随即被销毁,这个 Promise **不会 resolve**:别 await 它,也别在它后面接代码。
155
+ *
156
+ * @returns 不 resolve。
157
+ */
158
+ close: () => Promise<any>;
159
+ /**
160
+ * 让发起方插件视图真正拿到键盘焦点:先激活承载它的窗口,再聚焦这个视图。
161
+ * 透明 floating 浮层鼠标可点但键盘不激活、输入框敲不进字时用它。
162
+ *
163
+ * @param force 顶到前台。授权流程从系统浏览器切回来这类场景要开:此刻前台是别的应用,
164
+ * 普通 focus 会被 Windows 的前台锁定丢成任务栏闪烁。
165
+ * @returns 无返回。
166
+ */
167
+ focus: (force?: boolean) => Promise<any>;
168
+ /**
169
+ * 设置发起方窗口是否置顶(screen-saver 级,压得住普通窗口与菜单栏)。
170
+ * 钉屏窗口加载时调它,确保始终置顶且失焦不收起。
171
+ *
172
+ * @param value 是否置顶。
173
+ * @returns 设置后的置顶状态(boolean)。
174
+ */
175
+ setAlwaysOnTop: (value: boolean) => Promise<boolean>;
133
176
  /**
134
177
  * 切换发起方窗口的置顶状态(screen-saver 级),返回切换后的状态。
135
178
  * 同时向发起方视图广播 plugin.alwaysOnTopChanged 事件,便于渲染层同步回显。
@@ -22,6 +22,16 @@ export default class SystemShell {
22
22
  * @returns 操作完成(`Promise<void>`);返回值被丢弃,异常直接传播给调用方。
23
23
  */
24
24
  openPath: (path: string) => Promise<any>;
25
+ /**
26
+ * 把一段字节落到临时区再用默认程序打开。
27
+ *
28
+ * 给「文件不在这台机器上」用:连着别的电脑看对话时,附件的实体在执行端,
29
+ * 这一端只有内容本身,拿路径打不开。临时区每次启动清空。
30
+ *
31
+ * @param name 展示用文件名,决定扩展名,也就决定用哪个程序打开。
32
+ * @param base64 文件内容。
33
+ */
34
+ openBuffer: (name: string, base64: string) => Promise<any>;
25
35
  /** 在资源管理器/Finder 中显示并选中该文件(或文件夹) */
26
36
  showItemInFolder: (path: string) => Promise<any>;
27
37
  openTerminal: (options?: {