@kafca/agentdock 0.1.41 → 0.1.43
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/README.md +10 -0
- package/dist/renderer/assets/{Badge-BX3RC8l6.js → Badge-CbG6yRSm.js} +1 -1
- package/dist/renderer/assets/{Chat-DJwIciel.js → Chat-BvNNWGGX.js} +1 -1
- package/dist/renderer/assets/{ChatMarkdown-DfGbRR0S.js → ChatMarkdown-Ct-PvJB8.js} +1 -1
- package/dist/renderer/assets/{Config-2x2RDL3j.js → Config-lQX2qVVV.js} +2 -2
- package/dist/renderer/assets/CronList-Dj_3muVG.js +1 -0
- package/dist/renderer/assets/{Dashboard-2Wcx_bvJ.js → Dashboard-eh_RhGYb.js} +2 -2
- package/dist/renderer/assets/{EmptyState-DoCpF2tn.js → EmptyState-Bn2Qh14U.js} +1 -1
- package/dist/renderer/assets/{KnowledgeDetail-CBRu2bXX.js → KnowledgeDetail-Bsz6Ou41.js} +2 -2
- package/dist/renderer/assets/{KnowledgeHome-CItdlDVR.js → KnowledgeHome-Cgj4XFXn.js} +2 -2
- package/dist/renderer/assets/{Logs-Cd_7ZOXc.js → Logs-Dp-NF6GN.js} +1 -1
- package/dist/renderer/assets/{Modal-B4eOiBsB.js → Modal-B6YPPBN6.js} +1 -1
- package/dist/renderer/assets/MonitorList-xEZWg460.js +1 -0
- package/dist/renderer/assets/{Page-C5VH4D9U.js → Page-Bn7RjGOS.js} +1 -1
- package/dist/renderer/assets/{ProjectDetail-D_DA--IP.js → ProjectDetail-Cv_jIE61.js} +2 -2
- package/dist/renderer/assets/{ProjectList-CtCXJ50u.js → ProjectList-BDk5_gqY.js} +1 -1
- package/dist/renderer/assets/{Select-B6E68LcN.js → Select-xshfMIEQ.js} +1 -1
- package/dist/renderer/assets/SessionChat-BkHy90z3.js +1 -0
- package/dist/renderer/assets/{SessionList-Do-5-Xx6.js → SessionList-BZyXT92L.js} +1 -1
- package/dist/renderer/assets/{ThreadChat-B1tEtE3V.js → ThreadChat-DUOIyXrS.js} +7 -7
- package/dist/renderer/assets/{Workspace-BlhS2RFw.js → Workspace-Dl80fNEC.js} +1 -1
- package/dist/renderer/assets/{arrow-left-DF1_F9Pk.js → arrow-left-C54ZI-AS.js} +1 -1
- package/dist/renderer/assets/{book-open-DxRTGpWO.js → book-open-Dl7GO0zn.js} +1 -1
- package/dist/renderer/assets/{check-BAqPPYkf.js → check-CiqgtrOL.js} +1 -1
- package/dist/renderer/assets/{chevron-down-DIZZgSb3.js → chevron-down-DTL2OuVh.js} +1 -1
- package/dist/renderer/assets/{circle-B5raL_XK.js → circle-DaVKfa3P.js} +1 -1
- package/dist/renderer/assets/desktop-C6q6wBae.js +5 -0
- package/dist/renderer/assets/desktop-jaAiayNB.js +1 -0
- package/dist/renderer/assets/en-D2SkCrhG.js +1 -0
- package/dist/renderer/assets/{funnel-BPjLpVSB.js → funnel-CrmnLMJI.js} +1 -1
- package/dist/renderer/assets/{heart-D2M1xN1N.js → heart-BMfQI7y_.js} +1 -1
- package/dist/renderer/assets/{index-Cx71F_Lz.js → index-HPNb1zfx.js} +39 -34
- package/dist/renderer/assets/{index-BijJAYmo.js → index-nreiKY1f.js} +3 -3
- package/dist/renderer/assets/{pencil-DlDr8Mmp.js → pencil-wTQ4l465.js} +1 -1
- package/dist/renderer/assets/{play-BOEG_mY8.js → play-BK--MXmw.js} +1 -1
- package/dist/renderer/assets/{plug-BI27elC-.js → plug-BIx9fjip.js} +1 -1
- package/dist/renderer/assets/{plus-BhURuFIQ.js → plus-Dn7JFWQ8.js} +1 -1
- package/dist/renderer/assets/projects-brJZgwEJ.js +1 -0
- package/dist/renderer/assets/{save-RwkHNm9e.js → save-CC3zOSWb.js} +1 -1
- package/dist/renderer/assets/{search-DMNtm2WP.js → search-CDTy9Czu.js} +1 -1
- package/dist/renderer/assets/{server-CNaxKyAq.js → server-DtQk3t2a.js} +1 -1
- package/dist/renderer/assets/{session-utils-vsALZkZN.js → session-utils-4ep5D66T.js} +1 -1
- package/dist/renderer/assets/{sessions-BY-R1IL_.js → sessions-Dor1LteH.js} +1 -1
- package/dist/renderer/assets/{shield-check-BF-4oJbj.js → shield-check-Cz2LpU6r.js} +1 -1
- package/dist/renderer/assets/{trash-2-DduflM4q.js → trash-2-C2EmWFZV.js} +1 -1
- package/dist/renderer/assets/{user-DBcvnuW4.js → user-I8B-cs5A.js} +1 -1
- package/dist/renderer/assets/{wifi-off-X1nXESYu.js → wifi-off-CPevTQJn.js} +1 -1
- package/dist/renderer/assets/zh-DIcdHycM.js +1 -0
- package/dist/renderer/index.html +1 -1
- package/dist-electron/packages/contracts/src/local-core.js +28 -0
- package/dist-electron/packages/core-sdk/src/index.js +32 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +76 -39
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-session-coordinator.js +13 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-store.js +4 -1357
- package/dist-electron/services/local-ai-core/src/acp/local-core-slash-commands.js +28 -0
- package/dist-electron/services/local-ai-core/src/acp/store/agent-task-store.js +166 -0
- package/dist-electron/services/local-ai-core/src/acp/store/automation-monitor-store.js +231 -0
- package/dist-electron/services/local-ai-core/src/acp/store/local-core-acp-store.js +263 -0
- package/dist-electron/services/local-ai-core/src/acp/store/platform-store.js +186 -0
- package/dist-electron/services/local-ai-core/src/acp/store/scheduler-store.js +201 -0
- package/dist-electron/services/local-ai-core/src/acp/store/schema.js +275 -0
- package/dist-electron/services/local-ai-core/src/acp/store/security-store.js +266 -0
- package/dist-electron/services/local-ai-core/src/acp/store/thread-store.js +232 -0
- package/dist-electron/services/local-ai-core/src/acp/store/utils.js +47 -0
- package/dist-electron/services/local-ai-core/src/acp/store/workspace-registry-store.js +109 -0
- package/dist-electron/services/local-ai-core/src/automation/automation-conversation-executor.js +136 -0
- package/dist-electron/services/local-ai-core/src/automation/automation-monitor-repository.js +56 -0
- package/dist-electron/services/local-ai-core/src/automation/automation-monitor-service.js +359 -0
- package/dist-electron/services/local-ai-core/src/automation/condition-evaluator.js +77 -0
- package/dist-electron/services/local-ai-core/src/automation/mock-stock-provider.js +40 -0
- package/dist-electron/services/local-ai-core/src/automation/monitor-id.js +10 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/cards.js +55 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +101 -29
- package/dist-electron/services/local-ai-core/src/channel/shared/session-command-runtime.js +53 -0
- package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +51 -33
- package/dist-electron/services/local-ai-core/src/cli/lac.js +196 -0
- package/dist-electron/services/local-ai-core/src/cli/monitor-cli-parsers.js +37 -0
- package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +43 -0
- package/dist-electron/services/local-ai-core/src/kernel/capability-registry.js +12 -0
- package/dist-electron/services/local-ai-core/src/plugins/builtin/monitor-stock-plugin.js +30 -0
- package/dist-electron/services/local-ai-core/src/router/workspace-router.js +40 -4
- package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +41 -0
- package/dist-electron/services/local-ai-core/src/runtime/server-routes.js +31 -0
- package/dist-electron/services/local-ai-core/src/runtime/server.js +36 -0
- package/dist-electron/services/local-ai-core/src/scheduler/scheduled-bridge-session.js +25 -11
- package/dist-electron/services/local-ai-core/src/thread/session-command-service.js +275 -0
- package/dist-electron/services/local-ai-core/src/thread/slash-command-registry.js +27 -0
- package/dist-electron/services/local-ai-core/src/thread/thread-command-service.js +134 -0
- package/dist-electron/shared/desktop.js +21 -1
- package/dist-electron/tests/contracts/local-core-contracts.test.js +9 -0
- package/dist-electron/tests/electron/automation-monitor.test.js +38 -0
- package/dist-electron/tests/electron/lac-cli.test.js +146 -0
- package/dist-electron/tests/electron/plugin-kernel.test.js +26 -0
- package/dist-electron/tests/integration/local-core-refactor.test.js +359 -0
- package/package.json +1 -1
- package/dist/renderer/assets/CronList-C6nPAmEo.js +0 -1
- package/dist/renderer/assets/SessionChat-D1HtMdQS.js +0 -1
- package/dist/renderer/assets/desktop-CFcnzwcY.js +0 -1
- package/dist/renderer/assets/desktop-O8_6ZCVZ.js +0 -4
- package/dist/renderer/assets/en-CKhCF6St.js +0 -1
- package/dist/renderer/assets/projects-DpR972HM.js +0 -1
- package/dist/renderer/assets/zh-Dz5FLTBV.js +0 -1
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SessionCommandService = void 0;
|
|
4
|
+
exports.matchThread = matchThread;
|
|
5
|
+
const slash_command_registry_js_1 = require("./slash-command-registry.js");
|
|
6
|
+
const PAGE_SIZE = 8;
|
|
7
|
+
const DEFAULT_HISTORY_LIMIT = 10;
|
|
8
|
+
const MAX_HISTORY_LIMIT = 50;
|
|
9
|
+
class SessionCommandService {
|
|
10
|
+
operations;
|
|
11
|
+
registry = new slash_command_registry_js_1.SlashCommandRegistry();
|
|
12
|
+
constructor(operations) {
|
|
13
|
+
this.operations = operations;
|
|
14
|
+
this.registry.register({ names: ['new'], execute: (command, context) => this.newSession(command.args, context) });
|
|
15
|
+
this.registry.register({ names: ['list', 'sessions'], execute: (command, context) => this.listSessions(command.args, context) });
|
|
16
|
+
this.registry.register({ names: ['switch', 'sw'], execute: (command, context) => this.switchSession(command.args, context) });
|
|
17
|
+
this.registry.register({ names: ['history', 'his'], execute: (command, context) => this.history(command.args, context) });
|
|
18
|
+
this.registry.register({ names: ['name'], execute: (command, context) => this.name(command.args, context) });
|
|
19
|
+
this.registry.register({ names: ['search'], execute: (command, context) => this.search(command.args, context) });
|
|
20
|
+
this.registry.register({ names: ['del', 'delete', 'rm'], execute: (command, context) => this.delete(command.args, context) });
|
|
21
|
+
}
|
|
22
|
+
async execute(text, context) {
|
|
23
|
+
return await this.registry.execute(text, context) || { handled: false, displayText: '' };
|
|
24
|
+
}
|
|
25
|
+
async newSession(args, context) {
|
|
26
|
+
const title = args.join(' ').trim() || context.defaultTitle || `New thread ${new Date().toLocaleTimeString()}`;
|
|
27
|
+
const created = await this.operations.createThread(context.workspaceId, title);
|
|
28
|
+
return {
|
|
29
|
+
handled: true,
|
|
30
|
+
displayText: `已开始新会话:${created.title}`,
|
|
31
|
+
effects: [
|
|
32
|
+
{ type: 'created_thread', threadId: created.id },
|
|
33
|
+
{ type: 'activate_thread', threadId: created.id, reason: 'created' },
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
async listSessions(args, context) {
|
|
38
|
+
const all = await this.sortedThreads(context.workspaceId);
|
|
39
|
+
const page = parsePage(args[0]);
|
|
40
|
+
const pageInfo = paginate(all, page);
|
|
41
|
+
const text = this.renderList(pageInfo.items, context.currentThreadId, {
|
|
42
|
+
total: all.length,
|
|
43
|
+
page: pageInfo.page,
|
|
44
|
+
totalPages: pageInfo.totalPages,
|
|
45
|
+
empty: '当前工作区还没有会话。',
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
handled: true,
|
|
49
|
+
displayText: text,
|
|
50
|
+
card: {
|
|
51
|
+
title: '会话列表',
|
|
52
|
+
actions: this.listActions(pageInfo.items, pageInfo.page, pageInfo.totalPages),
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
async switchSession(args, context) {
|
|
57
|
+
const query = args.join(' ').trim();
|
|
58
|
+
if (!query) {
|
|
59
|
+
return { handled: true, displayText: '用法:`/switch <编号 | id前缀 | 标题>`' };
|
|
60
|
+
}
|
|
61
|
+
const matched = await this.resolveThread(context.workspaceId, query);
|
|
62
|
+
if (!matched) {
|
|
63
|
+
return { handled: true, displayText: `没有找到匹配的会话:${query}` };
|
|
64
|
+
}
|
|
65
|
+
if (matched.id === context.currentThreadId) {
|
|
66
|
+
return { handled: true, displayText: `当前已经在会话:${this.displayName(matched)}` };
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
handled: true,
|
|
70
|
+
displayText: `已切换到会话:${this.displayName(matched)}\n后续消息将发送到该会话。`,
|
|
71
|
+
effects: [
|
|
72
|
+
{ type: 'activate_thread', threadId: matched.id, reason: 'switched' },
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
async history(args, context) {
|
|
77
|
+
const limit = Math.max(1, Math.min(parseInt(String(args[0] || DEFAULT_HISTORY_LIMIT), 10) || DEFAULT_HISTORY_LIMIT, MAX_HISTORY_LIMIT));
|
|
78
|
+
const thread = await this.operations.getThread(context.currentThreadId);
|
|
79
|
+
const messages = thread.messages.slice(-limit);
|
|
80
|
+
if (!messages.length) {
|
|
81
|
+
return { handled: true, displayText: '当前会话还没有消息。' };
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
handled: true,
|
|
85
|
+
displayText: [
|
|
86
|
+
`最近 ${messages.length} 条消息:`,
|
|
87
|
+
...messages.map((message) => this.renderHistoryMessage(message)),
|
|
88
|
+
].join('\n\n'),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
async name(args, context) {
|
|
92
|
+
const raw = args.join(' ').trim();
|
|
93
|
+
if (!raw) {
|
|
94
|
+
return { handled: true, displayText: '用法:`/name <新名称>` 或 `/name <编号 | id前缀> <新名称>`' };
|
|
95
|
+
}
|
|
96
|
+
const [first = '', ...rest] = args;
|
|
97
|
+
let targetId = context.currentThreadId;
|
|
98
|
+
let nextName = raw;
|
|
99
|
+
if (rest.length > 0) {
|
|
100
|
+
const matched = await this.resolveThread(context.workspaceId, first);
|
|
101
|
+
if (matched) {
|
|
102
|
+
targetId = matched.id;
|
|
103
|
+
nextName = rest.join(' ').trim();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (!nextName) {
|
|
107
|
+
return { handled: true, displayText: '会话名称不能为空。' };
|
|
108
|
+
}
|
|
109
|
+
const renamed = await this.operations.renameThread(targetId, nextName);
|
|
110
|
+
return {
|
|
111
|
+
handled: true,
|
|
112
|
+
displayText: `已重命名会话:${renamed.title}`,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
async search(args, context) {
|
|
116
|
+
const query = args.join(' ').trim().toLowerCase();
|
|
117
|
+
if (!query) {
|
|
118
|
+
return { handled: true, displayText: '用法:`/search <关键词>`' };
|
|
119
|
+
}
|
|
120
|
+
const all = await this.sortedThreads(context.workspaceId);
|
|
121
|
+
const matched = all.filter((thread) => thread.title.toLowerCase().includes(query) ||
|
|
122
|
+
thread.id.toLowerCase().includes(query) ||
|
|
123
|
+
String(thread.excerpt || '').toLowerCase().includes(query));
|
|
124
|
+
const pageInfo = paginate(matched, 1);
|
|
125
|
+
const text = this.renderList(pageInfo.items, context.currentThreadId, {
|
|
126
|
+
total: matched.length,
|
|
127
|
+
page: 1,
|
|
128
|
+
totalPages: pageInfo.totalPages,
|
|
129
|
+
empty: `没有找到匹配的会话:${args.join(' ')}`,
|
|
130
|
+
title: `搜索结果:${args.join(' ')}`,
|
|
131
|
+
});
|
|
132
|
+
return {
|
|
133
|
+
handled: true,
|
|
134
|
+
displayText: text,
|
|
135
|
+
card: {
|
|
136
|
+
title: '搜索会话',
|
|
137
|
+
actions: this.listActions(pageInfo.items, 1, pageInfo.totalPages),
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
async delete(args, context) {
|
|
142
|
+
const confirm = args.some((arg) => arg === '--confirm' || arg.toLowerCase() === 'confirm');
|
|
143
|
+
const query = args.filter((arg) => arg !== '--confirm' && arg.toLowerCase() !== 'confirm').join(' ').trim();
|
|
144
|
+
if (!query) {
|
|
145
|
+
return { handled: true, displayText: '用法:`/del <编号 | id前缀 | 标题>`,确认删除:`/del <目标> --confirm`' };
|
|
146
|
+
}
|
|
147
|
+
const matched = await this.resolveThread(context.workspaceId, query);
|
|
148
|
+
if (!matched) {
|
|
149
|
+
return { handled: true, displayText: `没有找到匹配的会话:${query}` };
|
|
150
|
+
}
|
|
151
|
+
if (matched.id === context.currentThreadId) {
|
|
152
|
+
return { handled: true, displayText: '不能删除当前正在使用的会话。请先 `/switch` 到其他会话后再删除。' };
|
|
153
|
+
}
|
|
154
|
+
if (!confirm) {
|
|
155
|
+
return {
|
|
156
|
+
handled: true,
|
|
157
|
+
displayText: [
|
|
158
|
+
`确认删除会话:${this.displayName(matched)}?`,
|
|
159
|
+
'删除后会清空该会话历史。',
|
|
160
|
+
`确认命令:\`/del ${shortThreadId(matched.id)} --confirm\``,
|
|
161
|
+
].join('\n'),
|
|
162
|
+
card: {
|
|
163
|
+
title: '确认删除会话',
|
|
164
|
+
actions: [[
|
|
165
|
+
{ label: '确认删除', command: `/del ${shortThreadId(matched.id)} --confirm`, type: 'danger' },
|
|
166
|
+
{ label: '取消', command: '/list', type: 'default' },
|
|
167
|
+
]],
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
await this.operations.deleteThread(matched.id);
|
|
172
|
+
return {
|
|
173
|
+
handled: true,
|
|
174
|
+
displayText: `已删除会话:${this.displayName(matched)}`,
|
|
175
|
+
effects: [
|
|
176
|
+
{ type: 'deleted_thread', threadId: matched.id },
|
|
177
|
+
],
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
async sortedThreads(workspaceId) {
|
|
181
|
+
const threads = await this.operations.listThreads(workspaceId);
|
|
182
|
+
return [...threads].sort((a, b) => Date.parse(b.updatedAt) - Date.parse(a.updatedAt));
|
|
183
|
+
}
|
|
184
|
+
async resolveThread(workspaceId, query) {
|
|
185
|
+
const threads = await this.sortedThreads(workspaceId);
|
|
186
|
+
return matchThread(threads, query);
|
|
187
|
+
}
|
|
188
|
+
renderList(threads, currentThreadId, input) {
|
|
189
|
+
if (input.total === 0) {
|
|
190
|
+
return input.empty;
|
|
191
|
+
}
|
|
192
|
+
const lines = [
|
|
193
|
+
input.title || `会话列表(${input.total} 个)`,
|
|
194
|
+
...threads.map((thread, index) => {
|
|
195
|
+
const globalIndex = (input.page - 1) * PAGE_SIZE + index + 1;
|
|
196
|
+
const marker = thread.id === currentThreadId ? '▶' : '◻';
|
|
197
|
+
return `${marker} **${globalIndex}.** ${this.displayName(thread)} · ${thread.historyCount} msgs · ${formatDate(thread.updatedAt)}`;
|
|
198
|
+
}),
|
|
199
|
+
];
|
|
200
|
+
if (input.totalPages > 1) {
|
|
201
|
+
lines.push(`第 ${input.page}/${input.totalPages} 页,使用 \`/list ${input.page + 1 > input.totalPages ? input.totalPages : input.page + 1}\` 查看更多。`);
|
|
202
|
+
}
|
|
203
|
+
lines.push('使用 `/switch <编号>` 切换,`/history` 查看历史,`/new 名称` 新建会话。');
|
|
204
|
+
return lines.join('\n');
|
|
205
|
+
}
|
|
206
|
+
listActions(threads, page, totalPages) {
|
|
207
|
+
const rows = threads.map((thread, index) => {
|
|
208
|
+
const globalIndex = (page - 1) * PAGE_SIZE + index + 1;
|
|
209
|
+
return [
|
|
210
|
+
{ label: `${globalIndex}. ${truncate(this.displayName(thread), 18)}`, command: `/switch ${globalIndex}`, type: 'primary' },
|
|
211
|
+
{ label: '删除', command: `/del ${globalIndex}`, type: 'danger' },
|
|
212
|
+
];
|
|
213
|
+
});
|
|
214
|
+
const nav = [];
|
|
215
|
+
if (page > 1)
|
|
216
|
+
nav.push({ label: '上一页', command: `/list ${page - 1}` });
|
|
217
|
+
if (page < totalPages)
|
|
218
|
+
nav.push({ label: '下一页', command: `/list ${page + 1}` });
|
|
219
|
+
if (nav.length)
|
|
220
|
+
rows.push(nav);
|
|
221
|
+
return rows;
|
|
222
|
+
}
|
|
223
|
+
renderHistoryMessage(message) {
|
|
224
|
+
const role = message.role === 'assistant' ? '助手' : message.role === 'user' ? '用户' : '系统';
|
|
225
|
+
return `**${role}** · ${formatDate(message.timestamp)}\n${truncate(String(message.content || '').trim(), 600) || '(empty)'}`;
|
|
226
|
+
}
|
|
227
|
+
displayName(thread) {
|
|
228
|
+
const title = String(thread.title || '').trim();
|
|
229
|
+
if (title)
|
|
230
|
+
return title;
|
|
231
|
+
const excerpt = String(thread.excerpt || '').replace(/\s+/g, ' ').trim();
|
|
232
|
+
return excerpt ? truncate(excerpt, 40) : shortThreadId(thread.id);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
exports.SessionCommandService = SessionCommandService;
|
|
236
|
+
function matchThread(threads, query) {
|
|
237
|
+
const normalized = String(query || '').trim();
|
|
238
|
+
if (!normalized)
|
|
239
|
+
return undefined;
|
|
240
|
+
if (/^\d+$/.test(normalized)) {
|
|
241
|
+
const index = Number(normalized);
|
|
242
|
+
if (index >= 1 && index <= threads.length) {
|
|
243
|
+
return threads[index - 1];
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const lower = normalized.toLowerCase();
|
|
247
|
+
return threads.find((thread) => thread.id.startsWith(normalized))
|
|
248
|
+
|| threads.find((thread) => thread.title.toLowerCase() === lower)
|
|
249
|
+
|| threads.find((thread) => thread.title.toLowerCase().startsWith(lower))
|
|
250
|
+
|| threads.find((thread) => String(thread.excerpt || '').toLowerCase().includes(lower));
|
|
251
|
+
}
|
|
252
|
+
function paginate(items, requestedPage) {
|
|
253
|
+
const totalPages = Math.max(1, Math.ceil(items.length / PAGE_SIZE));
|
|
254
|
+
const page = Math.max(1, Math.min(requestedPage, totalPages));
|
|
255
|
+
const start = (page - 1) * PAGE_SIZE;
|
|
256
|
+
return { items: items.slice(start, start + PAGE_SIZE), page, totalPages };
|
|
257
|
+
}
|
|
258
|
+
function parsePage(value) {
|
|
259
|
+
const parsed = Number(value || 1);
|
|
260
|
+
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 1;
|
|
261
|
+
}
|
|
262
|
+
function shortThreadId(threadId) {
|
|
263
|
+
const normalized = threadId.includes(':') ? threadId.split(':').at(-1) || threadId : threadId;
|
|
264
|
+
return normalized.length > 12 ? normalized.slice(0, 12) : normalized;
|
|
265
|
+
}
|
|
266
|
+
function formatDate(value) {
|
|
267
|
+
const date = new Date(value);
|
|
268
|
+
if (Number.isNaN(date.getTime()))
|
|
269
|
+
return value;
|
|
270
|
+
return `${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;
|
|
271
|
+
}
|
|
272
|
+
function truncate(value, maxLength) {
|
|
273
|
+
const chars = Array.from(value);
|
|
274
|
+
return chars.length > maxLength ? `${chars.slice(0, maxLength).join('')}...` : value;
|
|
275
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SlashCommandRegistry = void 0;
|
|
4
|
+
const local_core_slash_commands_js_1 = require("../acp/local-core-slash-commands.js");
|
|
5
|
+
class SlashCommandRegistry {
|
|
6
|
+
handlers = new Map();
|
|
7
|
+
register(handler) {
|
|
8
|
+
for (const name of handler.names) {
|
|
9
|
+
const normalized = String(name || '').trim().toLowerCase();
|
|
10
|
+
if (normalized) {
|
|
11
|
+
this.handlers.set(normalized, handler);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
async execute(text, context) {
|
|
16
|
+
const command = (0, local_core_slash_commands_js_1.parseSlashCommand)(text);
|
|
17
|
+
if (!command) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const handler = this.handlers.get(command.name);
|
|
21
|
+
if (!handler) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return handler.execute(command, context);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.SlashCommandRegistry = SlashCommandRegistry;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ThreadCommandService = void 0;
|
|
4
|
+
const local_core_slash_commands_js_1 = require("../acp/local-core-slash-commands.js");
|
|
5
|
+
const index_js_1 = require("../agents/index.js");
|
|
6
|
+
const slash_command_registry_js_1 = require("./slash-command-registry.js");
|
|
7
|
+
class ThreadCommandService {
|
|
8
|
+
options;
|
|
9
|
+
registry = new slash_command_registry_js_1.SlashCommandRegistry();
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.options = options;
|
|
12
|
+
this.registry.register({
|
|
13
|
+
names: ['mode'],
|
|
14
|
+
execute: async (command, input) => ({
|
|
15
|
+
handled: true,
|
|
16
|
+
displayText: await this.executeModeCommand(input.threadId, input.workspaceId, command.args),
|
|
17
|
+
}),
|
|
18
|
+
});
|
|
19
|
+
this.registry.register({
|
|
20
|
+
names: ['agent'],
|
|
21
|
+
execute: (command, input) => ({
|
|
22
|
+
handled: true,
|
|
23
|
+
displayText: this.executeAgentCommand(input.threadId, input.workspaceId, command.args, input.defaultAgentType),
|
|
24
|
+
}),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
async execute(input) {
|
|
28
|
+
return await this.registry.execute(input.content, input) || { handled: false, displayText: '' };
|
|
29
|
+
}
|
|
30
|
+
async executeModeCommand(threadId, workspaceId, args) {
|
|
31
|
+
const row = this.options.getThreadRow(threadId);
|
|
32
|
+
const currentMode = row?.agent_mode || local_core_slash_commands_js_1.DEFAULT_AGENT_MODE;
|
|
33
|
+
const requested = args.join(' ').trim();
|
|
34
|
+
if (!requested) {
|
|
35
|
+
return (0, local_core_slash_commands_js_1.modeHelpText)(currentMode);
|
|
36
|
+
}
|
|
37
|
+
const mode = (0, local_core_slash_commands_js_1.normalizeAgentMode)(requested);
|
|
38
|
+
if (!mode) {
|
|
39
|
+
return `未知模式:${requested}\n\n${(0, local_core_slash_commands_js_1.modeHelpText)(currentMode)}`;
|
|
40
|
+
}
|
|
41
|
+
this.options.updateThreadAgentMode(threadId, mode);
|
|
42
|
+
try {
|
|
43
|
+
await this.options.setThreadMode?.(threadId, mode);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
this.options.log?.(`localcore-acp mode sync failed for ${threadId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
47
|
+
}
|
|
48
|
+
this.options.createAuditEvent({
|
|
49
|
+
type: 'permission.changed',
|
|
50
|
+
workspaceId,
|
|
51
|
+
actor: 'local',
|
|
52
|
+
summary: `Thread agent mode changed to ${mode}.`,
|
|
53
|
+
metadata: { threadId, mode },
|
|
54
|
+
});
|
|
55
|
+
if (mode === 'bypassPermissions') {
|
|
56
|
+
return '已切换到 yolo 模式:后续工具调用会跳过权限申请。使用 `/mode default` 可恢复默认权限。';
|
|
57
|
+
}
|
|
58
|
+
return `已切换到 ${(0, local_core_slash_commands_js_1.formatAgentMode)(mode)} 模式。`;
|
|
59
|
+
}
|
|
60
|
+
executeAgentCommand(threadId, workspaceId, args, defaultAgentType) {
|
|
61
|
+
const row = this.options.getThreadRow(threadId);
|
|
62
|
+
const currentAgent = (0, local_core_slash_commands_js_1.normalizeAgentCommandTarget)(row?.agent_type || defaultAgentType);
|
|
63
|
+
const defaultAgent = (0, local_core_slash_commands_js_1.normalizeAgentCommandTarget)(defaultAgentType);
|
|
64
|
+
const availableAgents = (0, local_core_slash_commands_js_1.formatAgentList)(this.options.getAgentTypes?.() || [defaultAgent, currentAgent]);
|
|
65
|
+
const [rawAction = '', ...rest] = args;
|
|
66
|
+
const action = String(rawAction || '').trim().toLowerCase();
|
|
67
|
+
if (!action || action === 'current') {
|
|
68
|
+
return [
|
|
69
|
+
`当前线程 Agent:${currentAgent}`,
|
|
70
|
+
`来源:${currentAgent === defaultAgent ? '默认设置' : '线程设置'}`,
|
|
71
|
+
`默认 Agent:${defaultAgent}`,
|
|
72
|
+
'使用 `/agent list` 查看可用 Agent,或 `/agent use <agent-id>` 切换。',
|
|
73
|
+
].join('\n');
|
|
74
|
+
}
|
|
75
|
+
if (action === 'list') {
|
|
76
|
+
return (0, local_core_slash_commands_js_1.agentHelpText)({ currentAgent, defaultAgent, availableAgents });
|
|
77
|
+
}
|
|
78
|
+
if (action === 'reset') {
|
|
79
|
+
if (currentAgent === defaultAgent) {
|
|
80
|
+
return `当前线程已经使用默认 Agent:${defaultAgent}。`;
|
|
81
|
+
}
|
|
82
|
+
const activeRun = this.hasActiveRun(threadId);
|
|
83
|
+
this.options.updateThreadAgentType(threadId, defaultAgent);
|
|
84
|
+
if (!activeRun) {
|
|
85
|
+
this.options.closeThreadSession?.(threadId);
|
|
86
|
+
}
|
|
87
|
+
this.options.createAuditEvent({
|
|
88
|
+
type: 'agent.changed',
|
|
89
|
+
workspaceId,
|
|
90
|
+
actor: 'local',
|
|
91
|
+
summary: `Thread agent reset to ${defaultAgent}.`,
|
|
92
|
+
metadata: { threadId, agentType: defaultAgent, previousAgentType: currentAgent },
|
|
93
|
+
});
|
|
94
|
+
return `已清除当前线程 Agent 设置。\n当前线程将回到默认 Agent:${defaultAgent}。`;
|
|
95
|
+
}
|
|
96
|
+
const rawRequested = action === 'use' ? rest.join(' ').trim() : args.join(' ').trim();
|
|
97
|
+
const requestedAgent = (0, local_core_slash_commands_js_1.normalizeAgentCommandTarget)(rawRequested);
|
|
98
|
+
if (!requestedAgent) {
|
|
99
|
+
return (0, local_core_slash_commands_js_1.agentHelpText)({ currentAgent, defaultAgent, availableAgents });
|
|
100
|
+
}
|
|
101
|
+
const definition = (0, index_js_1.resolveAgentRuntimeDefinition)(requestedAgent);
|
|
102
|
+
if (!definition) {
|
|
103
|
+
return `未知 Agent:${rawRequested}\n可用 Agent:${availableAgents.length ? availableAgents.join(', ') : '无'}`;
|
|
104
|
+
}
|
|
105
|
+
const canonicalAgent = definition.agentType;
|
|
106
|
+
if (!availableAgents.includes(canonicalAgent)) {
|
|
107
|
+
return `Agent "${canonicalAgent}" 当前不可用。\n可用 Agent:${availableAgents.length ? availableAgents.join(', ') : '无'}`;
|
|
108
|
+
}
|
|
109
|
+
if (canonicalAgent === currentAgent) {
|
|
110
|
+
return `当前线程已经使用 Agent:${canonicalAgent}。`;
|
|
111
|
+
}
|
|
112
|
+
const activeRun = this.hasActiveRun(threadId);
|
|
113
|
+
this.options.updateThreadAgentType(threadId, canonicalAgent);
|
|
114
|
+
if (!activeRun) {
|
|
115
|
+
this.options.closeThreadSession?.(threadId);
|
|
116
|
+
}
|
|
117
|
+
const runningNote = activeRun
|
|
118
|
+
? `\n当前正在运行的任务仍会继续使用 ${currentAgent},下一轮开始生效。`
|
|
119
|
+
: '';
|
|
120
|
+
this.options.createAuditEvent({
|
|
121
|
+
type: 'agent.changed',
|
|
122
|
+
workspaceId,
|
|
123
|
+
actor: 'local',
|
|
124
|
+
summary: `Thread agent changed to ${canonicalAgent}.`,
|
|
125
|
+
metadata: { threadId, agentType: canonicalAgent, previousAgentType: currentAgent },
|
|
126
|
+
});
|
|
127
|
+
return `已将当前线程 Agent 切换为 ${canonicalAgent}。\n后续消息将使用 ${canonicalAgent} 处理。${runningNote}`;
|
|
128
|
+
}
|
|
129
|
+
hasActiveRun(threadId) {
|
|
130
|
+
const latestRun = this.options.getLatestRunForThread(threadId);
|
|
131
|
+
return Boolean(latestRun && ['queued', 'running', 'awaiting_input'].includes(latestRun.status));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
exports.ThreadCommandService = ThreadCommandService;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CHANNEL_PROTOCOL_INSTRUCTION = exports.SCHEDULER_PROTOCOL_INSTRUCTION = exports.LOCALCORE_ACP_AGENT_TYPE = exports.DESKTOP_INTERACTIVE_PERMISSION_AGENT_TYPES = exports.DESKTOP_PROVIDER_THINKING_OPTIONS = exports.DESKTOP_PROVIDER_PRESET_OPTIONS = exports.DESKTOP_PLATFORM_TYPE_OPTIONS = exports.DESKTOP_AGENT_TYPE_OPTIONS = exports.DESKTOP_LARK_SDK_PACKAGE = exports.DESKTOP_CLAUDECODE_ACP_PACKAGE = exports.DESKTOP_CODEX_ACP_PACKAGE = exports.DESKTOP_PI_CODING_AGENT_PACKAGE = exports.DESKTOP_PI_ACP_PACKAGE = exports.DEFAULT_DESKTOP_CLAUDECODE_MODEL = exports.DEFAULT_DESKTOP_OPENCODE_MODEL = exports.DEFAULT_DESKTOP_AGENT_TYPE = void 0;
|
|
3
|
+
exports.CHANNEL_PROTOCOL_INSTRUCTION = exports.MONITOR_PROTOCOL_INSTRUCTION = exports.SCHEDULER_PROTOCOL_INSTRUCTION = exports.LOCALCORE_ACP_AGENT_TYPE = exports.DESKTOP_INTERACTIVE_PERMISSION_AGENT_TYPES = exports.DESKTOP_PROVIDER_THINKING_OPTIONS = exports.DESKTOP_PROVIDER_PRESET_OPTIONS = exports.DESKTOP_PLATFORM_TYPE_OPTIONS = exports.DESKTOP_AGENT_TYPE_OPTIONS = exports.DESKTOP_LARK_SDK_PACKAGE = exports.DESKTOP_CLAUDECODE_ACP_PACKAGE = exports.DESKTOP_CODEX_ACP_PACKAGE = exports.DESKTOP_PI_CODING_AGENT_PACKAGE = exports.DESKTOP_PI_ACP_PACKAGE = exports.DEFAULT_DESKTOP_CLAUDECODE_MODEL = exports.DEFAULT_DESKTOP_OPENCODE_MODEL = exports.DEFAULT_DESKTOP_AGENT_TYPE = void 0;
|
|
4
4
|
exports.deriveDesktopRuntimePhase = deriveDesktopRuntimePhase;
|
|
5
5
|
exports.deriveDesktopRuntimeRoles = deriveDesktopRuntimeRoles;
|
|
6
6
|
exports.normalizeDesktopPlatformType = normalizeDesktopPlatformType;
|
|
@@ -78,6 +78,24 @@ exports.SCHEDULER_PROTOCOL_INSTRUCTION = [
|
|
|
78
78
|
'Only use the scheduler CLI when the user explicitly asks for scheduled automation.',
|
|
79
79
|
'[/Scheduler Tools]',
|
|
80
80
|
].join('\n');
|
|
81
|
+
exports.MONITOR_PROTOCOL_INSTRUCTION = [
|
|
82
|
+
'[Monitor Tools]',
|
|
83
|
+
'If the user asks to create, view, edit, delete, or manually run an event monitor for this conversation, use the Bash tool to run the local monitor CLI.',
|
|
84
|
+
'Use these commands:',
|
|
85
|
+
'lac monitor add --title "<short title>" --source stock.quote --symbol "<ticker>" --condition "<metric operator value>" --message "<exact analysis prompt>" [--cooldown 15m] [--execution-mode same-thread|side-thread]',
|
|
86
|
+
'lac monitor list',
|
|
87
|
+
'lac monitor list --thread',
|
|
88
|
+
'lac monitor info <short-monitor-id>',
|
|
89
|
+
'lac monitor edit <short-monitor-id> [--title "<title>"] [--condition "<expr>"] [--message "<exact prompt>"] [--enabled true|false] [--cooldown 15m] [--execution-mode same-thread|side-thread]',
|
|
90
|
+
'lac monitor del <short-monitor-id>',
|
|
91
|
+
'lac monitor run <short-monitor-id>',
|
|
92
|
+
'Supported stock metrics include latestPrice, change_percent, changePercent, and abs_change_percent. Example condition: "abs_change_percent >= 3".',
|
|
93
|
+
'Environment variables LOCAL_AI_WORKSPACE_ID, LOCAL_AI_THREAD_ID, LOCAL_AI_PLATFORM, LOCAL_AI_CHAT_ID, and LOCAL_AI_PLATFORM_USER_ID are already set when available.',
|
|
94
|
+
'Prefer relying on those variables instead of inventing your own route.',
|
|
95
|
+
'Use --execution-mode side-thread by default so monitor analysis does not interrupt the current conversation.',
|
|
96
|
+
'Only use the monitor CLI when the user explicitly asks for event monitoring automation.',
|
|
97
|
+
'[/Monitor Tools]',
|
|
98
|
+
].join('\n');
|
|
81
99
|
exports.CHANNEL_PROTOCOL_INSTRUCTION = [
|
|
82
100
|
'[Channel Tools]',
|
|
83
101
|
'If the user asks you to send a local file back through the current channel conversation, use the Bash tool to run the local channel CLI.',
|
|
@@ -138,6 +156,8 @@ function wrapUserMessageWithSchedulerProtocol(content, extraBlocks = []) {
|
|
|
138
156
|
return [
|
|
139
157
|
exports.SCHEDULER_PROTOCOL_INSTRUCTION,
|
|
140
158
|
'',
|
|
159
|
+
exports.MONITOR_PROTOCOL_INSTRUCTION,
|
|
160
|
+
'',
|
|
141
161
|
exports.CHANNEL_PROTOCOL_INSTRUCTION,
|
|
142
162
|
...extraBlocks.flatMap((block) => (block ? ['', block] : [])),
|
|
143
163
|
'',
|
|
@@ -53,6 +53,15 @@ const index_js_1 = require("../../packages/contracts/src/index.js");
|
|
|
53
53
|
strict_1.default.equal((0, index_js_1.normalizeScheduledJobRunStatus)('complete'), 'succeeded');
|
|
54
54
|
strict_1.default.equal((0, index_js_1.normalizeScheduledJobRunStatus)('cancelled'), 'skipped');
|
|
55
55
|
});
|
|
56
|
+
(0, node_test_1.default)('automation monitor status normalization accepts canonical aliases', () => {
|
|
57
|
+
strict_1.default.equal((0, index_js_1.normalizeAutomationMonitorStatus)(undefined), 'queued');
|
|
58
|
+
strict_1.default.equal((0, index_js_1.normalizeAutomationMonitorStatus)('completed'), 'succeeded');
|
|
59
|
+
strict_1.default.equal((0, index_js_1.normalizeAutomationMonitorStatus)('SKIPPED'), 'skipped');
|
|
60
|
+
});
|
|
61
|
+
(0, node_test_1.default)('automation monitor condition operator normalization rejects unsupported values', () => {
|
|
62
|
+
strict_1.default.equal((0, index_js_1.normalizeAutomationMonitorConditionOperator)('>='), '>=');
|
|
63
|
+
strict_1.default.throws(() => (0, index_js_1.normalizeAutomationMonitorConditionOperator)('contains'), /condition operator must be/);
|
|
64
|
+
});
|
|
56
65
|
(0, node_test_1.default)('channel content part type normalization accepts canonical aliases', () => {
|
|
57
66
|
strict_1.default.equal((0, index_js_1.normalizeChannelContentPartType)(' TEXT '), 'text');
|
|
58
67
|
strict_1.default.equal((0, index_js_1.normalizeChannelContentPartType)('permission-card'), 'permission_card');
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_test_1 = __importDefault(require("node:test"));
|
|
7
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
8
|
+
const condition_evaluator_js_1 = require("../../services/local-ai-core/src/automation/condition-evaluator.js");
|
|
9
|
+
const event = {
|
|
10
|
+
id: 'event-1',
|
|
11
|
+
sourceType: 'stock.quote',
|
|
12
|
+
occurredAt: '2026-05-11T00:00:00.000Z',
|
|
13
|
+
subject: 'AAPL',
|
|
14
|
+
payload: {
|
|
15
|
+
latestPrice: 188,
|
|
16
|
+
change_percent: 4.2,
|
|
17
|
+
volumeRatio: 2.1,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
(0, node_test_1.default)('monitor condition evaluator supports simple comparisons and safe boolean expressions', () => {
|
|
21
|
+
strict_1.default.equal((0, condition_evaluator_js_1.evaluateMonitorCondition)({
|
|
22
|
+
metric: 'abs_change_percent',
|
|
23
|
+
operator: '>=',
|
|
24
|
+
value: 3,
|
|
25
|
+
}, event), true);
|
|
26
|
+
strict_1.default.equal((0, condition_evaluator_js_1.evaluateMonitorCondition)({
|
|
27
|
+
metric: 'expression',
|
|
28
|
+
operator: '==',
|
|
29
|
+
value: true,
|
|
30
|
+
expression: 'abs_change_percent >= 3 && latestPrice > 100',
|
|
31
|
+
}, event), true);
|
|
32
|
+
strict_1.default.equal((0, condition_evaluator_js_1.evaluateMonitorCondition)({
|
|
33
|
+
metric: 'expression',
|
|
34
|
+
operator: '==',
|
|
35
|
+
value: true,
|
|
36
|
+
expression: 'latestPrice < 100 || volumeRatio >= 2',
|
|
37
|
+
}, event), true);
|
|
38
|
+
});
|