pikiloom 0.4.16 → 0.4.17
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 +2 -1
- package/dashboard/dist/assets/{AgentTab-B5tmLxa7.js → AgentTab-CDVhy5K1.js} +1 -1
- package/dashboard/dist/assets/{DirBrowser-CBp5nyfS.js → DirBrowser-BElI1-4D.js} +1 -1
- package/dashboard/dist/assets/{ExtensionsTab-w4pkrNas.js → ExtensionsTab-BB8ipJ77.js} +1 -1
- package/dashboard/dist/assets/{IMAccessTab-37Po5LP1.js → IMAccessTab-IZt_yXoG.js} +1 -1
- package/dashboard/dist/assets/{Modal-CBMO5UcS.js → Modal-C1EAGSL1.js} +1 -1
- package/dashboard/dist/assets/{Modals-DMlEjJUG.js → Modals-DYUV5yR9.js} +1 -1
- package/dashboard/dist/assets/{Select-BiSTkS_t.js → Select-BnsbE6Qv.js} +1 -1
- package/dashboard/dist/assets/SessionPanel-Ca_TVTT1.js +1 -0
- package/dashboard/dist/assets/{SystemTab-Brzt5wTT.js → SystemTab-Dk6k2OTt.js} +1 -1
- package/dashboard/dist/assets/{index-5Q-Q7ByM.js → index-CK-3CNRp.js} +2 -2
- package/dashboard/dist/assets/index-CnJsD381.js +23 -0
- package/dashboard/dist/assets/index-dzfjF9Js.css +1 -0
- package/dashboard/dist/assets/{shared-P-W1OYQ6.js → shared-CZVD0MJD.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dist/agent/artifacts.js +160 -0
- package/dist/agent/images.js +51 -24
- package/dist/agent/index.js +4 -2
- package/dist/agent/mcp/tools/workspace.js +4 -3
- package/dist/bot/bot.js +83 -4
- package/dist/bot/commands.js +48 -2
- package/dist/bot/menu.js +1 -0
- package/dist/bot/session-hub.js +1 -1
- package/dist/channels/dingtalk/bot.js +9 -1
- package/dist/channels/discord/bot.js +9 -1
- package/dist/channels/feishu/bot.js +8 -1
- package/dist/channels/slack/bot.js +9 -1
- package/dist/channels/telegram/bot.js +8 -1
- package/dist/channels/wecom/bot.js +9 -1
- package/dist/channels/weixin/bot.js +9 -1
- package/dist/cli/main.js +1 -0
- package/dist/dashboard/routes/sessions.js +108 -27
- package/package.json +1 -1
- package/dashboard/dist/assets/SessionPanel-BVC7kwlX.js +0 -1
- package/dashboard/dist/assets/index-Dw3ty4QY.js +0 -23
- package/dashboard/dist/assets/index-FD86DEDF.css +0 -1
|
@@ -13,7 +13,7 @@ import { BOT_SHUTDOWN_FORCE_EXIT_MS, buildSessionTaskId } from '../../bot/orches
|
|
|
13
13
|
import { shutdownAllDrivers } from '../../agent/driver.js';
|
|
14
14
|
import { expandTilde } from '../../core/platform.js';
|
|
15
15
|
import { registerProcessRuntime, requestProcessRestart, } from '../../core/process-control.js';
|
|
16
|
-
import { getStatusDataAsync, getHostDataSync, getAgentsListData, getSkillsListData, getModelsListData, getSessionsPageData, getStartData, getWorkspacesData, } from '../../bot/commands.js';
|
|
16
|
+
import { getStatusDataAsync, getHostDataSync, getAgentsListData, getSkillsListData, getModelsListData, getSessionsPageData, getSessionsDigestData, formatSessionsDigestText, getStartData, getWorkspacesData, } from '../../bot/commands.js';
|
|
17
17
|
import { SlackChannel } from './channel.js';
|
|
18
18
|
import { getActiveUserConfig } from '../../core/config/user-config.js';
|
|
19
19
|
const SHUTDOWN_EXIT_CODE = {
|
|
@@ -131,6 +131,7 @@ export class SlackBot extends Bot {
|
|
|
131
131
|
'/switch [path] - Change workdir',
|
|
132
132
|
'/workspaces [#] - Pick saved workspace',
|
|
133
133
|
'/sessions [new|#] - List/switch sessions',
|
|
134
|
+
'/digest - Recent session digest',
|
|
134
135
|
'/skills - List project skills',
|
|
135
136
|
'/stop - Stop current task',
|
|
136
137
|
'/restart - Restart pikiloom',
|
|
@@ -165,6 +166,9 @@ export class SlackBot extends Bot {
|
|
|
165
166
|
case 'sessions':
|
|
166
167
|
await this.cmdSessions(ctx, args);
|
|
167
168
|
return true;
|
|
169
|
+
case 'digest':
|
|
170
|
+
await this.cmdDigest(ctx);
|
|
171
|
+
return true;
|
|
168
172
|
case 'skills':
|
|
169
173
|
await this.cmdSkills(ctx);
|
|
170
174
|
return true;
|
|
@@ -192,6 +196,10 @@ export class SlackBot extends Bot {
|
|
|
192
196
|
lines.push('', 'Ready. Send a message to start.');
|
|
193
197
|
await ctx.reply(lines.join('\n'));
|
|
194
198
|
}
|
|
199
|
+
async cmdDigest(ctx) {
|
|
200
|
+
const data = await getSessionsDigestData(this, ctx.chatId);
|
|
201
|
+
await ctx.reply(formatSessionsDigestText(data));
|
|
202
|
+
}
|
|
195
203
|
async cmdStatus(ctx) {
|
|
196
204
|
const d = await getStatusDataAsync(this, ctx.chatId);
|
|
197
205
|
const gitLine = formatGitStatusLine(d.git);
|
|
@@ -14,7 +14,7 @@ import { BOT_SHUTDOWN_FORCE_EXIT_MS, SessionMessageRegistry, buildBotMenuState,
|
|
|
14
14
|
import { stageSessionFiles, } from '../../agent/index.js';
|
|
15
15
|
import { shutdownAllDrivers } from '../../agent/driver.js';
|
|
16
16
|
import { SKILL_CMD_PREFIX, } from '../../bot/menu.js';
|
|
17
|
-
import { getStartData, getStatusDataAsync, getHostDataSync, getSessionTurnPreviewData, getWorkspacesData, resolveSkillPrompt, summarizePromptForStatus, handleGoalCommand, } from '../../bot/commands.js';
|
|
17
|
+
import { getStartData, getStatusDataAsync, getHostDataSync, getSessionTurnPreviewData, getWorkspacesData, resolveSkillPrompt, summarizePromptForStatus, handleGoalCommand, getSessionsDigestData, formatSessionsDigestText, } from '../../bot/commands.js';
|
|
18
18
|
import { buildAgentsCommandView, buildModelsCommandView, buildModeCommandView, buildSessionsCommandView, buildSkillsCommandView, decodeCommandAction, executeCommandAction, } from '../../bot/command-ui.js';
|
|
19
19
|
import { buildSwitchWorkdirView, buildWorkspacesView, resolveRegisteredPath } from './directory.js';
|
|
20
20
|
import { LivePreview } from './live-preview.js';
|
|
@@ -353,6 +353,10 @@ export class TelegramBot extends Bot {
|
|
|
353
353
|
async cmdSessions(ctx) {
|
|
354
354
|
await this.sendCommandView(ctx, await buildSessionsCommandView(this, ctx.chatId, 0, this.sessionsPageSize));
|
|
355
355
|
}
|
|
356
|
+
async cmdDigest(ctx) {
|
|
357
|
+
const data = await getSessionsDigestData(this, ctx.chatId);
|
|
358
|
+
await ctx.reply(formatSessionsDigestText(data));
|
|
359
|
+
}
|
|
356
360
|
async cmdStatus(ctx) {
|
|
357
361
|
const d = await getStatusDataAsync(this, ctx.chatId);
|
|
358
362
|
const gitLine = formatGitStatusLine(d.git);
|
|
@@ -1167,6 +1171,9 @@ export class TelegramBot extends Bot {
|
|
|
1167
1171
|
case 'sessions':
|
|
1168
1172
|
await this.cmdSessions(ctx);
|
|
1169
1173
|
return;
|
|
1174
|
+
case 'digest':
|
|
1175
|
+
await this.cmdDigest(ctx);
|
|
1176
|
+
return;
|
|
1170
1177
|
case 'agents':
|
|
1171
1178
|
await this.cmdAgents(ctx);
|
|
1172
1179
|
return;
|
|
@@ -9,7 +9,7 @@ import { BOT_SHUTDOWN_FORCE_EXIT_MS, buildSessionTaskId } from '../../bot/orches
|
|
|
9
9
|
import { shutdownAllDrivers } from '../../agent/driver.js';
|
|
10
10
|
import { expandTilde } from '../../core/platform.js';
|
|
11
11
|
import { registerProcessRuntime, requestProcessRestart, } from '../../core/process-control.js';
|
|
12
|
-
import { getStatusDataAsync, getHostDataSync, getAgentsListData, getSkillsListData, getModelsListData, getSessionsPageData, getStartData, getWorkspacesData, } from '../../bot/commands.js';
|
|
12
|
+
import { getStatusDataAsync, getHostDataSync, getAgentsListData, getSkillsListData, getModelsListData, getSessionsPageData, getSessionsDigestData, formatSessionsDigestText, getStartData, getWorkspacesData, } from '../../bot/commands.js';
|
|
13
13
|
import { WeComChannel } from './channel.js';
|
|
14
14
|
import { getActiveUserConfig } from '../../core/config/user-config.js';
|
|
15
15
|
const SHUTDOWN_EXIT_CODE = {
|
|
@@ -135,6 +135,7 @@ export class WeComBot extends Bot {
|
|
|
135
135
|
'/switch [path] - Change workdir',
|
|
136
136
|
'/workspaces [#] - Pick saved workspace',
|
|
137
137
|
'/sessions [new|#] - List/switch sessions',
|
|
138
|
+
'/digest - Recent session digest',
|
|
138
139
|
'/skills - List project skills',
|
|
139
140
|
'/stop - Stop current task',
|
|
140
141
|
'/restart - Restart pikiloom',
|
|
@@ -168,6 +169,9 @@ export class WeComBot extends Bot {
|
|
|
168
169
|
case 'sessions':
|
|
169
170
|
await this.cmdSessions(ctx, args);
|
|
170
171
|
return true;
|
|
172
|
+
case 'digest':
|
|
173
|
+
await this.cmdDigest(ctx);
|
|
174
|
+
return true;
|
|
171
175
|
case 'skills':
|
|
172
176
|
await this.cmdSkills(ctx);
|
|
173
177
|
return true;
|
|
@@ -195,6 +199,10 @@ export class WeComBot extends Bot {
|
|
|
195
199
|
lines.push('', 'Ready. Send a message to start.');
|
|
196
200
|
await ctx.reply(lines.join('\n'));
|
|
197
201
|
}
|
|
202
|
+
async cmdDigest(ctx) {
|
|
203
|
+
const data = await getSessionsDigestData(this, ctx.chatId);
|
|
204
|
+
await ctx.reply(formatSessionsDigestText(data));
|
|
205
|
+
}
|
|
198
206
|
async cmdStatus(ctx) {
|
|
199
207
|
const d = await getStatusDataAsync(this, ctx.chatId);
|
|
200
208
|
const gitLine = formatGitStatusLine(d.git);
|
|
@@ -11,7 +11,7 @@ import { BOT_SHUTDOWN_FORCE_EXIT_MS, buildSessionTaskId } from '../../bot/orches
|
|
|
11
11
|
import { shutdownAllDrivers } from '../../agent/driver.js';
|
|
12
12
|
import { expandTilde } from '../../core/platform.js';
|
|
13
13
|
import { registerProcessRuntime, requestProcessRestart, } from '../../core/process-control.js';
|
|
14
|
-
import { getStatusDataAsync, getHostDataSync, getModelsListData, getSessionsPageData, getStartData, getWorkspacesData, handleGoalCommand, } from '../../bot/commands.js';
|
|
14
|
+
import { getStatusDataAsync, getHostDataSync, getModelsListData, getSessionsPageData, getSessionsDigestData, formatSessionsDigestText, getStartData, getWorkspacesData, handleGoalCommand, } from '../../bot/commands.js';
|
|
15
15
|
import { WeixinChannel } from './channel.js';
|
|
16
16
|
import { getActiveUserConfig } from '../../core/config/user-config.js';
|
|
17
17
|
const SHUTDOWN_EXIT_CODE = {
|
|
@@ -175,6 +175,7 @@ export class WeixinBot extends Bot {
|
|
|
175
175
|
'/switch [path] - Change workdir',
|
|
176
176
|
'/workspaces [#] - Pick saved workspace',
|
|
177
177
|
'/sessions [new|#] - List/switch sessions',
|
|
178
|
+
'/digest - Recent session digest',
|
|
178
179
|
'/skills - List & run project skills',
|
|
179
180
|
'/cancel - Cancel an active interactive prompt',
|
|
180
181
|
'/stop - Stop current task',
|
|
@@ -210,6 +211,9 @@ export class WeixinBot extends Bot {
|
|
|
210
211
|
case 'sessions':
|
|
211
212
|
await this.cmdSessions(ctx, args);
|
|
212
213
|
return true;
|
|
214
|
+
case 'digest':
|
|
215
|
+
await this.cmdDigest(ctx);
|
|
216
|
+
return true;
|
|
213
217
|
case 'skills':
|
|
214
218
|
await this.cmdSkills(ctx);
|
|
215
219
|
return true;
|
|
@@ -241,6 +245,10 @@ export class WeixinBot extends Bot {
|
|
|
241
245
|
lines.push('', 'Ready. Send a message to start.');
|
|
242
246
|
await ctx.reply(lines.join('\n'));
|
|
243
247
|
}
|
|
248
|
+
async cmdDigest(ctx) {
|
|
249
|
+
const data = await getSessionsDigestData(this, ctx.chatId);
|
|
250
|
+
await ctx.reply(formatSessionsDigestText(data));
|
|
251
|
+
}
|
|
244
252
|
async cmdStatus(ctx) {
|
|
245
253
|
const d = await getStatusDataAsync(this, ctx.chatId);
|
|
246
254
|
const gitLine = formatGitStatusLine(d.git);
|
package/dist/cli/main.js
CHANGED
|
@@ -292,6 +292,7 @@ Environment variables (per agent):
|
|
|
292
292
|
|
|
293
293
|
Bot commands (available once running):
|
|
294
294
|
/sessions List or switch coding sessions
|
|
295
|
+
/digest Show a compact digest of recent sessions
|
|
295
296
|
/agents List or switch AI agents
|
|
296
297
|
/models List or switch models
|
|
297
298
|
/status Bot status, uptime, and token usage
|
|
@@ -5,8 +5,9 @@ import { Hono } from 'hono';
|
|
|
5
5
|
import fs from 'node:fs';
|
|
6
6
|
import os from 'node:os';
|
|
7
7
|
import path from 'node:path';
|
|
8
|
+
import { Readable } from 'node:stream';
|
|
8
9
|
import { loadUserConfig } from '../../core/config/user-config.js';
|
|
9
|
-
import { listAgents, listSkills, decodeAttachmentPathParam, resolveAllowedAttachmentPath,
|
|
10
|
+
import { listAgents, listSkills, decodeAttachmentPathParam, resolveAllowedAttachmentPath, rewriteAttachmentBlocksForTransport, deliveredArtifactBlocks, mimeForArtifact, } from '../../agent/index.js';
|
|
10
11
|
import { getSessionStatusForBot } from '../../bot/session-status.js';
|
|
11
12
|
import { findPikiloomSession } from '../../agent/session.js';
|
|
12
13
|
import { readAwaitResume } from '../../agent/await-resume.js';
|
|
@@ -425,26 +426,56 @@ app.post('/api/session-hub/session/messages', async (c) => {
|
|
|
425
426
|
turnLimit: Number.isFinite(turnLimit) ? turnLimit : undefined,
|
|
426
427
|
rich,
|
|
427
428
|
});
|
|
428
|
-
return c.json(
|
|
429
|
+
return c.json(prepareSessionMessagesForDashboard(result, agent, sessionId));
|
|
429
430
|
}
|
|
430
431
|
catch (e) {
|
|
431
432
|
return c.json({ ok: false, error: e.message }, 500);
|
|
432
433
|
}
|
|
433
434
|
});
|
|
434
|
-
//
|
|
435
|
-
//
|
|
436
|
-
|
|
437
|
-
|
|
435
|
+
// Prepare a session message read for the dashboard:
|
|
436
|
+
// 1. Rewrite on-disk image/file blocks into compact attachment HTTP URLs so a
|
|
437
|
+
// remote browser can fetch the bytes (inline data: images pass through).
|
|
438
|
+
// 2. Append the session's delivered artifacts (files the agent handed the
|
|
439
|
+
// user via `im_send_file`) as a trailing assistant message, so they render
|
|
440
|
+
// and stay retrievable after a reload regardless of which terminal
|
|
441
|
+
// delivered them. Only added when this window includes the conversation
|
|
442
|
+
// tail (`!hasNewer`) to avoid duplicating across paginated reads.
|
|
443
|
+
function prepareSessionMessagesForDashboard(result, agent, sessionId) {
|
|
444
|
+
// Only operate in rich mode — a `rich:false` read returns plain text and must
|
|
445
|
+
// not gain a synthetic richMessages array.
|
|
446
|
+
if (result.richMessages === undefined)
|
|
438
447
|
return result;
|
|
439
448
|
const richMessages = result.richMessages.map(message => ({
|
|
440
449
|
...message,
|
|
441
|
-
blocks:
|
|
450
|
+
blocks: rewriteAttachmentBlocksForTransport(message.blocks, { agent, sessionId }),
|
|
442
451
|
}));
|
|
452
|
+
const includesTail = !result.window || !result.window.hasNewer;
|
|
453
|
+
if (includesTail) {
|
|
454
|
+
const delivered = rewriteAttachmentBlocksForTransport(deliveredArtifactBlocks(agent, sessionId), { agent, sessionId });
|
|
455
|
+
if (delivered.length) {
|
|
456
|
+
const text = deliveredSummaryText(delivered);
|
|
457
|
+
richMessages.push({ role: 'assistant', text, blocks: delivered });
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
if (!richMessages.length)
|
|
461
|
+
return result;
|
|
443
462
|
return { ...result, richMessages };
|
|
444
463
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
464
|
+
/** Plain-text fallback for the delivered-artifacts message (IM tail / exports). */
|
|
465
|
+
function deliveredSummaryText(blocks) {
|
|
466
|
+
const names = blocks
|
|
467
|
+
.map(b => b.fileName || (b.type === 'image' ? 'image' : 'file'))
|
|
468
|
+
.filter(Boolean);
|
|
469
|
+
if (!names.length)
|
|
470
|
+
return '';
|
|
471
|
+
return names.length === 1 ? `Delivered: ${names[0]}` : `Delivered ${names.length} files: ${names.join(', ')}`;
|
|
472
|
+
}
|
|
473
|
+
// Attachment endpoint — serves on-disk images AND delivered files referenced by
|
|
474
|
+
// RichMessage image/file blocks via opaque base64url path tokens. The allowlist
|
|
475
|
+
// (see images.ts) confines reads to a known set of agent-managed dirs + the
|
|
476
|
+
// session's workdir + the per-session delivered-artifacts dir. Streams the
|
|
477
|
+
// bytes (no full-buffer) and supports Range so large artifacts (video,
|
|
478
|
+
// archives) download/seek without pinning memory.
|
|
448
479
|
app.get('/api/sessions/:agent/:id/attachment', async (c) => {
|
|
449
480
|
const agent = c.req.param('agent');
|
|
450
481
|
const sessionId = decodeURIComponent(c.req.param('id'));
|
|
@@ -491,28 +522,78 @@ app.get('/api/sessions/:agent/:id/attachment', async (c) => {
|
|
|
491
522
|
}
|
|
492
523
|
if (!stat.isFile())
|
|
493
524
|
return c.json({ ok: false, error: 'not a file' }, 400);
|
|
494
|
-
const
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
//
|
|
498
|
-
|
|
499
|
-
|
|
525
|
+
const mime = mimeForArtifact(resolved);
|
|
526
|
+
const downloadName = sanitizeDownloadName(c.req.query('n'), resolved);
|
|
527
|
+
// Inline images so <img> renders them; everything else downloads with its
|
|
528
|
+
// pristine name. RFC 5987 filename* carries non-ASCII names safely.
|
|
529
|
+
const disposition = mime.startsWith('image/') ? 'inline' : 'attachment';
|
|
530
|
+
const asciiName = downloadName.replace(/[^\x20-\x7e]/g, '_').replace(/"/g, "'");
|
|
531
|
+
const contentDisposition = `${disposition}; filename="${asciiName}"; filename*=UTF-8''${encodeURIComponent(downloadName)}`;
|
|
532
|
+
// The path is stamp-unique for delivered artifacts and hash-immutable for
|
|
533
|
+
// agent-managed dirs (`ig_<sha>.png`, …); the session lifecycle keeps the
|
|
534
|
+
// file stable — long cache is safe.
|
|
535
|
+
const baseHeaders = {
|
|
500
536
|
'Content-Type': mime,
|
|
501
|
-
'Content-
|
|
537
|
+
'Content-Disposition': contentDisposition,
|
|
502
538
|
'Cache-Control': 'private, max-age=31536000, immutable',
|
|
503
539
|
'X-Content-Type-Options': 'nosniff',
|
|
540
|
+
'Accept-Ranges': 'bytes',
|
|
541
|
+
};
|
|
542
|
+
// Honor a single byte-range request (e.g. video seek / resumable download).
|
|
543
|
+
const range = parseByteRange(c.req.header('range'), stat.size);
|
|
544
|
+
if (range) {
|
|
545
|
+
const nodeStream = fs.createReadStream(resolved, { start: range.start, end: range.end });
|
|
546
|
+
return c.body(Readable.toWeb(nodeStream), 206, {
|
|
547
|
+
...baseHeaders,
|
|
548
|
+
'Content-Range': `bytes ${range.start}-${range.end}/${stat.size}`,
|
|
549
|
+
'Content-Length': String(range.end - range.start + 1),
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
const nodeStream = fs.createReadStream(resolved);
|
|
553
|
+
return c.body(Readable.toWeb(nodeStream), 200, {
|
|
554
|
+
...baseHeaders,
|
|
555
|
+
'Content-Length': String(stat.size),
|
|
504
556
|
});
|
|
505
557
|
});
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
558
|
+
/** Sanitize the optional `&n=` download-name hint; fall back to the basename. */
|
|
559
|
+
function sanitizeDownloadName(raw, resolved) {
|
|
560
|
+
const candidate = (raw || '').trim();
|
|
561
|
+
const name = candidate || path.basename(resolved);
|
|
562
|
+
// Strip path separators / control chars; the on-disk path is already
|
|
563
|
+
// validated — `n` only affects the Content-Disposition filename.
|
|
564
|
+
return name.replace(/[/\\\0\r\n]+/g, '_').replace(/^\.+/, '').slice(0, 200) || 'download';
|
|
565
|
+
}
|
|
566
|
+
/** Parse a single `bytes=start-end` range header against `size`; null if absent
|
|
567
|
+
* or unsatisfiable (caller then serves the full body). */
|
|
568
|
+
function parseByteRange(header, size) {
|
|
569
|
+
if (!header || size <= 0)
|
|
570
|
+
return null;
|
|
571
|
+
const m = /^bytes=(\d*)-(\d*)$/.exec(header.trim());
|
|
572
|
+
if (!m)
|
|
573
|
+
return null;
|
|
574
|
+
const hasStart = m[1] !== '';
|
|
575
|
+
const hasEnd = m[2] !== '';
|
|
576
|
+
let start;
|
|
577
|
+
let end;
|
|
578
|
+
if (hasStart) {
|
|
579
|
+
start = parseInt(m[1], 10);
|
|
580
|
+
end = hasEnd ? parseInt(m[2], 10) : size - 1;
|
|
581
|
+
}
|
|
582
|
+
else if (hasEnd) {
|
|
583
|
+
// Suffix range: last N bytes.
|
|
584
|
+
const suffix = parseInt(m[2], 10);
|
|
585
|
+
if (suffix <= 0)
|
|
586
|
+
return null;
|
|
587
|
+
start = Math.max(0, size - suffix);
|
|
588
|
+
end = size - 1;
|
|
589
|
+
}
|
|
590
|
+
else {
|
|
591
|
+
return null;
|
|
592
|
+
}
|
|
593
|
+
end = Math.min(end, size - 1);
|
|
594
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || start > end || start < 0)
|
|
595
|
+
return null;
|
|
596
|
+
return { start, end };
|
|
516
597
|
}
|
|
517
598
|
app.post('/api/session-hub/migrate', async (c) => {
|
|
518
599
|
try {
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as t,j as n}from"./react-vendor-C7Sl8SE7.js";import{c as ht,I as pt,S as ke,k as Se,a as j,u as We,d as xt,g as kt,j as St,f as bt,s as vt,M as yt}from"./index-5Q-Q7ByM.js";import{l as It,n as Xe,m as Tt,a as jt,b as Rt,p as wt,u as Nt,c as Ot,T as Lt,R as Mt,U as Ve,d as Ct,e as Ge,f as Et,L as At,I as Pt}from"./index-Dw3ty4QY.js";import{M as Je,a as Ze}from"./Modal-CBMO5UcS.js";import"./router-DHISdpPk.js";import"./Select-BiSTkS_t.js";import"./DirBrowser-CBp5nyfS.js";import"./markdown-DxQYQFeH.js";import"./ExtensionsTab-w4pkrNas.js";function Ft({snapshot:f}){const[r,h]=t.useState(f.currentIndex??0),[C,R]=t.useState(""),[x,p]=t.useState(!1),[W,k]=t.useState(null);t.useEffect(()=>{h(f.currentIndex??0),R(""),k(null)},[f.promptId,f.currentIndex]);const w=f.questions||[],v=w[r]||null,X=w.length,N=!!(v?.options&&v.options.length),d=N?!!v?.allowFreeform:!0,F=o=>{o&&(h(g=>g+1),R(""))},ne=async o=>{if(!x){p(!0),k(null);try{const g=await j.interactionSelectOption(f.promptId,o);if(!g.ok){k(g.error||"Failed to submit selection.");return}F(g.advanced)}catch(g){k(g?.message||"Network error.")}finally{p(!1)}}},O=async()=>{if(x)return;const o=C.trim();if(!o&&!v?.allowEmpty){k("Please enter a response.");return}p(!0),k(null);try{const g=await j.interactionSubmitText(f.promptId,o);if(!g.ok){k(g.error||"Failed to submit answer.");return}F(g.advanced)}catch(g){k(g?.message||"Network error.")}finally{p(!1)}},m=async()=>{if(!x){p(!0),k(null);try{const o=await j.interactionSkip(f.promptId);if(!o.ok){k(o.error||"Failed to skip.");return}F(o.advanced)}catch(o){k(o?.message||"Network error.")}finally{p(!1)}}},H=async()=>{if(!x){p(!0);try{await j.interactionCancel(f.promptId)}catch{}}},V=t.useMemo(()=>{const o=[];return f.hint&&o.push(f.hint),X>1&&o.push(`Question ${r+1} of ${X}`),o.join(" · ")||void 0},[f.hint,r,X]);return n.jsxs(Je,{open:!0,onClose:H,wide:N&&(v?.options?.length||0)>3,children:[n.jsx(Ze,{title:f.title||"Pikiloom needs your input",description:V,onClose:H}),v?n.jsxs("div",{className:"space-y-4",children:[n.jsxs("div",{children:[n.jsx("div",{className:"text-xs font-medium uppercase tracking-wide text-fg-5",children:v.header||"Question"}),n.jsx("div",{className:"mt-1 whitespace-pre-wrap text-sm leading-relaxed text-fg",children:v.prompt})]}),N&&n.jsx("div",{className:"grid grid-cols-1 gap-2 sm:grid-cols-2",children:(v.options||[]).map(o=>n.jsxs("button",{type:"button",disabled:x,onClick:()=>ne(o.value||o.label),className:ht("group rounded-lg border border-edge bg-panel-alt px-3 py-2 text-left text-sm transition","hover:border-control-border-h hover:bg-control-h hover:shadow-sm","focus:outline-none focus:ring-2 focus:ring-[var(--th-glow-a)]","disabled:cursor-not-allowed disabled:opacity-50"),children:[n.jsx("div",{className:"font-medium text-fg group-hover:text-fg",children:o.label}),o.description&&n.jsx("div",{className:"mt-0.5 text-xs leading-snug text-fg-4",children:o.description})]},o.value||o.label))}),d&&n.jsx("div",{children:n.jsx(pt,{value:C,onChange:o=>R(o.target.value),onKeyDown:o=>{o.key==="Enter"&&!o.shiftKey&&!x&&(o.preventDefault(),O())},placeholder:N?"Or type a custom answer…":"Type your answer…",disabled:x,autoFocus:!N})}),W&&n.jsx("div",{className:"rounded-md border border-red-300/40 bg-red-500/10 px-3 py-2 text-xs text-red-600",children:W}),n.jsxs("div",{className:"flex items-center justify-between gap-3",children:[n.jsx("div",{className:"text-xs text-fg-5",children:x?n.jsxs("span",{className:"inline-flex items-center gap-2",children:[n.jsx(ke,{})," Submitting…"]}):n.jsxs("span",{children:["Press ",n.jsx("kbd",{className:"rounded border border-edge bg-panel-alt px-1.5 py-0.5 text-[10px] uppercase",children:"Enter"})," to send"]})}),n.jsxs("div",{className:"flex items-center gap-2",children:[n.jsx(Se,{variant:"ghost",size:"sm",onClick:m,disabled:x,children:"Skip"}),d&&n.jsx(Se,{variant:"primary",size:"sm",onClick:O,disabled:x||!C.trim()&&!v.allowEmpty,children:"Submit"})]})]})]}):n.jsxs("div",{className:"py-6 text-center text-sm text-fg-5",children:[n.jsx(ke,{className:"mr-2 inline-block"})," Waiting for the agent…"]})]})}const Pe=12,Ye=160,Ht=96,qt=20,z=new Map;function Dt(f,r){return`${f}:${r}`}function Ut(f,r){for(z.delete(f),z.set(f,r);z.size>qt;)z.delete(z.keys().next().value)}const Gt=t.memo(function({session:r,workdir:h,active:C=!0,onSessionChange:R,initialPendingPrompt:x,initialPendingImageUrls:p,onPendingPromptConsumed:W}){const k=We(e=>e.locale),w=We(e=>e.agentStatus?.agents?.find(s=>s.agent===r.agent)??null),v=w?.selectedEffort??null,X=w?.selectedModel??null,N=w?.byokProviderName??null,d=t.useMemo(()=>xt(k),[k]),F=kt(r.agent||""),ne=St(r),O=!!x||!!(p&&p.length),[m,H]=t.useState(null),[V,o]=t.useState(!O),[g,Fe]=t.useState(!1),[i,q]=t.useState(null),[G,Y]=t.useState(!1),[se,be]=t.useState(null),[et,He]=t.useState(0),[tt,ve]=t.useState(null),[le,ae]=t.useState([]),[rt,oe]=t.useState([]),[J,ye]=t.useState([]),[S,ue]=t.useState(x||null),[L,ce]=t.useState(p||[]),[nt,ie]=t.useState(null),Ie=t.useRef(null);Ie.current=nt;const[qe,D]=t.useState([]),De=t.useRef([]);De.current=qe;const Z=t.useRef(null),[st,Ue]=t.useState(null),[de,ee]=t.useState(null),[te,Te]=t.useState(""),[E,je]=t.useState(!1),lt=!!w?.capabilities?.fork,Re=t.useRef(null),U=t.useRef(p||[]),M=t.useRef(i),$e=t.useRef(G);M.current=i,$e.current=G;const A=t.useRef(null),fe=t.useRef(null),$=t.useRef(!0),B=t.useRef(!1),me=t.useRef(null),we=t.useRef(!1),Q=t.useRef(O),re=t.useRef(!1),P=t.useRef(!1),Ne=t.useRef(!1),Oe=t.useRef(!1),Be=t.useRef({model:null,effort:null}),at=t.useCallback(e=>{Be.current=e},[]);t.useEffect(()=>{Ne.current||!O||(Ne.current=!0,x&&!S&&ue(x),p&&p.length&&!L.length&&(ce(p),U.current=p),o(!1),He(e=>e+1),W?.())},[O,W]);const I=t.useCallback(()=>{ue(null),ce(e=>{for(const s of e)URL.revokeObjectURL(s);return[]}),U.current=[],ie(null)},[]),_=t.useCallback(()=>{D(e=>{if(!e.length)return e;for(const s of e)for(const l of s.imageUrls)URL.revokeObjectURL(l);return[]}),Z.current=null},[]),Qe=t.useCallback((e,s)=>{const l=!!M.current||$e.current,a=s||[];if(l){const u=`local-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`;Z.current=u,D(c=>[...c,{localId:u,taskId:null,prompt:e||"",imageUrls:a}]);return}for(const u of U.current)URL.revokeObjectURL(u);Z.current=null,ue(e||null),ce(a),U.current=a,ie(null)},[]),ot=t.useCallback(e=>{const s=Z.current;if(s){Z.current=null,D(l=>{const a=l.findIndex(c=>c.localId===s);if(a<0)return l;const u=l.slice();return u[a]={...u[a],taskId:e},u});return}ie(e)},[]),ut=t.useCallback(async()=>{if(!de)return;const e=te.trim();if(e){je(!0);try{const s=await j.forkSession(h,r.agent||"",r.sessionId,de.atTurn,e,{});if(!s.ok||!s.sessionKey){je(!1);return}const[l,a]=s.sessionKey.split(":");ee(null),Te(""),R?.({agent:l,sessionId:a,workdir:h})}finally{je(!1)}}},[de,te,h,r.agent,r.sessionId,R]);Re.current=ut;const ge=t.useCallback(async(e,s={})=>{try{const l=await It({workdir:h,agent:r.agent||"",sessionId:r.sessionId,rich:!0,turnOffset:e.turnOffset,turnLimit:e.turnLimit,lastNTurns:e.lastNTurns},{force:s.force});return l.ok?Xe(l):null}catch{return null}},[h,r.agent,r.sessionId]),T=t.useCallback(async({keepOlder:e,force:s=!1,scrollToBottom:l=!1})=>{const a=r.sessionId;if(me.current===a)return!1;me.current=a;try{const u=await ge({turnOffset:0,turnLimit:Pe},{force:s});if(!u||r.sessionId!==a)return!1;if(l&&(B.current=!0),H(c=>!c||!e?u:Tt(c,u)),o(!1),re.current&&(re.current=!1,I()),P.current){const c=P.current;P.current=!1;const b=c!==!0?c.taskId:null;M.current&&(c===!0||M.current.taskId===b)&&q(null)}return!0}finally{me.current===a&&(me.current=null)}},[ge,I,r.sessionId]),he=t.useCallback(async()=>{if(!m?.hasOlder||we.current)return;const e=A.current;e&&(fe.current={scrollHeight:e.scrollHeight,scrollTop:e.scrollTop}),we.current=!0,Fe(!0);try{const s=await ge({turnOffset:Math.max(0,m.totalTurns-m.startTurn),turnLimit:Pe});s?H(l=>l?jt(l,s):s):fe.current=null}finally{we.current=!1,Fe(!1)}},[ge,m]),pe=t.useRef(null),K=t.useCallback(e=>{if(e?.sessionId&&e.sessionId!==r.sessionId&&(Oe.current=!0,Le.current=`${r.agent}:${e.sessionId}`,R?.({agent:r.agent||"",sessionId:e.sessionId,workdir:h})),!e){const l=pe.current;Y(!1),l==="streaming"?(re.current=!0,P.current=!0,T({keepOlder:!0,force:!0,scrollToBottom:$.current})):q(null),l==="done"?(I(),_()):l===null&&Q.current&&T({keepOlder:!0,force:!0}),l!==null&&(Q.current=!1),ve(null),be(null),ae([]),oe([]),ye([]),pe.current=null;return}if(be(e.phase),ve(e.taskId||null),ae(e.queuedTaskIds&&e.queuedTaskIds.length?e.queuedTaskIds:[]),oe(e.queuedTasks&&e.queuedTasks.length?e.queuedTasks:[]),ye(Array.isArray(e.interactions)&&e.interactions.length?e.interactions:[]),e.phase==="streaming"){if(P.current&&M.current&&M.current.taskId!==null&&M.current.taskId!==(e.taskId||null)&&!(e.text||"").trim()||q({taskId:e.taskId||null,phase:"streaming",text:e.text||"",thinking:e.thinking||"",activity:e.activity,plan:e.plan??null,model:e.model??null,effort:e.effort??null,previewMeta:e.previewMeta??null,subAgents:e.previewMeta?.subAgents??null,generatingImages:e.previewMeta?.generatingImages??0,startedAt:typeof e.startedAt=="number"?e.startedAt:null,error:null,question:e.question??null}),Y(!0),e.taskId&&e.taskId!==Ie.current){const a=De.current,u=a.findIndex(c=>c.taskId===e.taskId);if(u>=0){const c=a[u];for(const b of U.current)URL.revokeObjectURL(b);ue(c.prompt||null),ce(c.imageUrls),U.current=c.imageUrls,ie(e.taskId),D(b=>b.filter((mt,gt)=>gt!==u))}}$.current&&(B.current=!0)}else if(e.phase==="queued")q(null),Y(!1);else if(e.phase==="done"){Y(!1),q(b=>b?{...b,phase:"done",error:e.error??null}:e.error?{taskId:e.taskId||null,phase:"done",text:"",thinking:"",activity:"",plan:null,model:e.model??null,effort:e.effort??null,previewMeta:e.previewMeta??null,subAgents:e.previewMeta?.subAgents??null,generatingImages:e.previewMeta?.generatingImages??0,error:e.error}:b);const l=!!e.queuedTaskIds?.length,a=M.current,u=!!a&&Rt(a),c=!!e.incomplete&&u&&!l;pe.current!=="done"&&(l||(re.current=!0),P.current=c?!1:{taskId:e.taskId||null},T({keepOlder:!0,force:!0,scrollToBottom:$.current})),l||(Q.current=!1)}const s=new Set;if(e.taskId&&s.add(e.taskId),Array.isArray(e.queuedTaskIds))for(const l of e.queuedTaskIds)s.add(l);D(l=>{let a=!1;const u=[];for(const c of l)if(!c.taskId||s.has(c.taskId))u.push(c);else{for(const b of c.imageUrls)URL.revokeObjectURL(b);a=!0}return a?u:l}),pe.current=e.phase},[I,_,T,r.sessionId,r.agent,R,h]),_e=t.useCallback(()=>{Q.current=!0,He(e=>e+1)},[]),ct=t.useCallback(async e=>{try{await j.recallSessionMessage(e),Ie.current===e&&I(),D(s=>{let l=!1;const a=[];for(const u of s)if(u.taskId===e){for(const c of u.imageUrls)URL.revokeObjectURL(c);l=!0}else a.push(u);return l?a:s}),ae(s=>s.filter(l=>l!==e)),oe(s=>s.filter(l=>l.taskId!==e)),ve(s=>s===e?null:s)}catch{}},[I]),it=t.useCallback(async e=>{try{await j.steerSession(e)}catch{}},[]),dt=t.useCallback(async()=>{try{await j.stopSession(r.agent||"",r.sessionId)}catch{}},[r.agent,r.sessionId]),xe=Dt(r.agent||"",r.sessionId);t.useEffect(()=>{if(Oe.current){Oe.current=!1;let u=!1;return T({keepOlder:!0,force:!0}).finally(()=>{u||o(!1)}),()=>{u=!0}}let e=!1;const s=wt({workdir:h,agent:r.agent||"",sessionId:r.sessionId,rich:!0,turnOffset:0,turnLimit:Pe},{allowStale:!0}),l=O&&!Ne.current,a=s?.ok?Xe(s):z.get(xe)||null;return o(l?!1:!a),H(a),q(null),Y(!1),be(null),ae([]),oe([]),ye([]),l||(I(),_(),Q.current=!1,re.current=!1,P.current=!1),$.current=!0,B.current=!0,l||T({keepOlder:!1,force:!0}).finally(()=>{e||o(!1)}),()=>{e=!0}},[T,r.agent,r.sessionId,h,xe,I,_]),t.useEffect(()=>{m&&m.turns.length>0&&Ut(xe,m)},[xe,m]),t.useEffect(()=>{C&&T({keepOlder:!0,force:!0})},[C,T]);const Le=t.useRef(`${r.agent}:${r.sessionId}`);Le.current=`${r.agent}:${r.sessionId}`,Nt("stream-update",t.useCallback(e=>{e.key===Le.current&&K(e.snapshot??null)},[K])),t.useEffect(()=>{let e=!0;return j.getSessionStreamState(r.agent||"",r.sessionId).then(s=>{e&&K(s.state)}).catch(()=>{}),()=>{e=!1}},[K,r.agent,r.sessionId,et]),Ot(t.useCallback(()=>{j.getSessionStreamState(r.agent||"",r.sessionId).then(e=>{K(e.state)}).catch(()=>{}),T({keepOlder:!0,force:!0})},[K,r.agent,r.sessionId,T])),t.useEffect(()=>{!Q.current&&ne!=="running"&&!G&&!i&&!se&&le.length===0&&(I(),_())},[ne,G,i,se,le.length,I,_]),t.useLayoutEffect(()=>{const e=fe.current,s=A.current;!e||!s||(fe.current=null,s.scrollTop=e.scrollTop+(s.scrollHeight-e.scrollHeight))},[m?.turns.length]),t.useLayoutEffect(()=>{if(!B.current)return;const e=A.current;e&&(B.current=!1,e.scrollTop=e.scrollHeight,requestAnimationFrame(()=>{$.current&&(e.scrollTop=e.scrollHeight)}))},[m,i]),t.useLayoutEffect(()=>{if(!S)return;const e=A.current;e&&(e.scrollTop=e.scrollHeight)},[S]),t.useEffect(()=>{if(!m?.hasOlder||V||g)return;const e=A.current;e&&e.scrollHeight<=e.clientHeight+Ye&&he()},[m?.hasOlder,m?.turns.length,he,V,g]);const ft=t.useCallback(()=>{const e=A.current;if(!e)return;const s=e.scrollHeight-e.scrollTop-e.clientHeight;$.current=s<=Ht,e.scrollTop<=Ye&&he()},[he]),Me=i?.model||r.model||X||null,Ce=bt(r.agent||"",i?.effort||r.thinkingEffort||v||null,r.workflowEnabled??w?.workflowEnabled)||null,Ke=Me?vt(Me):null,Ee=yt(r,{streaming:G,hasLiveStream:!!i,streamPhase:se,queuedTaskCount:le.length}),y=m?.turns||[],Ae=t.useMemo(()=>{if(!L.length||!y.length)return!1;const e=y[y.length-1];return!e.user||(e.user.text?.trim()||"")!==(S||"").trim()?!1:e.user.blocks.filter(l=>l.type==="image").length<L.length},[y,S,L.length]),ze=t.useMemo(()=>{let e=y;if(Ae){const b=e[e.length-1];e=[...e.slice(0,-1),{...b,user:null}]}if(!i||!e.length)return e;const s=e[e.length-1];if(!s.assistant)return e;const l=S??(i.question||null),a=(i.text||"").trim(),u=s.assistant.text?.trim()||"";return(l!=null?s.user?.text?.trim()===l.trim():!!u&&!!a&&(a.startsWith(u)||u.startsWith(a)))?[...e.slice(0,-1),{...s,assistant:null}]:e},[y,i,S,Ae]);return n.jsxs("div",{className:"flex flex-col h-full overflow-hidden",children:[n.jsx("div",{ref:A,onScroll:ft,className:"flex-1 overflow-y-auto overscroll-contain",children:V&&!S&&!L.length&&!i?n.jsx("div",{className:"flex items-center justify-center py-20",children:n.jsx(ke,{className:"h-5 w-5 text-fg-4"})}):ze.length===0&&!S&&!L.length&&!i&&!Ee?n.jsx("div",{className:"py-20 text-center text-[13px] text-fg-5",children:d("hub.noMessages")}):n.jsxs("div",{className:"max-w-[900px] mx-auto px-6 py-6 space-y-0",children:[(m?.hasOlder||g)&&n.jsxs("div",{className:"mb-4 flex items-center justify-center gap-2 text-[11px] text-fg-5",children:[g?n.jsx(ke,{className:"h-3 w-3 text-fg-5"}):n.jsx("span",{className:"h-1.5 w-1.5 rounded-full bg-fg-5/35"}),n.jsx("span",{children:d(g?"hub.loadingOlderTurns":"hub.loadOlderTurnsHint")})]}),r.migratedFrom?.kind==="fork"&&r.migratedFrom.sessionId&&n.jsxs("button",{type:"button",onClick:()=>R?.({agent:r.migratedFrom.agent||r.agent||"",sessionId:r.migratedFrom.sessionId,workdir:h}),className:"mb-4 inline-flex items-center gap-1.5 rounded-md border border-edge bg-panel-alt px-2.5 py-1 text-[11px] text-fg-5 transition hover:border-edge-h hover:text-fg-2",title:`#${r.migratedFrom.sessionId.slice(0,8)}`,children:[n.jsxs("svg",{width:"10",height:"10",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[n.jsx("circle",{cx:"6",cy:"6",r:"2"}),n.jsx("circle",{cx:"18",cy:"6",r:"2"}),n.jsx("circle",{cx:"12",cy:"20",r:"2"}),n.jsx("path",{d:"M6 8v3a3 3 0 0 0 3 3h6a3 3 0 0 0 3-3V8"}),n.jsx("path",{d:"M12 14v4"})]}),n.jsx("span",{children:d("hub.forkBadge")}),n.jsxs("span",{className:"font-mono",children:["#",r.migratedFrom.sessionId.slice(0,8)]}),typeof r.migratedFrom.forkedAtTurn=="number"&&n.jsxs("span",{className:"text-fg-5/70",children:["· ",d("hub.forkBadgeAt").replace("{turn}",String(r.migratedFrom.forkedAtTurn+1))]})]}),ze.map((e,s)=>{const l=(m?.startTurn||0)+s;return n.jsx(Lt,{turn:e,turnIndex:l,agent:r.agent||"",meta:F,model:Ke,effort:Ce,providerName:N,t:d,workdir:h,onResend:a=>{B.current=!0,Qe(a);const u=Be.current;j.sendSessionMessage(h,r.agent||"",r.sessionId,a,{model:u.model||Me||void 0,effort:u.effort||Ce||void 0}).then(c=>{c.ok&&_e()}).catch(()=>{I()})},onEdit:a=>Ue(a),onFork:lt?a=>{Te(""),ee({atTurn:a})}:void 0},`${m?.startTurn||0}:${s}`)}),Ee&&n.jsx("div",{className:"mb-5 animate-in",children:n.jsx(Mt,{detail:Ee,t:d})}),(S||L.length>0)&&(Ae||!(S&&y.length>0&&y[y.length-1]?.user?.text?.trim()===S.trim()))&&n.jsxs("div",{className:"session-turn",children:[n.jsx(Ve,{text:S||"",blocks:L.map(e=>({type:"image",content:e})),t:d}),!i&&n.jsx("div",{className:"mt-3 mb-5 animate-in",children:n.jsx(Ct,{className:"text-fg-5"})})]}),i&&Ge(i)&&!S&&i.question&&!(y.length>0&&y[y.length-1]?.user?.text?.trim()===i.question.trim())&&n.jsx("div",{className:"session-turn",children:n.jsx(Ve,{text:i.question,t:d})}),i&&Ge(i)&&n.jsxs("div",{className:"mb-6",children:[n.jsx(Et,{agent:r.agent||"",meta:F,model:Ke,effort:Ce,providerName:N,previewMeta:i.previewMeta,liveStartedAt:i.phase==="streaming"?i.startedAt??null:null}),n.jsx(At,{stream:i,t:d,workdir:h})]}),n.jsx("div",{className:"h-4"})]})}),n.jsx(Pt,{session:r,workdir:h,onStreamQueued:_e,onSendStart:Qe,onSendTaskAssigned:ot,onSessionChange:R,t:d,streamPhase:se,streamTaskId:tt,queuedTaskIds:le,queuedTasks:rt,pendingQueuedSends:qe,onRecall:ct,onSteer:it,onStopAll:dt,editDraft:st,onEditDraftConsumed:()=>Ue(null),onSelectionChange:at}),de&&n.jsxs(Je,{open:!0,onClose:()=>{E||ee(null)},children:[n.jsx(Ze,{title:d("hub.forkPromptTitle"),description:d("hub.forkPromptHint"),onClose:()=>{E||ee(null)}}),n.jsx("textarea",{autoFocus:!0,value:te,disabled:E,onChange:e=>Te(e.target.value),onKeyDown:e=>{e.key==="Enter"&&(e.metaKey||e.ctrlKey)&&te.trim()&&!E&&(e.preventDefault(),Re.current?.())},placeholder:d("hub.forkPromptPlaceholder"),className:"w-full min-h-[120px] resize-y rounded-md border border-edge bg-panel-alt px-3 py-2 text-[13px] leading-relaxed text-fg outline-none focus:border-edge-h"}),n.jsxs("div",{className:"mt-4 flex items-center justify-end gap-2",children:[n.jsx(Se,{variant:"ghost",disabled:E,onClick:()=>ee(null),children:d("modal.cancel")}),n.jsx(Se,{variant:"primary",disabled:E||!te.trim(),onClick:()=>{Re.current?.()},children:d(E?"hub.forkSubmitting":"hub.forkSubmit")})]})]}),C&&J.length>0&&n.jsx(Ft,{snapshot:J[J.length-1]},J[J.length-1].promptId)]})});export{Gt as SessionPanel};
|