@steerable/agent-shell 0.6.53 → 0.6.55

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 (47) hide show
  1. package/dist/brand.js +1 -1
  2. package/dist/host/pack-assembly.d.ts +6 -16
  3. package/dist/host/pack-assembly.js +3 -4
  4. package/dist/i18n.d.ts +23 -0
  5. package/dist/i18n.js +87 -0
  6. package/dist/image-attachment.d.ts +31 -4
  7. package/dist/image-attachment.js +13 -18
  8. package/dist/json-store.js +3 -6
  9. package/dist/local-backend/portable-bundle.js +8 -7
  10. package/dist/local-backend/router.js +2 -2
  11. package/dist/local-backend/skills/10-goal/SKILL.md +1 -1
  12. package/dist/local-backend/skills/11-loop/SKILL.md +1 -1
  13. package/dist/local-backend/skills/12-create-skill/SKILL.md +2 -2
  14. package/dist/local-script-registry.js +2 -2
  15. package/dist/mcp-server-registry.d.ts +3 -3
  16. package/dist/mcp-server-registry.js +2 -2
  17. package/dist/product-config.d.ts +10 -14
  18. package/dist/project-registry.d.ts +3 -3
  19. package/dist/project-registry.js +2 -2
  20. package/dist/runtime.d.ts +8 -54
  21. package/dist/runtime.js +15 -129
  22. package/dist/scenario/pack.d.ts +1 -1
  23. package/dist/scenario/registry.d.ts +4 -5
  24. package/dist/scenario/registry.js +2 -10
  25. package/dist/server/start.js +1 -2
  26. package/dist/sidecar/reverse-spawn.js +6 -15
  27. package/dist/sidecar/supervisor.d.ts +0 -2
  28. package/dist/sidecar/supervisor.js +7 -30
  29. package/dist/storage/index.js +1 -1
  30. package/dist/storage/write-lease-error.d.ts +2 -11
  31. package/dist/storage/write-lease-error.js +3 -8
  32. package/package.json +6 -13
  33. package/dist/app-update-electron.d.ts +0 -5
  34. package/dist/app-update-electron.js +0 -66
  35. package/dist/app-update.d.ts +0 -92
  36. package/dist/app-update.js +0 -213
  37. package/dist/host/ipc.d.ts +0 -11
  38. package/dist/host/ipc.js +0 -20
  39. package/dist/main.d.ts +0 -1
  40. package/dist/main.js +0 -967
  41. package/dist/preload-default.d.ts +0 -1
  42. package/dist/preload-default.js +0 -9
  43. package/dist/preload.cjs +0 -425
  44. package/dist/preload.d.ts +0 -20
  45. package/dist/preload.js +0 -442
  46. package/dist/single-instance.d.ts +0 -11
  47. package/dist/single-instance.js +0 -21
package/dist/preload.js DELETED
@@ -1,442 +0,0 @@
1
- import { contextBridge, ipcRenderer } from 'electron';
2
- // ---------------------------------------------------------------------------
3
- // Demo bypass: pre-seed the renderer's localStorage with a long-lived JWT so
4
- // the bundled web UI can talk to deeppath-api without going through the login
5
- // flow. This is intentionally limited to the agent shell and only activates
6
- // when AGENT_DEMO_TOKEN is set in the launch environment. Remove the env var
7
- // (or leave it unset) for a normal authenticated experience.
8
- // ---------------------------------------------------------------------------
9
- const DEMO_TOKEN_KEY = 'deeppath_access_token';
10
- const DEMO_USER_KEY = 'deeppath_user';
11
- const demoToken = process.env.AGENT_DEMO_TOKEN;
12
- const demoUserJson = process.env.AGENT_DEMO_USER;
13
- function seedDemoCredentials() {
14
- if (!demoToken)
15
- return;
16
- try {
17
- if (window.localStorage.getItem(DEMO_TOKEN_KEY) !== demoToken) {
18
- window.localStorage.setItem(DEMO_TOKEN_KEY, demoToken);
19
- }
20
- if (demoUserJson && !window.localStorage.getItem(DEMO_USER_KEY)) {
21
- window.localStorage.setItem(DEMO_USER_KEY, demoUserJson);
22
- }
23
- const expires = new Date(Date.now() + 30 * 864e5).toUTCString();
24
- document.cookie = `${DEMO_TOKEN_KEY}=${encodeURIComponent(demoToken)}; expires=${expires}; path=/; SameSite=Lax`;
25
- }
26
- catch (err) {
27
- console.error('[agent-shell] failed to seed demo token', err);
28
- }
29
- }
30
- try {
31
- seedDemoCredentials();
32
- }
33
- catch {
34
- // window/document may not be ready yet; retry on DOMContentLoaded.
35
- }
36
- window.addEventListener('DOMContentLoaded', seedDemoCredentials);
37
- /**
38
- * 构建并暴露 window.electron API(2.3 pack-preload 组合)。
39
- *
40
- * 包贡献经参数合并进顶层命名空间(如某包给出 `{ <pack>, <pack>Mock }`
41
- * 这样的 invoke 型命名空间);shell 本体不含任何
42
- * 包符号。由产品 preload 入口(products/<id>/preload.ts)或 dev 入口
43
- * (devtools/dev-preload.ts)调用。
44
- */
45
- export function buildPreloadApi(packContributions = {}) {
46
- const electronAPI = {
47
- runtime: 'local',
48
- platform: process.platform,
49
- onThemeChanged: (callback) => {
50
- ipcRenderer.on('theme-changed', (_event, isDark) => callback(isDark));
51
- },
52
- offThemeChanged: () => {
53
- ipcRenderer.removeAllListeners('theme-changed');
54
- },
55
- retryConnection: () => {
56
- ipcRenderer.send('retry-connection');
57
- },
58
- checkNetworkStatus: async () => {
59
- return await ipcRenderer.invoke('check-network-status');
60
- },
61
- onNetworkStatusChanged: (callback) => {
62
- ipcRenderer.on('network-status-changed', callback);
63
- },
64
- offNetworkStatusChanged: () => {
65
- ipcRenderer.removeAllListeners('network-status-changed');
66
- },
67
- showNotification: (payload) => {
68
- ipcRenderer.send('show-notification', payload);
69
- },
70
- onNotificationClicked: (callback) => {
71
- ipcRenderer.on('notification-clicked', (_event, data) => callback(data));
72
- },
73
- offNotificationClicked: () => {
74
- ipcRenderer.removeAllListeners('notification-clicked');
75
- },
76
- onMenuNewChat: (callback) => {
77
- ipcRenderer.on('menu:new-chat', () => callback());
78
- },
79
- offMenuNewChat: () => {
80
- ipcRenderer.removeAllListeners('menu:new-chat');
81
- },
82
- onMenuOpenTerminal: (callback) => {
83
- ipcRenderer.on('menu:open-terminal', () => callback());
84
- },
85
- offMenuOpenTerminal: () => {
86
- ipcRenderer.removeAllListeners('menu:open-terminal');
87
- },
88
- /**
89
- * AI 标题异步就绪。Backend 在每条 chat 的首条助手回复完成后 fire-and-forget
90
- * 跑 LLM 生成标题;完成后通过这个通道把 `{chatId, title}` 广播到所有 renderer。
91
- *
92
- * 返回 unsubscribe 函数——和 onMenuNewChat 不一样,这个通道有可能多处订阅
93
- * (主窗 + 未来的设置窗),所以用 add/remove 配对而不是 removeAllListeners。
94
- */
95
- onChatTitleUpdated: (callback) => {
96
- const handler = (_event, payload) => {
97
- if (typeof payload?.chatId === 'string' && typeof payload?.title === 'string') {
98
- callback({ chatId: payload.chatId, title: payload.title });
99
- }
100
- };
101
- ipcRenderer.on('chat-title-updated', handler);
102
- return () => {
103
- ipcRenderer.removeListener('chat-title-updated', handler);
104
- };
105
- },
106
- /**
107
- * 回合追问建议就绪。Backend 在助手回复完成后 fire-and-forget 生成 3 条
108
- * 下一轮用户输入;启发式兜底会立刻推一次,LLM 成功后再替换。返回 unsubscribe。
109
- */
110
- onSuggestedReplies: (callback) => {
111
- const handler = (_event, payload) => {
112
- if (typeof payload?.chatId === 'string' &&
113
- typeof payload?.messageId === 'string' &&
114
- Array.isArray(payload.suggestions) &&
115
- payload.suggestions.every((item) => typeof item === 'string')) {
116
- callback({
117
- chatId: payload.chatId,
118
- messageId: payload.messageId,
119
- suggestions: payload.suggestions,
120
- });
121
- }
122
- };
123
- ipcRenderer.on('suggested-replies', handler);
124
- return () => {
125
- ipcRenderer.removeListener('suggested-replies', handler);
126
- };
127
- },
128
- /**
129
- * 会话补建通知:Backend 在向本地不存在的 chatId 首次发送时按 URL 里的 id
130
- * 现场补建会话,并广播 `chat-created`({chatId, agentId})。返回 unsubscribe
131
- * (多处订阅用 add/remove 配对)。
132
- */
133
- onChatCreated: (callback) => {
134
- const handler = (_event, payload) => {
135
- if (typeof payload?.chatId === 'string' && payload.chatId) {
136
- callback({
137
- chatId: payload.chatId,
138
- agentId: typeof payload.agentId === 'string' ? payload.agentId : null,
139
- });
140
- }
141
- };
142
- ipcRenderer.on('chat-created', handler);
143
- return () => {
144
- ipcRenderer.removeListener('chat-created', handler);
145
- };
146
- },
147
- /**
148
- * 场景包广播事件订阅(3.1 通用化):按通道名订阅主进程广播,载荷
149
- * 原样透传(收窄校验在订阅方——包——一侧)。通道名约定带包前缀
150
- * (如 `<pack>:updated`)。返回解除订阅函数。
151
- */
152
- onPackEvent: (channel, callback) => {
153
- const handler = (_event, payload) => {
154
- callback(payload);
155
- };
156
- ipcRenderer.on(channel, handler);
157
- return () => {
158
- ipcRenderer.removeListener(channel, handler);
159
- };
160
- },
161
- /**
162
- * 4.6a 后台任务状态推送:任务到达终态(completed/failed)或 worktree
163
- * 合并/丢弃完成时,主进程广播 `task-updated`。载荷只带 chatId/taskId
164
- * ——renderer 收到后重新拉任务列表(GET /chats/:id/tasks),
165
- * 不把整条任务记录塞进 IPC(避免与 SQLite 双写漂移)。
166
- */
167
- onTaskUpdated: (callback) => {
168
- const handler = (_event, payload) => {
169
- if (typeof payload?.chatId === 'string' && typeof payload?.taskId === 'string') {
170
- callback({ chatId: payload.chatId, taskId: payload.taskId });
171
- }
172
- };
173
- ipcRenderer.on('task-updated', handler);
174
- return () => {
175
- ipcRenderer.removeListener('task-updated', handler);
176
- };
177
- },
178
- onTaskProcess: (callback) => {
179
- const handler = (_event, payload) => {
180
- if (typeof payload?.chatId !== 'string' || typeof payload?.taskId !== 'string')
181
- return;
182
- callback({
183
- chatId: payload.chatId,
184
- taskId: payload.taskId,
185
- timeline: payload.timeline,
186
- live: payload.live === true,
187
- });
188
- };
189
- ipcRenderer.on('task-process', handler);
190
- return () => {
191
- ipcRenderer.removeListener('task-process', handler);
192
- };
193
- },
194
- /**
195
- * W4-1 审批代数:sidecar 的 ApprovalExecutor 经反向通道请示,主进程把
196
- * 请求广播到 renderer(approval:request),输入区审批菜单应答后走
197
- * approval:decide 回到主进程。pending 供刷新后的 renderer 恢复仍在等待
198
- * 的请求。
199
- */
200
- approval: {
201
- onRequest: (callback) => {
202
- const handler = (_event, payload) => {
203
- const p = payload;
204
- if (typeof p?.requestId === 'string' && typeof p?.toolName === 'string') {
205
- callback(payload);
206
- }
207
- };
208
- ipcRenderer.on('approval:request', handler);
209
- return () => {
210
- ipcRenderer.removeListener('approval:request', handler);
211
- };
212
- },
213
- decide: async (decision) => {
214
- await ipcRenderer.invoke('approval:decide', decision);
215
- },
216
- pending: async () => {
217
- return await ipcRenderer.invoke('approval:pending');
218
- },
219
- },
220
- /**
221
- * W8 结构化提问:sidecar 的 ask_user 工具经反向通道请示,主进程把
222
- * 请求广播到 renderer(ask-user:request),问题卡片应答后走
223
- * ask-user:answer 回到主进程。pending 供刷新后的 renderer 恢复仍在等待
224
- * 的请求。
225
- */
226
- askUser: {
227
- onRequest: (callback) => {
228
- const handler = (_event, payload) => {
229
- const p = payload;
230
- if (typeof p?.requestId === 'string' && Array.isArray(p?.questions)) {
231
- callback(payload);
232
- }
233
- };
234
- ipcRenderer.on('ask-user:request', handler);
235
- return () => {
236
- ipcRenderer.removeListener('ask-user:request', handler);
237
- };
238
- },
239
- answer: async (reply) => {
240
- await ipcRenderer.invoke('ask-user:answer', reply);
241
- },
242
- pending: async () => {
243
- return await ipcRenderer.invoke('ask-user:pending');
244
- },
245
- },
246
- local: {
247
- selectDirectory: async (options) => {
248
- return await ipcRenderer.invoke('local:select-directory', options);
249
- },
250
- saveTextFile: async (options) => {
251
- return await ipcRenderer.invoke('local:save-text-file', options);
252
- },
253
- captureScreenshot: async (rect) => {
254
- return await ipcRenderer.invoke('local:capture-screenshot', rect ? { rect } : {});
255
- },
256
- execShell: async (request) => {
257
- return await ipcRenderer.invoke('local:exec-shell', request);
258
- },
259
- readFile: async (request) => {
260
- return await ipcRenderer.invoke('local:read-file', request);
261
- },
262
- writeFile: async (request) => {
263
- return await ipcRenderer.invoke('local:write-file', request);
264
- },
265
- openPath: async (request) => {
266
- return await ipcRenderer.invoke('local:open-path', request);
267
- },
268
- listScripts: async () => {
269
- return await ipcRenderer.invoke('local:list-scripts');
270
- },
271
- addScript: async (input) => {
272
- return await ipcRenderer.invoke('local:add-script', input);
273
- },
274
- updateScript: async (id, updates) => {
275
- return await ipcRenderer.invoke('local:update-script', { id, updates });
276
- },
277
- deleteScript: async (id) => {
278
- return await ipcRenderer.invoke('local:delete-script', id);
279
- },
280
- runScript: async (id) => {
281
- return await ipcRenderer.invoke('local:run-script', id);
282
- },
283
- getPlatformInfo: () => ({ platform: process.platform }),
284
- updateSafetyConfig: async (config) => {
285
- return await ipcRenderer.invoke('local:update-safety-config', config);
286
- },
287
- },
288
- /** 会话附件:把源文件路径 / base64 字节拷贝进会话空间(`attachments:save`)。 */
289
- attachments: {
290
- save: async (input) => {
291
- return await ipcRenderer.invoke('attachments:save', input);
292
- },
293
- },
294
- localBackend: {
295
- request: async (input) => {
296
- const result = await ipcRenderer.invoke('local-backend:request', input);
297
- if (!result.ok) {
298
- const err = new Error(result.error || 'Local backend request failed');
299
- err.status = result.status;
300
- throw err;
301
- }
302
- return result.data;
303
- },
304
- /**
305
- * 启动一条本地后端流。
306
- * 注意:Electron contextBridge 不支持把 ReadableStream 跨上下文传给 renderer
307
- * (structured clone 会把它剥成空对象,导致 web UI 拿到的 Response.body 永远不出数据)。
308
- * 这里只暴露 `startStream(input, onEvent)` 原语,由 web UI 端在 renderer 上下文
309
- * 内自己构造 ReadableStream 并喂入 onEvent 回调(callback 跨上下文是 proxied 的)。
310
- *
311
- * 返回 streamId 用于后续 cancelStream(id)。
312
- */
313
- startStream: async (input, onEvent) => {
314
- const result = (await ipcRenderer.invoke('local-backend:stream', input));
315
- if (!result.ok || !result.streamId) {
316
- try {
317
- onEvent({ type: 'error', error: result.error || `start stream failed (${result.status ?? 500})` });
318
- }
319
- catch {
320
- // best-effort: callback 抛错不应影响 IPC 路径
321
- }
322
- return null;
323
- }
324
- const channel = `local-backend:stream:${result.streamId}`;
325
- const handler = (_event, payload) => {
326
- try {
327
- onEvent(payload);
328
- }
329
- catch (err) {
330
- console.error('[preload] stream event callback threw', err);
331
- }
332
- if (payload.type === 'end' || payload.type === 'error') {
333
- ipcRenderer.removeListener(channel, handler);
334
- }
335
- };
336
- ipcRenderer.on(channel, handler);
337
- return result.streamId;
338
- },
339
- cancelStream: (streamId) => {
340
- ipcRenderer.send(`local-backend:stream:${streamId}:cancel`);
341
- },
342
- /**
343
- * 轮中转向:把一条用户消息注入该 chat 正在运行的 CoreLoop 回合。
344
- * 返回 true 表示已被接受(UI 立即上屏);false 表示当前没有可转向的
345
- * 回合(TS 循环路径或回合刚结束)——调用方应保留草稿。
346
- */
347
- steerChat: async (chatId, content) => {
348
- const result = (await ipcRenderer.invoke('local-backend:steer', {
349
- chatId,
350
- content,
351
- }));
352
- return result?.ok === true;
353
- },
354
- traces: {
355
- list: async (chatId, limit = 50) => {
356
- const path = `/api/v2/local/traces?chatId=${encodeURIComponent(chatId)}&limit=${limit}`;
357
- return await electronAPI.localBackend.request({ method: 'GET', path });
358
- },
359
- get: async (traceId) => {
360
- const path = `/api/v2/local/traces/${encodeURIComponent(traceId)}`;
361
- return await electronAPI.localBackend.request({ method: 'GET', path });
362
- },
363
- },
364
- },
365
- agent: {
366
- hasToken: async () => {
367
- return true;
368
- },
369
- provision: async (_userJWT) => {
370
- return { success: true };
371
- },
372
- start: async () => {
373
- return { success: true };
374
- },
375
- getStatus: async () => {
376
- return { status: 'registered', agentId: 'local-agent' };
377
- },
378
- getInfo: async () => {
379
- return {
380
- id: 'local-agent',
381
- machineId: 'local',
382
- name: 'Local Agent',
383
- platform: process.platform,
384
- hostname: 'localhost',
385
- shell: process.platform === 'darwin' ? 'zsh' : 'bash',
386
- isOnline: true,
387
- };
388
- },
389
- onNeedProvision: (_callback) => undefined,
390
- offNeedProvision: () => undefined,
391
- },
392
- terminal: {
393
- list: async () => {
394
- return await ipcRenderer.invoke('terminal:list');
395
- },
396
- spawn: async (options = {}) => {
397
- return await ipcRenderer.invoke('terminal:spawn', options);
398
- },
399
- ensure: async (options = {}) => {
400
- return await ipcRenderer.invoke('terminal:ensure', options);
401
- },
402
- write: async (id, data) => {
403
- return await ipcRenderer.invoke('terminal:write', { id, data });
404
- },
405
- resize: async (id, cols, rows) => {
406
- return await ipcRenderer.invoke('terminal:resize', { id, cols, rows });
407
- },
408
- kill: async (id) => {
409
- return await ipcRenderer.invoke('terminal:kill', id);
410
- },
411
- exec: async (payload) => {
412
- return await ipcRenderer.invoke('terminal:exec', payload);
413
- },
414
- onData: (callback) => {
415
- const fn = (_event, payload) => callback(payload);
416
- ipcRenderer.on('terminal:data', fn);
417
- return () => ipcRenderer.removeListener('terminal:data', fn);
418
- },
419
- onExit: (callback) => {
420
- const fn = (_event, payload) => callback(payload);
421
- ipcRenderer.on('terminal:exit', fn);
422
- return () => ipcRenderer.removeListener('terminal:exit', fn);
423
- },
424
- onSpawned: (callback) => {
425
- const fn = (_event, session) => callback(session);
426
- ipcRenderer.on('terminal:spawned', fn);
427
- return () => ipcRenderer.removeListener('terminal:spawned', fn);
428
- },
429
- },
430
- app: {
431
- snapshot: () => ipcRenderer.invoke('app:release-snapshot'),
432
- check: () => ipcRenderer.invoke('app:release-check'),
433
- install: () => ipcRenderer.invoke('app:release-install'),
434
- onState: (callback) => {
435
- const fn = (_event, snapshot) => callback(snapshot);
436
- ipcRenderer.on('app-update-state', fn);
437
- return () => ipcRenderer.removeListener('app-update-state', fn);
438
- },
439
- },
440
- };
441
- contextBridge.exposeInMainWorld('electron', { ...electronAPI, ...packContributions });
442
- }
@@ -1,11 +0,0 @@
1
- /**
2
- * Electron single-instance lock. Imported first from main.ts so a second
3
- * OS process exits before LocalStore opens the product DB.
4
- *
5
- * `requestSingleInstanceLock` must run before `app.whenReady`. `process.exit`
6
- * is required because `app.quit()` is async and later imports would still
7
- * construct LocalStore against the same userData.
8
- */
9
- type SecondInstanceHandler = () => void;
10
- export declare function setSecondInstanceHandler(handler: SecondInstanceHandler): void;
11
- export {};
@@ -1,21 +0,0 @@
1
- /**
2
- * Electron single-instance lock. Imported first from main.ts so a second
3
- * OS process exits before LocalStore opens the product DB.
4
- *
5
- * `requestSingleInstanceLock` must run before `app.whenReady`. `process.exit`
6
- * is required because `app.quit()` is async and later imports would still
7
- * construct LocalStore against the same userData.
8
- */
9
- import { app } from 'electron';
10
- const gotTheLock = app.requestSingleInstanceLock();
11
- if (!gotTheLock) {
12
- app.quit();
13
- process.exit(0);
14
- }
15
- let secondInstanceHandler = null;
16
- export function setSecondInstanceHandler(handler) {
17
- secondInstanceHandler = handler;
18
- }
19
- app.on('second-instance', () => {
20
- secondInstanceHandler?.();
21
- });