@kafca/agentdock 0.1.24 → 0.1.26
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 +2 -0
- package/dist/renderer/assets/{Badge-CXcUMqb-.js → Badge-qZHTUGI3.js} +1 -1
- package/dist/renderer/assets/{Chat-BRxdPzi-.js → Chat-W35BzaZk.js} +1 -1
- package/dist/renderer/assets/{ChatMarkdown-sWGBIfIb.js → ChatMarkdown-DxKT_YGp.js} +1 -1
- package/dist/renderer/assets/{Config-DqxuxIGg.js → Config-COTX6LDN.js} +1 -1
- package/dist/renderer/assets/{CronList-OgHxbti5.js → CronList-kxlfu9Iq.js} +1 -1
- package/dist/renderer/assets/{Dashboard-DmklXiH1.js → Dashboard-Y4PGxzuV.js} +2 -2
- package/dist/renderer/assets/{EmptyState-BvDmi8dq.js → EmptyState-Cjq33EOF.js} +1 -1
- package/dist/renderer/assets/{KnowledgeDetail-Bj59Hdpo.js → KnowledgeDetail-CztNt7i8.js} +1 -1
- package/dist/renderer/assets/{KnowledgeHome-CigQ-nSo.js → KnowledgeHome-BAOyy3I9.js} +1 -1
- package/dist/renderer/assets/{Logs-Bfc7gBpY.js → Logs-PRZ1IIEi.js} +1 -1
- package/dist/renderer/assets/{Modal-CfmiFknz.js → Modal-B1rgqkBM.js} +1 -1
- package/dist/renderer/assets/{Page-BxtLEw_b.js → Page-CjzAhgaU.js} +1 -1
- package/dist/renderer/assets/{ProjectDetail-CcUphxGm.js → ProjectDetail-XGh_5XxQ.js} +1 -1
- package/dist/renderer/assets/{ProjectList-CVkvDIfy.js → ProjectList-BkPTpQRZ.js} +1 -1
- package/dist/renderer/assets/{Select-DgQMZ0MD.js → Select-BZd1QJVw.js} +1 -1
- package/dist/renderer/assets/{SessionChat-CRmGKfBx.js → SessionChat-C3sOTWGT.js} +1 -1
- package/dist/renderer/assets/{SessionList-D0E8XbYw.js → SessionList-B5WK1big.js} +1 -1
- package/dist/renderer/assets/ThreadChat-DWiBLHC7.js +23 -0
- package/dist/renderer/assets/{Workspace-Q4g4rlRT.js → Workspace-CQ64VK-o.js} +1 -1
- package/dist/renderer/assets/{arrow-left-BySN_zhF.js → arrow-left-FLdaWHZ8.js} +1 -1
- package/dist/renderer/assets/{book-open-BGlrnrTc.js → book-open-DaSioPS9.js} +1 -1
- package/dist/renderer/assets/{check-BiJogEa5.js → check-BiSmsuGR.js} +1 -1
- package/dist/renderer/assets/{chevron-down-D2fHRJv6.js → chevron-down-pZ2KjmDR.js} +1 -1
- package/dist/renderer/assets/{circle-CIcjixmI.js → circle-296S3nw9.js} +1 -1
- package/dist/renderer/assets/{desktop-DyA_NYew.js → desktop-88jgnOZV.js} +1 -1
- package/dist/renderer/assets/{desktop-DAXY9Bxv.js → desktop-BkMW-h5p.js} +2 -2
- package/dist/renderer/assets/{funnel-BUzwb-id.js → funnel-Dvz31PLy.js} +1 -1
- package/dist/renderer/assets/{heart-DZEQ1z_V.js → heart-D9mxSZVO.js} +1 -1
- package/dist/renderer/assets/{index-DgsIo4vJ.js → index-CZA4lqvM.js} +1 -1
- package/dist/renderer/assets/{index-CB_KyLOr.js → index-dOdOWAa2.js} +3 -3
- package/dist/renderer/assets/{pencil-DGL4agcl.js → pencil-B2fRbaAo.js} +1 -1
- package/dist/renderer/assets/{play-DzasCLM4.js → play-CWPRHypD.js} +1 -1
- package/dist/renderer/assets/{plug-dubNMe_m.js → plug-B7IBVwSo.js} +1 -1
- package/dist/renderer/assets/{plus-DN4kML4k.js → plus-Cn3Awirs.js} +1 -1
- package/dist/renderer/assets/projects-CmCopAi6.js +1 -0
- package/dist/renderer/assets/{save-CNIc9tjl.js → save-MVjXRqom.js} +1 -1
- package/dist/renderer/assets/{search-C0EGbF3I.js → search-Bu5c7wTX.js} +1 -1
- package/dist/renderer/assets/{server-BMxQ4zmc.js → server-aDvfqBg_.js} +1 -1
- package/dist/renderer/assets/{session-utils-Bf5O-iMi.js → session-utils-DjDpjT9r.js} +1 -1
- package/dist/renderer/assets/{sessions-BMSs6rEI.js → sessions-D7dV8h0H.js} +1 -1
- package/dist/renderer/assets/{shield-check-i3Tf1TUz.js → shield-check-DJdEhc_e.js} +1 -1
- package/dist/renderer/assets/{trash-2-SKrcp18f.js → trash-2-BF3ejnXL.js} +1 -1
- package/dist/renderer/assets/{user-cNUZddjZ.js → user-BwuWCrin.js} +1 -1
- package/dist/renderer/assets/{wifi-off-Dyb84ANR.js → wifi-off-BahJZfDs.js} +1 -1
- package/dist/renderer/index.html +1 -1
- package/dist-electron/electron/agent-runtime-detector.test.js +64 -0
- package/dist-electron/electron/architecture-docs.test.js +56 -0
- package/dist-electron/electron/channel-content.test.js +8 -0
- package/dist-electron/electron/lac-cli.test.js +248 -0
- package/dist-electron/electron/local-core-refactor.test.js +795 -20
- package/dist-electron/electron/plugin-kernel.test.js +108 -0
- package/dist-electron/electron/renderer-packaging.test.js +77 -0
- package/dist-electron/electron/workspace-task-store.test.js +57 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +67 -6
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-permission-lifecycle.js +76 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-progress.js +201 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-session-coordinator.js +29 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-store.js +30 -18
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +112 -210
- package/dist-electron/services/local-ai-core/src/acp/local-core-slash-commands.js +60 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +44 -27
- package/dist-electron/services/local-ai-core/src/channel/lark/runtime-state.js +104 -12
- package/dist-electron/services/local-ai-core/src/channel/shared/file-utils.js +13 -1
- package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +8 -1
- package/dist-electron/services/local-ai-core/src/cli/lac.js +3 -1
- package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +1 -0
- package/dist-electron/services/local-ai-core/src/plugins/builtin/agent-localcore-acp-plugin.js +9 -0
- package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +221 -10
- package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +39 -9
- package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +1 -0
- package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-service.js +1 -0
- package/dist-electron/services/local-ai-core/src/runtime/server-routes.js +439 -0
- package/dist-electron/services/local-ai-core/src/runtime/server.js +208 -303
- package/dist-electron/shared/desktop.js +7 -3
- package/dist-electron/src/pages/Threads/thread-chat-message-blocks.js +187 -0
- package/dist-electron/src/pages/Threads/thread-chat-model.js +1 -0
- package/dist-electron/src/pages/Threads/thread-chat-page-state.js +96 -0
- package/dist-electron/src/pages/Threads/thread-chat-permission.js +18 -0
- package/dist-electron/src/pages/Threads/thread-chat-permission.test.js +277 -0
- package/package.json +4 -2
- package/dist/renderer/assets/ThreadChat-DUgqZSBB.js +0 -23
- package/dist/renderer/assets/projects-migPtfRe.js +0 -1
|
@@ -10,15 +10,215 @@ const node_os_1 = require("node:os");
|
|
|
10
10
|
const node_path_1 = require("node:path");
|
|
11
11
|
const node_stream_1 = require("node:stream");
|
|
12
12
|
const local_core_acp_response_processor_js_1 = require("../services/local-ai-core/src/acp/local-core-acp-response-processor.js");
|
|
13
|
+
const local_core_slash_commands_js_1 = require("../services/local-ai-core/src/acp/local-core-slash-commands.js");
|
|
13
14
|
const scheduled_conversation_executor_js_1 = require("../services/local-ai-core/src/scheduler/scheduled-conversation-executor.js");
|
|
14
15
|
const scheduler_run_lifecycle_js_1 = require("../services/local-ai-core/src/scheduler/scheduler-run-lifecycle.js");
|
|
15
16
|
const lark_execution_policies_js_1 = require("../services/local-ai-core/src/scheduler/lark-execution-policies.js");
|
|
16
17
|
const local_schedule_adapter_js_1 = require("../services/local-ai-core/src/scheduler/local-schedule-adapter.js");
|
|
17
18
|
const local_core_weixin_gateway_js_1 = require("../services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js");
|
|
18
19
|
const local_core_lark_gateway_js_1 = require("../services/local-ai-core/src/channel/lark/local-core-lark-gateway.js");
|
|
20
|
+
const runtime_state_js_1 = require("../services/local-ai-core/src/channel/lark/runtime-state.js");
|
|
19
21
|
const local_core_acp_turn_coordinator_js_1 = require("../services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js");
|
|
20
22
|
const local_core_acp_store_js_1 = require("../services/local-ai-core/src/acp/local-core-acp-store.js");
|
|
23
|
+
const local_core_acp_progress_js_1 = require("../services/local-ai-core/src/acp/local-core-acp-progress.js");
|
|
24
|
+
const local_core_acp_permission_lifecycle_js_1 = require("../services/local-ai-core/src/acp/local-core-acp-permission-lifecycle.js");
|
|
21
25
|
const workspace_acp_permissions_js_1 = require("../services/local-ai-core/src/acp/workspace-acp-permissions.js");
|
|
26
|
+
const server_routes_js_1 = require("../services/local-ai-core/src/runtime/server-routes.js");
|
|
27
|
+
(0, node_test_1.default)('local core route parser separates runtime refresh and runtime detail routes', () => {
|
|
28
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/runtimes'), { name: 'runtimes.list' });
|
|
29
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/runtimes/refresh'), { name: 'runtimes.refresh' });
|
|
30
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/runtimes/codex'), {
|
|
31
|
+
name: 'runtimes.detail',
|
|
32
|
+
runtimeId: 'codex',
|
|
33
|
+
});
|
|
34
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/runtimes/codex/refresh'), {
|
|
35
|
+
name: 'runtimes.refresh-one',
|
|
36
|
+
runtimeId: 'codex',
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
(0, node_test_1.default)('local core route parser keeps scheduler job get, runs, and run distinct', () => {
|
|
40
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/scheduler/jobs/job-abc'), {
|
|
41
|
+
name: 'scheduler.job.get',
|
|
42
|
+
jobId: 'job-abc',
|
|
43
|
+
});
|
|
44
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/scheduler/jobs/job-abc/runs'), {
|
|
45
|
+
name: 'scheduler.job.runs',
|
|
46
|
+
jobId: 'job-abc',
|
|
47
|
+
});
|
|
48
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/scheduler/jobs/job-abc/run'), {
|
|
49
|
+
name: 'scheduler.job.run',
|
|
50
|
+
jobId: 'job-abc',
|
|
51
|
+
});
|
|
52
|
+
strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/scheduler/jobs/job-abc/run'), null);
|
|
53
|
+
});
|
|
54
|
+
(0, node_test_1.default)('local core route parser keeps thread actions separate from generic thread routes', () => {
|
|
55
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/threads'), { name: 'threads.list' });
|
|
56
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/threads'), { name: 'threads.create' });
|
|
57
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/threads/workspace%2Fa%3A%3Athread%2F1'), {
|
|
58
|
+
name: 'thread.get',
|
|
59
|
+
threadId: 'workspace/a::thread/1',
|
|
60
|
+
});
|
|
61
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('PATCH', '/api/local/v1/threads/thread-1/knowledge-bases'), {
|
|
62
|
+
name: 'thread.update-knowledge-bases',
|
|
63
|
+
threadId: 'thread-1',
|
|
64
|
+
});
|
|
65
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/threads/thread-1/messages'), {
|
|
66
|
+
name: 'thread.messages.send',
|
|
67
|
+
threadId: 'thread-1',
|
|
68
|
+
});
|
|
69
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/threads/thread-1/actions'), {
|
|
70
|
+
name: 'thread.actions.send',
|
|
71
|
+
threadId: 'thread-1',
|
|
72
|
+
});
|
|
73
|
+
strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/threads/thread-1/messages'), null);
|
|
74
|
+
});
|
|
75
|
+
(0, node_test_1.default)('local core route parser only accepts run interrupt action with POST', () => {
|
|
76
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/runs/run-1/interrupt'), {
|
|
77
|
+
name: 'run.interrupt',
|
|
78
|
+
runId: 'run-1',
|
|
79
|
+
});
|
|
80
|
+
strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/runs/run-1/interrupt'), null);
|
|
81
|
+
});
|
|
82
|
+
(0, node_test_1.default)('local core route parser keeps workspace state routes bounded to one id segment', () => {
|
|
83
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/workspaces'), { name: 'workspaces.list' });
|
|
84
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/workspaces/workspace%2Fone/streaming-probe'), {
|
|
85
|
+
name: 'workspace.streaming-probe',
|
|
86
|
+
workspaceId: 'workspace/one',
|
|
87
|
+
});
|
|
88
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/workspace-registry'), { name: 'workspace-registry.create' });
|
|
89
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('PATCH', '/api/local/v1/workspace-registry/workspace%2Fone'), {
|
|
90
|
+
name: 'workspace-registry.update',
|
|
91
|
+
workspaceId: 'workspace/one',
|
|
92
|
+
});
|
|
93
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/workspace-security/workspace%2Fone'), {
|
|
94
|
+
name: 'workspace-security.get',
|
|
95
|
+
workspaceId: 'workspace/one',
|
|
96
|
+
});
|
|
97
|
+
strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/workspace-registry/workspace-1/extra'), null);
|
|
98
|
+
strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/workspace-security/workspace-1'), null);
|
|
99
|
+
strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/workspaces/workspace-1/streaming-probe'), null);
|
|
100
|
+
});
|
|
101
|
+
(0, node_test_1.default)('local core route parser keeps approval resolution separate from approval detail', () => {
|
|
102
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/approvals'), { name: 'approvals.list' });
|
|
103
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/approvals'), { name: 'approvals.create' });
|
|
104
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/approvals/approval-1'), {
|
|
105
|
+
name: 'approval.get',
|
|
106
|
+
approvalId: 'approval-1',
|
|
107
|
+
});
|
|
108
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/approvals/approval-1/resolve'), {
|
|
109
|
+
name: 'approval.resolve',
|
|
110
|
+
approvalId: 'approval-1',
|
|
111
|
+
});
|
|
112
|
+
strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/approvals/approval-1/resolve'), null);
|
|
113
|
+
});
|
|
114
|
+
(0, node_test_1.default)('local core route parser keeps task collection and task detail routes distinct', () => {
|
|
115
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/audit-events'), { name: 'audit-events.list' });
|
|
116
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/security/command-risk'), { name: 'security.command-risk.classify' });
|
|
117
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/tasks'), { name: 'tasks.list' });
|
|
118
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/tasks'), { name: 'tasks.create' });
|
|
119
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('PATCH', '/api/local/v1/tasks/task%2Fone'), {
|
|
120
|
+
name: 'task.update',
|
|
121
|
+
taskId: 'task/one',
|
|
122
|
+
});
|
|
123
|
+
strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('DELETE', '/api/local/v1/tasks/task-1'), null);
|
|
124
|
+
});
|
|
125
|
+
(0, node_test_1.default)('local core route parser keeps knowledge collection and folder routes distinct', () => {
|
|
126
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/knowledge/sources'), { name: 'knowledge.sources.list' });
|
|
127
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/knowledge/config'), { name: 'knowledge.config.read' });
|
|
128
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/knowledge/config'), { name: 'knowledge.config.update' });
|
|
129
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/knowledge/folders'), { name: 'knowledge.folders.list' });
|
|
130
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/knowledge/folders'), { name: 'knowledge.folders.create' });
|
|
131
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('PATCH', '/api/local/v1/knowledge/folders/folder%2Fone'), {
|
|
132
|
+
name: 'knowledge.folder.update',
|
|
133
|
+
folderId: 'folder/one',
|
|
134
|
+
});
|
|
135
|
+
strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/knowledge/folders/folder-1'), null);
|
|
136
|
+
});
|
|
137
|
+
(0, node_test_1.default)('local core route parser keeps knowledge base files and search routes distinct', () => {
|
|
138
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/knowledge/bases'), { name: 'knowledge.bases.list' });
|
|
139
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/knowledge/bases'), { name: 'knowledge.bases.create' });
|
|
140
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/knowledge/bases/base%2Fone'), {
|
|
141
|
+
name: 'knowledge.base.get',
|
|
142
|
+
knowledgeBaseId: 'base/one',
|
|
143
|
+
});
|
|
144
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/knowledge/bases/base%2Fone/files'), {
|
|
145
|
+
name: 'knowledge.base.files.list',
|
|
146
|
+
knowledgeBaseId: 'base/one',
|
|
147
|
+
});
|
|
148
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/knowledge/bases/base%2Fone/search'), {
|
|
149
|
+
name: 'knowledge.base.search',
|
|
150
|
+
knowledgeBaseId: 'base/one',
|
|
151
|
+
});
|
|
152
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('DELETE', '/api/local/v1/knowledge/bases/base%2Fone/files/file%2Fone'), {
|
|
153
|
+
name: 'knowledge.base.file.delete',
|
|
154
|
+
knowledgeBaseId: 'base/one',
|
|
155
|
+
fileId: 'file/one',
|
|
156
|
+
});
|
|
157
|
+
strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('PATCH', '/api/local/v1/knowledge/bases/base-1/files'), null);
|
|
158
|
+
});
|
|
159
|
+
(0, node_test_1.default)('local core route parser recognizes capability, plugin, and event routes', () => {
|
|
160
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/capabilities'), { name: 'capabilities.read' });
|
|
161
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/capabilities/snapshot'), { name: 'capabilities.snapshot' });
|
|
162
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/plugins/diagnostics'), { name: 'plugins.diagnostics' });
|
|
163
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/events'), { name: 'events.stream' });
|
|
164
|
+
strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/events'), null);
|
|
165
|
+
});
|
|
166
|
+
(0, node_test_1.default)('local core route parser keeps platform read routes distinct', () => {
|
|
167
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/platforms/lark'), {
|
|
168
|
+
name: 'platform.gateways.list',
|
|
169
|
+
platform: 'lark',
|
|
170
|
+
});
|
|
171
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/platforms/lark/pairings'), {
|
|
172
|
+
name: 'platform.pairings.list',
|
|
173
|
+
platform: 'lark',
|
|
174
|
+
});
|
|
175
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/platforms/lark/users'), {
|
|
176
|
+
name: 'platform.users.list',
|
|
177
|
+
platform: 'lark',
|
|
178
|
+
});
|
|
179
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/platforms/lark/workspace%2Fone'), {
|
|
180
|
+
name: 'platform.gateway.get',
|
|
181
|
+
platform: 'lark',
|
|
182
|
+
workspaceId: 'workspace/one',
|
|
183
|
+
});
|
|
184
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/platforms/lark/workspace%2Fone/qrcode/status'), {
|
|
185
|
+
name: 'platform.qrcode.status',
|
|
186
|
+
platform: 'lark',
|
|
187
|
+
workspaceId: 'workspace/one',
|
|
188
|
+
});
|
|
189
|
+
strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('GET', '/api/local/v1/platforms/lark/workspace-1/qrcode'), null);
|
|
190
|
+
});
|
|
191
|
+
(0, node_test_1.default)('local core route parser keeps platform write routes distinct', () => {
|
|
192
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/platforms/lark/pairings/approve'), {
|
|
193
|
+
name: 'platform.pairing.approve',
|
|
194
|
+
platform: 'lark',
|
|
195
|
+
});
|
|
196
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/platforms/lark/pairings/reject'), {
|
|
197
|
+
name: 'platform.pairing.reject',
|
|
198
|
+
platform: 'lark',
|
|
199
|
+
});
|
|
200
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/platforms/lark/workspace%2Fone/test'), {
|
|
201
|
+
name: 'platform.gateway.test',
|
|
202
|
+
platform: 'lark',
|
|
203
|
+
workspaceId: 'workspace/one',
|
|
204
|
+
});
|
|
205
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/platforms/lark/workspace%2Fone/files'), {
|
|
206
|
+
name: 'platform.file.send',
|
|
207
|
+
platform: 'lark',
|
|
208
|
+
workspaceId: 'workspace/one',
|
|
209
|
+
});
|
|
210
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/platforms/lark/workspace%2Fone/messages'), {
|
|
211
|
+
name: 'platform.message.send',
|
|
212
|
+
platform: 'lark',
|
|
213
|
+
workspaceId: 'workspace/one',
|
|
214
|
+
});
|
|
215
|
+
strict_1.default.deepEqual((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/platforms/lark/workspace%2Fone/qrcode'), {
|
|
216
|
+
name: 'platform.qrcode.create',
|
|
217
|
+
platform: 'lark',
|
|
218
|
+
workspaceId: 'workspace/one',
|
|
219
|
+
});
|
|
220
|
+
strict_1.default.equal((0, server_routes_js_1.parseLocalAiCoreRoute)('POST', '/api/local/v1/platforms/lark/pairings/qrcode'), null);
|
|
221
|
+
});
|
|
22
222
|
(0, node_test_1.default)('ACP tool call update is emitted with its pending tool name', () => {
|
|
23
223
|
const appended = [];
|
|
24
224
|
const emitted = [];
|
|
@@ -95,6 +295,271 @@ const workspace_acp_permissions_js_1 = require("../services/local-ai-core/src/ac
|
|
|
95
295
|
kind: 'allow',
|
|
96
296
|
}), 'allow');
|
|
97
297
|
});
|
|
298
|
+
(0, node_test_1.default)('ACP permission lifecycle parses actionable options and fallback prompt content', () => {
|
|
299
|
+
const options = (0, local_core_acp_permission_lifecycle_js_1.parsePermissionOptions)([
|
|
300
|
+
{ optionId: 'approve-once', name: 'Allow once', kind: 'allow' },
|
|
301
|
+
{ optionId: 'approve-always', name: 'Always allow', kind: 'allow' },
|
|
302
|
+
{ optionId: '', name: 'missing id', kind: 'reject' },
|
|
303
|
+
]);
|
|
304
|
+
strict_1.default.deepEqual(options, [
|
|
305
|
+
{
|
|
306
|
+
optionId: 'approve-once',
|
|
307
|
+
name: 'Allow once',
|
|
308
|
+
kind: 'allow',
|
|
309
|
+
normalizedAction: 'allow',
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
optionId: 'approve-always',
|
|
313
|
+
name: 'Always allow',
|
|
314
|
+
kind: 'allow',
|
|
315
|
+
normalizedAction: 'allow all',
|
|
316
|
+
},
|
|
317
|
+
]);
|
|
318
|
+
strict_1.default.deepEqual((0, local_core_acp_permission_lifecycle_js_1.createRunningPermissionRequest)({
|
|
319
|
+
requestId: 42,
|
|
320
|
+
toolTitle: 'Terminal: lac scheduler add --cron "* * * * *"',
|
|
321
|
+
options,
|
|
322
|
+
approvalId: 'approval-1',
|
|
323
|
+
}), {
|
|
324
|
+
requestId: 42,
|
|
325
|
+
approvalId: 'approval-1',
|
|
326
|
+
toolTitle: 'Terminal: lac scheduler add --cron "* * * * *"',
|
|
327
|
+
isSchedulerAdd: true,
|
|
328
|
+
options,
|
|
329
|
+
});
|
|
330
|
+
strict_1.default.deepEqual((0, local_core_acp_permission_lifecycle_js_1.createPermissionApprovalInput)({
|
|
331
|
+
threadId: 'thread-1',
|
|
332
|
+
runId: 'run-1',
|
|
333
|
+
toolTitle: 'Terminal: npm test',
|
|
334
|
+
options,
|
|
335
|
+
}), {
|
|
336
|
+
threadId: 'thread-1',
|
|
337
|
+
runId: 'run-1',
|
|
338
|
+
title: 'Approve Terminal: npm test',
|
|
339
|
+
description: 'Terminal: npm test',
|
|
340
|
+
command: 'Terminal: npm test',
|
|
341
|
+
options,
|
|
342
|
+
});
|
|
343
|
+
strict_1.default.equal((0, local_core_acp_permission_lifecycle_js_1.isSchedulerAddCommand)('Terminal: lac scheduler add --cron "* * * * *"'), true);
|
|
344
|
+
strict_1.default.match((0, local_core_acp_permission_lifecycle_js_1.createPermissionPrompt)('Terminal: npm test'), /Terminal: npm test/);
|
|
345
|
+
strict_1.default.match((0, local_core_acp_permission_lifecycle_js_1.createPermissionPrompt)('Terminal: npm test'), /allow all \/ allow \/ deny/);
|
|
346
|
+
});
|
|
347
|
+
(0, node_test_1.default)('ACP permission lifecycle writes pending permission state and tool detail together', () => {
|
|
348
|
+
const permissionRequest = (0, local_core_acp_permission_lifecycle_js_1.createRunningPermissionRequest)({
|
|
349
|
+
requestId: 42,
|
|
350
|
+
toolTitle: 'Terminal: npm test',
|
|
351
|
+
options: [],
|
|
352
|
+
approvalId: 'approval-1',
|
|
353
|
+
});
|
|
354
|
+
const toolCall = {
|
|
355
|
+
key: 'call-1',
|
|
356
|
+
title: 'Terminal',
|
|
357
|
+
messageId: 'run-1-tool-1',
|
|
358
|
+
sequence: 1,
|
|
359
|
+
emitted: false,
|
|
360
|
+
};
|
|
361
|
+
const currentTurn = {
|
|
362
|
+
runId: 'run-1',
|
|
363
|
+
replyCtx: 'run-1',
|
|
364
|
+
previewHandle: 'preview-1',
|
|
365
|
+
thoughtPreviewHandle: 'thought-preview-1',
|
|
366
|
+
thoughtMessageId: 'run-1-thought',
|
|
367
|
+
assistantText: '',
|
|
368
|
+
thoughtText: '',
|
|
369
|
+
typingStarted: true,
|
|
370
|
+
previewStarted: false,
|
|
371
|
+
thoughtPreviewStarted: false,
|
|
372
|
+
permission: null,
|
|
373
|
+
pendingToolCalls: { 'call-1': toolCall },
|
|
374
|
+
pendingToolCallOrder: ['call-1'],
|
|
375
|
+
activeToolCallKey: 'call-1',
|
|
376
|
+
};
|
|
377
|
+
const session = {
|
|
378
|
+
currentTurn,
|
|
379
|
+
pendingPermissionByRun: new Map(),
|
|
380
|
+
};
|
|
381
|
+
const synced = [];
|
|
382
|
+
(0, local_core_acp_permission_lifecycle_js_1.applyPendingPermissionRequest)({
|
|
383
|
+
session,
|
|
384
|
+
runId: 'run-1',
|
|
385
|
+
permissionRequest,
|
|
386
|
+
resolveFallbackToolCall: () => toolCall,
|
|
387
|
+
syncLegacyPendingToolCall: (_turn, nextToolCall) => synced.push(nextToolCall?.detail || ''),
|
|
388
|
+
});
|
|
389
|
+
strict_1.default.equal(session.pendingPermissionByRun.get('run-1'), permissionRequest);
|
|
390
|
+
strict_1.default.equal(currentTurn.permission, permissionRequest);
|
|
391
|
+
strict_1.default.equal(currentTurn.pendingToolCallDetail, 'Terminal: npm test');
|
|
392
|
+
strict_1.default.equal(toolCall.detail, 'Terminal: npm test');
|
|
393
|
+
strict_1.default.deepEqual(synced, ['Terminal: npm test']);
|
|
394
|
+
});
|
|
395
|
+
(0, node_test_1.default)('ACP pending permission is projected into refreshed thread detail payloads', () => {
|
|
396
|
+
const coordinator = new local_core_acp_turn_coordinator_js_1.LocalCoreAcpTurnCoordinator({
|
|
397
|
+
emitBridge: () => { },
|
|
398
|
+
appendMessage: () => { },
|
|
399
|
+
updateRunStatus: () => { },
|
|
400
|
+
sendRaw: () => true,
|
|
401
|
+
});
|
|
402
|
+
const permissionRequest = (0, local_core_acp_permission_lifecycle_js_1.createRunningPermissionRequest)({
|
|
403
|
+
requestId: 42,
|
|
404
|
+
toolTitle: 'Terminal: npm test',
|
|
405
|
+
options: (0, local_core_acp_permission_lifecycle_js_1.parsePermissionOptions)([
|
|
406
|
+
{ optionId: 'approve-once', name: 'Allow once', kind: 'allow' },
|
|
407
|
+
{ optionId: 'reject', name: 'Deny', kind: 'reject' },
|
|
408
|
+
]),
|
|
409
|
+
approvalId: 'approval-1',
|
|
410
|
+
});
|
|
411
|
+
const session = {
|
|
412
|
+
currentRunId: 'run-1',
|
|
413
|
+
pendingPermissionByRun: new Map([['run-1', permissionRequest]]),
|
|
414
|
+
};
|
|
415
|
+
const detail = {
|
|
416
|
+
messages: [
|
|
417
|
+
{
|
|
418
|
+
id: 'permission-message',
|
|
419
|
+
role: 'assistant',
|
|
420
|
+
content: '等待工具确认',
|
|
421
|
+
},
|
|
422
|
+
],
|
|
423
|
+
};
|
|
424
|
+
const pending = coordinator.getPendingPermissionRequest(session, detail);
|
|
425
|
+
strict_1.default.ok(pending);
|
|
426
|
+
strict_1.default.equal(pending.id, 'permission-message');
|
|
427
|
+
strict_1.default.equal(pending.content, '等待工具确认');
|
|
428
|
+
strict_1.default.equal(pending.actionReplyCtx, 'run-1');
|
|
429
|
+
strict_1.default.equal(pending.actionMode, 'permission');
|
|
430
|
+
strict_1.default.equal(pending.actionInteractive, true);
|
|
431
|
+
strict_1.default.deepEqual(pending.actions.flat().map((action) => action.data), ['allow', 'deny']);
|
|
432
|
+
});
|
|
433
|
+
(0, node_test_1.default)('ACP progress projection extracts tool output and formats durable progress content', () => {
|
|
434
|
+
strict_1.default.equal((0, local_core_acp_progress_js_1.extractToolCallKey)({ tool_call_id: ' call-a ' }), 'call-a');
|
|
435
|
+
strict_1.default.equal((0, local_core_acp_progress_js_1.extractToolCallKey)({ invocationId: 42 }), '42');
|
|
436
|
+
strict_1.default.equal((0, local_core_acp_progress_js_1.extractToolCallKey)({ id: ' ' }), '');
|
|
437
|
+
strict_1.default.equal((0, local_core_acp_progress_js_1.extractToolUpdateContent)([
|
|
438
|
+
{ type: 'content', content: { type: 'text', text: 'first line' } },
|
|
439
|
+
{ type: 'content', content: { type: 'image', text: 'ignored' } },
|
|
440
|
+
{ type: 'content', content: { type: 'text', text: 'second line' } },
|
|
441
|
+
]), 'first line\nsecond line');
|
|
442
|
+
strict_1.default.equal((0, local_core_acp_progress_js_1.formatToolProgressMessage)({
|
|
443
|
+
toolName: 'Terminal',
|
|
444
|
+
title: 'npm test',
|
|
445
|
+
status: 'completed',
|
|
446
|
+
content: 'ok',
|
|
447
|
+
}), '🔧 Terminal: npm test - completed - ok');
|
|
448
|
+
strict_1.default.equal((0, local_core_acp_progress_js_1.resolveToolUpdateDisplayTitle)({
|
|
449
|
+
title: 'Tool update',
|
|
450
|
+
status: 'completed',
|
|
451
|
+
priorDetail: 'npm test',
|
|
452
|
+
}), 'npm test');
|
|
453
|
+
strict_1.default.equal((0, local_core_acp_progress_js_1.isEmptyRunningToolUpdate)({
|
|
454
|
+
title: 'Tool update',
|
|
455
|
+
status: 'running',
|
|
456
|
+
content: '',
|
|
457
|
+
}), true);
|
|
458
|
+
});
|
|
459
|
+
(0, node_test_1.default)('ACP progress projection applies assistant and thought chunks with bridge metadata', () => {
|
|
460
|
+
const currentTurn = {
|
|
461
|
+
previewHandle: 'preview-1',
|
|
462
|
+
thoughtPreviewHandle: 'thought-preview-1',
|
|
463
|
+
thoughtMessageId: 'run-1-thought',
|
|
464
|
+
assistantText: '',
|
|
465
|
+
thoughtText: '',
|
|
466
|
+
previewStarted: false,
|
|
467
|
+
thoughtPreviewStarted: false,
|
|
468
|
+
};
|
|
469
|
+
strict_1.default.deepEqual((0, local_core_acp_progress_js_1.applyAssistantMessageChunk)(currentTurn, 'hello'), {
|
|
470
|
+
bridgeType: 'preview_start',
|
|
471
|
+
previewHandle: 'preview-1',
|
|
472
|
+
content: 'hello',
|
|
473
|
+
});
|
|
474
|
+
strict_1.default.deepEqual((0, local_core_acp_progress_js_1.applyAssistantMessageChunk)(currentTurn, ' world'), {
|
|
475
|
+
bridgeType: 'update_message',
|
|
476
|
+
previewHandle: 'preview-1',
|
|
477
|
+
content: 'hello world',
|
|
478
|
+
});
|
|
479
|
+
strict_1.default.deepEqual((0, local_core_acp_progress_js_1.applyThoughtChunk)(currentTurn, '先理解'), {
|
|
480
|
+
bridgeType: 'preview_start',
|
|
481
|
+
previewHandle: 'thought-preview-1',
|
|
482
|
+
messageId: 'run-1-thought',
|
|
483
|
+
content: '💭 先理解',
|
|
484
|
+
});
|
|
485
|
+
strict_1.default.deepEqual((0, local_core_acp_progress_js_1.applyThoughtChunk)(currentTurn, ',再修改'), {
|
|
486
|
+
bridgeType: 'update_message',
|
|
487
|
+
previewHandle: 'thought-preview-1',
|
|
488
|
+
messageId: 'run-1-thought',
|
|
489
|
+
content: '💭 先理解,再修改',
|
|
490
|
+
});
|
|
491
|
+
});
|
|
492
|
+
(0, node_test_1.default)('ACP thought chunks merge provider snapshots without duplicating text', () => {
|
|
493
|
+
const currentTurn = {
|
|
494
|
+
thoughtPreviewHandle: 'thought-preview-1',
|
|
495
|
+
thoughtMessageId: 'run-1-thought',
|
|
496
|
+
thoughtText: '',
|
|
497
|
+
thoughtPreviewStarted: false,
|
|
498
|
+
};
|
|
499
|
+
strict_1.default.deepEqual((0, local_core_acp_progress_js_1.applyThoughtChunk)(currentTurn, 'The user wants to see their desktop files.'), {
|
|
500
|
+
bridgeType: 'preview_start',
|
|
501
|
+
previewHandle: 'thought-preview-1',
|
|
502
|
+
messageId: 'run-1-thought',
|
|
503
|
+
content: '💭 The user wants to see their desktop files.',
|
|
504
|
+
});
|
|
505
|
+
strict_1.default.deepEqual((0, local_core_acp_progress_js_1.applyThoughtChunk)(currentTurn, 'The user wants to see their desktop files. Let me show them.'), {
|
|
506
|
+
bridgeType: 'update_message',
|
|
507
|
+
previewHandle: 'thought-preview-1',
|
|
508
|
+
messageId: 'run-1-thought',
|
|
509
|
+
content: '💭 The user wants to see their desktop files. Let me show them.',
|
|
510
|
+
});
|
|
511
|
+
strict_1.default.equal(currentTurn.thoughtText, 'The user wants to see their desktop files. Let me show them.');
|
|
512
|
+
});
|
|
513
|
+
(0, node_test_1.default)('ACP progress projection registers pending tool calls in order', () => {
|
|
514
|
+
const currentTurn = {
|
|
515
|
+
toolCallSequence: 0,
|
|
516
|
+
pendingToolCalls: {},
|
|
517
|
+
pendingToolCallOrder: [],
|
|
518
|
+
};
|
|
519
|
+
strict_1.default.deepEqual((0, local_core_acp_progress_js_1.registerPendingToolCall)({
|
|
520
|
+
currentTurn,
|
|
521
|
+
runId: 'run-1',
|
|
522
|
+
update: { id: 'call-a', title: 'Terminal', rawInput: { command: 'npm test' } },
|
|
523
|
+
}), {
|
|
524
|
+
key: 'call-a',
|
|
525
|
+
title: 'Terminal',
|
|
526
|
+
messageId: 'run-1-tool-1',
|
|
527
|
+
input: { command: 'npm test' },
|
|
528
|
+
sequence: 1,
|
|
529
|
+
emitted: false,
|
|
530
|
+
});
|
|
531
|
+
strict_1.default.deepEqual((0, local_core_acp_progress_js_1.registerPendingToolCall)({
|
|
532
|
+
currentTurn,
|
|
533
|
+
runId: 'run-1',
|
|
534
|
+
update: { title: 'Read' },
|
|
535
|
+
}), {
|
|
536
|
+
key: 'sequence:2',
|
|
537
|
+
title: 'Read',
|
|
538
|
+
messageId: 'run-1-tool-2',
|
|
539
|
+
sequence: 2,
|
|
540
|
+
emitted: false,
|
|
541
|
+
});
|
|
542
|
+
strict_1.default.deepEqual(currentTurn.pendingToolCallOrder, ['call-a', 'sequence:2']);
|
|
543
|
+
strict_1.default.equal(currentTurn.activeToolCallKey, 'sequence:2');
|
|
544
|
+
strict_1.default.deepEqual((0, local_core_acp_progress_js_1.getToolCallsInOrder)(currentTurn).map((toolCall) => toolCall.key), ['call-a', 'sequence:2']);
|
|
545
|
+
strict_1.default.equal((0, local_core_acp_progress_js_1.resolveFallbackToolCall)(currentTurn)?.key, 'sequence:2');
|
|
546
|
+
strict_1.default.equal((0, local_core_acp_progress_js_1.resolveToolCallForUpdate)(currentTurn, { id: 'call-a' })?.key, 'call-a');
|
|
547
|
+
strict_1.default.equal(currentTurn.activeToolCallKey, 'call-a');
|
|
548
|
+
(0, local_core_acp_progress_js_1.syncLegacyPendingToolCall)(currentTurn, currentTurn.pendingToolCalls['call-a']);
|
|
549
|
+
strict_1.default.equal(currentTurn.pendingToolCallTitle, 'Terminal');
|
|
550
|
+
strict_1.default.equal(currentTurn.pendingToolCallId, 'run-1-tool-1');
|
|
551
|
+
(0, local_core_acp_progress_js_1.deletePendingToolCall)(currentTurn, 'call-a');
|
|
552
|
+
strict_1.default.deepEqual(currentTurn.pendingToolCallOrder, ['sequence:2']);
|
|
553
|
+
strict_1.default.equal(currentTurn.activeToolCallKey, undefined);
|
|
554
|
+
});
|
|
555
|
+
(0, node_test_1.default)('ACP progress projection ignores empty plan entries', () => {
|
|
556
|
+
strict_1.default.equal((0, local_core_acp_progress_js_1.formatPlanProgress)([
|
|
557
|
+
{ content: '检查消息流' },
|
|
558
|
+
{ content: ' ' },
|
|
559
|
+
{ content: '修复持久化' },
|
|
560
|
+
]), '💭 检查消息流 | 修复持久化');
|
|
561
|
+
strict_1.default.equal((0, local_core_acp_progress_js_1.formatPlanProgress)([{ content: '' }]), '');
|
|
562
|
+
});
|
|
98
563
|
(0, node_test_1.default)('ACP tool call running and completed updates share one message id', () => {
|
|
99
564
|
const upserted = [];
|
|
100
565
|
const emitted = [];
|
|
@@ -172,7 +637,7 @@ const workspace_acp_permissions_js_1 = require("../services/local-ai-core/src/ac
|
|
|
172
637
|
const emitted = [];
|
|
173
638
|
const coordinator = new local_core_acp_turn_coordinator_js_1.LocalCoreAcpTurnCoordinator({
|
|
174
639
|
appendMessage: () => strict_1.default.fail('tool updates should be upserted'),
|
|
175
|
-
upsertMessage: (_threadId, id, _role, content, kind) => upserted.push({ id, content, kind }),
|
|
640
|
+
upsertMessage: (_threadId, id, _role, content, kind, toolCall) => upserted.push({ id, content, kind, toolCall }),
|
|
176
641
|
emitBridge: (event) => emitted.push(event),
|
|
177
642
|
updateRunStatus: () => { },
|
|
178
643
|
sendRaw: () => true,
|
|
@@ -200,6 +665,7 @@ const workspace_acp_permissions_js_1 = require("../services/local-ai-core/src/ac
|
|
|
200
665
|
sessionUpdate: 'tool_call',
|
|
201
666
|
id: 'call-a',
|
|
202
667
|
title: 'Terminal',
|
|
668
|
+
rawInput: { command: 'npm test' },
|
|
203
669
|
},
|
|
204
670
|
},
|
|
205
671
|
});
|
|
@@ -250,6 +716,74 @@ const workspace_acp_permissions_js_1 = require("../services/local-ai-core/src/ac
|
|
|
250
716
|
strict_1.default.equal(upserted[1]?.id, 'run-1-tool-1');
|
|
251
717
|
strict_1.default.equal(upserted[1]?.content, '🔧 Terminal: completed - terminal output');
|
|
252
718
|
strict_1.default.deepEqual(emitted.map((event) => event.messageId), ['run-1-tool-2', 'run-1-tool-1']);
|
|
719
|
+
strict_1.default.deepEqual(upserted[1]?.toolCall, {
|
|
720
|
+
id: 'call-a',
|
|
721
|
+
name: 'Terminal',
|
|
722
|
+
status: 'completed',
|
|
723
|
+
input: { command: 'npm test' },
|
|
724
|
+
output: 'terminal output',
|
|
725
|
+
detail: undefined,
|
|
726
|
+
label: '工具结果',
|
|
727
|
+
});
|
|
728
|
+
strict_1.default.deepEqual(emitted[1]?.toolCall, upserted[1]?.toolCall);
|
|
729
|
+
});
|
|
730
|
+
(0, node_test_1.default)('ACP tool call update backfills rawInput from pi ACP updates', () => {
|
|
731
|
+
const upserted = [];
|
|
732
|
+
const coordinator = new local_core_acp_turn_coordinator_js_1.LocalCoreAcpTurnCoordinator({
|
|
733
|
+
appendMessage: () => strict_1.default.fail('tool updates should be upserted'),
|
|
734
|
+
upsertMessage: (_threadId, id, _role, content, _kind, toolCall) => upserted.push({ id, content, toolCall }),
|
|
735
|
+
emitBridge: () => { },
|
|
736
|
+
updateRunStatus: () => { },
|
|
737
|
+
sendRaw: () => true,
|
|
738
|
+
});
|
|
739
|
+
const session = {
|
|
740
|
+
threadId: 'thread-1',
|
|
741
|
+
bridgeSessionKey: 'session:thread-1',
|
|
742
|
+
currentRunId: 'run-1',
|
|
743
|
+
currentTurn: {
|
|
744
|
+
runId: 'run-1',
|
|
745
|
+
replyCtx: 'run-1',
|
|
746
|
+
previewHandle: 'preview-1',
|
|
747
|
+
assistantText: '',
|
|
748
|
+
typingStarted: true,
|
|
749
|
+
previewStarted: false,
|
|
750
|
+
permission: null,
|
|
751
|
+
},
|
|
752
|
+
loadReplayMode: false,
|
|
753
|
+
schedulerJobCreatedByRun: new Map(),
|
|
754
|
+
};
|
|
755
|
+
coordinator.handleAgentNotification(session, {
|
|
756
|
+
method: 'session/update',
|
|
757
|
+
params: {
|
|
758
|
+
update: {
|
|
759
|
+
sessionUpdate: 'tool_call',
|
|
760
|
+
toolCallId: 'call-a',
|
|
761
|
+
title: 'bash',
|
|
762
|
+
},
|
|
763
|
+
},
|
|
764
|
+
});
|
|
765
|
+
coordinator.handleAgentNotification(session, {
|
|
766
|
+
method: 'session/update',
|
|
767
|
+
params: {
|
|
768
|
+
update: {
|
|
769
|
+
sessionUpdate: 'tool_call_update',
|
|
770
|
+
toolCallId: 'call-a',
|
|
771
|
+
status: 'completed',
|
|
772
|
+
rawInput: { command: 'ls -la ~/Desktop' },
|
|
773
|
+
content: [
|
|
774
|
+
{
|
|
775
|
+
type: 'content',
|
|
776
|
+
content: {
|
|
777
|
+
type: 'text',
|
|
778
|
+
text: 'total 32',
|
|
779
|
+
},
|
|
780
|
+
},
|
|
781
|
+
],
|
|
782
|
+
},
|
|
783
|
+
},
|
|
784
|
+
});
|
|
785
|
+
strict_1.default.equal(upserted[0]?.toolCall?.name, 'bash');
|
|
786
|
+
strict_1.default.deepEqual(upserted[0]?.toolCall?.input, { command: 'ls -la ~/Desktop' });
|
|
253
787
|
});
|
|
254
788
|
(0, node_test_1.default)('ACP permission tool parameters are preserved in completed tool cards', () => {
|
|
255
789
|
const upserted = [];
|
|
@@ -378,6 +912,61 @@ const workspace_acp_permissions_js_1 = require("../services/local-ai-core/src/ac
|
|
|
378
912
|
]]);
|
|
379
913
|
strict_1.default.equal(session.pendingPermissionByRun.get('run-1')?.options[1]?.optionId, 'approve-always');
|
|
380
914
|
});
|
|
915
|
+
(0, node_test_1.default)('ACP yolo mode auto-selects permission requests without rendering cards', () => {
|
|
916
|
+
const emitted = [];
|
|
917
|
+
const appended = [];
|
|
918
|
+
const rawPayloads = [];
|
|
919
|
+
const coordinator = new local_core_acp_turn_coordinator_js_1.LocalCoreAcpTurnCoordinator({
|
|
920
|
+
appendMessage: (_threadId, _role, content) => appended.push(content),
|
|
921
|
+
emitBridge: (event) => emitted.push(event),
|
|
922
|
+
updateRunStatus: () => { },
|
|
923
|
+
getThreadAgentMode: () => 'bypassPermissions',
|
|
924
|
+
sendRaw: (_session, payload) => {
|
|
925
|
+
rawPayloads.push(payload);
|
|
926
|
+
return true;
|
|
927
|
+
},
|
|
928
|
+
});
|
|
929
|
+
const session = {
|
|
930
|
+
threadId: 'thread-1',
|
|
931
|
+
bridgeSessionKey: 'session:thread-1',
|
|
932
|
+
currentRunId: 'run-1',
|
|
933
|
+
currentTurn: {
|
|
934
|
+
runId: 'run-1',
|
|
935
|
+
replyCtx: 'run-1',
|
|
936
|
+
previewHandle: 'preview-1',
|
|
937
|
+
assistantText: '',
|
|
938
|
+
typingStarted: true,
|
|
939
|
+
previewStarted: false,
|
|
940
|
+
permission: null,
|
|
941
|
+
},
|
|
942
|
+
pendingPermissionByRun: new Map(),
|
|
943
|
+
loadReplayMode: false,
|
|
944
|
+
schedulerJobCreatedByRun: new Map(),
|
|
945
|
+
};
|
|
946
|
+
coordinator.handleAgentRequest(session, {
|
|
947
|
+
method: 'session/request_permission',
|
|
948
|
+
id: 42,
|
|
949
|
+
params: {
|
|
950
|
+
toolCall: {
|
|
951
|
+
title: 'Terminal',
|
|
952
|
+
parameters: {
|
|
953
|
+
command: 'system_profiler SPHardwareDataType',
|
|
954
|
+
},
|
|
955
|
+
},
|
|
956
|
+
options: [
|
|
957
|
+
{ optionId: 'approve-once', name: 'Allow once', kind: 'allow' },
|
|
958
|
+
{ optionId: 'approve-always', name: 'Always allow', kind: 'allow_all' },
|
|
959
|
+
{ optionId: 'reject', name: 'Reject', kind: 'reject' },
|
|
960
|
+
],
|
|
961
|
+
},
|
|
962
|
+
});
|
|
963
|
+
strict_1.default.equal(rawPayloads[0]?.id, 42);
|
|
964
|
+
strict_1.default.equal(rawPayloads[0]?.result?.outcome?.outcome, 'selected');
|
|
965
|
+
strict_1.default.equal(rawPayloads[0]?.result?.outcome?.optionId, 'approve-always');
|
|
966
|
+
strict_1.default.equal(session.pendingPermissionByRun.size, 0);
|
|
967
|
+
strict_1.default.deepEqual(appended, []);
|
|
968
|
+
strict_1.default.deepEqual(emitted, []);
|
|
969
|
+
});
|
|
381
970
|
(0, node_test_1.default)('ACP bare tool call is flushed before assistant text', () => {
|
|
382
971
|
const appended = [];
|
|
383
972
|
const emitted = [];
|
|
@@ -559,7 +1148,35 @@ const workspace_acp_permissions_js_1 = require("../services/local-ai-core/src/ac
|
|
|
559
1148
|
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
560
1149
|
}
|
|
561
1150
|
});
|
|
562
|
-
(0, node_test_1.default)('
|
|
1151
|
+
(0, node_test_1.default)('ACP store preserves structured tool call progress metadata', () => {
|
|
1152
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-tool-store-'));
|
|
1153
|
+
const store = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
|
|
1154
|
+
try {
|
|
1155
|
+
const thread = store.createThread('project-1', 'Thread');
|
|
1156
|
+
store.upsertMessage(thread.id, 'run-1-tool-1', 'assistant', '🔧 bash: completed - total 32', 'progress', {
|
|
1157
|
+
id: 'call-1',
|
|
1158
|
+
name: 'bash',
|
|
1159
|
+
status: 'completed',
|
|
1160
|
+
input: { command: 'ls -la ~/Desktop' },
|
|
1161
|
+
detail: 'ls -la ~/Desktop',
|
|
1162
|
+
output: 'total 32',
|
|
1163
|
+
label: '工具结果',
|
|
1164
|
+
});
|
|
1165
|
+
const detail = store.getThread(thread.id, []);
|
|
1166
|
+
strict_1.default.equal(detail.messages.length, 1);
|
|
1167
|
+
strict_1.default.equal(detail.messages[0]?.id, 'run-1-tool-1');
|
|
1168
|
+
strict_1.default.equal(detail.messages[0]?.toolCall?.name, 'bash');
|
|
1169
|
+
strict_1.default.equal(detail.messages[0]?.toolCall?.status, 'completed');
|
|
1170
|
+
strict_1.default.deepEqual(detail.messages[0]?.toolCall?.input, { command: 'ls -la ~/Desktop' });
|
|
1171
|
+
strict_1.default.equal(detail.messages[0]?.toolCall?.detail, 'ls -la ~/Desktop');
|
|
1172
|
+
strict_1.default.equal(detail.messages[0]?.toolCall?.output, 'total 32');
|
|
1173
|
+
}
|
|
1174
|
+
finally {
|
|
1175
|
+
store.close();
|
|
1176
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
1177
|
+
}
|
|
1178
|
+
});
|
|
1179
|
+
(0, node_test_1.default)('lark bridge sends completed thought once before final answer', async () => {
|
|
563
1180
|
const createdCards = [];
|
|
564
1181
|
const patchedCards = [];
|
|
565
1182
|
const storedMessageIds = [];
|
|
@@ -639,14 +1256,12 @@ const workspace_acp_permissions_js_1 = require("../services/local-ai-core/src/ac
|
|
|
639
1256
|
content: '最终回答',
|
|
640
1257
|
});
|
|
641
1258
|
strict_1.default.equal(createdCards.length, 2);
|
|
642
|
-
strict_1.default.equal(createdCards[0]?.text, '💭
|
|
1259
|
+
strict_1.default.equal(createdCards[0]?.text, '💭 先理解问题,再检查代码');
|
|
643
1260
|
strict_1.default.equal(createdCards[1]?.text, '最终回答');
|
|
644
|
-
strict_1.default.equal(patchedCards.length,
|
|
645
|
-
strict_1.default.equal(patchedCards[0]?.messageId, 'lark-msg-1');
|
|
646
|
-
strict_1.default.equal(patchedCards[0]?.text, '💭 先理解问题,再检查代码');
|
|
1261
|
+
strict_1.default.equal(patchedCards.length, 0);
|
|
647
1262
|
strict_1.default.deepEqual(storedMessageIds, ['lark-msg-2']);
|
|
648
1263
|
});
|
|
649
|
-
(0, node_test_1.default)('lark bridge
|
|
1264
|
+
(0, node_test_1.default)('lark bridge patches streamed final card with final reply', async () => {
|
|
650
1265
|
const createdCards = [];
|
|
651
1266
|
const patchedCards = [];
|
|
652
1267
|
const storedMessageIds = [];
|
|
@@ -717,11 +1332,103 @@ const workspace_acp_permissions_js_1 = require("../services/local-ai-core/src/ac
|
|
|
717
1332
|
replyCtx: 'run-1',
|
|
718
1333
|
content: '真正最终回答',
|
|
719
1334
|
});
|
|
720
|
-
strict_1.default.deepEqual(createdCards.map((card) => card.text), ['流式中的最终回答草稿'
|
|
721
|
-
strict_1.default.
|
|
722
|
-
|
|
1335
|
+
strict_1.default.deepEqual(createdCards.map((card) => card.text), ['流式中的最终回答草稿']);
|
|
1336
|
+
strict_1.default.deepEqual(patchedCards, [
|
|
1337
|
+
{ messageId: 'lark-msg-1', text: '真正最终回答' },
|
|
1338
|
+
]);
|
|
1339
|
+
strict_1.default.deepEqual(storedMessageIds, ['lark-msg-1']);
|
|
723
1340
|
});
|
|
724
|
-
(0, node_test_1.default)('lark bridge
|
|
1341
|
+
(0, node_test_1.default)('lark bridge patches partial final card after tool progress', async () => {
|
|
1342
|
+
const createdCards = [];
|
|
1343
|
+
const patchedCards = [];
|
|
1344
|
+
const storedMessageIds = [];
|
|
1345
|
+
const client = {
|
|
1346
|
+
im: {
|
|
1347
|
+
message: {
|
|
1348
|
+
create: async (request) => {
|
|
1349
|
+
const messageId = `lark-msg-${createdCards.length + 1}`;
|
|
1350
|
+
const card = JSON.parse(String(request.data.content || '{}'));
|
|
1351
|
+
createdCards.push({
|
|
1352
|
+
messageId,
|
|
1353
|
+
text: String(card.elements?.[0]?.content || ''),
|
|
1354
|
+
});
|
|
1355
|
+
return { data: { message_id: messageId } };
|
|
1356
|
+
},
|
|
1357
|
+
patch: async (request) => {
|
|
1358
|
+
const card = JSON.parse(String(request.data.content || '{}'));
|
|
1359
|
+
patchedCards.push({
|
|
1360
|
+
messageId: String(request.path.message_id || ''),
|
|
1361
|
+
text: String(card.elements?.[0]?.content || ''),
|
|
1362
|
+
});
|
|
1363
|
+
},
|
|
1364
|
+
},
|
|
1365
|
+
},
|
|
1366
|
+
};
|
|
1367
|
+
const gateway = new local_core_lark_gateway_js_1.LocalCoreLarkGateway({
|
|
1368
|
+
store: {
|
|
1369
|
+
getPlatformThreadBinding: () => ({
|
|
1370
|
+
workspace_id: 'default',
|
|
1371
|
+
platform: 'lark',
|
|
1372
|
+
chat_id: 'chat-1',
|
|
1373
|
+
platform_user_id: 'user-1',
|
|
1374
|
+
thread_id: 'thread-1',
|
|
1375
|
+
last_platform_message_id: null,
|
|
1376
|
+
}),
|
|
1377
|
+
updatePlatformThreadMessageId: (_workspaceId, _chatId, _platformUserId, messageId) => {
|
|
1378
|
+
storedMessageIds.push(messageId);
|
|
1379
|
+
},
|
|
1380
|
+
},
|
|
1381
|
+
readConfig: async () => null,
|
|
1382
|
+
getWorkspaceRouter: () => ({}),
|
|
1383
|
+
eventBus: { emit: () => { }, on: () => () => { } },
|
|
1384
|
+
});
|
|
1385
|
+
const internals = gateway;
|
|
1386
|
+
internals.runtime.set('default', {
|
|
1387
|
+
workspaceId: 'default',
|
|
1388
|
+
enabled: true,
|
|
1389
|
+
status: 'running',
|
|
1390
|
+
connected: true,
|
|
1391
|
+
appId: 'app-1',
|
|
1392
|
+
client,
|
|
1393
|
+
});
|
|
1394
|
+
internals.threadRouting.set('session:thread-1', {
|
|
1395
|
+
workspaceId: 'default',
|
|
1396
|
+
platformUserId: 'user-1',
|
|
1397
|
+
chatId: 'chat-1',
|
|
1398
|
+
threadId: 'thread-1',
|
|
1399
|
+
});
|
|
1400
|
+
await gateway.onBridgeEvent({
|
|
1401
|
+
type: 'update_message',
|
|
1402
|
+
sessionKey: 'session:thread-1',
|
|
1403
|
+
replyCtx: 'run-1',
|
|
1404
|
+
content: '好的,文件存在,现在发送给你:已',
|
|
1405
|
+
});
|
|
1406
|
+
await gateway.onBridgeEvent({
|
|
1407
|
+
type: 'reply',
|
|
1408
|
+
sessionKey: 'session:thread-1',
|
|
1409
|
+
replyCtx: 'run-1',
|
|
1410
|
+
messageId: 'tool-1',
|
|
1411
|
+
content: '🔧 bash: completed - Sent file CLAUDE.md to chat-1: msg-file-1',
|
|
1412
|
+
});
|
|
1413
|
+
await gateway.onBridgeEvent({
|
|
1414
|
+
type: 'reply',
|
|
1415
|
+
sessionKey: 'session:thread-1',
|
|
1416
|
+
replyCtx: 'run-1',
|
|
1417
|
+
content: '好的,文件存在,现在发送给你:已发送!`CLAUDE.md` 文件已经发出去了,请查收',
|
|
1418
|
+
});
|
|
1419
|
+
strict_1.default.deepEqual(createdCards.map((card) => card.text), [
|
|
1420
|
+
'好的,文件存在,现在发送给你:已',
|
|
1421
|
+
'🔧 bash',
|
|
1422
|
+
]);
|
|
1423
|
+
strict_1.default.deepEqual(patchedCards, [
|
|
1424
|
+
{
|
|
1425
|
+
messageId: 'lark-msg-1',
|
|
1426
|
+
text: '好的,文件存在,现在发送给你:已发送!`CLAUDE.md` 文件已经发出去了,请查收',
|
|
1427
|
+
},
|
|
1428
|
+
]);
|
|
1429
|
+
strict_1.default.deepEqual(storedMessageIds, ['lark-msg-1']);
|
|
1430
|
+
});
|
|
1431
|
+
(0, node_test_1.default)('lark bridge does not stream thought updates before completion', async () => {
|
|
725
1432
|
const createdCards = [];
|
|
726
1433
|
const patchedCards = [];
|
|
727
1434
|
const client = {
|
|
@@ -808,11 +1515,8 @@ const workspace_acp_permissions_js_1 = require("../services/local-ai-core/src/ac
|
|
|
808
1515
|
sessionKey: 'session:thread-1',
|
|
809
1516
|
replyCtx: 'run-1',
|
|
810
1517
|
});
|
|
811
|
-
strict_1.default.deepEqual(createdCards.map((card) => card.text), ['💭 The user']);
|
|
812
|
-
strict_1.default.deepEqual(patchedCards.map((card) => card.text), [
|
|
813
|
-
'💭 The user sent a short casual message.',
|
|
814
|
-
'💭 The user sent a short casual message. I should reply briefly.',
|
|
815
|
-
]);
|
|
1518
|
+
strict_1.default.deepEqual(createdCards.map((card) => card.text), ['💭 The user sent a short casual message. I should reply briefly.']);
|
|
1519
|
+
strict_1.default.deepEqual(patchedCards.map((card) => card.text), []);
|
|
816
1520
|
strict_1.default.ok(!createdCards.some((card) => /处理中|正在思考/.test(card.text)));
|
|
817
1521
|
});
|
|
818
1522
|
(0, node_test_1.default)('lark permission requests render as clickable card buttons', async () => {
|
|
@@ -1037,7 +1741,7 @@ const workspace_acp_permissions_js_1 = require("../services/local-ai-core/src/ac
|
|
|
1037
1741
|
strict_1.default.equal(createdCards.length, 1);
|
|
1038
1742
|
strict_1.default.match(createdCards[0]?.card?.elements?.[0]?.content || '', /桌面文件列表/);
|
|
1039
1743
|
});
|
|
1040
|
-
(0, node_test_1.default)('lark channel
|
|
1744
|
+
(0, node_test_1.default)('lark channel sends tool name and parameters once without streaming output', async () => {
|
|
1041
1745
|
const createdCards = [];
|
|
1042
1746
|
const patchedCards = [];
|
|
1043
1747
|
const client = {
|
|
@@ -1088,10 +1792,34 @@ const workspace_acp_permissions_js_1 = require("../services/local-ai-core/src/ac
|
|
|
1088
1792
|
sessionKey: 'session:thread-1',
|
|
1089
1793
|
replyCtx: 'run-1',
|
|
1090
1794
|
messageId: 'tool-1',
|
|
1091
|
-
content: '🔧 Terminal
|
|
1795
|
+
content: '🔧 Terminal',
|
|
1796
|
+
toolCall: {
|
|
1797
|
+
id: 'tool-1',
|
|
1798
|
+
name: 'Terminal',
|
|
1799
|
+
status: 'running',
|
|
1800
|
+
input: { command: 'ls ~/Desktop' },
|
|
1801
|
+
output: '',
|
|
1802
|
+
},
|
|
1092
1803
|
});
|
|
1093
|
-
|
|
1094
|
-
|
|
1804
|
+
await gateway.onBridgeEvent({
|
|
1805
|
+
type: 'reply',
|
|
1806
|
+
sessionKey: 'session:thread-1',
|
|
1807
|
+
replyCtx: 'run-1',
|
|
1808
|
+
messageId: 'tool-1',
|
|
1809
|
+
content: '🔧 Terminal: ls ~/Desktop - completed - secret terminal output',
|
|
1810
|
+
toolCall: {
|
|
1811
|
+
id: 'tool-1',
|
|
1812
|
+
name: 'Terminal',
|
|
1813
|
+
status: 'completed',
|
|
1814
|
+
input: { command: 'ls ~/Desktop' },
|
|
1815
|
+
output: 'secret terminal output',
|
|
1816
|
+
},
|
|
1817
|
+
});
|
|
1818
|
+
const text = createdCards[0]?.elements?.[0]?.content || '';
|
|
1819
|
+
strict_1.default.match(text, /🔧 Terminal/);
|
|
1820
|
+
strict_1.default.match(text, /参数:`\{"command":"ls ~\/Desktop"\}`/);
|
|
1821
|
+
strict_1.default.equal(patchedCards.length, 0);
|
|
1822
|
+
strict_1.default.doesNotMatch(text, /completed/);
|
|
1095
1823
|
strict_1.default.doesNotMatch(text, /secret terminal output/);
|
|
1096
1824
|
});
|
|
1097
1825
|
(0, node_test_1.default)('lark card action message id can be extracted from full callback payload', async () => {
|
|
@@ -1668,6 +2396,7 @@ const workspace_acp_permissions_js_1 = require("../services/local-ai-core/src/ac
|
|
|
1668
2396
|
const users = new Map();
|
|
1669
2397
|
const threadBindings = new Map();
|
|
1670
2398
|
const createdThreads = [];
|
|
2399
|
+
const updatedModes = [];
|
|
1671
2400
|
const sentCards = [];
|
|
1672
2401
|
const platformKey = 'lark:lark-hot';
|
|
1673
2402
|
const bindingKey = `${platformKey}:chat-1:user-1`;
|
|
@@ -1683,6 +2412,8 @@ const workspace_acp_permissions_js_1 = require("../services/local-ai-core/src/ac
|
|
|
1683
2412
|
},
|
|
1684
2413
|
getPlatformThreadBinding: () => threadBindings.get(bindingKey),
|
|
1685
2414
|
upsertPlatformThreadBinding: (binding) => threadBindings.set(bindingKey, binding),
|
|
2415
|
+
getThreadRow: (threadId) => ({ id: threadId, agent_mode: threadId === 'thread-1' ? 'bypassPermissions' : 'default' }),
|
|
2416
|
+
updateThreadAgentMode: (threadId, mode) => updatedModes.push({ threadId, mode }),
|
|
1686
2417
|
getLatestRunForThread: () => null,
|
|
1687
2418
|
clearPlatformThreadMessageId: () => { },
|
|
1688
2419
|
},
|
|
@@ -1736,8 +2467,29 @@ const workspace_acp_permissions_js_1 = require("../services/local-ai-core/src/ac
|
|
|
1736
2467
|
});
|
|
1737
2468
|
strict_1.default.equal(users.get(`${platformKey}:user-1`)?.thread_id, 'thread-2');
|
|
1738
2469
|
strict_1.default.deepEqual(createdThreads.map((item) => item.split(':')[0]), ['thread-1', 'thread-2']);
|
|
2470
|
+
strict_1.default.deepEqual(updatedModes, [{ threadId: 'thread-2', mode: 'bypassPermissions' }]);
|
|
1739
2471
|
strict_1.default.match(sentCards[0]?.elements?.[0]?.content || '', /已开始新会话/);
|
|
1740
2472
|
});
|
|
2473
|
+
(0, node_test_1.default)('lark rendering suppresses noisy pending tool progress cards', () => {
|
|
2474
|
+
const turn = (0, runtime_state_js_1.createLarkTurnState)('session-1');
|
|
2475
|
+
const rendered = (0, runtime_state_js_1.renderLarkBridgeEventMessage)(turn, {
|
|
2476
|
+
type: 'update_message',
|
|
2477
|
+
sessionKey: 'session-1',
|
|
2478
|
+
replyCtx: 'run-1',
|
|
2479
|
+
previewHandle: 'tool-1',
|
|
2480
|
+
content: '🔧 bash: Tool update - pending',
|
|
2481
|
+
});
|
|
2482
|
+
strict_1.default.equal(rendered.text, '');
|
|
2483
|
+
strict_1.default.equal(rendered.key, 'noop');
|
|
2484
|
+
const completed = (0, runtime_state_js_1.renderLarkBridgeEventMessage)(turn, {
|
|
2485
|
+
type: 'update_message',
|
|
2486
|
+
sessionKey: 'session-1',
|
|
2487
|
+
replyCtx: 'run-1',
|
|
2488
|
+
previewHandle: 'tool-1',
|
|
2489
|
+
content: '🔧 bash: Tool update - completed - verbose output',
|
|
2490
|
+
});
|
|
2491
|
+
strict_1.default.equal(completed.text, '🔧 bash: Tool update');
|
|
2492
|
+
});
|
|
1741
2493
|
(0, node_test_1.default)('ACP skips empty generic running tool updates', () => {
|
|
1742
2494
|
const appended = [];
|
|
1743
2495
|
const emitted = [];
|
|
@@ -1863,6 +2615,29 @@ const workspace_acp_permissions_js_1 = require("../services/local-ai-core/src/ac
|
|
|
1863
2615
|
strict_1.default.equal(processed.displayContent.trim(), '已为你创建。');
|
|
1864
2616
|
strict_1.default.match(processed.systemResponses[0] || '', /已创建定时任务/);
|
|
1865
2617
|
});
|
|
2618
|
+
(0, node_test_1.default)('slash mode commands normalize yolo aliases and expose current help', () => {
|
|
2619
|
+
strict_1.default.deepEqual((0, local_core_slash_commands_js_1.parseSlashCommand)('/mode yolo'), { name: 'mode', args: ['yolo'] });
|
|
2620
|
+
strict_1.default.equal((0, local_core_slash_commands_js_1.normalizeAgentMode)('yolo'), 'bypassPermissions');
|
|
2621
|
+
strict_1.default.equal((0, local_core_slash_commands_js_1.normalizeAgentMode)('accept-edits'), 'acceptEdits');
|
|
2622
|
+
strict_1.default.equal((0, local_core_slash_commands_js_1.formatAgentMode)('bypassPermissions'), 'yolo');
|
|
2623
|
+
strict_1.default.match((0, local_core_slash_commands_js_1.modeHelpText)('bypassPermissions'), /当前模式:yolo/);
|
|
2624
|
+
});
|
|
2625
|
+
(0, node_test_1.default)('thread agent mode persists with thread state', () => {
|
|
2626
|
+
const dir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-mode-'));
|
|
2627
|
+
try {
|
|
2628
|
+
const store = new local_core_acp_store_js_1.LocalCoreAcpStore(dir);
|
|
2629
|
+
const thread = store.createThread('workspace-a', 'Mode test', 'claudecode');
|
|
2630
|
+
strict_1.default.equal(store.getThreadRow(thread.id)?.agent_mode, 'default');
|
|
2631
|
+
const inheritedThread = store.createThread('workspace-a', 'Inherited mode test', 'claudecode', 'bypassPermissions');
|
|
2632
|
+
strict_1.default.equal(store.getThreadRow(inheritedThread.id)?.agent_mode, 'bypassPermissions');
|
|
2633
|
+
store.updateThreadAgentMode(thread.id, 'bypassPermissions');
|
|
2634
|
+
strict_1.default.equal(store.getThreadRow(thread.id)?.agent_mode, 'bypassPermissions');
|
|
2635
|
+
store.close();
|
|
2636
|
+
}
|
|
2637
|
+
finally {
|
|
2638
|
+
(0, node_fs_1.rmSync)(dir, { recursive: true, force: true });
|
|
2639
|
+
}
|
|
2640
|
+
});
|
|
1866
2641
|
(0, node_test_1.default)('scheduled conversation executor uses execution policy hooks around a thread run', async () => {
|
|
1867
2642
|
const calls = [];
|
|
1868
2643
|
const job = {
|