pikiclaw 0.2.53 → 0.2.55
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 +1 -1
- package/dist/bot-feishu-render.js +3 -1
- package/dist/bot-feishu.js +15 -1
- package/dist/bot-telegram-render.js +4 -2
- package/dist/bot-telegram.js +2 -1
- package/dist/channel-feishu.js +14 -1
- package/dist/code-agent.js +36 -4
- package/dist/driver-codex.js +20 -3
- package/dist/driver-gemini.js +126 -11
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -199,8 +199,10 @@ export function buildHumanLoopPromptMarkdown(prompt) {
|
|
|
199
199
|
// ---------------------------------------------------------------------------
|
|
200
200
|
// LivePreview renderer — produces Markdown for Feishu card elements
|
|
201
201
|
// ---------------------------------------------------------------------------
|
|
202
|
-
export function buildInitialPreviewMarkdown(agent, model, effort) {
|
|
202
|
+
export function buildInitialPreviewMarkdown(agent, model, effort, waiting = false) {
|
|
203
203
|
const parts = [];
|
|
204
|
+
if (waiting)
|
|
205
|
+
parts.push('Waiting in queue...');
|
|
204
206
|
if (model)
|
|
205
207
|
parts.push(model);
|
|
206
208
|
else
|
package/dist/bot-feishu.js
CHANGED
|
@@ -28,6 +28,7 @@ const SHUTDOWN_EXIT_CODE = {
|
|
|
28
28
|
SIGINT: 130,
|
|
29
29
|
SIGTERM: 143,
|
|
30
30
|
};
|
|
31
|
+
const FEISHU_FILE_STAGE_REACTION = 'Get';
|
|
31
32
|
function describeError(err) {
|
|
32
33
|
if (!(err instanceof Error))
|
|
33
34
|
return String(err ?? 'unknown error');
|
|
@@ -464,6 +465,17 @@ export class FeishuBot extends Bot {
|
|
|
464
465
|
return active.result;
|
|
465
466
|
};
|
|
466
467
|
}
|
|
468
|
+
async safeSetMessageReaction(chatId, messageId, reactions) {
|
|
469
|
+
if (!supportsChannelCapability(this.channel, 'messageReactions'))
|
|
470
|
+
return;
|
|
471
|
+
const setReaction = this.channel?.setMessageReaction;
|
|
472
|
+
if (typeof setReaction !== 'function')
|
|
473
|
+
return;
|
|
474
|
+
try {
|
|
475
|
+
await setReaction.call(this.channel, chatId, messageId, reactions);
|
|
476
|
+
}
|
|
477
|
+
catch { }
|
|
478
|
+
}
|
|
467
479
|
// ---- streaming bridge -----------------------------------------------------
|
|
468
480
|
async handleMessage(msg, ctx) {
|
|
469
481
|
const text = msg.text.trim();
|
|
@@ -507,6 +519,7 @@ export class FeishuBot extends Bot {
|
|
|
507
519
|
throw new Error('no files persisted');
|
|
508
520
|
this.log(`[handleMessage] staged files chat=${ctx.chatId} session=${staged.sessionId} files=${staged.importedFiles.length}`);
|
|
509
521
|
this.registerSessionMessage(ctx.chatId, ctx.messageId, session);
|
|
522
|
+
await this.safeSetMessageReaction(ctx.chatId, ctx.messageId, [FEISHU_FILE_STAGE_REACTION]);
|
|
510
523
|
}
|
|
511
524
|
catch (e) {
|
|
512
525
|
this.log(`[handleMessage] stage files failed: ${e?.message || e}`);
|
|
@@ -525,6 +538,7 @@ export class FeishuBot extends Bot {
|
|
|
525
538
|
const start = Date.now();
|
|
526
539
|
this.log(`[handleMessage] start chat=${ctx.chatId} agent=${session.agent} session=${session.sessionId || '(new)'} ` +
|
|
527
540
|
`files=${files.length} prompt="${prompt.slice(0, 100)}"`);
|
|
541
|
+
const waiting = this.sessionHasPendingWork(session);
|
|
528
542
|
const taskId = this.createTaskId(session);
|
|
529
543
|
this.beginTask({
|
|
530
544
|
taskId,
|
|
@@ -538,7 +552,7 @@ export class FeishuBot extends Bot {
|
|
|
538
552
|
const stopKeyboard = this.buildStopKeyboard(this.actionIdForTask(taskId));
|
|
539
553
|
const model = session.modelId || this.modelForAgent(session.agent);
|
|
540
554
|
const effort = this.effortForAgent(session.agent);
|
|
541
|
-
const placeholderId = await this.channel.sendStreamingCard(ctx.chatId, buildInitialPreviewMarkdown(session.agent, model, effort), {
|
|
555
|
+
const placeholderId = await this.channel.sendStreamingCard(ctx.chatId, buildInitialPreviewMarkdown(session.agent, model, effort, waiting), {
|
|
542
556
|
replyTo: ctx.messageId || undefined,
|
|
543
557
|
keyboard: stopKeyboard,
|
|
544
558
|
});
|
|
@@ -327,8 +327,10 @@ function trimActivityForPreview(text, maxChars = 900) {
|
|
|
327
327
|
return text.slice(0, Math.max(0, maxChars - 3)).trimEnd() + '...';
|
|
328
328
|
return [...head, '...', ...tail].join('\n');
|
|
329
329
|
}
|
|
330
|
-
export function buildInitialPreviewHtml(agent) {
|
|
331
|
-
return
|
|
330
|
+
export function buildInitialPreviewHtml(agent, waiting = false) {
|
|
331
|
+
return waiting
|
|
332
|
+
? `<i>Waiting in queue...</i>\n\n${formatPreviewFooterHtml(agent, 0)}`
|
|
333
|
+
: formatPreviewFooterHtml(agent, 0);
|
|
332
334
|
}
|
|
333
335
|
export function buildStreamPreviewHtml(input) {
|
|
334
336
|
const maxBody = 2400;
|
package/dist/bot-telegram.js
CHANGED
|
@@ -502,9 +502,10 @@ export class TelegramBot extends Bot {
|
|
|
502
502
|
sourceMessageId: ctx.messageId,
|
|
503
503
|
});
|
|
504
504
|
const stopKeyboard = this.buildStopKeyboard(this.actionIdForTask(taskId));
|
|
505
|
+
const waiting = this.sessionHasPendingWork(session);
|
|
505
506
|
let phId = null;
|
|
506
507
|
if (canEditMessages) {
|
|
507
|
-
const placeholderId = await ctx.reply(buildInitialPreviewHtml(session.agent), { parseMode: 'HTML', messageThreadId, keyboard: stopKeyboard });
|
|
508
|
+
const placeholderId = await ctx.reply(buildInitialPreviewHtml(session.agent, waiting), { parseMode: 'HTML', messageThreadId, keyboard: stopKeyboard });
|
|
508
509
|
phId = typeof placeholderId === 'number' ? placeholderId : null;
|
|
509
510
|
if (phId != null) {
|
|
510
511
|
this.registerSessionMessage(ctx.chatId, phId, session);
|
package/dist/channel-feishu.js
CHANGED
|
@@ -217,7 +217,7 @@ class FeishuChannel extends Channel {
|
|
|
217
217
|
typingIndicators: false,
|
|
218
218
|
commandMenu: true,
|
|
219
219
|
callbackActions: true,
|
|
220
|
-
messageReactions:
|
|
220
|
+
messageReactions: true,
|
|
221
221
|
fileUpload: true,
|
|
222
222
|
fileDownload: true,
|
|
223
223
|
threads: false,
|
|
@@ -710,6 +710,19 @@ class FeishuChannel extends Channel {
|
|
|
710
710
|
async sendTyping(_chatId) {
|
|
711
711
|
// Feishu has no typing indicator API — no-op
|
|
712
712
|
}
|
|
713
|
+
async setMessageReaction(_chatId, msgId, reactions) {
|
|
714
|
+
const messageId = String(msgId || '').trim();
|
|
715
|
+
const emojiTypes = [...new Set(reactions.map(reaction => String(reaction || '').trim()).filter(Boolean))];
|
|
716
|
+
if (!messageId || !emojiTypes.length)
|
|
717
|
+
return;
|
|
718
|
+
this._logOutgoing('setReaction', `msg_id=${messageId} reactions=${emojiTypes.join(',')}`);
|
|
719
|
+
for (const emojiType of emojiTypes) {
|
|
720
|
+
await this.client.im.messageReaction.create({
|
|
721
|
+
path: { message_id: messageId },
|
|
722
|
+
data: { reaction_type: { emoji_type: emojiType } },
|
|
723
|
+
}).catch(() => { });
|
|
724
|
+
}
|
|
725
|
+
}
|
|
713
726
|
// ========================================================================
|
|
714
727
|
// Streaming cards (CardKit v1) — typewriter effect
|
|
715
728
|
// ========================================================================
|
package/dist/code-agent.js
CHANGED
|
@@ -97,6 +97,36 @@ export function shortValue(value, max = 90) {
|
|
|
97
97
|
return text;
|
|
98
98
|
return `${text.slice(0, Math.max(0, max - 3)).trimEnd()}...`;
|
|
99
99
|
}
|
|
100
|
+
export function normalizeErrorMessage(value) {
|
|
101
|
+
if (typeof value === 'string')
|
|
102
|
+
return value.trim();
|
|
103
|
+
if (value instanceof Error)
|
|
104
|
+
return value.message.trim();
|
|
105
|
+
if (Array.isArray(value)) {
|
|
106
|
+
return value.map(item => normalizeErrorMessage(item)).filter(Boolean).join('; ').trim();
|
|
107
|
+
}
|
|
108
|
+
if (value && typeof value === 'object') {
|
|
109
|
+
const record = value;
|
|
110
|
+
const preferred = normalizeErrorMessage(record.message)
|
|
111
|
+
|| normalizeErrorMessage(record.error)
|
|
112
|
+
|| normalizeErrorMessage(record.detail)
|
|
113
|
+
|| normalizeErrorMessage(record.type)
|
|
114
|
+
|| normalizeErrorMessage(record.code)
|
|
115
|
+
|| normalizeErrorMessage(record.status);
|
|
116
|
+
if (preferred)
|
|
117
|
+
return preferred;
|
|
118
|
+
try {
|
|
119
|
+
return JSON.stringify(value).trim();
|
|
120
|
+
}
|
|
121
|
+
catch { }
|
|
122
|
+
}
|
|
123
|
+
return value == null ? '' : String(value).trim();
|
|
124
|
+
}
|
|
125
|
+
export function joinErrorMessages(errors) {
|
|
126
|
+
if (!errors?.length)
|
|
127
|
+
return '';
|
|
128
|
+
return errors.map(error => normalizeErrorMessage(error)).filter(Boolean).join('; ').trim();
|
|
129
|
+
}
|
|
100
130
|
export function appendSystemPrompt(base, extra) {
|
|
101
131
|
const lhs = String(base || '').trim();
|
|
102
132
|
const rhs = String(extra || '').trim();
|
|
@@ -655,6 +685,7 @@ export async function run(cmd, opts, parseLine) {
|
|
|
655
685
|
recentActivity: [],
|
|
656
686
|
claudeToolsById: new Map(),
|
|
657
687
|
seenClaudeToolIds: new Set(),
|
|
688
|
+
geminiToolsById: new Map(),
|
|
658
689
|
};
|
|
659
690
|
const shellCmd = cmd.map(Q).join(' ');
|
|
660
691
|
agentLog(`[spawn] full command: cd ${Q(opts.workdir)} && ${shellCmd}`);
|
|
@@ -729,16 +760,17 @@ export async function run(cmd, opts, parseLine) {
|
|
|
729
760
|
s.text = s.msgs.join('\n\n');
|
|
730
761
|
if (!s.thinking.trim() && s.thinkParts.length)
|
|
731
762
|
s.thinking = s.thinkParts.join('\n\n');
|
|
763
|
+
const errorText = joinErrorMessages(s.errors);
|
|
732
764
|
const ok = procOk && !s.errors && !timedOut && !interrupted;
|
|
733
|
-
const error =
|
|
765
|
+
const error = errorText
|
|
734
766
|
|| (interrupted ? 'Interrupted by user.' : null)
|
|
735
767
|
|| (timedOut ? `Timed out after ${opts.timeout}s before the agent reported completion.` : null)
|
|
736
768
|
|| (!procOk ? (stderr.trim() || `Failed (exit=${code}).`) : null);
|
|
737
769
|
const incomplete = !ok || s.stopReason === 'max_tokens' || s.stopReason === 'timeout';
|
|
738
770
|
const elapsed = ((Date.now() - start) / 1000).toFixed(1);
|
|
739
771
|
agentLog(`[result] ok=${ok && !s.errors} elapsed=${elapsed}s text=${s.text.length}chars thinking=${s.thinking.length}chars session=${s.sessionId || '?'}`);
|
|
740
|
-
if (
|
|
741
|
-
agentLog(`[result] errors: ${
|
|
772
|
+
if (errorText)
|
|
773
|
+
agentLog(`[result] errors: ${errorText}`);
|
|
742
774
|
if (s.stopReason)
|
|
743
775
|
agentLog(`[result] stop_reason=${s.stopReason}`);
|
|
744
776
|
if (stderr.trim() && !procOk)
|
|
@@ -746,7 +778,7 @@ export async function run(cmd, opts, parseLine) {
|
|
|
746
778
|
return {
|
|
747
779
|
ok, sessionId: s.sessionId, workspacePath: null,
|
|
748
780
|
model: s.model, thinkingEffort: s.thinkingEffort,
|
|
749
|
-
message: s.text.trim() ||
|
|
781
|
+
message: s.text.trim() || errorText || (procOk ? '(no textual response)' : `Failed (exit=${code}).\n\n${stderr.trim() || '(no output)'}`),
|
|
750
782
|
thinking: s.thinking.trim() || null,
|
|
751
783
|
elapsedS: (Date.now() - start) / 1000,
|
|
752
784
|
inputTokens: s.inputTokens, outputTokens: s.outputTokens, cachedInputTokens: s.cachedInputTokens,
|
package/dist/driver-codex.js
CHANGED
|
@@ -320,6 +320,20 @@ function buildCodexCumulativeUsage(raw) {
|
|
|
320
320
|
return null;
|
|
321
321
|
return { input: input ?? 0, output: output ?? 0, cached: cached ?? 0 };
|
|
322
322
|
}
|
|
323
|
+
function buildCodexContextUsage(raw) {
|
|
324
|
+
if (!raw || typeof raw !== 'object')
|
|
325
|
+
return null;
|
|
326
|
+
const total = numberOrNull(raw.totalTokens, raw.total_tokens);
|
|
327
|
+
if (total != null && total >= 0)
|
|
328
|
+
return total;
|
|
329
|
+
const input = numberOrNull(raw.inputTokens, raw.input_tokens);
|
|
330
|
+
const output = numberOrNull(raw.outputTokens, raw.output_tokens);
|
|
331
|
+
if (input != null && output != null)
|
|
332
|
+
return input + output;
|
|
333
|
+
if (input != null)
|
|
334
|
+
return input;
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
323
337
|
function applyCodexTokenUsage(s, rawUsage, prev) {
|
|
324
338
|
if (!rawUsage || typeof rawUsage !== 'object')
|
|
325
339
|
return;
|
|
@@ -337,6 +351,9 @@ function applyCodexTokenUsage(s, rawUsage, prev) {
|
|
|
337
351
|
s.cachedInputTokens = lastCached;
|
|
338
352
|
if (lastCacheCreation != null)
|
|
339
353
|
s.cacheCreationInputTokens = lastCacheCreation;
|
|
354
|
+
const lastContextUsage = buildCodexContextUsage(last);
|
|
355
|
+
if (lastContextUsage != null)
|
|
356
|
+
s.contextUsedTokens = lastContextUsage;
|
|
340
357
|
const totalUsage = info.total ?? info.totalTokenUsage ?? info.total_token_usage ?? rawUsage.total ?? rawUsage;
|
|
341
358
|
const total = buildCodexCumulativeUsage(totalUsage);
|
|
342
359
|
if (total) {
|
|
@@ -349,9 +366,9 @@ function applyCodexTokenUsage(s, rawUsage, prev) {
|
|
|
349
366
|
s.cachedInputTokens = prev ? Math.max(0, total.cached - prev.cached) : total.cached;
|
|
350
367
|
}
|
|
351
368
|
// NOTE: do NOT set s.contextUsedTokens from cumulative totals —
|
|
352
|
-
//
|
|
353
|
-
//
|
|
354
|
-
//
|
|
369
|
+
// those counters span the full thread, not the current turn. Use the per-turn
|
|
370
|
+
// `last` usage only. `cached_input_tokens` is already a subset of
|
|
371
|
+
// `input_tokens`, so adding it again inflates the context percentage.
|
|
355
372
|
const contextWindow = numberOrNull(info.modelContextWindow, info.model_context_window, rawUsage.modelContextWindow, rawUsage.model_context_window);
|
|
356
373
|
if (contextWindow != null && contextWindow > 0)
|
|
357
374
|
s.contextWindow = contextWindow;
|
package/dist/driver-gemini.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { registerDriver } from './agent-driver.js';
|
|
8
8
|
import fs from 'node:fs';
|
|
9
9
|
import path from 'node:path';
|
|
10
|
-
import { run, agentLog, detectAgentBin, pushRecentActivity, listPikiclawSessions, isPendingSessionId, emptyUsage, } from './code-agent.js';
|
|
10
|
+
import { run, agentLog, detectAgentBin, pushRecentActivity, firstNonEmptyLine, shortValue, normalizeErrorMessage, listPikiclawSessions, isPendingSessionId, emptyUsage, } from './code-agent.js';
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
12
|
// Command & parser
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
@@ -49,6 +49,101 @@ function geminiContextWindowFromModel(model) {
|
|
|
49
49
|
return 1_048_576;
|
|
50
50
|
return null;
|
|
51
51
|
}
|
|
52
|
+
function geminiToolName(value) {
|
|
53
|
+
const name = typeof value === 'string' ? value.trim() : '';
|
|
54
|
+
return name || 'tool';
|
|
55
|
+
}
|
|
56
|
+
function geminiToolLabel(name) {
|
|
57
|
+
return name
|
|
58
|
+
.replace(/^mcp_/, '')
|
|
59
|
+
.replace(/^discovered_tool_/, '')
|
|
60
|
+
.replace(/_/g, ' ')
|
|
61
|
+
.replace(/\s+/g, ' ')
|
|
62
|
+
.trim() || 'tool';
|
|
63
|
+
}
|
|
64
|
+
function geminiToolSummary(name, parameters) {
|
|
65
|
+
const tool = geminiToolName(name);
|
|
66
|
+
const params = parameters && typeof parameters === 'object' ? parameters : {};
|
|
67
|
+
switch (tool) {
|
|
68
|
+
case 'read_file': {
|
|
69
|
+
const target = shortValue(params.file_path || params.path, 140);
|
|
70
|
+
return target ? `Read ${target}` : 'Read file';
|
|
71
|
+
}
|
|
72
|
+
case 'read_many_files': {
|
|
73
|
+
const include = shortValue(params.include || params.pattern, 120);
|
|
74
|
+
return include ? `Read files: ${include}` : 'Read files';
|
|
75
|
+
}
|
|
76
|
+
case 'write_file': {
|
|
77
|
+
const target = shortValue(params.file_path || params.path, 140);
|
|
78
|
+
return target ? `Write ${target}` : 'Write file';
|
|
79
|
+
}
|
|
80
|
+
case 'replace': {
|
|
81
|
+
const target = shortValue(params.file_path || params.path, 140);
|
|
82
|
+
return target ? `Edit ${target}` : 'Edit file';
|
|
83
|
+
}
|
|
84
|
+
case 'list_directory': {
|
|
85
|
+
const dir = shortValue(params.dir_path || params.path, 120);
|
|
86
|
+
return dir ? `List files: ${dir}` : 'List files';
|
|
87
|
+
}
|
|
88
|
+
case 'glob': {
|
|
89
|
+
const pattern = shortValue(params.pattern || params.glob, 120);
|
|
90
|
+
return pattern ? `Find files: ${pattern}` : 'Find files';
|
|
91
|
+
}
|
|
92
|
+
case 'grep_search':
|
|
93
|
+
case 'search_file_content': {
|
|
94
|
+
const pattern = shortValue(params.pattern || params.query, 120);
|
|
95
|
+
return pattern ? `Search text: ${pattern}` : 'Search text';
|
|
96
|
+
}
|
|
97
|
+
case 'run_shell_command': {
|
|
98
|
+
const command = shortValue(params.command, 120);
|
|
99
|
+
return command ? `Run shell: ${command}` : 'Run shell';
|
|
100
|
+
}
|
|
101
|
+
case 'web_fetch': {
|
|
102
|
+
const target = shortValue(params.url || params.prompt, 120);
|
|
103
|
+
return target ? `Fetch ${target}` : 'Fetch web page';
|
|
104
|
+
}
|
|
105
|
+
case 'google_web_search': {
|
|
106
|
+
const query = shortValue(params.query, 120);
|
|
107
|
+
return query ? `Search web: ${query}` : 'Search web';
|
|
108
|
+
}
|
|
109
|
+
case 'write_todos': return 'Update todo list';
|
|
110
|
+
case 'save_memory': return 'Save memory';
|
|
111
|
+
case 'ask_user': return 'Request user input';
|
|
112
|
+
case 'activate_skill': {
|
|
113
|
+
const skill = shortValue(params.name, 80);
|
|
114
|
+
return skill ? `Activate skill: ${skill}` : 'Activate skill';
|
|
115
|
+
}
|
|
116
|
+
case 'get_internal_docs': {
|
|
117
|
+
const target = shortValue(params.path, 120);
|
|
118
|
+
return target ? `Read docs: ${target}` : 'Read docs';
|
|
119
|
+
}
|
|
120
|
+
case 'enter_plan_mode': return 'Enter plan mode';
|
|
121
|
+
case 'exit_plan_mode': return 'Exit plan mode';
|
|
122
|
+
default: {
|
|
123
|
+
const detail = shortValue(params.file_path
|
|
124
|
+
|| params.path
|
|
125
|
+
|| params.dir_path
|
|
126
|
+
|| params.pattern
|
|
127
|
+
|| params.query
|
|
128
|
+
|| params.command
|
|
129
|
+
|| params.url
|
|
130
|
+
|| params.name, 120);
|
|
131
|
+
const label = shortValue(geminiToolLabel(tool), 80);
|
|
132
|
+
return detail ? `Use ${label}: ${detail}` : `Use ${label}`;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function geminiToolResultSummary(tool, ev) {
|
|
137
|
+
const fallbackSummary = geminiToolSummary(tool?.name || ev.tool_name || ev.name || ev.tool, ev.parameters || ev.args || ev.input || {});
|
|
138
|
+
const summary = tool?.summary || fallbackSummary;
|
|
139
|
+
const detail = shortValue(firstNonEmptyLine(normalizeErrorMessage(ev.error)
|
|
140
|
+
|| ev.output
|
|
141
|
+
|| ev.message
|
|
142
|
+
|| ''), 120);
|
|
143
|
+
if (ev.status === 'error')
|
|
144
|
+
return detail ? `${summary} failed: ${detail}` : `${summary} failed`;
|
|
145
|
+
return detail ? `${summary} -> ${detail}` : `${summary} done`;
|
|
146
|
+
}
|
|
52
147
|
function geminiParse(ev, s) {
|
|
53
148
|
const t = ev.type || '';
|
|
54
149
|
// init event: {"type":"init","session_id":"...","model":"..."}
|
|
@@ -58,26 +153,46 @@ function geminiParse(ev, s) {
|
|
|
58
153
|
s.contextWindow = geminiContextWindowFromModel(s.model) ?? s.contextWindow;
|
|
59
154
|
}
|
|
60
155
|
// message delta: {"type":"message","role":"assistant","content":"...","delta":true}
|
|
61
|
-
if (t === 'message' && ev.role === 'assistant'
|
|
62
|
-
|
|
156
|
+
if (t === 'message' && ev.role === 'assistant') {
|
|
157
|
+
if (ev.delta)
|
|
158
|
+
s.text += ev.content || '';
|
|
159
|
+
else if (!s.text.trim())
|
|
160
|
+
s.text = ev.content || '';
|
|
63
161
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
162
|
+
if (t === 'tool_use' || t === 'tool_call') {
|
|
163
|
+
const name = geminiToolName(ev.tool_name || ev.name || ev.tool);
|
|
164
|
+
const summary = geminiToolSummary(name, ev.parameters || ev.args || ev.input || {});
|
|
165
|
+
const toolId = String(ev.tool_id || ev.id || '').trim();
|
|
166
|
+
if (toolId)
|
|
167
|
+
s.geminiToolsById.set(toolId, { name, summary });
|
|
168
|
+
pushRecentActivity(s.recentActivity, summary);
|
|
68
169
|
s.activity = s.recentActivity.join('\n');
|
|
69
170
|
}
|
|
70
|
-
// tool_result event
|
|
71
171
|
if (t === 'tool_result') {
|
|
72
|
-
const
|
|
73
|
-
|
|
172
|
+
const toolId = String(ev.tool_id || ev.id || '').trim();
|
|
173
|
+
const tool = toolId ? s.geminiToolsById.get(toolId) : undefined;
|
|
174
|
+
pushRecentActivity(s.recentActivity, geminiToolResultSummary(tool, ev));
|
|
74
175
|
s.activity = s.recentActivity.join('\n');
|
|
75
176
|
}
|
|
177
|
+
if (t === 'error') {
|
|
178
|
+
const message = normalizeErrorMessage(ev.message || ev.error) || 'Gemini reported an error';
|
|
179
|
+
if (ev.severity === 'error') {
|
|
180
|
+
s.errors = [...(s.errors || []), message];
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
pushRecentActivity(s.recentActivity, message);
|
|
184
|
+
s.activity = s.recentActivity.join('\n');
|
|
185
|
+
}
|
|
186
|
+
}
|
|
76
187
|
// result event: {"type":"result","status":"success","stats":{...}}
|
|
77
188
|
if (t === 'result') {
|
|
78
189
|
s.sessionId = ev.session_id ?? s.sessionId;
|
|
79
190
|
if (ev.status === 'error' || ev.status === 'failure') {
|
|
80
|
-
|
|
191
|
+
const message = normalizeErrorMessage(ev.error)
|
|
192
|
+
|| normalizeErrorMessage(ev.errors)
|
|
193
|
+
|| normalizeErrorMessage(ev.message)
|
|
194
|
+
|| `Gemini returned status: ${ev.status}`;
|
|
195
|
+
s.errors = [message];
|
|
81
196
|
}
|
|
82
197
|
s.stopReason = ev.status === 'success' ? 'end_turn' : ev.status;
|
|
83
198
|
const u = ev.stats;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pikiclaw",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.55",
|
|
4
4
|
"description": "Put the world's smartest AI agents in your pocket. Command local Claude & Gemini via IM. | 让最好用的 IM 变成你电脑上的顶级 Agent 控制台",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"command": "set -ae && . ./.env && set +a && tsx src/run.ts",
|
|
32
32
|
"build:dashboard": "tsx scripts/build-dashboard.ts",
|
|
33
33
|
"build": "npm run build:dashboard && tsc",
|
|
34
|
-
"
|
|
34
|
+
"prepack": "npm run build",
|
|
35
35
|
"test": "vitest run",
|
|
36
36
|
"test:e2e": "set -ae && . ./.env && set +a && vitest run --config vitest.e2e.config.ts test/e2e/",
|
|
37
37
|
"test:watch": "vitest"
|