pikiloom 0.4.39 → 0.4.40

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.
Files changed (23) hide show
  1. package/dashboard/dist/assets/{AgentTab-Ds8D6yrl.js → AgentTab-Cj7sekcS.js} +1 -1
  2. package/dashboard/dist/assets/{ConnectionModal-Buj2d5L5.js → ConnectionModal-g_6IHAWE.js} +1 -1
  3. package/dashboard/dist/assets/{DirBrowser-BTv7rH8E.js → DirBrowser-DVe6q12N.js} +1 -1
  4. package/dashboard/dist/assets/{ExtensionsTab-BnGwxS9U.js → ExtensionsTab-GqxD61Pp.js} +1 -1
  5. package/dashboard/dist/assets/{IMAccessTab-DjQnpOmF.js → IMAccessTab-DIqTzZJ9.js} +1 -1
  6. package/dashboard/dist/assets/{Modal-DpuinsE3.js → Modal-CIYrMwm0.js} +1 -1
  7. package/dashboard/dist/assets/{Modals-Bs842H3n.js → Modals-B-Afu2rD.js} +1 -1
  8. package/dashboard/dist/assets/{Select-Dvia29HZ.js → Select-B1UL27gw.js} +1 -1
  9. package/dashboard/dist/assets/SessionPanel-BHuG5hxj.js +1 -0
  10. package/dashboard/dist/assets/{SystemTab-BzPtwDxq.js → SystemTab-Bv1uT5-h.js} +1 -1
  11. package/dashboard/dist/assets/{index-S0NmlDEH.js → index-BOEW0RIx.js} +13 -13
  12. package/dashboard/dist/assets/{index-yZ-iG1qk.js → index-qKexKdJl.js} +3 -3
  13. package/dashboard/dist/assets/{shared-p3kZpiD4.js → shared-624PaUfm.js} +1 -1
  14. package/dashboard/dist/index.html +1 -1
  15. package/dist/agent/drivers/codex.js +5 -3
  16. package/dist/agent/index.js +1 -1
  17. package/dist/agent/stream.js +13 -1
  18. package/dist/dashboard/routes/agents.js +8 -4
  19. package/package.json +1 -1
  20. package/packages/kernel/dist/drivers/claude.js +17 -0
  21. package/packages/kernel/dist/drivers/codex.d.ts +18 -1
  22. package/packages/kernel/dist/drivers/codex.js +83 -7
  23. package/dashboard/dist/assets/SessionPanel-CVItEgcH.js +0 -1
@@ -1 +1 @@
1
- import{j as s}from"./react-vendor-C7Sl8SE7.js";import{N as t,m as i,S as a}from"./index-yZ-iG1qk.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};
1
+ import{j as s}from"./react-vendor-C7Sl8SE7.js";import{N as t,m as i,S as a}from"./index-qKexKdJl.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,7 +6,7 @@
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-yZ-iG1qk.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-qKexKdJl.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
12
  <link rel="stylesheet" crossorigin href="/assets/index-Bthwt6K_.css">
@@ -34,9 +34,11 @@ export class CodexAppServer {
34
34
  return new Promise((resolve) => {
35
35
  const timer = setTimeout(() => { this.kill(); resolve(false); }, CODEX_APPSERVER_SPAWN_TIMEOUT_MS);
36
36
  const args = ['app-server'];
37
- const overrides = this.configOverrides.some(entry => /^features\.goals\s*=/.test(entry))
38
- ? this.configOverrides
39
- : [...this.configOverrides, 'features.goals=true'];
37
+ // Do NOT force model_reasoning_summary — reasoning summaries stay OFF by default (respecting
38
+ // ~/.codex/config.toml = original behavior). A caller wanting thinking passes it via configOverrides.
39
+ const overrides = [...this.configOverrides];
40
+ if (!overrides.some(entry => /^features\.goals\s*=/.test(entry)))
41
+ overrides.push('features.goals=true');
40
42
  for (const c of overrides)
41
43
  args.push('-c', c);
42
44
  agentLog(`[codex-rpc] spawning: codex ${args.join(' ')}`);
@@ -7,7 +7,7 @@ export { attachAgentImage, attachInlineImage, materializeImage, rewriteAttachmen
7
7
  export { deliverArtifact, readDeliveredArtifacts, deliveredArtifactBlocks, tailDeliveredBlocks, latestDeliveredTaskId, mimeForArtifact, } from './artifacts.js';
8
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, 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
- export { detectAgentBin, listAgents, resolveDefaultAgent, run, doStream, recoverProfileIdForModel, listModels, resolveAgentModels, getUsage, getAgentBoundModelId, setAgentBoundModelId, } from './stream.js';
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';
12
12
  export { getProjectSkillPaths, initializeProjectSkills, listSkills, getGlobalSkillsRoot, collapseSkillPrompt, } from './skills.js';
13
13
  export { readGoal, writeGoal, clearGoal, setGoal, pauseGoal, resumeGoal, completeGoal, accountTurn, bumpContinuationCount, shouldContinueAfterTurn, renderContinuationPrompt, renderBudgetLimitPrompt, sessionGoalPath, DEFAULT_MAX_CONTINUATIONS, } from './goal.js';
@@ -658,6 +658,17 @@ export function listModels(agent, opts = {}) {
658
658
  function isLocalProviderBaseURL(baseURL) {
659
659
  return /^https?:\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\])(?::|\/|$)/i.test(baseURL);
660
660
  }
661
+ // A model surfaced as a BYOK profile must NOT also appear under the native group. Drivers seed
662
+ // their native list with the active model (via opts.currentModel) so it stays visible — but when
663
+ // a BYOK profile is active that "current model" is the profile's third-party model (e.g. a 豆包 /
664
+ // deepseek id), which then wrongly shows up as an "official / 官方 / Native" model. The profile row
665
+ // already represents it, so drop any native entry shadowed by a profile id.
666
+ export function dropNativeShadowedByProfiles(native, profileRows) {
667
+ if (!profileRows.length)
668
+ return native;
669
+ const shadowed = new Set(profileRows.map(m => m.id.trim().toLowerCase()).filter(Boolean));
670
+ return native.filter(m => !shadowed.has(m.id.trim().toLowerCase()));
671
+ }
661
672
  export async function resolveAgentModels(agent, opts = {}) {
662
673
  const driver = getDriver(agent);
663
674
  let nativeResult;
@@ -667,7 +678,7 @@ export async function resolveAgentModels(agent, opts = {}) {
667
678
  catch {
668
679
  nativeResult = { agent, models: [], sources: [], note: null };
669
680
  }
670
- const native = nativeResult.models.map(m => ({
681
+ const nativeRaw = nativeResult.models.map(m => ({
671
682
  id: m.id,
672
683
  alias: m.alias,
673
684
  group: 'native',
@@ -693,6 +704,7 @@ export async function resolveAgentModels(agent, opts = {}) {
693
704
  (isLocal ? local : cloud).push(entry);
694
705
  }
695
706
  }
707
+ const native = dropNativeShadowedByProfiles(nativeRaw, [...cloud, ...local]);
696
708
  const sources = [...nativeResult.sources];
697
709
  if (cloud.length)
698
710
  sources.push(`${cloud.length} cloud profile${cloud.length === 1 ? '' : 's'}`);
@@ -137,6 +137,11 @@ async function buildAgentStatusResponse(config = loadUserConfig(), agentOptions
137
137
  }
138
138
  const runtimeSelectedModel = runtime.getRuntimeModel(agentId, config);
139
139
  const runtimeSelectedEffort = runtime.getRuntimeEffort(agentId, config);
140
+ const activeProfile = getActiveProfile(agentId);
141
+ // runtimeSelectedModel is the model that will RUN (profile-aware): when a BYOK profile is
142
+ // bound it is the profile's third-party model. The NATIVE model list must not be seeded with
143
+ // it, else it shows under the "官方 / Native" group (it is surfaced separately via byokModels).
144
+ const nativeRuntimeModel = activeProfile ? '' : runtimeSelectedModel;
140
145
  let models = [];
141
146
  let usage = emptyUsage(agentId, 'Agent not installed.');
142
147
  let nativeConfig = null;
@@ -149,10 +154,10 @@ async function buildAgentStatusResponse(config = loadUserConfig(), agentOptions
149
154
  }
150
155
  catch { }
151
156
  }
152
- const modelFallback = runtimeSelectedModel ? [{ id: runtimeSelectedModel, alias: null }] : [];
157
+ const modelFallback = nativeRuntimeModel ? [{ id: nativeRuntimeModel, alias: null }] : [];
153
158
  const cachedUsage = driver.getUsage({ agent: agentId, model: runtimeSelectedModel });
154
159
  const [resolvedModels, resolvedUsage] = await Promise.all([
155
- withTimeoutFallback(driver.listModels({ workdir, currentModel: runtimeSelectedModel }).then(result => dedupeModels([
160
+ withTimeoutFallback(driver.listModels({ workdir, currentModel: nativeRuntimeModel || undefined }).then(result => dedupeModels([
156
161
  ...modelFallback,
157
162
  ...result.models,
158
163
  ])), AGENT_STATUS_MODELS_TIMEOUT_MS, modelFallback),
@@ -169,14 +174,13 @@ async function buildAgentStatusResponse(config = loadUserConfig(), agentOptions
169
174
  }
170
175
  }
171
176
  const updateState = getAgentUpdateState(agentId);
172
- const activeProfile = getActiveProfile(agentId);
173
177
  const byokProvider = activeProfile ? getProvider(activeProfile.providerId) : null;
174
178
  const byokProviderName = byokProvider?.name || null;
175
179
  const byokProfileName = activeProfile
176
180
  && activeProfile.name.trim().toLowerCase() !== activeProfile.modelId.trim().toLowerCase()
177
181
  ? activeProfile.name
178
182
  : null;
179
- const nativeSelectedModel = runtimeSelectedModel || nativeConfig?.model || null;
183
+ const nativeSelectedModel = nativeRuntimeModel || nativeConfig?.model || null;
180
184
  const nativeSelectedEffort = runtimeSelectedEffort || nativeConfig?.effort || null;
181
185
  const selectedModel = activeProfile?.modelId || nativeSelectedModel;
182
186
  const selectedEffort = activeProfile?.effort || nativeSelectedEffort;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pikiloom",
3
- "version": "0.4.39",
3
+ "version": "0.4.40",
4
4
  "description": "Put the world's smartest AI agents in your pocket. Command local Claude & Gemini via IM. | 让最好用的 IM 变成你电脑上的顶级 Agent 控制台",
5
5
  "type": "module",
6
6
  "bin": {
@@ -232,6 +232,23 @@ export function handleClaudeEvent(ev, s, emit) {
232
232
  s.cacheCreation = u?.cache_creation_input_tokens ?? 0;
233
233
  s.output = 0;
234
234
  }
235
+ else if (inner.type === 'content_block_start') {
236
+ // Claude emits multiple text/thinking blocks per turn (one set per tool-use round). Insert a
237
+ // paragraph break before a NEW block when prior content exists, so the live preview shows
238
+ // breaks between segments instead of running them together. Mirrors the legacy driver; the
239
+ // separator is emitted as a delta so the runtime's accumulated snapshot stays in sync with s.
240
+ const bt = inner.content_block?.type;
241
+ if (bt === 'text' && s.text && !s.text.endsWith('\n\n')) {
242
+ const sep = s.text.endsWith('\n') ? '\n' : '\n\n';
243
+ s.text += sep;
244
+ emit({ type: 'text', delta: sep });
245
+ }
246
+ else if (bt === 'thinking' && s.reasoning && !s.reasoning.endsWith('\n\n')) {
247
+ const sep = s.reasoning.endsWith('\n') ? '\n' : '\n\n';
248
+ s.reasoning += sep;
249
+ emit({ type: 'reasoning', delta: sep });
250
+ }
251
+ }
235
252
  else if (inner.type === 'content_block_delta') {
236
253
  const d = inner.delta || {};
237
254
  if (d.type === 'text_delta' && d.text) {
@@ -1,5 +1,22 @@
1
- import type { AgentDriver, AgentTurnInput, DriverContext, DriverResult, TuiInput, TuiSpec } from '../contracts/driver.js';
1
+ import type { AgentDriver, AgentTurnInput, DriverContext, DriverEvent, DriverResult, TuiInput, TuiSpec } from '../contracts/driver.js';
2
2
  import type { UniversalUsage } from '../protocol/index.js';
3
+ export declare function codexToolSummary(item: any): {
4
+ id: string;
5
+ name: string;
6
+ summary: string;
7
+ } | null;
8
+ export interface CodexContentState {
9
+ text: string;
10
+ reasoning: string;
11
+ streamedReasoning: boolean;
12
+ msgs: string[];
13
+ thinkParts: string[];
14
+ }
15
+ export declare function codexReasoningItemText(item: any): string;
16
+ export declare function captureCodexAgentMessage(item: any, s: CodexContentState, deltaItems: Set<string>, phases: Map<string, string>, emit: (e: DriverEvent) => void): void;
17
+ export declare function captureCodexReasoning(text: string, s: CodexContentState, emit: (e: DriverEvent) => void): void;
18
+ export declare function codexFinalText(s: CodexContentState): string;
19
+ export declare function codexFinalReasoning(s: CodexContentState): string;
3
20
  export declare class CodexDriver implements AgentDriver {
4
21
  private readonly bin;
5
22
  readonly id = "codex";
@@ -20,7 +20,12 @@ class AppServer {
20
20
  onServerRequest(cb) { this.requestResponder = cb; }
21
21
  async start() {
22
22
  const args = ['app-server'];
23
- const overrides = this.configOverrides.some(c => /^features\.goals\s*=/.test(c)) ? this.configOverrides : [...this.configOverrides, 'features.goals=true'];
23
+ // Do NOT force model_reasoning_summary codex reasoning summaries stay OFF by default
24
+ // (respecting ~/.codex/config.toml, which is the original behavior). A caller that wants
25
+ // thinking can pass `model_reasoning_summary=...` via configOverrides; we never inject it.
26
+ const overrides = [...this.configOverrides];
27
+ if (!overrides.some(c => /^features\.goals\s*=/.test(c)))
28
+ overrides.push('features.goals=true');
24
29
  for (const c of overrides)
25
30
  args.push('-c', c);
26
31
  try {
@@ -126,7 +131,12 @@ function codexFileChangeSummary(item) {
126
131
  }
127
132
  // Normalize a codex app-server item -> {id, name, summary}. The summary mirrors pikiloom's
128
133
  // vocabulary ("Run shell: <cmd>", "Edit <path>") so the activity projection reads naturally.
129
- function codexToolSummary(item) {
134
+ // Only ACTUAL tool calls become Activity rows. agentMessage (the answer text) and reasoning
135
+ // (thinking) are CONTENT — they render below as message text / thinking, never as tools. The old
136
+ // fallback to `item.type` wrongly turned every item (incl. agentMessage/reasoning) into a bogus
137
+ // "tool" in the Activity card. Mirrors the legacy driver's isCodexToolCallItem whitelist.
138
+ const CODEX_TOOL_CALL_TYPES = new Set(['dynamicToolCall', 'mcpToolCall', 'collabAgentToolCall']);
139
+ export function codexToolSummary(item) {
130
140
  const id = String(item?.id || '');
131
141
  if (!id)
132
142
  return null;
@@ -136,8 +146,13 @@ function codexToolSummary(item) {
136
146
  }
137
147
  if (item.type === 'fileChange' || item.type === 'patch')
138
148
  return { id, name: 'edit', summary: codexFileChangeSummary(item) };
139
- const tool = typeof item?.tool === 'string' ? item.tool : typeof item?.name === 'string' ? item.name : item?.type;
140
- return tool ? { id, name: String(tool), summary: String(tool) } : null;
149
+ if (CODEX_TOOL_CALL_TYPES.has(item.type)) {
150
+ const raw = typeof item.tool === 'string' && item.tool.trim() ? item.tool.trim()
151
+ : typeof item.name === 'string' && item.name.trim() ? item.name.trim() : '';
152
+ const name = raw ? (raw.split('.').pop() || raw) : 'tool';
153
+ return { id, name, summary: raw ? `Use ${name}` : 'Use tool' };
154
+ }
155
+ return null;
141
156
  }
142
157
  // codex `item/tool/requestUserInput` params -> a normalized UniversalInteraction
143
158
  // (faithful to the legacy codex driver's toAgentInteraction shape).
@@ -160,6 +175,51 @@ function codexUserInputToInteraction(params, promptId) {
160
175
  return null;
161
176
  return { promptId, kind: 'user-input', title: 'User Input Required', hint: 'Use the buttons when available, or reply with text.', questions };
162
177
  }
178
+ // Reasoning text from a completed `reasoning` item (item/completed or rawResponseItem/completed):
179
+ // summary/content arrays of plain strings or {text} objects.
180
+ export function codexReasoningItemText(item) {
181
+ const parts = [
182
+ ...(Array.isArray(item?.summary) ? item.summary : []),
183
+ ...(Array.isArray(item?.content) ? item.content : []),
184
+ ];
185
+ return parts.map((p) => (typeof p === 'string' ? p : p?.text || '')).filter(Boolean).join('\n').trim();
186
+ }
187
+ // A completed final_answer agentMessage that did NOT stream deltas: append + emit it live.
188
+ // deltaItems holds the ids already streamed, so a completed item echoing a streamed one is
189
+ // not double-counted (matches the legacy driver's deltaSeenForItem guard).
190
+ export function captureCodexAgentMessage(item, s, deltaItems, phases, emit) {
191
+ const phase = item?.phase || (item?.id ? phases.get(item.id) : null) || 'final_answer';
192
+ if (phase !== 'final_answer')
193
+ return;
194
+ const text = typeof item?.text === 'string' ? item.text.trim() : '';
195
+ if (!text)
196
+ return;
197
+ s.msgs.push(text);
198
+ if (item.id && deltaItems.has(item.id))
199
+ return;
200
+ const delta = s.text.trim() ? `\n\n${text}` : text;
201
+ s.text += delta;
202
+ emit({ type: 'text', delta });
203
+ }
204
+ // A completed reasoning item: keep it for the end-of-turn fallback, and stream it live only
205
+ // when nothing arrived as reasoning deltas (so the streamed path is never double-emitted).
206
+ export function captureCodexReasoning(text, s, emit) {
207
+ if (!text)
208
+ return;
209
+ s.thinkParts.push(text);
210
+ if (s.streamedReasoning)
211
+ return;
212
+ const delta = s.reasoning.trim() ? `\n\n${text}` : text;
213
+ s.reasoning += delta;
214
+ emit({ type: 'reasoning', delta });
215
+ }
216
+ // End-of-turn finalizers: prefer streamed text/reasoning, fall back to completed-item parts.
217
+ export function codexFinalText(s) {
218
+ return s.text.trim() ? s.text : s.msgs.join('\n\n');
219
+ }
220
+ export function codexFinalReasoning(s) {
221
+ return s.reasoning.trim() ? s.reasoning : s.thinkParts.join('\n\n');
222
+ }
163
223
  export class CodexDriver {
164
224
  bin;
165
225
  id = 'codex';
@@ -173,9 +233,10 @@ export class CodexDriver {
173
233
  // to the native account.
174
234
  const config = [...(input.configOverrides || [])];
175
235
  const srv = new AppServer(this.bin, config, input.env);
176
- const state = { text: '', reasoning: '', sessionId: input.sessionId ?? null, input: null, output: null, cached: null, contextUsed: null, contextWindow: null, status: null, error: null, turnId: null };
236
+ const state = { text: '', reasoning: '', streamedReasoning: false, msgs: [], thinkParts: [], sessionId: input.sessionId ?? null, input: null, output: null, cached: null, contextUsed: null, contextWindow: null, status: null, error: null, turnId: null };
177
237
  const phases = new Map();
178
238
  const toolSummaries = new Map();
239
+ const deltaItems = new Set();
179
240
  let steerRegistered = false;
180
241
  const ok = await srv.start();
181
242
  if (!ok)
@@ -239,6 +300,8 @@ export class CodexDriver {
239
300
  const phase = params?.itemId ? (phases.get(params.itemId) || 'final_answer') : 'final_answer';
240
301
  if (phase === 'final_answer' && params?.delta) {
241
302
  state.text += params.delta;
303
+ if (params.itemId)
304
+ deltaItems.add(params.itemId);
242
305
  ctx.emit({ type: 'text', delta: params.delta });
243
306
  }
244
307
  break;
@@ -247,16 +310,28 @@ export class CodexDriver {
247
310
  case 'item/reasoning/summaryTextDelta':
248
311
  if (params?.delta) {
249
312
  state.reasoning += params.delta;
313
+ state.streamedReasoning = true;
250
314
  ctx.emit({ type: 'reasoning', delta: params.delta });
251
315
  }
252
316
  break;
253
317
  case 'item/completed': {
254
318
  const item = params?.item || {};
319
+ // Final answer / reasoning delivered as a completed item (no preceding deltas).
320
+ if (item.type === 'agentMessage')
321
+ captureCodexAgentMessage(item, state, deltaItems, phases, ctx.emit);
322
+ else if (item.type === 'reasoning')
323
+ captureCodexReasoning(codexReasoningItemText(item), state, ctx.emit);
255
324
  const t = codexToolSummary(item);
256
325
  if (t && toolSummaries.has(t.id))
257
326
  ctx.emit({ type: 'tool', call: { id: t.id, name: t.name, summary: toolSummaries.get(t.id) || t.summary, status: item.status === 'failed' ? 'failed' : 'done' } });
258
327
  break;
259
328
  }
329
+ case 'rawResponseItem/completed': {
330
+ const item = params?.item || {};
331
+ if (item?.type === 'reasoning')
332
+ captureCodexReasoning(codexReasoningItemText(item), state, ctx.emit);
333
+ break;
334
+ }
260
335
  case 'turn/plan/updated': {
261
336
  const plan = planFromUpdate(params);
262
337
  if (plan)
@@ -307,10 +382,11 @@ export class CodexDriver {
307
382
  await turnDone;
308
383
  const usage = codexUsageOf(state);
309
384
  const ok2 = (state.status === 'completed' || state.status == null) && !state.error && !ctx.signal.aborted;
385
+ const finalReasoning = codexFinalReasoning(state);
310
386
  return {
311
387
  ok: ok2,
312
- text: state.text,
313
- reasoning: state.reasoning || undefined,
388
+ text: codexFinalText(state),
389
+ reasoning: finalReasoning || undefined,
314
390
  error: state.error || (ctx.signal.aborted ? 'Interrupted by user.' : null),
315
391
  stopReason: ctx.signal.aborted ? 'interrupted' : (state.status || 'end_turn'),
316
392
  sessionId: state.sessionId,
@@ -1 +0,0 @@
1
- import{r as t,j as n}from"./react-vendor-C7Sl8SE7.js";import{c as Lt,I as Ct,S as Le,m as Ce,a as w,u as ct,d as Pt,g as Ut,l as qt,e as Ft,j as Ht,f as Dt,s as Qt,X as _t}from"./index-yZ-iG1qk.js";import{s as $t,l as Bt,n as it,m as Kt,a as zt,o as Wt,d as Xt,b as Yt,p as Gt,c as dt,e as Ge,T as Vt,R as Jt,U as ft,f as Zt,g as mt,h as er,L as tr,I as rr}from"./index-S0NmlDEH.js";import{M as pt,a as ht}from"./Modal-DpuinsE3.js";import"./router-DHISdpPk.js";import"./Select-Dvia29HZ.js";import"./DirBrowser-BTv7rH8E.js";import"./markdown-DxQYQFeH.js";import"./ExtensionsTab-BnGwxS9U.js";function nr({snapshot:a}){const[r,d]=t.useState(a.currentIndex??0),[M,v]=t.useState(""),[k,p]=t.useState(!1),[L,S]=t.useState(null);t.useEffect(()=>{d(a.currentIndex??0),v(""),S(null)},[a.promptId,a.currentIndex]);const E=a.questions||[],T=E[r]||null,z=E.length,C=!!(T?.options&&T.options.length),oe=C?!!T?.allowFreeform:!0,f=u=>{u&&(d(h=>h+1),v(""))},ye=async u=>{if(!k){p(!0),S(null);try{const h=await w.interactionSelectOption(a.promptId,u);if(!h.ok){S(h.error||"Failed to submit selection.");return}f(h.advanced)}catch(h){S(h?.message||"Network error.")}finally{p(!1)}}},ue=async()=>{if(k)return;const u=M.trim();if(!u&&!T?.allowEmpty){S("Please enter a response.");return}p(!0),S(null);try{const h=await w.interactionSubmitText(a.promptId,u);if(!h.ok){S(h.error||"Failed to submit answer.");return}f(h.advanced)}catch(h){S(h?.message||"Network error.")}finally{p(!1)}},q=async()=>{if(!k){p(!0),S(null);try{const u=await w.interactionSkip(a.promptId);if(!u.ok){S(u.error||"Failed to skip.");return}f(u.advanced)}catch(u){S(u?.message||"Network error.")}finally{p(!1)}}},m=async()=>{if(!k){p(!0);try{await w.interactionCancel(a.promptId)}catch{}}},ce=t.useMemo(()=>{const u=[];return a.hint&&u.push(a.hint),z>1&&u.push(`Question ${r+1} of ${z}`),u.join(" · ")||void 0},[a.hint,r,z]);return n.jsxs(pt,{open:!0,onClose:m,wide:C&&(T?.options?.length||0)>3,children:[n.jsx(ht,{title:a.title||"Pikiloom needs your input",description:ce,onClose:m}),T?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:T.header||"Question"}),n.jsx("div",{className:"mt-1 whitespace-pre-wrap text-sm leading-relaxed text-fg",children:T.prompt})]}),C&&n.jsx("div",{className:"grid grid-cols-1 gap-2 sm:grid-cols-2",children:(T.options||[]).map(u=>n.jsxs("button",{type:"button",disabled:k,onClick:()=>ye(u.value||u.label),className:Lt("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))}),oe&&n.jsx("div",{children:n.jsx(Ct,{value:M,onChange:u=>v(u.target.value),onKeyDown:u=>{u.key==="Enter"&&!u.shiftKey&&!k&&(u.preventDefault(),ue())},placeholder:C?"Or type a custom answer…":"Type your answer…",disabled:k,autoFocus:!C})}),L&&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:L}),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(Le,{})," 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(Ce,{variant:"ghost",size:"sm",onClick:q,disabled:k,children:"Skip"}),oe&&n.jsx(Ce,{variant:"primary",size:"sm",onClick:ue,disabled:k||!M.trim()&&!T.allowEmpty,children:"Submit"})]})]})]}):n.jsxs("div",{className:"py-6 text-center text-sm text-fg-5",children:[n.jsx(Le,{className:"mr-2 inline-block"})," Waiting for the agent…"]})]})}function sr(a){return a.isNull?a.localStreamPending||a.holdsActiveState?"reject-null":"apply":(typeof a.updatedAt=="number"?a.updatedAt:0)<a.lastAppliedUpdatedAt?"reject-stale":"apply"}function lr(a,r){return typeof r!="number"?a:r>a?r:a}function ar(a,r){return!a||!a.length?[]:r.size?a.filter(d=>!r.has(d)):a.slice()}function or(a,r,d,M){if(!a.size)return a;const v=new Set(r);let k=!1;const p=new Map;for(const[L,S]of a){if(d-S>M||!v.has(L)){k=!0;continue}p.set(L,S)}return k?p:a}const Ve=12,gt=160,ur=96,cr=[],ir=[],dr=[],fr=20,mr=6e4,ae=new Map;function gr(a,r){return`${a}:${r}`}function pr(a,r){for(ae.delete(a),ae.set(a,r);ae.size>fr;)ae.delete(ae.keys().next().value)}const Rr=t.memo(function({session:r,workdir:d,active:M=!0,onSessionChange:v,initialPendingPrompt:k,initialPendingImageUrls:p,onPendingPromptConsumed:L}){const S=ct(e=>e.locale),E=ct(e=>e.agentStatus?.agents?.find(s=>s.agent===r.agent)??null),T=E?.selectedEffort??null,z=E?.selectedModel??null,C=E?.byokProviderName??null,oe=E?.byokProfileName??null,f=t.useMemo(()=>Pt(S),[S]),ye=Ut(r.agent||""),ue=qt(r),q=!!k||!!(p&&p.length),[m,ce]=t.useState(null),[u,h]=t.useState(!q),[ie,Je]=t.useState(!1),[i,F]=t.useState(null),[de,fe]=t.useState(!1),[W,Pe]=t.useState(null),[xt,Ze]=t.useState(0),[kt,Ue]=t.useState(null),[X,Ie]=t.useState([]),[St,ve]=t.useState([]),[me,qe]=t.useState([]),[b,Y]=t.useState(k||null),[P,G]=t.useState(p||[]),[bt,V]=t.useState(null),H=t.useRef(null);H.current=bt;const et=t.useRef(b);et.current=b;const[tt,U]=t.useState([]),Te=t.useRef([]);Te.current=tt;const ge=t.useRef(null),[yt,rt]=t.useState(null),[Re,pe]=t.useState(null),[he,Fe]=t.useState(""),[D,He]=t.useState(!1),It=!!E?.capabilities?.fork,De=t.useRef(null),O=t.useRef(p||[]),Q=t.useRef(i),Qe=t.useRef(de);Q.current=i,Qe.current=de;const _e=t.useRef(X);_e.current=X;const nt=t.useRef(W);nt.current=W;const _=t.useRef(null),je=t.useRef(null),J=t.useRef(!0),Z=t.useRef(!1),we=t.useRef(null),$e=t.useRef(!1),$=t.useRef(q),xe=t.useRef(!1),ee=t.useRef(!1),Be=t.useRef(!1),Ke=t.useRef(!1),B=t.useRef(null),Ne=t.useRef(0),ke=t.useRef(new Map),st=t.useRef({model:null,effort:null}),vt=t.useCallback(e=>{st.current=e},[]);t.useEffect(()=>{Be.current||!q||(Be.current=!0,k&&!b&&Y(k),p&&p.length&&!P.length&&(G(p),O.current=p),h(!1),Ze(e=>e+1),L?.())},[q,L]);const N=t.useCallback(()=>{Y(null),G(e=>{for(const s of e)URL.revokeObjectURL(s);return[]}),O.current=[],V(null)},[]),te=t.useCallback(()=>{U(e=>{if(!e.length)return e;for(const s of e)for(const l of s.imageUrls)URL.revokeObjectURL(l);return[]}),ge.current=null},[]),lt=t.useCallback((e,s)=>{const l=$t({streaming:Qe.current,liveStreamPhase:Q.current?.phase??null,streamPhase:nt.current,queuedTaskCount:_e.current.length,pendingQueuedCount:Te.current.length}),o=s||[];if(l){const c=`local-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`;ge.current=c,U(x=>[...x,{localId:c,taskId:null,prompt:e||"",imageUrls:o}]);return}Q.current?.phase==="done"&&F(null);for(const c of O.current)URL.revokeObjectURL(c);ge.current=null,Y(e||null),G(o),O.current=o,V(null)},[]),Tt=t.useCallback(e=>{const s=ge.current;if(s){ge.current=null,U(l=>{const o=l.findIndex(x=>x.localId===s);if(o<0)return l;const c=l.slice();return c[o]={...c[o],taskId:e},c});return}V(e)},[]),Rt=t.useCallback(async()=>{if(!Re)return;const e=he.trim();if(e){He(!0);try{const s=await w.forkSession(d,r.agent||"",r.sessionId,Re.atTurn,e,{});if(!s.ok||!s.sessionKey){He(!1);return}const[l,o]=s.sessionKey.split(":");pe(null),Fe(""),v?.({agent:l,sessionId:o,workdir:d})}finally{He(!1)}}},[Re,he,d,r.agent,r.sessionId,v]);De.current=Rt;const Ae=t.useCallback(async(e,s={})=>{try{const l=await Bt({workdir:d,agent:r.agent||"",sessionId:r.sessionId,rich:!0,turnOffset:e.turnOffset,turnLimit:e.turnLimit,lastNTurns:e.lastNTurns},{force:s.force});return l.ok?it(l):null}catch{return null}},[d,r.agent,r.sessionId]),A=t.useCallback(async({keepOlder:e,force:s=!1,scrollToBottom:l=!1})=>{const o=r.sessionId;if(we.current===o)return!1;we.current=o;try{const c=await Ae({turnOffset:0,turnLimit:Ve},{force:s});if(!c||r.sessionId!==o)return!1;if(l&&(Z.current=!0),ce(x=>!x||!e?c:Kt(x,c)),h(!1),xe.current&&(xe.current=!1,N()),ee.current){const x=ee.current;ee.current=!1;const ne=x!==!0?x.taskId:null;Q.current&&(x===!0||Q.current.taskId===ne)&&F(null)}return!0}finally{we.current===o&&(we.current=null)}},[Ae,N,r.sessionId]),Oe=t.useCallback(async()=>{if(!m?.hasOlder||$e.current)return;const e=_.current;e&&(je.current={scrollHeight:e.scrollHeight,scrollTop:e.scrollTop}),$e.current=!0,Je(!0);try{const s=await Ae({turnOffset:Math.max(0,m.totalTurns-m.startTurn),turnLimit:Ve});s?ce(l=>l?zt(l,s):s):je.current=null}finally{$e.current=!1,Je(!1)}},[Ae,m]),Me=t.useRef(null),re=t.useCallback((e,s="ws")=>{if(sr({updatedAt:e?.updatedAt,isNull:!e,lastAppliedUpdatedAt:Ne.current,localStreamPending:$.current,holdsActiveState:Qe.current||_e.current.length>0})!=="apply")return;if(e&&(Ne.current=lr(Ne.current,e.updatedAt)),e?.sessionId&&e.sessionId!==r.sessionId&&(Ke.current=!0,Se.current=`${r.agent}:${e.sessionId}`,v?.({agent:r.agent||"",sessionId:e.sessionId,workdir:d})),!e){const g=Me.current;fe(!1),g==="streaming"?(xe.current=!0,ee.current=!0,A({keepOlder:!0,force:!0,scrollToBottom:J.current})):F(null),$.current&&g!=="streaming"?A({keepOlder:!0,force:!0}):(g==="done"&&(N(),te()),g!==null&&($.current=!1)),Ue(null),Pe(null),Ie([]),ve([]),qe([]),Me.current=null;return}Pe(e.phase),Ue(e.taskId||null);const o=[];e.taskId&&o.push(e.taskId),Array.isArray(e.queuedTaskIds)&&o.push(...e.queuedTaskIds),ke.current=or(ke.current,o,Date.now(),mr);const c=ke.current,x=ar(e.queuedTaskIds,c),ne=(Array.isArray(e.queuedTasks)?e.queuedTasks:[]).filter(g=>!c.has(g.taskId));if(Ie(x.length?x:cr),ve(ne.length?ne:ir),qe(Array.isArray(e.interactions)&&e.interactions.length?e.interactions:dr),Wt({pendingTaskId:H.current,streamTaskId:e.taskId||null,queuedTaskIds:e.queuedTaskIds})){const g=H.current,j=et.current||"",I=O.current;U(y=>y.some(le=>le.taskId===g)?y:[...y,{localId:`demote-${g}`,taskId:g,prompt:j,imageUrls:I}]),Y(null),G([]),O.current=[],V(null),H.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}),fe(!0),e.taskId&&e.taskId!==H.current){const g=Te.current,j=g.findIndex(I=>I.taskId===e.taskId);if(j>=0){const I=g[j];for(const y of O.current)URL.revokeObjectURL(y);Y(I.prompt||null),G(I.imageUrls),O.current=I.imageUrls,V(e.taskId),U(y=>y.filter((le,K)=>K!==j))}}J.current&&(Z.current=!0)}else if(e.phase==="queued")F(null),fe(!1);else if(e.phase==="done"){const g=Xt(Q.current?.taskId??null,e.taskId||null),j=x.length>0;if(g){fe(!1),F(K=>K?{...K,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}:K);const I=Q.current,y=!!I&&Yt(I),le=!!e.incomplete&&y&&!j;if(Me.current!=="done"){j||(xe.current=!0),ee.current=le?!1:{taskId:e.taskId||null},A({keepOlder:!0,force:!0,scrollToBottom:J.current});const K=r.agent||"",Ot=r.sessionId,Mt=Se.current;B.current&&clearTimeout(B.current),B.current=setTimeout(()=>{B.current=null,w.getSessionStreamState(K,Ot).then(Et=>{Se.current===Mt&&ze.current(Et.state,"seed")}).catch(()=>{})},900)}}j||($.current=!1)}const se=new Set;if(e.taskId&&se.add(e.taskId),Array.isArray(e.queuedTaskIds))for(const g of e.queuedTaskIds)se.add(g);U(g=>{let j=!1;const I=[];for(const y of g)if(!y.taskId||se.has(y.taskId))I.push(y);else{for(const le of y.imageUrls)URL.revokeObjectURL(le);j=!0}return j?I:g}),Me.current=e.phase},[N,te,A,r.sessionId,r.agent,v,d]),ze=t.useRef(re);ze.current=re;const at=t.useCallback(()=>{$.current=!0,Ze(e=>e+1)},[]);t.useEffect(()=>()=>{B.current&&(clearTimeout(B.current),B.current=null)},[]);const jt=t.useCallback(async e=>{try{ke.current.set(e,Date.now()),await w.recallSessionMessage(e),H.current===e&&N(),U(s=>{let l=!1;const o=[];for(const c of s)if(c.taskId===e){for(const x of c.imageUrls)URL.revokeObjectURL(x);l=!0}else o.push(c);return l?o:s}),Ie(s=>s.filter(l=>l!==e)),ve(s=>s.filter(l=>l.taskId!==e)),Ue(s=>s===e?null:s)}catch{}},[N]),wt=t.useCallback(async e=>{const s=Te.current.find(l=>l.taskId===e)||null;if(s&&s.imageUrls.length>0){for(const l of O.current)URL.revokeObjectURL(l);Y(s.prompt||null),G(s.imageUrls),O.current=s.imageUrls,V(e),H.current=e,U(l=>l.filter(o=>o.taskId!==e))}try{await w.steerSession(e)}catch{}},[]),Nt=t.useCallback(async()=>{try{await w.stopSession(r.agent||"",r.sessionId)}catch{}},[r.agent,r.sessionId]),Ee=gr(r.agent||"",r.sessionId);t.useEffect(()=>{if(Ke.current){Ke.current=!1;let c=!1;return A({keepOlder:!0,force:!0}).finally(()=>{c||h(!1)}),()=>{c=!0}}let e=!1;const s=Gt({workdir:d,agent:r.agent||"",sessionId:r.sessionId,rich:!0,turnOffset:0,turnLimit:Ve},{allowStale:!0}),l=q&&!Be.current,o=s?.ok?it(s):ae.get(Ee)||null;return h(l?!1:!o),ce(o),F(null),fe(!1),Pe(null),Ie([]),ve([]),qe([]),Ne.current=0,ke.current=new Map,l||(N(),te(),$.current=!1,xe.current=!1,ee.current=!1),J.current=!0,Z.current=!0,l||A({keepOlder:!1,force:!0}).finally(()=>{e||h(!1)}),()=>{e=!0}},[A,r.agent,r.sessionId,d,Ee,N,te]),t.useEffect(()=>{m&&m.turns.length>0&&pr(Ee,m)},[Ee,m]),t.useEffect(()=>{M&&A({keepOlder:!0,force:!0})},[M,A]);const Se=t.useRef(`${r.agent}:${r.sessionId}`);Se.current=`${r.agent}:${r.sessionId}`,Ft("stream-update",t.useCallback(e=>{e.key===Se.current&&re(e.snapshot??null)},[re])),t.useEffect(()=>{let e=!0;return w.getSessionStreamState(r.agent||"",r.sessionId).then(s=>{e&&ze.current(s.state,"seed")}).catch(()=>{}),()=>{e=!1}},[r.agent,r.sessionId,xt]),Ht(t.useCallback(()=>{w.getSessionStreamState(r.agent||"",r.sessionId).then(e=>{re(e.state,"seed")}).catch(()=>{}),A({keepOlder:!0,force:!0})},[re,r.agent,r.sessionId,A])),t.useEffect(()=>{!$.current&&ue!=="running"&&!de&&!i&&!W&&X.length===0&&(N(),te())},[ue,de,i,W,X.length,N,te]),t.useLayoutEffect(()=>{const e=je.current,s=_.current;!e||!s||(je.current=null,s.scrollTop=e.scrollTop+(s.scrollHeight-e.scrollHeight))},[m?.turns.length]),t.useLayoutEffect(()=>{if(!Z.current)return;const e=_.current;e&&(Z.current=!1,e.scrollTop=e.scrollHeight,requestAnimationFrame(()=>{J.current&&(e.scrollTop=e.scrollHeight)}))},[m,i]),t.useLayoutEffect(()=>{if(!b)return;const e=_.current;e&&(e.scrollTop=e.scrollHeight)},[b]),t.useEffect(()=>{if(!m?.hasOlder||u||ie)return;const e=_.current;e&&e.scrollHeight<=e.clientHeight+gt&&Oe()},[m?.hasOlder,m?.turns.length,Oe,u,ie]);const At=t.useCallback(()=>{const e=_.current;if(!e)return;const s=e.scrollHeight-e.scrollTop-e.clientHeight;J.current=s<=ur,e.scrollTop<=gt&&Oe()},[Oe]),be=i?.model||r.model||z||null,We=Dt(r.agent||"",i?.effort||r.thinkingEffort||T||null,r.workflowEnabled??E?.workflowEnabled)||null,ot=oe&&(!be||be===z)?oe:be?Qt(be):null,Xe=_t(r,{streaming:de,hasLiveStream:!!i,streamPhase:W,queuedTaskCount:X.length}),R=m?.turns||[],Ye=t.useMemo(()=>{if(!P.length||!R.length)return!1;const e=R[R.length-1];return!e.user||!dt(e.user.text,b)?!1:e.user.blocks.filter(l=>l.type==="image").length<P.length},[R,b,P.length]),ut=t.useMemo(()=>{let e=R;if(Ye){const se=e[e.length-1];e=[...e.slice(0,-1),{...se,user:null}]}if(!i||!e.length)return e;const s=e[e.length-1];if(!s.assistant)return e;const l=b??(i.question||null),o=(i.text||"").trim(),c=s.assistant.text?.trim()||"";if(!(l!=null?Ge(s.user?.text,l):!!c&&!!o&&(o.startsWith(c)||c.startsWith(o))))return e;const ne=s.user&&l&&!dt(s.user.text,l)?{...s.user,text:l}:s.user;return[...e.slice(0,-1),{...s,user:ne,assistant:null}]},[R,i,b,Ye]);return n.jsxs("div",{className:"flex flex-col h-full overflow-hidden",children:[n.jsx("div",{ref:_,onScroll:At,className:"flex-1 overflow-y-auto overscroll-contain",children:u&&!b&&!P.length&&!i?n.jsx("div",{className:"flex items-center justify-center py-20",children:n.jsx(Le,{className:"h-5 w-5 text-fg-4"})}):ut.length===0&&!b&&!P.length&&!i&&!Xe?n.jsx("div",{className:"py-20 text-center text-[13px] text-fg-5",children:f("hub.noMessages")}):n.jsxs("div",{className:"max-w-[900px] mx-auto px-6 py-6 space-y-0",children:[(m?.hasOlder||ie)&&n.jsxs("div",{className:"mb-4 flex items-center justify-center gap-2 text-[11px] text-fg-5",children:[ie?n.jsx(Le,{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:f(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:d}),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:f("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:["· ",f("hub.forkBadgeAt").replace("{turn}",String(r.migratedFrom.forkedAtTurn+1))]})]}),ut.map((e,s)=>{const l=(m?.startTurn||0)+s;return n.jsx(Vt,{turn:e,turnIndex:l,agent:r.agent||"",meta:ye,model:ot,effort:We,providerName:C,t:f,workdir:d,onResend:o=>{Z.current=!0,lt(o);const c=st.current;w.sendSessionMessage(d,r.agent||"",r.sessionId,o,{model:c.model||be||void 0,effort:c.effort||We||void 0}).then(x=>{x.ok&&at()}).catch(()=>{N()})},onEdit:o=>rt(o),onFork:It?o=>{Fe(""),pe({atTurn:o})}:void 0},`${m?.startTurn||0}:${s}`)}),Xe&&n.jsx("div",{className:"mb-5 animate-in",children:n.jsx(Jt,{detail:Xe,t:f})}),(b||P.length>0)&&(Ye||!(b&&R.length>0&&Ge(R[R.length-1]?.user?.text,b)))&&n.jsxs("div",{className:"session-turn",children:[n.jsx(ft,{text:b||"",blocks:P.map(e=>({type:"image",content:e})),t:f}),!i&&n.jsx("div",{className:"mt-3 mb-5 animate-in",children:n.jsx(Zt,{className:"text-fg-5"})})]}),i&&mt(i)&&!b&&i.question&&!(R.length>0&&Ge(R[R.length-1]?.user?.text,i.question))&&n.jsx("div",{className:"session-turn",children:n.jsx(ft,{text:i.question,t:f})}),i&&mt(i)&&n.jsxs("div",{className:"mb-6",children:[n.jsx(er,{agent:r.agent||"",meta:ye,model:ot,effort:We,providerName:C,previewMeta:i.previewMeta,hideContextUsage:!0}),n.jsx(tr,{stream:i,t:f,workdir:d})]}),n.jsx("div",{className:"h-4"})]})}),n.jsx(rr,{session:r,workdir:d,onStreamQueued:at,onSendStart:lt,onSendTaskAssigned:Tt,onSessionChange:v,t:f,streamPhase:W,streamTaskId:kt,queuedTaskIds:X,queuedTasks:St,pendingQueuedSends:tt,onRecall:jt,onSteer:wt,onStopAll:Nt,editDraft:yt,onEditDraftConsumed:()=>rt(null),onSelectionChange:vt}),Re&&n.jsxs(pt,{open:!0,onClose:()=>{D||pe(null)},children:[n.jsx(ht,{title:f("hub.forkPromptTitle"),description:f("hub.forkPromptHint"),onClose:()=>{D||pe(null)}}),n.jsx("textarea",{autoFocus:!0,value:he,disabled:D,onChange:e=>Fe(e.target.value),onKeyDown:e=>{e.key==="Enter"&&(e.metaKey||e.ctrlKey)&&he.trim()&&!D&&(e.preventDefault(),De.current?.())},placeholder:f("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(Ce,{variant:"ghost",disabled:D,onClick:()=>pe(null),children:f("modal.cancel")}),n.jsx(Ce,{variant:"primary",disabled:D||!he.trim(),onClick:()=>{De.current?.()},children:f(D?"hub.forkSubmitting":"hub.forkSubmit")})]})]}),M&&me.length>0&&n.jsx(nr,{snapshot:me[me.length-1]},me[me.length-1].promptId)]})});export{Rr as SessionPanel};