@kafca/agentdock 0.1.44 → 0.1.45
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 +71 -11
- package/bin/agentdock.mjs +5 -5
- package/dist/renderer/assets/{Badge-Qk9qHovH.js → Badge-BIojwD6s.js} +1 -1
- package/dist/renderer/assets/{Chat-BK-fm0Xr.js → Chat-wjBR3XEX.js} +1 -1
- package/dist/renderer/assets/{ChatMarkdown-BKdW8OON.js → ChatMarkdown-DqszsPB_.js} +1 -1
- package/dist/renderer/assets/Config-BDCHs5A2.js +21 -0
- package/dist/renderer/assets/{CronList-Z3TvSRKP.js → CronList-CEWQYTov.js} +1 -1
- package/dist/renderer/assets/Dashboard-DrWftfk3.js +26 -0
- package/dist/renderer/assets/{EmptyState-kD9yAwtw.js → EmptyState-BqCx1oWr.js} +1 -1
- package/dist/renderer/assets/{KnowledgeDetail-BAMCLrjz.js → KnowledgeDetail-BwK8Gpfa.js} +2 -2
- package/dist/renderer/assets/{KnowledgeHome-ChvjOfrc.js → KnowledgeHome-hhDTZMDO.js} +3 -3
- package/dist/renderer/assets/{Logs-Cv0nth3j.js → Logs-C9xPEhFT.js} +1 -1
- package/dist/renderer/assets/{Modal-DwY4lecS.js → Modal-D2U4r2V0.js} +1 -1
- package/dist/renderer/assets/{MonitorList-JEvMoWUm.js → MonitorList-CayKbfwJ.js} +1 -1
- package/dist/renderer/assets/{Page-CnB9ePsb.js → Page-urf5GcT_.js} +1 -1
- package/dist/renderer/assets/ProjectDetail-B1GyAr7a.js +11 -0
- package/dist/renderer/assets/{ProjectList-D6CXumR_.js → ProjectList-BoipQypE.js} +1 -1
- package/dist/renderer/assets/{Select-zfoksLGB.js → Select-CAscWpy5.js} +1 -1
- package/dist/renderer/assets/{SessionChat-DUTh4WoM.js → SessionChat-BAVy5221.js} +1 -1
- package/dist/renderer/assets/{SessionList-D9o-lUst.js → SessionList-BZpuMvh0.js} +1 -1
- package/dist/renderer/assets/{ThreadChat-BYXlT-Kc.js → ThreadChat-JX7m9biG.js} +1 -1
- package/dist/renderer/assets/Workspace-CFW8otjU.js +14 -0
- package/dist/renderer/assets/{arrow-left-CNEAuElg.js → arrow-left-7HrNuS-V.js} +1 -1
- package/dist/renderer/assets/{book-open-BNJMhNI-.js → book-open-BDoAu82s.js} +1 -1
- package/dist/renderer/assets/{check-BnclClRU.js → check-DGlyq4xX.js} +1 -1
- package/dist/renderer/assets/{chevron-down-Bh53e0O-.js → chevron-down-Bb_3EHq9.js} +1 -1
- package/dist/renderer/assets/{circle-ClZTWCWO.js → circle-DIpqZnYw.js} +1 -1
- package/dist/renderer/assets/desktop-Ci1fnRDR.js +1 -0
- package/dist/renderer/assets/{desktop-C6q6wBae.js → desktop-Do_UrC-o.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-Bbf0ERLP.js} +1 -1
- package/dist/renderer/assets/{heart-u4Q1aOqj.js → heart-CGqU-qfw.js} +1 -1
- package/dist/renderer/assets/{index-BNFx4uYv.js → index-Df-Trovt.js} +2 -2
- package/dist/renderer/assets/{index-B36eowj-.js → index-DwhTjLA1.js} +36 -36
- package/dist/renderer/assets/index-RaTga2BC.css +11 -0
- package/dist/renderer/assets/ja-DtvtBRTW.js +1 -0
- package/dist/renderer/assets/{pencil-DLh9QNmT.js → pencil-CHiy1MvK.js} +1 -1
- package/dist/renderer/assets/{play-BDTk9Scm.js → play-CGYnmC4Q.js} +1 -1
- package/dist/renderer/assets/{plug-BG0JHW6B.js → plug-Mexu4gyV.js} +1 -1
- package/dist/renderer/assets/{plus-BSc8POsz.js → plus-B_Q5-Oj7.js} +1 -1
- package/dist/renderer/assets/projects-C6bbkC-O.js +1 -0
- package/dist/renderer/assets/{save-D5AO_G6a.js → save-CJ_QFPw0.js} +1 -1
- package/dist/renderer/assets/{search-CniPk7Ey.js → search-D8Dkhsyq.js} +1 -1
- package/dist/renderer/assets/{server-GsXyQAZm.js → server-Cd96R7WL.js} +1 -1
- package/dist/renderer/assets/{session-utils-BDxzgTBC.js → session-utils-BNUbQBfF.js} +1 -1
- package/dist/renderer/assets/{sessions-CLcKA1Tt.js → sessions-BACfV8s1.js} +1 -1
- package/dist/renderer/assets/{shield-check-4i0E3hmf.js → shield-check-Cnn-yQUA.js} +1 -1
- package/dist/renderer/assets/{trash-2-DhtOCsO5.js → trash-2-oWCxwQ9D.js} +1 -1
- package/dist/renderer/assets/{user-BtC_pVXV.js → user-BzE9BwsY.js} +1 -1
- package/dist/renderer/assets/{wifi-off-BHPzPY_B.js → wifi-off-DHxnNkdY.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/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 +59 -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/Config-B93CQGvY.js +0 -16
- package/dist/renderer/assets/Dashboard-Ch3QkWwf.js +0 -26
- package/dist/renderer/assets/ProjectDetail-DJa3sMmU.js +0 -11
- 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/projects-DwfZwxDW.js +0 -1
- package/dist/renderer/assets/zh-DIcdHycM.js +0 -1
- package/dist/renderer/assets/zh-TW-BtRwreY_.js +0 -1
|
@@ -10,6 +10,7 @@ const node_path_1 = require("node:path");
|
|
|
10
10
|
const node_os_1 = require("node:os");
|
|
11
11
|
const local_core_acp_store_js_1 = require("../../services/local-ai-core/src/acp/local-core-acp-store.js");
|
|
12
12
|
const local_core_acp_session_coordinator_js_1 = require("../../services/local-ai-core/src/acp/local-core-acp-session-coordinator.js");
|
|
13
|
+
const local_core_acp_transport_js_1 = require("../../services/local-ai-core/src/acp/local-core-acp-transport.js");
|
|
13
14
|
const local_core_acp_turn_coordinator_js_1 = require("../../services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js");
|
|
14
15
|
const scheduler_service_js_1 = require("../../services/local-ai-core/src/scheduler/scheduler-service.js");
|
|
15
16
|
const lark_schedule_adapter_js_1 = require("../../services/local-ai-core/src/scheduler/lark-schedule-adapter.js");
|
|
@@ -40,6 +41,141 @@ const local_core_controller_js_1 = require("../../services/local-ai-core/src/run
|
|
|
40
41
|
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
41
42
|
}
|
|
42
43
|
});
|
|
44
|
+
(0, node_test_1.default)('model providers persist independently from workspace config', () => {
|
|
45
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'model-provider-store-'));
|
|
46
|
+
try {
|
|
47
|
+
const store = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
|
|
48
|
+
const provider = store.upsertModelProvider({
|
|
49
|
+
name: 'deepseek',
|
|
50
|
+
api_key: 'secret',
|
|
51
|
+
base_url: 'https://api.deepseek.com',
|
|
52
|
+
model: 'deepseek-v4-flash',
|
|
53
|
+
env: { CUSTOM_PROVIDER_ENV: '1' },
|
|
54
|
+
});
|
|
55
|
+
strict_1.default.equal(provider.id, 'deepseek');
|
|
56
|
+
strict_1.default.equal(provider.api_key, 'secret');
|
|
57
|
+
strict_1.default.equal(store.listModelProviders().length, 1);
|
|
58
|
+
const reopened = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
|
|
59
|
+
strict_1.default.equal(reopened.getModelProvider('deepseek')?.model, 'deepseek-v4-flash');
|
|
60
|
+
reopened.close();
|
|
61
|
+
store.close();
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
(0, node_test_1.default)('external project and thread mappings persist with isolated workspace paths', () => {
|
|
68
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'external-store-'));
|
|
69
|
+
try {
|
|
70
|
+
const store = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
|
|
71
|
+
const project = store.upsertExternalProject({
|
|
72
|
+
userId: 'user-a',
|
|
73
|
+
externalProjectId: 'project-a',
|
|
74
|
+
workspaceId: 'external-user-a-project-a',
|
|
75
|
+
workspacePath: '/data/users/user-a/projects/project-a',
|
|
76
|
+
displayName: 'Project A',
|
|
77
|
+
agentType: 'pi',
|
|
78
|
+
providerId: 'deepseek',
|
|
79
|
+
metadata: { source: 'external' },
|
|
80
|
+
createdAt: '2026-05-15T00:00:00.000Z',
|
|
81
|
+
updatedAt: '2026-05-15T00:00:00.000Z',
|
|
82
|
+
});
|
|
83
|
+
const thread = store.createThread(project.workspaceId, 'Thread A', 'pi');
|
|
84
|
+
store.upsertExternalThread({
|
|
85
|
+
userId: project.userId,
|
|
86
|
+
externalProjectId: project.externalProjectId,
|
|
87
|
+
externalThreadId: 'thread-a',
|
|
88
|
+
workspaceId: project.workspaceId,
|
|
89
|
+
threadId: thread.id,
|
|
90
|
+
workspacePath: '/data/users/user-a/projects/project-a/threads/thread-a/workspace',
|
|
91
|
+
metadata: { channel: 'api' },
|
|
92
|
+
createdAt: '2026-05-15T00:00:00.000Z',
|
|
93
|
+
updatedAt: '2026-05-15T00:00:00.000Z',
|
|
94
|
+
});
|
|
95
|
+
store.close();
|
|
96
|
+
const reopened = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
|
|
97
|
+
strict_1.default.equal(reopened.getExternalProject('user-a', 'project-a')?.workspacePath, '/data/users/user-a/projects/project-a');
|
|
98
|
+
strict_1.default.equal(reopened.getExternalThreadByThreadId(thread.id)?.workspacePath, '/data/users/user-a/projects/project-a/threads/thread-a/workspace');
|
|
99
|
+
reopened.close();
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
(0, node_test_1.default)('controller migrates embedded project providers into shared provider store', async () => {
|
|
106
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'model-provider-migration-'));
|
|
107
|
+
try {
|
|
108
|
+
const runtime = (0, bootstrap_js_1.bootstrapLocalCoreRuntime)({
|
|
109
|
+
userDataPath,
|
|
110
|
+
enableKnowledge: false,
|
|
111
|
+
log: () => { },
|
|
112
|
+
});
|
|
113
|
+
const controller = new local_core_controller_js_1.LocalCoreController(userDataPath, runtime);
|
|
114
|
+
await controller.saveStructuredConfigFile({
|
|
115
|
+
projects: [{
|
|
116
|
+
name: 'workspace-a',
|
|
117
|
+
agent: {
|
|
118
|
+
type: 'pi',
|
|
119
|
+
options: { work_dir: '/tmp/workspace-a' },
|
|
120
|
+
providers: [{
|
|
121
|
+
name: 'deepseek-v4-flash',
|
|
122
|
+
api_key: 'secret',
|
|
123
|
+
base_url: 'https://api.deepseek.com',
|
|
124
|
+
model: 'deepseek-v4-flash',
|
|
125
|
+
}],
|
|
126
|
+
},
|
|
127
|
+
platforms: [],
|
|
128
|
+
}],
|
|
129
|
+
});
|
|
130
|
+
const config = await controller.readConfigFile();
|
|
131
|
+
strict_1.default.equal(config.parsed?.projects?.[0]?.agent.options?.provider_id, 'deepseek');
|
|
132
|
+
strict_1.default.equal(config.parsed?.projects?.[0]?.agent.providers, undefined);
|
|
133
|
+
const providers = await controller.listModelProviders();
|
|
134
|
+
strict_1.default.equal(providers.providers[0]?.id, 'deepseek');
|
|
135
|
+
strict_1.default.equal(providers.providers[0]?.name, 'deepseek');
|
|
136
|
+
await controller.close();
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
(0, node_test_1.default)('workspace router resolves projects that select a shared provider', async () => {
|
|
143
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'workspace-provider-ref-'));
|
|
144
|
+
try {
|
|
145
|
+
const runtime = (0, bootstrap_js_1.bootstrapLocalCoreRuntime)({
|
|
146
|
+
userDataPath,
|
|
147
|
+
enableKnowledge: false,
|
|
148
|
+
log: () => { },
|
|
149
|
+
});
|
|
150
|
+
const controller = new local_core_controller_js_1.LocalCoreController(userDataPath, runtime);
|
|
151
|
+
const provider = await controller.createModelProvider({
|
|
152
|
+
name: 'deepseek',
|
|
153
|
+
api_key: 'secret',
|
|
154
|
+
base_url: 'https://api.deepseek.com',
|
|
155
|
+
model: 'deepseek-v4-flash',
|
|
156
|
+
});
|
|
157
|
+
await controller.saveStructuredConfigFile({
|
|
158
|
+
projects: [{
|
|
159
|
+
name: 'workspace-a',
|
|
160
|
+
agent: {
|
|
161
|
+
type: 'pi',
|
|
162
|
+
options: {
|
|
163
|
+
work_dir: '/tmp/workspace-a',
|
|
164
|
+
provider_id: provider.id,
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
platforms: [],
|
|
168
|
+
}],
|
|
169
|
+
});
|
|
170
|
+
const workspaces = await controller.listWorkspaces();
|
|
171
|
+
strict_1.default.equal(workspaces[0]?.id, 'workspace-a');
|
|
172
|
+
strict_1.default.equal(workspaces[0]?.agentType, 'pi');
|
|
173
|
+
await controller.close();
|
|
174
|
+
}
|
|
175
|
+
finally {
|
|
176
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
177
|
+
}
|
|
178
|
+
});
|
|
43
179
|
(0, node_test_1.default)('scheduler create resolves a Lark delivery route without binding the job to a thread', async () => {
|
|
44
180
|
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'scheduler-binding-route-'));
|
|
45
181
|
try {
|
|
@@ -443,6 +579,50 @@ const local_core_controller_js_1 = require("../../services/local-ai-core/src/run
|
|
|
443
579
|
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
444
580
|
}
|
|
445
581
|
});
|
|
582
|
+
(0, node_test_1.default)('ACP runtime PATH includes service-safe user bin directories', () => {
|
|
583
|
+
const previousHome = process.env.HOME;
|
|
584
|
+
process.env.HOME = '/home/agentdock-test';
|
|
585
|
+
try {
|
|
586
|
+
const path = (0, local_core_acp_session_coordinator_js_1.buildAgentPath)('/usr/bin:/bin', '/opt/agentdock/bin');
|
|
587
|
+
strict_1.default.equal(path, '/opt/agentdock/bin:/home/agentdock-test/.local/bin:/home/agentdock-test/bin:/usr/bin:/bin');
|
|
588
|
+
}
|
|
589
|
+
finally {
|
|
590
|
+
if (previousHome === undefined) {
|
|
591
|
+
delete process.env.HOME;
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
process.env.HOME = previousHome;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
(0, node_test_1.default)('ACP transport reports missing agent commands without an unhandled process error', async () => {
|
|
599
|
+
const closed = new Promise((resolve) => {
|
|
600
|
+
const transport = new local_core_acp_transport_js_1.LocalCoreAcpTransport({
|
|
601
|
+
log: () => { },
|
|
602
|
+
onAgentRequest: () => { },
|
|
603
|
+
onAgentNotification: () => { },
|
|
604
|
+
onSessionClosed: (_session, error) => {
|
|
605
|
+
resolve(error);
|
|
606
|
+
},
|
|
607
|
+
});
|
|
608
|
+
transport.spawnSession({
|
|
609
|
+
threadId: 'thread-missing-command',
|
|
610
|
+
bridgeSessionKey: 'session:missing-command',
|
|
611
|
+
config: {
|
|
612
|
+
workspaceId: 'workspace-a',
|
|
613
|
+
agentType: 'hermes',
|
|
614
|
+
command: 'agentdock-command-that-does-not-exist',
|
|
615
|
+
args: ['acp'],
|
|
616
|
+
env: {},
|
|
617
|
+
workDir: (0, node_os_1.tmpdir)(),
|
|
618
|
+
model: '',
|
|
619
|
+
},
|
|
620
|
+
runtimeEnv: {},
|
|
621
|
+
});
|
|
622
|
+
});
|
|
623
|
+
const error = await closed;
|
|
624
|
+
strict_1.default.equal(error.message, 'ACP agent command not found: agentdock-command-that-does-not-exist');
|
|
625
|
+
});
|
|
446
626
|
(0, node_test_1.default)('ACP scheduled session can override permission mode without changing thread mode', async () => {
|
|
447
627
|
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'scheduler-permission-mode-'));
|
|
448
628
|
try {
|
|
@@ -515,6 +695,83 @@ const local_core_controller_js_1 = require("../../services/local-ai-core/src/run
|
|
|
515
695
|
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
516
696
|
}
|
|
517
697
|
});
|
|
698
|
+
(0, node_test_1.default)('ACP sandbox sessions use container workspace cwd', async () => {
|
|
699
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'sandbox-session-cwd-'));
|
|
700
|
+
try {
|
|
701
|
+
const store = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
|
|
702
|
+
const thread = store.createThread('workspace-a', 'Thread');
|
|
703
|
+
let sessionNewParams;
|
|
704
|
+
const coordinator = new local_core_acp_session_coordinator_js_1.LocalCoreAcpSessionCoordinator({
|
|
705
|
+
store,
|
|
706
|
+
transport: {
|
|
707
|
+
spawnSession(input) {
|
|
708
|
+
return {
|
|
709
|
+
threadId: input.threadId,
|
|
710
|
+
bridgeSessionKey: input.bridgeSessionKey,
|
|
711
|
+
closed: false,
|
|
712
|
+
sessionId: '',
|
|
713
|
+
supportsLoad: false,
|
|
714
|
+
pendingPermissionByRun: new Map(),
|
|
715
|
+
schedulerJobCreatedByRun: new Map(),
|
|
716
|
+
launchPermissionMode: '',
|
|
717
|
+
};
|
|
718
|
+
},
|
|
719
|
+
initializeSession: async () => { },
|
|
720
|
+
request: async (_session, method, params) => {
|
|
721
|
+
if (method === 'session/new') {
|
|
722
|
+
sessionNewParams = params;
|
|
723
|
+
return { sessionId: 'session-1' };
|
|
724
|
+
}
|
|
725
|
+
return {};
|
|
726
|
+
},
|
|
727
|
+
closeSession: () => { },
|
|
728
|
+
closeSessionWithError: () => { },
|
|
729
|
+
sendRaw: () => true,
|
|
730
|
+
},
|
|
731
|
+
runThreadMap: new Map(),
|
|
732
|
+
emitBridge: () => { },
|
|
733
|
+
});
|
|
734
|
+
await coordinator.ensureSession(thread.id, 'session:thread-1', {
|
|
735
|
+
workspaceId: 'workspace-a',
|
|
736
|
+
agentType: 'pi',
|
|
737
|
+
command: process.execPath,
|
|
738
|
+
args: ['/host/sandbox-stdio-proxy.js'],
|
|
739
|
+
env: {},
|
|
740
|
+
workDir: '/host/workspace-a',
|
|
741
|
+
model: '',
|
|
742
|
+
sandbox: {
|
|
743
|
+
enabled: true,
|
|
744
|
+
provider: 'opensandbox',
|
|
745
|
+
transport: 'http-ndjson',
|
|
746
|
+
serverUrl: 'http://127.0.0.1:8080',
|
|
747
|
+
apiKeyEnv: 'OPEN_SANDBOX_API_KEY',
|
|
748
|
+
image: 'agentdock/pi-acp:local',
|
|
749
|
+
acpPort: 8080,
|
|
750
|
+
entrypoint: ['node', '/opt/agentdock/acp-bridge.mjs'],
|
|
751
|
+
timeoutSeconds: 600,
|
|
752
|
+
lifecycle: 'per_thread',
|
|
753
|
+
idleSeconds: 900,
|
|
754
|
+
warmPoolSize: 0,
|
|
755
|
+
cpu: '1000m',
|
|
756
|
+
memory: '1Gi',
|
|
757
|
+
userId: 'local',
|
|
758
|
+
projectId: 'workspace-a',
|
|
759
|
+
stateScope: 'project',
|
|
760
|
+
workspaceHostPath: '/host/workspace-a',
|
|
761
|
+
workspaceMountPath: '/workspace',
|
|
762
|
+
stateHostPath: '/host/state',
|
|
763
|
+
stateMountPath: '/agent-state',
|
|
764
|
+
runtimeCommand: '/usr/local/bin/pi-acp',
|
|
765
|
+
runtimeArgs: [],
|
|
766
|
+
runtimeEnv: {},
|
|
767
|
+
},
|
|
768
|
+
});
|
|
769
|
+
strict_1.default.equal(sessionNewParams?.cwd, '/workspace');
|
|
770
|
+
}
|
|
771
|
+
finally {
|
|
772
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
773
|
+
}
|
|
774
|
+
});
|
|
518
775
|
(0, node_test_1.default)('ACP permission requests honor scheduled session permission override', () => {
|
|
519
776
|
const sentPayloads = [];
|
|
520
777
|
let approvalCreated = false;
|
|
@@ -687,6 +687,65 @@ function findLarkPostMdText(content) {
|
|
|
687
687
|
strict_1.default.deepEqual(patchedCards.map((card) => card.text), []);
|
|
688
688
|
strict_1.default.ok(!createdCards.some((card) => /处理中|正在思考/.test(card.text)));
|
|
689
689
|
});
|
|
690
|
+
(0, node_test_1.default)('lark gateway records structured error state when startup fails', async () => {
|
|
691
|
+
const emittedEvents = [];
|
|
692
|
+
const gateway = new local_core_lark_gateway_js_1.LocalCoreLarkGateway({
|
|
693
|
+
store: {
|
|
694
|
+
expirePendingPairings: () => { },
|
|
695
|
+
listPendingPairings: () => [],
|
|
696
|
+
listAuthorizedUsers: () => [],
|
|
697
|
+
},
|
|
698
|
+
readConfig: async () => null,
|
|
699
|
+
getWorkspaceRouter: () => ({}),
|
|
700
|
+
eventBus: {
|
|
701
|
+
emit: (event) => emittedEvents.push(event),
|
|
702
|
+
on: () => () => { },
|
|
703
|
+
},
|
|
704
|
+
});
|
|
705
|
+
gateway.larkModulePromise = Promise.resolve({
|
|
706
|
+
AppType: { SelfBuild: 'SelfBuild' },
|
|
707
|
+
Domain: { Feishu: 'Feishu' },
|
|
708
|
+
LoggerLevel: { info: 'info' },
|
|
709
|
+
Client: class {
|
|
710
|
+
request() {
|
|
711
|
+
return Promise.resolve({ bot: { open_id: 'bot-open-id' } });
|
|
712
|
+
}
|
|
713
|
+
},
|
|
714
|
+
EventDispatcher: class {
|
|
715
|
+
register() { }
|
|
716
|
+
},
|
|
717
|
+
WSClient: class {
|
|
718
|
+
start() {
|
|
719
|
+
throw new Error('invalid app credentials');
|
|
720
|
+
}
|
|
721
|
+
on() { }
|
|
722
|
+
},
|
|
723
|
+
});
|
|
724
|
+
await gateway.startWorkspace({
|
|
725
|
+
workspaceId: 'workspace-1',
|
|
726
|
+
instanceId: 'default',
|
|
727
|
+
displayName: 'Workspace 1',
|
|
728
|
+
platformKey: 'lark',
|
|
729
|
+
appId: 'cli_a1',
|
|
730
|
+
appSecret: 'secret',
|
|
731
|
+
encryptKey: '',
|
|
732
|
+
verificationToken: '',
|
|
733
|
+
autoApprove: false,
|
|
734
|
+
cardActionsEnabled: true,
|
|
735
|
+
groupReplyAll: false,
|
|
736
|
+
enabled: true,
|
|
737
|
+
brand: 'lark',
|
|
738
|
+
project: { name: 'workspace-1' },
|
|
739
|
+
});
|
|
740
|
+
const status = gateway.getStatus('workspace-1');
|
|
741
|
+
strict_1.default.equal(status.status, 'error');
|
|
742
|
+
strict_1.default.equal(status.connected, false);
|
|
743
|
+
strict_1.default.equal(status.lastErrorInfo?.code, 'channel_auth_failed');
|
|
744
|
+
strict_1.default.equal(status.lastErrorInfo?.suggestedAction, 'Check app credentials and restart the Lark gateway.');
|
|
745
|
+
strict_1.default.match(status.lastError || '', /invalid app credentials/);
|
|
746
|
+
strict_1.default.ok(status.lastErrorAt);
|
|
747
|
+
strict_1.default.equal(emittedEvents.some((event) => event.type === 'localcore.error' && event.payload?.scope === 'channel.lark'), true);
|
|
748
|
+
});
|
|
690
749
|
(0, node_test_1.default)('lark permission requests render as clickable card buttons', async () => {
|
|
691
750
|
const createdCards = [];
|
|
692
751
|
const patchedCards = [];
|
|
@@ -15,10 +15,11 @@ const scheduler_run_lifecycle_js_1 = require("../../services/local-ai-core/src/s
|
|
|
15
15
|
const lark_execution_policies_js_1 = require("../../services/local-ai-core/src/scheduler/lark-execution-policies.js");
|
|
16
16
|
const local_schedule_adapter_js_1 = require("../../services/local-ai-core/src/scheduler/local-schedule-adapter.js");
|
|
17
17
|
const cards_js_1 = require("../../services/local-ai-core/src/channel/lark/cards.js");
|
|
18
|
+
const session_command_runtime_js_1 = require("../../services/local-ai-core/src/channel/shared/session-command-runtime.js");
|
|
18
19
|
const local_core_acp_backend_js_1 = require("../../services/local-ai-core/src/acp/local-core-acp-backend.js");
|
|
19
20
|
const local_core_acp_store_js_1 = require("../../services/local-ai-core/src/acp/local-core-acp-store.js");
|
|
20
21
|
const session_command_service_js_1 = require("../../services/local-ai-core/src/thread/session-command-service.js");
|
|
21
|
-
const
|
|
22
|
+
const thread_slash_command_dispatcher_js_1 = require("../../services/local-ai-core/src/thread/thread-slash-command-dispatcher.js");
|
|
22
23
|
(0, node_test_1.default)('response processor derives slash fallback replies and cron system responses', async () => {
|
|
23
24
|
const processor = new local_core_acp_response_processor_js_1.LocalCoreAcpResponseProcessor({
|
|
24
25
|
getScheduledDeliveryBinding: (threadId) => threadId === 'thread-1'
|
|
@@ -76,7 +77,15 @@ const thread_command_service_js_1 = require("../../services/local-ai-core/src/th
|
|
|
76
77
|
availableAgents: ['codex', 'pi', 'hermes'],
|
|
77
78
|
}), /当前线程 Agent:pi/);
|
|
78
79
|
});
|
|
79
|
-
(0, node_test_1.default)('
|
|
80
|
+
(0, node_test_1.default)('slash help command exposes global local command help', () => {
|
|
81
|
+
const help = (0, thread_slash_command_dispatcher_js_1.slashHelpText)();
|
|
82
|
+
strict_1.default.match(help, /`\/help`/);
|
|
83
|
+
strict_1.default.match(help, /`\/stop`/);
|
|
84
|
+
strict_1.default.match(help, /`\/mode /);
|
|
85
|
+
strict_1.default.match(help, /`\/agent /);
|
|
86
|
+
strict_1.default.match(help, /`\/list`/);
|
|
87
|
+
});
|
|
88
|
+
(0, node_test_1.default)('thread slash command dispatcher handles local slash commands without ACP transport', async () => {
|
|
80
89
|
const row = {
|
|
81
90
|
id: 'thread-1',
|
|
82
91
|
workspace_id: 'workspace-a',
|
|
@@ -95,24 +104,39 @@ const thread_command_service_js_1 = require("../../services/local-ai-core/src/th
|
|
|
95
104
|
const audits = [];
|
|
96
105
|
const syncedModes = [];
|
|
97
106
|
const closedThreads = [];
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
createAuditEvent: (input) => {
|
|
108
|
-
audits.push(input);
|
|
109
|
-
},
|
|
110
|
-
getAgentTypes: () => ['codex', 'pi', 'hermes', 'claudecode'],
|
|
111
|
-
setThreadMode: async (_threadId, mode) => {
|
|
112
|
-
syncedModes.push(mode);
|
|
107
|
+
const interruptedRuns = [];
|
|
108
|
+
let latestRun;
|
|
109
|
+
const service = new thread_slash_command_dispatcher_js_1.ThreadSlashCommandDispatcher({
|
|
110
|
+
session: {
|
|
111
|
+
listThreads: () => [],
|
|
112
|
+
getThread: () => ({ id: row.id, title: row.title, messages: [] }),
|
|
113
|
+
createThread: () => ({ id: 'created-thread', title: 'Created', messages: [] }),
|
|
114
|
+
renameThread: () => ({ id: row.id, title: 'Renamed', messages: [] }),
|
|
115
|
+
deleteThread: () => ({ deleted: true }),
|
|
113
116
|
},
|
|
114
|
-
|
|
115
|
-
|
|
117
|
+
thread: {
|
|
118
|
+
getThreadRow: () => row,
|
|
119
|
+
updateThreadAgentMode: (_threadId, mode) => {
|
|
120
|
+
row.agent_mode = mode;
|
|
121
|
+
},
|
|
122
|
+
updateThreadAgentType: (_threadId, agentType) => {
|
|
123
|
+
row.agent_type = agentType;
|
|
124
|
+
},
|
|
125
|
+
getLatestRunForThread: () => latestRun,
|
|
126
|
+
createAuditEvent: (input) => {
|
|
127
|
+
audits.push(input);
|
|
128
|
+
},
|
|
129
|
+
getAgentTypes: () => ['codex', 'pi', 'hermes', 'claudecode'],
|
|
130
|
+
setThreadMode: async (_threadId, mode) => {
|
|
131
|
+
syncedModes.push(mode);
|
|
132
|
+
},
|
|
133
|
+
closeThreadSession: (threadId) => {
|
|
134
|
+
closedThreads.push(threadId);
|
|
135
|
+
},
|
|
136
|
+
interruptRun: async (runId) => {
|
|
137
|
+
interruptedRuns.push(runId);
|
|
138
|
+
return { interrupted: true };
|
|
139
|
+
},
|
|
116
140
|
},
|
|
117
141
|
});
|
|
118
142
|
strict_1.default.deepEqual(await service.execute({
|
|
@@ -121,6 +145,32 @@ const thread_command_service_js_1 = require("../../services/local-ai-core/src/th
|
|
|
121
145
|
content: '/not-local',
|
|
122
146
|
defaultAgentType: 'codex',
|
|
123
147
|
}), { handled: false, displayText: '' });
|
|
148
|
+
const inactiveStopResult = await service.execute({
|
|
149
|
+
threadId: row.id,
|
|
150
|
+
workspaceId: row.workspace_id,
|
|
151
|
+
content: '/stop',
|
|
152
|
+
defaultAgentType: 'codex',
|
|
153
|
+
});
|
|
154
|
+
strict_1.default.equal(inactiveStopResult.handled, true);
|
|
155
|
+
strict_1.default.match(inactiveStopResult.displayText, /没有正在运行的任务/);
|
|
156
|
+
latestRun = {
|
|
157
|
+
id: 'run-active',
|
|
158
|
+
thread_id: row.id,
|
|
159
|
+
status: 'running',
|
|
160
|
+
started_at: '2026-05-11T00:00:00.000Z',
|
|
161
|
+
updated_at: '2026-05-11T00:00:00.000Z',
|
|
162
|
+
};
|
|
163
|
+
const stopResult = await service.execute({
|
|
164
|
+
threadId: row.id,
|
|
165
|
+
workspaceId: row.workspace_id,
|
|
166
|
+
content: '/stop',
|
|
167
|
+
defaultAgentType: 'codex',
|
|
168
|
+
});
|
|
169
|
+
strict_1.default.equal(stopResult.handled, true);
|
|
170
|
+
strict_1.default.match(stopResult.displayText, /已请求停止当前任务/);
|
|
171
|
+
strict_1.default.deepEqual(interruptedRuns, ['run-active']);
|
|
172
|
+
strict_1.default.equal(audits.at(-1)?.type, 'task.updated');
|
|
173
|
+
latestRun = undefined;
|
|
124
174
|
const modeResult = await service.execute({
|
|
125
175
|
threadId: row.id,
|
|
126
176
|
workspaceId: row.workspace_id,
|
|
@@ -153,6 +203,111 @@ const thread_command_service_js_1 = require("../../services/local-ai-core/src/th
|
|
|
153
203
|
strict_1.default.match(resetResult.displayText, /回到默认 Agent:codex/);
|
|
154
204
|
strict_1.default.deepEqual(closedThreads, ['thread-1', 'thread-1']);
|
|
155
205
|
});
|
|
206
|
+
(0, node_test_1.default)('channel session command runtime uses unified slash dispatcher for thread commands', async () => {
|
|
207
|
+
const row = {
|
|
208
|
+
id: 'thread-1',
|
|
209
|
+
workspace_id: 'workspace-a',
|
|
210
|
+
session_id: 'session-1',
|
|
211
|
+
bridge_session_key: 'bridge-1',
|
|
212
|
+
title: 'Channel command',
|
|
213
|
+
agent_type: 'codex',
|
|
214
|
+
created_at: '2026-05-11T00:00:00.000Z',
|
|
215
|
+
updated_at: '2026-05-11T00:00:00.000Z',
|
|
216
|
+
history_count: 0,
|
|
217
|
+
excerpt: '',
|
|
218
|
+
acp_session_id: null,
|
|
219
|
+
acp_supports_load: 0,
|
|
220
|
+
agent_mode: 'default',
|
|
221
|
+
};
|
|
222
|
+
const sentResults = [];
|
|
223
|
+
const interruptedRuns = [];
|
|
224
|
+
const routes = new Map();
|
|
225
|
+
const dispatcher = new thread_slash_command_dispatcher_js_1.ThreadSlashCommandDispatcher({
|
|
226
|
+
session: {
|
|
227
|
+
listThreads: () => [],
|
|
228
|
+
getThread: (threadId) => ({ id: threadId, title: row.title, messages: [] }),
|
|
229
|
+
createThread: () => ({ id: 'created-thread', title: 'Created channel thread', messages: [] }),
|
|
230
|
+
renameThread: (threadId, title) => ({ id: threadId, title, messages: [] }),
|
|
231
|
+
deleteThread: () => ({ deleted: true }),
|
|
232
|
+
},
|
|
233
|
+
thread: {
|
|
234
|
+
getThreadRow: () => row,
|
|
235
|
+
updateThreadAgentMode: (_threadId, mode) => {
|
|
236
|
+
row.agent_mode = mode;
|
|
237
|
+
},
|
|
238
|
+
updateThreadAgentType: (_threadId, agentType) => {
|
|
239
|
+
row.agent_type = agentType;
|
|
240
|
+
},
|
|
241
|
+
getLatestRunForThread: () => ({
|
|
242
|
+
id: 'run-channel',
|
|
243
|
+
thread_id: row.id,
|
|
244
|
+
status: 'running',
|
|
245
|
+
started_at: '2026-05-11T00:00:00.000Z',
|
|
246
|
+
updated_at: '2026-05-11T00:00:00.000Z',
|
|
247
|
+
}),
|
|
248
|
+
createAuditEvent: () => { },
|
|
249
|
+
getAgentTypes: () => ['codex', 'pi'],
|
|
250
|
+
interruptRun: async (runId) => {
|
|
251
|
+
interruptedRuns.push(runId);
|
|
252
|
+
return { interrupted: true };
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
const runtime = new session_command_runtime_js_1.ChannelSessionCommandRuntime({
|
|
257
|
+
dispatcher,
|
|
258
|
+
store: {
|
|
259
|
+
getThreadRow: () => row,
|
|
260
|
+
updateThreadAgentMode: (_threadId, mode) => {
|
|
261
|
+
row.agent_mode = mode;
|
|
262
|
+
},
|
|
263
|
+
updateAuthorizedUserThread: () => { },
|
|
264
|
+
upsertPlatformThreadBinding: () => { },
|
|
265
|
+
},
|
|
266
|
+
getThreadSessionKey: (threadId) => `session:${threadId}`,
|
|
267
|
+
setThreadRoute: (sessionKey, route) => {
|
|
268
|
+
routes.set(sessionKey, route);
|
|
269
|
+
},
|
|
270
|
+
createRoute: (input, threadId) => ({
|
|
271
|
+
workspaceId: input.workspaceId,
|
|
272
|
+
instanceId: input.instanceId,
|
|
273
|
+
platformKey: input.platformKey,
|
|
274
|
+
platformUserId: input.platformUserId,
|
|
275
|
+
chatId: input.chatId,
|
|
276
|
+
threadId,
|
|
277
|
+
}),
|
|
278
|
+
sendResult: async (_input, result) => {
|
|
279
|
+
sentResults.push(result);
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
const stopResult = await runtime.execute({
|
|
283
|
+
workspaceId: 'workspace-a',
|
|
284
|
+
currentThreadId: row.id,
|
|
285
|
+
text: '/stop',
|
|
286
|
+
defaultTitle: 'Channel thread',
|
|
287
|
+
defaultAgentType: 'codex',
|
|
288
|
+
chatId: 'chat-1',
|
|
289
|
+
platformUserId: 'user-1',
|
|
290
|
+
platformKey: 'lark',
|
|
291
|
+
instanceId: 'default',
|
|
292
|
+
});
|
|
293
|
+
strict_1.default.equal(stopResult.handled, true);
|
|
294
|
+
strict_1.default.deepEqual(interruptedRuns, ['run-channel']);
|
|
295
|
+
strict_1.default.match(sentResults.at(-1)?.displayText || '', /已请求停止当前任务/);
|
|
296
|
+
const newResult = await runtime.execute({
|
|
297
|
+
workspaceId: 'workspace-a',
|
|
298
|
+
currentThreadId: row.id,
|
|
299
|
+
text: '/new Follow up',
|
|
300
|
+
defaultTitle: 'Channel thread',
|
|
301
|
+
defaultAgentType: 'codex',
|
|
302
|
+
chatId: 'chat-1',
|
|
303
|
+
platformUserId: 'user-1',
|
|
304
|
+
platformKey: 'lark',
|
|
305
|
+
instanceId: 'default',
|
|
306
|
+
});
|
|
307
|
+
strict_1.default.equal(newResult.handled, true);
|
|
308
|
+
strict_1.default.equal(newResult.threadId, 'created-thread');
|
|
309
|
+
strict_1.default.equal(routes.get('session:created-thread')?.threadId, 'created-thread');
|
|
310
|
+
});
|
|
156
311
|
(0, node_test_1.default)('thread agent mode persists with thread state', () => {
|
|
157
312
|
const dir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-mode-'));
|
|
158
313
|
try {
|
|
@@ -193,9 +348,10 @@ const thread_command_service_js_1 = require("../../services/local-ai-core/src/th
|
|
|
193
348
|
const thread = store.createThread('workspace-a', 'Agent command', 'codex');
|
|
194
349
|
const bridgeEvents = [];
|
|
195
350
|
const acceptedMessages = [];
|
|
351
|
+
const runThreadMap = new Map();
|
|
196
352
|
const backend = new local_core_acp_backend_js_1.LocalCoreAcpBackend({
|
|
197
353
|
store,
|
|
198
|
-
runThreadMap
|
|
354
|
+
runThreadMap,
|
|
199
355
|
emitBridge: (event) => bridgeEvents.push(event),
|
|
200
356
|
eventBus: {
|
|
201
357
|
emit: (event) => acceptedMessages.push(event),
|
|
@@ -236,6 +392,13 @@ const thread_command_service_js_1 = require("../../services/local-ai-core/src/th
|
|
|
236
392
|
await backend.sendThreadMessage(thread.id, '/agent use hermes', config);
|
|
237
393
|
strict_1.default.equal(store.getThreadRow(thread.id)?.agent_type, 'hermes');
|
|
238
394
|
strict_1.default.match(store.getThread(thread.id, []).messages.at(-1)?.content || '', /下一轮开始生效/);
|
|
395
|
+
store.updateRun('run-stop', thread.id, 'running');
|
|
396
|
+
runThreadMap.set('run-stop', thread.id);
|
|
397
|
+
await backend.sendThreadMessage(thread.id, '/stop', config);
|
|
398
|
+
strict_1.default.equal(store.getRun('run-stop')?.status, 'interrupted');
|
|
399
|
+
strict_1.default.match(store.getThread(thread.id, []).messages.at(-1)?.content || '', /已将当前任务标记为停止|已请求停止当前任务/);
|
|
400
|
+
await backend.sendThreadMessage(thread.id, '/help', config);
|
|
401
|
+
strict_1.default.match(store.getThread(thread.id, []).messages.at(-1)?.content || '', /`\/stop`/);
|
|
239
402
|
strict_1.default.ok(acceptedMessages.length >= 8);
|
|
240
403
|
backend.close();
|
|
241
404
|
store.close();
|
|
@@ -81,6 +81,40 @@ const server_routes_js_1 = require("../../services/local-ai-core/src/runtime/ser
|
|
|
81
81
|
strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/workspace-security/workspace-1'), null);
|
|
82
82
|
strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/workspaces/workspace-1/streaming-probe'), null);
|
|
83
83
|
});
|
|
84
|
+
(0, node_test_1.default)('local core route parser handles shared model provider routes', () => {
|
|
85
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/providers'), { name: 'providers.list' });
|
|
86
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/providers'), { name: 'providers.create' });
|
|
87
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('PUT', '/api/local/v1/providers/deepseek'), {
|
|
88
|
+
name: 'provider.update',
|
|
89
|
+
providerId: 'deepseek',
|
|
90
|
+
});
|
|
91
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('DELETE', '/api/local/v1/providers/deepseek'), {
|
|
92
|
+
name: 'provider.delete',
|
|
93
|
+
providerId: 'deepseek',
|
|
94
|
+
});
|
|
95
|
+
strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/providers/deepseek/extra'), null);
|
|
96
|
+
});
|
|
97
|
+
(0, node_test_1.default)('local core route parser handles external project and run routes', () => {
|
|
98
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/external/projects'), {
|
|
99
|
+
name: 'external.project.ensure',
|
|
100
|
+
});
|
|
101
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/external/runs'), {
|
|
102
|
+
name: 'external.run.create',
|
|
103
|
+
});
|
|
104
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/external/runs/run%2Fone/events'), {
|
|
105
|
+
name: 'external.run.events',
|
|
106
|
+
runId: 'run/one',
|
|
107
|
+
});
|
|
108
|
+
strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/external/projects'), null);
|
|
109
|
+
strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/external/runs/run-1/events'), null);
|
|
110
|
+
});
|
|
111
|
+
(0, node_test_1.default)('local core route parser handles OpenAI-compatible chat completions route', () => {
|
|
112
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/openai/chat/completions'), {
|
|
113
|
+
name: 'openai.chat.completions',
|
|
114
|
+
});
|
|
115
|
+
strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/openai/chat/completions'), null);
|
|
116
|
+
strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/openai/completions'), null);
|
|
117
|
+
});
|
|
84
118
|
(0, node_test_1.default)('local core route parser keeps approval resolution separate from approval detail', () => {
|
|
85
119
|
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/approvals'), { name: 'approvals.list' });
|
|
86
120
|
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/approvals'), { name: 'approvals.create' });
|