pikiloom 0.4.15 → 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-CKoy_-w4.js → AgentTab-CDVhy5K1.js} +1 -1
- package/dashboard/dist/assets/{DirBrowser-DpbuN0OL.js → DirBrowser-BElI1-4D.js} +1 -1
- package/dashboard/dist/assets/{ExtensionsTab-ymr7K8dU.js → ExtensionsTab-BB8ipJ77.js} +1 -1
- package/dashboard/dist/assets/{IMAccessTab-CaTtCn3l.js → IMAccessTab-IZt_yXoG.js} +1 -1
- package/dashboard/dist/assets/{Modal-DA-9kJxp.js → Modal-C1EAGSL1.js} +1 -1
- package/dashboard/dist/assets/{Modals-BkLIRnNK.js → Modals-DYUV5yR9.js} +1 -1
- package/dashboard/dist/assets/{Select-B0pZtuzF.js → Select-BnsbE6Qv.js} +1 -1
- package/dashboard/dist/assets/SessionPanel-Ca_TVTT1.js +1 -0
- package/dashboard/dist/assets/{SystemTab-B9TcGMzc.js → SystemTab-Dk6k2OTt.js} +1 -1
- package/dashboard/dist/assets/index-CK-3CNRp.js +3 -0
- package/dashboard/dist/assets/index-CnJsD381.js +23 -0
- package/dashboard/dist/assets/index-dzfjF9Js.css +1 -0
- package/dashboard/dist/assets/{shared-i_XUH0xm.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/bridge.js +201 -7
- package/dist/agent/mcp/extensions.js +20 -9
- package/dist/agent/mcp/tools/workspace.js +4 -3
- package/dist/agent/stream.js +3 -2
- 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/config.js +134 -12
- package/dist/dashboard/routes/sessions.js +108 -27
- package/package.json +1 -1
- package/dashboard/dist/assets/SessionPanel-CYQtZZNX.js +0 -1
- package/dashboard/dist/assets/index-BCYshErN.js +0 -3
- package/dashboard/dist/assets/index-C5irxzzD.js +0 -23
- package/dashboard/dist/assets/index-FD86DEDF.css +0 -1
|
@@ -6,8 +6,9 @@ import fs from 'node:fs';
|
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import os from 'node:os';
|
|
8
8
|
import { spawn, spawnSync } from 'node:child_process';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
9
10
|
import { loadUserConfig, saveUserConfig, applyUserConfig, hasUserConfigFile } from '../../core/config/user-config.js';
|
|
10
|
-
import { expandTilde } from '../../core/platform.js';
|
|
11
|
+
import { expandTilde, whichSync } from '../../core/platform.js';
|
|
11
12
|
import { readGitStatus } from '../../core/git.js';
|
|
12
13
|
import { isSetupReady } from '../../cli/onboarding.js';
|
|
13
14
|
import { validateDingtalkConfig, validateDiscordConfig, validateFeishuConfig, validateSlackConfig, validateTelegramConfig, validateWecomConfig, validateWeixinConfig, } from '../../core/config/validation.js';
|
|
@@ -66,7 +67,96 @@ function runOpenCommand(command, args) {
|
|
|
66
67
|
throw new Error(detail || `Failed to run ${command} ${args.join(' ')}`);
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
|
-
function
|
|
70
|
+
function stripOpenPathWrapping(value) {
|
|
71
|
+
let text = value.trim();
|
|
72
|
+
const pairs = [['`', '`'], ['"', '"'], ["'", "'"], ['<', '>']];
|
|
73
|
+
let changed = true;
|
|
74
|
+
while (changed && text.length >= 2) {
|
|
75
|
+
changed = false;
|
|
76
|
+
for (const [left, right] of pairs) {
|
|
77
|
+
if (text.startsWith(left) && text.endsWith(right)) {
|
|
78
|
+
text = text.slice(left.length, -right.length).trim();
|
|
79
|
+
changed = true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return text;
|
|
84
|
+
}
|
|
85
|
+
function decodeOpenPathInput(raw) {
|
|
86
|
+
const text = stripOpenPathWrapping(raw);
|
|
87
|
+
if (text.startsWith('file://')) {
|
|
88
|
+
try {
|
|
89
|
+
return fileURLToPath(text);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return decodeURI(text.slice('file://'.length));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (text.startsWith('vscode://file/')) {
|
|
96
|
+
return decodeURI(`/${text.slice('vscode://file/'.length)}`);
|
|
97
|
+
}
|
|
98
|
+
return text;
|
|
99
|
+
}
|
|
100
|
+
function resolveOpenBasePath(basePath) {
|
|
101
|
+
const base = typeof basePath === 'string' && basePath.trim()
|
|
102
|
+
? basePath.trim()
|
|
103
|
+
: runtime.getRuntimeWorkdir(loadUserConfig());
|
|
104
|
+
return path.resolve(expandTilde(base || process.cwd()));
|
|
105
|
+
}
|
|
106
|
+
function splitExistingLineSuffix(candidate) {
|
|
107
|
+
const normalized = path.normalize(candidate);
|
|
108
|
+
if (fs.existsSync(normalized))
|
|
109
|
+
return { filePath: normalized, line: null, column: null };
|
|
110
|
+
const match = /^(.*?)(?::(\d+)(?::(\d+))?)$/.exec(normalized);
|
|
111
|
+
if (!match || !match[1])
|
|
112
|
+
return { filePath: normalized, line: null, column: null };
|
|
113
|
+
const filePath = path.normalize(match[1]);
|
|
114
|
+
if (!fs.existsSync(filePath))
|
|
115
|
+
return { filePath: normalized, line: null, column: null };
|
|
116
|
+
return {
|
|
117
|
+
filePath,
|
|
118
|
+
line: Number(match[2]),
|
|
119
|
+
column: match[3] ? Number(match[3]) : null,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
export function resolveOpenPathLocator(rawPath, basePath) {
|
|
123
|
+
const decoded = decodeOpenPathInput(rawPath);
|
|
124
|
+
const expanded = expandTilde(decoded);
|
|
125
|
+
const absolute = path.isAbsolute(expanded)
|
|
126
|
+
? path.resolve(expanded)
|
|
127
|
+
: path.resolve(resolveOpenBasePath(basePath), expanded);
|
|
128
|
+
return splitExistingLineSuffix(absolute);
|
|
129
|
+
}
|
|
130
|
+
function editorGotoArg(filePath, location) {
|
|
131
|
+
if (!location?.line)
|
|
132
|
+
return null;
|
|
133
|
+
return `${filePath}:${location.line}${location.column ? `:${location.column}` : ''}`;
|
|
134
|
+
}
|
|
135
|
+
function tryOpenCommand(command, args) {
|
|
136
|
+
if (!whichSync(command))
|
|
137
|
+
return false;
|
|
138
|
+
try {
|
|
139
|
+
runOpenCommand(command, args);
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function tryOpenVSCodeUrl(filePath, location) {
|
|
147
|
+
if (!location?.line)
|
|
148
|
+
return false;
|
|
149
|
+
const suffix = `:${location.line}${location.column ? `:${location.column}` : ''}`;
|
|
150
|
+
try {
|
|
151
|
+
runOpenCommand('open', [`vscode://file${encodeURI(filePath)}${suffix}`]);
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function openPathWithTarget(filePath, target, isDirectory, location) {
|
|
159
|
+
const gotoArg = isDirectory ? null : editorGotoArg(filePath, location);
|
|
70
160
|
if (process.platform === 'darwin') {
|
|
71
161
|
switch (target) {
|
|
72
162
|
case 'finder':
|
|
@@ -76,13 +166,21 @@ function openPathWithTarget(filePath, target, isDirectory) {
|
|
|
76
166
|
runOpenCommand('open', [filePath]);
|
|
77
167
|
return;
|
|
78
168
|
case 'cursor':
|
|
169
|
+
if (gotoArg && tryOpenCommand('cursor', ['-g', gotoArg]))
|
|
170
|
+
return;
|
|
79
171
|
runOpenCommand('open', ['-a', 'Cursor', filePath]);
|
|
80
172
|
return;
|
|
81
173
|
case 'windsurf':
|
|
174
|
+
if (gotoArg && tryOpenCommand('windsurf', ['-g', gotoArg]))
|
|
175
|
+
return;
|
|
82
176
|
runOpenCommand('open', ['-a', 'Windsurf', filePath]);
|
|
83
177
|
return;
|
|
84
178
|
case 'vscode':
|
|
85
179
|
default:
|
|
180
|
+
if (gotoArg && tryOpenCommand('code', ['-g', gotoArg]))
|
|
181
|
+
return;
|
|
182
|
+
if (gotoArg && tryOpenVSCodeUrl(filePath, location))
|
|
183
|
+
return;
|
|
86
184
|
runOpenCommand('open', ['-a', 'Visual Studio Code', filePath]);
|
|
87
185
|
return;
|
|
88
186
|
}
|
|
@@ -90,10 +188,16 @@ function openPathWithTarget(filePath, target, isDirectory) {
|
|
|
90
188
|
if (process.platform === 'win32') {
|
|
91
189
|
switch (target) {
|
|
92
190
|
case 'cursor':
|
|
93
|
-
|
|
191
|
+
if (gotoArg)
|
|
192
|
+
runOpenCommand('cursor', ['-g', gotoArg]);
|
|
193
|
+
else
|
|
194
|
+
runOpenCommand('cursor', [filePath]);
|
|
94
195
|
return;
|
|
95
196
|
case 'windsurf':
|
|
96
|
-
|
|
197
|
+
if (gotoArg)
|
|
198
|
+
runOpenCommand('windsurf', ['-g', gotoArg]);
|
|
199
|
+
else
|
|
200
|
+
runOpenCommand('windsurf', [filePath]);
|
|
97
201
|
return;
|
|
98
202
|
case 'finder':
|
|
99
203
|
case 'default':
|
|
@@ -101,16 +205,25 @@ function openPathWithTarget(filePath, target, isDirectory) {
|
|
|
101
205
|
return;
|
|
102
206
|
case 'vscode':
|
|
103
207
|
default:
|
|
104
|
-
|
|
208
|
+
if (gotoArg)
|
|
209
|
+
runOpenCommand('code', ['-g', gotoArg]);
|
|
210
|
+
else
|
|
211
|
+
runOpenCommand('code', [filePath]);
|
|
105
212
|
return;
|
|
106
213
|
}
|
|
107
214
|
}
|
|
108
215
|
switch (target) {
|
|
109
216
|
case 'cursor':
|
|
110
|
-
|
|
217
|
+
if (gotoArg)
|
|
218
|
+
runOpenCommand('cursor', ['-g', gotoArg]);
|
|
219
|
+
else
|
|
220
|
+
runOpenCommand('cursor', [filePath]);
|
|
111
221
|
return;
|
|
112
222
|
case 'windsurf':
|
|
113
|
-
|
|
223
|
+
if (gotoArg)
|
|
224
|
+
runOpenCommand('windsurf', ['-g', gotoArg]);
|
|
225
|
+
else
|
|
226
|
+
runOpenCommand('windsurf', [filePath]);
|
|
114
227
|
return;
|
|
115
228
|
case 'finder':
|
|
116
229
|
case 'default':
|
|
@@ -118,7 +231,10 @@ function openPathWithTarget(filePath, target, isDirectory) {
|
|
|
118
231
|
return;
|
|
119
232
|
case 'vscode':
|
|
120
233
|
default:
|
|
121
|
-
|
|
234
|
+
if (gotoArg)
|
|
235
|
+
runOpenCommand('code', ['-g', gotoArg]);
|
|
236
|
+
else
|
|
237
|
+
runOpenCommand('code', [filePath]);
|
|
122
238
|
return;
|
|
123
239
|
}
|
|
124
240
|
}
|
|
@@ -453,14 +569,20 @@ app.post('/api/open-in-editor', async (c) => {
|
|
|
453
569
|
try {
|
|
454
570
|
const body = await c.req.json();
|
|
455
571
|
const filePath = typeof body?.filePath === 'string' ? body.filePath.trim() : '';
|
|
572
|
+
const basePath = typeof body?.basePath === 'string' && body.basePath.trim()
|
|
573
|
+
? body.basePath.trim()
|
|
574
|
+
: typeof body?.workdir === 'string' && body.workdir.trim()
|
|
575
|
+
? body.workdir.trim()
|
|
576
|
+
: null;
|
|
456
577
|
const target = isOpenTarget(body?.target) ? body.target : 'vscode';
|
|
457
578
|
if (!filePath)
|
|
458
579
|
return c.json({ ok: false, error: 'filePath is required' }, 400);
|
|
459
|
-
|
|
580
|
+
const resolved = resolveOpenPathLocator(filePath, basePath);
|
|
581
|
+
if (!fs.existsSync(resolved.filePath))
|
|
460
582
|
return c.json({ ok: false, error: 'Path not found' }, 404);
|
|
461
|
-
const stat = fs.statSync(filePath);
|
|
462
|
-
openPathWithTarget(filePath, target, stat.isDirectory());
|
|
463
|
-
return c.json({ ok: true });
|
|
583
|
+
const stat = fs.statSync(resolved.filePath);
|
|
584
|
+
openPathWithTarget(resolved.filePath, target, stat.isDirectory(), resolved);
|
|
585
|
+
return c.json({ ok: true, filePath: resolved.filePath, line: resolved.line, column: resolved.column });
|
|
464
586
|
}
|
|
465
587
|
catch (err) {
|
|
466
588
|
const detail = err instanceof Error ? err.message : String(err);
|
|
@@ -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-BCYshErN.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-C5irxzzD.js";import{M as Je,a as Ze}from"./Modal-DA-9kJxp.js";import"./router-DHISdpPk.js";import"./Select-B0pZtuzF.js";import"./DirBrowser-DpbuN0OL.js";import"./markdown-DxQYQFeH.js";import"./ExtensionsTab-ymr7K8dU.js";function Ft({snapshot:f}){const[r,p]=t.useState(f.currentIndex??0),[C,R]=t.useState(""),[x,h]=t.useState(!1),[W,k]=t.useState(null);t.useEffect(()=>{p(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&&(p(g=>g+1),R(""))},ne=async o=>{if(!x){h(!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{h(!1)}}},O=async()=>{if(x)return;const o=C.trim();if(!o&&!v?.allowEmpty){k("Please enter a response.");return}h(!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{h(!1)}},m=async()=>{if(!x){h(!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{h(!1)}}},H=async()=>{if(!x){h(!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:p,active:C=!0,onSessionChange:R,initialPendingPrompt:x,initialPendingImageUrls:h,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||!!(h&&h.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(h||[]),[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(h||[]),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),h&&h.length&&!L.length&&(ce(h),U.current=h),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(p,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:p})}finally{je(!1)}}},[de,te,p,r.agent,r.sessionId,R]);Re.current=ut;const ge=t.useCallback(async(e,s={})=>{try{const l=await It({workdir:p,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}},[p,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:p})),!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,p]),_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:p,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,p,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:p}),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,onResend:a=>{B.current=!0,Qe(a);const u=Be.current;j.sendSessionMessage(p,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})]}),n.jsx("div",{className:"h-4"})]})}),n.jsx(Pt,{session:r,workdir:p,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};
|