@kafca/agentdock 0.1.44 → 0.1.46
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 +76 -11
- package/bin/agentdock.mjs +5 -5
- package/dist/renderer/assets/{Badge-Qk9qHovH.js → Badge-BIesaDYG.js} +1 -1
- package/dist/renderer/assets/Chat-CpataoDh.js +2 -0
- package/dist/renderer/assets/{ChatMarkdown-BKdW8OON.js → ChatMarkdown-_Mdmltgv.js} +1 -1
- package/dist/renderer/assets/Config-Pe5-zKZk.js +21 -0
- package/dist/renderer/assets/CronList-BnaOSPT8.js +1 -0
- package/dist/renderer/assets/Dashboard-D05l8GKE.js +26 -0
- package/dist/renderer/assets/{EmptyState-kD9yAwtw.js → EmptyState-BDBavDll.js} +1 -1
- package/dist/renderer/assets/{KnowledgeDetail-BAMCLrjz.js → KnowledgeDetail-DvR798l_.js} +2 -2
- package/dist/renderer/assets/KnowledgeHome-r2ZuvU8-.js +11 -0
- package/dist/renderer/assets/Logs-BaljtmAV.js +1 -0
- package/dist/renderer/assets/{Modal-DwY4lecS.js → Modal-CXhD_OWR.js} +1 -1
- package/dist/renderer/assets/MonitorList-D7tyipbx.js +1 -0
- package/dist/renderer/assets/{Page-CnB9ePsb.js → Page-KUuFYgjg.js} +1 -1
- package/dist/renderer/assets/ProjectDetail-Ddq94-yu.js +11 -0
- package/dist/renderer/assets/ProjectList-CbdHd5Ev.js +1 -0
- package/dist/renderer/assets/{Select-zfoksLGB.js → Select-Djc8BXMC.js} +1 -1
- package/dist/renderer/assets/SessionChat-DOy0iKQR.js +1 -0
- package/dist/renderer/assets/SessionList-DHgl1s1D.js +1 -0
- package/dist/renderer/assets/ThreadChat-I7dYibDc.js +23 -0
- package/dist/renderer/assets/Workspace-DTocqY-F.js +19 -0
- package/dist/renderer/assets/{arrow-left-CNEAuElg.js → arrow-left-CXyya3hP.js} +1 -1
- package/dist/renderer/assets/{book-open-BNJMhNI-.js → book-open-9n4Kxc7E.js} +1 -1
- package/dist/renderer/assets/bot-C9HBk5XH.js +6 -0
- package/dist/renderer/assets/{check-BnclClRU.js → check-CutSg9a5.js} +1 -1
- package/dist/renderer/assets/{chevron-down-Bh53e0O-.js → chevron-down-DrydR4i3.js} +1 -1
- package/dist/renderer/assets/{circle-ClZTWCWO.js → circle-BEjtahw6.js} +1 -1
- package/dist/renderer/assets/desktop-DATgk_ka.js +1 -0
- package/dist/renderer/assets/{desktop-C6q6wBae.js → desktop-JQkhNOF4.js} +5 -5
- package/dist/renderer/assets/en-CmAr_0e6.js +1 -0
- package/dist/renderer/assets/es-Cs9x52ca.js +1 -0
- package/dist/renderer/assets/{funnel-_5hsz0xI.js → funnel-BhqZpS4f.js} +1 -1
- package/dist/renderer/assets/{heart-u4Q1aOqj.js → heart-Dp8Vs-7_.js} +1 -1
- package/dist/renderer/assets/{index-BNFx4uYv.js → index-BKpv0P0s.js} +2 -2
- package/dist/renderer/assets/index-CO4fGqmv.css +11 -0
- package/dist/renderer/assets/{index-B36eowj-.js → index-cI3pWBnN.js} +36 -36
- package/dist/renderer/assets/ja-DtvtBRTW.js +1 -0
- package/dist/renderer/assets/{pencil-DLh9QNmT.js → pencil-ywcJzeOo.js} +1 -1
- package/dist/renderer/assets/{play-BDTk9Scm.js → play-AtN492Cx.js} +1 -1
- package/dist/renderer/assets/{plus-BSc8POsz.js → plus-BEj14BBV.js} +1 -1
- package/dist/renderer/assets/projects-Clar8lgb.js +1 -0
- package/dist/renderer/assets/save-D6ov4CNv.js +11 -0
- package/dist/renderer/assets/{search-CniPk7Ey.js → search-C3bvw7AR.js} +1 -1
- package/dist/renderer/assets/{server-GsXyQAZm.js → server-C1Uyedyx.js} +1 -1
- package/dist/renderer/assets/{session-utils-BDxzgTBC.js → session-utils-DpRwJ6Nh.js} +1 -1
- package/dist/renderer/assets/{sessions-CLcKA1Tt.js → sessions-BWmAppwK.js} +1 -1
- package/dist/renderer/assets/{shield-check-4i0E3hmf.js → shield-check-CVx1mEs2.js} +1 -1
- package/dist/renderer/assets/{trash-2-DhtOCsO5.js → trash-2-o-heXOgs.js} +1 -1
- package/dist/renderer/assets/user-dPOzUqIc.js +6 -0
- package/dist/renderer/assets/{wifi-off-BHPzPY_B.js → wifi-off-Fz5c3_x_.js} +1 -1
- package/dist/renderer/assets/zh-CcJ9vBZj.js +1 -0
- package/dist/renderer/assets/zh-TW-elI72AC1.js +1 -0
- package/dist/renderer/index.html +2 -2
- package/dist-electron/electron/main.js +26 -1
- package/dist-electron/packages/core-sdk/src/index.js +28 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +111 -62
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-session-coordinator.js +80 -7
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-transport.js +78 -5
- package/dist-electron/services/local-ai-core/src/acp/store/external-store.js +98 -0
- package/dist-electron/services/local-ai-core/src/acp/store/local-core-acp-store.js +33 -0
- package/dist-electron/services/local-ai-core/src/acp/store/model-provider-store.js +88 -0
- package/dist-electron/services/local-ai-core/src/acp/store/schema.js +43 -0
- package/dist-electron/services/local-ai-core/src/agents/pi/launch.js +2 -119
- package/dist-electron/services/local-ai-core/src/agents/pi/provider-materializer.js +148 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/inbound.js +55 -1
- package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +65 -10
- package/dist-electron/services/local-ai-core/src/channel/shared/session-command-runtime.js +4 -2
- package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +139 -29
- package/dist-electron/services/local-ai-core/src/execution/agent-execution-backend.js +56 -0
- package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +18 -57
- package/dist-electron/services/local-ai-core/src/kernel/local-core-errors.js +255 -0
- package/dist-electron/services/local-ai-core/src/plugins/builtin/catalog.js +78 -0
- package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +3 -1
- package/dist-electron/services/local-ai-core/src/router/workspace-router.js +64 -9
- package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +5 -2
- package/dist-electron/services/local-ai-core/src/runtime/config-migration.js +152 -0
- package/dist-electron/services/local-ai-core/src/runtime/deployment-diagnostics.js +175 -0
- package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +330 -3
- package/dist-electron/services/local-ai-core/src/runtime/local-core-runtime-state.js +11 -2
- package/dist-electron/services/local-ai-core/src/runtime/provider-config-migration.js +32 -0
- package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-service.js +76 -8
- package/dist-electron/services/local-ai-core/src/runtime/server-routes.js +62 -0
- package/dist-electron/services/local-ai-core/src/runtime/server.js +708 -4
- package/dist-electron/services/local-ai-core/src/runtime/standalone.js +13 -3
- package/dist-electron/services/local-ai-core/src/sandbox/opensandbox-client.js +113 -0
- package/dist-electron/services/local-ai-core/src/sandbox/sandbox-config.js +273 -0
- package/dist-electron/services/local-ai-core/src/sandbox/sandbox-manager.js +215 -0
- package/dist-electron/services/local-ai-core/src/sandbox/sandbox-stdio-proxy.js +188 -0
- package/dist-electron/services/local-ai-core/src/thread/slash-command-registry.js +3 -0
- package/dist-electron/services/local-ai-core/src/thread/thread-command-service.js +32 -0
- package/dist-electron/services/local-ai-core/src/thread/thread-slash-command-dispatcher.js +71 -0
- package/dist-electron/shared/desktop.js +85 -1
- package/dist-electron/tests/contracts/architecture-docs.test.js +83 -0
- package/dist-electron/tests/electron/plugin-kernel.test.js +225 -18
- package/dist-electron/tests/electron/runtime-detection-service.test.js +57 -5
- package/dist-electron/tests/electron/sandbox-config.test.js +541 -0
- package/dist-electron/tests/electron/workspace-task-store.test.js +257 -0
- package/dist-electron/tests/integration/lark-gateway.test.js +139 -0
- package/dist-electron/tests/integration/local-core-refactor.test.js +183 -20
- package/dist-electron/tests/integration/local-core-routes.test.js +34 -0
- package/package.json +2 -3
- package/dist/renderer/assets/Chat-BK-fm0Xr.js +0 -2
- package/dist/renderer/assets/Config-B93CQGvY.js +0 -16
- package/dist/renderer/assets/CronList-Z3TvSRKP.js +0 -1
- package/dist/renderer/assets/Dashboard-Ch3QkWwf.js +0 -26
- package/dist/renderer/assets/KnowledgeHome-ChvjOfrc.js +0 -11
- package/dist/renderer/assets/Logs-Cv0nth3j.js +0 -1
- package/dist/renderer/assets/MonitorList-JEvMoWUm.js +0 -1
- package/dist/renderer/assets/ProjectDetail-DJa3sMmU.js +0 -11
- package/dist/renderer/assets/ProjectList-D6CXumR_.js +0 -1
- package/dist/renderer/assets/SessionChat-DUTh4WoM.js +0 -1
- package/dist/renderer/assets/SessionList-D9o-lUst.js +0 -1
- package/dist/renderer/assets/ThreadChat-BYXlT-Kc.js +0 -23
- package/dist/renderer/assets/Workspace-BrhSwRQ-.js +0 -14
- package/dist/renderer/assets/desktop-C5vBT0yM.js +0 -1
- package/dist/renderer/assets/en-D2SkCrhG.js +0 -1
- package/dist/renderer/assets/es-C8UBPhxM.js +0 -1
- package/dist/renderer/assets/index-CaQZSRbg.css +0 -11
- package/dist/renderer/assets/ja-ChF4vbBL.js +0 -1
- package/dist/renderer/assets/plug-BG0JHW6B.js +0 -6
- package/dist/renderer/assets/projects-DwfZwxDW.js +0 -1
- package/dist/renderer/assets/save-D5AO_G6a.js +0 -6
- package/dist/renderer/assets/user-BtC_pVXV.js +0 -11
- package/dist/renderer/assets/zh-DIcdHycM.js +0 -1
- package/dist/renderer/assets/zh-TW-BtRwreY_.js +0 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.LocalCoreAcpSessionCoordinator = void 0;
|
|
4
|
+
exports.buildAgentPath = buildAgentPath;
|
|
4
5
|
const node_path_1 = require("node:path");
|
|
6
|
+
const node_os_1 = require("node:os");
|
|
5
7
|
const scheduled_job_route_js_1 = require("../scheduler/scheduled-job-route.js");
|
|
6
8
|
class LocalCoreAcpSessionCoordinator {
|
|
7
9
|
options;
|
|
@@ -14,6 +16,7 @@ class LocalCoreAcpSessionCoordinator {
|
|
|
14
16
|
}
|
|
15
17
|
closeAll() {
|
|
16
18
|
for (const session of this.sessions.values()) {
|
|
19
|
+
this.clearIdleClose(session);
|
|
17
20
|
this.options.transport.closeSession(session);
|
|
18
21
|
}
|
|
19
22
|
this.sessions.clear();
|
|
@@ -23,9 +26,21 @@ class LocalCoreAcpSessionCoordinator {
|
|
|
23
26
|
if (!session) {
|
|
24
27
|
return;
|
|
25
28
|
}
|
|
29
|
+
this.clearIdleClose(session);
|
|
26
30
|
this.options.transport.closeSession(session);
|
|
27
31
|
this.sessions.delete(threadId);
|
|
28
32
|
}
|
|
33
|
+
releaseThreadSession(threadId, config) {
|
|
34
|
+
if (!config.sandbox?.enabled || config.sandbox.lifecycle === 'per_run' || config.sandbox.stateScope === 'run') {
|
|
35
|
+
this.closeThreadSession(threadId);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const session = this.sessions.get(threadId);
|
|
39
|
+
if (!session || session.closed) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
this.scheduleIdleClose(session, config.sandbox.idleSeconds);
|
|
43
|
+
}
|
|
29
44
|
async ensureSession(threadId, bridgeSessionKey, config, options = {}) {
|
|
30
45
|
const existing = this.sessions.get(threadId);
|
|
31
46
|
const permissionMode = this.resolveLaunchPermissionMode(threadId, options.permissionMode);
|
|
@@ -37,11 +52,15 @@ class LocalCoreAcpSessionCoordinator {
|
|
|
37
52
|
&& existing.launchPermissionMode === permissionMode
|
|
38
53
|
&& existing.launchConfigKey === configKey
|
|
39
54
|
&& existing.launchRuntimeEnvKey === runtimeEnvKey) {
|
|
55
|
+
this.clearIdleClose(existing);
|
|
56
|
+
this.options.log?.(`[acp.session:${threadId}] reuse existing session mode=${config.sandbox?.enabled ? 'sandbox' : 'local'}`);
|
|
40
57
|
return existing;
|
|
41
58
|
}
|
|
42
59
|
if (existing) {
|
|
43
60
|
this.closeThreadSession(threadId);
|
|
44
61
|
}
|
|
62
|
+
const startedAt = Date.now();
|
|
63
|
+
this.options.log?.(`[acp.session:${threadId}] spawn mode=${config.sandbox?.enabled ? 'sandbox' : 'local'} transport=${config.execution?.transport || 'stdio'}`);
|
|
45
64
|
const baseEnv = {
|
|
46
65
|
...process.env,
|
|
47
66
|
...config.env,
|
|
@@ -50,20 +69,21 @@ class LocalCoreAcpSessionCoordinator {
|
|
|
50
69
|
threadId,
|
|
51
70
|
bridgeSessionKey,
|
|
52
71
|
config,
|
|
53
|
-
runtimeEnv: this.buildAgentRuntimeEnv(threadId, String(baseEnv.PATH || ''), options.runtimeEnv),
|
|
72
|
+
runtimeEnv: this.buildAgentRuntimeEnv(threadId, String(baseEnv.PATH || ''), options.runtimeEnv, options.runId),
|
|
54
73
|
});
|
|
55
74
|
session.launchPermissionMode = permissionMode;
|
|
56
75
|
session.launchConfigKey = configKey;
|
|
57
76
|
session.launchRuntimeEnvKey = runtimeEnvKey;
|
|
58
77
|
this.sessions.set(threadId, session);
|
|
59
78
|
await this.options.transport.initializeSession(session);
|
|
79
|
+
this.options.log?.(`[acp.session:${threadId}] initialize done in ${Date.now() - startedAt}ms`);
|
|
60
80
|
const row = this.options.store.getThreadRow(threadId);
|
|
61
81
|
if (row?.acp_session_id && row.acp_supports_load && session.supportsLoad) {
|
|
62
82
|
try {
|
|
63
83
|
session.loadReplayMode = true;
|
|
64
84
|
await this.options.transport.request(session, 'session/load', {
|
|
65
85
|
sessionId: row.acp_session_id,
|
|
66
|
-
cwd: config
|
|
86
|
+
cwd: acpSessionCwd(config),
|
|
67
87
|
mcpServers: [],
|
|
68
88
|
}, 30000);
|
|
69
89
|
session.sessionId = row.acp_session_id;
|
|
@@ -78,7 +98,7 @@ class LocalCoreAcpSessionCoordinator {
|
|
|
78
98
|
if (!session.sessionId) {
|
|
79
99
|
try {
|
|
80
100
|
const created = await this.options.transport.request(session, 'session/new', {
|
|
81
|
-
cwd: config
|
|
101
|
+
cwd: acpSessionCwd(config),
|
|
82
102
|
mcpServers: [],
|
|
83
103
|
_meta: this.buildSessionMeta(threadId, permissionMode),
|
|
84
104
|
}, 30000);
|
|
@@ -87,12 +107,14 @@ class LocalCoreAcpSessionCoordinator {
|
|
|
87
107
|
throw new Error('ACP session/new did not return a sessionId');
|
|
88
108
|
}
|
|
89
109
|
this.options.store.updateThreadSession(threadId, session.sessionId, session.supportsLoad);
|
|
110
|
+
this.options.log?.(`[acp.session:${threadId}] session/new done in ${Date.now() - startedAt}ms`);
|
|
90
111
|
}
|
|
91
112
|
catch (error) {
|
|
92
113
|
this.closeThreadSession(threadId);
|
|
93
114
|
throw error;
|
|
94
115
|
}
|
|
95
116
|
}
|
|
117
|
+
this.options.log?.(`[acp.session:${threadId}] ready in ${Date.now() - startedAt}ms`);
|
|
96
118
|
return session;
|
|
97
119
|
}
|
|
98
120
|
async setThreadMode(threadId, mode) {
|
|
@@ -152,6 +174,7 @@ class LocalCoreAcpSessionCoordinator {
|
|
|
152
174
|
return;
|
|
153
175
|
}
|
|
154
176
|
const activeRunId = session.currentRunId;
|
|
177
|
+
this.clearIdleClose(session);
|
|
155
178
|
this.options.transport.closeSessionWithError(session, error);
|
|
156
179
|
if (activeRunId) {
|
|
157
180
|
this.options.store.updateRun(activeRunId, session.threadId, 'failed');
|
|
@@ -166,13 +189,31 @@ class LocalCoreAcpSessionCoordinator {
|
|
|
166
189
|
this.sessions.delete(session.threadId);
|
|
167
190
|
}
|
|
168
191
|
}
|
|
192
|
+
scheduleIdleClose(session, idleSeconds) {
|
|
193
|
+
this.clearIdleClose(session);
|
|
194
|
+
const seconds = Number.isFinite(idleSeconds) && idleSeconds > 0 ? idleSeconds : 900;
|
|
195
|
+
this.options.log?.(`[acp.session:${session.threadId}] keep sandbox session warm for ${seconds}s`);
|
|
196
|
+
session.idleCloseTimer = setTimeout(() => {
|
|
197
|
+
if (this.sessions.get(session.threadId) === session && !session.closed && !session.currentRunId) {
|
|
198
|
+
this.options.log?.(`[acp.session:${session.threadId}] idle timeout reached; closing sandbox session`);
|
|
199
|
+
this.closeThreadSession(session.threadId);
|
|
200
|
+
}
|
|
201
|
+
}, seconds * 1000);
|
|
202
|
+
session.idleCloseTimer.unref?.();
|
|
203
|
+
}
|
|
204
|
+
clearIdleClose(session) {
|
|
205
|
+
if (session.idleCloseTimer) {
|
|
206
|
+
clearTimeout(session.idleCloseTimer);
|
|
207
|
+
session.idleCloseTimer = undefined;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
169
210
|
buildRuntimeEnvKey(runtimeEnv = {}) {
|
|
170
211
|
return Object.keys(runtimeEnv)
|
|
171
212
|
.sort()
|
|
172
213
|
.map((key) => `${key}=${runtimeEnv[key]}`)
|
|
173
214
|
.join('\n');
|
|
174
215
|
}
|
|
175
|
-
buildAgentRuntimeEnv(threadId, existingPath, runtimeEnv = {}) {
|
|
216
|
+
buildAgentRuntimeEnv(threadId, existingPath, runtimeEnv = {}, runId = '') {
|
|
176
217
|
const row = this.options.store.getThreadRow(threadId);
|
|
177
218
|
if (!row) {
|
|
178
219
|
return {};
|
|
@@ -182,6 +223,7 @@ class LocalCoreAcpSessionCoordinator {
|
|
|
182
223
|
LOCAL_AI_CORE_BASE: this.options.localCoreBase || 'http://127.0.0.1:9831/api/local/v1',
|
|
183
224
|
LOCAL_AI_WORKSPACE_ID: row.workspace_id,
|
|
184
225
|
LOCAL_AI_THREAD_ID: threadId,
|
|
226
|
+
...(runId ? { AGENTDOCK_SANDBOX_RUN_ID: runId } : {}),
|
|
185
227
|
};
|
|
186
228
|
const workspace = this.options.store.getWorkspaceRegistryEntry(row.workspace_id);
|
|
187
229
|
if (workspace?.path) {
|
|
@@ -198,9 +240,10 @@ class LocalCoreAcpSessionCoordinator {
|
|
|
198
240
|
env.LOCAL_AI_PLATFORM_USER_ID = binding.platform_user_id;
|
|
199
241
|
}
|
|
200
242
|
if (this.options.cliBinDir) {
|
|
201
|
-
env.PATH = existingPath
|
|
202
|
-
|
|
203
|
-
|
|
243
|
+
env.PATH = buildAgentPath(existingPath, this.options.cliBinDir);
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
env.PATH = buildAgentPath(existingPath);
|
|
204
247
|
}
|
|
205
248
|
return {
|
|
206
249
|
...env,
|
|
@@ -215,6 +258,8 @@ class LocalCoreAcpSessionCoordinator {
|
|
|
215
258
|
args: config.args || [],
|
|
216
259
|
env: config.env || {},
|
|
217
260
|
model: config.model || '',
|
|
261
|
+
execution: config.execution || null,
|
|
262
|
+
sandbox: config.sandbox || null,
|
|
218
263
|
});
|
|
219
264
|
}
|
|
220
265
|
resolveLaunchPermissionMode(threadId, permissionModeOverride = '') {
|
|
@@ -241,3 +286,31 @@ class LocalCoreAcpSessionCoordinator {
|
|
|
241
286
|
}
|
|
242
287
|
}
|
|
243
288
|
exports.LocalCoreAcpSessionCoordinator = LocalCoreAcpSessionCoordinator;
|
|
289
|
+
function acpSessionCwd(config) {
|
|
290
|
+
return config.sandbox?.enabled ? config.sandbox.workspaceMountPath : config.workDir;
|
|
291
|
+
}
|
|
292
|
+
function buildAgentPath(existingPath, cliBinDir) {
|
|
293
|
+
const entries = [
|
|
294
|
+
cliBinDir,
|
|
295
|
+
...userBinDirs(),
|
|
296
|
+
...String(existingPath || '').split(node_path_1.delimiter),
|
|
297
|
+
];
|
|
298
|
+
const seen = new Set();
|
|
299
|
+
return entries
|
|
300
|
+
.map((entry) => String(entry || '').trim())
|
|
301
|
+
.filter((entry) => {
|
|
302
|
+
if (!entry || seen.has(entry)) {
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
seen.add(entry);
|
|
306
|
+
return true;
|
|
307
|
+
})
|
|
308
|
+
.join(node_path_1.delimiter);
|
|
309
|
+
}
|
|
310
|
+
function userBinDirs() {
|
|
311
|
+
if (process.platform === 'win32') {
|
|
312
|
+
return [];
|
|
313
|
+
}
|
|
314
|
+
const home = process.env.HOME || (0, node_os_1.homedir)();
|
|
315
|
+
return home ? [`${home}/.local/bin`, `${home}/bin`] : [];
|
|
316
|
+
}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.LocalCoreAcpTransport = void 0;
|
|
4
4
|
const node_child_process_1 = require("node:child_process");
|
|
5
|
+
const node_fs_1 = require("node:fs");
|
|
6
|
+
const local_core_errors_js_1 = require("../kernel/local-core-errors.js");
|
|
5
7
|
class LocalCoreAcpTransport {
|
|
6
8
|
options;
|
|
7
9
|
constructor(options) {
|
|
@@ -12,8 +14,9 @@ class LocalCoreAcpTransport {
|
|
|
12
14
|
...process.env,
|
|
13
15
|
...input.config.env,
|
|
14
16
|
};
|
|
17
|
+
const cwd = resolveTransportCwd(input.config);
|
|
15
18
|
const child = (0, node_child_process_1.spawn)(input.config.command, input.config.args, {
|
|
16
|
-
cwd
|
|
19
|
+
cwd,
|
|
17
20
|
env: {
|
|
18
21
|
...baseEnv,
|
|
19
22
|
...input.runtimeEnv,
|
|
@@ -51,7 +54,37 @@ class LocalCoreAcpTransport {
|
|
|
51
54
|
this.handlePipeFailure(session, error);
|
|
52
55
|
});
|
|
53
56
|
child.on('exit', (code, signal) => {
|
|
54
|
-
|
|
57
|
+
if (session.closed) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
this.options.onSessionClosed(session, new local_core_errors_js_1.LocalCoreError('runtime_exited', `ACP agent exited with code ${code ?? 'unknown'}${signal ? ` (${signal})` : ''}`, {
|
|
61
|
+
details: {
|
|
62
|
+
code: code ?? 'unknown',
|
|
63
|
+
signal: signal || '',
|
|
64
|
+
threadId: input.threadId,
|
|
65
|
+
runtimeId: input.config.agentType,
|
|
66
|
+
},
|
|
67
|
+
}));
|
|
68
|
+
});
|
|
69
|
+
child.on('error', (error) => {
|
|
70
|
+
this.options.onSessionClosed(session, error.code === 'ENOENT'
|
|
71
|
+
? new local_core_errors_js_1.LocalCoreError('runtime_not_found', `ACP agent command not found: ${input.config.command}`, {
|
|
72
|
+
details: {
|
|
73
|
+
command: input.config.command,
|
|
74
|
+
cwd,
|
|
75
|
+
threadId: input.threadId,
|
|
76
|
+
runtimeId: input.config.agentType,
|
|
77
|
+
},
|
|
78
|
+
})
|
|
79
|
+
: new local_core_errors_js_1.LocalCoreError('runtime_start_failed', `ACP agent failed to start: ${error.message}`, {
|
|
80
|
+
cause: error.code,
|
|
81
|
+
details: {
|
|
82
|
+
command: input.config.command,
|
|
83
|
+
cwd,
|
|
84
|
+
threadId: input.threadId,
|
|
85
|
+
runtimeId: input.config.agentType,
|
|
86
|
+
},
|
|
87
|
+
}));
|
|
55
88
|
});
|
|
56
89
|
return session;
|
|
57
90
|
}
|
|
@@ -79,7 +112,14 @@ class LocalCoreAcpTransport {
|
|
|
79
112
|
return new Promise((resolve, reject) => {
|
|
80
113
|
const timeout = setTimeout(() => {
|
|
81
114
|
session.pending.delete(id);
|
|
82
|
-
reject(new
|
|
115
|
+
reject(new local_core_errors_js_1.LocalCoreError('runtime_protocol_timeout', `Timed out waiting for ACP ${method} after ${timeoutMs}ms`, {
|
|
116
|
+
details: {
|
|
117
|
+
method,
|
|
118
|
+
timeoutMs,
|
|
119
|
+
threadId: session.threadId,
|
|
120
|
+
runtimeId: session.currentTurn?.agentType || '',
|
|
121
|
+
},
|
|
122
|
+
}));
|
|
83
123
|
}, timeoutMs);
|
|
84
124
|
session.pending.set(id, {
|
|
85
125
|
resolve: (value) => {
|
|
@@ -175,7 +215,15 @@ class LocalCoreAcpTransport {
|
|
|
175
215
|
}
|
|
176
216
|
session.pending.delete(payload.id);
|
|
177
217
|
if (payload.error) {
|
|
178
|
-
pending.reject(new
|
|
218
|
+
pending.reject(new local_core_errors_js_1.LocalCoreError(errorCodeForAcpError(payload.error), payload.error.message || `ACP request failed: ${payload.id}`, {
|
|
219
|
+
details: {
|
|
220
|
+
payloadId: payload.id,
|
|
221
|
+
errorCode: payload.error.code,
|
|
222
|
+
errorData: payload.error.data,
|
|
223
|
+
threadId: session.threadId,
|
|
224
|
+
runtimeId: session.currentTurn?.agentType || '',
|
|
225
|
+
},
|
|
226
|
+
}));
|
|
179
227
|
}
|
|
180
228
|
else {
|
|
181
229
|
pending.resolve(payload.result);
|
|
@@ -184,9 +232,34 @@ class LocalCoreAcpTransport {
|
|
|
184
232
|
}
|
|
185
233
|
}
|
|
186
234
|
handlePipeFailure(session, error) {
|
|
235
|
+
if (session.closed) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
187
238
|
const message = error instanceof Error ? error.message : String(error);
|
|
188
239
|
this.options.log?.(`[localcore-acp:${session.threadId}] stdin failure: ${message}`);
|
|
189
|
-
this.options.onSessionClosed(session, new
|
|
240
|
+
this.options.onSessionClosed(session, new local_core_errors_js_1.LocalCoreError('runtime_protocol_error', message, {
|
|
241
|
+
details: {
|
|
242
|
+
threadId: session.threadId,
|
|
243
|
+
runtimeId: session.currentTurn?.agentType || '',
|
|
244
|
+
},
|
|
245
|
+
}));
|
|
190
246
|
}
|
|
191
247
|
}
|
|
192
248
|
exports.LocalCoreAcpTransport = LocalCoreAcpTransport;
|
|
249
|
+
function resolveTransportCwd(config) {
|
|
250
|
+
if (config.sandbox?.enabled) {
|
|
251
|
+
const proxyCwd = String(config.sandbox.proxyCwd || '').trim();
|
|
252
|
+
if (proxyCwd && (0, node_fs_1.existsSync)(proxyCwd)) {
|
|
253
|
+
return proxyCwd;
|
|
254
|
+
}
|
|
255
|
+
return process.cwd();
|
|
256
|
+
}
|
|
257
|
+
return config.workDir;
|
|
258
|
+
}
|
|
259
|
+
function errorCodeForAcpError(error) {
|
|
260
|
+
const message = String(error?.message || '').toLowerCase();
|
|
261
|
+
if (message.includes('authentication required') || message.includes('api key') || message.includes('oauth')) {
|
|
262
|
+
return 'provider_auth_failed';
|
|
263
|
+
}
|
|
264
|
+
return 'runtime_protocol_error';
|
|
265
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LocalExternalStore = void 0;
|
|
4
|
+
const utils_js_1 = require("./utils.js");
|
|
5
|
+
class LocalExternalStore {
|
|
6
|
+
db;
|
|
7
|
+
constructor(db) {
|
|
8
|
+
this.db = db;
|
|
9
|
+
}
|
|
10
|
+
getProject(userId, externalProjectId) {
|
|
11
|
+
const row = this.db.prepare(`
|
|
12
|
+
SELECT user_id, external_project_id, workspace_id, workspace_path, display_name, agent_type, provider_id, metadata_json, created_at, updated_at
|
|
13
|
+
FROM external_projects
|
|
14
|
+
WHERE user_id = ? AND external_project_id = ?
|
|
15
|
+
`).get(userId, externalProjectId);
|
|
16
|
+
return row ? this.toProject(row) : undefined;
|
|
17
|
+
}
|
|
18
|
+
upsertProject(input) {
|
|
19
|
+
const now = new Date().toISOString();
|
|
20
|
+
const existing = this.getProject(input.userId, input.externalProjectId);
|
|
21
|
+
this.db.prepare(`
|
|
22
|
+
INSERT INTO external_projects (
|
|
23
|
+
user_id, external_project_id, workspace_id, workspace_path, display_name, agent_type, provider_id,
|
|
24
|
+
metadata_json, created_at, updated_at
|
|
25
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
26
|
+
ON CONFLICT(user_id, external_project_id) DO UPDATE SET
|
|
27
|
+
workspace_id = excluded.workspace_id,
|
|
28
|
+
workspace_path = excluded.workspace_path,
|
|
29
|
+
display_name = excluded.display_name,
|
|
30
|
+
agent_type = excluded.agent_type,
|
|
31
|
+
provider_id = excluded.provider_id,
|
|
32
|
+
metadata_json = excluded.metadata_json,
|
|
33
|
+
updated_at = excluded.updated_at
|
|
34
|
+
`).run(input.userId, input.externalProjectId, input.workspaceId, input.workspacePath, input.displayName, input.agentType, input.providerId, JSON.stringify(input.metadata || existing?.metadata || {}), existing?.createdAt || input.createdAt || now, now);
|
|
35
|
+
return this.getProject(input.userId, input.externalProjectId);
|
|
36
|
+
}
|
|
37
|
+
getThread(userId, externalProjectId, externalThreadId) {
|
|
38
|
+
const row = this.db.prepare(`
|
|
39
|
+
SELECT user_id, external_project_id, external_thread_id, workspace_id, thread_id, workspace_path, metadata_json, created_at, updated_at
|
|
40
|
+
FROM external_threads
|
|
41
|
+
WHERE user_id = ? AND external_project_id = ? AND external_thread_id = ?
|
|
42
|
+
`).get(userId, externalProjectId, externalThreadId);
|
|
43
|
+
return row ? this.toThread(row) : undefined;
|
|
44
|
+
}
|
|
45
|
+
getThreadByThreadId(threadId) {
|
|
46
|
+
const row = this.db.prepare(`
|
|
47
|
+
SELECT user_id, external_project_id, external_thread_id, workspace_id, thread_id, workspace_path, metadata_json, created_at, updated_at
|
|
48
|
+
FROM external_threads
|
|
49
|
+
WHERE thread_id = ?
|
|
50
|
+
`).get(threadId);
|
|
51
|
+
return row ? this.toThread(row) : undefined;
|
|
52
|
+
}
|
|
53
|
+
upsertThread(input) {
|
|
54
|
+
const now = new Date().toISOString();
|
|
55
|
+
const existing = this.getThread(input.userId, input.externalProjectId, input.externalThreadId);
|
|
56
|
+
this.db.prepare(`
|
|
57
|
+
INSERT INTO external_threads (
|
|
58
|
+
user_id, external_project_id, external_thread_id, workspace_id, thread_id, workspace_path,
|
|
59
|
+
metadata_json, created_at, updated_at
|
|
60
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
61
|
+
ON CONFLICT(user_id, external_project_id, external_thread_id) DO UPDATE SET
|
|
62
|
+
workspace_id = excluded.workspace_id,
|
|
63
|
+
thread_id = excluded.thread_id,
|
|
64
|
+
workspace_path = excluded.workspace_path,
|
|
65
|
+
metadata_json = excluded.metadata_json,
|
|
66
|
+
updated_at = excluded.updated_at
|
|
67
|
+
`).run(input.userId, input.externalProjectId, input.externalThreadId, input.workspaceId, input.threadId, input.workspacePath, JSON.stringify(input.metadata || existing?.metadata || {}), existing?.createdAt || input.createdAt || now, now);
|
|
68
|
+
return this.getThread(input.userId, input.externalProjectId, input.externalThreadId);
|
|
69
|
+
}
|
|
70
|
+
toProject(row) {
|
|
71
|
+
return {
|
|
72
|
+
userId: row.user_id,
|
|
73
|
+
externalProjectId: row.external_project_id,
|
|
74
|
+
workspaceId: row.workspace_id,
|
|
75
|
+
workspacePath: row.workspace_path,
|
|
76
|
+
displayName: row.display_name,
|
|
77
|
+
agentType: row.agent_type,
|
|
78
|
+
providerId: row.provider_id,
|
|
79
|
+
createdAt: row.created_at,
|
|
80
|
+
updatedAt: row.updated_at,
|
|
81
|
+
metadata: (0, utils_js_1.parseJson)(row.metadata_json, {}),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
toThread(row) {
|
|
85
|
+
return {
|
|
86
|
+
userId: row.user_id,
|
|
87
|
+
externalProjectId: row.external_project_id,
|
|
88
|
+
externalThreadId: row.external_thread_id,
|
|
89
|
+
workspaceId: row.workspace_id,
|
|
90
|
+
threadId: row.thread_id,
|
|
91
|
+
workspacePath: row.workspace_path,
|
|
92
|
+
createdAt: row.created_at,
|
|
93
|
+
updatedAt: row.updated_at,
|
|
94
|
+
metadata: (0, utils_js_1.parseJson)(row.metadata_json, {}),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
exports.LocalExternalStore = LocalExternalStore;
|
|
@@ -13,6 +13,8 @@ const scheduler_store_js_1 = require("./scheduler-store.js");
|
|
|
13
13
|
const security_store_js_1 = require("./security-store.js");
|
|
14
14
|
const thread_store_js_1 = require("./thread-store.js");
|
|
15
15
|
const workspace_registry_store_js_1 = require("./workspace-registry-store.js");
|
|
16
|
+
const model_provider_store_js_1 = require("./model-provider-store.js");
|
|
17
|
+
const external_store_js_1 = require("./external-store.js");
|
|
16
18
|
class LocalCoreAcpStore {
|
|
17
19
|
db;
|
|
18
20
|
threads;
|
|
@@ -22,6 +24,8 @@ class LocalCoreAcpStore {
|
|
|
22
24
|
scheduler;
|
|
23
25
|
automationMonitors;
|
|
24
26
|
platform;
|
|
27
|
+
modelProviders;
|
|
28
|
+
external;
|
|
25
29
|
constructor(userDataPath) {
|
|
26
30
|
const dbPath = (0, node_path_1.join)(userDataPath, 'runtime', 'local-core.db');
|
|
27
31
|
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(dbPath), { recursive: true });
|
|
@@ -37,6 +41,8 @@ class LocalCoreAcpStore {
|
|
|
37
41
|
this.scheduler = new scheduler_store_js_1.LocalSchedulerStore(this.db);
|
|
38
42
|
this.automationMonitors = new automation_monitor_store_js_1.LocalAutomationMonitorStore(this.db);
|
|
39
43
|
this.platform = new platform_store_js_1.LocalPlatformStore(this.db);
|
|
44
|
+
this.modelProviders = new model_provider_store_js_1.LocalModelProviderStore(this.db);
|
|
45
|
+
this.external = new external_store_js_1.LocalExternalStore(this.db);
|
|
40
46
|
(0, schema_js_1.ensureLocalCoreAcpSchema)(this.db);
|
|
41
47
|
}
|
|
42
48
|
close() {
|
|
@@ -95,6 +101,33 @@ class LocalCoreAcpStore {
|
|
|
95
101
|
touchWorkspaceRegistryEntry(workspaceId) {
|
|
96
102
|
this.workspaceRegistry.touch(workspaceId);
|
|
97
103
|
}
|
|
104
|
+
listModelProviders() {
|
|
105
|
+
return this.modelProviders.list();
|
|
106
|
+
}
|
|
107
|
+
getModelProvider(providerId) {
|
|
108
|
+
return this.modelProviders.get(providerId);
|
|
109
|
+
}
|
|
110
|
+
upsertModelProvider(input) {
|
|
111
|
+
return this.modelProviders.upsert(input);
|
|
112
|
+
}
|
|
113
|
+
deleteModelProvider(providerId) {
|
|
114
|
+
return this.modelProviders.delete(providerId);
|
|
115
|
+
}
|
|
116
|
+
getExternalProject(userId, externalProjectId) {
|
|
117
|
+
return this.external.getProject(userId, externalProjectId);
|
|
118
|
+
}
|
|
119
|
+
upsertExternalProject(input) {
|
|
120
|
+
return this.external.upsertProject(input);
|
|
121
|
+
}
|
|
122
|
+
getExternalThread(userId, externalProjectId, externalThreadId) {
|
|
123
|
+
return this.external.getThread(userId, externalProjectId, externalThreadId);
|
|
124
|
+
}
|
|
125
|
+
getExternalThreadByThreadId(threadId) {
|
|
126
|
+
return this.external.getThreadByThreadId(threadId);
|
|
127
|
+
}
|
|
128
|
+
upsertExternalThread(input) {
|
|
129
|
+
return this.external.upsertThread(input);
|
|
130
|
+
}
|
|
98
131
|
getWorkspaceSecuritySettings(workspaceId) {
|
|
99
132
|
return this.security.getWorkspaceSecuritySettings(workspaceId);
|
|
100
133
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LocalModelProviderStore = void 0;
|
|
4
|
+
exports.slugifyProviderId = slugifyProviderId;
|
|
5
|
+
const utils_js_1 = require("./utils.js");
|
|
6
|
+
class LocalModelProviderStore {
|
|
7
|
+
db;
|
|
8
|
+
constructor(db) {
|
|
9
|
+
this.db = db;
|
|
10
|
+
}
|
|
11
|
+
list() {
|
|
12
|
+
const rows = this.db.prepare(`
|
|
13
|
+
SELECT id, name, api_key, base_url, model, models_json, thinking, env_json, created_at, updated_at
|
|
14
|
+
FROM model_providers
|
|
15
|
+
ORDER BY name ASC, id ASC
|
|
16
|
+
`).all();
|
|
17
|
+
return rows.map((row) => this.toProvider(row));
|
|
18
|
+
}
|
|
19
|
+
get(providerId) {
|
|
20
|
+
const row = this.db.prepare(`
|
|
21
|
+
SELECT id, name, api_key, base_url, model, models_json, thinking, env_json, created_at, updated_at
|
|
22
|
+
FROM model_providers
|
|
23
|
+
WHERE id = ?
|
|
24
|
+
`).get(providerId);
|
|
25
|
+
return row ? this.toProvider(row) : undefined;
|
|
26
|
+
}
|
|
27
|
+
upsert(input) {
|
|
28
|
+
const now = new Date().toISOString();
|
|
29
|
+
const requestedId = String(input.id || '').trim();
|
|
30
|
+
const id = requestedId || this.allocateProviderId(input.name);
|
|
31
|
+
const existing = this.get(id);
|
|
32
|
+
this.db.prepare(`
|
|
33
|
+
INSERT INTO model_providers (
|
|
34
|
+
id, name, api_key, base_url, model, models_json, thinking, env_json, created_at, updated_at
|
|
35
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
36
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
37
|
+
name = excluded.name,
|
|
38
|
+
api_key = excluded.api_key,
|
|
39
|
+
base_url = excluded.base_url,
|
|
40
|
+
model = excluded.model,
|
|
41
|
+
models_json = excluded.models_json,
|
|
42
|
+
thinking = excluded.thinking,
|
|
43
|
+
env_json = excluded.env_json,
|
|
44
|
+
updated_at = excluded.updated_at
|
|
45
|
+
`).run(id, String(input.name || id).trim() || id, stringOrNull(input.api_key), stringOrNull(input.base_url), stringOrNull(input.model), JSON.stringify(Array.isArray(input.models) ? input.models : []), stringOrNull(input.thinking), JSON.stringify(input.env && typeof input.env === 'object' ? input.env : {}), existing?.createdAt || now, now);
|
|
46
|
+
return this.get(id);
|
|
47
|
+
}
|
|
48
|
+
delete(providerId) {
|
|
49
|
+
this.db.prepare('DELETE FROM model_providers WHERE id = ?').run(providerId);
|
|
50
|
+
return { deleted: true };
|
|
51
|
+
}
|
|
52
|
+
allocateProviderId(name) {
|
|
53
|
+
const base = slugifyProviderId(name, 'provider');
|
|
54
|
+
let candidate = base;
|
|
55
|
+
let index = 2;
|
|
56
|
+
while (this.get(candidate)) {
|
|
57
|
+
candidate = `${base}-${index}`;
|
|
58
|
+
index += 1;
|
|
59
|
+
}
|
|
60
|
+
return candidate;
|
|
61
|
+
}
|
|
62
|
+
toProvider(row) {
|
|
63
|
+
return {
|
|
64
|
+
id: row.id,
|
|
65
|
+
name: row.name,
|
|
66
|
+
api_key: row.api_key || undefined,
|
|
67
|
+
base_url: row.base_url || undefined,
|
|
68
|
+
model: row.model || undefined,
|
|
69
|
+
models: (0, utils_js_1.parseJson)(row.models_json, []),
|
|
70
|
+
thinking: row.thinking || undefined,
|
|
71
|
+
env: (0, utils_js_1.parseJson)(row.env_json, {}),
|
|
72
|
+
createdAt: row.created_at,
|
|
73
|
+
updatedAt: row.updated_at,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
exports.LocalModelProviderStore = LocalModelProviderStore;
|
|
78
|
+
function slugifyProviderId(value, fallback) {
|
|
79
|
+
return String(value || '')
|
|
80
|
+
.trim()
|
|
81
|
+
.toLowerCase()
|
|
82
|
+
.replace(/[^a-z0-9._-]+/g, '-')
|
|
83
|
+
.replace(/^-+|-+$/g, '') || fallback;
|
|
84
|
+
}
|
|
85
|
+
function stringOrNull(value) {
|
|
86
|
+
const normalized = String(value || '').trim();
|
|
87
|
+
return normalized || null;
|
|
88
|
+
}
|
|
@@ -176,6 +176,49 @@ function ensureLocalCoreAcpSchema(db) {
|
|
|
176
176
|
last_opened_at TEXT
|
|
177
177
|
);
|
|
178
178
|
CREATE INDEX IF NOT EXISTS idx_workspace_registry_updated ON workspace_registry (updated_at DESC);
|
|
179
|
+
CREATE TABLE IF NOT EXISTS model_providers (
|
|
180
|
+
id TEXT PRIMARY KEY,
|
|
181
|
+
name TEXT NOT NULL,
|
|
182
|
+
api_key TEXT,
|
|
183
|
+
base_url TEXT,
|
|
184
|
+
model TEXT,
|
|
185
|
+
models_json TEXT NOT NULL DEFAULT '[]',
|
|
186
|
+
thinking TEXT,
|
|
187
|
+
env_json TEXT NOT NULL DEFAULT '{}',
|
|
188
|
+
created_at TEXT NOT NULL,
|
|
189
|
+
updated_at TEXT NOT NULL
|
|
190
|
+
);
|
|
191
|
+
CREATE INDEX IF NOT EXISTS idx_model_providers_name ON model_providers (name);
|
|
192
|
+
CREATE TABLE IF NOT EXISTS external_projects (
|
|
193
|
+
user_id TEXT NOT NULL,
|
|
194
|
+
external_project_id TEXT NOT NULL,
|
|
195
|
+
workspace_id TEXT NOT NULL,
|
|
196
|
+
workspace_path TEXT NOT NULL,
|
|
197
|
+
display_name TEXT NOT NULL,
|
|
198
|
+
agent_type TEXT NOT NULL,
|
|
199
|
+
provider_id TEXT NOT NULL,
|
|
200
|
+
metadata_json TEXT NOT NULL DEFAULT '{}',
|
|
201
|
+
created_at TEXT NOT NULL,
|
|
202
|
+
updated_at TEXT NOT NULL,
|
|
203
|
+
PRIMARY KEY (user_id, external_project_id),
|
|
204
|
+
UNIQUE(workspace_id)
|
|
205
|
+
);
|
|
206
|
+
CREATE TABLE IF NOT EXISTS external_threads (
|
|
207
|
+
user_id TEXT NOT NULL,
|
|
208
|
+
external_project_id TEXT NOT NULL,
|
|
209
|
+
external_thread_id TEXT NOT NULL,
|
|
210
|
+
workspace_id TEXT NOT NULL,
|
|
211
|
+
thread_id TEXT NOT NULL,
|
|
212
|
+
workspace_path TEXT NOT NULL,
|
|
213
|
+
metadata_json TEXT NOT NULL DEFAULT '{}',
|
|
214
|
+
created_at TEXT NOT NULL,
|
|
215
|
+
updated_at TEXT NOT NULL,
|
|
216
|
+
PRIMARY KEY (user_id, external_project_id, external_thread_id),
|
|
217
|
+
UNIQUE(thread_id),
|
|
218
|
+
FOREIGN KEY (thread_id) REFERENCES threads(id) ON DELETE CASCADE
|
|
219
|
+
);
|
|
220
|
+
CREATE INDEX IF NOT EXISTS idx_external_threads_thread ON external_threads (thread_id);
|
|
221
|
+
CREATE INDEX IF NOT EXISTS idx_external_threads_workspace ON external_threads (workspace_id);
|
|
179
222
|
CREATE TABLE IF NOT EXISTS agent_tasks (
|
|
180
223
|
id TEXT PRIMARY KEY,
|
|
181
224
|
workspace_id TEXT NOT NULL,
|