@kafca/agentdock 0.1.46 → 0.1.48
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 +11 -0
- package/dist/renderer/assets/{Badge-BIesaDYG.js → Badge-C92lMj9Q.js} +1 -1
- package/dist/renderer/assets/{Chat-CpataoDh.js → Chat-DqoKCPwz.js} +1 -1
- package/dist/renderer/assets/{ChatMarkdown-_Mdmltgv.js → ChatMarkdown-C4psQvmt.js} +1 -1
- package/dist/renderer/assets/{Config-Pe5-zKZk.js → Config-1uk8FQL7.js} +2 -2
- package/dist/renderer/assets/{CronList-BnaOSPT8.js → CronList-DnKCiYWC.js} +1 -1
- package/dist/renderer/assets/{Dashboard-D05l8GKE.js → Dashboard-rOAQcJA0.js} +2 -2
- package/dist/renderer/assets/{EmptyState-BDBavDll.js → EmptyState-bF6Sc61O.js} +1 -1
- package/dist/renderer/assets/{KnowledgeDetail-DvR798l_.js → KnowledgeDetail-C5-Dp95R.js} +2 -2
- package/dist/renderer/assets/{KnowledgeHome-r2ZuvU8-.js → KnowledgeHome-DGa1EWXD.js} +2 -2
- package/dist/renderer/assets/{Logs-BaljtmAV.js → Logs-CKKVW2k8.js} +1 -1
- package/dist/renderer/assets/{Modal-CXhD_OWR.js → Modal-DIc9HxD5.js} +3 -3
- package/dist/renderer/assets/{MonitorList-D7tyipbx.js → MonitorList-D2YOJRIX.js} +1 -1
- package/dist/renderer/assets/{Page-KUuFYgjg.js → Page-vZ-_l6Kn.js} +1 -1
- package/dist/renderer/assets/ProjectDetail-CqTa6kAh.js +11 -0
- package/dist/renderer/assets/{ProjectList-CbdHd5Ev.js → ProjectList-LgDQVVyZ.js} +1 -1
- package/dist/renderer/assets/{Select-Djc8BXMC.js → Select-BN2HMOmY.js} +1 -1
- package/dist/renderer/assets/SessionChat-Beco1N1T.js +1 -0
- package/dist/renderer/assets/{SessionList-DHgl1s1D.js → SessionList-3NkskCJJ.js} +1 -1
- package/dist/renderer/assets/ThreadChat-Ji8pOm5p.js +28 -0
- package/dist/renderer/assets/{Workspace-DTocqY-F.js → Workspace-yarrGItB.js} +1 -1
- package/dist/renderer/assets/{arrow-left-CXyya3hP.js → arrow-left-DNovvfPp.js} +1 -1
- package/dist/renderer/assets/{book-open-9n4Kxc7E.js → book-open-CarZFsXv.js} +1 -1
- package/dist/renderer/assets/{bot-C9HBk5XH.js → bot-g16rGMp6.js} +1 -1
- package/dist/renderer/assets/{check-CutSg9a5.js → check-DjFX73Zi.js} +1 -1
- package/dist/renderer/assets/{chevron-down-DrydR4i3.js → chevron-down-B02YlQS_.js} +1 -1
- package/dist/renderer/assets/{circle-BEjtahw6.js → circle-DPfWdG1F.js} +1 -1
- package/dist/renderer/assets/{desktop-DATgk_ka.js → desktop-DMkue1T6.js} +1 -1
- package/dist/renderer/assets/{funnel-BhqZpS4f.js → funnel-Dy1VL-D9.js} +1 -1
- package/dist/renderer/assets/{heart-Dp8Vs-7_.js → heart-YjvGwq5-.js} +1 -1
- package/dist/renderer/assets/{index-BKpv0P0s.js → index-CZN1STMc.js} +2 -2
- package/dist/renderer/assets/index-CjCuR6DI.css +11 -0
- package/dist/renderer/assets/{index-cI3pWBnN.js → index-mvjDN9uV.js} +9 -9
- package/dist/renderer/assets/{pencil-ywcJzeOo.js → pencil-qpMjLIFd.js} +1 -1
- package/dist/renderer/assets/{play-AtN492Cx.js → play-CPCcNQcH.js} +1 -1
- package/dist/renderer/assets/{plus-BEj14BBV.js → plus-B76JWGKd.js} +1 -1
- package/dist/renderer/assets/projects-C1nFINjh.js +1 -0
- package/dist/renderer/assets/{save-D6ov4CNv.js → save-DtvrD3W1.js} +1 -1
- package/dist/renderer/assets/{search-C3bvw7AR.js → search-Ddth-YHH.js} +1 -1
- package/dist/renderer/assets/{server-C1Uyedyx.js → server-D0zmghb3.js} +1 -1
- package/dist/renderer/assets/{session-utils-DpRwJ6Nh.js → session-utils-C9oe-21F.js} +1 -1
- package/dist/renderer/assets/{sessions-BWmAppwK.js → sessions-BXOnLLah.js} +1 -1
- package/dist/renderer/assets/{shield-check-CVx1mEs2.js → shield-check-Cv5d_blH.js} +1 -1
- package/dist/renderer/assets/{trash-2-o-heXOgs.js → trash-2-C2mimX2b.js} +1 -1
- package/dist/renderer/assets/{user-dPOzUqIc.js → user-BrLJ4elW.js} +1 -1
- package/dist/renderer/assets/{wifi-off-Fz5c3_x_.js → wifi-off-DvRJYLbG.js} +1 -1
- package/dist/renderer/index.html +2 -2
- package/dist-electron/packages/core-sdk/src/index.js +4 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-session-coordinator.js +53 -7
- package/dist-electron/services/local-ai-core/src/acp/store/local-core-acp-store.js +3 -0
- package/dist-electron/services/local-ai-core/src/acp/store/platform-store.js +13 -5
- package/dist-electron/services/local-ai-core/src/acp/store/schema.js +1 -0
- package/dist-electron/services/local-ai-core/src/acp/store/thread-store.js +3 -0
- package/dist-electron/services/local-ai-core/src/agents/pi/launch.js +1 -1
- package/dist-electron/services/local-ai-core/src/agents/shared/launch-utils.js +22 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +132 -348
- package/dist-electron/services/local-ai-core/src/channel/shared/base-channel-gateway.js +306 -0
- package/dist-electron/services/local-ai-core/src/channel/shared/session-command-runtime.js +14 -3
- package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +131 -382
- package/dist-electron/services/local-ai-core/src/router/workspace-router.js +8 -1
- package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +2 -1
- package/dist-electron/services/local-ai-core/src/runtime/channel-service.js +150 -0
- package/dist-electron/services/local-ai-core/src/runtime/env-utils.js +12 -0
- package/dist-electron/services/local-ai-core/src/runtime/external-service.js +234 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/automation-handler.js +34 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/capabilities-handler.js +12 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/channel-handler.js +65 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/external-handler.js +17 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/knowledge-handler.js +65 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/openai-handler.js +496 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/provider-handler.js +24 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/runtime-handler.js +68 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/runtimes-handler.js +37 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/scheduler-handler.js +34 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/security-handler.js +49 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/task-handler.js +27 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/thread-handler.js +46 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/workspace-handler.js +29 -0
- package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +40 -592
- package/dist-electron/services/local-ai-core/src/runtime/server-helpers.js +96 -0
- package/dist-electron/services/local-ai-core/src/runtime/server-routes.js +3 -0
- package/dist-electron/services/local-ai-core/src/runtime/server.js +107 -1084
- package/dist-electron/services/local-ai-core/src/runtime/standalone.js +13 -1
- package/dist-electron/services/local-ai-core/src/thread/thread-command-service.js +23 -2
- package/dist-electron/services/local-ai-core/src/thread/thread-slash-command-dispatcher.js +1 -0
- package/dist-electron/src/pages/Projects/project-provider-config.js +75 -0
- package/dist-electron/src/pages/Threads/thread-chat-model.js +2 -0
- package/dist-electron/tests/contracts/architecture-docs.test.js +1 -0
- package/dist-electron/tests/electron/agent-runtime-detector.test.js +18 -0
- package/dist-electron/tests/electron/knowledge-skill-script.test.js +15 -5
- package/dist-electron/tests/electron/lac-cli.test.js +591 -688
- package/dist-electron/tests/electron/plugin-kernel.test.js +47 -31
- package/dist-electron/tests/electron/project-provider-config.test.js +71 -0
- package/dist-electron/tests/electron/workspace-task-store.test.js +40 -11
- package/dist-electron/tests/integration/local-core-cors.test.js +22 -0
- package/dist-electron/tests/integration/local-core-refactor.test.js +177 -0
- package/dist-electron/tests/integration/local-core-routes.test.js +45 -0
- package/package.json +2 -2
- package/dist/renderer/assets/ProjectDetail-Ddq94-yu.js +0 -11
- package/dist/renderer/assets/SessionChat-DOy0iKQR.js +0 -1
- package/dist/renderer/assets/ThreadChat-I7dYibDc.js +0 -23
- package/dist/renderer/assets/index-CO4fGqmv.css +0 -11
- package/dist/renderer/assets/projects-Clar8lgb.js +0 -1
|
@@ -437,7 +437,12 @@ OPENAI_API_KEY = "override-openai-key"
|
|
|
437
437
|
strict_1.default.equal(route?.agentType, 'pi');
|
|
438
438
|
strict_1.default.equal(route?.config.command, process.execPath);
|
|
439
439
|
strict_1.default.match(route?.config.args[0] || '', /pi-acp[/\\]dist[/\\]index\.js$/);
|
|
440
|
-
|
|
440
|
+
if (process.platform === 'win32') {
|
|
441
|
+
strict_1.default.match(route?.config.env.PI_ACP_PI_COMMAND || '', /[\\/]node_modules[\\/]\.bin[\\/]pi\.CMD$/i);
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
strict_1.default.match(route?.config.env.PI_ACP_PI_COMMAND || '', /@mariozechner[/\\]pi-coding-agent[/\\]dist[/\\]cli\.js$/);
|
|
445
|
+
}
|
|
441
446
|
strict_1.default.equal(route?.config.env.OPENAI_API_KEY, 'override-openai-key');
|
|
442
447
|
strict_1.default.match(route?.config.env.PI_CODING_AGENT_DIR || '', /[\\/]\.pi-agent[\\/]pi-workspace$/);
|
|
443
448
|
strict_1.default.deepEqual(await runtime.workspaceRouter.listWorkspaces(), [
|
|
@@ -735,14 +740,14 @@ model = "deepseek-v4-flash"
|
|
|
735
740
|
});
|
|
736
741
|
await controller.init();
|
|
737
742
|
strict_1.default.equal(started, true);
|
|
738
|
-
strict_1.default.deepEqual(await controller.
|
|
739
|
-
strict_1.default.deepEqual(await controller.
|
|
743
|
+
strict_1.default.deepEqual(await controller.kernel.getCapabilitySnapshot(), capabilitySnapshot);
|
|
744
|
+
strict_1.default.deepEqual(await controller.channelService.getQrCode('test-channel', 'workspace-1', 'instance-1'), {
|
|
740
745
|
ticket: 'ticket:workspace-1',
|
|
741
746
|
expiresIn: 60,
|
|
742
747
|
qrCodeUrl: 'https://example.test/qr',
|
|
743
748
|
instanceId: 'instance-1',
|
|
744
749
|
});
|
|
745
|
-
strict_1.default.deepEqual(await controller.
|
|
750
|
+
strict_1.default.deepEqual(await controller.channelService.checkQrCodeStatus('test-channel', 'workspace-1', 'signed-ticket'), {
|
|
746
751
|
status: 'signed',
|
|
747
752
|
});
|
|
748
753
|
await controller.saveStructuredConfigFile({ projects: [] });
|
|
@@ -936,8 +941,12 @@ model = "deepseek-v4-flash"
|
|
|
936
941
|
});
|
|
937
942
|
(0, node_test_1.default)('server QR routes dispatch through generic channel bindings', async () => {
|
|
938
943
|
const calls = [];
|
|
939
|
-
const
|
|
940
|
-
getChannelQrCode:
|
|
944
|
+
const controller = Object.assign(new node_events_1.EventEmitter(), {
|
|
945
|
+
getChannelQrCode: undefined,
|
|
946
|
+
checkChannelQrCodeStatus: undefined,
|
|
947
|
+
});
|
|
948
|
+
const channelService = {
|
|
949
|
+
getQrCode: async (platform, workspaceId, instanceId) => {
|
|
941
950
|
calls.push(`get:${platform}:${workspaceId}:${instanceId || ''}`);
|
|
942
951
|
return {
|
|
943
952
|
ticket: 'ticket-1',
|
|
@@ -946,11 +955,12 @@ model = "deepseek-v4-flash"
|
|
|
946
955
|
instanceId,
|
|
947
956
|
};
|
|
948
957
|
},
|
|
949
|
-
|
|
958
|
+
checkQrCodeStatus: async (platform, workspaceId, ticket, instanceId) => {
|
|
950
959
|
calls.push(`check:${platform}:${workspaceId}:${ticket}:${instanceId || ''}`);
|
|
951
960
|
return { status: 'signed' };
|
|
952
961
|
},
|
|
953
|
-
}
|
|
962
|
+
};
|
|
963
|
+
const bindings = { controller, channelService };
|
|
954
964
|
const server = new server_js_1.LocalAiCoreServer(bindings, { port: 0 });
|
|
955
965
|
const qrResponse = await invokeServer(server, 'POST', '/api/local/v1/platforms/slack/workspace-1/qrcode?instance_id=bot-1');
|
|
956
966
|
strict_1.default.equal(qrResponse.body.data.ticket, 'ticket-1');
|
|
@@ -962,7 +972,8 @@ model = "deepseek-v4-flash"
|
|
|
962
972
|
]);
|
|
963
973
|
});
|
|
964
974
|
(0, node_test_1.default)('server unknown routes return structured error responses', async () => {
|
|
965
|
-
const
|
|
975
|
+
const controller = new node_events_1.EventEmitter();
|
|
976
|
+
const bindings = { controller };
|
|
966
977
|
const server = new server_js_1.LocalAiCoreServer(bindings, { port: 0 });
|
|
967
978
|
const response = await invokeServer(server, 'GET', '/api/local/v1/not-found');
|
|
968
979
|
strict_1.default.equal(response.statusCode, 404);
|
|
@@ -973,24 +984,7 @@ model = "deepseek-v4-flash"
|
|
|
973
984
|
});
|
|
974
985
|
(0, node_test_1.default)('server diagnostics routes dispatch through generic bindings', async () => {
|
|
975
986
|
const calls = [];
|
|
976
|
-
const
|
|
977
|
-
listDiagnosticErrors: async () => {
|
|
978
|
-
calls.push('errors');
|
|
979
|
-
return [{
|
|
980
|
-
key: 'channel.weixin:channel_session_expired:workspace-1',
|
|
981
|
-
count: 2,
|
|
982
|
-
firstSeenAt: '2026-05-12T00:00:00.000Z',
|
|
983
|
-
lastSeenAt: '2026-05-12T00:05:00.000Z',
|
|
984
|
-
errorInfo: {
|
|
985
|
-
code: 'channel_session_expired',
|
|
986
|
-
message: 'WeChat login expired.',
|
|
987
|
-
userMessage: 'Channel login has expired.',
|
|
988
|
-
severity: 'error',
|
|
989
|
-
retryable: false,
|
|
990
|
-
suggestedAction: 'Reconnect the channel.',
|
|
991
|
-
},
|
|
992
|
-
}];
|
|
993
|
-
},
|
|
987
|
+
const controller = Object.assign(new node_events_1.EventEmitter(), {
|
|
994
988
|
runDiagnosticsDoctor: async () => {
|
|
995
989
|
calls.push('doctor');
|
|
996
990
|
return {
|
|
@@ -1012,6 +1006,26 @@ model = "deepseek-v4-flash"
|
|
|
1012
1006
|
};
|
|
1013
1007
|
},
|
|
1014
1008
|
});
|
|
1009
|
+
const errorReporter = {
|
|
1010
|
+
list: async () => {
|
|
1011
|
+
calls.push('errors');
|
|
1012
|
+
return [{
|
|
1013
|
+
key: 'channel.weixin:channel_session_expired:workspace-1',
|
|
1014
|
+
count: 2,
|
|
1015
|
+
firstSeenAt: '2026-05-12T00:00:00.000Z',
|
|
1016
|
+
lastSeenAt: '2026-05-12T00:05:00.000Z',
|
|
1017
|
+
errorInfo: {
|
|
1018
|
+
code: 'channel_session_expired',
|
|
1019
|
+
message: 'WeChat login expired.',
|
|
1020
|
+
userMessage: 'Channel login has expired.',
|
|
1021
|
+
severity: 'error',
|
|
1022
|
+
retryable: false,
|
|
1023
|
+
suggestedAction: 'Reconnect the channel.',
|
|
1024
|
+
},
|
|
1025
|
+
}];
|
|
1026
|
+
},
|
|
1027
|
+
};
|
|
1028
|
+
const bindings = { controller, errorReporter };
|
|
1015
1029
|
const server = new server_js_1.LocalAiCoreServer(bindings, { port: 0 });
|
|
1016
1030
|
const errorsResponse = await invokeServer(server, 'GET', '/api/local/v1/diagnostics/errors');
|
|
1017
1031
|
const doctorResponse = await invokeServer(server, 'POST', '/api/local/v1/diagnostics/doctor');
|
|
@@ -1023,8 +1037,9 @@ model = "deepseek-v4-flash"
|
|
|
1023
1037
|
});
|
|
1024
1038
|
(0, node_test_1.default)('server OpenAI-compatible chat route maps metadata to sandbox yolo external run', async () => {
|
|
1025
1039
|
let externalInput;
|
|
1026
|
-
const
|
|
1027
|
-
|
|
1040
|
+
const controller = new node_events_1.EventEmitter();
|
|
1041
|
+
const externalService = {
|
|
1042
|
+
createRun: async (input) => {
|
|
1028
1043
|
externalInput = input;
|
|
1029
1044
|
return {
|
|
1030
1045
|
project: {},
|
|
@@ -1036,7 +1051,7 @@ model = "deepseek-v4-flash"
|
|
|
1036
1051
|
events_url: '/api/local/v1/external/runs/run-1/events',
|
|
1037
1052
|
};
|
|
1038
1053
|
},
|
|
1039
|
-
|
|
1054
|
+
getRunSnapshot: async () => ({
|
|
1040
1055
|
runId: 'run-1',
|
|
1041
1056
|
task: {
|
|
1042
1057
|
taskId: 'task-1',
|
|
@@ -1069,7 +1084,8 @@ model = "deepseek-v4-flash"
|
|
|
1069
1084
|
}],
|
|
1070
1085
|
},
|
|
1071
1086
|
}),
|
|
1072
|
-
}
|
|
1087
|
+
};
|
|
1088
|
+
const bindings = { controller, externalService };
|
|
1073
1089
|
const server = new server_js_1.LocalAiCoreServer(bindings, { port: 0 });
|
|
1074
1090
|
const response = await invokeServer(server, 'POST', '/api/local/v1/openai/chat/completions', {
|
|
1075
1091
|
model: 'model-1',
|
|
@@ -0,0 +1,71 @@
|
|
|
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 project_provider_config_1 = require("../../src/pages/Projects/project-provider-config");
|
|
9
|
+
function createConfig() {
|
|
10
|
+
return {
|
|
11
|
+
projects: [{
|
|
12
|
+
name: 'agentdock',
|
|
13
|
+
agent: {
|
|
14
|
+
type: 'localcore-acp',
|
|
15
|
+
options: {
|
|
16
|
+
model: 'old-model',
|
|
17
|
+
provider_id: 'old-provider',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
platforms: [],
|
|
21
|
+
}],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
(0, node_test_1.default)('project provider selection updates agent provider_id without mutating the original config', () => {
|
|
25
|
+
const config = createConfig();
|
|
26
|
+
const next = (0, project_provider_config_1.selectProjectProvider)(config, 'agentdock', 'deepseek');
|
|
27
|
+
strict_1.default.equal((0, project_provider_config_1.getProjectProviderId)(next, 'agentdock'), 'deepseek');
|
|
28
|
+
strict_1.default.equal((0, project_provider_config_1.getProjectProviderId)(config, 'agentdock'), 'old-provider');
|
|
29
|
+
strict_1.default.equal(next.projects?.[0]?.agent.options?.model, 'old-model');
|
|
30
|
+
});
|
|
31
|
+
(0, node_test_1.default)('project model selection updates only the project model override', () => {
|
|
32
|
+
const config = createConfig();
|
|
33
|
+
const next = (0, project_provider_config_1.selectProjectModel)(config, 'agentdock', 'deepseek-chat');
|
|
34
|
+
strict_1.default.equal(next.projects?.[0]?.agent.options?.model, 'deepseek-chat');
|
|
35
|
+
strict_1.default.equal(next.projects?.[0]?.agent.options?.provider_id, 'old-provider');
|
|
36
|
+
strict_1.default.equal(config.projects?.[0]?.agent.options?.model, 'old-model');
|
|
37
|
+
});
|
|
38
|
+
(0, node_test_1.default)('project provider selection reports missing projects clearly', () => {
|
|
39
|
+
strict_1.default.throws(() => (0, project_provider_config_1.selectProjectProvider)(createConfig(), 'missing', 'deepseek'), /Project not found in config: missing/);
|
|
40
|
+
});
|
|
41
|
+
(0, node_test_1.default)('provider removal clears references from every project without mutating the original config', () => {
|
|
42
|
+
const config = createConfig();
|
|
43
|
+
config.projects?.push({
|
|
44
|
+
name: 'project-2',
|
|
45
|
+
agent: {
|
|
46
|
+
type: 'localcore-acp',
|
|
47
|
+
options: {
|
|
48
|
+
model: 'other-model',
|
|
49
|
+
provider_id: 'old-provider',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
platforms: [],
|
|
53
|
+
});
|
|
54
|
+
config.projects?.push({
|
|
55
|
+
name: 'project-3',
|
|
56
|
+
agent: {
|
|
57
|
+
type: 'localcore-acp',
|
|
58
|
+
options: {
|
|
59
|
+
model: 'third-model',
|
|
60
|
+
provider_id: 'keep-provider',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
platforms: [],
|
|
64
|
+
});
|
|
65
|
+
const next = (0, project_provider_config_1.removeProviderReferences)(config, 'old-provider');
|
|
66
|
+
strict_1.default.equal((0, project_provider_config_1.getProjectProviderId)(next, 'agentdock'), '');
|
|
67
|
+
strict_1.default.equal((0, project_provider_config_1.getProjectProviderId)(next, 'project-2'), '');
|
|
68
|
+
strict_1.default.equal((0, project_provider_config_1.getProjectProviderId)(next, 'project-3'), 'keep-provider');
|
|
69
|
+
strict_1.default.equal((0, project_provider_config_1.getProjectProviderId)(config, 'agentdock'), 'old-provider');
|
|
70
|
+
strict_1.default.equal((0, project_provider_config_1.getProjectProviderId)(config, 'project-2'), 'old-provider');
|
|
71
|
+
});
|
|
@@ -16,6 +16,7 @@ const scheduler_service_js_1 = require("../../services/local-ai-core/src/schedul
|
|
|
16
16
|
const lark_schedule_adapter_js_1 = require("../../services/local-ai-core/src/scheduler/lark-schedule-adapter.js");
|
|
17
17
|
const bootstrap_js_1 = require("../../services/local-ai-core/src/kernel/bootstrap.js");
|
|
18
18
|
const local_core_controller_js_1 = require("../../services/local-ai-core/src/runtime/local-core-controller.js");
|
|
19
|
+
const env_utils_js_1 = require("../../services/local-ai-core/src/runtime/env-utils.js");
|
|
19
20
|
(0, node_test_1.default)('workspace registry entries persist in LocalCoreAcpStore', () => {
|
|
20
21
|
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'workspace-registry-'));
|
|
21
22
|
try {
|
|
@@ -130,9 +131,9 @@ const local_core_controller_js_1 = require("../../services/local-ai-core/src/run
|
|
|
130
131
|
const config = await controller.readConfigFile();
|
|
131
132
|
strict_1.default.equal(config.parsed?.projects?.[0]?.agent.options?.provider_id, 'deepseek');
|
|
132
133
|
strict_1.default.equal(config.parsed?.projects?.[0]?.agent.providers, undefined);
|
|
133
|
-
const providers =
|
|
134
|
-
strict_1.default.equal(providers
|
|
135
|
-
strict_1.default.equal(providers
|
|
134
|
+
const providers = controller.store.listModelProviders();
|
|
135
|
+
strict_1.default.equal(providers[0]?.id, 'deepseek');
|
|
136
|
+
strict_1.default.equal(providers[0]?.name, 'deepseek');
|
|
136
137
|
await controller.close();
|
|
137
138
|
}
|
|
138
139
|
finally {
|
|
@@ -148,7 +149,7 @@ const local_core_controller_js_1 = require("../../services/local-ai-core/src/run
|
|
|
148
149
|
log: () => { },
|
|
149
150
|
});
|
|
150
151
|
const controller = new local_core_controller_js_1.LocalCoreController(userDataPath, runtime);
|
|
151
|
-
const provider =
|
|
152
|
+
const provider = controller.store.upsertModelProvider({
|
|
152
153
|
name: 'deepseek',
|
|
153
154
|
api_key: 'secret',
|
|
154
155
|
base_url: 'https://api.deepseek.com',
|
|
@@ -167,7 +168,7 @@ const local_core_controller_js_1 = require("../../services/local-ai-core/src/run
|
|
|
167
168
|
platforms: [],
|
|
168
169
|
}],
|
|
169
170
|
});
|
|
170
|
-
const workspaces = await controller.listWorkspaces();
|
|
171
|
+
const workspaces = await controller.workspaceRouter.listWorkspaces();
|
|
171
172
|
strict_1.default.equal(workspaces[0]?.id, 'workspace-a');
|
|
172
173
|
strict_1.default.equal(workspaces[0]?.agentType, 'pi');
|
|
173
174
|
await controller.close();
|
|
@@ -197,7 +198,7 @@ const local_core_controller_js_1 = require("../../services/local-ai-core/src/run
|
|
|
197
198
|
created_at: now,
|
|
198
199
|
updated_at: now,
|
|
199
200
|
});
|
|
200
|
-
const job = await controller.
|
|
201
|
+
const job = await controller.scheduledJobs.createJob({
|
|
201
202
|
workspaceId: 'workspace-a',
|
|
202
203
|
threadId: thread.id,
|
|
203
204
|
executionMode: 'side-thread',
|
|
@@ -240,7 +241,7 @@ const local_core_controller_js_1 = require("../../services/local-ai-core/src/run
|
|
|
240
241
|
created_at: now,
|
|
241
242
|
updated_at: now,
|
|
242
243
|
});
|
|
243
|
-
const job = await controller.
|
|
244
|
+
const job = await controller.scheduledJobs.createJob({
|
|
244
245
|
workspaceId: 'workspace-a',
|
|
245
246
|
threadId: thread.id,
|
|
246
247
|
executionMode: 'side-thread',
|
|
@@ -583,8 +584,12 @@ const local_core_controller_js_1 = require("../../services/local-ai-core/src/run
|
|
|
583
584
|
const previousHome = process.env.HOME;
|
|
584
585
|
process.env.HOME = '/home/agentdock-test';
|
|
585
586
|
try {
|
|
586
|
-
const
|
|
587
|
-
|
|
587
|
+
const existingPath = ['/usr/bin', '/bin'].join(node_path_1.delimiter);
|
|
588
|
+
const path = (0, local_core_acp_session_coordinator_js_1.buildAgentPath)(existingPath, '/opt/agentdock/bin', { pathExists: () => false });
|
|
589
|
+
const expectedEntries = process.platform === 'win32'
|
|
590
|
+
? ['/opt/agentdock/bin', '/usr/bin', '/bin']
|
|
591
|
+
: ['/opt/agentdock/bin', '/home/agentdock-test/.local/bin', '/home/agentdock-test/bin', '/usr/bin', '/bin'];
|
|
592
|
+
strict_1.default.equal(path, expectedEntries.join(node_path_1.delimiter));
|
|
588
593
|
}
|
|
589
594
|
finally {
|
|
590
595
|
if (previousHome === undefined) {
|
|
@@ -595,6 +600,26 @@ const local_core_controller_js_1 = require("../../services/local-ai-core/src/run
|
|
|
595
600
|
}
|
|
596
601
|
}
|
|
597
602
|
});
|
|
603
|
+
(0, node_test_1.default)('ACP runtime PATH adds Git Bash bin when Windows PATH only has Git cmd', () => {
|
|
604
|
+
const existingPath = [
|
|
605
|
+
'C:\\Windows\\System32',
|
|
606
|
+
'D:\\Program Files\\Git\\cmd',
|
|
607
|
+
].join(';');
|
|
608
|
+
const path = (0, local_core_acp_session_coordinator_js_1.buildAgentPath)(existingPath, undefined, {
|
|
609
|
+
platform: 'win32',
|
|
610
|
+
pathExists: (candidate) => candidate === 'D:\\Program Files\\Git\\bin\\bash.exe',
|
|
611
|
+
});
|
|
612
|
+
strict_1.default.equal(path, [
|
|
613
|
+
'D:\\Program Files\\Git\\bin',
|
|
614
|
+
'C:\\Windows\\System32',
|
|
615
|
+
'D:\\Program Files\\Git\\cmd',
|
|
616
|
+
].join(';'));
|
|
617
|
+
});
|
|
618
|
+
(0, node_test_1.default)('ACP runtime reads Windows Path env regardless of key casing', () => {
|
|
619
|
+
strict_1.default.equal((0, env_utils_js_1.getPathEnv)({ Path: 'C:\\Windows\\System32;C:\\Program Files\\nodejs' }), 'C:\\Windows\\System32;C:\\Program Files\\nodejs');
|
|
620
|
+
strict_1.default.equal((0, env_utils_js_1.getPathEnv)({ PATH: '/usr/bin:/bin' }), '/usr/bin:/bin');
|
|
621
|
+
strict_1.default.equal((0, env_utils_js_1.getPathEnv)({ Path: 'C:\\Windows\\System32', PATH: 'D:\\agentdock\\bin' }), 'D:\\agentdock\\bin');
|
|
622
|
+
});
|
|
598
623
|
(0, node_test_1.default)('ACP transport reports missing agent commands without an unhandled process error', async () => {
|
|
599
624
|
const closed = new Promise((resolve) => {
|
|
600
625
|
const transport = new local_core_acp_transport_js_1.LocalCoreAcpTransport({
|
|
@@ -625,8 +650,9 @@ const local_core_controller_js_1 = require("../../services/local-ai-core/src/run
|
|
|
625
650
|
});
|
|
626
651
|
(0, node_test_1.default)('ACP scheduled session can override permission mode without changing thread mode', async () => {
|
|
627
652
|
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'scheduler-permission-mode-'));
|
|
653
|
+
let store = null;
|
|
628
654
|
try {
|
|
629
|
-
|
|
655
|
+
store = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
|
|
630
656
|
const thread = store.createThread('workspace-a', 'Thread');
|
|
631
657
|
const capturedSessionNewParams = [];
|
|
632
658
|
let closeCount = 0;
|
|
@@ -692,13 +718,15 @@ const local_core_controller_js_1 = require("../../services/local-ai-core/src/run
|
|
|
692
718
|
});
|
|
693
719
|
}
|
|
694
720
|
finally {
|
|
721
|
+
store?.close();
|
|
695
722
|
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
696
723
|
}
|
|
697
724
|
});
|
|
698
725
|
(0, node_test_1.default)('ACP sandbox sessions use container workspace cwd', async () => {
|
|
699
726
|
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'sandbox-session-cwd-'));
|
|
727
|
+
let store = null;
|
|
700
728
|
try {
|
|
701
|
-
|
|
729
|
+
store = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
|
|
702
730
|
const thread = store.createThread('workspace-a', 'Thread');
|
|
703
731
|
let sessionNewParams;
|
|
704
732
|
const coordinator = new local_core_acp_session_coordinator_js_1.LocalCoreAcpSessionCoordinator({
|
|
@@ -769,6 +797,7 @@ const local_core_controller_js_1 = require("../../services/local-ai-core/src/run
|
|
|
769
797
|
strict_1.default.equal(sessionNewParams?.cwd, '/workspace');
|
|
770
798
|
}
|
|
771
799
|
finally {
|
|
800
|
+
store?.close();
|
|
772
801
|
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
773
802
|
}
|
|
774
803
|
});
|
|
@@ -0,0 +1,22 @@
|
|
|
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 server_helpers_js_1 = require("../../services/local-ai-core/src/runtime/server-helpers.js");
|
|
9
|
+
class HeaderRecorder {
|
|
10
|
+
headers = new Map();
|
|
11
|
+
setHeader(name, value) {
|
|
12
|
+
this.headers.set(name, value);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
(0, node_test_1.default)('local core CORS preflight allows provider update PUT requests', () => {
|
|
16
|
+
const req = { headers: { origin: 'http://127.0.0.1:5173' } };
|
|
17
|
+
const res = new HeaderRecorder();
|
|
18
|
+
(0, server_helpers_js_1.setCorsHeaders)(req, res);
|
|
19
|
+
strict_1.default.equal(res.headers.get('Access-Control-Allow-Origin'), 'http://127.0.0.1:5173');
|
|
20
|
+
strict_1.default.match(String(res.headers.get('Access-Control-Allow-Methods')), /(?:^|,)PUT(?:,|$)/);
|
|
21
|
+
strict_1.default.match(String(res.headers.get('Access-Control-Allow-Headers')), /Content-Type/);
|
|
22
|
+
});
|
|
@@ -313,11 +313,15 @@ const thread_slash_command_dispatcher_js_1 = require("../../services/local-ai-co
|
|
|
313
313
|
try {
|
|
314
314
|
const store = new local_core_acp_store_js_1.LocalCoreAcpStore(dir);
|
|
315
315
|
const thread = store.createThread('workspace-a', 'Mode test', 'claudecode');
|
|
316
|
+
strict_1.default.equal(thread.agentMode, 'default');
|
|
316
317
|
strict_1.default.equal(store.getThreadRow(thread.id)?.agent_mode, 'default');
|
|
317
318
|
const inheritedThread = store.createThread('workspace-a', 'Inherited mode test', 'claudecode', 'bypassPermissions');
|
|
319
|
+
strict_1.default.equal(inheritedThread.agentMode, 'bypassPermissions');
|
|
318
320
|
strict_1.default.equal(store.getThreadRow(inheritedThread.id)?.agent_mode, 'bypassPermissions');
|
|
319
321
|
store.updateThreadAgentMode(thread.id, 'bypassPermissions');
|
|
320
322
|
strict_1.default.equal(store.getThreadRow(thread.id)?.agent_mode, 'bypassPermissions');
|
|
323
|
+
strict_1.default.equal(store.getThread(thread.id, []).agentMode, 'bypassPermissions');
|
|
324
|
+
strict_1.default.equal(store.listThreadSummaries('workspace-a').find((item) => item.id === thread.id)?.agentMode, 'bypassPermissions');
|
|
321
325
|
store.close();
|
|
322
326
|
}
|
|
323
327
|
finally {
|
|
@@ -894,3 +898,176 @@ const thread_slash_command_dispatcher_js_1 = require("../../services/local-ai-co
|
|
|
894
898
|
sessionKey: 'session:thread-origin',
|
|
895
899
|
});
|
|
896
900
|
});
|
|
901
|
+
(0, node_test_1.default)('platform thread binding persists preferred agent across upserts and updates', () => {
|
|
902
|
+
const dir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-binding-pref-'));
|
|
903
|
+
try {
|
|
904
|
+
const store = new local_core_acp_store_js_1.LocalCoreAcpStore(dir);
|
|
905
|
+
const now = new Date().toISOString();
|
|
906
|
+
store.upsertPlatformThreadBinding({
|
|
907
|
+
workspace_id: 'workspace-a',
|
|
908
|
+
platform: 'lark',
|
|
909
|
+
chat_id: 'chat-1',
|
|
910
|
+
platform_user_id: 'user-1',
|
|
911
|
+
thread_id: 'thread-1',
|
|
912
|
+
last_platform_message_id: null,
|
|
913
|
+
created_at: now,
|
|
914
|
+
updated_at: now,
|
|
915
|
+
});
|
|
916
|
+
store.updatePlatformThreadPreferredAgent('workspace-a', 'chat-1', 'user-1', 'claudecode', 'lark');
|
|
917
|
+
let binding = store.getPlatformThreadBinding('workspace-a', 'chat-1', 'user-1', 'lark');
|
|
918
|
+
strict_1.default.equal(binding?.preferred_agent_type, 'claudecode');
|
|
919
|
+
const later = new Date().toISOString();
|
|
920
|
+
store.upsertPlatformThreadBinding({
|
|
921
|
+
workspace_id: 'workspace-a',
|
|
922
|
+
platform: 'lark',
|
|
923
|
+
chat_id: 'chat-1',
|
|
924
|
+
platform_user_id: 'user-1',
|
|
925
|
+
thread_id: 'thread-2',
|
|
926
|
+
last_platform_message_id: null,
|
|
927
|
+
created_at: later,
|
|
928
|
+
updated_at: later,
|
|
929
|
+
});
|
|
930
|
+
binding = store.getPlatformThreadBinding('workspace-a', 'chat-1', 'user-1', 'lark');
|
|
931
|
+
strict_1.default.equal(binding?.thread_id, 'thread-2');
|
|
932
|
+
strict_1.default.equal(binding?.preferred_agent_type, 'claudecode');
|
|
933
|
+
store.updatePlatformThreadPreferredAgent('workspace-a', 'chat-1', 'user-1', null, 'lark');
|
|
934
|
+
binding = store.getPlatformThreadBinding('workspace-a', 'chat-1', 'user-1', 'lark');
|
|
935
|
+
strict_1.default.equal(binding?.preferred_agent_type, null);
|
|
936
|
+
store.close();
|
|
937
|
+
}
|
|
938
|
+
finally {
|
|
939
|
+
(0, node_fs_1.rmSync)(dir, { recursive: true, force: true });
|
|
940
|
+
}
|
|
941
|
+
});
|
|
942
|
+
(0, node_test_1.default)('slash agent command in a channel writes preferred agent to the binding', async () => {
|
|
943
|
+
const dir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-channel-agent-cmd-'));
|
|
944
|
+
try {
|
|
945
|
+
const store = new local_core_acp_store_js_1.LocalCoreAcpStore(dir);
|
|
946
|
+
const thread = store.createThread('workspace-a', 'Channel thread', 'codex');
|
|
947
|
+
const now = new Date().toISOString();
|
|
948
|
+
store.upsertPlatformThreadBinding({
|
|
949
|
+
workspace_id: 'workspace-a',
|
|
950
|
+
platform: 'lark',
|
|
951
|
+
chat_id: 'chat-1',
|
|
952
|
+
platform_user_id: 'user-1',
|
|
953
|
+
thread_id: thread.id,
|
|
954
|
+
last_platform_message_id: null,
|
|
955
|
+
created_at: now,
|
|
956
|
+
updated_at: now,
|
|
957
|
+
});
|
|
958
|
+
const row = store.getThreadRow(thread.id);
|
|
959
|
+
const dispatcher = new thread_slash_command_dispatcher_js_1.ThreadSlashCommandDispatcher({
|
|
960
|
+
session: {
|
|
961
|
+
listThreads: () => [],
|
|
962
|
+
getThread: (id) => ({ id, title: row.title, messages: [] }),
|
|
963
|
+
createThread: (workspaceId, title) => ({ id: 'new-thread', title, messages: [] }),
|
|
964
|
+
renameThread: (id, title) => ({ id, title, messages: [] }),
|
|
965
|
+
deleteThread: () => ({ deleted: true }),
|
|
966
|
+
},
|
|
967
|
+
thread: {
|
|
968
|
+
getThreadRow: (id) => store.getThreadRow(id),
|
|
969
|
+
updateThreadAgentMode: (id, mode) => store.updateThreadAgentMode(id, mode),
|
|
970
|
+
updateThreadAgentType: (id, agentType) => store.updateThreadAgentType(id, agentType),
|
|
971
|
+
getLatestRunForThread: () => undefined,
|
|
972
|
+
createAuditEvent: () => { },
|
|
973
|
+
getAgentTypes: () => ['codex', 'pi', 'claudecode'],
|
|
974
|
+
setChannelPreferredAgent: (input) => store.updatePlatformThreadPreferredAgent(input.workspaceId, input.chatId, input.platformUserId, input.agentType, input.platform),
|
|
975
|
+
},
|
|
976
|
+
});
|
|
977
|
+
const useResult = await dispatcher.execute({
|
|
978
|
+
threadId: thread.id,
|
|
979
|
+
workspaceId: 'workspace-a',
|
|
980
|
+
content: '/agent use claudecode',
|
|
981
|
+
defaultAgentType: 'codex',
|
|
982
|
+
channel: { chatId: 'chat-1', platformUserId: 'user-1', platform: 'lark' },
|
|
983
|
+
});
|
|
984
|
+
strict_1.default.equal(useResult.handled, true);
|
|
985
|
+
strict_1.default.equal(store.getThreadRow(thread.id)?.agent_type, 'claudecode');
|
|
986
|
+
strict_1.default.equal(store.getPlatformThreadBinding('workspace-a', 'chat-1', 'user-1', 'lark')?.preferred_agent_type, 'claudecode');
|
|
987
|
+
const resetResult = await dispatcher.execute({
|
|
988
|
+
threadId: thread.id,
|
|
989
|
+
workspaceId: 'workspace-a',
|
|
990
|
+
content: '/agent reset',
|
|
991
|
+
defaultAgentType: 'codex',
|
|
992
|
+
channel: { chatId: 'chat-1', platformUserId: 'user-1', platform: 'lark' },
|
|
993
|
+
});
|
|
994
|
+
strict_1.default.equal(resetResult.handled, true);
|
|
995
|
+
strict_1.default.equal(store.getThreadRow(thread.id)?.agent_type, 'codex');
|
|
996
|
+
strict_1.default.equal(store.getPlatformThreadBinding('workspace-a', 'chat-1', 'user-1', 'lark')?.preferred_agent_type, null);
|
|
997
|
+
store.close();
|
|
998
|
+
}
|
|
999
|
+
finally {
|
|
1000
|
+
(0, node_fs_1.rmSync)(dir, { recursive: true, force: true });
|
|
1001
|
+
}
|
|
1002
|
+
});
|
|
1003
|
+
(0, node_test_1.default)('channel session runtime applies preferred agent when creating a new thread', async () => {
|
|
1004
|
+
const dir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-channel-new-inherit-'));
|
|
1005
|
+
try {
|
|
1006
|
+
const store = new local_core_acp_store_js_1.LocalCoreAcpStore(dir);
|
|
1007
|
+
const original = store.createThread('workspace-a', 'Original channel thread', 'codex');
|
|
1008
|
+
const now = new Date().toISOString();
|
|
1009
|
+
store.upsertPlatformThreadBinding({
|
|
1010
|
+
workspace_id: 'workspace-a',
|
|
1011
|
+
platform: 'lark',
|
|
1012
|
+
chat_id: 'chat-1',
|
|
1013
|
+
platform_user_id: 'user-1',
|
|
1014
|
+
thread_id: original.id,
|
|
1015
|
+
last_platform_message_id: null,
|
|
1016
|
+
created_at: now,
|
|
1017
|
+
updated_at: now,
|
|
1018
|
+
});
|
|
1019
|
+
store.updatePlatformThreadPreferredAgent('workspace-a', 'chat-1', 'user-1', 'claudecode', 'lark');
|
|
1020
|
+
store.updateThreadAgentType(original.id, 'claudecode');
|
|
1021
|
+
const createdThreadIds = [];
|
|
1022
|
+
const routes = new Map();
|
|
1023
|
+
const dispatcher = new thread_slash_command_dispatcher_js_1.ThreadSlashCommandDispatcher({
|
|
1024
|
+
session: {
|
|
1025
|
+
listThreads: () => [],
|
|
1026
|
+
getThread: (id) => ({ id, title: 'X', messages: [] }),
|
|
1027
|
+
createThread: (_workspaceId, title) => {
|
|
1028
|
+
const created = store.createThread('workspace-a', title, 'codex');
|
|
1029
|
+
createdThreadIds.push({ id: created.id, initialAgentType: 'codex' });
|
|
1030
|
+
return created;
|
|
1031
|
+
},
|
|
1032
|
+
renameThread: (id, title) => ({ id, title, messages: [] }),
|
|
1033
|
+
deleteThread: () => ({ deleted: true }),
|
|
1034
|
+
},
|
|
1035
|
+
thread: {
|
|
1036
|
+
getThreadRow: (id) => store.getThreadRow(id),
|
|
1037
|
+
updateThreadAgentMode: (id, mode) => store.updateThreadAgentMode(id, mode),
|
|
1038
|
+
updateThreadAgentType: (id, agentType) => store.updateThreadAgentType(id, agentType),
|
|
1039
|
+
getLatestRunForThread: () => undefined,
|
|
1040
|
+
createAuditEvent: () => { },
|
|
1041
|
+
getAgentTypes: () => ['codex', 'claudecode'],
|
|
1042
|
+
},
|
|
1043
|
+
});
|
|
1044
|
+
const runtime = new session_command_runtime_js_1.ChannelSessionCommandRuntime({
|
|
1045
|
+
dispatcher,
|
|
1046
|
+
store,
|
|
1047
|
+
getThreadSessionKey: (id) => `session:${id}`,
|
|
1048
|
+
setThreadRoute: (sessionKey, route) => { routes.set(sessionKey, route); },
|
|
1049
|
+
createRoute: (input, id) => ({ workspaceId: input.workspaceId, chatId: input.chatId, threadId: id }),
|
|
1050
|
+
sendResult: async () => { },
|
|
1051
|
+
});
|
|
1052
|
+
const result = await runtime.execute({
|
|
1053
|
+
workspaceId: 'workspace-a',
|
|
1054
|
+
currentThreadId: original.id,
|
|
1055
|
+
text: '/new Follow up',
|
|
1056
|
+
defaultTitle: 'Follow up',
|
|
1057
|
+
defaultAgentType: 'codex',
|
|
1058
|
+
chatId: 'chat-1',
|
|
1059
|
+
platformUserId: 'user-1',
|
|
1060
|
+
platformKey: 'lark',
|
|
1061
|
+
instanceId: 'default',
|
|
1062
|
+
});
|
|
1063
|
+
strict_1.default.equal(result.handled, true);
|
|
1064
|
+
strict_1.default.equal(result.threadId, createdThreadIds[0]?.id);
|
|
1065
|
+
strict_1.default.equal(createdThreadIds[0]?.initialAgentType, 'codex', 'createThread starts with default agent before inheritance');
|
|
1066
|
+
strict_1.default.equal(store.getThreadRow(result.threadId)?.agent_type, 'claudecode', 'new thread should inherit preferred agent after creation');
|
|
1067
|
+
strict_1.default.equal(store.getPlatformThreadBinding('workspace-a', 'chat-1', 'user-1', 'lark')?.preferred_agent_type, 'claudecode');
|
|
1068
|
+
store.close();
|
|
1069
|
+
}
|
|
1070
|
+
finally {
|
|
1071
|
+
(0, node_fs_1.rmSync)(dir, { recursive: true, force: true });
|
|
1072
|
+
}
|
|
1073
|
+
});
|
|
@@ -5,7 +5,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const node_test_1 = __importDefault(require("node:test"));
|
|
7
7
|
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
8
|
+
const node_stream_1 = require("node:stream");
|
|
8
9
|
const server_routes_js_1 = require("../../services/local-ai-core/src/runtime/server-routes.js");
|
|
10
|
+
const thread_handler_js_1 = require("../../services/local-ai-core/src/runtime/handlers/thread-handler.js");
|
|
9
11
|
(0, node_test_1.default)('local core route parser separates runtime refresh and runtime detail routes', () => {
|
|
10
12
|
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/logs'), { name: 'logs.list' });
|
|
11
13
|
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/runtimes'), { name: 'runtimes.list' });
|
|
@@ -45,6 +47,10 @@ const server_routes_js_1 = require("../../services/local-ai-core/src/runtime/ser
|
|
|
45
47
|
name: 'thread.update-knowledge-bases',
|
|
46
48
|
threadId: 'thread-1',
|
|
47
49
|
});
|
|
50
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('PATCH', '/api/local/v1/threads/thread-1/mode'), {
|
|
51
|
+
name: 'thread.update-mode',
|
|
52
|
+
threadId: 'thread-1',
|
|
53
|
+
});
|
|
48
54
|
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/threads/thread-1/messages'), {
|
|
49
55
|
name: 'thread.messages.send',
|
|
50
56
|
threadId: 'thread-1',
|
|
@@ -55,6 +61,45 @@ const server_routes_js_1 = require("../../services/local-ai-core/src/runtime/ser
|
|
|
55
61
|
});
|
|
56
62
|
strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/threads/thread-1/messages'), null);
|
|
57
63
|
});
|
|
64
|
+
(0, node_test_1.default)('thread mode route handler persists normalized mode through workspace router', async () => {
|
|
65
|
+
const calls = [];
|
|
66
|
+
const map = new Map();
|
|
67
|
+
(0, thread_handler_js_1.registerThreadHandlers)(map, {
|
|
68
|
+
setThreadMode: async (threadId, mode) => {
|
|
69
|
+
calls.push({ threadId, mode });
|
|
70
|
+
return {
|
|
71
|
+
id: threadId,
|
|
72
|
+
workspaceId: 'workspace-a',
|
|
73
|
+
title: 'Thread',
|
|
74
|
+
live: false,
|
|
75
|
+
updatedAt: '2026-06-14T00:00:00.000Z',
|
|
76
|
+
createdAt: '2026-06-14T00:00:00.000Z',
|
|
77
|
+
historyCount: 0,
|
|
78
|
+
excerpt: '',
|
|
79
|
+
agentMode: 'bypassPermissions',
|
|
80
|
+
messages: [],
|
|
81
|
+
selectedKnowledgeBaseIds: [],
|
|
82
|
+
};
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
const req = node_stream_1.Readable.from([Buffer.from(JSON.stringify({ mode: 'bypassPermissions' }))]);
|
|
86
|
+
const headers = {};
|
|
87
|
+
let responseBody = '';
|
|
88
|
+
const res = {
|
|
89
|
+
statusCode: 0,
|
|
90
|
+
setHeader: (name, value) => {
|
|
91
|
+
headers[name] = value;
|
|
92
|
+
},
|
|
93
|
+
end: (body) => {
|
|
94
|
+
responseBody = body;
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
await map.get('thread.update-mode')({ name: 'thread.update-mode', threadId: 'thread-1' }, req, res, new URL('http://127.0.0.1/api/local/v1/threads/thread-1/mode'));
|
|
98
|
+
strict_1.default.deepEqual(calls, [{ threadId: 'thread-1', mode: 'bypassPermissions' }]);
|
|
99
|
+
strict_1.default.equal(res.statusCode, 200);
|
|
100
|
+
strict_1.default.equal(headers['Content-Type'], 'application/json; charset=utf-8');
|
|
101
|
+
strict_1.default.equal(JSON.parse(responseBody).data.agentMode, 'bypassPermissions');
|
|
102
|
+
});
|
|
58
103
|
(0, node_test_1.default)('local core route parser only accepts run interrupt action with POST', () => {
|
|
59
104
|
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/runs/run-1/interrupt'), {
|
|
60
105
|
name: 'run.interrupt',
|