pikiloom 0.4.46 → 0.4.48
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-B8V2eV_D.js → AgentTab-B3lWNXnI.js} +1 -1
- package/dashboard/dist/assets/{ConnectionModal-BOVwXLJB.js → ConnectionModal-BBxZiex0.js} +1 -1
- package/dashboard/dist/assets/{DirBrowser-DbeAWYiL.js → DirBrowser-BaPa173n.js} +1 -1
- package/dashboard/dist/assets/{ExtensionsTab-BMS3PW9N.js → ExtensionsTab-CrQgsSUZ.js} +1 -1
- package/dashboard/dist/assets/{IMAccessTab-B1sRgJk3.js → IMAccessTab-BrjEDiUT.js} +1 -1
- package/dashboard/dist/assets/{Modal-DCwGz46r.js → Modal-GPR6mupI.js} +1 -1
- package/dashboard/dist/assets/{Modals-Bh_0en5P.js → Modals-gHblG4YD.js} +1 -1
- package/dashboard/dist/assets/{Select-DC6zguGC.js → Select-BANiu3wO.js} +1 -1
- package/dashboard/dist/assets/SessionPanel-BxHxCMiG.js +1 -0
- package/dashboard/dist/assets/{SystemTab-uFqZinIZ.js → SystemTab-Dd1qR0vo.js} +1 -1
- package/dashboard/dist/assets/{index-DwmXPtDd.css → index-BcPBJtn2.css} +1 -1
- package/dashboard/dist/assets/{index-DooLxbPX.js → index-DWNgWDr7.js} +15 -15
- package/dashboard/dist/assets/index-YNK_eNh1.js +3 -0
- package/dashboard/dist/assets/{shared-Byy2BNLq.js → shared-A44PMrg2.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dist/agent/drivers/claude.js +5 -1
- package/dist/agent/kernel-bridge.js +3 -1
- package/dist/agent/stream.js +3 -3
- package/dist/bot/bot.js +21 -3
- package/dist/bot/render-shared.js +49 -6
- package/package.json +1 -1
- package/packages/kernel/dist/drivers/claude.d.ts +2 -0
- package/packages/kernel/dist/drivers/claude.js +58 -1
- package/packages/kernel/dist/index.d.ts +1 -1
- package/packages/kernel/dist/index.js +1 -1
- package/dashboard/dist/assets/SessionPanel-DNlLd-PL.js +0 -1
- package/dashboard/dist/assets/index-CL5H13Cl.js +0 -3
|
@@ -93,16 +93,59 @@ export function trimActivityForPreview(text, maxChars = 900) {
|
|
|
93
93
|
return tail.join('\n');
|
|
94
94
|
return [ellipsis, ...tail].join('\n');
|
|
95
95
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
function usageWindowResetSeconds(window, now) {
|
|
97
|
+
if (window.resetAt) {
|
|
98
|
+
const resetAtMs = Date.parse(window.resetAt);
|
|
99
|
+
if (Number.isFinite(resetAtMs))
|
|
100
|
+
return Math.round((resetAtMs - now) / 1000);
|
|
101
|
+
}
|
|
102
|
+
return window.resetAfterSeconds;
|
|
103
|
+
}
|
|
104
|
+
function formatUsageWindowReset(window, now) {
|
|
105
|
+
const seconds = usageWindowResetSeconds(window, now);
|
|
106
|
+
if (seconds == null || !Number.isFinite(seconds))
|
|
107
|
+
return null;
|
|
108
|
+
if (seconds <= 0)
|
|
109
|
+
return 'reset now';
|
|
110
|
+
return `reset ${formatUsageResetDuration(seconds)}`;
|
|
111
|
+
}
|
|
112
|
+
function formatUsageResetDuration(seconds) {
|
|
113
|
+
const wholeSeconds = Math.max(0, Math.round(seconds));
|
|
114
|
+
if (wholeSeconds < 60)
|
|
115
|
+
return `${wholeSeconds}s`;
|
|
116
|
+
const minutes = Math.max(1, Math.round(wholeSeconds / 60));
|
|
117
|
+
if (minutes < 60)
|
|
118
|
+
return `${minutes}m`;
|
|
119
|
+
const hours = Math.floor(minutes / 60);
|
|
120
|
+
const remMinutes = minutes % 60;
|
|
121
|
+
if (hours < 24)
|
|
122
|
+
return remMinutes ? `${hours}h${remMinutes}m` : `${hours}h`;
|
|
123
|
+
const days = Math.floor(hours / 24);
|
|
124
|
+
const remHours = hours % 24;
|
|
125
|
+
return remHours ? `${days}d${remHours}h` : `${days}d`;
|
|
126
|
+
}
|
|
127
|
+
function formatUsageWindowSummary(window, now) {
|
|
128
|
+
const percent = window.usedPercent != null ? `${Math.round(window.usedPercent)}%` : null;
|
|
129
|
+
const reset = formatUsageWindowReset(window, now);
|
|
130
|
+
const main = percent ? `${window.label} ${percent}` : window.label;
|
|
131
|
+
return reset ? `${main} (${reset})` : main;
|
|
132
|
+
}
|
|
133
|
+
// Compact "5h 42% (reset 3h12m) · 7d 18% (reset 4d6h)" summary of a usage
|
|
134
|
+
// snapshot, or a short reason when no quota numbers are available.
|
|
135
|
+
export function formatUsageWindowsSummary(usage, now = Date.now()) {
|
|
99
136
|
if (!usage || !usage.ok)
|
|
100
137
|
return 'unavailable';
|
|
101
138
|
const parts = usage.windows
|
|
102
139
|
.filter(w => w.usedPercent != null)
|
|
103
|
-
.map(w =>
|
|
140
|
+
.map(w => formatUsageWindowSummary(w, now));
|
|
104
141
|
if (parts.length)
|
|
105
142
|
return parts.join(' · ');
|
|
143
|
+
const resetWindow = usage.windows.find(w => usageWindowResetSeconds(w, now) != null);
|
|
144
|
+
if (resetWindow) {
|
|
145
|
+
const reset = formatUsageWindowReset(resetWindow, now);
|
|
146
|
+
const status = usage.status ? `status=${usage.status}` : resetWindow.label;
|
|
147
|
+
return reset ? `${status} (${reset})` : status;
|
|
148
|
+
}
|
|
106
149
|
return usage.status ? `status=${usage.status}` : 'no data';
|
|
107
150
|
}
|
|
108
151
|
// Freshest capturedAt (ISO string) across the given agents' usage and their accounts' usage, or
|
|
@@ -143,11 +186,11 @@ export function buildUsageOverviewLines(overview, now = Date.now()) {
|
|
|
143
186
|
if (agent.accounts.length) {
|
|
144
187
|
for (const account of agent.accounts) {
|
|
145
188
|
const mark = account.active ? '●' : '○';
|
|
146
|
-
lines.push({ text: ` ${mark} ${account.label}: ${formatUsageWindowsSummary(account.usage)}` });
|
|
189
|
+
lines.push({ text: ` ${mark} ${account.label}: ${formatUsageWindowsSummary(account.usage, now)}` });
|
|
147
190
|
}
|
|
148
191
|
}
|
|
149
192
|
else {
|
|
150
|
-
lines.push({ text: ` ${formatUsageWindowsSummary(agent.usage)}` });
|
|
193
|
+
lines.push({ text: ` ${formatUsageWindowsSummary(agent.usage, now)}` });
|
|
151
194
|
}
|
|
152
195
|
}
|
|
153
196
|
return lines;
|
package/package.json
CHANGED
|
@@ -32,6 +32,8 @@ export declare function claudeEffectiveContextWindow(advertised: number | null):
|
|
|
32
32
|
export declare function handleClaudeEvent(ev: any, s: any, emit: (e: DriverEvent) => void): void;
|
|
33
33
|
export declare function claudeBgHoldCapMs(): number;
|
|
34
34
|
export declare function claudeBgSettleQuietMs(): number;
|
|
35
|
+
export declare function claudeModelStallMs(): number;
|
|
36
|
+
export declare function claudeUserEventHasToolResult(ev: any): boolean;
|
|
35
37
|
export declare function isTerminalTaskStatus(status: unknown): boolean;
|
|
36
38
|
export declare function trackClaudeBackgroundTask(ev: any, s: any): void;
|
|
37
39
|
export declare function markClaudeTaskNotificationTerminal(content: any, s: any): void;
|
|
@@ -77,6 +77,10 @@ export class ClaudeDriver {
|
|
|
77
77
|
// wake-up turns can still land before we close (see claudeBgSettleQuietMs).
|
|
78
78
|
let holdCapTimer = null;
|
|
79
79
|
let quietTimer = null;
|
|
80
|
+
// modelStall: post-tool watchdog — fires only while the turn is waiting on the MODEL (a
|
|
81
|
+
// tool_result handed control back and no reply is streaming), never while a tool or
|
|
82
|
+
// background task is still running (see armModelStall).
|
|
83
|
+
let modelStallTimer = null;
|
|
80
84
|
const usageOf = () => this.usage(state);
|
|
81
85
|
const unref = (tm) => { if (tm && typeof tm.unref === 'function')
|
|
82
86
|
tm.unref(); };
|
|
@@ -88,6 +92,10 @@ export class ClaudeDriver {
|
|
|
88
92
|
clearTimeout(quietTimer);
|
|
89
93
|
quietTimer = null;
|
|
90
94
|
} };
|
|
95
|
+
const clearModelStall = () => { if (modelStallTimer) {
|
|
96
|
+
clearTimeout(modelStallTimer);
|
|
97
|
+
modelStallTimer = null;
|
|
98
|
+
} };
|
|
91
99
|
// kill=true SIGTERMs immediately — fast exit, used once nothing is left running in the
|
|
92
100
|
// background (a normal turn, or a wake-up turn after every background task finished).
|
|
93
101
|
// kill=false only ends stdin and lets Claude shut down on its own, so any still-running
|
|
@@ -99,6 +107,7 @@ export class ClaudeDriver {
|
|
|
99
107
|
settled = true;
|
|
100
108
|
clearHoldCap();
|
|
101
109
|
clearQuiet();
|
|
110
|
+
clearModelStall();
|
|
102
111
|
try {
|
|
103
112
|
child?.stdin?.end();
|
|
104
113
|
}
|
|
@@ -121,7 +130,7 @@ export class ClaudeDriver {
|
|
|
121
130
|
resolve(r);
|
|
122
131
|
};
|
|
123
132
|
const settleResult = (opts = {}) => finish({
|
|
124
|
-
ok: !state.error, text: state.text, reasoning: state.reasoning || undefined,
|
|
133
|
+
ok: opts.ok ?? !state.error, text: state.text, reasoning: state.reasoning || undefined,
|
|
125
134
|
error: state.error, stopReason: opts.stopReason ?? state.stopReason, sessionId: state.sessionId, usage: usageOf(),
|
|
126
135
|
}, opts.kill ?? true);
|
|
127
136
|
// Absolute cap while holding for a still-running background task (stopReason marks it as
|
|
@@ -141,6 +150,29 @@ export class ClaudeDriver {
|
|
|
141
150
|
settleResult({ kill: false }); }, claudeBgSettleQuietMs());
|
|
142
151
|
unref(quietTimer);
|
|
143
152
|
};
|
|
153
|
+
// Post-tool model-stall watchdog: after a tool_result hands control back to the model, the
|
|
154
|
+
// model normally streams its next message within a couple of seconds. If instead it goes
|
|
155
|
+
// fully silent — no stream/assistant events — the turn is stuck waiting on the MODEL (a
|
|
156
|
+
// provider stall / rate-limit backoff), NOT on a tool (a running tool has no tool_result
|
|
157
|
+
// yet, so this is never armed then) and NOT on background work (its own hold, re-checked
|
|
158
|
+
// below). Left alone the kernel turn hangs forever with the answer never delivered. Bound
|
|
159
|
+
// it: settle gracefully (no kill) as incomplete with stopReason 'stalled' so the terminal
|
|
160
|
+
// shows a clear "resend to continue" note instead of a dead spinner. Armed on tool_result,
|
|
161
|
+
// cleared the moment the model emits anything.
|
|
162
|
+
const armModelStall = () => {
|
|
163
|
+
clearModelStall();
|
|
164
|
+
modelStallTimer = setTimeout(() => {
|
|
165
|
+
if (settled)
|
|
166
|
+
return;
|
|
167
|
+
// Background work legitimately produces no stream events; keep waiting (the bg hold owns it).
|
|
168
|
+
if (pendingClaudeBackgroundTasks(state) > 0) {
|
|
169
|
+
armModelStall();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
settleResult({ stopReason: 'stalled', kill: false, ok: false });
|
|
173
|
+
}, claudeModelStallMs());
|
|
174
|
+
unref(modelStallTimer);
|
|
175
|
+
};
|
|
144
176
|
try {
|
|
145
177
|
child = spawn(this.bin, args, { cwd: input.workdir, env: { ...process.env, ...(input.env || {}) }, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
146
178
|
}
|
|
@@ -190,7 +222,11 @@ export class ClaudeDriver {
|
|
|
190
222
|
}
|
|
191
223
|
handleClaudeEvent(ev, state, ctx.emit);
|
|
192
224
|
const pending = pendingClaudeBackgroundTasks(state);
|
|
225
|
+
// Any model output means the model is alive and streaming — cancel the post-tool stall watchdog.
|
|
226
|
+
if (ev.type === 'stream_event' || ev.type === 'assistant')
|
|
227
|
+
clearModelStall();
|
|
193
228
|
if (ev.type === 'result') {
|
|
229
|
+
clearModelStall();
|
|
194
230
|
const hasError = !!ev.is_error || (Array.isArray(ev.errors) && ev.errors.length > 0) || !!state.error;
|
|
195
231
|
const decision = decideClaudeResultSettle({ hasError, pendingBackground: pending, sawBackground: state.bgStarted.size > 0 });
|
|
196
232
|
if (decision === 'settle') {
|
|
@@ -221,6 +257,10 @@ export class ClaudeDriver {
|
|
|
221
257
|
else
|
|
222
258
|
armQuiet();
|
|
223
259
|
}
|
|
260
|
+
// A tool_result handed control back to the model with no background pending — arm the
|
|
261
|
+
// post-tool stall watchdog. Cleared above the moment the model's next event streams in.
|
|
262
|
+
if (ev.type === 'user' && pending === 0 && claudeUserEventHasToolResult(ev))
|
|
263
|
+
armModelStall();
|
|
224
264
|
}
|
|
225
265
|
});
|
|
226
266
|
child.stderr.on('data', (c) => { stderr += c.toString('utf8'); });
|
|
@@ -559,6 +599,23 @@ export function claudeBgSettleQuietMs() {
|
|
|
559
599
|
const raw = Number(process.env.PIKILOOM_CLAUDE_BG_SETTLE_QUIET_MS);
|
|
560
600
|
return Number.isFinite(raw) && raw > 0 ? raw : CLAUDE_BG_SETTLE_QUIET_DEFAULT_MS;
|
|
561
601
|
}
|
|
602
|
+
// How long the model may stay COMPLETELY silent after a tool_result (control handed back to it,
|
|
603
|
+
// no background pending) before the driver gives up and settles the turn as 'stalled'. Deliberately
|
|
604
|
+
// generous: legitimate silent extended-thinking (subscription accounts stream no thinking text) and
|
|
605
|
+
// slow providers must not trip it, and a still-running tool never trips it (it has no tool_result
|
|
606
|
+
// yet). This is the backstop for a turn that would otherwise hang forever with the answer never
|
|
607
|
+
// delivered. Override with PIKILOOM_CLAUDE_MODEL_STALL_MS.
|
|
608
|
+
const CLAUDE_MODEL_STALL_DEFAULT_MS = 120_000;
|
|
609
|
+
export function claudeModelStallMs() {
|
|
610
|
+
const raw = Number(process.env.PIKILOOM_CLAUDE_MODEL_STALL_MS);
|
|
611
|
+
return Number.isFinite(raw) && raw > 0 ? raw : CLAUDE_MODEL_STALL_DEFAULT_MS;
|
|
612
|
+
}
|
|
613
|
+
// True when a claude `type:'user'` stream event carries at least one tool_result block — i.e. the
|
|
614
|
+
// tool loop just handed control back to the model. Pure + exported for hermetic testing.
|
|
615
|
+
export function claudeUserEventHasToolResult(ev) {
|
|
616
|
+
const c = ev?.message?.content;
|
|
617
|
+
return Array.isArray(c) && c.some((b) => b?.type === 'tool_result');
|
|
618
|
+
}
|
|
562
619
|
// After we settle a held turn gracefully (stdin closed, no kill), force-kill the lingering
|
|
563
620
|
// process only if it hasn't exited on its own within this window. Backstop against leaks.
|
|
564
621
|
const CLAUDE_EXIT_LEAK_GUARD_MS = 15_000;
|
|
@@ -9,7 +9,7 @@ export type { SessionStore, CoreSessionRecord, ModelResolver, ModelInjection, To
|
|
|
9
9
|
export type { LoomIO, PromptInput, Surface, SurfaceCapabilities, Plugin, SpawnContribution, } from './contracts/surface.js';
|
|
10
10
|
export { FsSessionStore, NullModelResolver, NoopToolProvider, PassthroughSystemPromptBuilder, AutoCancelInteractionHandler, DeferToTerminalInteractionHandler, NoopCatalog, defaultBaseDir, } from './ports/defaults.js';
|
|
11
11
|
export { EchoDriver } from './drivers/echo.js';
|
|
12
|
-
export { ClaudeDriver, isTerminalTaskStatus, trackClaudeBackgroundTask, pendingClaudeBackgroundTasks, markClaudeTaskNotificationTerminal, decideClaudeResultSettle, claudeBgHoldCapMs, claudeBgSettleQuietMs, type ClaudeResultSettleDecision, } from './drivers/claude.js';
|
|
12
|
+
export { ClaudeDriver, isTerminalTaskStatus, trackClaudeBackgroundTask, pendingClaudeBackgroundTasks, markClaudeTaskNotificationTerminal, decideClaudeResultSettle, claudeBgHoldCapMs, claudeBgSettleQuietMs, type ClaudeResultSettleDecision, claudeModelStallMs, claudeUserEventHasToolResult, } from './drivers/claude.js';
|
|
13
13
|
export { CodexDriver } from './drivers/codex.js';
|
|
14
14
|
export { GeminiDriver } from './drivers/gemini.js';
|
|
15
15
|
export { AcpDriver, type AcpDriverConfig } from './drivers/acp.js';
|
|
@@ -19,7 +19,7 @@ export { attachTui } from './runtime/tui.js';
|
|
|
19
19
|
export { FsSessionStore, NullModelResolver, NoopToolProvider, PassthroughSystemPromptBuilder, AutoCancelInteractionHandler, DeferToTerminalInteractionHandler, NoopCatalog, defaultBaseDir, } from './ports/defaults.js';
|
|
20
20
|
// Drivers & surfaces (also available via subpath exports)
|
|
21
21
|
export { EchoDriver } from './drivers/echo.js';
|
|
22
|
-
export { ClaudeDriver, isTerminalTaskStatus, trackClaudeBackgroundTask, pendingClaudeBackgroundTasks, markClaudeTaskNotificationTerminal, decideClaudeResultSettle, claudeBgHoldCapMs, claudeBgSettleQuietMs, } from './drivers/claude.js';
|
|
22
|
+
export { ClaudeDriver, isTerminalTaskStatus, trackClaudeBackgroundTask, pendingClaudeBackgroundTasks, markClaudeTaskNotificationTerminal, decideClaudeResultSettle, claudeBgHoldCapMs, claudeBgSettleQuietMs, claudeModelStallMs, claudeUserEventHasToolResult, } from './drivers/claude.js';
|
|
23
23
|
export { CodexDriver } from './drivers/codex.js';
|
|
24
24
|
export { GeminiDriver } from './drivers/gemini.js';
|
|
25
25
|
export { AcpDriver } from './drivers/acp.js';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as t,j as n}from"./react-vendor-C7Sl8SE7.js";import{c as Ut,I as Ft,S as Ce,m as Pe,a as j,u as Ze,d as Ht,g as Bt,l as Dt,e as Qt,j as _t,f as $t,s as Kt,Y as zt}from"./index-CL5H13Cl.js";import{s as Wt,l as Yt,n as pt,m as Xt,a as Gt,o as Vt,d as Jt,b as Zt,p as er,c as et,e as tt,T as tr,R as rr,U as ht,f as nr,g as xt,h as sr,L as lr,I as ar}from"./index-DooLxbPX.js";import{M as St,a as bt}from"./Modal-DCwGz46r.js";import"./router-DHISdpPk.js";import"./Select-DC6zguGC.js";import"./DirBrowser-DbeAWYiL.js";import"./markdown-DxQYQFeH.js";import"./ExtensionsTab-BMS3PW9N.js";function or({snapshot:a}){const[r,f]=t.useState(a.currentIndex??0),[O,v]=t.useState(""),[k,g]=t.useState(!1),[C,S]=t.useState(null);t.useEffect(()=>{f(a.currentIndex??0),v(""),S(null)},[a.promptId,a.currentIndex]);const L=a.questions||[],y=L[r]||null,K=L.length,P=!!(y?.options&&y.options.length),M=P?!!y?.allowFreeform:!0,ae=u=>{u&&(f(c=>c+1),v(""))},Ie=async u=>{if(!k){g(!0),S(null);try{const c=await j.interactionSelectOption(a.promptId,u);if(!c.ok){S(c.error||"Failed to submit selection.");return}ae(c.advanced)}catch(c){S(c?.message||"Network error.")}finally{g(!1)}}},oe=async()=>{if(k)return;const u=O.trim();if(!u&&!y?.allowEmpty){S("Please enter a response.");return}g(!0),S(null);try{const c=await j.interactionSubmitText(a.promptId,u);if(!c.ok){S(c.error||"Failed to submit answer.");return}ae(c.advanced)}catch(c){S(c?.message||"Network error.")}finally{g(!1)}},p=async()=>{if(!k){g(!0),S(null);try{const u=await j.interactionSkip(a.promptId);if(!u.ok){S(u.error||"Failed to skip.");return}ae(u.advanced)}catch(u){S(u?.message||"Network error.")}finally{g(!1)}}},ue=async()=>{if(!k){g(!0);try{await j.interactionCancel(a.promptId)}catch{}}},ve=t.useMemo(()=>{const u=[];return a.hint&&u.push(a.hint),K>1&&u.push(`Question ${r+1} of ${K}`),u.join(" · ")||void 0},[a.hint,r,K]);return n.jsxs(St,{open:!0,onClose:ue,wide:P&&(y?.options?.length||0)>3,children:[n.jsx(bt,{title:a.title||"Pikiloom needs your input",description:ve,onClose:ue}),y?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:y.header||"Question"}),n.jsx("div",{className:"mt-1 whitespace-pre-wrap text-sm leading-relaxed text-fg",children:y.prompt})]}),P&&n.jsx("div",{className:"grid grid-cols-1 gap-2 sm:grid-cols-2",children:(y.options||[]).map(u=>n.jsxs("button",{type:"button",disabled:k,onClick:()=>Ie(u.value||u.label),className:Ut("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:u.label}),u.description&&n.jsx("div",{className:"mt-0.5 text-xs leading-snug text-fg-4",children:u.description})]},u.value||u.label))}),M&&n.jsx("div",{children:n.jsx(Ft,{value:O,onChange:u=>v(u.target.value),onKeyDown:u=>{u.key==="Enter"&&!u.shiftKey&&!k&&(u.preventDefault(),oe())},placeholder:P?"Or type a custom answer…":"Type your answer…",disabled:k,autoFocus:!P})}),C&&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:C}),n.jsxs("div",{className:"flex items-center justify-between gap-3",children:[n.jsx("div",{className:"text-xs text-fg-5",children:k?n.jsxs("span",{className:"inline-flex items-center gap-2",children:[n.jsx(Ce,{})," 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(Pe,{variant:"ghost",size:"sm",onClick:p,disabled:k,children:"Skip"}),M&&n.jsx(Pe,{variant:"primary",size:"sm",onClick:oe,disabled:k||!O.trim()&&!y.allowEmpty,children:"Submit"})]})]})]}):n.jsxs("div",{className:"py-6 text-center text-sm text-fg-5",children:[n.jsx(Ce,{className:"mr-2 inline-block"})," Waiting for the agent…"]})]})}function ur(a){return a.isNull?a.localStreamPending||a.holdsActiveState?"reject-null":"apply":(typeof a.updatedAt=="number"?a.updatedAt:0)<a.lastAppliedUpdatedAt?"reject-stale":"apply"}function cr(a,r){return typeof r!="number"?a:r>a?r:a}function ir(a,r){return!a||!a.length?[]:r.size?a.filter(f=>!r.has(f)):a.slice()}function dr(a,r,f,O){if(!a.size)return a;const v=new Set(r);let k=!1;const g=new Map;for(const[C,S]of a){if(f-S>O||!v.has(C)){k=!0;continue}g.set(C,S)}return k?g:a}const rt=12,kt=160,fr=96,mr=[],gr=[],pr=[],hr=20,xr=6e4,le=new Map;function kr(a,r){return`${a}:${r}`}function Sr(a,r){for(le.delete(a),le.set(a,r);le.size>hr;)le.delete(le.keys().next().value)}const Ar=t.memo(function({session:r,workdir:f,active:O=!0,onSessionChange:v,initialPendingPrompt:k,initialPendingImageUrls:g,onPendingPromptConsumed:C}){const S=Ze(e=>e.locale),L=Ze(e=>e.agentStatus?.agents?.find(s=>s.agent===r.agent)??null),y=L?.selectedEffort??null,K=L?.selectedModel??null,P=Ze(e=>e.modelLayer),M=r.profileId?P?.profiles.find(e=>e.id===r.profileId)??null:null,ae=M?P?.providers.find(e=>e.id===M.providerId)??null:null,Ie=r.profileId===void 0?L?.byokProviderName??null:ae?.name??null,oe=r.profileId===void 0?L?.byokProfileName??null:M&&M.name.trim().toLowerCase()!==M.modelId.trim().toLowerCase()?M.name:null,p=t.useMemo(()=>Ht(S),[S]),ue=Bt(r.agent||""),ve=Dt(r),u=!!k||!!(g&&g.length),[c,qe]=t.useState(null),[Ue,ce]=t.useState(!u),[ie,nt]=t.useState(!1),[i,U]=t.useState(null),[de,fe]=t.useState(!1),[z,Fe]=t.useState(null),[It,st]=t.useState(0),[vt,He]=t.useState(null),[W,ye]=t.useState([]),[yt,Te]=t.useState([]),[me,Be]=t.useState([]),[h,Y]=t.useState(k||null),[E,X]=t.useState(g||[]),[Tt,G]=t.useState(null),F=t.useRef(null);F.current=Tt;const lt=t.useRef(h);lt.current=h;const[at,q]=t.useState([]),Re=t.useRef([]);Re.current=at;const ge=t.useRef(null),[Rt,ot]=t.useState(null),[je,pe]=t.useState(null),[he,De]=t.useState(""),[H,Qe]=t.useState(!1),jt=!!L?.capabilities?.fork,_e=t.useRef(null),A=t.useRef(g||[]),B=t.useRef(i),$e=t.useRef(de);B.current=i,$e.current=de;const Ke=t.useRef(W);Ke.current=W;const ut=t.useRef(z);ut.current=z;const D=t.useRef(null),we=t.useRef(null),V=t.useRef(!0),J=t.useRef(!1),Ne=t.useRef(null),ze=t.useRef(!1),Q=t.useRef(u),xe=t.useRef(!1),Z=t.useRef(!1),We=t.useRef(!1),Ye=t.useRef(!1),_=t.useRef(null),Ae=t.useRef(0),ke=t.useRef(new Map),ct=t.useRef({model:null,effort:null}),wt=t.useCallback(e=>{ct.current=e},[]);t.useEffect(()=>{We.current||!u||(We.current=!0,k&&!h&&Y(k),g&&g.length&&!E.length&&(X(g),A.current=g),ce(!1),st(e=>e+1),C?.())},[u,C]);const w=t.useCallback(()=>{Y(null),X(e=>{for(const s of e)URL.revokeObjectURL(s);return[]}),A.current=[],G(null)},[]),ee=t.useCallback(()=>{q(e=>{if(!e.length)return e;for(const s of e)for(const l of s.imageUrls)URL.revokeObjectURL(l);return[]}),ge.current=null},[]),it=t.useCallback((e,s)=>{const l=Wt({streaming:$e.current,liveStreamPhase:B.current?.phase??null,streamPhase:ut.current,queuedTaskCount:Ke.current.length,pendingQueuedCount:Re.current.length}),o=s||[];if(l){const d=`local-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`;ge.current=d,q(x=>[...x,{localId:d,taskId:null,prompt:e||"",imageUrls:o}]);return}B.current?.phase==="done"&&U(null);for(const d of A.current)URL.revokeObjectURL(d);ge.current=null,Y(e||null),X(o),A.current=o,G(null)},[]),Nt=t.useCallback(e=>{const s=ge.current;if(s){ge.current=null,q(l=>{const o=l.findIndex(x=>x.localId===s);if(o<0)return l;const d=l.slice();return d[o]={...d[o],taskId:e},d});return}G(e)},[]),At=t.useCallback(async()=>{if(!je)return;const e=he.trim();if(e){Qe(!0);try{const s=await j.forkSession(f,r.agent||"",r.sessionId,je.atTurn,e,{});if(!s.ok||!s.sessionKey){Qe(!1);return}const[l,o]=s.sessionKey.split(":");pe(null),De(""),v?.({agent:l,sessionId:o,workdir:f})}finally{Qe(!1)}}},[je,he,f,r.agent,r.sessionId,v]);_e.current=At;const Oe=t.useCallback(async(e,s={})=>{try{const l=await Yt({workdir:f,agent:r.agent||"",sessionId:r.sessionId,rich:!0,turnOffset:e.turnOffset,turnLimit:e.turnLimit,lastNTurns:e.lastNTurns},{force:s.force});return l.ok?pt(l):null}catch{return null}},[f,r.agent,r.sessionId]),N=t.useCallback(async({keepOlder:e,force:s=!1,scrollToBottom:l=!1})=>{const o=r.sessionId;if(Ne.current===o)return!1;Ne.current=o;try{const d=await Oe({turnOffset:0,turnLimit:rt},{force:s});if(!d||r.sessionId!==o)return!1;if(l&&(J.current=!0),qe(x=>!x||!e?d:Xt(x,d)),ce(!1),xe.current&&(xe.current=!1,w()),Z.current){const x=Z.current;Z.current=!1;const re=x!==!0?x.taskId:null;B.current&&(x===!0||B.current.taskId===re)&&U(null)}return!0}finally{Ne.current===o&&(Ne.current=null)}},[Oe,w,r.sessionId]),Le=t.useCallback(async()=>{if(!c?.hasOlder||ze.current)return;const e=D.current;e&&(we.current={scrollHeight:e.scrollHeight,scrollTop:e.scrollTop}),ze.current=!0,nt(!0);try{const s=await Oe({turnOffset:Math.max(0,c.totalTurns-c.startTurn),turnLimit:rt});s?qe(l=>l?Gt(l,s):s):we.current=null}finally{ze.current=!1,nt(!1)}},[Oe,c]),Me=t.useRef(null),te=t.useCallback((e,s="ws")=>{if(ur({updatedAt:e?.updatedAt,isNull:!e,lastAppliedUpdatedAt:Ae.current,localStreamPending:Q.current,holdsActiveState:$e.current||Ke.current.length>0})!=="apply")return;if(e&&(Ae.current=cr(Ae.current,e.updatedAt)),e?.sessionId&&e.sessionId!==r.sessionId&&(Ye.current=!0,Se.current=`${r.agent}:${e.sessionId}`,v?.({agent:r.agent||"",sessionId:e.sessionId,workdir:f})),!e){const m=Me.current;fe(!1),m==="streaming"?(xe.current=!0,Z.current=!0,N({keepOlder:!0,force:!0,scrollToBottom:V.current})):U(null),Q.current&&m!=="streaming"?N({keepOlder:!0,force:!0}):(m==="done"&&(w(),ee()),m!==null&&(Q.current=!1)),He(null),Fe(null),ye([]),Te([]),Be([]),Me.current=null;return}Fe(e.phase),He(e.taskId||null);const o=[];e.taskId&&o.push(e.taskId),Array.isArray(e.queuedTaskIds)&&o.push(...e.queuedTaskIds),ke.current=dr(ke.current,o,Date.now(),xr);const d=ke.current,x=ir(e.queuedTaskIds,d),re=(Array.isArray(e.queuedTasks)?e.queuedTasks:[]).filter(m=>!d.has(m.taskId));if(ye(x.length?x:mr),Te(re.length?re:gr),Be(Array.isArray(e.interactions)&&e.interactions.length?e.interactions:pr),Vt({pendingTaskId:F.current,streamTaskId:e.taskId||null,queuedTaskIds:e.queuedTaskIds})){const m=F.current,R=lt.current||"",I=A.current;q(b=>b.some(se=>se.taskId===m)?b:[...b,{localId:`demote-${m}`,taskId:m,prompt:R,imageUrls:I}]),Y(null),X([]),A.current=[],G(null),F.current=null}if(e.phase==="streaming"){if(U({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}),fe(!0),e.taskId&&e.taskId!==F.current){const m=Re.current,R=m.findIndex(I=>I.taskId===e.taskId);if(R>=0){const I=m[R];for(const b of A.current)URL.revokeObjectURL(b);Y(I.prompt||null),X(I.imageUrls),A.current=I.imageUrls,G(e.taskId),q(b=>b.filter((se,$)=>$!==R))}}V.current&&(J.current=!0)}else if(e.phase==="queued")U(null),fe(!1);else if(e.phase==="done"){const m=Jt(B.current?.taskId??null,e.taskId||null),R=x.length>0;if(m){fe(!1),U($=>$?{...$,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}:$);const I=B.current,b=!!I&&Zt(I),se=!!e.incomplete&&b&&!R;if(Me.current!=="done"){R||(xe.current=!0),Z.current=se?!1:{taskId:e.taskId||null},N({keepOlder:!0,force:!0,scrollToBottom:V.current});const $=r.agent||"",Ct=r.sessionId,Pt=Se.current;_.current&&clearTimeout(_.current),_.current=setTimeout(()=>{_.current=null,j.getSessionStreamState($,Ct).then(qt=>{Se.current===Pt&&Xe.current(qt.state,"seed")}).catch(()=>{})},900)}}R||(Q.current=!1)}const ne=new Set;if(e.taskId&&ne.add(e.taskId),Array.isArray(e.queuedTaskIds))for(const m of e.queuedTaskIds)ne.add(m);q(m=>{let R=!1;const I=[];for(const b of m)if(!b.taskId||ne.has(b.taskId))I.push(b);else{for(const se of b.imageUrls)URL.revokeObjectURL(se);R=!0}return R?I:m}),Me.current=e.phase},[w,ee,N,r.sessionId,r.agent,v,f]),Xe=t.useRef(te);Xe.current=te;const dt=t.useCallback(()=>{Q.current=!0,st(e=>e+1)},[]);t.useEffect(()=>()=>{_.current&&(clearTimeout(_.current),_.current=null)},[]);const Ot=t.useCallback(async e=>{try{ke.current.set(e,Date.now()),await j.recallSessionMessage(e),F.current===e&&w(),q(s=>{let l=!1;const o=[];for(const d of s)if(d.taskId===e){for(const x of d.imageUrls)URL.revokeObjectURL(x);l=!0}else o.push(d);return l?o:s}),ye(s=>s.filter(l=>l!==e)),Te(s=>s.filter(l=>l.taskId!==e)),He(s=>s===e?null:s)}catch{}},[w]),Lt=t.useCallback(async e=>{const s=Re.current.find(l=>l.taskId===e)||null;if(s&&s.imageUrls.length>0){for(const l of A.current)URL.revokeObjectURL(l);Y(s.prompt||null),X(s.imageUrls),A.current=s.imageUrls,G(e),F.current=e,q(l=>l.filter(o=>o.taskId!==e))}try{await j.steerSession(e)}catch{}},[]),Mt=t.useCallback(async()=>{try{await j.stopSession(r.agent||"",r.sessionId)}catch{}},[r.agent,r.sessionId]),Ee=kr(r.agent||"",r.sessionId);t.useEffect(()=>{if(Ye.current){Ye.current=!1;let d=!1;return N({keepOlder:!0,force:!0}).finally(()=>{d||ce(!1)}),()=>{d=!0}}let e=!1;const s=er({workdir:f,agent:r.agent||"",sessionId:r.sessionId,rich:!0,turnOffset:0,turnLimit:rt},{allowStale:!0}),l=u&&!We.current,o=s?.ok?pt(s):le.get(Ee)||null;return ce(l?!1:!o),qe(o),U(null),fe(!1),Fe(null),ye([]),Te([]),Be([]),Ae.current=0,ke.current=new Map,l||(w(),ee(),Q.current=!1,xe.current=!1,Z.current=!1),V.current=!0,J.current=!0,l||N({keepOlder:!1,force:!0}).finally(()=>{e||ce(!1)}),()=>{e=!0}},[N,r.agent,r.sessionId,f,Ee,w,ee]),t.useEffect(()=>{c&&c.turns.length>0&&Sr(Ee,c)},[Ee,c]),t.useEffect(()=>{O&&N({keepOlder:!0,force:!0})},[O,N]);const Se=t.useRef(`${r.agent}:${r.sessionId}`);Se.current=`${r.agent}:${r.sessionId}`,Qt("stream-update",t.useCallback(e=>{e.key===Se.current&&te(e.snapshot??null)},[te])),t.useEffect(()=>{let e=!0;return j.getSessionStreamState(r.agent||"",r.sessionId).then(s=>{e&&Xe.current(s.state,"seed")}).catch(()=>{}),()=>{e=!1}},[r.agent,r.sessionId,It]),_t(t.useCallback(()=>{j.getSessionStreamState(r.agent||"",r.sessionId).then(e=>{te(e.state,"seed")}).catch(()=>{}),N({keepOlder:!0,force:!0})},[te,r.agent,r.sessionId,N])),t.useEffect(()=>{!Q.current&&ve!=="running"&&!de&&!i&&!z&&W.length===0&&(w(),ee())},[ve,de,i,z,W.length,w,ee]),t.useLayoutEffect(()=>{const e=we.current,s=D.current;!e||!s||(we.current=null,s.scrollTop=e.scrollTop+(s.scrollHeight-e.scrollHeight))},[c?.turns.length]),t.useLayoutEffect(()=>{if(!J.current)return;const e=D.current;e&&(J.current=!1,e.scrollTop=e.scrollHeight,requestAnimationFrame(()=>{V.current&&(e.scrollTop=e.scrollHeight)}))},[c,i]),t.useLayoutEffect(()=>{if(!h)return;const e=D.current;e&&(e.scrollTop=e.scrollHeight)},[h]),t.useEffect(()=>{if(!c?.hasOlder||Ue||ie)return;const e=D.current;e&&e.scrollHeight<=e.clientHeight+kt&&Le()},[c?.hasOlder,c?.turns.length,Le,Ue,ie]);const Et=t.useCallback(()=>{const e=D.current;if(!e)return;const s=e.scrollHeight-e.scrollTop-e.clientHeight;V.current=s<=fr,e.scrollTop<=kt&&Le()},[Le]),be=i?.model||r.model||K||null,Ge=$t(r.agent||"",i?.effort||r.thinkingEffort||y||null,r.workflowEnabled??L?.workflowEnabled)||null,ft=oe&&(!be||be===K)?oe:be?Kt(be):null,Ve=zt(r,{streaming:de,hasLiveStream:!!i,streamPhase:z,queuedTaskCount:W.length}),T=c?.turns||[],Je=t.useMemo(()=>{if(!E.length||!T.length)return!1;const e=T[T.length-1];return!e.user||!et(e.user.text,h)?!1:e.user.blocks.filter(l=>l.type==="image").length<E.length},[T,h,E.length]),mt=t.useMemo(()=>E.length?E.map(e=>({type:"image",content:e})):!h||!i?.questionBlocks?.length?[]:et(h,i.question)?i.questionBlocks:[],[E,h,i]),gt=t.useMemo(()=>{let e=T;if(Je){const ne=e[e.length-1];e=[...e.slice(0,-1),{...ne,user:null}]}if(!i||!e.length)return e;const s=e[e.length-1];if(!s.assistant)return e;const l=h??(i.question||null),o=(i.text||"").trim(),d=s.assistant.text?.trim()||"";if(!(l!=null?tt(s.user?.text,l):!!d&&!!o&&(o.startsWith(d)||d.startsWith(o))))return e;const re=s.user&&l&&!et(s.user.text,l)?{...s.user,text:l}:s.user;return[...e.slice(0,-1),{...s,user:re,assistant:null}]},[T,i,h,Je]);return n.jsxs("div",{className:"flex flex-col h-full overflow-hidden",children:[n.jsx("div",{ref:D,onScroll:Et,className:"flex-1 overflow-y-auto overscroll-contain",children:Ue&&!h&&!E.length&&!i?n.jsx("div",{className:"flex items-center justify-center py-20",children:n.jsx(Ce,{className:"h-5 w-5 text-fg-4"})}):gt.length===0&&!h&&!E.length&&!i&&!Ve?n.jsx("div",{className:"py-20 text-center text-[13px] text-fg-5",children:p("hub.noMessages")}):n.jsxs("div",{className:"max-w-[900px] mx-auto px-6 py-6 space-y-0",children:[(c?.hasOlder||ie)&&n.jsxs("div",{className:"mb-4 flex items-center justify-center gap-2 text-[11px] text-fg-5",children:[ie?n.jsx(Ce,{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:p(ie?"hub.loadingOlderTurns":"hub.loadOlderTurnsHint")})]}),r.migratedFrom?.kind==="fork"&&r.migratedFrom.sessionId&&n.jsxs("button",{type:"button",onClick:()=>v?.({agent:r.migratedFrom.agent||r.agent||"",sessionId:r.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:`#${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:p("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:["· ",p("hub.forkBadgeAt").replace("{turn}",String(r.migratedFrom.forkedAtTurn+1))]})]}),gt.map((e,s)=>{const l=(c?.startTurn||0)+s;return n.jsx(tr,{turn:e,turnIndex:l,agent:r.agent||"",meta:ue,model:ft,effort:Ge,providerName:Ie,t:p,workdir:f,onResend:o=>{J.current=!0,it(o);const d=ct.current;j.sendSessionMessage(f,r.agent||"",r.sessionId,o,{model:d.model||be||void 0,effort:d.effort||Ge||void 0}).then(x=>{x.ok&&dt()}).catch(()=>{w()})},onEdit:o=>ot(o),onFork:jt?o=>{De(""),pe({atTurn:o})}:void 0},`${c?.startTurn||0}:${s}`)}),Ve&&n.jsx("div",{className:"mb-5 animate-in",children:n.jsx(rr,{detail:Ve,t:p})}),(h||mt.length>0)&&(Je||!(h&&T.length>0&&tt(T[T.length-1]?.user?.text,h)))&&n.jsxs("div",{className:"session-turn",children:[n.jsx(ht,{text:h||"",blocks:mt,t:p}),!i&&n.jsx("div",{className:"mt-3 mb-5 animate-in",children:n.jsx(nr,{className:"text-fg-5"})})]}),i&&xt(i)&&!h&&i.question&&!(T.length>0&&tt(T[T.length-1]?.user?.text,i.question))&&n.jsx("div",{className:"session-turn",children:n.jsx(ht,{text:i.question,blocks:i.questionBlocks||void 0,t:p})}),i&&xt(i)&&n.jsxs("div",{className:"mb-6",children:[n.jsx(sr,{agent:r.agent||"",meta:ue,model:ft,effort:Ge,providerName:Ie,previewMeta:i.previewMeta,hideContextUsage:!0}),n.jsx(lr,{stream:i,t:p,workdir:f})]}),n.jsx("div",{className:"h-4"})]})}),n.jsx(ar,{session:r,workdir:f,onStreamQueued:dt,onSendStart:it,onSendTaskAssigned:Nt,onSessionChange:v,t:p,streamPhase:z,streamTaskId:vt,queuedTaskIds:W,queuedTasks:yt,pendingQueuedSends:at,onRecall:Ot,onSteer:Lt,onStopAll:Mt,editDraft:Rt,onEditDraftConsumed:()=>ot(null),onSelectionChange:wt}),je&&n.jsxs(St,{open:!0,onClose:()=>{H||pe(null)},children:[n.jsx(bt,{title:p("hub.forkPromptTitle"),description:p("hub.forkPromptHint"),onClose:()=>{H||pe(null)}}),n.jsx("textarea",{autoFocus:!0,value:he,disabled:H,onChange:e=>De(e.target.value),onKeyDown:e=>{e.key==="Enter"&&(e.metaKey||e.ctrlKey)&&he.trim()&&!H&&(e.preventDefault(),_e.current?.())},placeholder:p("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(Pe,{variant:"ghost",disabled:H,onClick:()=>pe(null),children:p("modal.cancel")}),n.jsx(Pe,{variant:"primary",disabled:H||!he.trim(),onClick:()=>{_e.current?.()},children:p(H?"hub.forkSubmitting":"hub.forkSubmit")})]})]}),O&&me.length>0&&n.jsx(or,{snapshot:me[me.length-1]},me[me.length-1].promptId)]})});export{Ar as SessionPanel};
|