@kafca/agentdock 0.1.14 → 0.1.16
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/bin/agentdock.mjs +73 -3
- package/dist/renderer/assets/Badge-O6YyBOXO.js +1 -0
- package/dist/renderer/assets/Chat-Bb42Xu99.js +2 -0
- package/dist/renderer/assets/ChatMarkdown-CKIl8nVw.js +45 -0
- package/dist/renderer/assets/Config-U1HHZNv-.js +16 -0
- package/dist/renderer/assets/CronList-Qfok-TFN.js +1 -0
- package/dist/renderer/assets/Dashboard-DIxtJDEV.js +26 -0
- package/dist/renderer/assets/EmptyState-Ce5baC0x.js +6 -0
- package/dist/renderer/assets/KnowledgeDetail-DKjqGzjI.js +16 -0
- package/dist/renderer/assets/KnowledgeHome-CsYBKv3L.js +11 -0
- package/dist/renderer/assets/Logs-BV93l6f2.js +1 -0
- package/dist/renderer/assets/Modal-JbmTZbCL.js +10 -0
- package/dist/renderer/assets/Page-854R0NMM.js +1 -0
- package/dist/renderer/assets/ProjectDetail-Ct7yASjV.js +11 -0
- package/dist/renderer/assets/ProjectList-mJJeoUcP.js +1 -0
- package/dist/renderer/assets/SessionChat-CClPXCjv.js +1 -0
- package/dist/renderer/assets/SessionList-Cxilghws.js +1 -0
- package/dist/renderer/assets/ThreadChat-BUFr0IbT.js +23 -0
- package/dist/renderer/assets/Workspace-B_sQxTj-.js +14 -0
- package/dist/renderer/assets/arrow-left-mguKZKTu.js +6 -0
- package/dist/renderer/assets/book-open-3Awj2zba.js +6 -0
- package/dist/renderer/assets/circle-BWSc4-qr.js +6 -0
- package/dist/renderer/assets/desktop-D4Z7M9RM.js +3 -0
- package/dist/renderer/assets/funnel-DE2QCcoy.js +6 -0
- package/dist/renderer/assets/heart-B6xi7jGt.js +6 -0
- package/dist/renderer/assets/index-B2JL40NF.js +232 -0
- package/dist/renderer/assets/index-CJZeKEpW.css +11 -0
- package/dist/renderer/assets/pencil-BD6oAfUg.js +6 -0
- package/dist/renderer/assets/play-C9Ue_X4n.js +6 -0
- package/dist/renderer/assets/plug-CIaRVhae.js +6 -0
- package/dist/renderer/assets/plus-BWHFEPZn.js +6 -0
- package/dist/renderer/assets/projects-DCdaAlgs.js +1 -0
- package/dist/renderer/assets/save-DsSaJ2pw.js +6 -0
- package/dist/renderer/assets/search-Ci4i20HH.js +6 -0
- package/dist/renderer/assets/server-07G4ybtv.js +11 -0
- package/dist/renderer/assets/session-utils-D9njomRn.js +6 -0
- package/dist/renderer/assets/sessions-C4c2fLQB.js +1 -0
- package/dist/renderer/assets/shield-check-DjJz3hYk.js +6 -0
- package/dist/renderer/assets/trash-2-C5_ZLIsu.js +6 -0
- package/dist/renderer/assets/user-DHBITt1w.js +11 -0
- package/dist/renderer/assets/wifi-off-DuwKaMRJ.js +11 -0
- package/dist/renderer/index.html +2 -2
- package/dist-electron/electron/agent-runtime-detector.test.js +121 -0
- package/dist-electron/electron/lac-cli.test.js +63 -0
- package/dist-electron/electron/local-core-refactor.test.js +54 -0
- package/dist-electron/electron/plugin-kernel.test.js +6 -6
- package/dist-electron/electron/runtime-detection-service.test.js +116 -0
- package/dist-electron/electron/security-approval-store.test.js +89 -0
- package/dist-electron/electron/workspace-task-store.test.js +66 -0
- package/dist-electron/packages/core-sdk/src/index.js +124 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +68 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-store.js +617 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +11 -0
- package/dist-electron/services/local-ai-core/src/cli/lac.js +19 -12
- package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +5 -0
- package/dist-electron/services/local-ai-core/src/plugins/builtin/scheduler-local-plugin.js +35 -0
- package/dist-electron/services/local-ai-core/src/router/workspace-router.js +197 -0
- package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +237 -0
- package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +72 -0
- package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-service.js +138 -0
- package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-store.js +46 -0
- package/dist-electron/services/local-ai-core/src/runtime/server.js +151 -0
- package/dist-electron/services/local-ai-core/src/scheduler/local-schedule-adapter.js +61 -0
- package/dist-electron/services/local-ai-core/src/security/command-risk.js +57 -0
- package/package.json +1 -1
- package/dist/renderer/assets/index-BM0Jl5gB.js +0 -487
- package/dist/renderer/assets/index-DrLbJPYN.css +0 -11
|
@@ -166,8 +166,8 @@ function plugin(id, dependsOn = []) {
|
|
|
166
166
|
strict_1.default.deepEqual(runtime.kernel.getCapabilitySnapshot().scheduler, {
|
|
167
167
|
enabled: true,
|
|
168
168
|
triggerTypes: ['cron', 'once'],
|
|
169
|
-
deliveryTargets: ['lark', 'weixin'],
|
|
170
|
-
platforms: ['lark', 'weixin'],
|
|
169
|
+
deliveryTargets: ['local', 'lark', 'weixin'],
|
|
170
|
+
platforms: ['local', 'lark', 'weixin'],
|
|
171
171
|
});
|
|
172
172
|
await runtime.start();
|
|
173
173
|
await runtime.stop();
|
|
@@ -199,8 +199,8 @@ function plugin(id, dependsOn = []) {
|
|
|
199
199
|
strict_1.default.deepEqual(runtime.kernel.getCapabilitySnapshot().scheduler, {
|
|
200
200
|
enabled: true,
|
|
201
201
|
triggerTypes: ['cron', 'once'],
|
|
202
|
-
deliveryTargets: ['lark', 'weixin'],
|
|
203
|
-
platforms: ['lark', 'weixin'],
|
|
202
|
+
deliveryTargets: ['local', 'lark', 'weixin'],
|
|
203
|
+
platforms: ['local', 'lark', 'weixin'],
|
|
204
204
|
});
|
|
205
205
|
}
|
|
206
206
|
finally {
|
|
@@ -265,8 +265,8 @@ function plugin(id, dependsOn = []) {
|
|
|
265
265
|
strict_1.default.deepEqual(runtime.kernel.getCapabilitySnapshot().scheduler, {
|
|
266
266
|
enabled: true,
|
|
267
267
|
triggerTypes: ['cron', 'once'],
|
|
268
|
-
deliveryTargets: ['weixin'],
|
|
269
|
-
platforms: ['weixin'],
|
|
268
|
+
deliveryTargets: ['local', 'weixin'],
|
|
269
|
+
platforms: ['local', 'weixin'],
|
|
270
270
|
});
|
|
271
271
|
}
|
|
272
272
|
finally {
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_test_1 = __importDefault(require("node:test"));
|
|
7
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
const node_os_1 = require("node:os");
|
|
11
|
+
const runtime_detection_service_js_1 = require("../services/local-ai-core/src/runtime/runtime-detection-service.js");
|
|
12
|
+
(0, node_test_1.default)('runtime detection service returns unknown results before first detection', () => {
|
|
13
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'runtime-detection-service-'));
|
|
14
|
+
try {
|
|
15
|
+
const service = new runtime_detection_service_js_1.RuntimeDetectionService({
|
|
16
|
+
userDataPath,
|
|
17
|
+
readConfig: async () => null,
|
|
18
|
+
detect: () => [],
|
|
19
|
+
});
|
|
20
|
+
const runtimes = service.list();
|
|
21
|
+
const opencode = runtimes.find((runtime) => runtime.runtimeId === 'opencode');
|
|
22
|
+
strict_1.default.equal(opencode?.status, 'unknown');
|
|
23
|
+
strict_1.default.equal(opencode?.installed, false);
|
|
24
|
+
strict_1.default.equal(runtimes.find((runtime) => runtime.runtimeId === 'localcore-acp')?.status, 'installed');
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
(0, node_test_1.default)('runtime detection service refresh persists latest results', async () => {
|
|
31
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'runtime-detection-service-persist-'));
|
|
32
|
+
try {
|
|
33
|
+
const detectedAt = '2026-04-27T00:00:00.000Z';
|
|
34
|
+
const result = runtimeResult({ detectedAt, version: '1.2.3' });
|
|
35
|
+
const service = new runtime_detection_service_js_1.RuntimeDetectionService({
|
|
36
|
+
userDataPath,
|
|
37
|
+
readConfig: async () => null,
|
|
38
|
+
detect: () => [result],
|
|
39
|
+
});
|
|
40
|
+
strict_1.default.deepEqual(await service.refresh(), [result]);
|
|
41
|
+
const nextService = new runtime_detection_service_js_1.RuntimeDetectionService({
|
|
42
|
+
userDataPath,
|
|
43
|
+
readConfig: async () => null,
|
|
44
|
+
detect: () => [],
|
|
45
|
+
});
|
|
46
|
+
strict_1.default.equal(nextService.list()[0]?.version, '1.2.3');
|
|
47
|
+
strict_1.default.match((0, node_fs_1.readFileSync)((0, node_path_1.join)(userDataPath, 'runtime', 'runtime-detection.json'), 'utf8'), /"version": "1.2.3"/);
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
(0, node_test_1.default)('runtime detection service ignores corrupted persisted state', () => {
|
|
54
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'runtime-detection-service-corrupt-'));
|
|
55
|
+
try {
|
|
56
|
+
const statePath = (0, node_path_1.join)(userDataPath, 'runtime', 'runtime-detection.json');
|
|
57
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.join)(userDataPath, 'runtime'), { recursive: true });
|
|
58
|
+
(0, node_fs_1.writeFileSync)(statePath, '{not-json', 'utf8');
|
|
59
|
+
const service = new runtime_detection_service_js_1.RuntimeDetectionService({
|
|
60
|
+
userDataPath,
|
|
61
|
+
readConfig: async () => null,
|
|
62
|
+
detect: () => [],
|
|
63
|
+
});
|
|
64
|
+
strict_1.default.equal(service.list().find((runtime) => runtime.runtimeId === 'opencode')?.status, 'unknown');
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
(0, node_test_1.default)('runtime detection service emits detection events and filters single runtime refresh response', async () => {
|
|
71
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'runtime-detection-service-events-'));
|
|
72
|
+
try {
|
|
73
|
+
const events = [];
|
|
74
|
+
const service = new runtime_detection_service_js_1.RuntimeDetectionService({
|
|
75
|
+
userDataPath,
|
|
76
|
+
readConfig: async () => null,
|
|
77
|
+
detect: () => [
|
|
78
|
+
runtimeResult({ runtimeId: 'opencode', agentType: 'opencode' }),
|
|
79
|
+
runtimeResult({ runtimeId: 'codex', agentType: 'codex' }),
|
|
80
|
+
],
|
|
81
|
+
emit: (event) => {
|
|
82
|
+
events.push(event.type);
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
const refreshed = await service.refresh('codex');
|
|
86
|
+
strict_1.default.deepEqual(refreshed.map((runtime) => runtime.runtimeId), ['codex']);
|
|
87
|
+
strict_1.default.deepEqual(events, [
|
|
88
|
+
'runtime.detect.started',
|
|
89
|
+
'runtime.detect.completed',
|
|
90
|
+
'runtime.status.changed',
|
|
91
|
+
'runtime.status.changed',
|
|
92
|
+
]);
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
function runtimeResult(input = {}) {
|
|
99
|
+
return {
|
|
100
|
+
agentType: input.agentType || 'opencode',
|
|
101
|
+
runtimeId: input.runtimeId || input.agentType || 'opencode',
|
|
102
|
+
displayName: input.displayName || 'OpenCode',
|
|
103
|
+
status: input.status || 'installed',
|
|
104
|
+
installed: input.installed ?? true,
|
|
105
|
+
command: input.command || '/tmp/opencode',
|
|
106
|
+
binaryPath: input.binaryPath || input.command || '/tmp/opencode',
|
|
107
|
+
version: input.version,
|
|
108
|
+
detectedAt: input.detectedAt || '2026-04-27T00:00:00.000Z',
|
|
109
|
+
summary: input.summary || 'OpenCode is installed.',
|
|
110
|
+
details: input.details,
|
|
111
|
+
issues: input.issues || [],
|
|
112
|
+
recommendedActions: input.recommendedActions || [],
|
|
113
|
+
source: input.source || 'path',
|
|
114
|
+
error: input.error,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_test_1 = __importDefault(require("node:test"));
|
|
7
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_os_1 = require("node:os");
|
|
10
|
+
const node_path_1 = require("node:path");
|
|
11
|
+
const local_core_acp_store_js_1 = require("../services/local-ai-core/src/acp/local-core-acp-store.js");
|
|
12
|
+
const command_risk_js_1 = require("../services/local-ai-core/src/security/command-risk.js");
|
|
13
|
+
(0, node_test_1.default)('workspace security settings persist and create audit events', () => {
|
|
14
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'security-settings-'));
|
|
15
|
+
const store = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
|
|
16
|
+
const settings = store.updateWorkspaceSecuritySettings('workspace-a', {
|
|
17
|
+
permissions: {
|
|
18
|
+
'command.execute': 'deny',
|
|
19
|
+
'network.access': 'allow',
|
|
20
|
+
},
|
|
21
|
+
allowPaths: ['/tmp/project'],
|
|
22
|
+
denyPaths: ['/tmp/project/.env'],
|
|
23
|
+
updatedBy: 'tester',
|
|
24
|
+
});
|
|
25
|
+
strict_1.default.equal(settings.permissions['command.execute'], 'deny');
|
|
26
|
+
strict_1.default.equal(settings.permissions['workspace.read'], 'allow');
|
|
27
|
+
strict_1.default.deepEqual(settings.allowPaths, ['/tmp/project']);
|
|
28
|
+
strict_1.default.deepEqual(settings.denyPaths, ['/tmp/project/.env']);
|
|
29
|
+
const audit = store.listAuditEvents({ workspaceId: 'workspace-a', type: 'permission.changed' });
|
|
30
|
+
strict_1.default.equal(audit.events.length, 1);
|
|
31
|
+
strict_1.default.equal(audit.events[0].actor, 'tester');
|
|
32
|
+
store.close();
|
|
33
|
+
const reopened = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
|
|
34
|
+
strict_1.default.equal(reopened.getWorkspaceSecuritySettings('workspace-a').permissions['command.execute'], 'deny');
|
|
35
|
+
reopened.close();
|
|
36
|
+
});
|
|
37
|
+
(0, node_test_1.default)('approval requests persist, resolve, attach to tasks, and audit lifecycle', () => {
|
|
38
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'approval-requests-'));
|
|
39
|
+
const store = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
|
|
40
|
+
const task = store.createAgentTask({
|
|
41
|
+
workspaceId: 'workspace-a',
|
|
42
|
+
deviceId: 'local',
|
|
43
|
+
runtimeId: 'codex',
|
|
44
|
+
threadId: 'thread-a',
|
|
45
|
+
runId: 'run-a',
|
|
46
|
+
title: 'Dangerous command',
|
|
47
|
+
status: 'waiting_for_user',
|
|
48
|
+
});
|
|
49
|
+
const approval = store.createApprovalRequest({
|
|
50
|
+
workspaceId: 'workspace-a',
|
|
51
|
+
taskId: task.taskId,
|
|
52
|
+
threadId: 'thread-a',
|
|
53
|
+
runId: 'run-a',
|
|
54
|
+
kind: 'command',
|
|
55
|
+
riskLevel: 'high',
|
|
56
|
+
title: 'Approve rm',
|
|
57
|
+
description: 'rm -rf /tmp/project TOKEN=secret-value',
|
|
58
|
+
requestedAction: 'rm -rf /tmp/project',
|
|
59
|
+
command: 'rm -rf /tmp/project',
|
|
60
|
+
scopes: ['command.execute', 'workspace.write'],
|
|
61
|
+
requestedBy: 'agent',
|
|
62
|
+
});
|
|
63
|
+
strict_1.default.equal(approval.status, 'pending');
|
|
64
|
+
strict_1.default.match(approval.description, /\[REDACTED_SECRET\]/);
|
|
65
|
+
strict_1.default.ok(store.getAgentTask(task.taskId)?.approvalIds.includes(approval.approvalId));
|
|
66
|
+
const resolved = store.resolveApprovalRequest(approval.approvalId, {
|
|
67
|
+
status: 'approved',
|
|
68
|
+
resolvedBy: 'tester',
|
|
69
|
+
resolution: 'allow once',
|
|
70
|
+
});
|
|
71
|
+
strict_1.default.equal(resolved.status, 'approved');
|
|
72
|
+
strict_1.default.equal(resolved.resolvedBy, 'tester');
|
|
73
|
+
strict_1.default.equal(store.listApprovalRequests({ status: 'approved' }).approvals.length, 1);
|
|
74
|
+
strict_1.default.equal(store.listAuditEvents({ approvalId: approval.approvalId }).events.length, 2);
|
|
75
|
+
store.close();
|
|
76
|
+
});
|
|
77
|
+
(0, node_test_1.default)('command risk classification and secret redaction cover baseline rules', () => {
|
|
78
|
+
const high = (0, command_risk_js_1.classifyCommandRisk)('git reset --hard HEAD');
|
|
79
|
+
strict_1.default.equal(high.riskLevel, 'high');
|
|
80
|
+
strict_1.default.equal(high.requiresApproval, true);
|
|
81
|
+
strict_1.default.ok(high.scopes.includes('git.modify'));
|
|
82
|
+
const medium = (0, command_risk_js_1.classifyCommandRisk)('pnpm install');
|
|
83
|
+
strict_1.default.equal(medium.riskLevel, 'medium');
|
|
84
|
+
strict_1.default.ok(medium.scopes.includes('network.access'));
|
|
85
|
+
const low = (0, command_risk_js_1.classifyCommandRisk)('ls src');
|
|
86
|
+
strict_1.default.equal(low.riskLevel, 'low');
|
|
87
|
+
strict_1.default.equal(low.requiresApproval, false);
|
|
88
|
+
strict_1.default.equal((0, local_core_acp_store_js_1.redactSecrets)('OPENAI_API_KEY=sk-test1234567890 Bearer abc.def'), 'OPENAI_API_KEY=[REDACTED_SECRET] Bearer [REDACTED_SECRET]');
|
|
89
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_test_1 = __importDefault(require("node:test"));
|
|
7
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
const node_os_1 = require("node:os");
|
|
11
|
+
const local_core_acp_store_js_1 = require("../services/local-ai-core/src/acp/local-core-acp-store.js");
|
|
12
|
+
(0, node_test_1.default)('workspace registry entries persist in LocalCoreAcpStore', () => {
|
|
13
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'workspace-registry-'));
|
|
14
|
+
try {
|
|
15
|
+
const store = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
|
|
16
|
+
store.upsertWorkspaceRegistryEntry({
|
|
17
|
+
workspaceId: 'workspace-a',
|
|
18
|
+
displayName: 'Workspace A',
|
|
19
|
+
path: '/tmp/workspace-a',
|
|
20
|
+
deviceId: 'local',
|
|
21
|
+
defaultRuntimeId: 'opencode',
|
|
22
|
+
health: { status: 'healthy', summary: 'ok', issues: [] },
|
|
23
|
+
git: { isRepo: false },
|
|
24
|
+
});
|
|
25
|
+
store.close();
|
|
26
|
+
const nextStore = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
|
|
27
|
+
const workspace = nextStore.getWorkspaceRegistryEntry('workspace-a');
|
|
28
|
+
strict_1.default.equal(workspace?.displayName, 'Workspace A');
|
|
29
|
+
strict_1.default.equal(workspace?.defaultRuntimeId, 'opencode');
|
|
30
|
+
strict_1.default.equal(workspace?.health.status, 'healthy');
|
|
31
|
+
nextStore.close();
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
(0, node_test_1.default)('agent tasks persist, update status, and can be found by run id', () => {
|
|
38
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agent-task-store-'));
|
|
39
|
+
try {
|
|
40
|
+
const store = new local_core_acp_store_js_1.LocalCoreAcpStore(userDataPath);
|
|
41
|
+
const task = store.createAgentTask({
|
|
42
|
+
workspaceId: 'workspace-a',
|
|
43
|
+
deviceId: 'local',
|
|
44
|
+
runtimeId: 'opencode',
|
|
45
|
+
threadId: 'thread-1',
|
|
46
|
+
runId: 'run-1',
|
|
47
|
+
title: 'Implement feature',
|
|
48
|
+
prompt: 'Please implement feature',
|
|
49
|
+
status: 'running',
|
|
50
|
+
});
|
|
51
|
+
const updated = store.updateAgentTask(task.taskId, {
|
|
52
|
+
status: 'completed',
|
|
53
|
+
summary: 'done',
|
|
54
|
+
log: { level: 'info', message: 'finished' },
|
|
55
|
+
});
|
|
56
|
+
strict_1.default.equal(updated.status, 'completed');
|
|
57
|
+
strict_1.default.equal(updated.summary, 'done');
|
|
58
|
+
strict_1.default.equal(updated.logs[0]?.message, 'finished');
|
|
59
|
+
strict_1.default.equal(store.getAgentTaskByRunId('run-1')?.taskId, task.taskId);
|
|
60
|
+
strict_1.default.equal(store.listAgentTasks({ status: 'completed' }).tasks.length, 1);
|
|
61
|
+
store.close();
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
65
|
+
}
|
|
66
|
+
});
|
|
@@ -8,6 +8,11 @@ exports.startCoreService = startCoreService;
|
|
|
8
8
|
exports.stopCoreService = stopCoreService;
|
|
9
9
|
exports.restartCoreService = restartCoreService;
|
|
10
10
|
exports.getCoreLogs = getCoreLogs;
|
|
11
|
+
exports.listInstalledAgentRuntimes = listInstalledAgentRuntimes;
|
|
12
|
+
exports.listRuntimeDetections = listRuntimeDetections;
|
|
13
|
+
exports.getRuntimeDetection = getRuntimeDetection;
|
|
14
|
+
exports.refreshRuntimeDetections = refreshRuntimeDetections;
|
|
15
|
+
exports.refreshRuntimeDetection = refreshRuntimeDetection;
|
|
11
16
|
exports.readCoreConfigFile = readCoreConfigFile;
|
|
12
17
|
exports.saveCoreRawConfigFile = saveCoreRawConfigFile;
|
|
13
18
|
exports.saveCoreStructuredConfigFile = saveCoreStructuredConfigFile;
|
|
@@ -33,6 +38,23 @@ exports.listLarkAuthorizedUsers = listLarkAuthorizedUsers;
|
|
|
33
38
|
exports.getWeixinQrCode = getWeixinQrCode;
|
|
34
39
|
exports.checkWeixinQrCodeStatus = checkWeixinQrCodeStatus;
|
|
35
40
|
exports.listWorkspaces = listWorkspaces;
|
|
41
|
+
exports.listWorkspaceRegistry = listWorkspaceRegistry;
|
|
42
|
+
exports.getWorkspaceRegistryEntry = getWorkspaceRegistryEntry;
|
|
43
|
+
exports.createWorkspaceRegistryEntry = createWorkspaceRegistryEntry;
|
|
44
|
+
exports.updateWorkspaceRegistryEntry = updateWorkspaceRegistryEntry;
|
|
45
|
+
exports.deleteWorkspaceRegistryEntry = deleteWorkspaceRegistryEntry;
|
|
46
|
+
exports.listAgentTasks = listAgentTasks;
|
|
47
|
+
exports.getAgentTask = getAgentTask;
|
|
48
|
+
exports.createAgentTask = createAgentTask;
|
|
49
|
+
exports.updateAgentTask = updateAgentTask;
|
|
50
|
+
exports.getWorkspaceSecuritySettings = getWorkspaceSecuritySettings;
|
|
51
|
+
exports.updateWorkspaceSecuritySettings = updateWorkspaceSecuritySettings;
|
|
52
|
+
exports.classifyCommand = classifyCommand;
|
|
53
|
+
exports.listApprovalRequests = listApprovalRequests;
|
|
54
|
+
exports.getApprovalRequest = getApprovalRequest;
|
|
55
|
+
exports.createApprovalRequest = createApprovalRequest;
|
|
56
|
+
exports.resolveApprovalRequest = resolveApprovalRequest;
|
|
57
|
+
exports.listAuditEvents = listAuditEvents;
|
|
36
58
|
exports.listScheduledJobs = listScheduledJobs;
|
|
37
59
|
exports.getScheduledJob = getScheduledJob;
|
|
38
60
|
exports.createScheduledJob = createScheduledJob;
|
|
@@ -116,6 +138,10 @@ function ensureEventSource() {
|
|
|
116
138
|
};
|
|
117
139
|
[
|
|
118
140
|
'runtime.updated',
|
|
141
|
+
'runtime.detect.started',
|
|
142
|
+
'runtime.detect.completed',
|
|
143
|
+
'runtime.detect.failed',
|
|
144
|
+
'runtime.status.changed',
|
|
119
145
|
'thread.updated',
|
|
120
146
|
'message.created',
|
|
121
147
|
'message.updated',
|
|
@@ -180,6 +206,21 @@ async function getCoreLogs(limit) {
|
|
|
180
206
|
const suffix = typeof limit === 'number' ? `?limit=${encodeURIComponent(String(limit))}` : '';
|
|
181
207
|
return coreRequest('GET', `/runtime/logs${suffix}`);
|
|
182
208
|
}
|
|
209
|
+
async function listInstalledAgentRuntimes() {
|
|
210
|
+
return coreRequest('GET', '/runtime/agent-runtimes');
|
|
211
|
+
}
|
|
212
|
+
async function listRuntimeDetections() {
|
|
213
|
+
return coreRequest('GET', '/runtimes');
|
|
214
|
+
}
|
|
215
|
+
async function getRuntimeDetection(runtimeId) {
|
|
216
|
+
return coreRequest('GET', `/runtimes/${encodeURIComponent(runtimeId)}`);
|
|
217
|
+
}
|
|
218
|
+
async function refreshRuntimeDetections() {
|
|
219
|
+
return coreRequest('POST', '/runtimes/refresh');
|
|
220
|
+
}
|
|
221
|
+
async function refreshRuntimeDetection(runtimeId) {
|
|
222
|
+
return coreRequest('POST', `/runtimes/${encodeURIComponent(runtimeId)}/refresh`);
|
|
223
|
+
}
|
|
183
224
|
async function readCoreConfigFile() {
|
|
184
225
|
return coreRequest('GET', '/runtime/config');
|
|
185
226
|
}
|
|
@@ -257,6 +298,89 @@ async function checkWeixinQrCodeStatus(workspaceId, ticket) {
|
|
|
257
298
|
async function listWorkspaces() {
|
|
258
299
|
return coreRequest('GET', '/workspaces');
|
|
259
300
|
}
|
|
301
|
+
async function listWorkspaceRegistry() {
|
|
302
|
+
return coreRequest('GET', '/workspace-registry');
|
|
303
|
+
}
|
|
304
|
+
async function getWorkspaceRegistryEntry(workspaceId) {
|
|
305
|
+
return coreRequest('GET', `/workspace-registry/${encodeURIComponent(workspaceId)}`);
|
|
306
|
+
}
|
|
307
|
+
async function createWorkspaceRegistryEntry(input) {
|
|
308
|
+
return coreRequest('POST', '/workspace-registry', input);
|
|
309
|
+
}
|
|
310
|
+
async function updateWorkspaceRegistryEntry(workspaceId, input) {
|
|
311
|
+
return coreRequest('PATCH', `/workspace-registry/${encodeURIComponent(workspaceId)}`, input);
|
|
312
|
+
}
|
|
313
|
+
async function deleteWorkspaceRegistryEntry(workspaceId) {
|
|
314
|
+
return coreRequest('DELETE', `/workspace-registry/${encodeURIComponent(workspaceId)}`);
|
|
315
|
+
}
|
|
316
|
+
async function listAgentTasks(query = {}) {
|
|
317
|
+
const params = new URLSearchParams();
|
|
318
|
+
if (query.workspaceId)
|
|
319
|
+
params.set('workspace_id', query.workspaceId);
|
|
320
|
+
if (query.runtimeId)
|
|
321
|
+
params.set('runtime_id', query.runtimeId);
|
|
322
|
+
if (query.status)
|
|
323
|
+
params.set('status', Array.isArray(query.status) ? query.status.join(',') : query.status);
|
|
324
|
+
if (query.limit)
|
|
325
|
+
params.set('limit', String(query.limit));
|
|
326
|
+
const suffix = params.toString() ? `?${params.toString()}` : '';
|
|
327
|
+
return coreRequest('GET', `/tasks${suffix}`);
|
|
328
|
+
}
|
|
329
|
+
async function getAgentTask(taskId) {
|
|
330
|
+
return coreRequest('GET', `/tasks/${encodeURIComponent(taskId)}`);
|
|
331
|
+
}
|
|
332
|
+
async function createAgentTask(input) {
|
|
333
|
+
return coreRequest('POST', '/tasks', input);
|
|
334
|
+
}
|
|
335
|
+
async function updateAgentTask(taskId, input) {
|
|
336
|
+
return coreRequest('PATCH', `/tasks/${encodeURIComponent(taskId)}`, input);
|
|
337
|
+
}
|
|
338
|
+
async function getWorkspaceSecuritySettings(workspaceId) {
|
|
339
|
+
return coreRequest('GET', `/workspace-security/${encodeURIComponent(workspaceId)}`);
|
|
340
|
+
}
|
|
341
|
+
async function updateWorkspaceSecuritySettings(workspaceId, input) {
|
|
342
|
+
return coreRequest('PATCH', `/workspace-security/${encodeURIComponent(workspaceId)}`, input);
|
|
343
|
+
}
|
|
344
|
+
async function classifyCommand(command, workspaceId) {
|
|
345
|
+
return coreRequest('POST', '/security/command-risk', { command, workspaceId });
|
|
346
|
+
}
|
|
347
|
+
async function listApprovalRequests(query = {}) {
|
|
348
|
+
const params = new URLSearchParams();
|
|
349
|
+
if (query.workspaceId)
|
|
350
|
+
params.set('workspace_id', query.workspaceId);
|
|
351
|
+
if (query.taskId)
|
|
352
|
+
params.set('task_id', query.taskId);
|
|
353
|
+
if (query.status)
|
|
354
|
+
params.set('status', Array.isArray(query.status) ? query.status.join(',') : query.status);
|
|
355
|
+
if (query.limit)
|
|
356
|
+
params.set('limit', String(query.limit));
|
|
357
|
+
const suffix = params.toString() ? `?${params.toString()}` : '';
|
|
358
|
+
return coreRequest('GET', `/approvals${suffix}`);
|
|
359
|
+
}
|
|
360
|
+
async function getApprovalRequest(approvalId) {
|
|
361
|
+
return coreRequest('GET', `/approvals/${encodeURIComponent(approvalId)}`);
|
|
362
|
+
}
|
|
363
|
+
async function createApprovalRequest(input) {
|
|
364
|
+
return coreRequest('POST', '/approvals', input);
|
|
365
|
+
}
|
|
366
|
+
async function resolveApprovalRequest(approvalId, input) {
|
|
367
|
+
return coreRequest('POST', `/approvals/${encodeURIComponent(approvalId)}/resolve`, input);
|
|
368
|
+
}
|
|
369
|
+
async function listAuditEvents(query = {}) {
|
|
370
|
+
const params = new URLSearchParams();
|
|
371
|
+
if (query.workspaceId)
|
|
372
|
+
params.set('workspace_id', query.workspaceId);
|
|
373
|
+
if (query.taskId)
|
|
374
|
+
params.set('task_id', query.taskId);
|
|
375
|
+
if (query.approvalId)
|
|
376
|
+
params.set('approval_id', query.approvalId);
|
|
377
|
+
if (query.type)
|
|
378
|
+
params.set('type', Array.isArray(query.type) ? query.type.join(',') : query.type);
|
|
379
|
+
if (query.limit)
|
|
380
|
+
params.set('limit', String(query.limit));
|
|
381
|
+
const suffix = params.toString() ? `?${params.toString()}` : '';
|
|
382
|
+
return coreRequest('GET', `/audit-events${suffix}`);
|
|
383
|
+
}
|
|
260
384
|
async function listScheduledJobs(workspaceId) {
|
|
261
385
|
const suffix = workspaceId ? `?workspace_id=${encodeURIComponent(workspaceId)}` : '';
|
|
262
386
|
return coreRequest('GET', `/scheduler/jobs${suffix}`);
|
|
@@ -7,6 +7,7 @@ const local_core_acp_transport_js_1 = require("./local-core-acp-transport.js");
|
|
|
7
7
|
const local_core_acp_turn_coordinator_js_1 = require("./local-core-acp-turn-coordinator.js");
|
|
8
8
|
const local_core_acp_session_coordinator_js_1 = require("./local-core-acp-session-coordinator.js");
|
|
9
9
|
const local_core_acp_response_processor_js_1 = require("./local-core-acp-response-processor.js");
|
|
10
|
+
const command_risk_js_1 = require("../security/command-risk.js");
|
|
10
11
|
const ACP_PROMPT_TIMEOUT_MS = 15 * 60 * 1000;
|
|
11
12
|
class LocalCoreAcpBackend {
|
|
12
13
|
options;
|
|
@@ -32,6 +33,42 @@ class LocalCoreAcpBackend {
|
|
|
32
33
|
},
|
|
33
34
|
updateRunStatus: (runId, threadId, status) => {
|
|
34
35
|
this.options.store.updateRun(runId, threadId, status);
|
|
36
|
+
const task = this.options.store.getAgentTaskByRunId(runId);
|
|
37
|
+
if (task) {
|
|
38
|
+
this.options.store.updateAgentTask(task.taskId, {
|
|
39
|
+
status: status === 'awaiting_input' ? 'waiting_for_user' : 'running',
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
createApprovalRequest: ({ threadId, runId, title, description, command, options }) => {
|
|
44
|
+
const row = this.options.store.getThreadRow(threadId);
|
|
45
|
+
if (!row) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
const task = this.options.store.getAgentTaskByRunId(runId);
|
|
49
|
+
const classification = (0, command_risk_js_1.classifyCommandRisk)(command || description || title);
|
|
50
|
+
const approval = this.options.store.createApprovalRequest({
|
|
51
|
+
workspaceId: row.workspace_id,
|
|
52
|
+
taskId: task?.taskId,
|
|
53
|
+
threadId,
|
|
54
|
+
runId,
|
|
55
|
+
deviceId: 'local',
|
|
56
|
+
kind: classification.scopes.includes('git.modify') ? 'git' : 'command',
|
|
57
|
+
riskLevel: classification.riskLevel,
|
|
58
|
+
title,
|
|
59
|
+
description,
|
|
60
|
+
requestedAction: command || description || title,
|
|
61
|
+
command,
|
|
62
|
+
scopes: classification.scopes,
|
|
63
|
+
options: options.map((option) => ({
|
|
64
|
+
optionId: option.optionId,
|
|
65
|
+
label: option.name || option.optionId,
|
|
66
|
+
action: option.normalizedAction === 'deny' ? 'reject' : 'approve',
|
|
67
|
+
})),
|
|
68
|
+
requestedBy: 'agent',
|
|
69
|
+
metadata: { classification },
|
|
70
|
+
});
|
|
71
|
+
return approval.approvalId;
|
|
35
72
|
},
|
|
36
73
|
sendRaw: (session, payload) => this.transport.sendRaw(session, payload),
|
|
37
74
|
});
|
|
@@ -112,6 +149,16 @@ class LocalCoreAcpBackend {
|
|
|
112
149
|
const runId = `run:${threadId}:${Date.now()}`;
|
|
113
150
|
this.options.runThreadMap.set(runId, threadId);
|
|
114
151
|
this.options.store.updateRun(runId, threadId, 'running');
|
|
152
|
+
this.options.store.createAgentTask({
|
|
153
|
+
workspaceId: row.workspace_id,
|
|
154
|
+
deviceId: 'local',
|
|
155
|
+
runtimeId: row.agent_type,
|
|
156
|
+
threadId,
|
|
157
|
+
runId,
|
|
158
|
+
title: content.trim().slice(0, 80) || row.title || 'Agent task',
|
|
159
|
+
prompt: content,
|
|
160
|
+
status: 'running',
|
|
161
|
+
});
|
|
115
162
|
this.options.eventBus.emit({
|
|
116
163
|
type: 'run.started',
|
|
117
164
|
payload: {
|
|
@@ -154,6 +201,13 @@ class LocalCoreAcpBackend {
|
|
|
154
201
|
if (!accepted) {
|
|
155
202
|
throw new Error(session.closeReason || 'ACP session is not writable');
|
|
156
203
|
}
|
|
204
|
+
if (pendingPermission.approvalId) {
|
|
205
|
+
this.options.store.resolveApprovalRequest(pendingPermission.approvalId, {
|
|
206
|
+
status: matched.normalizedAction === 'deny' ? 'rejected' : 'approved',
|
|
207
|
+
resolvedBy: 'local',
|
|
208
|
+
resolution: matched.name || matched.optionId,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
157
211
|
session.pendingPermissionByRun.delete(session.currentRunId);
|
|
158
212
|
this.emitBridgeEvent({
|
|
159
213
|
type: 'typing_start',
|
|
@@ -280,6 +334,13 @@ class LocalCoreAcpBackend {
|
|
|
280
334
|
}
|
|
281
335
|
const nextStatus = result?.stopReason === 'cancelled' ? 'interrupted' : 'completed';
|
|
282
336
|
this.options.store.updateRun(runId, threadId, nextStatus);
|
|
337
|
+
const task = this.options.store.getAgentTaskByRunId(runId);
|
|
338
|
+
if (task) {
|
|
339
|
+
this.options.store.updateAgentTask(task.taskId, {
|
|
340
|
+
status: nextStatus === 'interrupted' ? 'cancelled' : 'completed',
|
|
341
|
+
summary: result?.stopReason === 'cancelled' ? 'Request cancelled.' : 'Task completed.',
|
|
342
|
+
});
|
|
343
|
+
}
|
|
283
344
|
this.options.eventBus.emit({
|
|
284
345
|
type: 'run.completed',
|
|
285
346
|
payload: {
|
|
@@ -298,6 +359,13 @@ class LocalCoreAcpBackend {
|
|
|
298
359
|
catch (error) {
|
|
299
360
|
const errorContent = `Agent error: ${error instanceof Error ? error.message : String(error)}`;
|
|
300
361
|
this.options.store.updateRun(runId, threadId, 'failed');
|
|
362
|
+
const task = this.options.store.getAgentTaskByRunId(runId);
|
|
363
|
+
if (task) {
|
|
364
|
+
this.options.store.updateAgentTask(task.taskId, {
|
|
365
|
+
status: 'failed',
|
|
366
|
+
error: error instanceof Error ? error.message : String(error),
|
|
367
|
+
});
|
|
368
|
+
}
|
|
301
369
|
this.options.store.appendMessage(threadId, 'assistant', errorContent, 'final');
|
|
302
370
|
this.options.eventBus.emit({
|
|
303
371
|
type: 'thread.message.accepted',
|