shennian 0.2.89 → 0.2.90

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 (118) hide show
  1. package/dist/assets/wechat-channel/macos/manifest.json +13 -4
  2. package/dist/assets/wechat-channel/macos/shennian-wechat-channel-helper +0 -0
  3. package/dist/bin/shennian.js +1 -1
  4. package/dist/publish-build-manifest.json +548 -0
  5. package/dist/scripts/wechat-rpa-confirmation.mjs +5 -97
  6. package/dist/src/agent-env.js +4 -105
  7. package/dist/src/agents/adapter.js +1 -19
  8. package/dist/src/agents/claude.js +8 -305
  9. package/dist/src/agents/codex-control.js +2 -188
  10. package/dist/src/agents/codex-utils.js +7 -200
  11. package/dist/src/agents/codex.js +15 -916
  12. package/dist/src/agents/command-spec.js +2 -413
  13. package/dist/src/agents/config-status.js +1 -226
  14. package/dist/src/agents/cursor.js +1 -249
  15. package/dist/src/agents/custom.js +4 -271
  16. package/dist/src/agents/detect.js +1 -56
  17. package/dist/src/agents/external-channel-instructions.js +10 -94
  18. package/dist/src/agents/gemini.js +1 -173
  19. package/dist/src/agents/manager.js +13 -157
  20. package/dist/src/agents/model-registry/cache.js +1 -37
  21. package/dist/src/agents/model-registry/discovery.js +2 -187
  22. package/dist/src/agents/model-registry/parsers.js +4 -447
  23. package/dist/src/agents/model-registry/runner.js +1 -30
  24. package/dist/src/agents/model-registry/service.js +1 -78
  25. package/dist/src/agents/model-registry/types.js +1 -8
  26. package/dist/src/agents/model-registry.js +1 -18
  27. package/dist/src/agents/openclaw.js +2 -275
  28. package/dist/src/agents/opencode.js +1 -231
  29. package/dist/src/agents/pi-context.js +12 -217
  30. package/dist/src/agents/pi.js +14 -723
  31. package/dist/src/agents/platform-instructions.js +9 -54
  32. package/dist/src/channels/base.js +1 -3
  33. package/dist/src/channels/registry.js +1 -30
  34. package/dist/src/channels/reply-split.js +10 -89
  35. package/dist/src/channels/runtime.js +5 -564
  36. package/dist/src/channels/secret-registry.js +1 -46
  37. package/dist/src/channels/websocket.js +8 -378
  38. package/dist/src/channels/wechat-channel/anchor.js +1 -65
  39. package/dist/src/channels/wechat-channel/client.js +1 -96
  40. package/dist/src/channels/wechat-channel/cooldown.js +1 -38
  41. package/dist/src/channels/wechat-channel/fingerprint.js +1 -71
  42. package/dist/src/channels/wechat-channel/helper-assets.d.ts +10 -1
  43. package/dist/src/channels/wechat-channel/helper-assets.js +1 -68
  44. package/dist/src/channels/wechat-channel/helper-client.js +3 -149
  45. package/dist/src/channels/wechat-channel/helper-protocol.d.ts +1 -1
  46. package/dist/src/channels/wechat-channel/helper-protocol.js +1 -115
  47. package/dist/src/channels/wechat-channel/index.d.ts +1 -0
  48. package/dist/src/channels/wechat-channel/index.js +1 -19
  49. package/dist/src/channels/wechat-channel/ledger.js +1 -54
  50. package/dist/src/channels/wechat-channel/media-resolver.js +1 -181
  51. package/dist/src/channels/wechat-channel/message-key.js +1 -105
  52. package/dist/src/channels/wechat-channel/observer.js +1 -118
  53. package/dist/src/channels/wechat-channel/outbound-ledger.d.ts +3 -0
  54. package/dist/src/channels/wechat-channel/outbound-ledger.js +2 -112
  55. package/dist/src/channels/wechat-channel/outbound-sender.d.ts +26 -0
  56. package/dist/src/channels/wechat-channel/outbound-sender.js +1 -0
  57. package/dist/src/channels/wechat-channel/preflight.js +1 -48
  58. package/dist/src/channels/wechat-channel/runner.js +1 -84
  59. package/dist/src/channels/wechat-channel/runtime.js +1 -66
  60. package/dist/src/channels/wechat-channel/scheduler.d.ts +5 -0
  61. package/dist/src/channels/wechat-channel/scheduler.js +1 -152
  62. package/dist/src/channels/wechat-rpa/macos-flow.js +1 -96
  63. package/dist/src/channels/wechat-rpa/macos.js +6 -48
  64. package/dist/src/channels/wechat-rpa/normalizer.js +7 -127
  65. package/dist/src/channels/wechat-rpa.js +6 -1028
  66. package/dist/src/channels/wecom.js +4 -357
  67. package/dist/src/commands/agent.js +6 -131
  68. package/dist/src/commands/daemon-windows.js +8 -48
  69. package/dist/src/commands/daemon.js +19 -1013
  70. package/dist/src/commands/external-attachments.js +1 -51
  71. package/dist/src/commands/external.js +1 -137
  72. package/dist/src/commands/manager.js +2 -391
  73. package/dist/src/commands/pair-qr.js +1 -6
  74. package/dist/src/commands/pair.js +9 -287
  75. package/dist/src/commands/tools.js +1 -34
  76. package/dist/src/commands/upgrade.js +1 -198
  77. package/dist/src/config/index.js +1 -35
  78. package/dist/src/daemon-log.js +6 -58
  79. package/dist/src/env-path.js +1 -64
  80. package/dist/src/fs/boundary.js +1 -126
  81. package/dist/src/fs/handler.js +1 -130
  82. package/dist/src/fs/security.js +1 -32
  83. package/dist/src/fs/text-decoder.js +1 -110
  84. package/dist/src/index.js +2 -404
  85. package/dist/src/log-reporter.js +1 -16
  86. package/dist/src/manager/prompt.js +29 -34
  87. package/dist/src/manager/registry.js +2 -269
  88. package/dist/src/manager/runtime.js +19 -1007
  89. package/dist/src/native-fusion/config.js +1 -5
  90. package/dist/src/native-fusion/opencode-parser.js +3 -123
  91. package/dist/src/native-fusion/parser-common.js +8 -264
  92. package/dist/src/native-fusion/parsers.js +8 -729
  93. package/dist/src/native-fusion/service.js +2 -225
  94. package/dist/src/native-fusion/state.js +1 -22
  95. package/dist/src/native-fusion/types.js +1 -1
  96. package/dist/src/region.js +1 -88
  97. package/dist/src/relay/client.js +1 -343
  98. package/dist/src/session/archive-zip.js +1 -220
  99. package/dist/src/session/handlers/agent-config.js +1 -150
  100. package/dist/src/session/handlers/agents.js +1 -55
  101. package/dist/src/session/handlers/chat.js +2 -751
  102. package/dist/src/session/handlers/control.js +1 -55
  103. package/dist/src/session/handlers/fs.js +1 -783
  104. package/dist/src/session/handlers/session-refresh.js +1 -47
  105. package/dist/src/session/handlers/skills.js +1 -121
  106. package/dist/src/session/handlers/title.js +1 -60
  107. package/dist/src/session/handlers/tool-detail.js +1 -218
  108. package/dist/src/session/manager.js +1 -319
  109. package/dist/src/session/projection.js +1 -54
  110. package/dist/src/session/queue.js +4 -317
  111. package/dist/src/session/remote-attachments.js +1 -72
  112. package/dist/src/session/store.js +3 -109
  113. package/dist/src/session/types.js +1 -4
  114. package/dist/src/skills/registry.js +15 -148
  115. package/dist/src/skills/setup.js +1 -101
  116. package/dist/src/tools/markdown-to-pdf.js +10 -346
  117. package/dist/src/upgrade/engine.js +3 -347
  118. package/package.json +3 -2
@@ -1,319 +1 @@
1
- // @arch docs/architecture/cli/daemon.md#会话管理
2
- // @test src/__tests__/session-store.test.ts
3
- // @test src/__tests__/model-switching.test.ts
4
- import { getRegisteredAgents, unregisterAgent } from '../agents/adapter.js';
5
- import { loadConfig } from '../config/index.js';
6
- import { handleUpgradeStart, handleUpgradeStatus } from '../commands/upgrade.js';
7
- import { handleAgentsRefresh, handleModelsRefresh } from './handlers/agents.js';
8
- import { handleAgentConfigClear, handleAgentConfigGet, handleAgentConfigTest, handleAgentConfigUpsert, } from './handlers/agent-config.js';
9
- import { handleChatAbort, handleChatSend } from './handlers/chat.js';
10
- import { handleSessionRefresh } from './handlers/session-refresh.js';
11
- import { handleSessionToolDetail } from './handlers/tool-detail.js';
12
- import { handleSessionTitleSet } from './handlers/title.js';
13
- import { cleanupPendingTransfers, handleFsLs, handleFsRead, handleFsRename, handleFsWrite, handleFsTransfer, handleFsTransferAbort, handleFsTransferChunk, handleFsTransferFinish, handleFsTransferStart, handleFsExportMarkdownPdf, handleFsArchiveZip, } from './handlers/fs.js';
14
- import { handleSkillDoctor, handleSkillInstall, handleSkillList, handleSkillSetup, handleSkillUse, } from './handlers/skills.js';
15
- import { handleRegionProbe, handleRegionSwitch, handleUpgradeSetPolicy, } from './handlers/control.js';
16
- import { ManagerRuntimeService, setManagerRuntimeService } from '../manager/runtime.js';
17
- import { ChatQueueManager } from './queue.js';
18
- import { createAuthorizedFsRoot, resolveAuthorizedPath, resolveSessionWorkDir, } from '../fs/boundary.js';
19
- // Side-effect imports to register built-in agent adapters.
20
- import '../agents/claude.js';
21
- import '../agents/codex.js';
22
- import '../agents/gemini.js';
23
- import '../agents/cursor.js';
24
- // OpenClaw support is intentionally disabled.
25
- // import '../agents/openclaw.js'
26
- import '../agents/opencode.js';
27
- import '../agents/pi.js';
28
- import '../agents/manager.js';
29
- import { registerCustomAgent } from '../agents/custom.js';
30
- const MAX_SESSIONS = 50;
31
- export { resolveSessionWorkDir } from '../fs/boundary.js';
32
- export class SessionManager {
33
- client;
34
- nativeFusion;
35
- cliVersion;
36
- sessions = new Map();
37
- /** Track processed request IDs to deduplicate replayed offline messages */
38
- processedReqIds = new Set();
39
- /** Accumulate agent text per run for messageSummary on final event */
40
- runTextAcc = new Map();
41
- /** In-flight chunked uploads: transferId → metadata */
42
- pendingTransfers = new Map();
43
- managerRuntime;
44
- chatQueue;
45
- activityProbeTimer = null;
46
- constructor(client, nativeFusion = null, cliVersion) {
47
- this.client = client;
48
- this.nativeFusion = nativeFusion;
49
- this.cliVersion = cliVersion;
50
- this.managerRuntime = new ManagerRuntimeService({
51
- getRuntime: () => this.getRuntime(),
52
- dispatchReq: (req) => this.handleReq(req),
53
- });
54
- this.chatQueue = new ChatQueueManager({
55
- getRuntime: () => this.getRuntime(),
56
- dispatchReq: (req) => this.handleReq(req),
57
- });
58
- setManagerRuntimeService(this.managerRuntime);
59
- void this.managerRuntime.start();
60
- this.reloadCustomAgents();
61
- this.activityProbeTimer = setInterval(() => {
62
- void this.publishManagedActivitySnapshots().catch((error) => {
63
- console.error('[session.activity] managed probe failed', error);
64
- });
65
- }, 15_000);
66
- this.activityProbeTimer.unref?.();
67
- }
68
- getRuntime() {
69
- return {
70
- client: this.client,
71
- pendingTransfers: this.pendingTransfers,
72
- processedReqIds: this.processedReqIds,
73
- reloadCustomAgents: () => this.reloadCustomAgents(),
74
- resolvePath: (pathValue) => this.resolvePath(pathValue),
75
- resolveAuthorizedPath: (pathValue, rootPath) => this.resolveAuthorizedPath(pathValue, rootPath),
76
- runTextAcc: this.runTextAcc,
77
- sessions: this.sessions,
78
- evictIdleSessions: () => this.evictIdleSessions(),
79
- nativeFusion: this.nativeFusion,
80
- managerRuntime: this.managerRuntime,
81
- chatQueue: this.chatQueue,
82
- activityPublisher: {
83
- publish: (sessionId, activity) => this.publishSessionActivity(sessionId, activity),
84
- },
85
- };
86
- }
87
- publishSessionActivity(sessionId, activity) {
88
- this.client.sendEvent({
89
- type: 'event',
90
- event: 'session.activity',
91
- payload: { sessionId, activity },
92
- });
93
- }
94
- async publishManagedActivitySnapshots() {
95
- for (const [sessionId, session] of this.sessions.entries()) {
96
- if (!session.currentRunId || !session.adapter.getStatus)
97
- continue;
98
- const status = await session.adapter.getStatus().catch(() => null);
99
- if (!status?.active || !status.runPhase)
100
- continue;
101
- const nowIso = new Date().toISOString();
102
- this.publishSessionActivity(sessionId, {
103
- sessionId,
104
- runId: status.runId || session.currentRunId,
105
- runPhase: status.runPhase,
106
- startedAt: new Date(session.lastActiveAt).toISOString(),
107
- updatedAt: nowIso,
108
- canStop: status.canStop ?? true,
109
- });
110
- const seq = session.heartbeatSeq++;
111
- this.client.sendAgentEvent({
112
- type: 'event',
113
- event: 'agent',
114
- payload: {
115
- state: 'heartbeat',
116
- sessionId,
117
- runId: status.runId || session.currentRunId,
118
- seq,
119
- runPhase: status.runPhase,
120
- canStop: status.canStop ?? true,
121
- },
122
- seq,
123
- id: `agent-status-${status.runId || session.currentRunId}-${Date.now()}`,
124
- });
125
- }
126
- }
127
- reloadCustomAgents() {
128
- for (const agentType of getRegisteredAgents()) {
129
- if (agentType.startsWith('custom:')) {
130
- unregisterAgent(agentType);
131
- }
132
- }
133
- const config = loadConfig();
134
- for (const [name, entry] of Object.entries(config.customAgents ?? {})) {
135
- registerCustomAgent(name, entry);
136
- }
137
- }
138
- async handleReq(req) {
139
- const runtime = this.getRuntime();
140
- try {
141
- switch (req.method) {
142
- case 'chat.send':
143
- await handleChatSend(runtime, req);
144
- break;
145
- case 'chat.enqueue':
146
- await this.chatQueue.handleEnqueue(req);
147
- break;
148
- case 'chat.queue.get':
149
- await this.chatQueue.handleGet(req);
150
- break;
151
- case 'chat.queue.edit':
152
- await this.chatQueue.handleEdit(req);
153
- break;
154
- case 'chat.queue.delete':
155
- await this.chatQueue.handleDelete(req);
156
- break;
157
- case 'chat.abort':
158
- await handleChatAbort(runtime, req);
159
- break;
160
- case 'session.refresh':
161
- await handleSessionRefresh(runtime, req);
162
- break;
163
- case 'session.tool.detail':
164
- await handleSessionToolDetail(runtime, req);
165
- break;
166
- case 'session.title.set':
167
- await handleSessionTitleSet(runtime, req);
168
- break;
169
- case 'fs.ls':
170
- await handleFsLs(runtime, req);
171
- break;
172
- case 'fs.read':
173
- await handleFsRead(runtime, req);
174
- break;
175
- case 'fs.write':
176
- await handleFsWrite(runtime, req);
177
- break;
178
- case 'fs.export.markdown-pdf':
179
- await handleFsExportMarkdownPdf(runtime, req);
180
- break;
181
- case 'fs.archive.zip':
182
- await handleFsArchiveZip(runtime, req);
183
- break;
184
- case 'fs.rename':
185
- await handleFsRename(runtime, req);
186
- break;
187
- case 'skill.list':
188
- await handleSkillList(runtime, req);
189
- break;
190
- case 'skill.install':
191
- await handleSkillInstall(runtime, req);
192
- break;
193
- case 'skill.doctor':
194
- await handleSkillDoctor(runtime, req);
195
- break;
196
- case 'skill.setup':
197
- await handleSkillSetup(runtime, req);
198
- break;
199
- case 'skill.use':
200
- await handleSkillUse(runtime, req);
201
- break;
202
- case 'fs.transfer':
203
- await handleFsTransfer(runtime, req);
204
- break;
205
- case 'fs.transfer.start':
206
- await handleFsTransferStart(runtime, req);
207
- break;
208
- case 'fs.transfer.chunk':
209
- await handleFsTransferChunk(runtime, req);
210
- break;
211
- case 'fs.transfer.finish':
212
- await handleFsTransferFinish(runtime, req);
213
- break;
214
- case 'fs.transfer.abort':
215
- await handleFsTransferAbort(runtime, req);
216
- break;
217
- case 'region.probe':
218
- await handleRegionProbe(runtime, req);
219
- break;
220
- case 'region.switch':
221
- await handleRegionSwitch(runtime, req);
222
- break;
223
- case 'upgrade.start':
224
- await handleUpgradeStart(this.client, req.id, req.params.version, {
225
- currentVersion: this.cliVersion,
226
- });
227
- break;
228
- case 'upgrade.status':
229
- await handleUpgradeStatus(this.client, req.id, { currentVersion: this.cliVersion });
230
- break;
231
- case 'upgrade.set-policy':
232
- await handleUpgradeSetPolicy(runtime, req);
233
- break;
234
- case 'agents.refresh':
235
- await handleAgentsRefresh(runtime, req);
236
- break;
237
- case 'models.refresh':
238
- await handleModelsRefresh(runtime, req);
239
- break;
240
- case 'agent.config.get':
241
- await handleAgentConfigGet(runtime, req);
242
- break;
243
- case 'agent.config.upsert':
244
- await handleAgentConfigUpsert(runtime, req);
245
- break;
246
- case 'agent.config.clear':
247
- await handleAgentConfigClear(runtime, req);
248
- break;
249
- case 'agent.config.test':
250
- await handleAgentConfigTest(runtime, req);
251
- break;
252
- case 'manager.channel.get':
253
- case 'manager.channel.upsert':
254
- await runtime.managerRuntime?.handleAppReq(req);
255
- break;
256
- default:
257
- this.client.sendRes({
258
- type: 'res',
259
- id: req.id,
260
- ok: false,
261
- error: `Unknown method: ${req.method}`,
262
- });
263
- }
264
- }
265
- catch (err) {
266
- this.client.sendRes({
267
- type: 'res',
268
- id: req.id,
269
- ok: false,
270
- error: err instanceof Error ? err.message : String(err),
271
- });
272
- }
273
- }
274
- // ─── Session eviction ──────────────────────────────────────────────────────
275
- evictIdleSessions() {
276
- if (this.sessions.size < MAX_SESSIONS)
277
- return;
278
- let oldest = null;
279
- for (const [key, session] of this.sessions) {
280
- if (!oldest || session.lastActiveAt < oldest.session.lastActiveAt) {
281
- oldest = { key, session };
282
- }
283
- }
284
- if (oldest) {
285
- if (oldest.session.heartbeatTimer) {
286
- clearInterval(oldest.session.heartbeatTimer);
287
- oldest.session.heartbeatTimer = null;
288
- }
289
- oldest.session.adapter.removeAllListeners();
290
- oldest.session.adapter.stop().catch(() => { });
291
- this.sessions.delete(oldest.key);
292
- }
293
- }
294
- // ─── Helpers ──────────────────────────────────────────────────────────────
295
- resolvePath(p) {
296
- return resolveSessionWorkDir(p);
297
- }
298
- resolveAuthorizedPath(p, rootPath) {
299
- return resolveAuthorizedPath(p, createAuthorizedFsRoot(rootPath));
300
- }
301
- cleanup() {
302
- if (this.activityProbeTimer) {
303
- clearInterval(this.activityProbeTimer);
304
- this.activityProbeTimer = null;
305
- }
306
- for (const [, session] of this.sessions) {
307
- if (session.heartbeatTimer) {
308
- clearInterval(session.heartbeatTimer);
309
- session.heartbeatTimer = null;
310
- }
311
- session.adapter.stop().catch(() => { });
312
- }
313
- this.sessions.clear();
314
- this.runTextAcc.clear();
315
- cleanupPendingTransfers(this.getRuntime());
316
- void this.managerRuntime.stop();
317
- setManagerRuntimeService(null);
318
- }
319
- }
1
+ import{getRegisteredAgents as r,unregisterAgent as o}from"../agents/adapter.js";import{loadConfig as h}from"../config/index.js";import{handleUpgradeStart as c,handleUpgradeStatus as l}from"../commands/upgrade.js";import{handleAgentsRefresh as u,handleModelsRefresh as d}from"./handlers/agents.js";import{handleAgentConfigClear as m,handleAgentConfigGet as p,handleAgentConfigTest as f,handleAgentConfigUpsert as g}from"./handlers/agent-config.js";import{handleChatAbort as b,handleChatSend as k}from"./handlers/chat.js";import{handleSessionRefresh as w}from"./handlers/session-refresh.js";import{handleSessionToolDetail as v}from"./handlers/tool-detail.js";import{handleSessionTitleSet as S}from"./handlers/title.js";import{cleanupPendingTransfers as A,handleFsLs as R,handleFsRead as y,handleFsRename as T,handleFsWrite as I,handleFsTransfer as P,handleFsTransferAbort as F,handleFsTransferChunk as C,handleFsTransferFinish as M,handleFsTransferStart as Q,handleFsExportMarkdownPdf as x,handleFsArchiveZip as D}from"./handlers/fs.js";import{handleSkillDoctor as z,handleSkillInstall as E,handleSkillList as U,handleSkillSetup as V,handleSkillUse as O}from"./handlers/skills.js";import{handleRegionProbe as W,handleRegionSwitch as L,handleUpgradeSetPolicy as $}from"./handlers/control.js";import{ManagerRuntimeService as G,setManagerRuntimeService as n}from"../manager/runtime.js";import{ChatQueueManager as _}from"./queue.js";import{createAuthorizedFsRoot as j,resolveAuthorizedPath as N,resolveSessionWorkDir as X}from"../fs/boundary.js";import"../agents/claude.js";import"../agents/codex.js";import"../agents/gemini.js";import"../agents/cursor.js";import"../agents/opencode.js";import"../agents/pi.js";import"../agents/manager.js";import{registerCustomAgent as Z}from"../agents/custom.js";const B=50;import{resolveSessionWorkDir as Ae}from"../fs/boundary.js";class we{client;nativeFusion;cliVersion;sessions=new Map;processedReqIds=new Set;runTextAcc=new Map;pendingTransfers=new Map;managerRuntime;chatQueue;activityProbeTimer=null;constructor(e,t=null,a){this.client=e,this.nativeFusion=t,this.cliVersion=a,this.managerRuntime=new G({getRuntime:()=>this.getRuntime(),dispatchReq:s=>this.handleReq(s)}),this.chatQueue=new _({getRuntime:()=>this.getRuntime(),dispatchReq:s=>this.handleReq(s)}),n(this.managerRuntime),this.managerRuntime.start(),this.reloadCustomAgents(),this.activityProbeTimer=setInterval(()=>{this.publishManagedActivitySnapshots().catch(s=>{console.error("[session.activity] managed probe failed",s)})},15e3),this.activityProbeTimer.unref?.()}getRuntime(){return{client:this.client,pendingTransfers:this.pendingTransfers,processedReqIds:this.processedReqIds,reloadCustomAgents:()=>this.reloadCustomAgents(),resolvePath:e=>this.resolvePath(e),resolveAuthorizedPath:(e,t)=>this.resolveAuthorizedPath(e,t),runTextAcc:this.runTextAcc,sessions:this.sessions,evictIdleSessions:()=>this.evictIdleSessions(),nativeFusion:this.nativeFusion,managerRuntime:this.managerRuntime,chatQueue:this.chatQueue,activityPublisher:{publish:(e,t)=>this.publishSessionActivity(e,t)}}}publishSessionActivity(e,t){this.client.sendEvent({type:"event",event:"session.activity",payload:{sessionId:e,activity:t}})}async publishManagedActivitySnapshots(){for(const[e,t]of this.sessions.entries()){if(!t.currentRunId||!t.adapter.getStatus)continue;const a=await t.adapter.getStatus().catch(()=>null);if(!a?.active||!a.runPhase)continue;const s=new Date().toISOString();this.publishSessionActivity(e,{sessionId:e,runId:a.runId||t.currentRunId,runPhase:a.runPhase,startedAt:new Date(t.lastActiveAt).toISOString(),updatedAt:s,canStop:a.canStop??!0});const i=t.heartbeatSeq++;this.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:a.runId||t.currentRunId,seq:i,runPhase:a.runPhase,canStop:a.canStop??!0},seq:i,id:`agent-status-${a.runId||t.currentRunId}-${Date.now()}`})}}reloadCustomAgents(){for(const t of r())t.startsWith("custom:")&&o(t);const e=h();for(const[t,a]of Object.entries(e.customAgents??{}))Z(t,a)}async handleReq(e){const t=this.getRuntime();try{switch(e.method){case"chat.send":await k(t,e);break;case"chat.enqueue":await this.chatQueue.handleEnqueue(e);break;case"chat.queue.get":await this.chatQueue.handleGet(e);break;case"chat.queue.edit":await this.chatQueue.handleEdit(e);break;case"chat.queue.delete":await this.chatQueue.handleDelete(e);break;case"chat.abort":await b(t,e);break;case"session.refresh":await w(t,e);break;case"session.tool.detail":await v(t,e);break;case"session.title.set":await S(t,e);break;case"fs.ls":await R(t,e);break;case"fs.read":await y(t,e);break;case"fs.write":await I(t,e);break;case"fs.export.markdown-pdf":await x(t,e);break;case"fs.archive.zip":await D(t,e);break;case"fs.rename":await T(t,e);break;case"skill.list":await U(t,e);break;case"skill.install":await E(t,e);break;case"skill.doctor":await z(t,e);break;case"skill.setup":await V(t,e);break;case"skill.use":await O(t,e);break;case"fs.transfer":await P(t,e);break;case"fs.transfer.start":await Q(t,e);break;case"fs.transfer.chunk":await C(t,e);break;case"fs.transfer.finish":await M(t,e);break;case"fs.transfer.abort":await F(t,e);break;case"region.probe":await W(t,e);break;case"region.switch":await L(t,e);break;case"upgrade.start":await c(this.client,e.id,e.params.version,{currentVersion:this.cliVersion});break;case"upgrade.status":await l(this.client,e.id,{currentVersion:this.cliVersion});break;case"upgrade.set-policy":await $(t,e);break;case"agents.refresh":await u(t,e);break;case"models.refresh":await d(t,e);break;case"agent.config.get":await p(t,e);break;case"agent.config.upsert":await g(t,e);break;case"agent.config.clear":await m(t,e);break;case"agent.config.test":await f(t,e);break;case"manager.channel.get":case"manager.channel.upsert":await t.managerRuntime?.handleAppReq(e);break;default:this.client.sendRes({type:"res",id:e.id,ok:!1,error:`Unknown method: ${e.method}`})}}catch(a){this.client.sendRes({type:"res",id:e.id,ok:!1,error:a instanceof Error?a.message:String(a)})}}evictIdleSessions(){if(this.sessions.size<B)return;let e=null;for(const[t,a]of this.sessions)(!e||a.lastActiveAt<e.session.lastActiveAt)&&(e={key:t,session:a});e&&(e.session.heartbeatTimer&&(clearInterval(e.session.heartbeatTimer),e.session.heartbeatTimer=null),e.session.adapter.removeAllListeners(),e.session.adapter.stop().catch(()=>{}),this.sessions.delete(e.key))}resolvePath(e){return X(e)}resolveAuthorizedPath(e,t){return N(e,j(t))}cleanup(){this.activityProbeTimer&&(clearInterval(this.activityProbeTimer),this.activityProbeTimer=null);for(const[,e]of this.sessions)e.heartbeatTimer&&(clearInterval(e.heartbeatTimer),e.heartbeatTimer=null),e.adapter.stop().catch(()=>{});this.sessions.clear(),this.runTextAcc.clear(),A(this.getRuntime()),this.managerRuntime.stop(),n(null)}}export{we as SessionManager,Ae as resolveSessionWorkDir};
@@ -1,54 +1 @@
1
- // @arch docs/features/manager-agent.md
2
- // @test src/__tests__/manager-runtime.test.ts
3
- import fs from 'node:fs';
4
- import path from 'node:path';
5
- import { resolveShennianPath } from '../config/index.js';
6
- const PROJECTION_FILE = resolveShennianPath('session-list-projection.json');
7
- function emptyProjection() {
8
- return { updatedAt: new Date(0).toISOString(), sessions: {} };
9
- }
10
- function readProjectionFile() {
11
- try {
12
- const parsed = JSON.parse(fs.readFileSync(PROJECTION_FILE, 'utf-8'));
13
- return {
14
- updatedAt: parsed.updatedAt ?? new Date(0).toISOString(),
15
- sessions: parsed.sessions ?? {},
16
- };
17
- }
18
- catch {
19
- return emptyProjection();
20
- }
21
- }
22
- function writeProjectionFile(file) {
23
- fs.mkdirSync(path.dirname(PROJECTION_FILE), { recursive: true });
24
- fs.writeFileSync(PROJECTION_FILE, JSON.stringify(file, null, 2));
25
- }
26
- export function listProjectedSessions() {
27
- return Object.values(readProjectionFile().sessions);
28
- }
29
- export function mergeProjectedSessions(sessions) {
30
- if (!sessions?.length)
31
- return;
32
- try {
33
- const file = readProjectionFile();
34
- for (const session of sessions) {
35
- if (!session?.id)
36
- continue;
37
- if (session.deletedAt) {
38
- delete file.sessions[session.id];
39
- continue;
40
- }
41
- const existing = file.sessions[session.id];
42
- const existingMs = Date.parse(existing?.updatedAt ?? existing?.lastActivityAt ?? '');
43
- const incomingMs = Date.parse(session.updatedAt ?? session.lastActivityAt ?? '');
44
- if (!existing || !Number.isFinite(existingMs) || !Number.isFinite(incomingMs) || incomingMs >= existingMs) {
45
- file.sessions[session.id] = session;
46
- }
47
- }
48
- file.updatedAt = new Date().toISOString();
49
- writeProjectionFile(file);
50
- }
51
- catch {
52
- // Projection is a local cache. Losing it should not break chat delivery.
53
- }
54
- }
1
+ import s from"node:fs";import d from"node:path";import{resolveShennianPath as u}from"../config/index.js";const o=u("session-list-projection.json");function f(){return{updatedAt:new Date(0).toISOString(),sessions:{}}}function a(){try{const e=JSON.parse(s.readFileSync(o,"utf-8"));return{updatedAt:e.updatedAt??new Date(0).toISOString(),sessions:e.sessions??{}}}catch{return f()}}function p(e){s.mkdirSync(d.dirname(o),{recursive:!0}),s.writeFileSync(o,JSON.stringify(e,null,2))}function A(){return Object.values(a().sessions)}function g(e){if(e?.length)try{const i=a();for(const t of e){if(!t?.id)continue;if(t.deletedAt){delete i.sessions[t.id];continue}const n=i.sessions[t.id],r=Date.parse(n?.updatedAt??n?.lastActivityAt??""),c=Date.parse(t.updatedAt??t.lastActivityAt??"");(!n||!Number.isFinite(r)||!Number.isFinite(c)||c>=r)&&(i.sessions[t.id]=t)}i.updatedAt=new Date().toISOString(),p(i)}catch{}}export{A as listProjectedSessions,g as mergeProjectedSessions};