@kafca/agentdock 0.1.24 → 0.1.25

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 (83) hide show
  1. package/README.md +2 -0
  2. package/dist/renderer/assets/{Badge-CXcUMqb-.js → Badge-Bn2h-Qkf.js} +1 -1
  3. package/dist/renderer/assets/{Chat-BRxdPzi-.js → Chat-Cgo9aneg.js} +1 -1
  4. package/dist/renderer/assets/{ChatMarkdown-sWGBIfIb.js → ChatMarkdown-CYzHfSiR.js} +1 -1
  5. package/dist/renderer/assets/{Config-DqxuxIGg.js → Config-DljB2-eP.js} +1 -1
  6. package/dist/renderer/assets/{CronList-OgHxbti5.js → CronList-DeVbiY9a.js} +1 -1
  7. package/dist/renderer/assets/{Dashboard-DmklXiH1.js → Dashboard-fSqx5sm8.js} +2 -2
  8. package/dist/renderer/assets/{EmptyState-BvDmi8dq.js → EmptyState-DgqKDwlq.js} +1 -1
  9. package/dist/renderer/assets/{KnowledgeDetail-Bj59Hdpo.js → KnowledgeDetail-u8s2SmPV.js} +1 -1
  10. package/dist/renderer/assets/{KnowledgeHome-CigQ-nSo.js → KnowledgeHome-BdQw8nbY.js} +1 -1
  11. package/dist/renderer/assets/{Logs-Bfc7gBpY.js → Logs-DSbndMpy.js} +1 -1
  12. package/dist/renderer/assets/{Modal-CfmiFknz.js → Modal-0d0EmAuK.js} +1 -1
  13. package/dist/renderer/assets/{Page-BxtLEw_b.js → Page-CVbOH_Kh.js} +1 -1
  14. package/dist/renderer/assets/{ProjectDetail-CcUphxGm.js → ProjectDetail-C91tF7uA.js} +1 -1
  15. package/dist/renderer/assets/{ProjectList-CVkvDIfy.js → ProjectList-D78Pc-BQ.js} +1 -1
  16. package/dist/renderer/assets/{Select-DgQMZ0MD.js → Select-BbKzAIuO.js} +1 -1
  17. package/dist/renderer/assets/{SessionChat-CRmGKfBx.js → SessionChat-BERvL5e4.js} +1 -1
  18. package/dist/renderer/assets/{SessionList-D0E8XbYw.js → SessionList-Bomz335M.js} +1 -1
  19. package/dist/renderer/assets/ThreadChat-CLr3amU2.js +23 -0
  20. package/dist/renderer/assets/{Workspace-Q4g4rlRT.js → Workspace-In91z4W2.js} +1 -1
  21. package/dist/renderer/assets/{arrow-left-BySN_zhF.js → arrow-left-FBnVKKBl.js} +1 -1
  22. package/dist/renderer/assets/{book-open-BGlrnrTc.js → book-open-4_CuP6Sa.js} +1 -1
  23. package/dist/renderer/assets/{check-BiJogEa5.js → check-vsoVjvac.js} +1 -1
  24. package/dist/renderer/assets/{chevron-down-D2fHRJv6.js → chevron-down-DFpmFTJW.js} +1 -1
  25. package/dist/renderer/assets/{circle-CIcjixmI.js → circle-vRULegER.js} +1 -1
  26. package/dist/renderer/assets/{desktop-DAXY9Bxv.js → desktop-BkMW-h5p.js} +2 -2
  27. package/dist/renderer/assets/{desktop-DyA_NYew.js → desktop-ul0Tpzyc.js} +1 -1
  28. package/dist/renderer/assets/{funnel-BUzwb-id.js → funnel-Gu0E_hJ_.js} +1 -1
  29. package/dist/renderer/assets/{heart-DZEQ1z_V.js → heart-DsdofOHf.js} +1 -1
  30. package/dist/renderer/assets/{index-CB_KyLOr.js → index-BD3aCyBa.js} +3 -3
  31. package/dist/renderer/assets/{index-DgsIo4vJ.js → index-pJUFobU7.js} +1 -1
  32. package/dist/renderer/assets/{pencil-DGL4agcl.js → pencil-DbKmV8nD.js} +1 -1
  33. package/dist/renderer/assets/{play-DzasCLM4.js → play-BDMsRxSm.js} +1 -1
  34. package/dist/renderer/assets/{plug-dubNMe_m.js → plug-t2dMl78-.js} +1 -1
  35. package/dist/renderer/assets/{plus-DN4kML4k.js → plus-BUb6zOAe.js} +1 -1
  36. package/dist/renderer/assets/projects-4F6D_Q8t.js +1 -0
  37. package/dist/renderer/assets/{save-CNIc9tjl.js → save-C-_5rQdv.js} +1 -1
  38. package/dist/renderer/assets/{search-C0EGbF3I.js → search-R3ekHFPM.js} +1 -1
  39. package/dist/renderer/assets/{server-BMxQ4zmc.js → server-BTuvHf5n.js} +1 -1
  40. package/dist/renderer/assets/{session-utils-Bf5O-iMi.js → session-utils-ClX6VPFG.js} +1 -1
  41. package/dist/renderer/assets/{sessions-BMSs6rEI.js → sessions-AFKIC-ub.js} +1 -1
  42. package/dist/renderer/assets/{shield-check-i3Tf1TUz.js → shield-check-CqMW8CHe.js} +1 -1
  43. package/dist/renderer/assets/{trash-2-SKrcp18f.js → trash-2-BNLcHyUw.js} +1 -1
  44. package/dist/renderer/assets/{user-cNUZddjZ.js → user-BCCN8FYK.js} +1 -1
  45. package/dist/renderer/assets/{wifi-off-Dyb84ANR.js → wifi-off-BsNsx3oX.js} +1 -1
  46. package/dist/renderer/index.html +1 -1
  47. package/dist-electron/electron/agent-runtime-detector.test.js +64 -0
  48. package/dist-electron/electron/architecture-docs.test.js +56 -0
  49. package/dist-electron/electron/channel-content.test.js +8 -0
  50. package/dist-electron/electron/lac-cli.test.js +248 -0
  51. package/dist-electron/electron/local-core-refactor.test.js +666 -3
  52. package/dist-electron/electron/plugin-kernel.test.js +108 -0
  53. package/dist-electron/electron/renderer-packaging.test.js +77 -0
  54. package/dist-electron/electron/workspace-task-store.test.js +57 -0
  55. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +67 -6
  56. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-permission-lifecycle.js +76 -0
  57. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-progress.js +201 -0
  58. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-session-coordinator.js +29 -0
  59. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-store.js +30 -18
  60. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +112 -210
  61. package/dist-electron/services/local-ai-core/src/acp/local-core-slash-commands.js +60 -0
  62. package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +15 -3
  63. package/dist-electron/services/local-ai-core/src/channel/lark/runtime-state.js +11 -0
  64. package/dist-electron/services/local-ai-core/src/channel/shared/file-utils.js +13 -1
  65. package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +8 -1
  66. package/dist-electron/services/local-ai-core/src/cli/lac.js +3 -1
  67. package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +1 -0
  68. package/dist-electron/services/local-ai-core/src/plugins/builtin/agent-localcore-acp-plugin.js +9 -0
  69. package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +221 -10
  70. package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +39 -9
  71. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +1 -0
  72. package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-service.js +1 -0
  73. package/dist-electron/services/local-ai-core/src/runtime/server-routes.js +439 -0
  74. package/dist-electron/services/local-ai-core/src/runtime/server.js +208 -303
  75. package/dist-electron/shared/desktop.js +7 -3
  76. package/dist-electron/src/pages/Threads/thread-chat-message-blocks.js +187 -0
  77. package/dist-electron/src/pages/Threads/thread-chat-model.js +1 -0
  78. package/dist-electron/src/pages/Threads/thread-chat-page-state.js +96 -0
  79. package/dist-electron/src/pages/Threads/thread-chat-permission.js +18 -0
  80. package/dist-electron/src/pages/Threads/thread-chat-permission.test.js +277 -0
  81. package/package.json +4 -2
  82. package/dist/renderer/assets/ThreadChat-DUgqZSBB.js +0 -23
  83. package/dist/renderer/assets/projects-migPtfRe.js +0 -1
@@ -643,6 +643,10 @@ class LocalCoreWeixinGateway extends node_events_1.EventEmitter {
643
643
  if (slashCommand?.name === 'new') {
644
644
  const title = slashCommand.args.join(' ').trim() || `${input.displayName || 'WeChat'} ${new Date().toLocaleTimeString()}`;
645
645
  const nextThread = await router.createThread(input.workspaceId, title);
646
+ const inheritedMode = this.options.store.getThreadRow?.(threadId)?.agent_mode || '';
647
+ if (inheritedMode && inheritedMode !== 'default') {
648
+ this.options.store.updateThreadAgentMode?.(nextThread.id, inheritedMode);
649
+ }
646
650
  const now = new Date().toISOString();
647
651
  this.options.store.updateAuthorizedUserThread(input.workspaceId, input.platformUserId, nextThread.id, platformKey);
648
652
  this.options.store.upsertPlatformThreadBinding({
@@ -676,7 +680,9 @@ class LocalCoreWeixinGateway extends node_events_1.EventEmitter {
676
680
  return { paired: true, threadId };
677
681
  }
678
682
  this.options.store.updatePlatformThreadMessageId(input.workspaceId, input.chatId, input.platformUserId, input.contextToken || input.messageId, platformKey);
679
- const wrappedText = (0, desktop_js_1.wrapUserMessageWithSchedulerProtocol)(input.text);
683
+ const wrappedText = slashCommand
684
+ ? input.text
685
+ : (0, desktop_js_1.wrapUserMessageWithSchedulerProtocol)(input.text);
680
686
  await router.sendThreadMessage(threadId, (0, content_js_1.createChannelThreadMessageInput)(wrappedText, input.contentParts));
681
687
  return { paired: true, threadId };
682
688
  }
@@ -920,6 +926,7 @@ class LocalCoreWeixinGateway extends node_events_1.EventEmitter {
920
926
  const file = await (0, file_utils_js_1.prepareChannelFile)({
921
927
  path: part.path,
922
928
  fileName: part.fileName,
929
+ workspacePath: typeof part.metadata?.workspacePath === 'string' ? part.metadata.workspacePath : undefined,
923
930
  maxBytes: WEIXIN_MAX_UPLOAD_FILE_SIZE,
924
931
  platformLabel: 'WeChat',
925
932
  });
@@ -75,6 +75,7 @@ async function handleChannelSendFile(flags, env, io, json) {
75
75
  type: 'file',
76
76
  path: filePath,
77
77
  fileName: getFlag(flags, 'name') || undefined,
78
+ metadata: context.workspacePath ? { workspacePath: context.workspacePath } : undefined,
78
79
  }],
79
80
  });
80
81
  const file = result.attachments?.[0];
@@ -226,6 +227,7 @@ function resolveContext(flags, env) {
226
227
  return {
227
228
  baseUrl: getFlag(flags, 'base-url') || String(env.LOCAL_AI_CORE_BASE || DEFAULT_BASE_URL),
228
229
  workspaceId: getFlag(flags, 'workspace') || String(env.LOCAL_AI_WORKSPACE_ID || ''),
230
+ workspacePath: getFlag(flags, 'workspace-path') || String(env.LOCAL_AI_WORKSPACE_PATH || ''),
229
231
  threadId: normalizeMaybeBooleanFlag(getFlag(flags, 'thread')) || String(env.LOCAL_AI_THREAD_ID || ''),
230
232
  platform,
231
233
  routeType: String(env.LOCAL_AI_ROUTE_TYPE || '') || (platform === 'lark' && chatId && platformUserId ? 'channel.chat' : ''),
@@ -301,7 +303,7 @@ function printUsage(output) {
301
303
  ' lac scheduler edit <job-id> [--cron "<expr>"] [--message "<text>"] [--desc "<label>"] [--enabled true|false] [--execution-mode same-thread|side-thread] [--json]',
302
304
  ' lac scheduler del <job-id> [--json]',
303
305
  ' lac scheduler run <job-id> [--json]',
304
- ' lac channel send-file --path "<file>" [--target <chat-or-user-id>] [--workspace <id>] [--platform lark] [--name <filename>] [--json]',
306
+ ' lac channel send-file --path "<file>" [--target <chat-or-user-id>] [--workspace <id>] [--workspace-path <path>] [--platform lark] [--name <filename>] [--json]',
305
307
  ].join('\n') + '\n');
306
308
  }
307
309
  function formatJobLine(job) {
@@ -163,6 +163,7 @@ function bootstrapLocalCoreRuntime(options) {
163
163
  }
164
164
  const agentPlugins = [
165
165
  localCoreAgentPlugin,
166
+ (0, agent_localcore_acp_plugin_js_1.createBuiltinPiAgentPlugin)(),
166
167
  (0, agent_localcore_acp_plugin_js_1.createBuiltinOpencodeAgentPlugin)(),
167
168
  (0, agent_localcore_acp_plugin_js_1.createBuiltinCodexAgentPlugin)(),
168
169
  (0, agent_localcore_acp_plugin_js_1.createBuiltinClaudeCodeAgentPlugin)(),
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createBuiltinLocalCoreAcpAgentPlugin = createBuiltinLocalCoreAcpAgentPlugin;
4
4
  exports.createBuiltinOpencodeAgentPlugin = createBuiltinOpencodeAgentPlugin;
5
+ exports.createBuiltinPiAgentPlugin = createBuiltinPiAgentPlugin;
5
6
  exports.createBuiltinCodexAgentPlugin = createBuiltinCodexAgentPlugin;
6
7
  exports.createBuiltinClaudeCodeAgentPlugin = createBuiltinClaudeCodeAgentPlugin;
7
8
  exports.createBuiltinStaticAgentCapabilityPlugin = createBuiltinStaticAgentCapabilityPlugin;
@@ -86,6 +87,14 @@ function createBuiltinOpencodeAgentPlugin() {
86
87
  displayName: 'OpenCode',
87
88
  });
88
89
  }
90
+ function createBuiltinPiAgentPlugin() {
91
+ return createBuiltinAgentPlugin({
92
+ pluginId: 'builtin.agent-pi',
93
+ agentType: 'pi',
94
+ match: (normalizedAgentType) => normalizedAgentType === 'pi',
95
+ displayName: 'Pi',
96
+ });
97
+ }
89
98
  function createBuiltinCodexAgentPlugin() {
90
99
  return createBuiltinAgentPlugin({
91
100
  pluginId: 'builtin.agent-codex',
@@ -4,6 +4,7 @@ exports.normalizePlatformTypes = normalizePlatformTypes;
4
4
  exports.isLocalCoreNativeAcpProject = isLocalCoreNativeAcpProject;
5
5
  exports.toLocalCoreProjectConfig = toLocalCoreProjectConfig;
6
6
  const node_module_1 = require("node:module");
7
+ const node_fs_1 = require("node:fs");
7
8
  const node_path_1 = require("node:path");
8
9
  const desktop_js_1 = require("../../../../shared/desktop.js");
9
10
  function normalizePlatformTypes(project) {
@@ -17,10 +18,13 @@ function isLocalCoreNativeAcpProject(project) {
17
18
  || agentType === 'acp'
18
19
  || agentType === desktop_js_1.LOCALCORE_ACP_AGENT_TYPE;
19
20
  }
20
- function resolveOpencodeModel(project, providers) {
21
+ function resolveAgentModel(project, providers) {
21
22
  const agentType = String(project.agent?.type || '').trim().toLowerCase();
22
23
  const rawModel = String(project.agent?.options?.model || '').trim();
23
24
  const normalizedModel = (0, desktop_js_1.normalizeDesktopAgentModel)(agentType, rawModel);
25
+ if (agentType === 'pi') {
26
+ return rawModel || getFirstProviderModelId(providers);
27
+ }
24
28
  if (agentType !== 'opencode') {
25
29
  return normalizedModel;
26
30
  }
@@ -31,6 +35,15 @@ function resolveOpencodeModel(project, providers) {
31
35
  }
32
36
  return normalizedModel;
33
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
+ }
34
47
  function getFirstProviderModelRef(providers) {
35
48
  for (const provider of providers) {
36
49
  const providerId = normalizeOpencodeProviderId(provider.name);
@@ -131,6 +144,108 @@ function collectProviderEnv(providers) {
131
144
  }
132
145
  return env;
133
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
+ }
134
249
  function normalizeOpencodeProviderId(value) {
135
250
  return String(value || '')
136
251
  .trim()
@@ -138,6 +253,48 @@ function normalizeOpencodeProviderId(value) {
138
253
  .replace(/[^a-z0-9._-]+/g, '-')
139
254
  .replace(/^-+|-+$/g, '');
140
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
+ }
141
298
  function opencodeProviderApiKeyEnvName(providerId) {
142
299
  const suffix = providerId.toUpperCase().replace(/[^A-Z0-9]+/g, '_').replace(/^_+|_+$/g, '');
143
300
  return `AI_WORKSTATION_OPENCODE_${suffix || 'PROVIDER'}_API_KEY`;
@@ -152,7 +309,7 @@ function shouldUseOpenAiCompatibleProvider(providerId) {
152
309
  }
153
310
  function resolveBundledAcpCommand(packageName, binName) {
154
311
  const require = (0, node_module_1.createRequire)(__filename);
155
- const packageJsonPath = require.resolve(`${packageName}/package.json`);
312
+ const packageJsonPath = resolveBundledPackageJsonPath(packageName);
156
313
  const packageJson = require(packageJsonPath);
157
314
  const binField = packageJson.bin;
158
315
  const relativeBinPath = typeof binField === 'string'
@@ -166,12 +323,44 @@ function resolveBundledAcpCommand(packageName, binName) {
166
323
  args: [(0, node_path_1.resolve)((0, node_path_1.dirname)(packageJsonPath), relativeBinPath)],
167
324
  };
168
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
+ }
169
352
  function resolveBundledCodexCommand() {
170
353
  return resolveBundledAcpCommand(desktop_js_1.DESKTOP_CODEX_ACP_PACKAGE, 'codex-acp');
171
354
  }
172
355
  function resolveBundledClaudeCodeCommand() {
173
356
  return resolveBundledAcpCommand(desktop_js_1.DESKTOP_CLAUDECODE_ACP_PACKAGE, 'claude-agent-acp');
174
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];
363
+ }
175
364
  function toLocalCoreProjectConfig(configState, project) {
176
365
  const rawWorkDir = String(project.agent?.options?.work_dir || '.').trim() || '.';
177
366
  const configDir = (0, node_path_1.dirname)(configState.path);
@@ -188,8 +377,11 @@ function toLocalCoreProjectConfig(configState, project) {
188
377
  : {};
189
378
  const agentType = String(project.agent?.type || '').trim().toLowerCase();
190
379
  const providers = Array.isArray(project.agent?.providers) ? project.agent.providers : [];
191
- const model = resolveOpencodeModel(project, providers);
380
+ const model = resolveAgentModel(project, providers);
192
381
  const providerEnv = collectProviderEnv(providers);
382
+ const piProviderEnv = agentType === 'pi'
383
+ ? collectPiProviderEnv(providers)
384
+ : {};
193
385
  const opencodeInlineConfig = agentType === 'opencode'
194
386
  ? buildOpencodeInlineConfig(model, providers)
195
387
  : null;
@@ -204,6 +396,22 @@ function toLocalCoreProjectConfig(configState, project) {
204
396
  ANTHROPIC_MODEL: model,
205
397
  }
206
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
+ : {};
207
415
  const bundledCodex = agentType === 'codex'
208
416
  ? resolveBundledCodexCommand()
209
417
  : null;
@@ -212,18 +420,20 @@ function toLocalCoreProjectConfig(configState, project) {
212
420
  : null;
213
421
  const inferredCommand = agentType === 'opencode'
214
422
  ? 'opencode'
215
- : bundledCodex?.command || bundledClaudeCode?.command || '';
423
+ : bundledPiAcp?.command || bundledCodex?.command || bundledClaudeCode?.command || '';
216
424
  const command = String(project.agent?.options?.command || inferredCommand).trim();
217
425
  if (!command) {
218
426
  throw new Error(`Workspace "${project.name}" requires [projects.agent.options].command for Local AI Core ACP execution.`);
219
427
  }
220
428
  const defaultArgs = agentType === 'opencode'
221
429
  ? ['acp']
222
- : agentType === 'codex'
223
- ? [...(bundledCodex?.args || [])]
224
- : agentType === 'claudecode'
225
- ? [...(bundledClaudeCode?.args || [])]
226
- : [];
430
+ : agentType === 'pi'
431
+ ? [...(bundledPiAcp?.args || [])]
432
+ : agentType === 'codex'
433
+ ? [...(bundledCodex?.args || [])]
434
+ : agentType === 'claudecode'
435
+ ? [...(bundledClaudeCode?.args || [])]
436
+ : [];
227
437
  return {
228
438
  workspaceId: project.name,
229
439
  agentType,
@@ -231,9 +441,10 @@ function toLocalCoreProjectConfig(configState, project) {
231
441
  command,
232
442
  args: args.length > 0 ? args : defaultArgs,
233
443
  env: {
234
- ...providerEnv,
235
444
  ...inferredOpencodeEnv,
236
445
  ...inferredClaudeCodeEnv,
446
+ ...inferredPiEnv,
447
+ ...providerEnv,
237
448
  ...env,
238
449
  },
239
450
  model,
@@ -7,6 +7,7 @@ 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
9
  const DISPLAY_NAMES = {
10
+ pi: 'Pi',
10
11
  opencode: 'OpenCode',
11
12
  codex: 'Codex',
12
13
  claudecode: 'Claude Code',
@@ -17,6 +18,7 @@ const DISPLAY_NAMES = {
17
18
  [desktop_js_1.LOCALCORE_ACP_AGENT_TYPE]: 'LocalCore ACP',
18
19
  };
19
20
  const COMMAND_CANDIDATES = {
21
+ pi: ['pi'],
20
22
  opencode: ['opencode'],
21
23
  codex: ['codex-acp', 'codex'],
22
24
  claudecode: ['claude-agent-acp'],
@@ -26,6 +28,7 @@ const COMMAND_CANDIDATES = {
26
28
  iflow: ['iflow'],
27
29
  };
28
30
  const VERSION_ARGUMENTS = {
31
+ pi: ['--version'],
29
32
  opencode: ['--version'],
30
33
  codex: ['--version'],
31
34
  claudecode: ['--version'],
@@ -61,14 +64,16 @@ function detectInstalledAgentRuntimes(options = {}) {
61
64
  ? installedRuntime(agentType, resolved, 'config', detectedAt, env, versionTimeoutMs)
62
65
  : missingRuntime(agentType, detectedAt, `Configured command not found: ${configured}`);
63
66
  }
64
- if (agentType === 'codex' || agentType === 'claudecode') {
65
- const bundled = agentType === 'codex'
66
- ? resolveBundledAcpPackage(options.requireFrom, desktop_js_1.DESKTOP_CODEX_ACP_PACKAGE, ['bin/codex-acp.js'])
67
- : resolveBundledAcpPackage(options.requireFrom, desktop_js_1.DESKTOP_CLAUDECODE_ACP_PACKAGE, [
68
- 'dist/cli.js',
69
- 'bin/claude-agent-acp.js',
70
- 'cli.js',
71
- ]);
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
+ ]);
72
77
  if (bundled) {
73
78
  return installedRuntime(agentType, bundled, 'bundled', detectedAt, env, versionTimeoutMs);
74
79
  }
@@ -224,7 +229,7 @@ function parseVersionOutput(output) {
224
229
  function resolveBundledAcpPackage(requireFrom, packageName, candidates) {
225
230
  try {
226
231
  const require = (0, node_module_1.createRequire)(requireFrom || (0, node_path_1.join)(process.cwd(), 'package.json'));
227
- const packageJsonPath = require.resolve(`${packageName}/package.json`);
232
+ const packageJsonPath = resolveBundledPackageJsonPath(require, packageName);
228
233
  const packageDir = (0, node_path_1.dirname)(packageJsonPath);
229
234
  const packageJson = require(packageJsonPath);
230
235
  const binField = packageJson.bin;
@@ -246,3 +251,28 @@ function resolveBundledAcpPackage(requireFrom, packageName, candidates) {
246
251
  return null;
247
252
  }
248
253
  }
254
+ function resolveBundledPackageJsonPath(require, packageName) {
255
+ try {
256
+ return require.resolve(`${packageName}/package.json`);
257
+ }
258
+ catch (error) {
259
+ if (error?.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED') {
260
+ throw error;
261
+ }
262
+ }
263
+ for (const basePath of require.resolve.paths(packageName) || []) {
264
+ const packageJsonPath = (0, node_path_1.join)(basePath, ...packageName.split('/'), 'package.json');
265
+ if ((0, node_fs_1.existsSync)(packageJsonPath)) {
266
+ return packageJsonPath;
267
+ }
268
+ }
269
+ let current = (0, node_path_1.dirname)(require.resolve(packageName));
270
+ while (current && current !== (0, node_path_1.dirname)(current)) {
271
+ const packageJsonPath = (0, node_path_1.join)(current, 'package.json');
272
+ if ((0, node_fs_1.existsSync)(packageJsonPath)) {
273
+ return packageJsonPath;
274
+ }
275
+ current = (0, node_path_1.dirname)(current);
276
+ }
277
+ throw new Error(`Bundled package "${packageName}" package.json could not be resolved.`);
278
+ }
@@ -348,6 +348,7 @@ class LocalCoreController extends node_events_1.EventEmitter {
348
348
  type: 'file',
349
349
  path: input.path,
350
350
  fileName: input.fileName,
351
+ metadata: input.workspacePath ? { workspacePath: input.workspacePath } : undefined,
351
352
  }],
352
353
  });
353
354
  const attachment = result.attachments?.[0];
@@ -125,6 +125,7 @@ function changedRuntimes(previous, next) {
125
125
  }
126
126
  function displayName(agentType) {
127
127
  const names = {
128
+ pi: 'Pi',
128
129
  opencode: 'OpenCode',
129
130
  codex: 'Codex',
130
131
  claudecode: 'Claude Code',