@kafca/agentdock 0.1.30 → 0.1.33
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/LICENSE.md +73 -0
- package/README.md +15 -0
- package/dist/renderer/assets/{Badge-DlVJI1Ys.js → Badge-VeSLCPPV.js} +1 -1
- package/dist/renderer/assets/{Chat-oa8OjaHQ.js → Chat-DWTQeTgn.js} +1 -1
- package/dist/renderer/assets/{ChatMarkdown-C-N8KS5C.js → ChatMarkdown-C9gSQIVb.js} +1 -1
- package/dist/renderer/assets/{Config-D9sL_d4D.js → Config-CTA-pZNR.js} +1 -1
- package/dist/renderer/assets/{CronList-Z5JU3DHk.js → CronList-BngBCnFY.js} +1 -1
- package/dist/renderer/assets/{Dashboard-D3BwbG43.js → Dashboard-D4pGvDpc.js} +2 -2
- package/dist/renderer/assets/{EmptyState-QOj765Ul.js → EmptyState-DMf8rzDs.js} +1 -1
- package/dist/renderer/assets/{KnowledgeDetail-J7n01mPu.js → KnowledgeDetail-C1Ym_cHk.js} +1 -1
- package/dist/renderer/assets/{KnowledgeHome-BFnH0NVk.js → KnowledgeHome-DPRiR72G.js} +1 -1
- package/dist/renderer/assets/{Logs-DfSUifXc.js → Logs-CRHecmzO.js} +1 -1
- package/dist/renderer/assets/{Modal-QjEGhyru.js → Modal-BDhhPv2l.js} +1 -1
- package/dist/renderer/assets/{Page-qdHFSw7x.js → Page-DdDZN7AE.js} +1 -1
- package/dist/renderer/assets/{ProjectDetail-DS_rfvhh.js → ProjectDetail-DCsr3LkW.js} +1 -1
- package/dist/renderer/assets/{ProjectList-COtyZd7H.js → ProjectList-Cnm-iLvg.js} +1 -1
- package/dist/renderer/assets/{Select-BQ8mncVu.js → Select-Dw4FCXhJ.js} +1 -1
- package/dist/renderer/assets/{SessionChat-DjVnjuQy.js → SessionChat-O-6abK8x.js} +1 -1
- package/dist/renderer/assets/{SessionList-88dgGZJ8.js → SessionList-5yptvVL9.js} +1 -1
- package/dist/renderer/assets/ThreadChat-CIM_o2HO.js +23 -0
- package/dist/renderer/assets/{Workspace-D_hzSJjX.js → Workspace-D9UeUCNr.js} +1 -1
- package/dist/renderer/assets/{arrow-left-BMpknulW.js → arrow-left-CKfxNOXE.js} +1 -1
- package/dist/renderer/assets/{book-open-CiaOeQbU.js → book-open-CQddlENV.js} +1 -1
- package/dist/renderer/assets/{check-CgwYH4GF.js → check-CZYvOmIS.js} +1 -1
- package/dist/renderer/assets/{chevron-down-BzD2S1OO.js → chevron-down-CVIWFoXF.js} +1 -1
- package/dist/renderer/assets/{circle-BDHD7g7l.js → circle-DSfdQM7k.js} +1 -1
- package/dist/renderer/assets/{desktop-BiR5LPL5.js → desktop-B9KUgeOV.js} +1 -1
- package/dist/renderer/assets/{desktop-BkMW-h5p.js → desktop-O8_6ZCVZ.js} +2 -2
- package/dist/renderer/assets/{funnel-Bruf5FYA.js → funnel-pB5NegZB.js} +1 -1
- package/dist/renderer/assets/{heart-D7Mz04B0.js → heart-BT5laZ_b.js} +1 -1
- package/dist/renderer/assets/{index-CL1e8m3U.js → index-BYyDk28K.js} +2 -2
- package/dist/renderer/assets/{index-gZIRr4_o.js → index-zTUUYax3.js} +1 -1
- package/dist/renderer/assets/{pencil-C6gzDMse.js → pencil-C0thxLi6.js} +1 -1
- package/dist/renderer/assets/{play-DqTXX-Pr.js → play-CmZ0kWtt.js} +1 -1
- package/dist/renderer/assets/{plug-ykmbeaO3.js → plug-CeDQatBe.js} +1 -1
- package/dist/renderer/assets/{plus-DHE60vMX.js → plus-Di__xFOZ.js} +1 -1
- package/dist/renderer/assets/projects-B-pbhhtH.js +1 -0
- package/dist/renderer/assets/{save-CGIvrdQI.js → save-BDatCjfO.js} +1 -1
- package/dist/renderer/assets/{search-D7IFAwyE.js → search-DnTOjP0C.js} +1 -1
- package/dist/renderer/assets/{server-aOsirp7I.js → server-Ey_iHY6m.js} +1 -1
- package/dist/renderer/assets/{session-utils-Dxfp4ohQ.js → session-utils-DWaM7Rkr.js} +1 -1
- package/dist/renderer/assets/{sessions-Db1XPbG2.js → sessions-CYYjLD5I.js} +1 -1
- package/dist/renderer/assets/{shield-check-ChXeQK-d.js → shield-check-Br8BQzcn.js} +1 -1
- package/dist/renderer/assets/{trash-2-CfN-c1zw.js → trash-2-7YD616fw.js} +1 -1
- package/dist/renderer/assets/{user-C7OHaBp4.js → user-BR_5jmr9.js} +1 -1
- package/dist/renderer/assets/{wifi-off-CpaJigye.js → wifi-off-By9QdREz.js} +1 -1
- package/dist/renderer/index.html +1 -1
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +28 -4
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-response-processor.js +2 -4
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +102 -15
- package/dist-electron/services/local-ai-core/src/agents/claudecode/behavior.js +5 -0
- package/dist-electron/services/local-ai-core/src/agents/claudecode/definition.js +36 -0
- package/dist-electron/services/local-ai-core/src/agents/claudecode/index.js +15 -0
- package/dist-electron/services/local-ai-core/src/agents/codex/behavior.js +5 -0
- package/dist-electron/services/local-ai-core/src/agents/codex/definition.js +24 -0
- package/dist-electron/services/local-ai-core/src/agents/codex/index.js +15 -0
- package/dist-electron/services/local-ai-core/src/agents/hermes/behavior.js +29 -0
- package/dist-electron/services/local-ai-core/src/agents/hermes/definition.js +22 -0
- package/dist-electron/services/local-ai-core/src/agents/hermes/index.js +15 -0
- package/dist-electron/services/local-ai-core/src/agents/index.js +98 -0
- package/dist-electron/services/local-ai-core/src/agents/localcore-acp/behavior.js +5 -0
- package/dist-electron/services/local-ai-core/src/agents/localcore-acp/definition.js +14 -0
- package/dist-electron/services/local-ai-core/src/agents/localcore-acp/index.js +27 -0
- package/dist-electron/services/local-ai-core/src/agents/opencode/behavior.js +5 -0
- package/dist-electron/services/local-ai-core/src/agents/opencode/definition.js +18 -0
- package/dist-electron/services/local-ai-core/src/agents/opencode/index.js +15 -0
- package/dist-electron/services/local-ai-core/src/agents/opencode/launch.js +119 -0
- package/dist-electron/services/local-ai-core/src/agents/pi/behavior.js +5 -0
- package/dist-electron/services/local-ai-core/src/agents/pi/definition.js +23 -0
- package/dist-electron/services/local-ai-core/src/agents/pi/index.js +15 -0
- package/dist-electron/services/local-ai-core/src/agents/pi/launch.js +139 -0
- package/dist-electron/services/local-ai-core/src/agents/shared/acp-behavior.js +11 -0
- package/dist-electron/services/local-ai-core/src/agents/shared/agent-plugin.js +60 -0
- package/dist-electron/services/local-ai-core/src/agents/shared/definition.js +12 -0
- package/dist-electron/services/local-ai-core/src/agents/shared/launch-utils.js +122 -0
- package/dist-electron/services/local-ai-core/src/agents/shared/progress-replay.js +19 -0
- package/dist-electron/services/local-ai-core/src/agents/shared/replay-prefix.js +68 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +0 -2
- package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +0 -1
- package/dist-electron/services/local-ai-core/src/cli/lac.js +2 -1
- package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +17 -11
- package/dist-electron/services/local-ai-core/src/plugins/builtin/agent-localcore-acp-plugin.js +9 -130
- package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +22 -397
- package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +9 -45
- package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +9 -4
- package/dist-electron/services/local-ai-core/src/runtime/local-core-runtime-state.js +62 -1
- package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-service.js +2 -12
- package/dist-electron/services/local-ai-core/src/scheduler/lark-schedule-adapter.js +16 -7
- package/dist-electron/services/local-ai-core/src/scheduler/local-schedule-adapter.js +10 -2
- package/dist-electron/services/local-ai-core/src/scheduler/scheduled-job-route.js +24 -0
- package/dist-electron/services/local-ai-core/src/scheduler/weixin-schedule-adapter.js +15 -6
- package/dist-electron/shared/desktop.js +3 -1
- package/dist-electron/src/pages/Threads/thread-chat-model.js +27 -18
- package/dist-electron/src/pages/Threads/thread-chat-permission.test.js +57 -18
- package/dist-electron/{electron → tests/contracts}/local-core-contracts.test.js +1 -1
- package/dist-electron/{electron → tests/electron}/agent-runtime-detector.test.js +31 -1
- package/dist-electron/{electron → tests/electron}/lac-cli.test.js +6 -3
- package/dist-electron/{electron → tests/electron}/plugin-kernel.test.js +93 -13
- package/dist-electron/{electron → tests/electron}/renderer-packaging.test.js +11 -6
- package/dist-electron/{electron → tests/electron}/runtime-detection-service.test.js +1 -1
- package/dist-electron/{electron → tests/electron}/security-approval-store.test.js +2 -2
- package/dist-electron/{electron → tests/electron}/workspace-task-store.test.js +95 -7
- package/dist-electron/{electron → tests/integration}/channel-content.test.js +2 -2
- package/dist-electron/{electron → tests/integration}/local-core-acp-content.test.js +1 -1
- package/dist-electron/{electron → tests/integration}/local-core-refactor.test.js +437 -15
- package/package.json +3 -2
- package/dist/renderer/assets/ThreadChat-BCdbTxBo.js +0 -23
- package/dist/renderer/assets/projects-Bx5p10th.js +0 -1
- /package/dist-electron/{electron → tests/contracts}/architecture-docs.test.js +0 -0
- /package/dist-electron/{electron → tests/electron}/knowledge-skill-script.test.js +0 -0
|
@@ -9,14 +9,15 @@ const node_events_1 = require("node:events");
|
|
|
9
9
|
const node_fs_1 = require("node:fs");
|
|
10
10
|
const node_os_1 = require("node:os");
|
|
11
11
|
const node_path_1 = require("node:path");
|
|
12
|
-
const bootstrap_js_1 = require("
|
|
13
|
-
const bootstrap_js_2 = require("
|
|
14
|
-
const capability_registry_js_1 = require("
|
|
15
|
-
const event_bus_js_1 = require("
|
|
16
|
-
const lifecycle_manager_js_1 = require("
|
|
17
|
-
const plugin_registry_js_1 = require("
|
|
18
|
-
const local_core_controller_js_1 = require("
|
|
19
|
-
const
|
|
12
|
+
const bootstrap_js_1 = require("../../services/local-ai-core/src/kernel/bootstrap.js");
|
|
13
|
+
const bootstrap_js_2 = require("../../services/local-ai-core/src/kernel/bootstrap.js");
|
|
14
|
+
const capability_registry_js_1 = require("../../services/local-ai-core/src/kernel/capability-registry.js");
|
|
15
|
+
const event_bus_js_1 = require("../../services/local-ai-core/src/kernel/event-bus.js");
|
|
16
|
+
const lifecycle_manager_js_1 = require("../../services/local-ai-core/src/kernel/lifecycle-manager.js");
|
|
17
|
+
const plugin_registry_js_1 = require("../../services/local-ai-core/src/kernel/plugin-registry.js");
|
|
18
|
+
const local_core_controller_js_1 = require("../../services/local-ai-core/src/runtime/local-core-controller.js");
|
|
19
|
+
const local_core_runtime_state_js_1 = require("../../services/local-ai-core/src/runtime/local-core-runtime-state.js");
|
|
20
|
+
const server_js_1 = require("../../services/local-ai-core/src/runtime/server.js");
|
|
20
21
|
function plugin(id, dependsOn = []) {
|
|
21
22
|
return {
|
|
22
23
|
manifest: {
|
|
@@ -45,10 +46,10 @@ function plugin(id, dependsOn = []) {
|
|
|
45
46
|
},
|
|
46
47
|
snapshot: {
|
|
47
48
|
agents: [
|
|
48
|
-
{ id: 'agent.cursor', agentType: 'cursor', displayName: '
|
|
49
|
-
{ id: 'agent.gemini', agentType: 'gemini', displayName: '
|
|
50
|
-
{ id: 'agent.qoder', agentType: 'qoder', displayName: '
|
|
51
|
-
{ id: 'agent.iflow', agentType: 'iflow', displayName: '
|
|
49
|
+
{ id: 'agent.cursor', agentType: 'cursor', displayName: 'Cursor' },
|
|
50
|
+
{ id: 'agent.gemini', agentType: 'gemini', displayName: 'Gemini' },
|
|
51
|
+
{ id: 'agent.qoder', agentType: 'qoder', displayName: 'Qoder' },
|
|
52
|
+
{ id: 'agent.iflow', agentType: 'iflow', displayName: 'iFlow' },
|
|
52
53
|
{ id: 'agent.localcore-acp', agentType: 'localcore-acp', displayName: 'LocalCore ACP' },
|
|
53
54
|
],
|
|
54
55
|
channels: [
|
|
@@ -159,6 +160,7 @@ function plugin(id, dependsOn = []) {
|
|
|
159
160
|
'opencode',
|
|
160
161
|
'codex',
|
|
161
162
|
'claudecode',
|
|
163
|
+
'hermes',
|
|
162
164
|
]);
|
|
163
165
|
strict_1.default.deepEqual(runtime.kernel.getCapabilitySnapshot().adapters.channels, ['localcore-acp', 'lark', 'weixin']);
|
|
164
166
|
strict_1.default.deepEqual(runtime.kernel.getCapabilitySnapshot().adapters.knowledgeProviders, ['ai-vector']);
|
|
@@ -193,6 +195,7 @@ function plugin(id, dependsOn = []) {
|
|
|
193
195
|
'opencode',
|
|
194
196
|
'codex',
|
|
195
197
|
'claudecode',
|
|
198
|
+
'hermes',
|
|
196
199
|
]);
|
|
197
200
|
strict_1.default.deepEqual(runtime.kernel.getCapabilitySnapshot().adapters.channels, ['localcore-acp', 'lark', 'weixin']);
|
|
198
201
|
strict_1.default.deepEqual(runtime.kernel.getCapabilitySnapshot().adapters.knowledgeProviders, []);
|
|
@@ -245,6 +248,44 @@ type = "codex"
|
|
|
245
248
|
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
246
249
|
}
|
|
247
250
|
});
|
|
251
|
+
(0, node_test_1.default)('hermes agent runtime routes projects through hermes ACP command', async () => {
|
|
252
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-kernel-'));
|
|
253
|
+
try {
|
|
254
|
+
const runtime = (0, bootstrap_js_2.bootstrapLocalCoreRuntime)({
|
|
255
|
+
userDataPath,
|
|
256
|
+
enableKnowledge: false,
|
|
257
|
+
});
|
|
258
|
+
await runtime.state.saveRawConfigFile(`
|
|
259
|
+
[[projects]]
|
|
260
|
+
name = "hermes-workspace"
|
|
261
|
+
|
|
262
|
+
[projects.agent]
|
|
263
|
+
type = "hermes"
|
|
264
|
+
`);
|
|
265
|
+
const configState = await runtime.state.readConfigFile();
|
|
266
|
+
const project = configState.parsed?.projects?.find((entry) => entry.name === 'hermes-workspace');
|
|
267
|
+
const hermesRuntime = runtime.agentRuntimes.find((entry) => entry.agentType === 'hermes');
|
|
268
|
+
const route = project ? hermesRuntime?.createRoute(configState, project) : null;
|
|
269
|
+
strict_1.default.equal(route?.agentType, 'hermes');
|
|
270
|
+
strict_1.default.equal(route?.config.command, 'hermes');
|
|
271
|
+
strict_1.default.deepEqual(route?.config.args, ['acp']);
|
|
272
|
+
strict_1.default.equal(route?.config.env?.HERMES_YOLO_MODE, '1');
|
|
273
|
+
strict_1.default.deepEqual(await runtime.workspaceRouter.listWorkspaces(), [
|
|
274
|
+
{
|
|
275
|
+
id: 'hermes-workspace',
|
|
276
|
+
name: 'hermes-workspace',
|
|
277
|
+
agentType: 'hermes',
|
|
278
|
+
platforms: [],
|
|
279
|
+
sessionsCount: 0,
|
|
280
|
+
heartbeatEnabled: false,
|
|
281
|
+
},
|
|
282
|
+
]);
|
|
283
|
+
await runtime.stop();
|
|
284
|
+
}
|
|
285
|
+
finally {
|
|
286
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
287
|
+
}
|
|
288
|
+
});
|
|
248
289
|
(0, node_test_1.default)('pi agent runtime routes projects through bundled pi ACP and coding agent', async () => {
|
|
249
290
|
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-kernel-'));
|
|
250
291
|
try {
|
|
@@ -565,6 +606,45 @@ model = "deepseek-v4-flash"
|
|
|
565
606
|
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
566
607
|
}
|
|
567
608
|
});
|
|
609
|
+
(0, node_test_1.default)('runtime getLogs reads from disk after state is recreated', () => {
|
|
610
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-disk-logs-'));
|
|
611
|
+
try {
|
|
612
|
+
const state = (0, local_core_runtime_state_js_1.createLocalCoreRuntimeState)({ userDataPath });
|
|
613
|
+
state.pushLog('first persisted line');
|
|
614
|
+
state.pushLog('second persisted line');
|
|
615
|
+
const recreated = (0, local_core_runtime_state_js_1.createLocalCoreRuntimeState)({ userDataPath });
|
|
616
|
+
strict_1.default.deepEqual(recreated.getLogs(2).map((line) => line.replace(/^\S+\s+/, '')), [
|
|
617
|
+
'first persisted line',
|
|
618
|
+
'second persisted line',
|
|
619
|
+
]);
|
|
620
|
+
}
|
|
621
|
+
finally {
|
|
622
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
(0, node_test_1.default)('runtime getLogs fills from rotated log when current log has fewer lines', () => {
|
|
626
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-rotated-logs-'));
|
|
627
|
+
try {
|
|
628
|
+
const state = (0, local_core_runtime_state_js_1.createLocalCoreRuntimeState)({ userDataPath });
|
|
629
|
+
(0, node_fs_1.writeFileSync)(`${state.logPath}.1`, 'old-1\nold-2\nold-3\n', 'utf8');
|
|
630
|
+
(0, node_fs_1.writeFileSync)(state.logPath, 'new-1\n', 'utf8');
|
|
631
|
+
strict_1.default.deepEqual(state.getLogs(3), ['old-2', 'old-3', 'new-1']);
|
|
632
|
+
}
|
|
633
|
+
finally {
|
|
634
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
(0, node_test_1.default)('runtime getLogs returns only the requested tail lines from disk', () => {
|
|
638
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-tail-logs-'));
|
|
639
|
+
try {
|
|
640
|
+
const state = (0, local_core_runtime_state_js_1.createLocalCoreRuntimeState)({ userDataPath });
|
|
641
|
+
(0, node_fs_1.writeFileSync)(state.logPath, Array.from({ length: 1000 }, (_, index) => `line-${index + 1}`).join('\n') + '\n', 'utf8');
|
|
642
|
+
strict_1.default.deepEqual(state.getLogs(4), ['line-997', 'line-998', 'line-999', 'line-1000']);
|
|
643
|
+
}
|
|
644
|
+
finally {
|
|
645
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
646
|
+
}
|
|
647
|
+
});
|
|
568
648
|
(0, node_test_1.default)('controller logs are persisted to local-core.log', () => {
|
|
569
649
|
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-controller-logs-'));
|
|
570
650
|
try {
|
|
@@ -739,7 +819,7 @@ name = "pi-workspace"
|
|
|
739
819
|
[projects.agent]
|
|
740
820
|
type = "pi"
|
|
741
821
|
`);
|
|
742
|
-
strict_1.default.deepEqual(runtime.agentRuntimes.map((entry) => entry.agentType), ['localcore-acp', 'opencode', 'codex']);
|
|
822
|
+
strict_1.default.deepEqual(runtime.agentRuntimes.map((entry) => entry.agentType), ['localcore-acp', 'opencode', 'codex', 'hermes']);
|
|
743
823
|
strict_1.default.equal(runtime.kernel.getCapabilitySnapshot().snapshot.agents.some((capability) => capability.agentType === 'claudecode'), false);
|
|
744
824
|
strict_1.default.equal(runtime.kernel.getCapabilitySnapshot().snapshot.agents.some((capability) => capability.agentType === 'pi'), false);
|
|
745
825
|
strict_1.default.deepEqual(await runtime.workspaceRouter.listWorkspaces(), []);
|
|
@@ -21,7 +21,9 @@ const rootDir = node_process_1.default.cwd();
|
|
|
21
21
|
strict_1.default.match(testScript, /\bpnpm build:renderer\b/, 'pnpm test must build renderer assets');
|
|
22
22
|
strict_1.default.match(testScript, /\bpnpm build:electron\b/, 'pnpm test must compile Electron and Local AI Core tests');
|
|
23
23
|
strict_1.default.match(testScript, /\bnode --test\b/, 'pnpm test must run the Node test suite');
|
|
24
|
-
strict_1.default.match(testScript, /dist-electron\/electron\/\*\.test\.js/, 'pnpm test must include Electron tests');
|
|
24
|
+
strict_1.default.match(testScript, /dist-electron\/tests\/electron\/\*\.test\.js/, 'pnpm test must include Electron tests');
|
|
25
|
+
strict_1.default.match(testScript, /dist-electron\/tests\/contracts\/\*\.test\.js/, 'pnpm test must include contract tests');
|
|
26
|
+
strict_1.default.match(testScript, /dist-electron\/tests\/integration\/\*\.test\.js/, 'pnpm test must include integration tests');
|
|
25
27
|
strict_1.default.match(testScript, /dist-electron\/packages\/knowledge-api\/test\/\*\.test\.js/, 'pnpm test must include package tests');
|
|
26
28
|
strict_1.default.match(testScript, /dist-electron\/src\/pages\/Threads\/thread-chat-permission\.test\.js/, 'pnpm test must include renderer state tests');
|
|
27
29
|
const buildIndex = smokeScript.indexOf('pnpm build');
|
|
@@ -44,20 +46,23 @@ const rootDir = node_process_1.default.cwd();
|
|
|
44
46
|
});
|
|
45
47
|
(0, node_test_1.default)('release workflow keeps validation artifacts separate from formal releases', () => {
|
|
46
48
|
const ciWorkflow = (0, node_fs_1.readFileSync)((0, node_path_1.join)(rootDir, '.github', 'workflows', 'ci.yml'), 'utf8');
|
|
49
|
+
const packageMacosWorkflow = (0, node_fs_1.readFileSync)((0, node_path_1.join)(rootDir, '.github', 'workflows', 'package-macos.yml'), 'utf8');
|
|
47
50
|
const releaseWorkflow = (0, node_fs_1.readFileSync)((0, node_path_1.join)(rootDir, '.github', 'workflows', 'release.yml'), 'utf8');
|
|
48
51
|
const releaseDocs = (0, node_fs_1.readFileSync)((0, node_path_1.join)(rootDir, 'docs', 'operations', 'release-workflow.md'), 'utf8');
|
|
49
52
|
strict_1.default.match(ciWorkflow, /pull_request:/, 'CI must run before merge');
|
|
50
53
|
strict_1.default.match(ciWorkflow, /branches:\s*\n\s*- main/, 'CI must run on main pushes');
|
|
51
|
-
strict_1.default.
|
|
52
|
-
strict_1.default.match(
|
|
53
|
-
strict_1.default.match(
|
|
54
|
+
strict_1.default.doesNotMatch(ciWorkflow, /macos-latest/, 'main branch CI must not run macOS packaging');
|
|
55
|
+
strict_1.default.match(packageMacosWorkflow, /workflow_dispatch:/, 'macOS validation packaging must be manual');
|
|
56
|
+
strict_1.default.match(packageMacosWorkflow, /Run tests[\s\S]*?run: pnpm test/, 'manual macOS packaging must run tests first');
|
|
57
|
+
strict_1.default.match(packageMacosWorkflow, /--publish never/, 'manual macOS artifacts must not publish formal releases');
|
|
58
|
+
strict_1.default.match(packageMacosWorkflow, /retention-days: 14/, 'manual macOS validation artifacts must have bounded retention');
|
|
54
59
|
strict_1.default.match(releaseWorkflow, /tags:\s*\n\s*- 'v\*'/, 'formal releases must be tied to version tags');
|
|
55
60
|
strict_1.default.match(releaseWorkflow, /Run tests[\s\S]*?run: pnpm test/, 'release packaging must run the fast gate');
|
|
56
61
|
strict_1.default.match(releaseWorkflow, /Build app[\s\S]*?run: pnpm build/, 'release packaging must run a production build');
|
|
57
62
|
strict_1.default.match(releaseWorkflow, /--publish always/, 'release artifacts must publish only from the release workflow');
|
|
58
63
|
strict_1.default.match(releaseWorkflow, /tag_version.*package_version/s, 'tag releases must verify the package version');
|
|
59
|
-
strict_1.default.match(releaseDocs, /validation only/i, 'release docs must describe
|
|
60
|
-
strict_1.default.match(releaseDocs, /not formal releases/i, 'release docs must separate
|
|
64
|
+
strict_1.default.match(releaseDocs, /manual package artifacts are intended for validation only/i, 'release docs must describe manual artifacts as validation only');
|
|
65
|
+
strict_1.default.match(releaseDocs, /not formal releases/i, 'release docs must separate manual artifacts from formal releases');
|
|
61
66
|
});
|
|
62
67
|
(0, node_test_1.default)('production smoke gate launches the built app and checks runtime capabilities', () => {
|
|
63
68
|
const smokeScript = (0, node_fs_1.readFileSync)((0, node_path_1.join)(rootDir, 'scripts', 'e2e-smoke.mjs'), 'utf8');
|
|
@@ -8,7 +8,7 @@ const strict_1 = __importDefault(require("node:assert/strict"));
|
|
|
8
8
|
const node_fs_1 = require("node:fs");
|
|
9
9
|
const node_path_1 = require("node:path");
|
|
10
10
|
const node_os_1 = require("node:os");
|
|
11
|
-
const runtime_detection_service_js_1 = require("
|
|
11
|
+
const runtime_detection_service_js_1 = require("../../services/local-ai-core/src/runtime/runtime-detection-service.js");
|
|
12
12
|
(0, node_test_1.default)('runtime detection service returns unknown results before first detection', () => {
|
|
13
13
|
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'runtime-detection-service-'));
|
|
14
14
|
try {
|
|
@@ -8,8 +8,8 @@ const strict_1 = __importDefault(require("node:assert/strict"));
|
|
|
8
8
|
const node_fs_1 = require("node:fs");
|
|
9
9
|
const node_os_1 = require("node:os");
|
|
10
10
|
const node_path_1 = require("node:path");
|
|
11
|
-
const local_core_acp_store_js_1 = require("
|
|
12
|
-
const command_risk_js_1 = require("
|
|
11
|
+
const local_core_acp_store_js_1 = require("../../services/local-ai-core/src/acp/local-core-acp-store.js");
|
|
12
|
+
const command_risk_js_1 = require("../../services/local-ai-core/src/security/command-risk.js");
|
|
13
13
|
(0, node_test_1.default)('workspace security settings persist and create audit events', () => {
|
|
14
14
|
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'security-settings-'));
|
|
15
15
|
const store = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
|
|
@@ -8,11 +8,12 @@ const strict_1 = __importDefault(require("node:assert/strict"));
|
|
|
8
8
|
const node_fs_1 = require("node:fs");
|
|
9
9
|
const node_path_1 = require("node:path");
|
|
10
10
|
const node_os_1 = require("node:os");
|
|
11
|
-
const local_core_acp_store_js_1 = require("
|
|
12
|
-
const local_core_acp_session_coordinator_js_1 = require("
|
|
13
|
-
const scheduler_service_js_1 = require("
|
|
14
|
-
const
|
|
15
|
-
const
|
|
11
|
+
const local_core_acp_store_js_1 = require("../../services/local-ai-core/src/acp/local-core-acp-store.js");
|
|
12
|
+
const local_core_acp_session_coordinator_js_1 = require("../../services/local-ai-core/src/acp/local-core-acp-session-coordinator.js");
|
|
13
|
+
const scheduler_service_js_1 = require("../../services/local-ai-core/src/scheduler/scheduler-service.js");
|
|
14
|
+
const lark_schedule_adapter_js_1 = require("../../services/local-ai-core/src/scheduler/lark-schedule-adapter.js");
|
|
15
|
+
const bootstrap_js_1 = require("../../services/local-ai-core/src/kernel/bootstrap.js");
|
|
16
|
+
const local_core_controller_js_1 = require("../../services/local-ai-core/src/runtime/local-core-controller.js");
|
|
16
17
|
(0, node_test_1.default)('workspace registry entries persist in LocalCoreAcpStore', () => {
|
|
17
18
|
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'workspace-registry-'));
|
|
18
19
|
try {
|
|
@@ -38,7 +39,7 @@ const local_core_controller_js_1 = require("../services/local-ai-core/src/runtim
|
|
|
38
39
|
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
39
40
|
}
|
|
40
41
|
});
|
|
41
|
-
(0, node_test_1.default)('scheduler create resolves a Lark route
|
|
42
|
+
(0, node_test_1.default)('scheduler create resolves a Lark delivery route without binding the job to a thread', async () => {
|
|
42
43
|
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'scheduler-binding-route-'));
|
|
43
44
|
try {
|
|
44
45
|
const runtime = (0, bootstrap_js_1.bootstrapLocalCoreRuntime)({
|
|
@@ -74,7 +75,6 @@ const local_core_controller_js_1 = require("../services/local-ai-core/src/runtim
|
|
|
74
75
|
type: 'channel.chat',
|
|
75
76
|
channelId: 'chat-1',
|
|
76
77
|
participantId: 'user-1',
|
|
77
|
-
threadId: thread.id,
|
|
78
78
|
});
|
|
79
79
|
await controller.close();
|
|
80
80
|
}
|
|
@@ -82,6 +82,94 @@ const local_core_controller_js_1 = require("../services/local-ai-core/src/runtim
|
|
|
82
82
|
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
83
83
|
}
|
|
84
84
|
});
|
|
85
|
+
(0, node_test_1.default)('Lark scheduled same-thread execution resolves the latest channel thread and keeps channel delivery', async () => {
|
|
86
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'scheduler-lark-latest-thread-'));
|
|
87
|
+
try {
|
|
88
|
+
const store = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
|
|
89
|
+
const oldThread = store.createThread('workspace-a', 'Old Lark thread');
|
|
90
|
+
const latestThread = store.createThread('workspace-a', 'Latest Lark thread');
|
|
91
|
+
const now = new Date().toISOString();
|
|
92
|
+
store.upsertPlatformThreadBinding({
|
|
93
|
+
workspace_id: 'workspace-a',
|
|
94
|
+
platform: 'lark',
|
|
95
|
+
chat_id: 'chat-1',
|
|
96
|
+
platform_user_id: 'user-1',
|
|
97
|
+
thread_id: oldThread.id,
|
|
98
|
+
last_platform_message_id: null,
|
|
99
|
+
created_at: now,
|
|
100
|
+
updated_at: now,
|
|
101
|
+
});
|
|
102
|
+
store.upsertPlatformThreadBinding({
|
|
103
|
+
workspace_id: 'workspace-a',
|
|
104
|
+
platform: 'lark',
|
|
105
|
+
chat_id: 'chat-1',
|
|
106
|
+
platform_user_id: 'user-1',
|
|
107
|
+
thread_id: latestThread.id,
|
|
108
|
+
last_platform_message_id: null,
|
|
109
|
+
created_at: now,
|
|
110
|
+
updated_at: now,
|
|
111
|
+
});
|
|
112
|
+
const job = store.createScheduledJob({
|
|
113
|
+
workspaceId: 'workspace-a',
|
|
114
|
+
platform: 'lark',
|
|
115
|
+
route: { type: 'channel.chat', channelId: 'chat-1', participantId: 'user-1', threadId: oldThread.id },
|
|
116
|
+
executionMode: 'same-thread',
|
|
117
|
+
triggerType: 'cron',
|
|
118
|
+
cronExpr: '30 18 * * *',
|
|
119
|
+
promptTemplate: 'ping',
|
|
120
|
+
description: 'bound lark task',
|
|
121
|
+
enabled: true,
|
|
122
|
+
});
|
|
123
|
+
let sentThreadId = '';
|
|
124
|
+
let deliveredRoute;
|
|
125
|
+
const adapter = new lark_schedule_adapter_js_1.LarkScheduleAdapter({
|
|
126
|
+
store,
|
|
127
|
+
getWorkspaceRouter: () => ({
|
|
128
|
+
getThread: async (threadId) => ({
|
|
129
|
+
id: threadId,
|
|
130
|
+
workspaceId: 'workspace-a',
|
|
131
|
+
title: 'Thread',
|
|
132
|
+
live: false,
|
|
133
|
+
updatedAt: now,
|
|
134
|
+
createdAt: now,
|
|
135
|
+
historyCount: 1,
|
|
136
|
+
excerpt: '',
|
|
137
|
+
bridgeSessionKey: '',
|
|
138
|
+
agentType: 'localcore-acp',
|
|
139
|
+
selectedKnowledgeBaseIds: [],
|
|
140
|
+
pendingPermissionRequest: null,
|
|
141
|
+
messages: [{ id: 'message-1', role: 'assistant', kind: 'final', content: 'pong', timestamp: now }],
|
|
142
|
+
}),
|
|
143
|
+
sendThreadMessage: async (threadId) => {
|
|
144
|
+
sentThreadId = threadId;
|
|
145
|
+
store.updateRun('run-1', threadId, 'completed');
|
|
146
|
+
return { runId: 'run-1' };
|
|
147
|
+
},
|
|
148
|
+
}),
|
|
149
|
+
getChannelRuntime: () => ({
|
|
150
|
+
muteThreadBridge: () => { },
|
|
151
|
+
unmuteThreadBridge: () => { },
|
|
152
|
+
sendScheduledMessage: async (_workspaceId, route) => {
|
|
153
|
+
deliveredRoute = route;
|
|
154
|
+
return 'platform-message-1';
|
|
155
|
+
},
|
|
156
|
+
}),
|
|
157
|
+
});
|
|
158
|
+
const result = await adapter.execute({ job, triggeredAt: now });
|
|
159
|
+
strict_1.default.equal(sentThreadId, latestThread.id);
|
|
160
|
+
strict_1.default.equal(result.threadId, latestThread.id);
|
|
161
|
+
strict_1.default.equal(result.platformMessageId, 'platform-message-1');
|
|
162
|
+
strict_1.default.deepEqual(deliveredRoute, {
|
|
163
|
+
type: 'channel.chat',
|
|
164
|
+
channelId: 'chat-1',
|
|
165
|
+
participantId: 'user-1',
|
|
166
|
+
});
|
|
167
|
+
store.close();
|
|
168
|
+
}
|
|
169
|
+
finally {
|
|
170
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
171
|
+
}
|
|
172
|
+
});
|
|
85
173
|
(0, node_test_1.default)('ACP runtime env includes the current workspace path for file returns', async () => {
|
|
86
174
|
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'workspace-env-'));
|
|
87
175
|
try {
|
|
@@ -6,8 +6,8 @@ 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
8
|
const node_path_1 = require("node:path");
|
|
9
|
-
const content_js_1 = require("
|
|
10
|
-
const file_utils_js_1 = require("
|
|
9
|
+
const content_js_1 = require("../../services/local-ai-core/src/channel/shared/content.js");
|
|
10
|
+
const file_utils_js_1 = require("../../services/local-ai-core/src/channel/shared/file-utils.js");
|
|
11
11
|
(0, node_test_1.default)('channel thread message input stays plain text when there are no attachments', () => {
|
|
12
12
|
const input = (0, content_js_1.createChannelThreadMessageInput)('Alice: hello', [
|
|
13
13
|
{ type: 'text', text: 'hello' },
|
|
@@ -5,7 +5,7 @@ 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 local_core_acp_content_js_1 = require("
|
|
8
|
+
const local_core_acp_content_js_1 = require("../../services/local-ai-core/src/acp/local-core-acp-content.js");
|
|
9
9
|
(0, node_test_1.default)('thread message input keeps inbound file content parts', () => {
|
|
10
10
|
const normalized = (0, local_core_acp_content_js_1.normalizeThreadMessageInput)({
|
|
11
11
|
displayText: '',
|