pikiloom 0.4.56 → 0.4.58
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/dashboard/dist/assets/{AgentTab-BBrcBA3Q.js → AgentTab-CLlOp3lJ.js} +1 -1
- package/dashboard/dist/assets/{ConnectionModal-B8p6NzY7.js → ConnectionModal-BJhiouLS.js} +1 -1
- package/dashboard/dist/assets/{DirBrowser-BW7l1k3h.js → DirBrowser-H0N2gMRB.js} +1 -1
- package/dashboard/dist/assets/{ExtensionsTab-CTXhgsep.js → ExtensionsTab-5uaiMRcm.js} +1 -1
- package/dashboard/dist/assets/{IMAccessTab-BAymEU1l.js → IMAccessTab-ClNbxnzS.js} +1 -1
- package/dashboard/dist/assets/{Modal-CaunzS8l.js → Modal-Cy1zHcC7.js} +1 -1
- package/dashboard/dist/assets/{Modals-Co5yJFIK.js → Modals-BrLADO-I.js} +1 -1
- package/dashboard/dist/assets/{Select-Bur7mVMe.js → Select-D4DbOixc.js} +1 -1
- package/dashboard/dist/assets/SessionPanel-1hemRLg_.js +1 -0
- package/dashboard/dist/assets/{SystemTab-D1haU2ST.js → SystemTab-CDrID55_.js} +1 -1
- package/dashboard/dist/assets/index-BO-FYAny.css +1 -0
- package/dashboard/dist/assets/{index-BVBpUn_6.js → index-Iot9Vn3K.js} +3 -3
- package/dashboard/dist/assets/{index-C3fUMz3j.js → index-QFqtuRrZ.js} +17 -17
- package/dashboard/dist/assets/{shared-DwuSoSl7.js → shared-DQyw9U1Z.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dist/agent/drivers/claude.js +110 -17
- package/dist/agent/index.js +1 -1
- package/dist/agent/utils.js +33 -0
- package/dist/dashboard/routes/sessions.js +16 -1
- package/package.json +1 -1
- package/packages/kernel/dist/drivers/claude.d.ts +1 -0
- package/packages/kernel/dist/drivers/claude.js +56 -7
- package/dashboard/dist/assets/SessionPanel-lPdXcjdM.js +0 -1
- package/dashboard/dist/assets/index-N_mAxxEJ.css +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as s}from"./react-vendor-C7Sl8SE7.js";import{O as t,m as i,S as a}from"./index-
|
|
1
|
+
import{j as s}from"./react-vendor-C7Sl8SE7.js";import{O as t,m as i,S as a}from"./index-Iot9Vn3K.js";function o({primary:e,secondary:l,tertiary:n}){return s.jsxs("div",{className:"flex flex-col gap-2 pt-1 sm:flex-row sm:flex-wrap sm:items-center sm:justify-between",children:[s.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e&&s.jsxs(i,{tone:"primary",onClick:e.onClick,disabled:e.disabled,children:[e.loading&&s.jsx(a,{}),e.label]}),l&&s.jsx(i,{tone:"secondary",onClick:l.onClick,disabled:l.disabled,children:l.label})]}),n&&s.jsx("div",{className:"text-xs leading-relaxed text-fg-4",children:n})]})}function c({children:e,className:l}){return s.jsx(t,{padding:"md",elevation:"flat",className:l,children:e})}export{o as A,c as S};
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
<link rel="icon" type="image/png" href="/logo.png">
|
|
7
7
|
<title>Pikiloom</title>
|
|
8
8
|
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-Iot9Vn3K.js"></script>
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/react-vendor-C7Sl8SE7.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/router-DHISdpPk.js">
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BO-FYAny.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<div id="root"></div>
|
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { execSync, spawn } from 'node:child_process';
|
|
4
4
|
import { createInterface } from 'node:readline';
|
|
5
5
|
import { registerDriver } from '../driver.js';
|
|
6
|
-
import { Q, run, agentError, agentLog, agentWarn, buildStreamPreviewMeta, computeContext, pushRecentActivity, summarizeClaudeToolUse, summarizeClaudeToolResult, joinErrorMessages, parseTodoWriteAsPlan, previewToolCallInput, previewToolCallResult, detectClaudeApiError, isRetryableClaudeApiError, detectClaudeModelError, claudeModelErrorMessage, emitSessionIdUpdate, IMAGE_EXTS, mimeForExt, listPikiloomSessions, mergeManagedAndNativeSessions, managedRecordToSessionInfo, readTailLines, stripInjectedPrompts, sanitizeSessionUserPreviewText, isSystemInjectedUserText, SESSION_PREVIEW_IMAGE_PLACEHOLDER_RE, CLAUDE_AT_MENTION_IMAGE_RE, extractClaudeAtMentionImagePaths, attachAgentImage, applyTurnWindow, shortValue, roundPercent, toIsoFromEpochSeconds, modelFamily, normalizeClaudeModelId, emptyUsage, normalizeUsageStatus, collapseSkillPrompt, } from '../index.js';
|
|
6
|
+
import { Q, run, agentError, agentLog, agentWarn, buildStreamPreviewMeta, computeContext, pushRecentActivity, summarizeClaudeToolUse, summarizeClaudeToolResult, joinErrorMessages, parseTodoWriteAsPlan, applyClaudeTaskUpdateToPlan, previewToolCallInput, previewToolCallResult, detectClaudeApiError, isRetryableClaudeApiError, detectClaudeModelError, claudeModelErrorMessage, emitSessionIdUpdate, IMAGE_EXTS, mimeForExt, listPikiloomSessions, mergeManagedAndNativeSessions, managedRecordToSessionInfo, readTailLines, stripInjectedPrompts, sanitizeSessionUserPreviewText, isSystemInjectedUserText, SESSION_PREVIEW_IMAGE_PLACEHOLDER_RE, CLAUDE_AT_MENTION_IMAGE_RE, extractClaudeAtMentionImagePaths, attachAgentImage, applyTurnWindow, shortValue, roundPercent, toIsoFromEpochSeconds, modelFamily, normalizeClaudeModelId, emptyUsage, normalizeUsageStatus, collapseSkillPrompt, } from '../index.js';
|
|
7
7
|
import { AGENT_STREAM_HARD_KILL_GRACE_MS, AGENT_GRACEFUL_ABORT_GRACE_MS, SESSION_RUNNING_THRESHOLD_MS } from '../../core/constants.js';
|
|
8
8
|
import { terminateProcessTree } from '../../core/process-control.js';
|
|
9
9
|
import { getHome, IS_MAC, encodePathAsDirName } from '../../core/platform.js';
|
|
@@ -517,10 +517,13 @@ export function claudeParse(ev, s) {
|
|
|
517
517
|
const toolName = String(block?.name || 'Tool').trim() || 'Tool';
|
|
518
518
|
if (toolName === 'TodoWrite') {
|
|
519
519
|
const plan = parseTodoWriteAsPlan(block?.input);
|
|
520
|
-
if (plan)
|
|
520
|
+
if (plan) {
|
|
521
521
|
s.plan = plan;
|
|
522
|
+
s.claudeTodoPlan = plan;
|
|
523
|
+
}
|
|
522
524
|
s.seenClaudeToolIds.add(toolId);
|
|
523
525
|
s.claudeToolsById.set(toolId, { name: toolName, summary: 'Update plan' });
|
|
526
|
+
pushRecentActivity(s.recentActivity, 'Update plan');
|
|
524
527
|
continue;
|
|
525
528
|
}
|
|
526
529
|
if (toolName === 'TaskCreate') {
|
|
@@ -528,13 +531,18 @@ export function claudeParse(ev, s) {
|
|
|
528
531
|
if (subject)
|
|
529
532
|
s.pendingClaudeTaskCreates.set(toolId, { subject });
|
|
530
533
|
s.seenClaudeToolIds.add(toolId);
|
|
531
|
-
|
|
534
|
+
const createSummary = subject ? `Create task: ${subject}` : 'Create task';
|
|
535
|
+
s.claudeToolsById.set(toolId, { name: toolName, summary: createSummary });
|
|
536
|
+
pushRecentActivity(s.recentActivity, createSummary);
|
|
532
537
|
continue;
|
|
533
538
|
}
|
|
534
539
|
if (toolName === 'TaskUpdate') {
|
|
535
540
|
const taskId = String(block?.input?.taskId ?? '').trim();
|
|
536
541
|
const rawStatus = String(block?.input?.status ?? '').trim().toLowerCase();
|
|
537
|
-
|
|
542
|
+
// Chronological: the plan reflects the state AFTER this update. An id known to the
|
|
543
|
+
// TaskCreate store lands there; otherwise it lands positionally on the latest
|
|
544
|
+
// TodoWrite list (ids are 1-based positions when the todo panel owns the list).
|
|
545
|
+
if (taskId && s.claudeTaskList.has(taskId)) {
|
|
538
546
|
if (rawStatus === 'deleted') {
|
|
539
547
|
s.claudeTaskList.delete(taskId);
|
|
540
548
|
s.claudeTaskOrder = s.claudeTaskOrder.filter((id) => id !== taskId);
|
|
@@ -546,8 +554,17 @@ export function claudeParse(ev, s) {
|
|
|
546
554
|
}
|
|
547
555
|
rebuildClaudePlanFromTasks(s);
|
|
548
556
|
}
|
|
557
|
+
else if (taskId) {
|
|
558
|
+
const updated = applyClaudeTaskUpdateToPlan(s.claudeTodoPlan, taskId, rawStatus);
|
|
559
|
+
if (updated) {
|
|
560
|
+
s.plan = updated;
|
|
561
|
+
s.claudeTodoPlan = updated;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
549
564
|
s.seenClaudeToolIds.add(toolId);
|
|
550
|
-
|
|
565
|
+
const updateSummary = `Update task ${taskId || '?'} → ${rawStatus || 'unknown'}`;
|
|
566
|
+
s.claudeToolsById.set(toolId, { name: toolName, summary: updateSummary });
|
|
567
|
+
pushRecentActivity(s.recentActivity, updateSummary);
|
|
551
568
|
continue;
|
|
552
569
|
}
|
|
553
570
|
if (toolName === 'Task' || toolName === 'Agent') {
|
|
@@ -722,6 +739,7 @@ export function createClaudeStreamState(opts) {
|
|
|
722
739
|
activity: '',
|
|
723
740
|
recentActivity: [],
|
|
724
741
|
plan: null,
|
|
742
|
+
claudeTodoPlan: null,
|
|
725
743
|
claudeTaskList: new Map(),
|
|
726
744
|
claudeTaskOrder: [],
|
|
727
745
|
pendingClaudeTaskCreates: new Map(),
|
|
@@ -1376,7 +1394,26 @@ function claudeImageBlockFromEntry(entry) {
|
|
|
1376
1394
|
const mime = (source.media_type || 'image/png').toLowerCase();
|
|
1377
1395
|
return { type: 'image', content: `data:${mime};base64,${source.data}`, imageMime: mime };
|
|
1378
1396
|
}
|
|
1379
|
-
function
|
|
1397
|
+
function createClaudeHistoryTaskCtx() {
|
|
1398
|
+
return { planToolIds: new Set(), pendingTaskCreates: new Map(), taskList: new Map(), taskOrder: [], todoPlan: null };
|
|
1399
|
+
}
|
|
1400
|
+
function claudeTaskPlanFromCtx(ctx) {
|
|
1401
|
+
if (!ctx.taskOrder.length)
|
|
1402
|
+
return null;
|
|
1403
|
+
const steps = [];
|
|
1404
|
+
for (const id of ctx.taskOrder) {
|
|
1405
|
+
const task = ctx.taskList.get(id);
|
|
1406
|
+
if (!task)
|
|
1407
|
+
continue;
|
|
1408
|
+
const lowered = String(task.status || '').toLowerCase();
|
|
1409
|
+
const status = lowered === 'completed' ? 'completed'
|
|
1410
|
+
: lowered === 'in_progress' || lowered === 'inprogress' ? 'inProgress'
|
|
1411
|
+
: 'pending';
|
|
1412
|
+
steps.push({ step: task.subject, status });
|
|
1413
|
+
}
|
|
1414
|
+
return steps.length ? { explanation: null, steps } : null;
|
|
1415
|
+
}
|
|
1416
|
+
function extractClaudeBlocks(content, skipSystemBlocks = false, taskCtx, toolNamesByUseId) {
|
|
1380
1417
|
if (typeof content === 'string')
|
|
1381
1418
|
return [{ type: 'text', content }];
|
|
1382
1419
|
if (!Array.isArray(content))
|
|
@@ -1394,15 +1431,55 @@ function extractClaudeBlocks(content, skipSystemBlocks = false, todoWriteToolIds
|
|
|
1394
1431
|
blocks.push({ type: 'thinking', content: block.thinking });
|
|
1395
1432
|
}
|
|
1396
1433
|
else if (block.type === 'tool_use') {
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1434
|
+
const inputStr = block.input ? JSON.stringify(block.input, null, 2) : '';
|
|
1435
|
+
if (taskCtx && (block.name === 'TodoWrite' || block.name === 'TaskCreate' || block.name === 'TaskUpdate')) {
|
|
1436
|
+
// The command itself stays visible as an Activity row (matching the CLI's own
|
|
1437
|
+
// transcript); the structured task state additionally surfaces as a plan block.
|
|
1438
|
+
taskCtx.planToolIds.add(block.id);
|
|
1439
|
+
blocks.push({ type: 'tool_use', content: inputStr, toolName: block.name, toolId: block.id });
|
|
1440
|
+
if (block.name === 'TodoWrite') {
|
|
1441
|
+
const plan = parseTodoWriteAsPlan(block.input);
|
|
1442
|
+
if (plan) {
|
|
1443
|
+
taskCtx.todoPlan = plan;
|
|
1444
|
+
blocks.push({ type: 'plan', content: '', plan, toolId: block.id });
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
else if (block.name === 'TaskCreate') {
|
|
1448
|
+
const subject = typeof block.input?.subject === 'string' ? block.input.subject.trim() : '';
|
|
1449
|
+
if (subject)
|
|
1450
|
+
taskCtx.pendingTaskCreates.set(block.id, { subject });
|
|
1451
|
+
}
|
|
1452
|
+
else {
|
|
1453
|
+
const taskId = String(block.input?.taskId ?? '').trim();
|
|
1454
|
+
const rawStatus = String(block.input?.status ?? '').trim().toLowerCase();
|
|
1455
|
+
if (taskId && taskCtx.taskList.has(taskId)) {
|
|
1456
|
+
if (rawStatus === 'deleted') {
|
|
1457
|
+
taskCtx.taskList.delete(taskId);
|
|
1458
|
+
const idx = taskCtx.taskOrder.indexOf(taskId);
|
|
1459
|
+
if (idx >= 0)
|
|
1460
|
+
taskCtx.taskOrder.splice(idx, 1);
|
|
1461
|
+
}
|
|
1462
|
+
else if (rawStatus) {
|
|
1463
|
+
const existing = taskCtx.taskList.get(taskId);
|
|
1464
|
+
if (existing)
|
|
1465
|
+
existing.status = rawStatus;
|
|
1466
|
+
}
|
|
1467
|
+
const plan = claudeTaskPlanFromCtx(taskCtx);
|
|
1468
|
+
if (plan)
|
|
1469
|
+
blocks.push({ type: 'plan', content: '', plan, toolId: block.id });
|
|
1470
|
+
}
|
|
1471
|
+
else if (taskId) {
|
|
1472
|
+
// Unknown id in the TaskCreate store: land the update positionally on the latest
|
|
1473
|
+
// TodoWrite list so the plan card reflects the state AFTER this update.
|
|
1474
|
+
const updated = applyClaudeTaskUpdateToPlan(taskCtx.todoPlan, taskId, rawStatus);
|
|
1475
|
+
if (updated) {
|
|
1476
|
+
taskCtx.todoPlan = updated;
|
|
1477
|
+
blocks.push({ type: 'plan', content: '', plan: updated, toolId: block.id });
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1403
1480
|
}
|
|
1481
|
+
continue;
|
|
1404
1482
|
}
|
|
1405
|
-
const inputStr = block.input ? JSON.stringify(block.input, null, 2) : '';
|
|
1406
1483
|
blocks.push({ type: 'tool_use', content: inputStr, toolName: block.name || 'unknown', toolId: block.id });
|
|
1407
1484
|
}
|
|
1408
1485
|
else if (block.type === 'tool_result') {
|
|
@@ -1455,7 +1532,7 @@ function getClaudeSessionMessages(opts) {
|
|
|
1455
1532
|
let pendingBlocks = [];
|
|
1456
1533
|
let pendingUsage = null;
|
|
1457
1534
|
let pendingCallOutputs = new Map();
|
|
1458
|
-
const
|
|
1535
|
+
const taskCtx = createClaudeHistoryTaskCtx();
|
|
1459
1536
|
const subAgentBlocksById = new Map();
|
|
1460
1537
|
const subAgentToolIds = new Set();
|
|
1461
1538
|
const toolNamesByUseId = new Map();
|
|
@@ -1518,7 +1595,7 @@ function getClaudeSessionMessages(opts) {
|
|
|
1518
1595
|
if (ev.isMeta === true) {
|
|
1519
1596
|
if (pendingRole === 'assistant') {
|
|
1520
1597
|
const toolUseId = typeof ev.sourceToolUseID === 'string' ? ev.sourceToolUseID : '';
|
|
1521
|
-
if (toolUseId && !
|
|
1598
|
+
if (toolUseId && !taskCtx.planToolIds.has(toolUseId) && !subAgentToolIds.has(toolUseId)) {
|
|
1522
1599
|
const text = extractClaudeText(ev.message?.content, false);
|
|
1523
1600
|
if (text) {
|
|
1524
1601
|
pendingBlocks.push({ type: 'tool_result', content: text, toolId: toolUseId });
|
|
@@ -1535,7 +1612,23 @@ function getClaudeSessionMessages(opts) {
|
|
|
1535
1612
|
if (pendingRole === 'assistant') {
|
|
1536
1613
|
for (const block of contentArr) {
|
|
1537
1614
|
const toolUseId = block.tool_use_id;
|
|
1538
|
-
|
|
1615
|
+
// TaskCreate result: the assigned task id arrives here — register the task
|
|
1616
|
+
// and surface the refreshed list as a plan block on this turn.
|
|
1617
|
+
const pendingCreate = toolUseId ? taskCtx.pendingTaskCreates.get(toolUseId) : undefined;
|
|
1618
|
+
if (pendingCreate) {
|
|
1619
|
+
taskCtx.pendingTaskCreates.delete(toolUseId);
|
|
1620
|
+
const assignedId = readClaudeTaskCreateId(ev, block);
|
|
1621
|
+
if (assignedId) {
|
|
1622
|
+
if (!taskCtx.taskList.has(assignedId))
|
|
1623
|
+
taskCtx.taskOrder.push(assignedId);
|
|
1624
|
+
taskCtx.taskList.set(assignedId, { subject: pendingCreate.subject, status: 'pending' });
|
|
1625
|
+
const plan = claudeTaskPlanFromCtx(taskCtx);
|
|
1626
|
+
if (plan)
|
|
1627
|
+
pendingBlocks.push({ type: 'plan', content: '', plan, toolId: toolUseId });
|
|
1628
|
+
}
|
|
1629
|
+
continue;
|
|
1630
|
+
}
|
|
1631
|
+
if (taskCtx.planToolIds.has(toolUseId))
|
|
1539
1632
|
continue;
|
|
1540
1633
|
if (subAgentToolIds.has(toolUseId))
|
|
1541
1634
|
continue;
|
|
@@ -1654,7 +1747,7 @@ function getClaudeSessionMessages(opts) {
|
|
|
1654
1747
|
toolNamesByUseId.set(inner.id, inner.name);
|
|
1655
1748
|
}
|
|
1656
1749
|
}
|
|
1657
|
-
const blocks = extractClaudeBlocks(ev.message?.content, true,
|
|
1750
|
+
const blocks = extractClaudeBlocks(ev.message?.content, true, taskCtx, toolNamesByUseId);
|
|
1658
1751
|
const contents = Array.isArray(ev.message?.content) ? ev.message.content : [];
|
|
1659
1752
|
for (let i = 0; i < blocks.length; i++) {
|
|
1660
1753
|
const block = blocks[i];
|
package/dist/agent/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import './drivers/hermes.js';
|
|
|
5
5
|
export { IMAGE_EXTS } from './types.js';
|
|
6
6
|
export { attachAgentImage, attachInlineImage, materializeImage, rewriteAttachmentBlocksForTransport, attachmentUrl, resolveAllowedAttachmentPath, allowedAttachmentRoots, decodeAttachmentPathParam, sessionAttachmentsDir, codexHome, } from './images.js';
|
|
7
7
|
export { deliverArtifact, readDeliveredArtifacts, deliveredArtifactBlocks, tailDeliveredBlocks, latestDeliveredTaskId, mimeForArtifact, } from './artifacts.js';
|
|
8
|
-
export { Q, agentLog, agentWarn, agentError, dedupeStrings, numberOrNull, normalizeStreamPreviewPlan, parseTodoWriteAsPlan, normalizeActivityLine, pushRecentActivity, detectClaudeApiError, isRetryableClaudeApiError, detectClaudeModelError, claudeModelErrorMessage, firstNonEmptyLine, shortValue, normalizeErrorMessage, joinErrorMessages, appendSystemPrompt, mimeForExt, computeContext, buildStreamPreviewMeta, summarizeClaudeToolUse, summarizeClaudeToolResult, previewToolCallInput, previewToolCallResult, roundPercent, toIsoFromEpochSeconds, normalizeUsageStatus, labelFromWindowMinutes, usageWindowFromRateLimit, parseJsonTail, modelFamily, normalizeClaudeModelId, emptyUsage, readTailLines, stripInjectedPrompts, sanitizeSessionUserPreviewText, isSystemInjectedUserText, SESSION_PREVIEW_IMAGE_PLACEHOLDER_RE, CLAUDE_AT_MENTION_IMAGE_RE, extractClaudeAtMentionImagePaths, stripClaudeAtMentionImages, isPendingSessionId, emitSessionIdUpdate, sessionListDisplayTitle, } from './utils.js';
|
|
8
|
+
export { Q, agentLog, agentWarn, agentError, dedupeStrings, numberOrNull, normalizeStreamPreviewPlan, parseTodoWriteAsPlan, applyClaudeTaskUpdateToPlan, normalizeActivityLine, pushRecentActivity, detectClaudeApiError, isRetryableClaudeApiError, detectClaudeModelError, claudeModelErrorMessage, firstNonEmptyLine, shortValue, normalizeErrorMessage, joinErrorMessages, appendSystemPrompt, mimeForExt, computeContext, buildStreamPreviewMeta, summarizeClaudeToolUse, summarizeClaudeToolResult, previewToolCallInput, previewToolCallResult, roundPercent, toIsoFromEpochSeconds, normalizeUsageStatus, labelFromWindowMinutes, usageWindowFromRateLimit, parseJsonTail, modelFamily, normalizeClaudeModelId, emptyUsage, readTailLines, stripInjectedPrompts, sanitizeSessionUserPreviewText, isSystemInjectedUserText, SESSION_PREVIEW_IMAGE_PLACEHOLDER_RE, CLAUDE_AT_MENTION_IMAGE_RE, extractClaudeAtMentionImagePaths, stripClaudeAtMentionImages, isPendingSessionId, emitSessionIdUpdate, sessionListDisplayTitle, } from './utils.js';
|
|
9
9
|
export { updateSessionMeta, promoteSessionId, recordFork, resolveCanonicalSessionId, getSessionPromotions, listPikiloomSessions, findPikiloomSession, getSessionStoredConfig, ensureManagedSession, findManagedThreadSession, stageSessionFiles, mergeManagedAndNativeSessions, managedRecordToSessionInfo, getSessions, getSessionTail, getSessionMessages, applyTurnWindow, applyTurnFilter, classifySession, deriveUserStatus, exportSession, importSession, deleteAgentSession, isProcessAlive, isRunningSessionStale, reconcileOrphanedRunningSessions, } from './session.js';
|
|
10
10
|
export { detectAgentBin, listAgents, resolveDefaultAgent, run, doStream, recoverProfileIdForModel, listModels, resolveAgentModels, dropNativeShadowedByProfiles, getUsage, getAgentBoundModelId, setAgentBoundModelId, } from './stream.js';
|
|
11
11
|
export { registerDriver, getDriver, getDriverCapabilities, allDrivers, allDriverIds, hasDriver, shutdownAllDrivers, } from './driver.js';
|
package/dist/agent/utils.js
CHANGED
|
@@ -89,6 +89,30 @@ export function parseTodoWriteAsPlan(input) {
|
|
|
89
89
|
return null;
|
|
90
90
|
return { explanation: null, steps };
|
|
91
91
|
}
|
|
92
|
+
// Apply a TaskUpdate to a TodoWrite-produced plan positionally (taskId = 1-based item index).
|
|
93
|
+
// Used when the id isn't in the TaskCreate store, so an update issued AFTER a TodoWrite still
|
|
94
|
+
// lands on the displayed list — the plan card always reflects the state after the LAST change,
|
|
95
|
+
// whichever mechanism wrote it. Returns a fresh plan (never mutates) or null when inapplicable.
|
|
96
|
+
export function applyClaudeTaskUpdateToPlan(plan, taskId, rawStatus) {
|
|
97
|
+
if (!plan || !Array.isArray(plan.steps) || !plan.steps.length)
|
|
98
|
+
return null;
|
|
99
|
+
if (!/^\d+$/.test(taskId))
|
|
100
|
+
return null;
|
|
101
|
+
const idx = Number(taskId) - 1;
|
|
102
|
+
if (idx < 0 || idx >= plan.steps.length)
|
|
103
|
+
return null;
|
|
104
|
+
if (rawStatus === 'deleted') {
|
|
105
|
+
const steps = plan.steps.filter((_, i) => i !== idx);
|
|
106
|
+
return steps.length ? { explanation: plan.explanation ?? null, steps } : null;
|
|
107
|
+
}
|
|
108
|
+
const status = rawStatus === 'completed' ? 'completed'
|
|
109
|
+
: rawStatus === 'in_progress' || rawStatus === 'inprogress' ? 'inProgress'
|
|
110
|
+
: rawStatus === 'pending' ? 'pending' : null;
|
|
111
|
+
if (!status)
|
|
112
|
+
return null;
|
|
113
|
+
const steps = plan.steps.map((step, i) => i === idx ? { ...step, status } : step);
|
|
114
|
+
return { explanation: plan.explanation ?? null, steps };
|
|
115
|
+
}
|
|
92
116
|
export function normalizeActivityLine(text) { return text.replace(/\s+/g, ' ').trim(); }
|
|
93
117
|
export function pushRecentActivity(lines, line, maxLines = 80) {
|
|
94
118
|
const cleaned = normalizeActivityLine(line);
|
|
@@ -339,6 +363,15 @@ export function summarizeClaudeToolUse(name, input) {
|
|
|
339
363
|
return q ? `Search web: ${q}` : 'Search web';
|
|
340
364
|
}
|
|
341
365
|
case 'TodoWrite': return 'Update plan';
|
|
366
|
+
case 'TaskCreate': {
|
|
367
|
+
const subject = shortValue(input?.subject, 120);
|
|
368
|
+
return subject ? `Create task: ${subject}` : 'Create task';
|
|
369
|
+
}
|
|
370
|
+
case 'TaskUpdate': {
|
|
371
|
+
const taskId = shortValue(input?.taskId, 24);
|
|
372
|
+
const status = shortValue(input?.status, 24);
|
|
373
|
+
return taskId ? `Update task ${taskId}${status ? ` → ${status}` : ''}` : 'Update task';
|
|
374
|
+
}
|
|
342
375
|
case 'AskUserQuestion': {
|
|
343
376
|
const qs = Array.isArray(input?.questions) ? input.questions : [];
|
|
344
377
|
const first = qs[0];
|
|
@@ -99,6 +99,20 @@ function sanitizeUploadFileName(rawName, mimeType, index) {
|
|
|
99
99
|
const ext = parsed.ext || extensionForMimeType(mimeType) || '.bin';
|
|
100
100
|
return `${safeStem}${ext.toLowerCase()}`;
|
|
101
101
|
}
|
|
102
|
+
export function reserveUploadFileName(rawName, mimeType, index, usedNames) {
|
|
103
|
+
const desired = sanitizeUploadFileName(rawName, mimeType, index);
|
|
104
|
+
const parsed = path.parse(desired);
|
|
105
|
+
const stem = parsed.name || `attachment-${index + 1}`;
|
|
106
|
+
const ext = parsed.ext;
|
|
107
|
+
let candidate = desired;
|
|
108
|
+
let suffix = 2;
|
|
109
|
+
while (usedNames.has(candidate.toLowerCase())) {
|
|
110
|
+
candidate = `${stem}-${suffix}${ext}`;
|
|
111
|
+
suffix++;
|
|
112
|
+
}
|
|
113
|
+
usedNames.add(candidate.toLowerCase());
|
|
114
|
+
return candidate;
|
|
115
|
+
}
|
|
102
116
|
async function materializeUploadedFiles(entries) {
|
|
103
117
|
const files = entries.filter(isUploadFile);
|
|
104
118
|
if (!files.length) {
|
|
@@ -107,8 +121,9 @@ async function materializeUploadedFiles(entries) {
|
|
|
107
121
|
const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'pikiloom-dashboard-upload-'));
|
|
108
122
|
try {
|
|
109
123
|
const attachments = [];
|
|
124
|
+
const usedNames = new Set();
|
|
110
125
|
for (const [index, file] of files.entries()) {
|
|
111
|
-
const filename =
|
|
126
|
+
const filename = reserveUploadFileName(String(file.name || ''), String(file.type || ''), index, usedNames);
|
|
112
127
|
const filePath = path.join(tempDir, filename);
|
|
113
128
|
await fs.promises.writeFile(filePath, Buffer.from(await file.arrayBuffer()));
|
|
114
129
|
attachments.push(filePath);
|
package/package.json
CHANGED
|
@@ -57,6 +57,7 @@ export declare function emitClaudeImages(blocks: any[], s: any, emit: (e: Driver
|
|
|
57
57
|
export declare function todoWriteToPlan(input: any): UniversalPlan | null;
|
|
58
58
|
export declare function readClaudeTaskCreateId(ev: any, block: any): string | null;
|
|
59
59
|
export declare function rebuildClaudeTaskPlan(s: any): UniversalPlan | null;
|
|
60
|
+
export declare function applyTaskUpdateToTodoPlan(plan: UniversalPlan | null | undefined, taskId: string, rawStatus: string): UniversalPlan | null;
|
|
60
61
|
export declare function shortToolValue(value: unknown, max?: number): string;
|
|
61
62
|
export declare function summarizeToolUse(name: string, input: any): string;
|
|
62
63
|
export declare function firstResultLine(content: any): string;
|
|
@@ -531,29 +531,41 @@ export function handleClaudeEvent(ev, s, emit) {
|
|
|
531
531
|
const name = String(b.name || 'Tool');
|
|
532
532
|
if (name === 'TodoWrite') {
|
|
533
533
|
const plan = todoWriteToPlan(b.input);
|
|
534
|
-
if (plan)
|
|
534
|
+
if (plan) {
|
|
535
|
+
s.todoPlan = plan;
|
|
535
536
|
emit({ type: 'plan', plan });
|
|
537
|
+
}
|
|
538
|
+
const summary = 'Update plan';
|
|
539
|
+
(s.tools ||= new Map()).set(id, { name, summary });
|
|
540
|
+
emit({ type: 'tool', call: { id, name, summary, input: null, status: 'running' } });
|
|
536
541
|
continue;
|
|
537
542
|
}
|
|
538
543
|
// Task list (current Claude mechanism): stash the subject; the tool_result assigns the id.
|
|
539
|
-
//
|
|
544
|
+
// The command surfaces as an Activity row (matching the CLI's own transcript); the
|
|
545
|
+
// structured task state still flows through plan events.
|
|
540
546
|
if (name === 'TaskCreate') {
|
|
541
547
|
const subject = typeof b.input?.subject === 'string' ? b.input.subject.trim() : '';
|
|
542
548
|
if (subject)
|
|
543
549
|
(s.pendingTaskCreates ||= new Map()).set(id, { subject });
|
|
550
|
+
const summary = subject ? `Create task: ${subject}` : 'Create task';
|
|
551
|
+
(s.tools ||= new Map()).set(id, { name, summary });
|
|
552
|
+
emit({ type: 'tool', call: { id, name, summary, input: null, status: 'running' } });
|
|
544
553
|
continue;
|
|
545
554
|
}
|
|
546
555
|
if (name === 'TaskUpdate') {
|
|
547
556
|
const taskId = String(b.input?.taskId ?? '').trim();
|
|
548
557
|
const rawStatus = String(b.input?.status ?? '').trim().toLowerCase();
|
|
549
|
-
|
|
558
|
+
// Chronological: the plan reflects the state AFTER this update. An id known to the
|
|
559
|
+
// TaskCreate store lands there; otherwise it lands positionally on the latest
|
|
560
|
+
// TodoWrite list (ids are 1-based positions when the todo panel owns the list).
|
|
561
|
+
if (taskId && s.taskList?.has(taskId)) {
|
|
550
562
|
if (rawStatus === 'deleted') {
|
|
551
|
-
s.taskList
|
|
563
|
+
s.taskList.delete(taskId);
|
|
552
564
|
if (Array.isArray(s.taskOrder))
|
|
553
565
|
s.taskOrder = s.taskOrder.filter((x) => x !== taskId);
|
|
554
566
|
}
|
|
555
567
|
else if (rawStatus) {
|
|
556
|
-
const existing = s.taskList
|
|
568
|
+
const existing = s.taskList.get(taskId);
|
|
557
569
|
if (existing)
|
|
558
570
|
existing.status = rawStatus;
|
|
559
571
|
}
|
|
@@ -561,6 +573,16 @@ export function handleClaudeEvent(ev, s, emit) {
|
|
|
561
573
|
if (plan)
|
|
562
574
|
emit({ type: 'plan', plan });
|
|
563
575
|
}
|
|
576
|
+
else if (taskId) {
|
|
577
|
+
const updated = applyTaskUpdateToTodoPlan(s.todoPlan, taskId, rawStatus);
|
|
578
|
+
if (updated) {
|
|
579
|
+
s.todoPlan = updated;
|
|
580
|
+
emit({ type: 'plan', plan: updated });
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
const summary = `Update task ${taskId || '?'} → ${rawStatus || 'unknown'}`;
|
|
584
|
+
(s.tools ||= new Map()).set(id, { name, summary });
|
|
585
|
+
emit({ type: 'tool', call: { id, name, summary, input: null, status: 'running' } });
|
|
564
586
|
continue;
|
|
565
587
|
}
|
|
566
588
|
if (name === 'Task' || name === 'Agent') {
|
|
@@ -641,13 +663,16 @@ export function handleClaudeEvent(ev, s, emit) {
|
|
|
641
663
|
if (plan)
|
|
642
664
|
emit({ type: 'plan', plan });
|
|
643
665
|
}
|
|
666
|
+
if (tool)
|
|
667
|
+
emit({ type: 'tool', call: { id, name: tool.name, summary: tool.summary, status: b.is_error ? 'failed' : 'done', result: null } });
|
|
644
668
|
continue;
|
|
645
669
|
}
|
|
646
670
|
if (!tool)
|
|
647
671
|
continue;
|
|
648
672
|
const isError = !!b.is_error;
|
|
649
|
-
// File-shaped tools have no useful result detail (mirrors pikiloom): just mark done.
|
|
650
|
-
const fileTool = tool.name === 'Read' || tool.name === 'Edit' || tool.name === 'Write'
|
|
673
|
+
// File-shaped and task-list tools have no useful result detail (mirrors pikiloom): just mark done.
|
|
674
|
+
const fileTool = tool.name === 'Read' || tool.name === 'Edit' || tool.name === 'Write'
|
|
675
|
+
|| tool.name === 'TodoWrite' || tool.name === 'TaskCreate' || tool.name === 'TaskUpdate';
|
|
651
676
|
const detail = (isError || !fileTool) ? firstResultLine(b.content) : null;
|
|
652
677
|
emit({ type: 'tool', call: { id, name: tool.name, summary: tool.summary, status: isError ? 'failed' : 'done', result: detail || null } });
|
|
653
678
|
}
|
|
@@ -960,6 +985,30 @@ export function rebuildClaudeTaskPlan(s) {
|
|
|
960
985
|
}
|
|
961
986
|
return steps.length ? { explanation: null, steps } : null;
|
|
962
987
|
}
|
|
988
|
+
// Apply a TaskUpdate to a TodoWrite-produced plan positionally (taskId = 1-based item index).
|
|
989
|
+
// Used when the id isn't in the TaskCreate store, so an update issued AFTER a TodoWrite still
|
|
990
|
+
// lands on the displayed list — the plan reflects the state after the LAST change, whichever
|
|
991
|
+
// mechanism wrote it. Returns a fresh plan (never mutates) or null when inapplicable.
|
|
992
|
+
export function applyTaskUpdateToTodoPlan(plan, taskId, rawStatus) {
|
|
993
|
+
if (!plan || !Array.isArray(plan.steps) || !plan.steps.length)
|
|
994
|
+
return null;
|
|
995
|
+
if (!/^\d+$/.test(taskId))
|
|
996
|
+
return null;
|
|
997
|
+
const idx = Number(taskId) - 1;
|
|
998
|
+
if (idx < 0 || idx >= plan.steps.length)
|
|
999
|
+
return null;
|
|
1000
|
+
if (rawStatus === 'deleted') {
|
|
1001
|
+
const steps = plan.steps.filter((_, i) => i !== idx);
|
|
1002
|
+
return steps.length ? { explanation: plan.explanation ?? null, steps } : null;
|
|
1003
|
+
}
|
|
1004
|
+
const status = rawStatus === 'completed' ? 'completed'
|
|
1005
|
+
: (rawStatus === 'in_progress' || rawStatus === 'inprogress') ? 'inProgress'
|
|
1006
|
+
: rawStatus === 'pending' ? 'pending' : null;
|
|
1007
|
+
if (!status)
|
|
1008
|
+
return null;
|
|
1009
|
+
const steps = plan.steps.map((step, i) => i === idx ? { ...step, status } : step);
|
|
1010
|
+
return { explanation: plan.explanation ?? null, steps };
|
|
1011
|
+
}
|
|
963
1012
|
// ── Tool-call summarization (ported from pikiloom's summarizeClaudeToolUse) ──────────
|
|
964
1013
|
// Turns a Claude tool_use {name,input} into a one-line human summary. The runtime's
|
|
965
1014
|
// activity projector joins these into snapshot.activity; the structured form lives in
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as n,j as s}from"./react-vendor-C7Sl8SE7.js";import{c as sn,I as ln,S as Ke,m as ze,a as I,u as gt,d as on,g as an,l as un,e as cn,j as dn,f as fn,s as mn,Y as gn}from"./index-BVBpUn_6.js";import{s as pn,l as hn,n as Ot,m as xn,a as kn,o as Sn,d as bn,b as vn,p as In,c as Qe,e as Tn,f as Et,g as $e,h as pt,i as yn,j as Rn,T as jn,R as wn,U as Mt,k as Nn,q as Ct,L as Pt,r as An,I as Ln}from"./index-C3fUMz3j.js";import{M as qt,a as Dt}from"./Modal-CaunzS8l.js";import"./router-DHISdpPk.js";import"./Select-Bur7mVMe.js";import"./DirBrowser-BW7l1k3h.js";import"./markdown-DxQYQFeH.js";import"./ExtensionsTab-CTXhgsep.js";function On({snapshot:o}){const[t,f]=n.useState(o.currentIndex??0),[w,T]=n.useState(""),[x,p]=n.useState(!1),[P,S]=n.useState(null);n.useEffect(()=>{f(o.currentIndex??0),T(""),S(null)},[o.promptId,o.currentIndex]);const O=o.questions||[],y=O[t]||null,J=O.length,U=!!(y?.options&&y.options.length),E=U?!!y?.allowFreeform:!0,de=a=>{a&&(f(i=>i+1),T(""))},fe=async a=>{if(!x){p(!0),S(null);try{const i=await I.interactionSelectOption(o.promptId,a);if(!i.ok){S(i.error||"Failed to submit selection.");return}de(i.advanced)}catch(i){S(i?.message||"Network error.")}finally{p(!1)}}},me=async()=>{if(x)return;const a=w.trim();if(!a&&!y?.allowEmpty){S("Please enter a response.");return}p(!0),S(null);try{const i=await I.interactionSubmitText(o.promptId,a);if(!i.ok){S(i.error||"Failed to submit answer.");return}de(i.advanced)}catch(i){S(i?.message||"Network error.")}finally{p(!1)}},m=async()=>{if(!x){p(!0),S(null);try{const a=await I.interactionSkip(o.promptId);if(!a.ok){S(a.error||"Failed to skip.");return}de(a.advanced)}catch(a){S(a?.message||"Network error.")}finally{p(!1)}}},Z=async()=>{if(!x){p(!0);try{await I.interactionCancel(o.promptId)}catch{}}},ee=n.useMemo(()=>{const a=[];return o.hint&&a.push(o.hint),J>1&&a.push(`Question ${t+1} of ${J}`),a.join(" · ")||void 0},[o.hint,t,J]);return s.jsxs(qt,{open:!0,onClose:Z,wide:U&&(y?.options?.length||0)>3,children:[s.jsx(Dt,{title:o.title||"Pikiloom needs your input",description:ee,onClose:Z}),y?s.jsxs("div",{className:"space-y-4",children:[s.jsxs("div",{children:[s.jsx("div",{className:"text-xs font-medium uppercase tracking-wide text-fg-5",children:y.header||"Question"}),s.jsx("div",{className:"mt-1 whitespace-pre-wrap text-sm leading-relaxed text-fg",children:y.prompt})]}),U&&s.jsx("div",{className:"grid grid-cols-1 gap-2 sm:grid-cols-2",children:(y.options||[]).map(a=>s.jsxs("button",{type:"button",disabled:x,onClick:()=>fe(a.value||a.label),className:sn("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:[s.jsx("div",{className:"font-medium text-fg group-hover:text-fg",children:a.label}),a.description&&s.jsx("div",{className:"mt-0.5 text-xs leading-snug text-fg-4",children:a.description})]},a.value||a.label))}),E&&s.jsx("div",{children:s.jsx(ln,{value:w,onChange:a=>T(a.target.value),onKeyDown:a=>{a.key==="Enter"&&!a.shiftKey&&!x&&(a.preventDefault(),me())},placeholder:U?"Or type a custom answer…":"Type your answer…",disabled:x,autoFocus:!U})}),P&&s.jsx("div",{className:"rounded-md border border-red-300/40 bg-red-500/10 px-3 py-2 text-xs text-red-600",children:P}),s.jsxs("div",{className:"flex items-center justify-between gap-3",children:[s.jsx("div",{className:"text-xs text-fg-5",children:x?s.jsxs("span",{className:"inline-flex items-center gap-2",children:[s.jsx(Ke,{})," Submitting…"]}):s.jsxs("span",{children:["Press ",s.jsx("kbd",{className:"rounded border border-edge bg-panel-alt px-1.5 py-0.5 text-[10px] uppercase",children:"Enter"})," to send"]})}),s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx(ze,{variant:"ghost",size:"sm",onClick:m,disabled:x,children:"Skip"}),E&&s.jsx(ze,{variant:"primary",size:"sm",onClick:me,disabled:x||!w.trim()&&!y.allowEmpty,children:"Submit"})]})]})]}):s.jsxs("div",{className:"py-6 text-center text-sm text-fg-5",children:[s.jsx(Ke,{className:"mr-2 inline-block"})," Waiting for the agent…"]})]})}function En(o){return o.isNull?(o.localStreamPending||o.holdsActiveState)&&!o.holdExpired?"reject-null":"apply":(typeof o.updatedAt=="number"?o.updatedAt:0)<o.lastAppliedUpdatedAt?"reject-stale":"apply"}function Mn(o,t){return typeof t!="number"?o:t>o?t:o}function Cn(o,t){return!o||!o.length?[]:t.size?o.filter(f=>!t.has(f)):o.slice()}function Pn(o,t,f,w){if(!o.size)return o;const T=new Set(t);let x=!1;const p=new Map;for(const[P,S]of o){if(f-S>w||!T.has(P)){x=!0;continue}p.set(P,S)}return x?p:o}const ht=12,Ut=160,Un=96,qn=[],Dn=[],Hn=[],Fn={phase:"streaming",text:"",thinking:""},Bn=20,_n=6e4,Qn=15e3,$n=7e3,ie=new Map;function Kn(o,t){return`${o}:${t}`}function zn(o,t){for(ie.delete(o),ie.set(o,t);ie.size>Bn;)ie.delete(ie.keys().next().value)}const rr=n.memo(function({session:t,workdir:f,active:w=!0,onSessionChange:T,initialPendingPrompt:x,initialPendingImageUrls:p,onPendingPromptConsumed:P}){const S=gt(e=>e.locale),O=gt(e=>e.agentStatus?.agents?.find(r=>r.agent===t.agent)??null),y=O?.selectedEffort??null,J=O?.selectedModel??null,U=gt(e=>e.modelLayer),E=t.profileId?U?.profiles.find(e=>e.id===t.profileId)??null:null,de=E?U?.providers.find(e=>e.id===E.providerId)??null:null,fe=t.profileId===void 0?O?.byokProviderName??null:de?.name??null,me=t.profileId===void 0?O?.byokProfileName??null:E&&E.name.trim().toLowerCase()!==E.modelId.trim().toLowerCase()?E.name:null,m=n.useMemo(()=>on(S),[S]),Z=an(t.agent||""),ee=un(t),a=!!x||!!(p&&p.length),[i,We]=n.useState(null),[Ye,ge]=n.useState(!a),[pe,xt]=n.useState(!1),[c,F]=n.useState(null),[B,he]=n.useState(!1),[q,Ge]=n.useState(null),[Ht,kt]=n.useState(0),[Ft,Ve]=n.useState(null),[D,Re]=n.useState([]),[Bt,je]=n.useState([]),[xe,Xe]=n.useState([]),[h,te]=n.useState(x||null),[M,ne]=n.useState(p||[]),[_t,re]=n.useState(null),_=n.useRef(null);_.current=_t;const St=n.useRef(h);St.current=h;const[we,H]=n.useState([]),Ne=n.useRef([]);Ne.current=we;const ke=n.useRef(null),[Qt,bt]=n.useState(null),[Ae,Se]=n.useState(null),[be,Je]=n.useState(""),[Q,Ze]=n.useState(!1),$t=!!O?.capabilities?.fork,et=n.useRef(null),L=n.useRef(p||[]),$=n.useRef(c),tt=n.useRef(B);$.current=c,tt.current=B;const nt=n.useRef(D);nt.current=D;const vt=n.useRef(q);vt.current=q;const K=n.useRef(null),Le=n.useRef(null),z=n.useRef(!0),se=n.useRef(!1),Oe=n.useRef(null),rt=n.useRef(!1),C=n.useRef(a),le=n.useRef(!1),W=n.useRef(!1),st=n.useRef(!1),lt=n.useRef(!1),Y=n.useRef(null),Ee=n.useRef(0),ve=n.useRef(Date.now()),Ie=n.useRef(new Map),It=n.useRef({model:null,effort:null}),Kt=n.useCallback(e=>{It.current=e},[]);n.useEffect(()=>{st.current||!a||(st.current=!0,x&&!h&&te(x),p&&p.length&&!M.length&&(ne(p),L.current=p),ge(!1),kt(e=>e+1),P?.())},[a,P]);const N=n.useCallback(()=>{te(null),ne(e=>{for(const r of e)URL.revokeObjectURL(r);return[]}),L.current=[],re(null)},[]),G=n.useCallback(()=>{H(e=>{if(!e.length)return e;for(const r of e)for(const l of r.imageUrls)URL.revokeObjectURL(l);return[]}),ke.current=null},[]),Tt=n.useCallback((e,r)=>{ve.current=Date.now();const l=pn({streaming:tt.current,liveStreamPhase:$.current?.phase??null,streamPhase:vt.current,queuedTaskCount:nt.current.length,pendingQueuedCount:Ne.current.length}),u=r||[];if(l){const d=`local-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`;ke.current=d,H(k=>[...k,{localId:d,taskId:null,prompt:e||"",imageUrls:u}]);return}$.current?.phase==="done"&&F(null);for(const d of L.current)URL.revokeObjectURL(d);ke.current=null,te(e||null),ne(u),L.current=u,re(null)},[]),zt=n.useCallback(e=>{const r=ke.current;if(r){ke.current=null,H(l=>{const u=l.findIndex(k=>k.localId===r);if(u<0)return l;const d=l.slice();return d[u]={...d[u],taskId:e},d});return}re(e)},[]),Wt=n.useCallback(async()=>{if(!Ae)return;const e=be.trim();if(e){Ze(!0);try{const r=await I.forkSession(f,t.agent||"",t.sessionId,Ae.atTurn,e,{});if(!r.ok||!r.sessionKey){Ze(!1);return}const[l,u]=r.sessionKey.split(":");Se(null),Je(""),T?.({agent:l,sessionId:u,workdir:f})}finally{Ze(!1)}}},[Ae,be,f,t.agent,t.sessionId,T]);et.current=Wt;const Me=n.useCallback(async(e,r={})=>{try{const l=await hn({workdir:f,agent:t.agent||"",sessionId:t.sessionId,rich:!0,turnOffset:e.turnOffset,turnLimit:e.turnLimit,lastNTurns:e.lastNTurns},{force:r.force});return l.ok?Ot(l):null}catch{return null}},[f,t.agent,t.sessionId]),R=n.useCallback(async({keepOlder:e,force:r=!1,scrollToBottom:l=!1})=>{const u=t.sessionId;if(Oe.current===u)return!1;Oe.current=u;try{const d=await Me({turnOffset:0,turnLimit:ht},{force:r});if(!d||t.sessionId!==u)return!1;if(l&&(se.current=!0),We(k=>!k||!e?d:xn(k,d)),ge(!1),le.current&&(le.current=!1,N()),W.current){const k=W.current;W.current=!1;const V=k!==!0?k.taskId:null;$.current&&(k===!0||$.current.taskId===V)&&F(null)}return!0}finally{Oe.current===u&&(Oe.current=null)}},[Me,N,t.sessionId]),Ce=n.useCallback(async()=>{if(!i?.hasOlder||rt.current)return;const e=K.current;e&&(Le.current={scrollHeight:e.scrollHeight,scrollTop:e.scrollTop}),rt.current=!0,xt(!0);try{const r=await Me({turnOffset:Math.max(0,i.totalTurns-i.startTurn),turnLimit:ht});r?We(l=>l?kn(l,r):r):Le.current=null}finally{rt.current=!1,xt(!1)}},[Me,i]),Pe=n.useRef(null),oe=n.useCallback((e,r="ws")=>{const l=tt.current||nt.current.length>0,u=Date.now()-ve.current>Qn;if(En({updatedAt:e?.updatedAt,isNull:!e,lastAppliedUpdatedAt:Ee.current,localStreamPending:C.current,holdsActiveState:l,holdExpired:u})!=="apply")return;const k=!e&&u&&(C.current||l);if(e&&(ve.current=Date.now(),Ee.current=Mn(Ee.current,e.updatedAt)),e?.sessionId&&e.sessionId!==t.sessionId&&(lt.current=!0,Te.current=`${t.agent}:${e.sessionId}`,T?.({agent:t.agent||"",sessionId:e.sessionId,workdir:f})),!e){const g=Pe.current;he(!1),k?(C.current=!1,le.current=!0,W.current=!0,G(),R({keepOlder:!0,force:!0,scrollToBottom:z.current})):g==="streaming"?(le.current=!0,W.current=!0,R({keepOlder:!0,force:!0,scrollToBottom:z.current})):F(null),k||(C.current&&g!=="streaming"?R({keepOlder:!0,force:!0}):(g==="done"&&(N(),G()),g!==null&&(C.current=!1))),Ve(null),Ge(null),Re([]),je([]),Xe([]),Pe.current=null;return}Ge(e.phase),Ve(e.taskId||null);const V=[];e.taskId&&V.push(e.taskId),Array.isArray(e.queuedTaskIds)&&V.push(...e.queuedTaskIds),Ie.current=Pn(Ie.current,V,Date.now(),_n);const ue=Ie.current,ft=Cn(e.queuedTaskIds,ue),Lt=(Array.isArray(e.queuedTasks)?e.queuedTasks:[]).filter(g=>!ue.has(g.taskId));if(Re(ft.length?ft:qn),je(Lt.length?Lt:Dn),Xe(Array.isArray(e.interactions)&&e.interactions.length?e.interactions:Hn),Sn({pendingTaskId:_.current,streamTaskId:e.taskId||null,queuedTaskIds:e.queuedTaskIds})){const g=_.current,j=St.current||"",v=L.current;H(b=>b.some(ce=>ce.taskId===g)?b:[...b,{localId:`demote-${g}`,taskId:g,prompt:j,imageUrls:v}]),te(null),ne([]),L.current=[],re(null),_.current=null}if(e.phase==="streaming"){if(F({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,artifacts:e.artifacts??null,startedAt:typeof e.startedAt=="number"?e.startedAt:null,error:null,question:e.question??null,questionBlocks:e.questionBlocks??null}),he(!0),e.taskId&&e.taskId!==_.current){const g=Ne.current,j=g.findIndex(v=>v.taskId===e.taskId);if(j>=0){const v=g[j];for(const b of L.current)URL.revokeObjectURL(b);te(v.prompt||null),ne(v.imageUrls),L.current=v.imageUrls,re(e.taskId),H(b=>b.filter((ce,X)=>X!==j))}}z.current&&(se.current=!0)}else if(e.phase==="queued")F(null),he(!1);else if(e.phase==="done"){const g=bn($.current?.taskId??null,e.taskId||null),j=ft.length>0;if(g){he(!1),F(X=>X?{...X,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,artifacts:e.artifacts??null,error:e.error,question:e.question??null,questionBlocks:e.questionBlocks??null}:X);const v=$.current,b=!!v&&vn(v),ce=!!e.incomplete&&b&&!j;if(Pe.current!=="done"){j||(le.current=!0),W.current=ce?!1:{taskId:e.taskId||null},R({keepOlder:!0,force:!0,scrollToBottom:z.current});const X=t.agent||"",tn=t.sessionId,nn=Te.current;Y.current&&clearTimeout(Y.current),Y.current=setTimeout(()=>{Y.current=null,I.getSessionStreamState(X,tn).then(rn=>{Te.current===nn&&Ue.current(rn.state,"seed")}).catch(()=>{})},900)}}j||(C.current=!1)}const mt=new Set;if(e.taskId&&mt.add(e.taskId),Array.isArray(e.queuedTaskIds))for(const g of e.queuedTaskIds)mt.add(g);H(g=>{let j=!1;const v=[];for(const b of g)if(!b.taskId||mt.has(b.taskId))v.push(b);else{for(const ce of b.imageUrls)URL.revokeObjectURL(ce);j=!0}return j?v:g}),Pe.current=e.phase},[N,G,R,t.sessionId,t.agent,T,f]),Ue=n.useRef(oe);Ue.current=oe;const yt=n.useCallback(()=>{C.current=!0,ve.current=Date.now(),kt(e=>e+1)},[]);n.useEffect(()=>()=>{Y.current&&(clearTimeout(Y.current),Y.current=null)},[]);const Yt=n.useCallback(async e=>{try{Ie.current.set(e,Date.now()),await I.recallSessionMessage(e),_.current===e&&N(),H(r=>{let l=!1;const u=[];for(const d of r)if(d.taskId===e){for(const k of d.imageUrls)URL.revokeObjectURL(k);l=!0}else u.push(d);return l?u:r}),Re(r=>r.filter(l=>l!==e)),je(r=>r.filter(l=>l.taskId!==e)),Ve(r=>r===e?null:r)}catch{}},[N]),Gt=n.useCallback(async e=>{const r=Ne.current.find(l=>l.taskId===e)||null;if(r&&r.imageUrls.length>0){for(const l of L.current)URL.revokeObjectURL(l);te(r.prompt||null),ne(r.imageUrls),L.current=r.imageUrls,re(e),_.current=e,H(l=>l.filter(u=>u.taskId!==e))}try{await I.steerSession(e)}catch{}},[]),Vt=n.useCallback(async()=>{try{await I.stopSession(t.agent||"",t.sessionId)}catch{}},[t.agent,t.sessionId]),qe=Kn(t.agent||"",t.sessionId);n.useEffect(()=>{if(lt.current){lt.current=!1;let d=!1;return R({keepOlder:!0,force:!0}).finally(()=>{d||ge(!1)}),()=>{d=!0}}let e=!1;const r=In({workdir:f,agent:t.agent||"",sessionId:t.sessionId,rich:!0,turnOffset:0,turnLimit:ht},{allowStale:!0}),l=a&&!st.current,u=r?.ok?Ot(r):ie.get(qe)||null;return ge(l?!1:!u),We(u),F(null),he(!1),Ge(null),Re([]),je([]),Xe([]),Ee.current=0,ve.current=Date.now(),Ie.current=new Map,l||(N(),G(),C.current=!1,le.current=!1,W.current=!1),z.current=!0,se.current=!0,l||R({keepOlder:!1,force:!0}).finally(()=>{e||ge(!1)}),()=>{e=!0}},[R,t.agent,t.sessionId,f,qe,N,G]),n.useEffect(()=>{i&&i.turns.length>0&&zn(qe,i)},[qe,i]),n.useEffect(()=>{w&&R({keepOlder:!0,force:!0})},[w,R]);const Te=n.useRef(`${t.agent}:${t.sessionId}`);Te.current=`${t.agent}:${t.sessionId}`,cn("stream-update",n.useCallback(e=>{e.key===Te.current&&oe(e.snapshot??null)},[oe])),n.useEffect(()=>{let e=!0;return I.getSessionStreamState(t.agent||"",t.sessionId).then(r=>{e&&Ue.current(r.state,"seed")}).catch(()=>{}),()=>{e=!1}},[t.agent,t.sessionId,Ht]),dn(n.useCallback(()=>{I.getSessionStreamState(t.agent||"",t.sessionId).then(e=>{oe(e.state,"seed")}).catch(()=>{}),R({keepOlder:!0,force:!0})},[oe,t.agent,t.sessionId,R]));const Rt=B||!!q||D.length>0||!!h||we.length>0;n.useEffect(()=>{if(!w||!Rt)return;const e=setInterval(()=>{typeof document<"u"&&document.visibilityState!=="visible"||I.getSessionStreamState(t.agent||"",t.sessionId).then(r=>Ue.current(r.state,"seed")).catch(()=>{})},$n);return()=>clearInterval(e)},[w,Rt,t.agent,t.sessionId]),n.useEffect(()=>{!C.current&&ee!=="running"&&!B&&!c&&!q&&D.length===0&&(N(),G())},[ee,B,c,q,D.length,N,G]),n.useLayoutEffect(()=>{const e=Le.current,r=K.current;!e||!r||(Le.current=null,r.scrollTop=e.scrollTop+(r.scrollHeight-e.scrollHeight))},[i?.turns.length]),n.useLayoutEffect(()=>{if(!se.current)return;const e=K.current;e&&(se.current=!1,e.scrollTop=e.scrollHeight,requestAnimationFrame(()=>{z.current&&(e.scrollTop=e.scrollHeight)}))},[i,c]),n.useLayoutEffect(()=>{if(!h)return;const e=K.current;e&&(e.scrollTop=e.scrollHeight)},[h]),n.useEffect(()=>{if(!i?.hasOlder||Ye||pe)return;const e=K.current;e&&e.scrollHeight<=e.clientHeight+Ut&&Ce()},[i?.hasOlder,i?.turns.length,Ce,Ye,pe]);const Xt=n.useCallback(()=>{const e=K.current;if(!e)return;const r=e.scrollHeight-e.scrollTop-e.clientHeight;z.current=r<=Un,e.scrollTop<=Ut&&Ce()},[Ce]),ye=c?.model||t.model||J||null,De=fn(t.agent||"",c?.effort||t.thinkingEffort||y||null,t.workflowEnabled??O?.workflowEnabled)||null,ot=me&&(!ye||ye===J)?me:ye?mn(ye):null,at=gn(t,{streaming:B,hasLiveStream:!!c,streamPhase:q,queuedTaskCount:D.length}),A=i?.turns||[],He=n.useMemo(()=>{if(!M.length||!A.length)return!1;const e=A[A.length-1];return!e.user||!Qe(e.user.text,h)?!1:e.user.blocks.filter(l=>l.type==="image").length<M.length},[A,h,M.length]),ut=n.useMemo(()=>M.length?M.map(e=>({type:"image",content:e})):!h||!c?.questionBlocks?.length?[]:Qe(h,c.question)?c.questionBlocks:[],[M,h,c]),jt=c?.question||null,Fe=Tn(h,jt),ct=Et(jt,h),wt=A.length>0?A[A.length-1]?.user?.text:null,Nt=!!Fe&&A.length>0&&$e(wt,Fe),ae=n.useMemo(()=>{let e=A;if(He){const ue=e[e.length-1];e=[...e.slice(0,-1),{...ue,user:null}]}if(!c||!e.length)return e;const r=e[e.length-1],l=Fe;if(!r.assistant)return!!r.user&&!!l&&!Qe(r.user.text,l)&&($e(r.user.text,l)||Et(l,r.user.text))?[...e.slice(0,-1),{...r,user:{...r.user,text:l}}]:e;const u=(c.text||"").trim(),d=r.assistant.text?.trim()||"";if(!(l!=null?$e(r.user?.text,l):!!d&&!!u&&(u.startsWith(d)||d.startsWith(u))))return e;const V=r.user&&l&&!Qe(r.user.text,l)?{...r.user,text:l}:r.user;return[...e.slice(0,-1),{...r,user:V,assistant:null}]},[A,c,Fe,He]),Jt=(!!h||ut.length>0)&&!ct&&(He||!Nt)&&!c,Zt=!!c&&pt(c)&&c.phase==="streaming",it=yn({sessionRunning:ee==="running",streaming:B,streamPhase:q,queuedTaskCount:D.length,pendingQueuedCount:we.length,liveTurnStreaming:Zt,pendingBubbleDots:Jt}),Be=it&&ae.length>0?ae[ae.length-1]:null,_e=!!Be?.assistant&&Rn(Be.assistant),dt=_e?Be.assistant.usage??null:null,en=_e?Be.assistant.blocks.filter(e=>e.type==="tool_use").length:0,At=(()=>{if(!it||ee!=="running")return null;const e=t.runUpdatedAt?Date.parse(t.runUpdatedAt):NaN;return Number.isFinite(e)?e:null})();return s.jsxs("div",{className:"flex flex-col h-full overflow-hidden",children:[s.jsx("div",{ref:K,onScroll:Xt,className:"flex-1 overflow-y-auto overscroll-contain",children:Ye&&!h&&!M.length&&!c?s.jsx("div",{className:"flex items-center justify-center py-20",children:s.jsx(Ke,{className:"h-5 w-5 text-fg-4"})}):ae.length===0&&!h&&!M.length&&!c&&!at?s.jsx("div",{className:"py-20 text-center text-[13px] text-fg-5",children:m("hub.noMessages")}):s.jsxs("div",{className:"max-w-[900px] mx-auto px-6 py-6 space-y-0",children:[(i?.hasOlder||pe)&&s.jsxs("div",{className:"mb-4 flex items-center justify-center gap-2 text-[11px] text-fg-5",children:[pe?s.jsx(Ke,{className:"h-3 w-3 text-fg-5"}):s.jsx("span",{className:"h-1.5 w-1.5 rounded-full bg-fg-5/35"}),s.jsx("span",{children:m(pe?"hub.loadingOlderTurns":"hub.loadOlderTurnsHint")})]}),t.migratedFrom?.kind==="fork"&&t.migratedFrom.sessionId&&s.jsxs("button",{type:"button",onClick:()=>T?.({agent:t.migratedFrom.agent||t.agent||"",sessionId:t.migratedFrom.sessionId,workdir:f}),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:`#${t.migratedFrom.sessionId.slice(0,8)}`,children:[s.jsxs("svg",{width:"10",height:"10",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[s.jsx("circle",{cx:"6",cy:"6",r:"2"}),s.jsx("circle",{cx:"18",cy:"6",r:"2"}),s.jsx("circle",{cx:"12",cy:"20",r:"2"}),s.jsx("path",{d:"M6 8v3a3 3 0 0 0 3 3h6a3 3 0 0 0 3-3V8"}),s.jsx("path",{d:"M12 14v4"})]}),s.jsx("span",{children:m("hub.forkBadge")}),s.jsxs("span",{className:"font-mono",children:["#",t.migratedFrom.sessionId.slice(0,8)]}),typeof t.migratedFrom.forkedAtTurn=="number"&&s.jsxs("span",{className:"text-fg-5/70",children:["· ",m("hub.forkBadgeAt").replace("{turn}",String(t.migratedFrom.forkedAtTurn+1))]})]}),ae.map((e,r)=>{const l=(i?.startTurn||0)+r;return s.jsx(jn,{turn:e,turnIndex:l,hideHeaderUsage:_e&&r===ae.length-1,agent:t.agent||"",meta:Z,model:ot,effort:De,providerName:fe,t:m,workdir:f,onResend:u=>{se.current=!0,Tt(u);const d=It.current;I.sendSessionMessage(f,t.agent||"",t.sessionId,u,{model:d.model||ye||void 0,effort:d.effort||De||void 0}).then(k=>{k.ok&&yt()}).catch(()=>{N()})},onEdit:u=>bt(u),onFork:$t?u=>{Je(""),Se({atTurn:u})}:void 0},`${i?.startTurn||0}:${r}`)}),at&&s.jsx("div",{className:"mb-5 animate-in",children:s.jsx(wn,{detail:at,t:m})}),(h||ut.length>0)&&!ct&&(He||!Nt)&&s.jsxs("div",{className:"session-turn",children:[s.jsx(Mt,{text:h||"",blocks:ut,t:m}),!c&&s.jsx("div",{className:"mt-3 mb-5 animate-in",children:s.jsx(Nn,{className:"text-fg-5"})})]}),c&&pt(c)&&c.question&&(!h||ct)&&!(A.length>0&&$e(wt,c.question))&&s.jsx("div",{className:"session-turn",children:s.jsx(Mt,{text:c.question,blocks:c.questionBlocks||void 0,t:m})}),c&&pt(c)&&s.jsxs("div",{className:"mb-6",children:[s.jsx(Ct,{agent:t.agent||"",meta:Z,model:ot,effort:De,providerName:fe,previewMeta:c.previewMeta,hideContextUsage:!0}),s.jsx(Pt,{stream:c,t:m,workdir:f})]}),it&&(_e?s.jsx("div",{className:"-mt-3 mb-6 animate-in",children:s.jsx(An,{toolCount:en,ctxPct:dt?.contextPercent??null,ctxTokens:dt?.contextUsedTokens??0,turnOutTokens:dt?.turnOutputTokens??0,startedAt:At})}):s.jsxs("div",{className:"mb-6",children:[s.jsx(Ct,{agent:t.agent||"",meta:Z,model:ot,effort:De,providerName:fe,hideContextUsage:!0}),s.jsx(Pt,{stream:{...Fn,startedAt:At},t:m,workdir:f})]})),s.jsx("div",{className:"h-4"})]})}),s.jsx(Ln,{session:t,workdir:f,onStreamQueued:yt,onSendStart:Tt,onSendTaskAssigned:zt,onSessionChange:T,t:m,streamPhase:q,streamTaskId:Ft,queuedTaskIds:D,queuedTasks:Bt,pendingQueuedSends:we,onRecall:Yt,onSteer:Gt,onStopAll:Vt,editDraft:Qt,onEditDraftConsumed:()=>bt(null),onSelectionChange:Kt}),Ae&&s.jsxs(qt,{open:!0,onClose:()=>{Q||Se(null)},children:[s.jsx(Dt,{title:m("hub.forkPromptTitle"),description:m("hub.forkPromptHint"),onClose:()=>{Q||Se(null)}}),s.jsx("textarea",{autoFocus:!0,value:be,disabled:Q,onChange:e=>Je(e.target.value),onKeyDown:e=>{e.key==="Enter"&&(e.metaKey||e.ctrlKey)&&be.trim()&&!Q&&(e.preventDefault(),et.current?.())},placeholder:m("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"}),s.jsxs("div",{className:"mt-4 flex items-center justify-end gap-2",children:[s.jsx(ze,{variant:"ghost",disabled:Q,onClick:()=>Se(null),children:m("modal.cancel")}),s.jsx(ze,{variant:"primary",disabled:Q||!be.trim(),onClick:()=>{et.current?.()},children:m(Q?"hub.forkSubmitting":"hub.forkSubmit")})]})]}),w&&xe.length>0&&s.jsx(On,{snapshot:xe[xe.length-1]},xe[xe.length-1].promptId)]})});export{rr as SessionPanel};
|