@kafca/agentdock 0.1.19 → 0.1.21
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 +7 -0
- package/dist/renderer/assets/{Badge-DcFVSGwl.js → Badge-BRmQIqbd.js} +1 -1
- package/dist/renderer/assets/{Chat-CTy10fKR.js → Chat-D94ODABB.js} +1 -1
- package/dist/renderer/assets/{ChatMarkdown-C-ci0dQL.js → ChatMarkdown-DqW7OUvV.js} +1 -1
- package/dist/renderer/assets/{Config-Bl5vuqBS.js → Config-9lgzpTCp.js} +1 -1
- package/dist/renderer/assets/{CronList-BQOEXhHD.js → CronList-BYZ-x_bp.js} +1 -1
- package/dist/renderer/assets/{Dashboard-CauX4_SR.js → Dashboard-COISXbQy.js} +2 -2
- package/dist/renderer/assets/{EmptyState-DVYGGFl9.js → EmptyState-w5-I9zg7.js} +1 -1
- package/dist/renderer/assets/{KnowledgeDetail-BlV8Twrx.js → KnowledgeDetail-DVxwrTLw.js} +1 -1
- package/dist/renderer/assets/{KnowledgeHome-vd6AO-Fn.js → KnowledgeHome-BKkubcwi.js} +1 -1
- package/dist/renderer/assets/{Logs-BrtF26F7.js → Logs-D7HxZ8Nb.js} +1 -1
- package/dist/renderer/assets/{Modal-D6ucs8W2.js → Modal-Ceu_MP2e.js} +1 -1
- package/dist/renderer/assets/{Page-BdF9afpu.js → Page-CpSczAz8.js} +1 -1
- package/dist/renderer/assets/{ProjectDetail-RuoRHs1Q.js → ProjectDetail-FJ_bNXLF.js} +1 -1
- package/dist/renderer/assets/{ProjectList-r9MXhUu7.js → ProjectList-DZKL4K9C.js} +1 -1
- package/dist/renderer/assets/{Select-CEsAkZ5j.js → Select-BHQk6qCU.js} +1 -1
- package/dist/renderer/assets/{SessionChat-DwnZpTVu.js → SessionChat-DpgSXF3m.js} +1 -1
- package/dist/renderer/assets/{SessionList-C89Z6STB.js → SessionList-CU6drQ4N.js} +1 -1
- package/dist/renderer/assets/{ThreadChat-JB9GY3La.js → ThreadChat-D3s4GXGh.js} +1 -1
- package/dist/renderer/assets/Workspace-B7looTVo.js +14 -0
- package/dist/renderer/assets/{arrow-left-DuiS0IaQ.js → arrow-left-Dtal-eOi.js} +1 -1
- package/dist/renderer/assets/{book-open-B6l3y70b.js → book-open-q814R_Ov.js} +1 -1
- package/dist/renderer/assets/{check-CfewkzaJ.js → check-DjfCdLvw.js} +1 -1
- package/dist/renderer/assets/{circle-qzty4gzl.js → circle-vfhmyE8v.js} +1 -1
- package/dist/renderer/assets/{desktop-DTAjgvNs.js → desktop-BPIxWbkM.js} +1 -1
- package/dist/renderer/assets/desktop-DZHC4PDB.js +3 -0
- package/dist/renderer/assets/{funnel-CwjI0g4c.js → funnel-CUQibo1I.js} +1 -1
- package/dist/renderer/assets/{heart-CVHUi-lA.js → heart-DG9_b8P1.js} +1 -1
- package/dist/renderer/assets/{index-tQ6A4Zff.js → index-Bn8ane6_.js} +2 -2
- package/dist/renderer/assets/{index-BbDTSo1y.js → index-Ds0IN5rP.js} +1 -1
- package/dist/renderer/assets/{pencil-BvQPZxH6.js → pencil-Dij0LlRY.js} +1 -1
- package/dist/renderer/assets/{play-hJkkCcS6.js → play-CPFGjIyJ.js} +1 -1
- package/dist/renderer/assets/{plug-BGtbkjYJ.js → plug-DH3RKQyn.js} +1 -1
- package/dist/renderer/assets/{plus-DrYsrZyx.js → plus-afkK9_Y8.js} +1 -1
- package/dist/renderer/assets/projects-DTDS9SnT.js +1 -0
- package/dist/renderer/assets/{save-C8W3Wq2Z.js → save-B-cKgWcy.js} +1 -1
- package/dist/renderer/assets/{search-BuSByDes.js → search-DR6PeYkt.js} +1 -1
- package/dist/renderer/assets/{server-B9wEMLdo.js → server-BM6KOB5a.js} +1 -1
- package/dist/renderer/assets/{session-utils-K8E4S3Qx.js → session-utils-BDuThM0a.js} +1 -1
- package/dist/renderer/assets/{sessions-B-QTvESY.js → sessions-C1yKps4T.js} +1 -1
- package/dist/renderer/assets/{shield-check-C-oxstVp.js → shield-check-BFjMG-r2.js} +1 -1
- package/dist/renderer/assets/{trash-2-BQpE15-6.js → trash-2-CW1heMCf.js} +1 -1
- package/dist/renderer/assets/{user-CM4R9QVx.js → user-r_2sHG7m.js} +1 -1
- package/dist/renderer/assets/{wifi-off-CoaA7hVn.js → wifi-off-VMUokMpE.js} +1 -1
- package/dist/renderer/index.html +1 -1
- package/dist-electron/electron/agent-runtime-detector.test.js +11 -0
- package/dist-electron/electron/local-core-refactor.test.js +522 -0
- package/dist-electron/electron/plugin-kernel.test.js +77 -9
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +9 -9
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-content.js +60 -0
- package/dist-electron/services/local-ai-core/src/gateway/local-core-lark-gateway.js +337 -54
- package/dist-electron/services/local-ai-core/src/gateway/local-core-weixin-gateway.js +50 -2
- package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +2 -1
- package/dist-electron/services/local-ai-core/src/plugins/builtin/agent-localcore-acp-plugin.js +9 -0
- package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +19 -8
- package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +23 -12
- package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +34 -0
- package/dist-electron/shared/desktop.js +4 -2
- package/package.json +2 -1
- package/dist/renderer/assets/Workspace-Cftl8zps.js +0 -14
- package/dist/renderer/assets/desktop-D4Z7M9RM.js +0 -3
- package/dist/renderer/assets/projects-DcNm9FDK.js +0 -1
|
@@ -31,7 +31,7 @@ function plugin(id, dependsOn = []) {
|
|
|
31
31
|
strict_1.default.deepEqual(kernel.getCapabilitySnapshot(), {
|
|
32
32
|
adapters: {
|
|
33
33
|
channels: ['localcore-acp'],
|
|
34
|
-
agents: ['
|
|
34
|
+
agents: ['cursor', 'gemini', 'qoder', 'iflow', 'localcore-acp'],
|
|
35
35
|
knowledge: false,
|
|
36
36
|
knowledgeProviders: [],
|
|
37
37
|
},
|
|
@@ -43,7 +43,6 @@ function plugin(id, dependsOn = []) {
|
|
|
43
43
|
},
|
|
44
44
|
snapshot: {
|
|
45
45
|
agents: [
|
|
46
|
-
{ id: 'agent.codex', agentType: 'codex', displayName: 'codex' },
|
|
47
46
|
{ id: 'agent.cursor', agentType: 'cursor', displayName: 'cursor' },
|
|
48
47
|
{ id: 'agent.gemini', agentType: 'gemini', displayName: 'gemini' },
|
|
49
48
|
{ id: 'agent.qoder', agentType: 'qoder', displayName: 'qoder' },
|
|
@@ -115,10 +114,9 @@ function plugin(id, dependsOn = []) {
|
|
|
115
114
|
const kernel = (0, bootstrap_js_1.bootstrapLocalCoreKernel)();
|
|
116
115
|
await kernel.lifecycle.initAll();
|
|
117
116
|
const diagnostics = await kernel.diagnostics.snapshot();
|
|
118
|
-
strict_1.default.equal(diagnostics.pluginCount,
|
|
119
|
-
strict_1.default.equal(diagnostics.enabledPluginCount,
|
|
117
|
+
strict_1.default.equal(diagnostics.pluginCount, 6);
|
|
118
|
+
strict_1.default.equal(diagnostics.enabledPluginCount, 6);
|
|
120
119
|
strict_1.default.deepEqual(diagnostics.plugins.map((plugin) => plugin.pluginId).sort(), [
|
|
121
|
-
'builtin.agent-codex',
|
|
122
120
|
'builtin.agent-cursor',
|
|
123
121
|
'builtin.agent-gemini',
|
|
124
122
|
'builtin.agent-iflow',
|
|
@@ -131,7 +129,6 @@ function plugin(id, dependsOn = []) {
|
|
|
131
129
|
enabled: plugin.enabled,
|
|
132
130
|
health: plugin.health,
|
|
133
131
|
})), [
|
|
134
|
-
'builtin.agent-codex',
|
|
135
132
|
'builtin.agent-cursor',
|
|
136
133
|
'builtin.agent-gemini',
|
|
137
134
|
'builtin.agent-qoder',
|
|
@@ -151,13 +148,13 @@ function plugin(id, dependsOn = []) {
|
|
|
151
148
|
userDataPath,
|
|
152
149
|
});
|
|
153
150
|
strict_1.default.deepEqual(runtime.kernel.getCapabilitySnapshot().adapters.agents, [
|
|
154
|
-
'codex',
|
|
155
151
|
'cursor',
|
|
156
152
|
'gemini',
|
|
157
153
|
'qoder',
|
|
158
154
|
'iflow',
|
|
159
155
|
'localcore-acp',
|
|
160
156
|
'opencode',
|
|
157
|
+
'codex',
|
|
161
158
|
'claudecode',
|
|
162
159
|
]);
|
|
163
160
|
strict_1.default.deepEqual(runtime.kernel.getCapabilitySnapshot().adapters.channels, ['localcore-acp', 'lark', 'weixin']);
|
|
@@ -184,13 +181,13 @@ function plugin(id, dependsOn = []) {
|
|
|
184
181
|
enableKnowledge: false,
|
|
185
182
|
});
|
|
186
183
|
strict_1.default.deepEqual(runtime.kernel.getCapabilitySnapshot().adapters.agents, [
|
|
187
|
-
'codex',
|
|
188
184
|
'cursor',
|
|
189
185
|
'gemini',
|
|
190
186
|
'qoder',
|
|
191
187
|
'iflow',
|
|
192
188
|
'localcore-acp',
|
|
193
189
|
'opencode',
|
|
190
|
+
'codex',
|
|
194
191
|
'claudecode',
|
|
195
192
|
]);
|
|
196
193
|
strict_1.default.deepEqual(runtime.kernel.getCapabilitySnapshot().adapters.channels, ['localcore-acp', 'lark', 'weixin']);
|
|
@@ -207,6 +204,43 @@ function plugin(id, dependsOn = []) {
|
|
|
207
204
|
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
208
205
|
}
|
|
209
206
|
});
|
|
207
|
+
(0, node_test_1.default)('codex agent runtime routes projects through the bundled ACP adapter', async () => {
|
|
208
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-kernel-'));
|
|
209
|
+
try {
|
|
210
|
+
const runtime = (0, bootstrap_js_2.bootstrapLocalCoreRuntime)({
|
|
211
|
+
userDataPath,
|
|
212
|
+
enableKnowledge: false,
|
|
213
|
+
});
|
|
214
|
+
await runtime.state.saveRawConfigFile(`
|
|
215
|
+
[[projects]]
|
|
216
|
+
name = "codex-workspace"
|
|
217
|
+
|
|
218
|
+
[projects.agent]
|
|
219
|
+
type = "codex"
|
|
220
|
+
`);
|
|
221
|
+
const configState = await runtime.state.readConfigFile();
|
|
222
|
+
const project = configState.parsed?.projects?.find((entry) => entry.name === 'codex-workspace');
|
|
223
|
+
const codexRuntime = runtime.agentRuntimes.find((entry) => entry.agentType === 'codex');
|
|
224
|
+
const route = project ? codexRuntime?.createRoute(configState, project) : null;
|
|
225
|
+
strict_1.default.equal(route?.agentType, 'codex');
|
|
226
|
+
strict_1.default.equal(route?.config.command, process.execPath);
|
|
227
|
+
strict_1.default.match(route?.config.args[0] || '', /@zed-industries[/\\]codex-acp[/\\]bin[/\\]codex-acp\.js$/);
|
|
228
|
+
strict_1.default.deepEqual(await runtime.workspaceRouter.listWorkspaces(), [
|
|
229
|
+
{
|
|
230
|
+
id: 'codex-workspace',
|
|
231
|
+
name: 'codex-workspace',
|
|
232
|
+
agentType: 'codex',
|
|
233
|
+
platforms: [],
|
|
234
|
+
sessionsCount: 0,
|
|
235
|
+
heartbeatEnabled: false,
|
|
236
|
+
},
|
|
237
|
+
]);
|
|
238
|
+
await runtime.stop();
|
|
239
|
+
}
|
|
240
|
+
finally {
|
|
241
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
242
|
+
}
|
|
243
|
+
});
|
|
210
244
|
(0, node_test_1.default)('runtime bootstrap knowledge capabilities come from the selected provider plugin', () => {
|
|
211
245
|
const enabledUserDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-kernel-'));
|
|
212
246
|
const disabledUserDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-kernel-'));
|
|
@@ -408,6 +442,40 @@ function plugin(id, dependsOn = []) {
|
|
|
408
442
|
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
409
443
|
}
|
|
410
444
|
});
|
|
445
|
+
(0, node_test_1.default)('controller logs are persisted to local-core.log', () => {
|
|
446
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-controller-logs-'));
|
|
447
|
+
try {
|
|
448
|
+
const runtime = (0, bootstrap_js_2.bootstrapLocalCoreRuntime)({
|
|
449
|
+
userDataPath,
|
|
450
|
+
enableKnowledge: false,
|
|
451
|
+
});
|
|
452
|
+
const controller = new local_core_controller_js_1.LocalCoreController(userDataPath, runtime);
|
|
453
|
+
const emittedLogs = [];
|
|
454
|
+
controller.on('logs', (line) => emittedLogs.push(line));
|
|
455
|
+
controller.handleLog('localcore-lark inbound message for project-1');
|
|
456
|
+
const logPath = (0, node_path_1.join)(userDataPath, 'runtime', 'local-core.log');
|
|
457
|
+
const raw = (0, node_fs_1.readFileSync)(logPath, 'utf-8');
|
|
458
|
+
strict_1.default.deepEqual(emittedLogs, ['localcore-lark inbound message for project-1']);
|
|
459
|
+
strict_1.default.match(raw, /^\d{4}-\d{2}-\d{2}T.* localcore-lark inbound message for project-1/m);
|
|
460
|
+
}
|
|
461
|
+
finally {
|
|
462
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
(0, node_test_1.default)('controller buffers bootstrap logs until runtime state is assigned', () => {
|
|
466
|
+
const userDataPath = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'agentdock-bootstrap-logs-'));
|
|
467
|
+
try {
|
|
468
|
+
const controller = new local_core_controller_js_1.LocalCoreController(userDataPath);
|
|
469
|
+
const logPath = (0, node_path_1.join)(userDataPath, 'runtime', 'local-core.log');
|
|
470
|
+
const raw = (0, node_fs_1.readFileSync)(logPath, 'utf-8');
|
|
471
|
+
strict_1.default.match(raw, /\[plugin:builtin\.agent-codex\] registered/);
|
|
472
|
+
strict_1.default.equal((raw.match(/\[plugin:builtin\.agent-codex\] registered/g) || []).length, 1);
|
|
473
|
+
void controller.close();
|
|
474
|
+
}
|
|
475
|
+
finally {
|
|
476
|
+
(0, node_fs_1.rmSync)(userDataPath, { recursive: true, force: true });
|
|
477
|
+
}
|
|
478
|
+
});
|
|
411
479
|
(0, node_test_1.default)('channel plugin lifecycle start and stop are driven by the kernel lifecycle', async () => {
|
|
412
480
|
const calls = [];
|
|
413
481
|
const registry = new plugin_registry_js_1.LocalCorePluginRegistry();
|
|
@@ -504,7 +572,7 @@ name = "claude-workspace"
|
|
|
504
572
|
[projects.agent]
|
|
505
573
|
type = "claudecode"
|
|
506
574
|
`);
|
|
507
|
-
strict_1.default.deepEqual(runtime.agentRuntimes.map((entry) => entry.agentType), ['localcore-acp', 'opencode']);
|
|
575
|
+
strict_1.default.deepEqual(runtime.agentRuntimes.map((entry) => entry.agentType), ['localcore-acp', 'opencode', 'codex']);
|
|
508
576
|
strict_1.default.equal(runtime.kernel.getCapabilitySnapshot().snapshot.agents.some((capability) => capability.agentType === 'claudecode'), false);
|
|
509
577
|
strict_1.default.deepEqual(await runtime.workspaceRouter.listWorkspaces(), []);
|
|
510
578
|
await strict_1.default.rejects(() => runtime.workspaceRouter.listThreads('claude-workspace'), /Workspace "claude-workspace" is not configured as a Local AI Core ACP workspace/);
|
|
@@ -7,6 +7,7 @@ const local_core_acp_transport_js_1 = require("./local-core-acp-transport.js");
|
|
|
7
7
|
const local_core_acp_turn_coordinator_js_1 = require("./local-core-acp-turn-coordinator.js");
|
|
8
8
|
const local_core_acp_session_coordinator_js_1 = require("./local-core-acp-session-coordinator.js");
|
|
9
9
|
const local_core_acp_response_processor_js_1 = require("./local-core-acp-response-processor.js");
|
|
10
|
+
const local_core_acp_content_js_1 = require("./local-core-acp-content.js");
|
|
10
11
|
const command_risk_js_1 = require("../security/command-risk.js");
|
|
11
12
|
const ACP_PROMPT_TIMEOUT_MS = 15 * 60 * 1000;
|
|
12
13
|
class LocalCoreAcpBackend {
|
|
@@ -126,7 +127,7 @@ class LocalCoreAcpBackend {
|
|
|
126
127
|
this.options.store.deleteThread(threadId);
|
|
127
128
|
return { deleted: true };
|
|
128
129
|
}
|
|
129
|
-
async sendThreadMessage(threadId,
|
|
130
|
+
async sendThreadMessage(threadId, input, config) {
|
|
130
131
|
if (!config) {
|
|
131
132
|
throw new Error('localcore-acp message send requires a workspace config.');
|
|
132
133
|
}
|
|
@@ -134,6 +135,8 @@ class LocalCoreAcpBackend {
|
|
|
134
135
|
if (!row) {
|
|
135
136
|
throw new Error(`Thread not found: ${threadId}`);
|
|
136
137
|
}
|
|
138
|
+
const message = (0, local_core_acp_content_js_1.normalizeThreadMessageInput)(input);
|
|
139
|
+
const content = message.displayText;
|
|
137
140
|
this.options.store.appendMessage(threadId, 'user', content, 'final');
|
|
138
141
|
this.options.eventBus.emit({
|
|
139
142
|
type: 'thread.message.accepted',
|
|
@@ -169,7 +172,7 @@ class LocalCoreAcpBackend {
|
|
|
169
172
|
sessionKey: row.bridge_session_key,
|
|
170
173
|
},
|
|
171
174
|
});
|
|
172
|
-
void this.runPrompt(threadId, runId, row.bridge_session_key, config,
|
|
175
|
+
void this.runPrompt(threadId, runId, row.bridge_session_key, config, message).catch((error) => {
|
|
173
176
|
this.options.log?.(`localcore-acp prompt failed for ${threadId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
174
177
|
});
|
|
175
178
|
return { runId };
|
|
@@ -219,7 +222,7 @@ class LocalCoreAcpBackend {
|
|
|
219
222
|
async interruptRun(runId) {
|
|
220
223
|
return this.sessionCoordinator.interruptRun(runId);
|
|
221
224
|
}
|
|
222
|
-
async runPrompt(threadId, runId, bridgeSessionKey, config,
|
|
225
|
+
async runPrompt(threadId, runId, bridgeSessionKey, config, input) {
|
|
223
226
|
const row = this.options.store.getThreadRow(threadId);
|
|
224
227
|
if (!row) {
|
|
225
228
|
throw new Error(`Thread not found: ${threadId}`);
|
|
@@ -229,6 +232,8 @@ class LocalCoreAcpBackend {
|
|
|
229
232
|
sessionKey: bridgeSessionKey,
|
|
230
233
|
replyCtx: runId,
|
|
231
234
|
});
|
|
235
|
+
const message = (0, local_core_acp_content_js_1.normalizeThreadMessageInput)(input);
|
|
236
|
+
const content = message.displayText;
|
|
232
237
|
let session = null;
|
|
233
238
|
try {
|
|
234
239
|
session = await this.sessionCoordinator.ensureSession(threadId, bridgeSessionKey, config);
|
|
@@ -256,12 +261,7 @@ class LocalCoreAcpBackend {
|
|
|
256
261
|
const promptPromise = this.transport.request(session, 'session/prompt', {
|
|
257
262
|
sessionId: session.sessionId,
|
|
258
263
|
messageId: (0, node_crypto_1.randomUUID)(),
|
|
259
|
-
prompt:
|
|
260
|
-
{
|
|
261
|
-
type: 'text',
|
|
262
|
-
text: content,
|
|
263
|
-
},
|
|
264
|
-
],
|
|
264
|
+
prompt: message.contentParts,
|
|
265
265
|
}, ACP_PROMPT_TIMEOUT_MS);
|
|
266
266
|
session.promptPromise = promptPromise;
|
|
267
267
|
const result = await promptPromise;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeThreadMessageInput = normalizeThreadMessageInput;
|
|
4
|
+
exports.createTextMessageInput = createTextMessageInput;
|
|
5
|
+
function normalizeThreadMessageInput(input) {
|
|
6
|
+
if (typeof input === 'string') {
|
|
7
|
+
return {
|
|
8
|
+
displayText: input,
|
|
9
|
+
contentParts: [{ type: 'text', text: input }],
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
const displayText = String(input.displayText || '').trim();
|
|
13
|
+
const contentParts = Array.isArray(input.contentParts)
|
|
14
|
+
? input.contentParts.filter(isSupportedContentPart)
|
|
15
|
+
: [];
|
|
16
|
+
if (contentParts.length > 0) {
|
|
17
|
+
return {
|
|
18
|
+
displayText: displayText || summarizeContentParts(contentParts),
|
|
19
|
+
contentParts,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const fallbackText = displayText || '';
|
|
23
|
+
return {
|
|
24
|
+
displayText: fallbackText,
|
|
25
|
+
contentParts: [{ type: 'text', text: fallbackText }],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function createTextMessageInput(text) {
|
|
29
|
+
return normalizeThreadMessageInput(text);
|
|
30
|
+
}
|
|
31
|
+
function isSupportedContentPart(part) {
|
|
32
|
+
if (!part || typeof part !== 'object') {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
const type = String(part.type || '');
|
|
36
|
+
if (type === 'text') {
|
|
37
|
+
return Boolean(String(part.text || ''));
|
|
38
|
+
}
|
|
39
|
+
if (type === 'image') {
|
|
40
|
+
const image = part;
|
|
41
|
+
return Boolean(String(image.data || image.uri || ''));
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
function summarizeContentParts(parts) {
|
|
46
|
+
const text = parts
|
|
47
|
+
.filter((part) => part.type === 'text')
|
|
48
|
+
.map((part) => part.text.trim())
|
|
49
|
+
.filter(Boolean)
|
|
50
|
+
.join('\n\n')
|
|
51
|
+
.trim();
|
|
52
|
+
if (text) {
|
|
53
|
+
return text;
|
|
54
|
+
}
|
|
55
|
+
const imageCount = parts.filter((part) => part.type === 'image').length;
|
|
56
|
+
if (imageCount > 0) {
|
|
57
|
+
return imageCount === 1 ? '[Image]' : `[${imageCount} images]`;
|
|
58
|
+
}
|
|
59
|
+
return '';
|
|
60
|
+
}
|