@kafca/agentdock 0.1.7
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 +70 -0
- package/bin/agentdock.mjs +275 -0
- package/build/icon.png +0 -0
- package/dist/renderer/assets/index-BaNpzC6d.js +482 -0
- package/dist/renderer/assets/index-CLWJyO0E.css +11 -0
- package/dist/renderer/favicon.svg +19 -0
- package/dist/renderer/index.html +14 -0
- package/dist/renderer/logo-options.svg +153 -0
- package/dist-electron/electron/knowledge-skill-script.test.js +125 -0
- package/dist-electron/electron/lac-cli.test.js +221 -0
- package/dist-electron/electron/local-core-refactor.test.js +1075 -0
- package/dist-electron/electron/main.js +163 -0
- package/dist-electron/electron/plugin-kernel.test.js +534 -0
- package/dist-electron/electron/preload.js +2 -0
- package/dist-electron/package.json +3 -0
- package/dist-electron/packages/contracts/src/index.js +18 -0
- package/dist-electron/packages/contracts/src/local-core.js +2 -0
- package/dist-electron/packages/core-sdk/src/index.js +406 -0
- package/dist-electron/packages/knowledge-api/src/ai-vector-provider.js +388 -0
- package/dist-electron/packages/knowledge-api/src/index.js +146 -0
- package/dist-electron/packages/knowledge-api/src/sqlite-store.js +467 -0
- package/dist-electron/packages/knowledge-api/src/thread-knowledge-store.js +21 -0
- package/dist-electron/packages/knowledge-api/test/ai-vector-provider.test.js +412 -0
- package/dist-electron/packages/plugin-sdk/src/index.js +2 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +369 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-response-processor.js +110 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-session-coordinator.js +171 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-store.js +635 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-transport.js +190 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +244 -0
- package/dist-electron/services/local-ai-core/src/acp/workspace-acp-permissions.js +52 -0
- package/dist-electron/services/local-ai-core/src/cli/lac.js +290 -0
- package/dist-electron/services/local-ai-core/src/gateway/local-core-lark-gateway.js +976 -0
- package/dist-electron/services/local-ai-core/src/gateway/local-core-weixin-gateway.js +1143 -0
- package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +291 -0
- package/dist-electron/services/local-ai-core/src/kernel/capability-registry.js +67 -0
- package/dist-electron/services/local-ai-core/src/kernel/diagnostics.js +27 -0
- package/dist-electron/services/local-ai-core/src/kernel/event-bus.js +27 -0
- package/dist-electron/services/local-ai-core/src/kernel/lifecycle-manager.js +85 -0
- package/dist-electron/services/local-ai-core/src/kernel/plugin-registry.js +60 -0
- package/dist-electron/services/local-ai-core/src/plugins/builtin/agent-localcore-acp-plugin.js +114 -0
- package/dist-electron/services/local-ai-core/src/plugins/builtin/channel-lark-plugin.js +59 -0
- package/dist-electron/services/local-ai-core/src/plugins/builtin/channel-weixin-plugin.js +56 -0
- package/dist-electron/services/local-ai-core/src/plugins/builtin/knowledge-ai-vector-plugin.js +7 -0
- package/dist-electron/services/local-ai-core/src/plugins/builtin/knowledge-noop-plugin.js +7 -0
- package/dist-electron/services/local-ai-core/src/plugins/builtin/runtime-capabilities-plugin.js +62 -0
- package/dist-electron/services/local-ai-core/src/plugins/builtin/scheduler-cron-plugin.js +49 -0
- package/dist-electron/services/local-ai-core/src/plugins/builtin/scheduler-lark-plugin.js +38 -0
- package/dist-electron/services/local-ai-core/src/plugins/builtin/scheduler-weixin-plugin.js +38 -0
- package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +230 -0
- package/dist-electron/services/local-ai-core/src/router/workspace-router-types.js +2 -0
- package/dist-electron/services/local-ai-core/src/router/workspace-router.js +389 -0
- package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +315 -0
- package/dist-electron/services/local-ai-core/src/runtime/local-core-runtime-state.js +282 -0
- package/dist-electron/services/local-ai-core/src/runtime/server.js +465 -0
- package/dist-electron/services/local-ai-core/src/runtime/standalone.js +38 -0
- package/dist-electron/services/local-ai-core/src/scheduler/adapters.js +2 -0
- package/dist-electron/services/local-ai-core/src/scheduler/cron-command-detector.js +70 -0
- package/dist-electron/services/local-ai-core/src/scheduler/cron.js +53 -0
- package/dist-electron/services/local-ai-core/src/scheduler/execution-policy.js +2 -0
- package/dist-electron/services/local-ai-core/src/scheduler/lark-execution-policies.js +66 -0
- package/dist-electron/services/local-ai-core/src/scheduler/lark-schedule-adapter.js +77 -0
- package/dist-electron/services/local-ai-core/src/scheduler/scheduled-conversation-executor.js +46 -0
- package/dist-electron/services/local-ai-core/src/scheduler/scheduler-run-lifecycle.js +64 -0
- package/dist-electron/services/local-ai-core/src/scheduler/scheduler-service.js +133 -0
- package/dist-electron/services/local-ai-core/src/scheduler/weixin-execution-policies.js +66 -0
- package/dist-electron/services/local-ai-core/src/scheduler/weixin-schedule-adapter.js +78 -0
- package/dist-electron/services/local-ai-core/src/thread/workspace-thread-id.js +17 -0
- package/dist-electron/services/local-ai-core/src/thread/workspace-thread-mappers.js +43 -0
- package/dist-electron/shared/desktop.js +195 -0
- package/dist-electron/src/api/client.js +73 -0
- package/dist-electron/src/api/sessions.js +21 -0
- package/dist-electron/src/lib/session-utils.js +51 -0
- package/dist-electron/src/pages/Threads/thread-chat-model.js +246 -0
- package/dist-electron/src/pages/Threads/thread-chat-model.test.js +76 -0
- package/dist-electron/src/pages/Threads/thread-chat-permission.js +26 -0
- package/dist-electron/src/pages/Threads/thread-chat-permission.test.js +102 -0
- package/dist-electron/src/pages/Threads/thread-chat-task-state.js +65 -0
- package/package.json +146 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
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.DEFAULT_DESKTOP_CLAUDECODE_MODEL = exports.DEFAULT_DESKTOP_OPENCODE_MODEL = exports.DEFAULT_DESKTOP_AGENT_TYPE = void 0;
|
|
4
|
+
exports.deriveDesktopRuntimePhase = deriveDesktopRuntimePhase;
|
|
5
|
+
exports.deriveDesktopRuntimeRoles = deriveDesktopRuntimeRoles;
|
|
6
|
+
exports.normalizeDesktopPlatformType = normalizeDesktopPlatformType;
|
|
7
|
+
exports.wrapUserMessageWithSchedulerProtocol = wrapUserMessageWithSchedulerProtocol;
|
|
8
|
+
exports.getDefaultDesktopAgentModel = getDefaultDesktopAgentModel;
|
|
9
|
+
exports.normalizeDesktopAgentModel = normalizeDesktopAgentModel;
|
|
10
|
+
exports.normalizePermissionResponse = normalizePermissionResponse;
|
|
11
|
+
exports.isPermissionButtonOption = isPermissionButtonOption;
|
|
12
|
+
exports.normalizeDesktopBridgeButtonOption = normalizeDesktopBridgeButtonOption;
|
|
13
|
+
exports.supportsInteractivePermission = supportsInteractivePermission;
|
|
14
|
+
exports.isAcpAgentType = isAcpAgentType;
|
|
15
|
+
exports.DEFAULT_DESKTOP_AGENT_TYPE = 'opencode';
|
|
16
|
+
exports.DEFAULT_DESKTOP_OPENCODE_MODEL = 'opencode/minimax-m2.5-free';
|
|
17
|
+
exports.DEFAULT_DESKTOP_CLAUDECODE_MODEL = '';
|
|
18
|
+
exports.DESKTOP_CLAUDECODE_ACP_PACKAGE = '@agentclientprotocol/claude-agent-acp';
|
|
19
|
+
exports.DESKTOP_LARK_SDK_PACKAGE = '@larksuiteoapi/node-sdk';
|
|
20
|
+
exports.DESKTOP_AGENT_TYPE_OPTIONS = [
|
|
21
|
+
'opencode',
|
|
22
|
+
'codex',
|
|
23
|
+
'claudecode',
|
|
24
|
+
'cursor',
|
|
25
|
+
'gemini',
|
|
26
|
+
'qoder',
|
|
27
|
+
'iflow',
|
|
28
|
+
'localcore-acp',
|
|
29
|
+
];
|
|
30
|
+
exports.DESKTOP_PLATFORM_TYPE_OPTIONS = [
|
|
31
|
+
'telegram',
|
|
32
|
+
'feishu',
|
|
33
|
+
'lark',
|
|
34
|
+
'discord',
|
|
35
|
+
'slack',
|
|
36
|
+
'dingtalk',
|
|
37
|
+
'wecom',
|
|
38
|
+
'weixin',
|
|
39
|
+
'qq',
|
|
40
|
+
'qqbot',
|
|
41
|
+
'line',
|
|
42
|
+
];
|
|
43
|
+
exports.DESKTOP_PROVIDER_PRESET_OPTIONS = [
|
|
44
|
+
'openai',
|
|
45
|
+
'openrouter',
|
|
46
|
+
'anthropic',
|
|
47
|
+
'minimax',
|
|
48
|
+
'zhipuai',
|
|
49
|
+
'deepseek',
|
|
50
|
+
'siliconflow',
|
|
51
|
+
'moonshot',
|
|
52
|
+
'ollama',
|
|
53
|
+
];
|
|
54
|
+
exports.DESKTOP_PROVIDER_THINKING_OPTIONS = ['', 'enabled', 'disabled'];
|
|
55
|
+
exports.DESKTOP_INTERACTIVE_PERMISSION_AGENT_TYPES = ['opencode', 'claudecode', 'acp', 'localcore-acp'];
|
|
56
|
+
exports.LOCALCORE_ACP_AGENT_TYPE = 'localcore-acp';
|
|
57
|
+
exports.SCHEDULER_PROTOCOL_INSTRUCTION = [
|
|
58
|
+
'[Scheduler Tools]',
|
|
59
|
+
'If the user asks to create, view, edit, delete, or manually run a scheduled task for this conversation, use the Bash tool to run the local scheduler CLI.',
|
|
60
|
+
'Use these commands:',
|
|
61
|
+
'lac scheduler add --cron "<5-field cron>" --message "<exact message to send>" --desc "<short label>" [--execution-mode same-thread|side-thread]',
|
|
62
|
+
'lac scheduler list',
|
|
63
|
+
'lac scheduler list --thread',
|
|
64
|
+
'lac scheduler info <job-id>',
|
|
65
|
+
'lac scheduler edit <job-id> [--cron "<5-field cron>"] [--message "<exact message>"] [--desc "<short label>"] [--enabled true|false] [--execution-mode same-thread|side-thread]',
|
|
66
|
+
'lac scheduler del <job-id>',
|
|
67
|
+
'lac scheduler run <job-id>',
|
|
68
|
+
'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.',
|
|
69
|
+
'Prefer relying on those variables instead of inventing your own route or creating session-only cron jobs.',
|
|
70
|
+
'By default, `lac scheduler list` shows all scheduled tasks in the current workspace. Use `lac scheduler list --thread` to show only the current conversation thread.',
|
|
71
|
+
'Use `--execution-mode same-thread` to reuse the current thread, or `--execution-mode side-thread` to run in a dedicated scheduled thread.',
|
|
72
|
+
'Only use the scheduler CLI when the user explicitly asks for scheduled automation.',
|
|
73
|
+
'[/Scheduler Tools]',
|
|
74
|
+
].join('\n');
|
|
75
|
+
const PERMISSION_RESPONSE_MAP = {
|
|
76
|
+
allow: 'allow',
|
|
77
|
+
deny: 'deny',
|
|
78
|
+
'allow all': 'allow all',
|
|
79
|
+
allowall: 'allow all',
|
|
80
|
+
'perm:allow': 'allow',
|
|
81
|
+
'perm:deny': 'deny',
|
|
82
|
+
'perm:allow_all': 'allow all',
|
|
83
|
+
};
|
|
84
|
+
function deriveDesktopRuntimePhase(service) {
|
|
85
|
+
if (service.status === 'starting') {
|
|
86
|
+
return 'starting';
|
|
87
|
+
}
|
|
88
|
+
return 'api_ready';
|
|
89
|
+
}
|
|
90
|
+
function deriveDesktopRuntimeRoles(service) {
|
|
91
|
+
return {
|
|
92
|
+
conversation: {
|
|
93
|
+
status: 'running',
|
|
94
|
+
label: 'Local AI Core',
|
|
95
|
+
},
|
|
96
|
+
platformGateway: {
|
|
97
|
+
status: service.status,
|
|
98
|
+
label: 'Native Platform Gateway',
|
|
99
|
+
service,
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function normalizeDesktopPlatformType(platformType) {
|
|
104
|
+
const normalized = String(platformType || '').trim().toLowerCase();
|
|
105
|
+
if (normalized === 'feishu') {
|
|
106
|
+
return 'lark';
|
|
107
|
+
}
|
|
108
|
+
return normalized;
|
|
109
|
+
}
|
|
110
|
+
function wrapUserMessageWithSchedulerProtocol(content, extraBlocks = []) {
|
|
111
|
+
return [
|
|
112
|
+
exports.SCHEDULER_PROTOCOL_INSTRUCTION,
|
|
113
|
+
...extraBlocks.flatMap((block) => (block ? ['', block] : [])),
|
|
114
|
+
'',
|
|
115
|
+
'[User Message]',
|
|
116
|
+
content,
|
|
117
|
+
'[/User Message]',
|
|
118
|
+
].join('\n');
|
|
119
|
+
}
|
|
120
|
+
function getDefaultDesktopAgentModel(agentType) {
|
|
121
|
+
switch (String(agentType || '').trim().toLowerCase()) {
|
|
122
|
+
case 'opencode':
|
|
123
|
+
return exports.DEFAULT_DESKTOP_OPENCODE_MODEL;
|
|
124
|
+
case 'claudecode':
|
|
125
|
+
return exports.DEFAULT_DESKTOP_CLAUDECODE_MODEL;
|
|
126
|
+
default:
|
|
127
|
+
return '';
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function normalizeDesktopAgentModel(agentType, model) {
|
|
131
|
+
const normalizedType = String(agentType || '').trim().toLowerCase();
|
|
132
|
+
const normalizedModel = String(model || '').trim();
|
|
133
|
+
if (!normalizedType) {
|
|
134
|
+
return normalizedModel;
|
|
135
|
+
}
|
|
136
|
+
if (normalizedType === 'opencode') {
|
|
137
|
+
return normalizedModel || exports.DEFAULT_DESKTOP_OPENCODE_MODEL;
|
|
138
|
+
}
|
|
139
|
+
if (normalizedType === 'claudecode' && normalizedModel.startsWith('opencode/')) {
|
|
140
|
+
return '';
|
|
141
|
+
}
|
|
142
|
+
return normalizedModel;
|
|
143
|
+
}
|
|
144
|
+
function normalizePermissionResponse(input) {
|
|
145
|
+
if (!input) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
return PERMISSION_RESPONSE_MAP[String(input).trim().toLowerCase()] || null;
|
|
149
|
+
}
|
|
150
|
+
function isPermissionButtonOption(option) {
|
|
151
|
+
return Boolean(normalizePermissionResponse(option?.data));
|
|
152
|
+
}
|
|
153
|
+
function normalizeDesktopBridgeButtonOption(input) {
|
|
154
|
+
if (!input || typeof input !== 'object') {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
const record = input;
|
|
158
|
+
const rawText = typeof record.text === 'string'
|
|
159
|
+
? record.text
|
|
160
|
+
: typeof record.Text === 'string'
|
|
161
|
+
? record.Text
|
|
162
|
+
: '';
|
|
163
|
+
const rawData = typeof record.data === 'string'
|
|
164
|
+
? record.data
|
|
165
|
+
: typeof record.Data === 'string'
|
|
166
|
+
? record.Data
|
|
167
|
+
: '';
|
|
168
|
+
if (!rawText || !rawData) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
const permissionResponse = normalizePermissionResponse(rawData);
|
|
172
|
+
if (permissionResponse) {
|
|
173
|
+
return {
|
|
174
|
+
text: permissionResponse,
|
|
175
|
+
data: permissionResponse,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
text: rawText,
|
|
180
|
+
data: rawData,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function supportsInteractivePermission(agentType) {
|
|
184
|
+
if (!agentType) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
return exports.DESKTOP_INTERACTIVE_PERMISSION_AGENT_TYPES.includes(String(agentType).trim().toLowerCase());
|
|
188
|
+
}
|
|
189
|
+
function isAcpAgentType(agentType) {
|
|
190
|
+
const normalized = String(agentType || '').trim().toLowerCase();
|
|
191
|
+
return normalized === 'acp'
|
|
192
|
+
|| normalized === 'opencode'
|
|
193
|
+
|| normalized === 'claudecode'
|
|
194
|
+
|| normalized === exports.LOCALCORE_ACP_AGENT_TYPE;
|
|
195
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.api = exports.ApiError = void 0;
|
|
4
|
+
const DEFAULT_API_BASE = '/api/v1';
|
|
5
|
+
class ApiClient {
|
|
6
|
+
token = '';
|
|
7
|
+
baseUrl = DEFAULT_API_BASE;
|
|
8
|
+
setToken(token) {
|
|
9
|
+
this.token = token;
|
|
10
|
+
}
|
|
11
|
+
setBaseUrl(baseUrl) {
|
|
12
|
+
const trimmed = baseUrl.trim();
|
|
13
|
+
if (!trimmed) {
|
|
14
|
+
this.baseUrl = DEFAULT_API_BASE;
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
this.baseUrl = trimmed.endsWith('/api/v1')
|
|
18
|
+
? trimmed
|
|
19
|
+
: `${trimmed.replace(/\/+$/, '')}/api/v1`;
|
|
20
|
+
}
|
|
21
|
+
getToken() {
|
|
22
|
+
return this.token;
|
|
23
|
+
}
|
|
24
|
+
getBaseUrl() {
|
|
25
|
+
return this.baseUrl;
|
|
26
|
+
}
|
|
27
|
+
headers() {
|
|
28
|
+
const h = { 'Content-Type': 'application/json' };
|
|
29
|
+
if (this.token)
|
|
30
|
+
h['Authorization'] = `Bearer ${this.token}`;
|
|
31
|
+
return h;
|
|
32
|
+
}
|
|
33
|
+
async request(method, path, body, params) {
|
|
34
|
+
let url = this.resolveUrl(path);
|
|
35
|
+
if (params) {
|
|
36
|
+
const qs = new URLSearchParams(params).toString();
|
|
37
|
+
if (qs)
|
|
38
|
+
url += `?${qs}`;
|
|
39
|
+
}
|
|
40
|
+
const res = await fetch(url, {
|
|
41
|
+
method,
|
|
42
|
+
headers: this.headers(),
|
|
43
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
44
|
+
});
|
|
45
|
+
const json = await res.json();
|
|
46
|
+
if (!json.ok) {
|
|
47
|
+
throw new ApiError(json.error || 'Unknown error', res.status);
|
|
48
|
+
}
|
|
49
|
+
return json.data;
|
|
50
|
+
}
|
|
51
|
+
get(path, params) { return this.request('GET', path, undefined, params); }
|
|
52
|
+
post(path, body) { return this.request('POST', path, body); }
|
|
53
|
+
patch(path, body) { return this.request('PATCH', path, body); }
|
|
54
|
+
delete(path) { return this.request('DELETE', path); }
|
|
55
|
+
resolveUrl(path) {
|
|
56
|
+
const normalizedPath = path.replace(/^\/+/, '');
|
|
57
|
+
if (this.baseUrl.startsWith('http://') || this.baseUrl.startsWith('https://')) {
|
|
58
|
+
return new URL(normalizedPath, `${this.baseUrl}/`).toString();
|
|
59
|
+
}
|
|
60
|
+
return `${this.baseUrl}/${normalizedPath}`.replace(/([^:]\/)\/+/g, '$1');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
class ApiError extends Error {
|
|
64
|
+
status;
|
|
65
|
+
constructor(message, status) {
|
|
66
|
+
super(message);
|
|
67
|
+
this.status = status;
|
|
68
|
+
this.name = 'ApiError';
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.ApiError = ApiError;
|
|
72
|
+
exports.api = new ApiClient();
|
|
73
|
+
exports.default = exports.api;
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
exports.sendMessage = exports.switchSession = exports.deleteSession = exports.renameSession = exports.createSession = exports.getSession = exports.listSessions = void 0;
|
|
7
|
+
const client_1 = __importDefault(require("./client"));
|
|
8
|
+
const listSessions = (project) => client_1.default.get(`/projects/${project}/sessions`);
|
|
9
|
+
exports.listSessions = listSessions;
|
|
10
|
+
const getSession = (project, id, historyLimit) => client_1.default.get(`/projects/${project}/sessions/${id}`, historyLimit ? { history_limit: String(historyLimit) } : undefined);
|
|
11
|
+
exports.getSession = getSession;
|
|
12
|
+
const createSession = (project, body) => client_1.default.post(`/projects/${project}/sessions`, body);
|
|
13
|
+
exports.createSession = createSession;
|
|
14
|
+
const renameSession = (project, id, body) => client_1.default.patch(`/projects/${project}/sessions/${id}`, body);
|
|
15
|
+
exports.renameSession = renameSession;
|
|
16
|
+
const deleteSession = (project, id) => client_1.default.delete(`/projects/${project}/sessions/${id}`);
|
|
17
|
+
exports.deleteSession = deleteSession;
|
|
18
|
+
const switchSession = (project, body) => client_1.default.post(`/projects/${project}/sessions/switch`, body);
|
|
19
|
+
exports.switchSession = switchSession;
|
|
20
|
+
const sendMessage = (project, body) => client_1.default.post(`/projects/${project}/send`, body);
|
|
21
|
+
exports.sendMessage = sendMessage;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sessionLabel = sessionLabel;
|
|
4
|
+
exports.sessionMatchesSearch = sessionMatchesSearch;
|
|
5
|
+
exports.sortSessionsByLiveAndUpdated = sortSessionsByLiveAndUpdated;
|
|
6
|
+
exports.timeAgo = timeAgo;
|
|
7
|
+
function sessionLabel(session) {
|
|
8
|
+
return session.name || session.user_name || session.chat_name || `Session ${session.id.slice(0, 8)}`;
|
|
9
|
+
}
|
|
10
|
+
function sessionMatchesSearch(session, query) {
|
|
11
|
+
if (!query) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
const haystack = [
|
|
15
|
+
sessionLabel(session),
|
|
16
|
+
session.session_key,
|
|
17
|
+
session.last_message?.content || '',
|
|
18
|
+
session.user_name || '',
|
|
19
|
+
session.chat_name || '',
|
|
20
|
+
]
|
|
21
|
+
.join(' ')
|
|
22
|
+
.toLowerCase();
|
|
23
|
+
return haystack.includes(query);
|
|
24
|
+
}
|
|
25
|
+
function sortSessionsByLiveAndUpdated(items) {
|
|
26
|
+
return [...items].sort((a, b) => {
|
|
27
|
+
if (a.live !== b.live) {
|
|
28
|
+
return a.live ? -1 : 1;
|
|
29
|
+
}
|
|
30
|
+
return (b.updated_at || b.created_at || '').localeCompare(a.updated_at || a.created_at || '');
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function timeAgo(iso, justNowLabel = 'just now') {
|
|
34
|
+
if (!iso) {
|
|
35
|
+
return '';
|
|
36
|
+
}
|
|
37
|
+
const diff = Date.now() - new Date(iso).getTime();
|
|
38
|
+
const mins = Math.floor(diff / 60000);
|
|
39
|
+
if (mins < 1) {
|
|
40
|
+
return justNowLabel;
|
|
41
|
+
}
|
|
42
|
+
if (mins < 60) {
|
|
43
|
+
return `${mins}m`;
|
|
44
|
+
}
|
|
45
|
+
const hours = Math.floor(mins / 60);
|
|
46
|
+
if (hours < 24) {
|
|
47
|
+
return `${hours}h`;
|
|
48
|
+
}
|
|
49
|
+
const days = Math.floor(hours / 24);
|
|
50
|
+
return `${days}d`;
|
|
51
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.upsertSessionGroup = exports.ASSISTANT_REPLY_TIMEOUT_MS = void 0;
|
|
4
|
+
exports.isInternalProgressMessage = isInternalProgressMessage;
|
|
5
|
+
exports.isAwaitingInputMessage = isAwaitingInputMessage;
|
|
6
|
+
exports.extractVisibleMessageContent = extractVisibleMessageContent;
|
|
7
|
+
exports.sessionMatchesDesktop = sessionMatchesDesktop;
|
|
8
|
+
exports.toMessages = toMessages;
|
|
9
|
+
exports.toMessagesFromThread = toMessagesFromThread;
|
|
10
|
+
exports.toChatThreadSummary = toChatThreadSummary;
|
|
11
|
+
exports.toCoreChatThreadSummary = toCoreChatThreadSummary;
|
|
12
|
+
exports.sortChatThreadsByLiveAndUpdated = sortChatThreadsByLiveAndUpdated;
|
|
13
|
+
exports.chatThreadMatchesSearch = chatThreadMatchesSearch;
|
|
14
|
+
exports.sortChatMessages = sortChatMessages;
|
|
15
|
+
exports.formatMessageTimestamp = formatMessageTimestamp;
|
|
16
|
+
exports.formatRuntimePhase = formatRuntimePhase;
|
|
17
|
+
exports.sessionProjectFromKey = sessionProjectFromKey;
|
|
18
|
+
exports.normalizeBridgeActionRows = normalizeBridgeActionRows;
|
|
19
|
+
exports.upsertThreadGroup = upsertThreadGroup;
|
|
20
|
+
exports.upsertThreadInGroup = upsertThreadInGroup;
|
|
21
|
+
exports.isPermissionActionRow = isPermissionActionRow;
|
|
22
|
+
exports.formatTaskHint = formatTaskHint;
|
|
23
|
+
exports.isTaskRunningState = isTaskRunningState;
|
|
24
|
+
exports.isTaskInputLocked = isTaskInputLocked;
|
|
25
|
+
exports.canStreamingPromoteTaskState = canStreamingPromoteTaskState;
|
|
26
|
+
const desktop_1 = require("../../../shared/desktop");
|
|
27
|
+
const session_utils_1 = require("@/lib/session-utils");
|
|
28
|
+
exports.ASSISTANT_REPLY_TIMEOUT_MS = 90000;
|
|
29
|
+
function isInternalProgressMessage(content) {
|
|
30
|
+
if (!content) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return (content.startsWith('💭 ') ||
|
|
34
|
+
content.startsWith('🔧 ') ||
|
|
35
|
+
content.startsWith('📤 ') ||
|
|
36
|
+
content.startsWith('⏳ '));
|
|
37
|
+
}
|
|
38
|
+
function isAwaitingInputMessage(content) {
|
|
39
|
+
if (!content) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const normalized = content.replace(/\s+/g, ' ').trim();
|
|
43
|
+
return (/^Agent 提问(?:\s*\(\d+\/\d+\))?/i.test(normalized) ||
|
|
44
|
+
normalized.includes('请回复选项编号') ||
|
|
45
|
+
normalized.includes('直接输入你的回答') ||
|
|
46
|
+
normalized.includes('等待你的回复') ||
|
|
47
|
+
normalized.includes('请直接回复') ||
|
|
48
|
+
normalized.includes('请输入你的回答'));
|
|
49
|
+
}
|
|
50
|
+
function extractVisibleMessageContent(content) {
|
|
51
|
+
if (!content) {
|
|
52
|
+
return '';
|
|
53
|
+
}
|
|
54
|
+
const match = content.match(/\[User Message\]\s*([\s\S]*?)\s*\[\/User Message\]/);
|
|
55
|
+
if (!match) {
|
|
56
|
+
return content;
|
|
57
|
+
}
|
|
58
|
+
return match[1] || '';
|
|
59
|
+
}
|
|
60
|
+
function sessionMatchesDesktop(session) {
|
|
61
|
+
return session.platform === 'desktop' || session.session_key.startsWith('desktop:');
|
|
62
|
+
}
|
|
63
|
+
function toMessages(history) {
|
|
64
|
+
return history.map((message, index) => ({
|
|
65
|
+
id: `${index}-${message.timestamp || message.role}`,
|
|
66
|
+
role: message.role === 'user' ? 'user' : 'assistant',
|
|
67
|
+
content: message.role === 'user' ? extractVisibleMessageContent(message.content) : message.content,
|
|
68
|
+
kind: message.kind === 'progress' ? 'progress' : 'final',
|
|
69
|
+
order: index,
|
|
70
|
+
timestamp: message.timestamp,
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
function toMessagesFromThread(history) {
|
|
74
|
+
return history.map((message, index) => ({
|
|
75
|
+
id: message.id || `${index}-${message.timestamp || message.role}`,
|
|
76
|
+
role: message.role === 'user' ? 'user' : 'assistant',
|
|
77
|
+
content: message.role === 'user' ? extractVisibleMessageContent(message.content) : message.content,
|
|
78
|
+
kind: message.kind === 'progress'
|
|
79
|
+
? 'progress'
|
|
80
|
+
: message.kind === 'system'
|
|
81
|
+
? 'progress'
|
|
82
|
+
: 'final',
|
|
83
|
+
order: index,
|
|
84
|
+
timestamp: message.timestamp,
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
function toChatThreadSummary(project, session) {
|
|
88
|
+
return {
|
|
89
|
+
id: session.id,
|
|
90
|
+
project,
|
|
91
|
+
name: (0, session_utils_1.sessionLabel)(session),
|
|
92
|
+
live: session.live,
|
|
93
|
+
createdAt: session.created_at,
|
|
94
|
+
updatedAt: session.updated_at,
|
|
95
|
+
excerpt: extractVisibleMessageContent(session.last_message?.content || ''),
|
|
96
|
+
agentType: session.agent_type,
|
|
97
|
+
bridgeSessionKey: session.session_key,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function toCoreChatThreadSummary(thread) {
|
|
101
|
+
return {
|
|
102
|
+
id: thread.id,
|
|
103
|
+
project: thread.workspaceId,
|
|
104
|
+
name: thread.title,
|
|
105
|
+
live: thread.live,
|
|
106
|
+
createdAt: thread.createdAt,
|
|
107
|
+
updatedAt: thread.updatedAt,
|
|
108
|
+
excerpt: extractVisibleMessageContent(thread.excerpt),
|
|
109
|
+
agentType: thread.agentType,
|
|
110
|
+
bridgeSessionKey: thread.bridgeSessionKey,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function sortChatThreadsByLiveAndUpdated(items) {
|
|
114
|
+
return [...items].sort((a, b) => {
|
|
115
|
+
if (a.live !== b.live) {
|
|
116
|
+
return a.live ? -1 : 1;
|
|
117
|
+
}
|
|
118
|
+
return (b.updatedAt || b.createdAt || '').localeCompare(a.updatedAt || a.createdAt || '');
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function chatThreadMatchesSearch(thread, query) {
|
|
122
|
+
if (!query) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
const haystack = [thread.name, thread.excerpt, thread.bridgeSessionKey || ''].join(' ').toLowerCase();
|
|
126
|
+
return haystack.includes(query);
|
|
127
|
+
}
|
|
128
|
+
function sortChatMessages(messages) {
|
|
129
|
+
return [...messages].sort((a, b) => {
|
|
130
|
+
const aTime = a.timestamp ? new Date(a.timestamp).getTime() : Number.NaN;
|
|
131
|
+
const bTime = b.timestamp ? new Date(b.timestamp).getTime() : Number.NaN;
|
|
132
|
+
const aHasTime = Number.isFinite(aTime);
|
|
133
|
+
const bHasTime = Number.isFinite(bTime);
|
|
134
|
+
if (aHasTime && bHasTime && aTime !== bTime) {
|
|
135
|
+
return aTime - bTime;
|
|
136
|
+
}
|
|
137
|
+
if (a.order !== b.order) {
|
|
138
|
+
return a.order - b.order;
|
|
139
|
+
}
|
|
140
|
+
return a.id.localeCompare(b.id);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
function formatMessageTimestamp(timestamp) {
|
|
144
|
+
if (!timestamp) {
|
|
145
|
+
return '';
|
|
146
|
+
}
|
|
147
|
+
const date = new Date(timestamp);
|
|
148
|
+
if (Number.isNaN(date.getTime())) {
|
|
149
|
+
return '';
|
|
150
|
+
}
|
|
151
|
+
return new Intl.DateTimeFormat(undefined, {
|
|
152
|
+
hour: '2-digit',
|
|
153
|
+
minute: '2-digit',
|
|
154
|
+
second: '2-digit',
|
|
155
|
+
}).format(date);
|
|
156
|
+
}
|
|
157
|
+
function formatRuntimePhase(phase) {
|
|
158
|
+
switch (phase) {
|
|
159
|
+
case 'starting':
|
|
160
|
+
return '启动中';
|
|
161
|
+
case 'api_ready':
|
|
162
|
+
return '服务就绪';
|
|
163
|
+
case 'error':
|
|
164
|
+
return '运行异常';
|
|
165
|
+
default:
|
|
166
|
+
return '未启动';
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function sessionProjectFromKey(sessionKey) {
|
|
170
|
+
if (!sessionKey?.startsWith('desktop:')) {
|
|
171
|
+
return '';
|
|
172
|
+
}
|
|
173
|
+
const [, project = ''] = sessionKey.split(':');
|
|
174
|
+
return project;
|
|
175
|
+
}
|
|
176
|
+
function normalizeBridgeActionRows(input) {
|
|
177
|
+
if (!Array.isArray(input)) {
|
|
178
|
+
return [];
|
|
179
|
+
}
|
|
180
|
+
return input
|
|
181
|
+
.map((row) => {
|
|
182
|
+
if (!Array.isArray(row)) {
|
|
183
|
+
return [];
|
|
184
|
+
}
|
|
185
|
+
return row
|
|
186
|
+
.map((button) => (0, desktop_1.normalizeDesktopBridgeButtonOption)(button))
|
|
187
|
+
.filter((button) => Boolean(button));
|
|
188
|
+
})
|
|
189
|
+
.filter((row) => row.length > 0);
|
|
190
|
+
}
|
|
191
|
+
function upsertThreadGroup(groups, project, sessions) {
|
|
192
|
+
const next = groups.filter((group) => group.project !== project);
|
|
193
|
+
next.push({ project, sessions });
|
|
194
|
+
return next.sort((a, b) => a.project.localeCompare(b.project));
|
|
195
|
+
}
|
|
196
|
+
exports.upsertSessionGroup = upsertThreadGroup;
|
|
197
|
+
function upsertThreadInGroup(groups, project, thread) {
|
|
198
|
+
const current = groups.find((group) => group.project === project)?.sessions || [];
|
|
199
|
+
const nextSessions = sortChatThreadsByLiveAndUpdated([
|
|
200
|
+
thread,
|
|
201
|
+
...current.filter((item) => item.id !== thread.id),
|
|
202
|
+
]);
|
|
203
|
+
return upsertThreadGroup(groups, project, nextSessions);
|
|
204
|
+
}
|
|
205
|
+
function isPermissionActionRow(rows) {
|
|
206
|
+
return rows.some((row) => row.some((action) => (0, desktop_1.isPermissionButtonOption)(action)));
|
|
207
|
+
}
|
|
208
|
+
function formatTaskHint(taskState, typing) {
|
|
209
|
+
if (taskState === 'stopping') {
|
|
210
|
+
return 'Stopping current task…';
|
|
211
|
+
}
|
|
212
|
+
if (taskState === 'error') {
|
|
213
|
+
return '';
|
|
214
|
+
}
|
|
215
|
+
if (taskState === 'awaiting_input') {
|
|
216
|
+
return 'Agent is waiting for your reply.';
|
|
217
|
+
}
|
|
218
|
+
if (taskState === 'permission_submitted') {
|
|
219
|
+
return 'Permission sent. Waiting for the agent to continue…';
|
|
220
|
+
}
|
|
221
|
+
if (taskState === 'awaiting_permission') {
|
|
222
|
+
return 'Waiting for your permission response.';
|
|
223
|
+
}
|
|
224
|
+
if (typing) {
|
|
225
|
+
return 'Agent is typing…';
|
|
226
|
+
}
|
|
227
|
+
if (taskState === 'running') {
|
|
228
|
+
return 'Task is running…';
|
|
229
|
+
}
|
|
230
|
+
return '';
|
|
231
|
+
}
|
|
232
|
+
function isTaskRunningState(taskState) {
|
|
233
|
+
return (taskState === 'running' ||
|
|
234
|
+
taskState === 'awaiting_permission' ||
|
|
235
|
+
taskState === 'permission_submitted' ||
|
|
236
|
+
taskState === 'stopping');
|
|
237
|
+
}
|
|
238
|
+
function isTaskInputLocked(taskState) {
|
|
239
|
+
return taskState !== 'idle' && taskState !== 'awaiting_input' && taskState !== 'error';
|
|
240
|
+
}
|
|
241
|
+
function canStreamingPromoteTaskState(taskState) {
|
|
242
|
+
return taskState !== 'awaiting_input' &&
|
|
243
|
+
taskState !== 'awaiting_permission' &&
|
|
244
|
+
taskState !== 'permission_submitted' &&
|
|
245
|
+
taskState !== 'stopping';
|
|
246
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
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 thread_chat_model_1 = require("./thread-chat-model");
|
|
9
|
+
function createPermissionActions() {
|
|
10
|
+
return [[
|
|
11
|
+
{ text: 'Allow', data: 'allow' },
|
|
12
|
+
{ text: 'Deny', data: 'deny' },
|
|
13
|
+
]];
|
|
14
|
+
}
|
|
15
|
+
function createMessage(overrides) {
|
|
16
|
+
return {
|
|
17
|
+
id: 'message',
|
|
18
|
+
role: 'assistant',
|
|
19
|
+
content: 'Permission required',
|
|
20
|
+
order: 0,
|
|
21
|
+
...overrides,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
(0, node_test_1.default)('getLatestInteractivePermissionMessage returns the latest actionable permission prompt', () => {
|
|
25
|
+
const messages = [
|
|
26
|
+
createMessage({
|
|
27
|
+
id: 'older-permission',
|
|
28
|
+
order: 1,
|
|
29
|
+
actionMode: 'permission',
|
|
30
|
+
actionInteractive: true,
|
|
31
|
+
actions: createPermissionActions(),
|
|
32
|
+
}),
|
|
33
|
+
createMessage({
|
|
34
|
+
id: 'generic-buttons',
|
|
35
|
+
order: 2,
|
|
36
|
+
actionMode: 'generic',
|
|
37
|
+
actionInteractive: true,
|
|
38
|
+
actions: createPermissionActions(),
|
|
39
|
+
}),
|
|
40
|
+
createMessage({
|
|
41
|
+
id: 'latest-permission',
|
|
42
|
+
order: 3,
|
|
43
|
+
actionMode: 'permission',
|
|
44
|
+
actionInteractive: true,
|
|
45
|
+
actions: createPermissionActions(),
|
|
46
|
+
}),
|
|
47
|
+
];
|
|
48
|
+
const prompt = (0, thread_chat_model_1.getLatestInteractivePermissionMessage)(messages);
|
|
49
|
+
strict_1.default.ok(prompt);
|
|
50
|
+
strict_1.default.equal(prompt.id, 'latest-permission');
|
|
51
|
+
});
|
|
52
|
+
(0, node_test_1.default)('getLatestInteractivePermissionMessage ignores submitted or non-interactive prompts', () => {
|
|
53
|
+
const messages = [
|
|
54
|
+
createMessage({
|
|
55
|
+
id: 'submitted-permission',
|
|
56
|
+
order: 1,
|
|
57
|
+
actionMode: 'permission',
|
|
58
|
+
actionInteractive: true,
|
|
59
|
+
actions: [],
|
|
60
|
+
actionStatus: 'Permission sent',
|
|
61
|
+
}),
|
|
62
|
+
createMessage({
|
|
63
|
+
id: 'unsupported-permission',
|
|
64
|
+
order: 2,
|
|
65
|
+
actionMode: 'permission',
|
|
66
|
+
actionInteractive: false,
|
|
67
|
+
actions: createPermissionActions(),
|
|
68
|
+
}),
|
|
69
|
+
];
|
|
70
|
+
strict_1.default.equal((0, thread_chat_model_1.getLatestInteractivePermissionMessage)(messages), undefined);
|
|
71
|
+
});
|
|
72
|
+
(0, node_test_1.default)('taskStateAfterTypingStop keeps awaiting_permission prompts visible', () => {
|
|
73
|
+
strict_1.default.equal((0, thread_chat_model_1.taskStateAfterTypingStop)('awaiting_permission'), 'awaiting_permission');
|
|
74
|
+
strict_1.default.equal((0, thread_chat_model_1.taskStateAfterTypingStop)('running'), 'idle');
|
|
75
|
+
strict_1.default.equal((0, thread_chat_model_1.taskStateAfterTypingStop)('permission_submitted'), 'idle');
|
|
76
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toPendingPermissionRequest = toPendingPermissionRequest;
|
|
4
|
+
function toPendingPermissionRequest(message) {
|
|
5
|
+
if (!message.id ||
|
|
6
|
+
message.role !== 'assistant' ||
|
|
7
|
+
message.actionMode !== 'permission' ||
|
|
8
|
+
!message.actionInteractive ||
|
|
9
|
+
!message.content) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
const actions = message.actions?.filter((row) => row.length > 0) || [];
|
|
13
|
+
if (actions.length === 0) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
id: message.id,
|
|
18
|
+
content: message.content,
|
|
19
|
+
actions,
|
|
20
|
+
actionReplyCtx: message.actionReplyCtx,
|
|
21
|
+
actionPending: message.actionPending,
|
|
22
|
+
actionStatus: message.actionStatus,
|
|
23
|
+
actionMode: 'permission',
|
|
24
|
+
actionInteractive: true,
|
|
25
|
+
};
|
|
26
|
+
}
|