@kafca/agentdock 0.1.29 → 0.1.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/renderer/assets/{Badge-ngf0eCzI.js → Badge-DaHt-aUT.js} +1 -1
- package/dist/renderer/assets/{Chat-tCGY5odh.js → Chat-Dzeq3kiD.js} +1 -1
- package/dist/renderer/assets/{ChatMarkdown-vqDWgVNb.js → ChatMarkdown-B-L5Roai.js} +1 -1
- package/dist/renderer/assets/{Config-YsVDQmIs.js → Config-BI4LJ7Fw.js} +1 -1
- package/dist/renderer/assets/{CronList-DQN0T7Yc.js → CronList-Dic7pblj.js} +1 -1
- package/dist/renderer/assets/{Dashboard-C2O6Yxq3.js → Dashboard-C0_ATyGe.js} +2 -2
- package/dist/renderer/assets/{EmptyState-CTkA-fBQ.js → EmptyState-SQsG1DZP.js} +1 -1
- package/dist/renderer/assets/{KnowledgeDetail-DjLZDItQ.js → KnowledgeDetail-D4yUJzp7.js} +1 -1
- package/dist/renderer/assets/{KnowledgeHome-2NHeRZxW.js → KnowledgeHome-L0tsg-oo.js} +1 -1
- package/dist/renderer/assets/{Logs-CQ6j-Sct.js → Logs-C2Jdr3uQ.js} +1 -1
- package/dist/renderer/assets/{Modal-9g1uWJfC.js → Modal-GUH3iPXo.js} +1 -1
- package/dist/renderer/assets/{Page-B4mPLn6n.js → Page-m42FBy97.js} +1 -1
- package/dist/renderer/assets/{ProjectDetail-pBXOAeL4.js → ProjectDetail-RF_sagPO.js} +1 -1
- package/dist/renderer/assets/{ProjectList-B9PYfZaS.js → ProjectList-Cyr8RDK3.js} +1 -1
- package/dist/renderer/assets/{Select-BWFQJ2R8.js → Select-CRlDgI5s.js} +1 -1
- package/dist/renderer/assets/{SessionChat-fBrzJs3A.js → SessionChat-_HhzMwEO.js} +1 -1
- package/dist/renderer/assets/{SessionList-DjHOsk7X.js → SessionList-DNwZjGTv.js} +1 -1
- package/dist/renderer/assets/ThreadChat-DxBmdsAm.js +23 -0
- package/dist/renderer/assets/{Workspace-nfTcxlhw.js → Workspace-DFphG85O.js} +1 -1
- package/dist/renderer/assets/{arrow-left-CIWZ6JEB.js → arrow-left-bILMzse7.js} +1 -1
- package/dist/renderer/assets/{book-open-Ck6BgBhN.js → book-open-BNBWEEve.js} +1 -1
- package/dist/renderer/assets/{check-CtB2ZLVE.js → check-MGp5kvnb.js} +1 -1
- package/dist/renderer/assets/{chevron-down--8lODnN0.js → chevron-down-zsfoTlh7.js} +1 -1
- package/dist/renderer/assets/{circle-CKQOJcbh.js → circle-BX_xUSg-.js} +1 -1
- package/dist/renderer/assets/{desktop-Do2I28CT.js → desktop-CgrO_cdQ.js} +1 -1
- package/dist/renderer/assets/{desktop-BkMW-h5p.js → desktop-O8_6ZCVZ.js} +2 -2
- package/dist/renderer/assets/{funnel-CkZuFIwa.js → funnel-7442U1wq.js} +1 -1
- package/dist/renderer/assets/{heart-CAX_CqFQ.js → heart-B3d8bnSH.js} +1 -1
- package/dist/renderer/assets/{index--_L0Y77h.js → index-5YUqCvBf.js} +1 -1
- package/dist/renderer/assets/{index-DvAYflFj.js → index-BBaYsVjU.js} +2 -2
- package/dist/renderer/assets/{pencil-sIRhtchu.js → pencil-QzWgkmoH.js} +1 -1
- package/dist/renderer/assets/{play-CJNRbCL5.js → play-Bsc8hSI7.js} +1 -1
- package/dist/renderer/assets/{plug-DsFe9GBx.js → plug-CRY7sjvB.js} +1 -1
- package/dist/renderer/assets/{plus-KDwEI-Rs.js → plus-Dr5Ei8Ud.js} +1 -1
- package/dist/renderer/assets/projects-BDYSOFIJ.js +1 -0
- package/dist/renderer/assets/{save-C-p5r3K-.js → save-giJnp2Ki.js} +1 -1
- package/dist/renderer/assets/{search-BZxQJb2k.js → search-DcvHLmKs.js} +1 -1
- package/dist/renderer/assets/{server-BV-K022I.js → server-D_Qu8If5.js} +1 -1
- package/dist/renderer/assets/{session-utils-ClQTa8yS.js → session-utils-CsASahfU.js} +1 -1
- package/dist/renderer/assets/{sessions-B0FSCA9O.js → sessions-eMpPqp4P.js} +1 -1
- package/dist/renderer/assets/{shield-check-B6AjkXUd.js → shield-check-BpF0uIw-.js} +1 -1
- package/dist/renderer/assets/{trash-2-41Pv4qRx.js → trash-2-OJFmx3xk.js} +1 -1
- package/dist/renderer/assets/{user-NKKHCKm9.js → user-GL_ivaV6.js} +1 -1
- package/dist/renderer/assets/{wifi-off-E1ZNhLG5.js → wifi-off-CQT5VNQl.js} +1 -1
- package/dist/renderer/index.html +1 -1
- package/dist-electron/electron/agent-runtime-detector.test.js +30 -0
- package/dist-electron/electron/local-core-refactor.test.js +749 -44
- package/dist-electron/electron/plugin-kernel.test.js +81 -1
- package/dist-electron/electron/workspace-task-store.test.js +7 -1
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +34 -4
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-progress.js +239 -7
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-session-coordinator.js +10 -6
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-transport.js +1 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +169 -5
- package/dist-electron/services/local-ai-core/src/agents/claudecode/behavior.js +5 -0
- package/dist-electron/services/local-ai-core/src/agents/claudecode/definition.js +36 -0
- package/dist-electron/services/local-ai-core/src/agents/claudecode/index.js +15 -0
- package/dist-electron/services/local-ai-core/src/agents/codex/behavior.js +5 -0
- package/dist-electron/services/local-ai-core/src/agents/codex/definition.js +24 -0
- package/dist-electron/services/local-ai-core/src/agents/codex/index.js +15 -0
- package/dist-electron/services/local-ai-core/src/agents/hermes/behavior.js +29 -0
- package/dist-electron/services/local-ai-core/src/agents/hermes/definition.js +22 -0
- package/dist-electron/services/local-ai-core/src/agents/hermes/index.js +15 -0
- package/dist-electron/services/local-ai-core/src/agents/index.js +98 -0
- package/dist-electron/services/local-ai-core/src/agents/localcore-acp/behavior.js +5 -0
- package/dist-electron/services/local-ai-core/src/agents/localcore-acp/definition.js +14 -0
- package/dist-electron/services/local-ai-core/src/agents/localcore-acp/index.js +27 -0
- package/dist-electron/services/local-ai-core/src/agents/opencode/behavior.js +5 -0
- package/dist-electron/services/local-ai-core/src/agents/opencode/definition.js +18 -0
- package/dist-electron/services/local-ai-core/src/agents/opencode/index.js +15 -0
- package/dist-electron/services/local-ai-core/src/agents/opencode/launch.js +119 -0
- package/dist-electron/services/local-ai-core/src/agents/pi/behavior.js +5 -0
- package/dist-electron/services/local-ai-core/src/agents/pi/definition.js +23 -0
- package/dist-electron/services/local-ai-core/src/agents/pi/index.js +15 -0
- package/dist-electron/services/local-ai-core/src/agents/pi/launch.js +139 -0
- package/dist-electron/services/local-ai-core/src/agents/shared/acp-behavior.js +11 -0
- package/dist-electron/services/local-ai-core/src/agents/shared/agent-plugin.js +60 -0
- package/dist-electron/services/local-ai-core/src/agents/shared/definition.js +12 -0
- package/dist-electron/services/local-ai-core/src/agents/shared/launch-utils.js +122 -0
- package/dist-electron/services/local-ai-core/src/agents/shared/progress-replay.js +19 -0
- package/dist-electron/services/local-ai-core/src/agents/shared/replay-prefix.js +68 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +0 -2
- package/dist-electron/services/local-ai-core/src/channel/lark/runtime-state.js +1 -1
- package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +13 -2
- package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +8 -7
- package/dist-electron/services/local-ai-core/src/plugins/builtin/agent-localcore-acp-plugin.js +9 -130
- package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +22 -397
- package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +9 -45
- package/dist-electron/services/local-ai-core/src/runtime/local-core-runtime-state.js +62 -1
- package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-service.js +2 -12
- package/dist-electron/shared/desktop.js +3 -1
- package/dist-electron/src/pages/Threads/thread-chat-model.js +27 -18
- package/dist-electron/src/pages/Threads/thread-chat-permission.test.js +57 -18
- package/package.json +1 -1
- package/dist/renderer/assets/ThreadChat-MagPeJD0.js +0 -23
- package/dist/renderer/assets/projects-CJUs2vaq.js +0 -1
|
@@ -16,6 +16,7 @@ const event_bus_js_1 = require("../services/local-ai-core/src/kernel/event-bus.j
|
|
|
16
16
|
const lifecycle_manager_js_1 = require("../services/local-ai-core/src/kernel/lifecycle-manager.js");
|
|
17
17
|
const plugin_registry_js_1 = require("../services/local-ai-core/src/kernel/plugin-registry.js");
|
|
18
18
|
const local_core_controller_js_1 = require("../services/local-ai-core/src/runtime/local-core-controller.js");
|
|
19
|
+
const local_core_runtime_state_js_1 = require("../services/local-ai-core/src/runtime/local-core-runtime-state.js");
|
|
19
20
|
const server_js_1 = require("../services/local-ai-core/src/runtime/server.js");
|
|
20
21
|
function plugin(id, dependsOn = []) {
|
|
21
22
|
return {
|
|
@@ -159,6 +160,7 @@ function plugin(id, dependsOn = []) {
|
|
|
159
160
|
'opencode',
|
|
160
161
|
'codex',
|
|
161
162
|
'claudecode',
|
|
163
|
+
'hermes',
|
|
162
164
|
]);
|
|
163
165
|
strict_1.default.deepEqual(runtime.kernel.getCapabilitySnapshot().adapters.channels, ['localcore-acp', 'lark', 'weixin']);
|
|
164
166
|
strict_1.default.deepEqual(runtime.kernel.getCapabilitySnapshot().adapters.knowledgeProviders, ['ai-vector']);
|
|
@@ -193,6 +195,7 @@ function plugin(id, dependsOn = []) {
|
|
|
193
195
|
'opencode',
|
|
194
196
|
'codex',
|
|
195
197
|
'claudecode',
|
|
198
|
+
'hermes',
|
|
196
199
|
]);
|
|
197
200
|
strict_1.default.deepEqual(runtime.kernel.getCapabilitySnapshot().adapters.channels, ['localcore-acp', 'lark', 'weixin']);
|
|
198
201
|
strict_1.default.deepEqual(runtime.kernel.getCapabilitySnapshot().adapters.knowledgeProviders, []);
|
|
@@ -245,6 +248,44 @@ type = "codex"
|
|
|
245
248
|
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
246
249
|
}
|
|
247
250
|
});
|
|
251
|
+
(0, node_test_1.default)('hermes agent runtime routes projects through hermes ACP command', async () => {
|
|
252
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-kernel-'));
|
|
253
|
+
try {
|
|
254
|
+
const runtime = (0, bootstrap_js_2.bootstrapLocalCoreRuntime)({
|
|
255
|
+
userDataPath,
|
|
256
|
+
enableKnowledge: false,
|
|
257
|
+
});
|
|
258
|
+
await runtime.state.saveRawConfigFile(`
|
|
259
|
+
[[projects]]
|
|
260
|
+
name = "hermes-workspace"
|
|
261
|
+
|
|
262
|
+
[projects.agent]
|
|
263
|
+
type = "hermes"
|
|
264
|
+
`);
|
|
265
|
+
const configState = await runtime.state.readConfigFile();
|
|
266
|
+
const project = configState.parsed?.projects?.find((entry) => entry.name === 'hermes-workspace');
|
|
267
|
+
const hermesRuntime = runtime.agentRuntimes.find((entry) => entry.agentType === 'hermes');
|
|
268
|
+
const route = project ? hermesRuntime?.createRoute(configState, project) : null;
|
|
269
|
+
strict_1.default.equal(route?.agentType, 'hermes');
|
|
270
|
+
strict_1.default.equal(route?.config.command, 'hermes');
|
|
271
|
+
strict_1.default.deepEqual(route?.config.args, ['acp']);
|
|
272
|
+
strict_1.default.equal(route?.config.env?.HERMES_YOLO_MODE, '1');
|
|
273
|
+
strict_1.default.deepEqual(await runtime.workspaceRouter.listWorkspaces(), [
|
|
274
|
+
{
|
|
275
|
+
id: 'hermes-workspace',
|
|
276
|
+
name: 'hermes-workspace',
|
|
277
|
+
agentType: 'hermes',
|
|
278
|
+
platforms: [],
|
|
279
|
+
sessionsCount: 0,
|
|
280
|
+
heartbeatEnabled: false,
|
|
281
|
+
},
|
|
282
|
+
]);
|
|
283
|
+
await runtime.stop();
|
|
284
|
+
}
|
|
285
|
+
finally {
|
|
286
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
287
|
+
}
|
|
288
|
+
});
|
|
248
289
|
(0, node_test_1.default)('pi agent runtime routes projects through bundled pi ACP and coding agent', async () => {
|
|
249
290
|
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-kernel-'));
|
|
250
291
|
try {
|
|
@@ -565,6 +606,45 @@ model = "deepseek-v4-flash"
|
|
|
565
606
|
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
566
607
|
}
|
|
567
608
|
});
|
|
609
|
+
(0, node_test_1.default)('runtime getLogs reads from disk after state is recreated', () => {
|
|
610
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-disk-logs-'));
|
|
611
|
+
try {
|
|
612
|
+
const state = (0, local_core_runtime_state_js_1.createLocalCoreRuntimeState)({ userDataPath });
|
|
613
|
+
state.pushLog('first persisted line');
|
|
614
|
+
state.pushLog('second persisted line');
|
|
615
|
+
const recreated = (0, local_core_runtime_state_js_1.createLocalCoreRuntimeState)({ userDataPath });
|
|
616
|
+
strict_1.default.deepEqual(recreated.getLogs(2).map((line) => line.replace(/^\S+\s+/, '')), [
|
|
617
|
+
'first persisted line',
|
|
618
|
+
'second persisted line',
|
|
619
|
+
]);
|
|
620
|
+
}
|
|
621
|
+
finally {
|
|
622
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
(0, node_test_1.default)('runtime getLogs fills from rotated log when current log has fewer lines', () => {
|
|
626
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-rotated-logs-'));
|
|
627
|
+
try {
|
|
628
|
+
const state = (0, local_core_runtime_state_js_1.createLocalCoreRuntimeState)({ userDataPath });
|
|
629
|
+
(0, node_fs_1.writeFileSync)(`${state.logPath}.1`, 'old-1\nold-2\nold-3\n', 'utf8');
|
|
630
|
+
(0, node_fs_1.writeFileSync)(state.logPath, 'new-1\n', 'utf8');
|
|
631
|
+
strict_1.default.deepEqual(state.getLogs(3), ['old-2', 'old-3', 'new-1']);
|
|
632
|
+
}
|
|
633
|
+
finally {
|
|
634
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
(0, node_test_1.default)('runtime getLogs returns only the requested tail lines from disk', () => {
|
|
638
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-tail-logs-'));
|
|
639
|
+
try {
|
|
640
|
+
const state = (0, local_core_runtime_state_js_1.createLocalCoreRuntimeState)({ userDataPath });
|
|
641
|
+
(0, node_fs_1.writeFileSync)(state.logPath, Array.from({ length: 1000 }, (_, index) => `line-${index + 1}`).join('\n') + '\n', 'utf8');
|
|
642
|
+
strict_1.default.deepEqual(state.getLogs(4), ['line-997', 'line-998', 'line-999', 'line-1000']);
|
|
643
|
+
}
|
|
644
|
+
finally {
|
|
645
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
646
|
+
}
|
|
647
|
+
});
|
|
568
648
|
(0, node_test_1.default)('controller logs are persisted to local-core.log', () => {
|
|
569
649
|
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-controller-logs-'));
|
|
570
650
|
try {
|
|
@@ -739,7 +819,7 @@ name = "pi-workspace"
|
|
|
739
819
|
[projects.agent]
|
|
740
820
|
type = "pi"
|
|
741
821
|
`);
|
|
742
|
-
strict_1.default.deepEqual(runtime.agentRuntimes.map((entry) => entry.agentType), ['localcore-acp', 'opencode', 'codex']);
|
|
822
|
+
strict_1.default.deepEqual(runtime.agentRuntimes.map((entry) => entry.agentType), ['localcore-acp', 'opencode', 'codex', 'hermes']);
|
|
743
823
|
strict_1.default.equal(runtime.kernel.getCapabilitySnapshot().snapshot.agents.some((capability) => capability.agentType === 'claudecode'), false);
|
|
744
824
|
strict_1.default.equal(runtime.kernel.getCapabilitySnapshot().snapshot.agents.some((capability) => capability.agentType === 'pi'), false);
|
|
745
825
|
strict_1.default.deepEqual(await runtime.workspaceRouter.listWorkspaces(), []);
|
|
@@ -198,7 +198,13 @@ const local_core_controller_js_1 = require("../services/local-ai-core/src/runtim
|
|
|
198
198
|
model: '',
|
|
199
199
|
});
|
|
200
200
|
strict_1.default.equal(closeCount, 1);
|
|
201
|
-
strict_1.default.
|
|
201
|
+
strict_1.default.deepEqual(capturedSessionNewParams[1]?._meta, {
|
|
202
|
+
claudeCode: {
|
|
203
|
+
emitRawSDKMessages: [
|
|
204
|
+
{ type: 'system', subtype: 'local_command_output' },
|
|
205
|
+
],
|
|
206
|
+
},
|
|
207
|
+
});
|
|
202
208
|
}
|
|
203
209
|
finally {
|
|
204
210
|
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
@@ -10,6 +10,8 @@ const local_core_acp_response_processor_js_1 = require("./local-core-acp-respons
|
|
|
10
10
|
const local_core_acp_content_js_1 = require("./local-core-acp-content.js");
|
|
11
11
|
const command_risk_js_1 = require("../security/command-risk.js");
|
|
12
12
|
const local_core_slash_commands_js_1 = require("./local-core-slash-commands.js");
|
|
13
|
+
const local_core_acp_progress_js_1 = require("./local-core-acp-progress.js");
|
|
14
|
+
const index_js_1 = require("../agents/index.js");
|
|
13
15
|
const ACP_PROMPT_TIMEOUT_MS = 15 * 60 * 1000;
|
|
14
16
|
class LocalCoreAcpBackend {
|
|
15
17
|
options;
|
|
@@ -199,8 +201,10 @@ class LocalCoreAcpBackend {
|
|
|
199
201
|
sessionKey: row.bridge_session_key,
|
|
200
202
|
},
|
|
201
203
|
});
|
|
202
|
-
|
|
203
|
-
this.
|
|
204
|
+
setImmediate(() => {
|
|
205
|
+
void this.runPrompt(threadId, runId, row.bridge_session_key, config, message, options).catch((error) => {
|
|
206
|
+
this.options.log?.(`localcore-acp prompt failed for ${threadId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
207
|
+
});
|
|
204
208
|
});
|
|
205
209
|
return { runId };
|
|
206
210
|
}
|
|
@@ -300,15 +304,33 @@ class LocalCoreAcpBackend {
|
|
|
300
304
|
session = await this.sessionCoordinator.ensureSession(threadId, bridgeSessionKey, config, {
|
|
301
305
|
permissionMode: options.permissionMode,
|
|
302
306
|
});
|
|
307
|
+
const priorAssistantFinalMessages = this.options.store
|
|
308
|
+
.getThread(threadId, [])
|
|
309
|
+
.messages
|
|
310
|
+
.filter((entry) => entry.role === 'assistant' && entry.kind === 'final')
|
|
311
|
+
.map((entry) => entry.content);
|
|
312
|
+
const priorAssistantProgressMessages = this.options.store
|
|
313
|
+
.getThread(threadId, [])
|
|
314
|
+
.messages
|
|
315
|
+
.filter((entry) => entry.role === 'assistant' && entry.kind === 'progress')
|
|
316
|
+
.map((entry) => ({
|
|
317
|
+
kind: entry.bridgeKind,
|
|
318
|
+
content: entry.content,
|
|
319
|
+
}));
|
|
303
320
|
session.currentRunId = runId;
|
|
304
321
|
session.currentTurn = {
|
|
305
322
|
runId,
|
|
306
323
|
replyCtx: runId,
|
|
307
324
|
previewHandle: (0, node_crypto_1.randomUUID)(),
|
|
308
325
|
thoughtPreviewHandle: (0, node_crypto_1.randomUUID)(),
|
|
309
|
-
thoughtMessageId: `${runId}-thought`,
|
|
326
|
+
thoughtMessageId: `${runId}-thought-1`,
|
|
327
|
+
agentType: row.agent_type,
|
|
310
328
|
assistantText: '',
|
|
329
|
+
rawAssistantText: '',
|
|
330
|
+
priorAssistantFinalMessages,
|
|
331
|
+
priorAssistantProgressMessages,
|
|
311
332
|
thoughtText: '',
|
|
333
|
+
thoughtSequence: 1,
|
|
312
334
|
typingStarted: true,
|
|
313
335
|
previewStarted: false,
|
|
314
336
|
thoughtPreviewStarted: false,
|
|
@@ -319,6 +341,7 @@ class LocalCoreAcpBackend {
|
|
|
319
341
|
pendingToolCalls: {},
|
|
320
342
|
pendingToolCallOrder: [],
|
|
321
343
|
toolCallSequence: 0,
|
|
344
|
+
toolObservations: [],
|
|
322
345
|
permission: null,
|
|
323
346
|
};
|
|
324
347
|
const promptPromise = this.transport.request(session, 'session/prompt', {
|
|
@@ -332,9 +355,16 @@ class LocalCoreAcpBackend {
|
|
|
332
355
|
if (!currentTurn || currentTurn.runId !== runId) {
|
|
333
356
|
return;
|
|
334
357
|
}
|
|
358
|
+
this.turnCoordinator.closePendingThoughtSegment(session);
|
|
335
359
|
this.turnCoordinator.flushPendingToolCall(session);
|
|
336
360
|
if (currentTurn.assistantText) {
|
|
337
|
-
const
|
|
361
|
+
const behavior = (0, index_js_1.resolveAgentAcpBehavior)(currentTurn.agentType);
|
|
362
|
+
const normalizedFinalAssistantText = behavior.normalizeFinalAssistantText({
|
|
363
|
+
rawText: currentTurn.rawAssistantText || currentTurn.assistantText,
|
|
364
|
+
priorAssistantMessages: currentTurn.priorAssistantFinalMessages || [],
|
|
365
|
+
});
|
|
366
|
+
const assistantText = (0, local_core_acp_progress_js_1.stripObservedToolTranscriptsFromAssistantText)(normalizedFinalAssistantText, currentTurn.toolObservations);
|
|
367
|
+
const processed = await this.responseProcessor.processAssistantResponse(threadId, assistantText);
|
|
338
368
|
if (processed.displayContent) {
|
|
339
369
|
this.options.store.appendMessage(threadId, 'assistant', processed.displayContent, 'final');
|
|
340
370
|
this.options.eventBus.emit({
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.applyAssistantMessageChunk = applyAssistantMessageChunk;
|
|
4
4
|
exports.applyThoughtChunk = applyThoughtChunk;
|
|
5
|
+
exports.closeThoughtSegment = closeThoughtSegment;
|
|
5
6
|
exports.registerPendingToolCall = registerPendingToolCall;
|
|
6
7
|
exports.resolveToolCallForUpdate = resolveToolCallForUpdate;
|
|
7
8
|
exports.resolveFallbackToolCall = resolveFallbackToolCall;
|
|
@@ -16,6 +17,8 @@ exports.isTerminalToolStatus = isTerminalToolStatus;
|
|
|
16
17
|
exports.formatPlanProgress = formatPlanProgress;
|
|
17
18
|
exports.extractToolCallKey = extractToolCallKey;
|
|
18
19
|
exports.extractToolCallInput = extractToolCallInput;
|
|
20
|
+
exports.recordToolObservation = recordToolObservation;
|
|
21
|
+
exports.stripObservedToolTranscriptsFromAssistantText = stripObservedToolTranscriptsFromAssistantText;
|
|
19
22
|
function applyAssistantMessageChunk(currentTurn, text) {
|
|
20
23
|
if (!text) {
|
|
21
24
|
return null;
|
|
@@ -41,8 +44,12 @@ function applyThoughtChunk(currentTurn, text) {
|
|
|
41
44
|
if (!text) {
|
|
42
45
|
return null;
|
|
43
46
|
}
|
|
44
|
-
|
|
47
|
+
const projection = mergeThoughtSegment(currentTurn.thoughtText, text);
|
|
48
|
+
currentTurn.thoughtText = projection.fullText;
|
|
45
49
|
const content = currentTurn.thoughtText.trim();
|
|
50
|
+
if (!content) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
46
53
|
if (!currentTurn.thoughtPreviewStarted) {
|
|
47
54
|
currentTurn.thoughtPreviewStarted = true;
|
|
48
55
|
return {
|
|
@@ -61,23 +68,67 @@ function applyThoughtChunk(currentTurn, text) {
|
|
|
61
68
|
bridgeKind: 'thought',
|
|
62
69
|
};
|
|
63
70
|
}
|
|
64
|
-
function
|
|
71
|
+
function closeThoughtSegment(currentTurn) {
|
|
72
|
+
const thoughtText = currentTurn.thoughtText || '';
|
|
73
|
+
if (!thoughtText.trim()) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const nextSequence = (currentTurn.thoughtSequence || 1) + 1;
|
|
77
|
+
const runId = currentTurn.runId || 'thought';
|
|
78
|
+
currentTurn.thoughtSequence = nextSequence;
|
|
79
|
+
currentTurn.thoughtText = '';
|
|
80
|
+
currentTurn.thoughtPreviewStarted = false;
|
|
81
|
+
currentTurn.thoughtMessageId = `${runId}-thought-${nextSequence}`;
|
|
82
|
+
currentTurn.thoughtPreviewHandle = `${runId}-thought-preview-${nextSequence}`;
|
|
83
|
+
}
|
|
84
|
+
function mergeThoughtSegment(current, next) {
|
|
65
85
|
if (!current) {
|
|
66
|
-
return next;
|
|
86
|
+
return { fullText: next, segmentText: next };
|
|
67
87
|
}
|
|
68
88
|
if (next.startsWith(current)) {
|
|
69
|
-
return
|
|
89
|
+
return {
|
|
90
|
+
fullText: next,
|
|
91
|
+
segmentText: next.slice(current.length),
|
|
92
|
+
};
|
|
70
93
|
}
|
|
71
94
|
if (current.endsWith(next)) {
|
|
72
|
-
return
|
|
95
|
+
return {
|
|
96
|
+
fullText: current,
|
|
97
|
+
segmentText: '',
|
|
98
|
+
};
|
|
73
99
|
}
|
|
74
100
|
const maxOverlap = Math.min(current.length, next.length);
|
|
75
101
|
for (let size = maxOverlap; size > 0; size -= 1) {
|
|
76
102
|
if (current.endsWith(next.slice(0, size))) {
|
|
77
|
-
|
|
103
|
+
const suffix = next.slice(size);
|
|
104
|
+
return {
|
|
105
|
+
fullText: current + suffix,
|
|
106
|
+
segmentText: suffix,
|
|
107
|
+
};
|
|
78
108
|
}
|
|
79
109
|
}
|
|
80
|
-
return
|
|
110
|
+
return {
|
|
111
|
+
fullText: appendThoughtPiece(current, next),
|
|
112
|
+
segmentText: next,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function appendThoughtPiece(current, next) {
|
|
116
|
+
if (!current.trim()) {
|
|
117
|
+
return next;
|
|
118
|
+
}
|
|
119
|
+
if (!next.trim()) {
|
|
120
|
+
return current;
|
|
121
|
+
}
|
|
122
|
+
if (/^[,.;:!?,。;:!?、)】》]/.test(next.trimStart())) {
|
|
123
|
+
return current.trimEnd() + next.trimStart();
|
|
124
|
+
}
|
|
125
|
+
if (/^[-/]/.test(next.trimStart()) || /[-/][A-Za-z0-9]*$/.test(current.trimEnd())) {
|
|
126
|
+
return current.trimEnd() + next.trimStart();
|
|
127
|
+
}
|
|
128
|
+
if (/\s$/.test(current) || /^\s/.test(next)) {
|
|
129
|
+
return current + next;
|
|
130
|
+
}
|
|
131
|
+
return `${current.trimEnd()} ${next.trimStart()}`;
|
|
81
132
|
}
|
|
82
133
|
function registerPendingToolCall(input) {
|
|
83
134
|
const title = String(input.update.title || 'Running tool').trim();
|
|
@@ -203,3 +254,184 @@ function extractToolCallInput(update) {
|
|
|
203
254
|
}
|
|
204
255
|
return undefined;
|
|
205
256
|
}
|
|
257
|
+
function recordToolObservation(currentTurn, observation) {
|
|
258
|
+
const normalized = {
|
|
259
|
+
...observation,
|
|
260
|
+
name: observation.name?.trim() || undefined,
|
|
261
|
+
title: observation.title?.trim() || undefined,
|
|
262
|
+
status: observation.status?.trim() || undefined,
|
|
263
|
+
outputText: observation.outputText?.trim() || undefined,
|
|
264
|
+
};
|
|
265
|
+
if (!normalized.name && !normalized.title && normalized.input === undefined && !normalized.outputText) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
currentTurn.toolObservations = [
|
|
269
|
+
...(currentTurn.toolObservations || []),
|
|
270
|
+
normalized,
|
|
271
|
+
];
|
|
272
|
+
}
|
|
273
|
+
function stripObservedToolTranscriptsFromAssistantText(text, observations) {
|
|
274
|
+
let remaining = String(text || '');
|
|
275
|
+
const knownObservations = observations || [];
|
|
276
|
+
for (let guard = 0; guard < knownObservations.length; guard += 1) {
|
|
277
|
+
const stripped = stripOneObservedToolTranscriptPrefix(remaining, knownObservations);
|
|
278
|
+
if (stripped === remaining) {
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
remaining = stripped;
|
|
282
|
+
}
|
|
283
|
+
return remaining.trim();
|
|
284
|
+
}
|
|
285
|
+
function stripOneObservedToolTranscriptPrefix(text, observations) {
|
|
286
|
+
const trimmedStart = text.replace(/^\s+/, '');
|
|
287
|
+
if (!trimmedStart) {
|
|
288
|
+
return text;
|
|
289
|
+
}
|
|
290
|
+
const matchingEvidence = observations
|
|
291
|
+
.map((observation) => createToolObservationEvidence(observation))
|
|
292
|
+
.find((evidence) => matchesObservedToolTranscriptPrefix(trimmedStart, evidence));
|
|
293
|
+
if (!matchingEvidence) {
|
|
294
|
+
return text;
|
|
295
|
+
}
|
|
296
|
+
const boundary = findToolTranscriptPrefixBoundary(trimmedStart, matchingEvidence);
|
|
297
|
+
if (boundary <= 0) {
|
|
298
|
+
return text;
|
|
299
|
+
}
|
|
300
|
+
return trimmedStart.slice(boundary);
|
|
301
|
+
}
|
|
302
|
+
function createToolObservationEvidence(observation) {
|
|
303
|
+
const serializedInput = serializeObservationValue(observation.input);
|
|
304
|
+
const serializedOutput = observation.outputText || '';
|
|
305
|
+
const structuredValues = collectStructuredStringValues(serializedOutput);
|
|
306
|
+
return {
|
|
307
|
+
toolNames: uniqueNonEmpty([
|
|
308
|
+
observation.name,
|
|
309
|
+
observation.title,
|
|
310
|
+
].map((value) => normalizeEvidencePhrase(value || ''))),
|
|
311
|
+
urls: uniqueNonEmpty([
|
|
312
|
+
...extractUrls(serializedInput),
|
|
313
|
+
...extractUrls(serializedOutput),
|
|
314
|
+
]),
|
|
315
|
+
phrases: uniqueNonEmpty([
|
|
316
|
+
...structuredValues,
|
|
317
|
+
observation.title,
|
|
318
|
+
serializedInput.length <= 300 ? serializedInput : '',
|
|
319
|
+
].map((value) => normalizeEvidencePhrase(value || '')).filter((value) => value.length >= 12)),
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
function matchesObservedToolTranscriptPrefix(text, evidence) {
|
|
323
|
+
const prefix = text.slice(0, 3000);
|
|
324
|
+
const normalizedPrefix = normalizeEvidencePhrase(prefix);
|
|
325
|
+
const hasToolName = evidence.toolNames.some((name) => name && normalizedPrefix.includes(name));
|
|
326
|
+
const urlMatches = evidence.urls.filter((url) => prefix.includes(url)).length;
|
|
327
|
+
const phraseMatches = evidence.phrases.filter((phrase) => normalizedPrefix.includes(phrase)).length;
|
|
328
|
+
if (hasToolName && (urlMatches > 0 || phraseMatches > 0)) {
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
return urlMatches > 0 && phraseMatches > 0;
|
|
332
|
+
}
|
|
333
|
+
function findToolTranscriptPrefixBoundary(text, evidence) {
|
|
334
|
+
const lines = text.split(/\n/);
|
|
335
|
+
const firstContentLine = lines.find((line) => line.trim());
|
|
336
|
+
if (!firstContentLine || !isTranscriptStartLine(firstContentLine, evidence)) {
|
|
337
|
+
return 0;
|
|
338
|
+
}
|
|
339
|
+
let offset = 0;
|
|
340
|
+
let seenEvidence = false;
|
|
341
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
342
|
+
const line = lines[index] || '';
|
|
343
|
+
const lineWithNewlineLength = line.length + (index < lines.length - 1 ? 1 : 0);
|
|
344
|
+
if (lineContainsObservationEvidence(line, evidence)) {
|
|
345
|
+
seenEvidence = true;
|
|
346
|
+
}
|
|
347
|
+
if (seenEvidence && index > 0 && line.trim() && !isStructuredTranscriptLine(line, evidence)) {
|
|
348
|
+
return offset;
|
|
349
|
+
}
|
|
350
|
+
offset += lineWithNewlineLength;
|
|
351
|
+
}
|
|
352
|
+
return 0;
|
|
353
|
+
}
|
|
354
|
+
function isTranscriptStartLine(line, evidence) {
|
|
355
|
+
const trimmed = line.trim();
|
|
356
|
+
return lineContainsObservationEvidence(line, evidence) &&
|
|
357
|
+
(/^(```|\{|\[|\*\*[^*]+\*\*|#+\s)/.test(trimmed) || /^[A-Za-z0-9_.-]+:/.test(trimmed));
|
|
358
|
+
}
|
|
359
|
+
function isStructuredTranscriptLine(line, evidence) {
|
|
360
|
+
const trimmed = line.trim();
|
|
361
|
+
if (!trimmed) {
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
if (lineContainsObservationEvidence(line, evidence)) {
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
367
|
+
return /^(```|\{|\}|\[|\]|".+":|\*\*[^*]+:\*\*|\*[^*]+\*|#+\s)/.test(trimmed) ||
|
|
368
|
+
/[::]\s*$/.test(trimmed) ||
|
|
369
|
+
/[{}\[\]"]/g.test(trimmed) ||
|
|
370
|
+
/^[A-Za-z0-9_.-]+:/.test(trimmed);
|
|
371
|
+
}
|
|
372
|
+
function lineContainsObservationEvidence(line, evidence) {
|
|
373
|
+
const normalizedLine = normalizeEvidencePhrase(line);
|
|
374
|
+
return evidence.toolNames.some((name) => name && normalizedLine.includes(name)) ||
|
|
375
|
+
evidence.urls.some((url) => line.includes(url)) ||
|
|
376
|
+
evidence.phrases.some((phrase) => phrase && normalizedLine.includes(phrase));
|
|
377
|
+
}
|
|
378
|
+
function serializeObservationValue(value) {
|
|
379
|
+
if (value == null) {
|
|
380
|
+
return '';
|
|
381
|
+
}
|
|
382
|
+
if (typeof value === 'string') {
|
|
383
|
+
return value;
|
|
384
|
+
}
|
|
385
|
+
try {
|
|
386
|
+
return JSON.stringify(value);
|
|
387
|
+
}
|
|
388
|
+
catch {
|
|
389
|
+
return String(value);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
function collectStructuredStringValues(text) {
|
|
393
|
+
const values = [];
|
|
394
|
+
try {
|
|
395
|
+
collectStringValues(JSON.parse(text), values);
|
|
396
|
+
}
|
|
397
|
+
catch {
|
|
398
|
+
const matches = text.matchAll(/"(title|description|url|name|summary)"\s*:\s*"([^"]{12,})"/gi);
|
|
399
|
+
for (const match of matches) {
|
|
400
|
+
values.push(match[2] || '');
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return values;
|
|
404
|
+
}
|
|
405
|
+
function collectStringValues(value, values) {
|
|
406
|
+
if (typeof value === 'string') {
|
|
407
|
+
values.push(value);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
if (Array.isArray(value)) {
|
|
411
|
+
for (const item of value) {
|
|
412
|
+
collectStringValues(item, values);
|
|
413
|
+
}
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
if (!value || typeof value !== 'object') {
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
for (const entry of Object.values(value)) {
|
|
420
|
+
collectStringValues(entry, values);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
function extractUrls(text) {
|
|
424
|
+
return [...text.matchAll(/https?:\/\/[^\s"'`<>),\]}]+/g)]
|
|
425
|
+
.map((match) => match[0])
|
|
426
|
+
.filter(Boolean);
|
|
427
|
+
}
|
|
428
|
+
function normalizeEvidencePhrase(value) {
|
|
429
|
+
return String(value || '')
|
|
430
|
+
.replace(/\\[nrt]/g, ' ')
|
|
431
|
+
.replace(/\s+/g, ' ')
|
|
432
|
+
.trim()
|
|
433
|
+
.toLowerCase();
|
|
434
|
+
}
|
|
435
|
+
function uniqueNonEmpty(values) {
|
|
436
|
+
return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
|
|
437
|
+
}
|
|
@@ -194,14 +194,18 @@ class LocalCoreAcpSessionCoordinator {
|
|
|
194
194
|
}
|
|
195
195
|
buildSessionMeta(threadId, permissionModeOverride = '') {
|
|
196
196
|
const mode = this.resolveLaunchPermissionMode(threadId, permissionModeOverride);
|
|
197
|
-
if (!mode) {
|
|
198
|
-
return undefined;
|
|
199
|
-
}
|
|
200
197
|
return {
|
|
201
198
|
claudeCode: {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
199
|
+
emitRawSDKMessages: [
|
|
200
|
+
{ type: 'system', subtype: 'local_command_output' },
|
|
201
|
+
],
|
|
202
|
+
...(mode
|
|
203
|
+
? {
|
|
204
|
+
options: {
|
|
205
|
+
permissionMode: mode,
|
|
206
|
+
},
|
|
207
|
+
}
|
|
208
|
+
: {}),
|
|
205
209
|
},
|
|
206
210
|
};
|
|
207
211
|
}
|