@kafca/agentdock 0.1.30 → 0.1.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/LICENSE.md +73 -0
  2. package/README.md +15 -0
  3. package/dist/renderer/assets/{Badge-DlVJI1Ys.js → Badge-VeSLCPPV.js} +1 -1
  4. package/dist/renderer/assets/{Chat-oa8OjaHQ.js → Chat-DWTQeTgn.js} +1 -1
  5. package/dist/renderer/assets/{ChatMarkdown-C-N8KS5C.js → ChatMarkdown-C9gSQIVb.js} +1 -1
  6. package/dist/renderer/assets/{Config-D9sL_d4D.js → Config-CTA-pZNR.js} +1 -1
  7. package/dist/renderer/assets/{CronList-Z5JU3DHk.js → CronList-BngBCnFY.js} +1 -1
  8. package/dist/renderer/assets/{Dashboard-D3BwbG43.js → Dashboard-D4pGvDpc.js} +2 -2
  9. package/dist/renderer/assets/{EmptyState-QOj765Ul.js → EmptyState-DMf8rzDs.js} +1 -1
  10. package/dist/renderer/assets/{KnowledgeDetail-J7n01mPu.js → KnowledgeDetail-C1Ym_cHk.js} +1 -1
  11. package/dist/renderer/assets/{KnowledgeHome-BFnH0NVk.js → KnowledgeHome-DPRiR72G.js} +1 -1
  12. package/dist/renderer/assets/{Logs-DfSUifXc.js → Logs-CRHecmzO.js} +1 -1
  13. package/dist/renderer/assets/{Modal-QjEGhyru.js → Modal-BDhhPv2l.js} +1 -1
  14. package/dist/renderer/assets/{Page-qdHFSw7x.js → Page-DdDZN7AE.js} +1 -1
  15. package/dist/renderer/assets/{ProjectDetail-DS_rfvhh.js → ProjectDetail-DCsr3LkW.js} +1 -1
  16. package/dist/renderer/assets/{ProjectList-COtyZd7H.js → ProjectList-Cnm-iLvg.js} +1 -1
  17. package/dist/renderer/assets/{Select-BQ8mncVu.js → Select-Dw4FCXhJ.js} +1 -1
  18. package/dist/renderer/assets/{SessionChat-DjVnjuQy.js → SessionChat-O-6abK8x.js} +1 -1
  19. package/dist/renderer/assets/{SessionList-88dgGZJ8.js → SessionList-5yptvVL9.js} +1 -1
  20. package/dist/renderer/assets/ThreadChat-CIM_o2HO.js +23 -0
  21. package/dist/renderer/assets/{Workspace-D_hzSJjX.js → Workspace-D9UeUCNr.js} +1 -1
  22. package/dist/renderer/assets/{arrow-left-BMpknulW.js → arrow-left-CKfxNOXE.js} +1 -1
  23. package/dist/renderer/assets/{book-open-CiaOeQbU.js → book-open-CQddlENV.js} +1 -1
  24. package/dist/renderer/assets/{check-CgwYH4GF.js → check-CZYvOmIS.js} +1 -1
  25. package/dist/renderer/assets/{chevron-down-BzD2S1OO.js → chevron-down-CVIWFoXF.js} +1 -1
  26. package/dist/renderer/assets/{circle-BDHD7g7l.js → circle-DSfdQM7k.js} +1 -1
  27. package/dist/renderer/assets/{desktop-BiR5LPL5.js → desktop-B9KUgeOV.js} +1 -1
  28. package/dist/renderer/assets/{desktop-BkMW-h5p.js → desktop-O8_6ZCVZ.js} +2 -2
  29. package/dist/renderer/assets/{funnel-Bruf5FYA.js → funnel-pB5NegZB.js} +1 -1
  30. package/dist/renderer/assets/{heart-D7Mz04B0.js → heart-BT5laZ_b.js} +1 -1
  31. package/dist/renderer/assets/{index-CL1e8m3U.js → index-BYyDk28K.js} +2 -2
  32. package/dist/renderer/assets/{index-gZIRr4_o.js → index-zTUUYax3.js} +1 -1
  33. package/dist/renderer/assets/{pencil-C6gzDMse.js → pencil-C0thxLi6.js} +1 -1
  34. package/dist/renderer/assets/{play-DqTXX-Pr.js → play-CmZ0kWtt.js} +1 -1
  35. package/dist/renderer/assets/{plug-ykmbeaO3.js → plug-CeDQatBe.js} +1 -1
  36. package/dist/renderer/assets/{plus-DHE60vMX.js → plus-Di__xFOZ.js} +1 -1
  37. package/dist/renderer/assets/projects-B-pbhhtH.js +1 -0
  38. package/dist/renderer/assets/{save-CGIvrdQI.js → save-BDatCjfO.js} +1 -1
  39. package/dist/renderer/assets/{search-D7IFAwyE.js → search-DnTOjP0C.js} +1 -1
  40. package/dist/renderer/assets/{server-aOsirp7I.js → server-Ey_iHY6m.js} +1 -1
  41. package/dist/renderer/assets/{session-utils-Dxfp4ohQ.js → session-utils-DWaM7Rkr.js} +1 -1
  42. package/dist/renderer/assets/{sessions-Db1XPbG2.js → sessions-CYYjLD5I.js} +1 -1
  43. package/dist/renderer/assets/{shield-check-ChXeQK-d.js → shield-check-Br8BQzcn.js} +1 -1
  44. package/dist/renderer/assets/{trash-2-CfN-c1zw.js → trash-2-7YD616fw.js} +1 -1
  45. package/dist/renderer/assets/{user-C7OHaBp4.js → user-BR_5jmr9.js} +1 -1
  46. package/dist/renderer/assets/{wifi-off-CpaJigye.js → wifi-off-By9QdREz.js} +1 -1
  47. package/dist/renderer/index.html +1 -1
  48. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +28 -4
  49. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-response-processor.js +2 -4
  50. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +102 -15
  51. package/dist-electron/services/local-ai-core/src/agents/claudecode/behavior.js +5 -0
  52. package/dist-electron/services/local-ai-core/src/agents/claudecode/definition.js +36 -0
  53. package/dist-electron/services/local-ai-core/src/agents/claudecode/index.js +15 -0
  54. package/dist-electron/services/local-ai-core/src/agents/codex/behavior.js +5 -0
  55. package/dist-electron/services/local-ai-core/src/agents/codex/definition.js +24 -0
  56. package/dist-electron/services/local-ai-core/src/agents/codex/index.js +15 -0
  57. package/dist-electron/services/local-ai-core/src/agents/hermes/behavior.js +29 -0
  58. package/dist-electron/services/local-ai-core/src/agents/hermes/definition.js +22 -0
  59. package/dist-electron/services/local-ai-core/src/agents/hermes/index.js +15 -0
  60. package/dist-electron/services/local-ai-core/src/agents/index.js +98 -0
  61. package/dist-electron/services/local-ai-core/src/agents/localcore-acp/behavior.js +5 -0
  62. package/dist-electron/services/local-ai-core/src/agents/localcore-acp/definition.js +14 -0
  63. package/dist-electron/services/local-ai-core/src/agents/localcore-acp/index.js +27 -0
  64. package/dist-electron/services/local-ai-core/src/agents/opencode/behavior.js +5 -0
  65. package/dist-electron/services/local-ai-core/src/agents/opencode/definition.js +18 -0
  66. package/dist-electron/services/local-ai-core/src/agents/opencode/index.js +15 -0
  67. package/dist-electron/services/local-ai-core/src/agents/opencode/launch.js +119 -0
  68. package/dist-electron/services/local-ai-core/src/agents/pi/behavior.js +5 -0
  69. package/dist-electron/services/local-ai-core/src/agents/pi/definition.js +23 -0
  70. package/dist-electron/services/local-ai-core/src/agents/pi/index.js +15 -0
  71. package/dist-electron/services/local-ai-core/src/agents/pi/launch.js +139 -0
  72. package/dist-electron/services/local-ai-core/src/agents/shared/acp-behavior.js +11 -0
  73. package/dist-electron/services/local-ai-core/src/agents/shared/agent-plugin.js +60 -0
  74. package/dist-electron/services/local-ai-core/src/agents/shared/definition.js +12 -0
  75. package/dist-electron/services/local-ai-core/src/agents/shared/launch-utils.js +122 -0
  76. package/dist-electron/services/local-ai-core/src/agents/shared/progress-replay.js +19 -0
  77. package/dist-electron/services/local-ai-core/src/agents/shared/replay-prefix.js +68 -0
  78. package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +0 -2
  79. package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +0 -1
  80. package/dist-electron/services/local-ai-core/src/cli/lac.js +2 -1
  81. package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +17 -11
  82. package/dist-electron/services/local-ai-core/src/plugins/builtin/agent-localcore-acp-plugin.js +9 -130
  83. package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +22 -397
  84. package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +9 -45
  85. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +9 -4
  86. package/dist-electron/services/local-ai-core/src/runtime/local-core-runtime-state.js +62 -1
  87. package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-service.js +2 -12
  88. package/dist-electron/services/local-ai-core/src/scheduler/lark-schedule-adapter.js +16 -7
  89. package/dist-electron/services/local-ai-core/src/scheduler/local-schedule-adapter.js +10 -2
  90. package/dist-electron/services/local-ai-core/src/scheduler/scheduled-job-route.js +24 -0
  91. package/dist-electron/services/local-ai-core/src/scheduler/weixin-schedule-adapter.js +15 -6
  92. package/dist-electron/shared/desktop.js +3 -1
  93. package/dist-electron/src/pages/Threads/thread-chat-model.js +27 -18
  94. package/dist-electron/src/pages/Threads/thread-chat-permission.test.js +57 -18
  95. package/dist-electron/{electron → tests/contracts}/local-core-contracts.test.js +1 -1
  96. package/dist-electron/{electron → tests/electron}/agent-runtime-detector.test.js +31 -1
  97. package/dist-electron/{electron → tests/electron}/lac-cli.test.js +6 -3
  98. package/dist-electron/{electron → tests/electron}/plugin-kernel.test.js +93 -13
  99. package/dist-electron/{electron → tests/electron}/renderer-packaging.test.js +11 -6
  100. package/dist-electron/{electron → tests/electron}/runtime-detection-service.test.js +1 -1
  101. package/dist-electron/{electron → tests/electron}/security-approval-store.test.js +2 -2
  102. package/dist-electron/{electron → tests/electron}/workspace-task-store.test.js +95 -7
  103. package/dist-electron/{electron → tests/integration}/channel-content.test.js +2 -2
  104. package/dist-electron/{electron → tests/integration}/local-core-acp-content.test.js +1 -1
  105. package/dist-electron/{electron → tests/integration}/local-core-refactor.test.js +437 -15
  106. package/package.json +3 -2
  107. package/dist/renderer/assets/ThreadChat-BCdbTxBo.js +0 -23
  108. package/dist/renderer/assets/projects-Bx5p10th.js +0 -1
  109. /package/dist-electron/{electron → tests/contracts}/architecture-docs.test.js +0 -0
  110. /package/dist-electron/{electron → tests/electron}/knowledge-skill-script.test.js +0 -0
@@ -3,10 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.normalizePlatformTypes = normalizePlatformTypes;
4
4
  exports.isLocalCoreNativeAcpProject = isLocalCoreNativeAcpProject;
5
5
  exports.toLocalCoreProjectConfig = toLocalCoreProjectConfig;
6
- const node_module_1 = require("node:module");
7
- const node_fs_1 = require("node:fs");
8
6
  const node_path_1 = require("node:path");
9
7
  const desktop_js_1 = require("../../../../shared/desktop.js");
8
+ const index_js_1 = require("../agents/index.js");
9
+ const launch_utils_js_1 = require("../agents/shared/launch-utils.js");
10
10
  function normalizePlatformTypes(project) {
11
11
  return Array.isArray(project?.platforms)
12
12
  ? project.platforms.map((platform) => (0, desktop_js_1.normalizeDesktopPlatformType)(platform?.type)).filter(Boolean)
@@ -18,348 +18,16 @@ function isLocalCoreNativeAcpProject(project) {
18
18
  || agentType === 'acp'
19
19
  || agentType === desktop_js_1.LOCALCORE_ACP_AGENT_TYPE;
20
20
  }
21
- function resolveAgentModel(project, providers) {
21
+ function resolveAgentModel(project, providers, definition) {
22
22
  const agentType = String(project.agent?.type || '').trim().toLowerCase();
23
23
  const rawModel = String(project.agent?.options?.model || '').trim();
24
24
  const normalizedModel = (0, desktop_js_1.normalizeDesktopAgentModel)(agentType, rawModel);
25
- if (agentType === 'pi') {
26
- return rawModel || getFirstProviderModelId(providers);
27
- }
28
- if (agentType !== 'opencode') {
29
- return normalizedModel;
30
- }
31
- const configuredProviderModel = getFirstProviderModelRef(providers);
32
- if (configuredProviderModel &&
33
- (!rawModel || normalizedModel === desktop_js_1.DEFAULT_DESKTOP_OPENCODE_MODEL)) {
34
- return configuredProviderModel;
35
- }
36
- return normalizedModel;
37
- }
38
- function getFirstProviderModelId(providers) {
39
- for (const provider of providers) {
40
- const modelId = getProviderDefaultModelId(provider);
41
- if (modelId) {
42
- return modelId;
43
- }
44
- }
45
- return '';
46
- }
47
- function getFirstProviderModelRef(providers) {
48
- for (const provider of providers) {
49
- const providerId = normalizeOpencodeProviderId(provider.name);
50
- const modelId = getProviderDefaultModelId(provider);
51
- if (providerId && modelId) {
52
- return `${providerId}/${modelId}`;
53
- }
54
- }
55
- return '';
56
- }
57
- function getProviderDefaultModelId(provider) {
58
- const directModel = String(provider.model || '').trim();
59
- if (directModel) {
60
- return directModel;
61
- }
62
- const firstModel = Array.isArray(provider.models)
63
- ? provider.models.find((entry) => String(entry?.model || '').trim())
64
- : null;
65
- return String(firstModel?.model || '').trim();
66
- }
67
- function buildOpencodeInlineConfig(model, providers) {
68
- const config = {
69
- $schema: 'https://opencode.ai/config.json',
70
- };
71
- const env = {};
72
- if (model) {
73
- config.model = model;
74
- }
75
- const providerConfig = {};
76
- for (const provider of providers) {
77
- const providerId = normalizeOpencodeProviderId(provider.name);
78
- if (!providerId) {
79
- continue;
80
- }
81
- const entry = {
82
- name: String(provider.name || providerId),
83
- };
84
- if (shouldUseOpenAiCompatibleProvider(providerId)) {
85
- entry.npm = '@ai-sdk/openai-compatible';
86
- }
87
- const options = {};
88
- const baseUrl = String(provider.base_url || '').trim();
89
- if (baseUrl) {
90
- options.baseURL = baseUrl;
91
- }
92
- const apiKey = String(provider.api_key || '').trim();
93
- if (apiKey) {
94
- const envName = opencodeProviderApiKeyEnvName(providerId);
95
- env[envName] = apiKey;
96
- options.apiKey = `{env:${envName}}`;
97
- }
98
- if (Object.keys(options).length > 0) {
99
- entry.options = options;
100
- }
101
- const models = buildOpencodeProviderModels(provider);
102
- if (Object.keys(models).length > 0) {
103
- entry.models = models;
104
- }
105
- providerConfig[providerId] = entry;
106
- }
107
- if (Object.keys(providerConfig).length > 0) {
108
- config.provider = providerConfig;
109
- }
110
- return {
111
- config,
112
- env,
113
- };
114
- }
115
- function buildOpencodeProviderModels(provider) {
116
- const models = {};
117
- const addModel = (model, alias) => {
118
- const modelId = String(model || '').trim();
119
- if (!modelId || models[modelId]) {
120
- return;
121
- }
122
- models[modelId] = {
123
- name: String(alias || modelId).trim() || modelId,
124
- };
125
- };
126
- addModel(provider.model);
127
- for (const model of Array.isArray(provider.models) ? provider.models : []) {
128
- addModel(model?.model, model?.alias);
129
- }
130
- return models;
131
- }
132
- function collectProviderEnv(providers) {
133
- const env = {};
134
- for (const provider of providers) {
135
- if (!provider.env || typeof provider.env !== 'object') {
136
- continue;
137
- }
138
- for (const [key, value] of Object.entries(provider.env)) {
139
- const envKey = key.trim();
140
- if (envKey) {
141
- env[envKey] = String(value ?? '');
142
- }
143
- }
144
- }
145
- return env;
146
- }
147
- function collectPiProviderEnv(providers) {
148
- const env = {};
149
- for (const provider of providers) {
150
- const apiKey = String(provider.api_key || '').trim();
151
- const envName = piProviderApiKeyEnvName(provider.name);
152
- if (apiKey && envName) {
153
- env[envName] = apiKey;
154
- }
155
- }
156
- return env;
157
- }
158
- function resolvePiProviderModel(providers, model) {
159
- const [explicitProvider, explicitModel] = splitProviderModelRef(model);
160
- if (explicitProvider && explicitModel) {
161
- return {
162
- providerId: normalizePiProviderId(explicitProvider),
163
- modelId: explicitModel,
164
- };
165
- }
166
- for (const provider of providers) {
167
- const providerId = normalizePiProviderId(provider.name);
168
- const modelId = getProviderDefaultModelId(provider);
169
- if (providerId && modelId && (!model || model === modelId)) {
170
- return { providerId, modelId };
171
- }
172
- }
173
- const fallbackProvider = providers.find((provider) => normalizePiProviderId(provider.name));
174
- return {
175
- providerId: normalizePiProviderId(fallbackProvider?.name),
176
- modelId: model,
177
- };
178
- }
179
- function splitProviderModelRef(value) {
180
- const trimmed = value.trim();
181
- const slashIndex = trimmed.indexOf('/');
182
- if (slashIndex <= 0 || slashIndex >= trimmed.length - 1) {
183
- return ['', trimmed];
184
- }
185
- return [trimmed.slice(0, slashIndex), trimmed.slice(slashIndex + 1)];
186
- }
187
- function normalizePiProviderId(providerName) {
188
- const providerId = normalizeProviderId(providerName);
189
- const aliases = {
190
- google: 'google',
191
- gemini: 'google',
192
- kimi: 'kimi-coding',
193
- moonshot: 'moonshotai',
194
- zhipuai: 'zai',
195
- };
196
- return aliases[providerId] || providerId;
197
- }
198
- function piProviderAgentDir(configState, projectName) {
199
- const configDir = (0, node_path_1.dirname)(configState.path);
200
- const safeProjectName = String(projectName || 'workspace')
201
- .trim()
202
- .replace(/[^a-zA-Z0-9._-]+/g, '-')
203
- .replace(/^-+|-+$/g, '') || 'workspace';
204
- return (0, node_path_1.resolve)(configDir, '.pi-agent', safeProjectName);
205
- }
206
- function writeJsonFile(path, value, mode = 0o600) {
207
- (0, node_fs_1.writeFileSync)(path, `${JSON.stringify(value, null, 2)}\n`, { encoding: 'utf8', mode });
208
- (0, node_fs_1.chmodSync)(path, mode);
209
- }
210
- function writePiProviderRuntimeConfig(configState, project, providers, model) {
211
- if (providers.length === 0) {
212
- return {};
213
- }
214
- const { providerId, modelId } = resolvePiProviderModel(providers, model);
215
- const agentDir = piProviderAgentDir(configState, project.name);
216
- (0, node_fs_1.mkdirSync)(agentDir, { recursive: true, mode: 0o700 });
217
- (0, node_fs_1.chmodSync)(agentDir, 0o700);
218
- const auth = {};
219
- const modelProviders = {};
220
- for (const provider of providers) {
221
- const id = normalizePiProviderId(provider.name);
222
- if (!id) {
223
- continue;
224
- }
225
- const apiKey = String(provider.api_key || '').trim();
226
- const baseUrl = String(provider.base_url || '').trim();
227
- if (apiKey) {
228
- auth[id] = { type: 'api_key', key: apiKey };
229
- }
230
- if (baseUrl || apiKey) {
231
- modelProviders[id] = {
232
- name: String(provider.name || id).trim() || id,
233
- ...(baseUrl ? { baseUrl } : {}),
234
- ...(apiKey ? { apiKey } : {}),
235
- };
236
- }
237
- }
238
- writeJsonFile((0, node_path_1.resolve)(agentDir, 'auth.json'), auth);
239
- writeJsonFile((0, node_path_1.resolve)(agentDir, 'models.json'), { providers: modelProviders });
240
- writeJsonFile((0, node_path_1.resolve)(agentDir, 'settings.json'), {
241
- ...(providerId ? { defaultProvider: providerId } : {}),
242
- ...(modelId ? { defaultModel: modelId } : {}),
243
- quietStartup: true,
244
- }, 0o644);
245
- return {
246
- PI_CODING_AGENT_DIR: agentDir,
247
- };
248
- }
249
- function normalizeOpencodeProviderId(value) {
250
- return String(value || '')
251
- .trim()
252
- .toLowerCase()
253
- .replace(/[^a-z0-9._-]+/g, '-')
254
- .replace(/^-+|-+$/g, '');
255
- }
256
- function normalizeProviderId(value) {
257
- return String(value || '')
258
- .trim()
259
- .toLowerCase()
260
- .replace(/[^a-z0-9]+/g, '-')
261
- .replace(/^-+|-+$/g, '');
262
- }
263
- function piProviderApiKeyEnvName(providerName) {
264
- const providerId = normalizeProviderId(providerName);
265
- const envNames = {
266
- anthropic: 'ANTHROPIC_API_KEY',
267
- 'azure-openai': 'AZURE_OPENAI_API_KEY',
268
- 'azure-openai-responses': 'AZURE_OPENAI_API_KEY',
269
- cerebras: 'CEREBRAS_API_KEY',
270
- cloudflare: 'CLOUDFLARE_API_KEY',
271
- 'cloudflare-ai-gateway': 'CLOUDFLARE_API_KEY',
272
- 'cloudflare-workers-ai': 'CLOUDFLARE_API_KEY',
273
- deepseek: 'DEEPSEEK_API_KEY',
274
- fireworks: 'FIREWORKS_API_KEY',
275
- gemini: 'GEMINI_API_KEY',
276
- google: 'GEMINI_API_KEY',
277
- groq: 'GROQ_API_KEY',
278
- huggingface: 'HF_TOKEN',
279
- kimi: 'KIMI_API_KEY',
280
- 'kimi-coding': 'KIMI_API_KEY',
281
- minimax: 'MINIMAX_API_KEY',
282
- 'minimax-cn': 'MINIMAX_CN_API_KEY',
283
- mistral: 'MISTRAL_API_KEY',
284
- moonshot: 'KIMI_API_KEY',
285
- openai: 'OPENAI_API_KEY',
286
- opencode: 'OPENCODE_API_KEY',
287
- 'opencode-go': 'OPENCODE_API_KEY',
288
- openrouter: 'OPENROUTER_API_KEY',
289
- siliconflow: 'SILICONFLOW_API_KEY',
290
- vercel: 'AI_GATEWAY_API_KEY',
291
- 'vercel-ai-gateway': 'AI_GATEWAY_API_KEY',
292
- xai: 'XAI_API_KEY',
293
- zai: 'ZAI_API_KEY',
294
- zhipuai: 'ZAI_API_KEY',
295
- };
296
- return envNames[providerId] || '';
297
- }
298
- function opencodeProviderApiKeyEnvName(providerId) {
299
- const suffix = providerId.toUpperCase().replace(/[^A-Z0-9]+/g, '_').replace(/^_+|_+$/g, '');
300
- return `AI_WORKSTATION_OPENCODE_${suffix || 'PROVIDER'}_API_KEY`;
301
- }
302
- function shouldUseOpenAiCompatibleProvider(providerId) {
303
- const builtInNonCompatibleProviders = new Set([
304
- 'anthropic',
305
- 'google',
306
- 'gemini',
307
- ]);
308
- return !builtInNonCompatibleProviders.has(providerId);
309
- }
310
- function resolveBundledAcpCommand(packageName, binName) {
311
- const require = (0, node_module_1.createRequire)(__filename);
312
- const packageJsonPath = resolveBundledPackageJsonPath(packageName);
313
- const packageJson = require(packageJsonPath);
314
- const binField = packageJson.bin;
315
- const relativeBinPath = typeof binField === 'string'
316
- ? binField
317
- : binField?.[binName];
318
- if (!relativeBinPath) {
319
- throw new Error(`Bundled package "${packageName}" does not declare the ${binName} bin.`);
320
- }
321
- return {
322
- command: process.execPath,
323
- args: [(0, node_path_1.resolve)((0, node_path_1.dirname)(packageJsonPath), relativeBinPath)],
324
- };
325
- }
326
- function resolveBundledPackageJsonPath(packageName) {
327
- const require = (0, node_module_1.createRequire)(__filename);
328
- try {
329
- return require.resolve(`${packageName}/package.json`);
330
- }
331
- catch (error) {
332
- if (error?.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED') {
333
- throw error;
334
- }
335
- }
336
- for (const basePath of require.resolve.paths(packageName) || []) {
337
- const packageJsonPath = (0, node_path_1.resolve)(basePath, ...packageName.split('/'), 'package.json');
338
- if ((0, node_fs_1.existsSync)(packageJsonPath)) {
339
- return packageJsonPath;
340
- }
341
- }
342
- let current = (0, node_path_1.dirname)(require.resolve(packageName));
343
- while (current && current !== (0, node_path_1.dirname)(current)) {
344
- const packageJsonPath = (0, node_path_1.resolve)(current, 'package.json');
345
- if ((0, node_fs_1.existsSync)(packageJsonPath)) {
346
- return packageJsonPath;
347
- }
348
- current = (0, node_path_1.dirname)(current);
349
- }
350
- throw new Error(`Bundled package "${packageName}" package.json could not be resolved.`);
351
- }
352
- function resolveBundledCodexCommand() {
353
- return resolveBundledAcpCommand(desktop_js_1.DESKTOP_CODEX_ACP_PACKAGE, 'codex-acp');
354
- }
355
- function resolveBundledClaudeCodeCommand() {
356
- return resolveBundledAcpCommand(desktop_js_1.DESKTOP_CLAUDECODE_ACP_PACKAGE, 'claude-agent-acp');
357
- }
358
- function resolveBundledPiAcpCommand() {
359
- return resolveBundledAcpCommand(desktop_js_1.DESKTOP_PI_ACP_PACKAGE, 'pi-acp');
360
- }
361
- function resolveBundledPiCommand() {
362
- return resolveBundledAcpCommand(desktop_js_1.DESKTOP_PI_CODING_AGENT_PACKAGE, 'pi').args[0];
25
+ return definition?.resolveModel?.({
26
+ project,
27
+ providers,
28
+ rawModel,
29
+ normalizedModel,
30
+ }) || normalizedModel;
363
31
  }
364
32
  function toLocalCoreProjectConfig(configState, project) {
365
33
  const rawWorkDir = String(project.agent?.options?.work_dir || '.').trim() || '.';
@@ -377,63 +45,22 @@ function toLocalCoreProjectConfig(configState, project) {
377
45
  : {};
378
46
  const agentType = String(project.agent?.type || '').trim().toLowerCase();
379
47
  const providers = Array.isArray(project.agent?.providers) ? project.agent.providers : [];
380
- const model = resolveAgentModel(project, providers);
381
- const providerEnv = collectProviderEnv(providers);
382
- const piProviderEnv = agentType === 'pi'
383
- ? collectPiProviderEnv(providers)
384
- : {};
385
- const opencodeInlineConfig = agentType === 'opencode'
386
- ? buildOpencodeInlineConfig(model, providers)
387
- : null;
388
- const inferredOpencodeEnv = agentType === 'opencode'
389
- ? {
390
- ...(opencodeInlineConfig?.env || {}),
391
- OPENCODE_CONFIG_CONTENT: JSON.stringify(opencodeInlineConfig?.config || { $schema: 'https://opencode.ai/config.json' }),
392
- }
393
- : {};
394
- const inferredClaudeCodeEnv = agentType === 'claudecode' && model
395
- ? {
396
- ANTHROPIC_MODEL: model,
397
- }
398
- : {};
399
- const bundledPiAcp = agentType === 'pi'
400
- ? resolveBundledPiAcpCommand()
401
- : null;
402
- const bundledPiCommand = agentType === 'pi'
403
- ? resolveBundledPiCommand()
404
- : '';
405
- const piProviderRuntimeEnv = agentType === 'pi'
406
- ? writePiProviderRuntimeConfig(configState, project, providers, model)
407
- : {};
408
- const inferredPiEnv = agentType === 'pi'
409
- ? {
410
- ...piProviderEnv,
411
- ...piProviderRuntimeEnv,
412
- PI_ACP_PI_COMMAND: bundledPiCommand,
413
- }
414
- : {};
415
- const bundledCodex = agentType === 'codex'
416
- ? resolveBundledCodexCommand()
417
- : null;
418
- const bundledClaudeCode = agentType === 'claudecode'
419
- ? resolveBundledClaudeCodeCommand()
420
- : null;
421
- const inferredCommand = agentType === 'opencode'
422
- ? 'opencode'
423
- : bundledPiAcp?.command || bundledCodex?.command || bundledClaudeCode?.command || '';
48
+ const definition = (0, index_js_1.resolveAgentRuntimeDefinition)(agentType);
49
+ const model = resolveAgentModel(project, providers, definition);
50
+ const providerEnv = (0, launch_utils_js_1.collectProviderEnv)(providers);
51
+ const launchDefaults = definition?.buildLaunchConfig?.({
52
+ configState,
53
+ project,
54
+ agentType,
55
+ providers,
56
+ model,
57
+ }) || {};
58
+ const inferredCommand = launchDefaults.command || '';
424
59
  const command = String(project.agent?.options?.command || inferredCommand).trim();
425
60
  if (!command) {
426
61
  throw new Error(`Workspace "${project.name}" requires [projects.agent.options].command for Local AI Core ACP execution.`);
427
62
  }
428
- const defaultArgs = agentType === 'opencode'
429
- ? ['acp']
430
- : agentType === 'pi'
431
- ? [...(bundledPiAcp?.args || [])]
432
- : agentType === 'codex'
433
- ? [...(bundledCodex?.args || [])]
434
- : agentType === 'claudecode'
435
- ? [...(bundledClaudeCode?.args || [])]
436
- : [];
63
+ const defaultArgs = launchDefaults.args || [];
437
64
  return {
438
65
  workspaceId: project.name,
439
66
  agentType,
@@ -441,9 +68,7 @@ function toLocalCoreProjectConfig(configState, project) {
441
68
  command,
442
69
  args: args.length > 0 ? args : defaultArgs,
443
70
  env: {
444
- ...inferredOpencodeEnv,
445
- ...inferredClaudeCodeEnv,
446
- ...inferredPiEnv,
71
+ ...(launchDefaults.env || {}),
447
72
  ...providerEnv,
448
73
  ...env,
449
74
  },
@@ -6,44 +6,16 @@ const node_path_1 = require("node:path");
6
6
  const node_module_1 = require("node:module");
7
7
  const node_child_process_1 = require("node:child_process");
8
8
  const desktop_js_1 = require("../../../../shared/desktop.js");
9
- const DISPLAY_NAMES = {
10
- pi: 'Pi',
11
- opencode: 'OpenCode',
12
- codex: 'Codex',
13
- claudecode: 'Claude Code',
14
- cursor: 'Cursor',
15
- gemini: 'Gemini',
16
- qoder: 'Qoder',
17
- iflow: 'iFlow',
18
- [desktop_js_1.LOCALCORE_ACP_AGENT_TYPE]: 'LocalCore ACP',
19
- };
20
- const COMMAND_CANDIDATES = {
21
- pi: ['pi'],
22
- opencode: ['opencode'],
23
- codex: ['codex-acp', 'codex'],
24
- claudecode: ['claude-agent-acp', 'claude'],
25
- cursor: ['cursor-agent', 'cursor'],
26
- gemini: ['gemini'],
27
- qoder: ['qoder'],
28
- iflow: ['iflow'],
29
- };
30
- const VERSION_ARGUMENTS = {
31
- pi: ['--version'],
32
- opencode: ['--version'],
33
- codex: ['--version'],
34
- claudecode: ['--version'],
35
- cursor: ['--version'],
36
- gemini: ['--version'],
37
- qoder: ['--version'],
38
- iflow: ['--version'],
39
- };
9
+ const index_js_1 = require("../agents/index.js");
40
10
  function detectInstalledAgentRuntimes(options = {}) {
41
11
  const env = options.env || process.env;
42
12
  const configuredCommands = collectConfiguredAgentCommands(options.config);
43
13
  const detectedAt = (options.now || new Date()).toISOString();
44
14
  const versionTimeoutMs = options.versionTimeoutMs ?? 2500;
45
15
  return desktop_js_1.DESKTOP_AGENT_TYPE_OPTIONS.map((agentType) => {
46
- if (agentType === desktop_js_1.LOCALCORE_ACP_AGENT_TYPE) {
16
+ const definition = (0, index_js_1.resolveAgentRuntimeDefinition)(agentType);
17
+ const detection = definition?.detection;
18
+ if (detection?.builtin || agentType === desktop_js_1.LOCALCORE_ACP_AGENT_TYPE) {
47
19
  return {
48
20
  agentType,
49
21
  runtimeId: agentType,
@@ -64,21 +36,13 @@ function detectInstalledAgentRuntimes(options = {}) {
64
36
  ? installedRuntime(agentType, resolved, 'config', detectedAt, env, versionTimeoutMs)
65
37
  : missingRuntime(agentType, detectedAt, `Configured command not found: ${configured}`);
66
38
  }
67
- if (agentType === 'pi' || agentType === 'codex' || agentType === 'claudecode') {
68
- const bundled = agentType === 'pi'
69
- ? resolveBundledAcpPackage(options.requireFrom, desktop_js_1.DESKTOP_PI_CODING_AGENT_PACKAGE, ['dist/cli.js'])
70
- : agentType === 'codex'
71
- ? resolveBundledAcpPackage(options.requireFrom, desktop_js_1.DESKTOP_CODEX_ACP_PACKAGE, ['bin/codex-acp.js'])
72
- : resolveBundledAcpPackage(options.requireFrom, desktop_js_1.DESKTOP_CLAUDECODE_ACP_PACKAGE, [
73
- 'dist/cli.js',
74
- 'bin/claude-agent-acp.js',
75
- 'cli.js',
76
- ]);
39
+ for (const bundledRuntime of detection?.bundledRuntimes || []) {
40
+ const bundled = resolveBundledAcpPackage(options.requireFrom, bundledRuntime.packageName, bundledRuntime.candidates);
77
41
  if (bundled) {
78
42
  return installedRuntime(agentType, bundled, 'bundled', detectedAt, env, versionTimeoutMs);
79
43
  }
80
44
  }
81
- for (const command of COMMAND_CANDIDATES[agentType] || [agentType]) {
45
+ for (const command of detection?.commandCandidates || [agentType]) {
82
46
  const resolved = resolveCommand(command, env);
83
47
  if (resolved) {
84
48
  return installedRuntime(agentType, resolved, 'path', detectedAt, env, versionTimeoutMs);
@@ -143,7 +107,7 @@ function missingRuntime(agentType, detectedAt, error) {
143
107
  };
144
108
  }
145
109
  function displayName(agentType) {
146
- return DISPLAY_NAMES[agentType] || agentType;
110
+ return (0, index_js_1.resolveAgentRuntimeDefinition)(agentType)?.displayName || agentType;
147
111
  }
148
112
  function collectConfiguredAgentCommands(config) {
149
113
  const commands = new Map();
@@ -194,7 +158,7 @@ function isExecutableFile(path) {
194
158
  }
195
159
  }
196
160
  function detectRuntimeVersion(agentType, command, env, timeoutMs) {
197
- const args = VERSION_ARGUMENTS[agentType];
161
+ const args = (0, index_js_1.resolveAgentRuntimeDefinition)(agentType)?.detection?.versionArgs;
198
162
  if (!args || agentType === desktop_js_1.LOCALCORE_ACP_AGENT_TYPE) {
199
163
  return {};
200
164
  }
@@ -4,6 +4,7 @@ exports.LocalCoreController = void 0;
4
4
  const node_events_1 = require("node:events");
5
5
  const desktop_js_1 = require("../../../../shared/desktop.js");
6
6
  const bootstrap_js_1 = require("../kernel/bootstrap.js");
7
+ const scheduled_job_route_js_1 = require("../scheduler/scheduled-job-route.js");
7
8
  const runtime_detection_service_js_1 = require("./runtime-detection-service.js");
8
9
  class LocalCoreController extends node_events_1.EventEmitter {
9
10
  userDataPath;
@@ -195,7 +196,10 @@ class LocalCoreController extends node_events_1.EventEmitter {
195
196
  }
196
197
  resolveScheduledJobCreateInput(input) {
197
198
  if (input.platform && input.route) {
198
- return input;
199
+ return {
200
+ ...input,
201
+ route: (0, scheduled_job_route_js_1.withoutThreadRoute)(input.route),
202
+ };
199
203
  }
200
204
  const threadId = String(input.threadId || input.route?.threadId || '').trim();
201
205
  if (threadId) {
@@ -208,7 +212,6 @@ class LocalCoreController extends node_events_1.EventEmitter {
208
212
  type: 'channel.chat',
209
213
  channelId: binding.chat_id,
210
214
  participantId: binding.platform_user_id,
211
- threadId,
212
215
  },
213
216
  };
214
217
  }
@@ -219,12 +222,14 @@ class LocalCoreController extends node_events_1.EventEmitter {
219
222
  route: {
220
223
  type: 'local.thread',
221
224
  channelId: input.workspaceId,
222
- ...(threadId ? { threadId } : {}),
223
225
  },
224
226
  };
225
227
  }
226
228
  async updateScheduledJob(jobId, input) {
227
- return this.scheduler.updateJob(jobId, input);
229
+ return this.scheduler.updateJob(jobId, {
230
+ ...input,
231
+ ...(input.route ? { route: (0, scheduled_job_route_js_1.withoutThreadRoute)(input.route) } : {}),
232
+ });
228
233
  }
229
234
  async deleteScheduledJob(jobId) {
230
235
  return this.scheduler.deleteJob(jobId);
@@ -42,6 +42,8 @@ const DEFAULT_CONFIG = `# Managed by Local AI Core
42
42
  `;
43
43
  const LOG_FILE_NAME = 'local-core.log';
44
44
  const LOG_FILE_MAX_BYTES = 2 * 1024 * 1024;
45
+ const LOG_TAIL_CHUNK_BYTES = 64 * 1024;
46
+ const LOG_MAX_RETURN_LINES = 5000;
45
47
  class FileBackedLocalCoreRuntimeState {
46
48
  userDataPath;
47
49
  onLog;
@@ -112,7 +114,13 @@ class FileBackedLocalCoreRuntimeState {
112
114
  return this.settings.knowledge;
113
115
  }
114
116
  getLogs(limit = 200) {
115
- return this.logs.slice(-Math.max(limit, 1));
117
+ const normalizedLimit = normalizeLogLimit(limit);
118
+ const current = tailLogFile(this.logPath, normalizedLimit);
119
+ if (current.length >= normalizedLimit) {
120
+ return current.slice(-normalizedLimit);
121
+ }
122
+ const rotated = tailLogFile(`${this.logPath}.1`, normalizedLimit - current.length);
123
+ return [...rotated, ...current].slice(-normalizedLimit);
116
124
  }
117
125
  pushLog(message) {
118
126
  if (!message) {
@@ -273,6 +281,59 @@ function normalizePluginSettings(input) {
273
281
  }
274
282
  return output;
275
283
  }
284
+ function normalizeLogLimit(limit) {
285
+ if (!Number.isFinite(limit)) {
286
+ return 200;
287
+ }
288
+ return Math.min(Math.max(Math.floor(limit), 1), LOG_MAX_RETURN_LINES);
289
+ }
290
+ function tailLogFile(path, limit) {
291
+ if (limit <= 0 || !(0, node_fs_1.existsSync)(path)) {
292
+ return [];
293
+ }
294
+ let fd = -1;
295
+ try {
296
+ const size = (0, node_fs_1.statSync)(path).size;
297
+ if (size <= 0) {
298
+ return [];
299
+ }
300
+ fd = (0, node_fs_1.openSync)(path, 'r');
301
+ const chunks = [];
302
+ let position = size;
303
+ let newlineCount = 0;
304
+ while (position > 0 && newlineCount <= limit) {
305
+ const bytesToRead = Math.min(LOG_TAIL_CHUNK_BYTES, position);
306
+ position -= bytesToRead;
307
+ const chunk = Buffer.allocUnsafe(bytesToRead);
308
+ const bytesRead = (0, node_fs_1.readSync)(fd, chunk, 0, bytesToRead, position);
309
+ const slice = bytesRead === bytesToRead ? chunk : chunk.subarray(0, bytesRead);
310
+ chunks.unshift(slice);
311
+ for (let index = 0; index < slice.length; index += 1) {
312
+ if (slice[index] === 10) {
313
+ newlineCount += 1;
314
+ }
315
+ }
316
+ }
317
+ return Buffer.concat(chunks)
318
+ .toString('utf8')
319
+ .split(/\r?\n/)
320
+ .filter((line) => line.length > 0)
321
+ .slice(-limit);
322
+ }
323
+ catch {
324
+ return [];
325
+ }
326
+ finally {
327
+ if (fd >= 0) {
328
+ try {
329
+ (0, node_fs_1.closeSync)(fd);
330
+ }
331
+ catch {
332
+ // Log reads are best-effort; failed closes cannot be surfaced to callers.
333
+ }
334
+ }
335
+ }
336
+ }
276
337
  function createLocalCoreRuntimeState(options) {
277
338
  return new FileBackedLocalCoreRuntimeState(options.userDataPath, options.onLog);
278
339
  }