farai 0.1.6 → 0.1.7

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/dist/cli/index.js CHANGED
@@ -3635,7 +3635,7 @@ function proxyConfig(value) {
3635
3635
  }
3636
3636
  function resolveProxyConfig(config) {
3637
3637
  return {
3638
- transparent: config.proxy?.transparent === true,
3638
+ transparent: config.proxy?.transparent !== false,
3639
3639
  ports: config.proxy?.ports?.length ? config.proxy.ports : DEFAULT_TRANSPARENT_PROXY_PORTS
3640
3640
  };
3641
3641
  }
@@ -3960,7 +3960,7 @@ function atomicWriteFile(path, content, mode) {
3960
3960
  var DEFAULT_TRANSPARENT_PROXY_PORTS, LSP_SERVER_IDS, DEFAULT_CONFIG_TEMPLATE = `model = "big-pickle"
3961
3961
 
3962
3962
  [proxy]
3963
- transparent = false
3963
+ transparent = true
3964
3964
 
3965
3965
  [mcp_servers.mitmproxy-mcp]
3966
3966
  command = "mitmproxy-mcp"
@@ -24103,6 +24103,17 @@ var init_tool_call_journal = () => {};
24103
24103
  import { createHash as createHash4 } from "crypto";
24104
24104
  import { existsSync as existsSync12, mkdirSync as mkdirSync6, realpathSync as realpathSync3, writeFileSync as writeFileSync7 } from "fs";
24105
24105
  import { isAbsolute as isAbsolute4, join as join16, relative as relative6 } from "path";
24106
+ function shouldBufferInitialTextStream(text) {
24107
+ if (isInternalMetaReasoning(text))
24108
+ return true;
24109
+ if (text.includes(`
24110
+ `))
24111
+ return false;
24112
+ const normalized = text.trimStart().toLowerCase().replace(/\s+/g, " ");
24113
+ if (!normalized)
24114
+ return false;
24115
+ return INTERNAL_META_STREAM_PREFIXES.some((prefix) => prefix.startsWith(normalized));
24116
+ }
24106
24117
 
24107
24118
  class AgentRuntime {
24108
24119
  turnControllers = new Map;
@@ -25037,7 +25048,7 @@ class AgentRuntime {
25037
25048
  this.autoCompactFailures.delete(sessionId);
25038
25049
  this.fileState.clear(sessionId);
25039
25050
  for (const turn of this.store.listTurns(sessionId, 1000)) {
25040
- this.streamingParts.delete(turn.id);
25051
+ this.deleteStreamingParts(turn.id);
25041
25052
  }
25042
25053
  return this.store.clearSessionChat(sessionId);
25043
25054
  }
@@ -25752,7 +25763,7 @@ This completion is already terminal and was delivered automatically. Do not call
25752
25763
  } else if (action.kind === "respond") {
25753
25764
  sawResponse = true;
25754
25765
  if (hasToolAction && isInternalMetaReasoning(action.text)) {
25755
- this.discardStreamingText(turn.id);
25766
+ this.discardStreamingText(session.id, turn.id);
25756
25767
  shouldContinue = true;
25757
25768
  continue;
25758
25769
  }
@@ -25789,7 +25800,7 @@ This completion is already terminal and was delivered automatically. Do not call
25789
25800
  });
25790
25801
  const request = buildChatRequest(plannerInput, requestController.signal);
25791
25802
  let lastError = "";
25792
- this.streamingParts.delete(turn.id);
25803
+ this.deleteStreamingParts(turn.id);
25793
25804
  try {
25794
25805
  for (let attempt = 1;; attempt += 1) {
25795
25806
  this.emitPlannerAttempt(session, turn, assistantMessage, plannerName, attempt, plannerInput, context);
@@ -25928,7 +25939,7 @@ This completion is already terminal and was delivered automatically. Do not call
25928
25939
  shouldContinue: shouldContinue2
25929
25940
  };
25930
25941
  }
25931
- this.prepareStreamingRetry(turn.id);
25942
+ this.prepareStreamingRetry(session.id, turn.id);
25932
25943
  const retry = plannerRetryState(error, attempt, dispatched.length === 0);
25933
25944
  const errorPayload = {
25934
25945
  turnId: turn.id,
@@ -25997,7 +26008,7 @@ This completion is already terminal and was delivered automatically. Do not call
25997
26008
  const rawRespondText = content.trim();
25998
26009
  const respondText = dispatched.length > 0 && isInternalMetaReasoning(rawRespondText) ? "" : rawRespondText;
25999
26010
  if (!respondText && rawRespondText && dispatched.length > 0)
26000
- this.discardStreamingText(turn.id);
26011
+ this.discardStreamingText(session.id, turn.id);
26001
26012
  const outcomes = await Promise.all(dispatched);
26002
26013
  const toolCancelled = outcomes.some((outcome) => outcome.cancelled);
26003
26014
  for (const outcome of outcomes) {
@@ -26151,9 +26162,11 @@ This completion is already terminal and was delivered automatically. Do not call
26151
26162
  rationale: ""
26152
26163
  });
26153
26164
  if (state) {
26165
+ state.reasoningAccum = "";
26166
+ this.publishStreamingReasoning(session.id, turn.id, state);
26154
26167
  delete state.reasoningPartId;
26155
26168
  delete state.reasoningAccum;
26156
- delete state.lastReasoningRender;
26169
+ delete state.lastReasoningPersist;
26157
26170
  }
26158
26171
  return;
26159
26172
  }
@@ -26162,13 +26175,15 @@ This completion is already terminal and was delivered automatically. Do not call
26162
26175
  rationale
26163
26176
  });
26164
26177
  if (state?.reasoningPartId) {
26178
+ state.reasoningAccum = rationale;
26179
+ this.publishStreamingReasoning(session.id, turn.id, state);
26165
26180
  this.store.updatePartPayload(state.reasoningPartId, {
26166
26181
  planner: plannerName,
26167
26182
  rationale
26168
26183
  });
26169
26184
  delete state.reasoningPartId;
26170
26185
  delete state.reasoningAccum;
26171
- delete state.lastReasoningRender;
26186
+ delete state.lastReasoningPersist;
26172
26187
  } else {
26173
26188
  this.store.addPart({
26174
26189
  sessionId: session.id,
@@ -26186,6 +26201,8 @@ This completion is already terminal and was delivered automatically. Do not call
26186
26201
  responses.push(text);
26187
26202
  const streamed = this.streamingParts.get(turn.id);
26188
26203
  if (streamed?.textPartId) {
26204
+ streamed.textAccum = text;
26205
+ this.publishStreamingText(session.id, turn.id, streamed);
26189
26206
  this.store.updatePartPayload(streamed.textPartId, {
26190
26207
  text
26191
26208
  });
@@ -26669,6 +26686,40 @@ This completion is already terminal and was delivered automatically. Do not call
26669
26686
  };
26670
26687
  }
26671
26688
  }
26689
+ deleteStreamingParts(turnId) {
26690
+ this.streamingParts.delete(turnId);
26691
+ }
26692
+ publishStreamingText(sessionId, turnId, state) {
26693
+ if (!state.textPartId)
26694
+ return;
26695
+ this.store.publishTransientEvent({
26696
+ id: id(),
26697
+ sessionId,
26698
+ type: "stream_text",
26699
+ payload: {
26700
+ turnId,
26701
+ partId: state.textPartId,
26702
+ text: state.textAccum
26703
+ },
26704
+ createdAt: nowIso()
26705
+ });
26706
+ }
26707
+ publishStreamingReasoning(sessionId, turnId, state) {
26708
+ if (!state.reasoningPartId)
26709
+ return;
26710
+ const rationale = normalizeReasoningSummary(state.reasoningAccum ?? "");
26711
+ this.store.publishTransientEvent({
26712
+ id: id(),
26713
+ sessionId,
26714
+ type: "stream_reasoning",
26715
+ payload: {
26716
+ turnId,
26717
+ partId: state.reasoningPartId,
26718
+ rationale
26719
+ },
26720
+ createdAt: nowIso()
26721
+ });
26722
+ }
26672
26723
  applyStreamEvent(session, turn, assistantMessage, event) {
26673
26724
  const now = Date.now();
26674
26725
  const state = this.streamingParts.get(turn.id) ?? {
@@ -26693,26 +26744,39 @@ This completion is already terminal and was delivered automatically. Do not call
26693
26744
  }
26694
26745
  });
26695
26746
  state.reasoningPartId = part.id;
26696
- state.lastReasoningRender = now;
26697
- } else if (now - (state.lastReasoningRender ?? 0) >= STREAM_RENDER_INTERVAL_MS) {
26747
+ state.lastReasoningPersist = now;
26748
+ } else if (now - (state.lastReasoningPersist ?? 0) >= STREAM_PERSIST_INTERVAL_MS) {
26698
26749
  this.store.updatePartPayload(state.reasoningPartId, {
26699
26750
  rationale
26700
26751
  });
26701
- state.lastReasoningRender = now;
26752
+ state.lastReasoningPersist = now;
26702
26753
  }
26754
+ this.publishStreamingReasoning(session.id, turn.id, state);
26703
26755
  this.streamingParts.set(turn.id, state);
26704
26756
  return;
26705
26757
  }
26706
26758
  state.textAccum += event.delta;
26707
26759
  if (!state.textPartId) {
26708
- if (isInternalMetaReasoning(state.textAccum)) {
26760
+ if (shouldBufferInitialTextStream(state.textAccum)) {
26709
26761
  this.streamingParts.set(turn.id, state);
26710
26762
  return;
26711
26763
  }
26712
- if (state.textAccum.length < 80 && !state.textAccum.includes(`
26713
- `)) {
26714
- this.streamingParts.set(turn.id, state);
26715
- return;
26764
+ if (!state.reasoningPartId && state.reasoningAccum) {
26765
+ const rationale = normalizeReasoningSummary(state.reasoningAccum);
26766
+ if (rationale) {
26767
+ const reasoningPart = this.store.addPart({
26768
+ sessionId: session.id,
26769
+ turnId: turn.id,
26770
+ messageId: assistantMessage.id,
26771
+ type: "reasoning_summary",
26772
+ payload: {
26773
+ rationale
26774
+ }
26775
+ });
26776
+ state.reasoningPartId = reasoningPart.id;
26777
+ state.lastReasoningPersist = now;
26778
+ this.publishStreamingReasoning(session.id, turn.id, state);
26779
+ }
26716
26780
  }
26717
26781
  const part = this.store.addPart({
26718
26782
  sessionId: session.id,
@@ -26724,30 +26788,17 @@ This completion is already terminal and was delivered automatically. Do not call
26724
26788
  }
26725
26789
  });
26726
26790
  state.textPartId = part.id;
26727
- state.lastTextRender = now;
26728
26791
  state.lastTextPersist = now;
26729
26792
  } else if (now - (state.lastTextPersist ?? 0) >= STREAM_PERSIST_INTERVAL_MS) {
26730
26793
  this.store.updatePartPayload(state.textPartId, {
26731
26794
  text: state.textAccum
26732
26795
  });
26733
- state.lastTextRender = now;
26734
26796
  state.lastTextPersist = now;
26735
- } else if (now - (state.lastTextRender ?? 0) >= STREAM_RENDER_INTERVAL_MS) {
26736
- this.store.publishTransientEvent({
26737
- id: id(),
26738
- sessionId: session.id,
26739
- type: "stream_text",
26740
- payload: {
26741
- partId: state.textPartId,
26742
- text: state.textAccum
26743
- },
26744
- createdAt: nowIso()
26745
- });
26746
- state.lastTextRender = now;
26747
26797
  }
26798
+ this.publishStreamingText(session.id, turn.id, state);
26748
26799
  this.streamingParts.set(turn.id, state);
26749
26800
  }
26750
- discardStreamingText(turnId) {
26801
+ discardStreamingText(sessionId, turnId) {
26751
26802
  const state = this.streamingParts.get(turnId);
26752
26803
  if (!state)
26753
26804
  return;
@@ -26756,11 +26807,11 @@ This completion is already terminal and was delivered automatically. Do not call
26756
26807
  text: ""
26757
26808
  });
26758
26809
  state.textAccum = "";
26810
+ this.publishStreamingText(sessionId, turnId, state);
26759
26811
  delete state.textPartId;
26760
- delete state.lastTextRender;
26761
26812
  delete state.lastTextPersist;
26762
26813
  }
26763
- prepareStreamingRetry(turnId) {
26814
+ prepareStreamingRetry(sessionId, turnId) {
26764
26815
  const state = this.streamingParts.get(turnId);
26765
26816
  if (!state)
26766
26817
  return;
@@ -26773,11 +26824,13 @@ This completion is already terminal and was delivered automatically. Do not call
26773
26824
  rationale: ""
26774
26825
  });
26775
26826
  state.textAccum = "";
26827
+ state.reasoningAccum = "";
26828
+ this.publishStreamingText(sessionId, turnId, state);
26829
+ this.publishStreamingReasoning(sessionId, turnId, state);
26776
26830
  delete state.reasoningPartId;
26777
26831
  delete state.reasoningAccum;
26778
- delete state.lastReasoningRender;
26779
- state.lastTextRender = Date.now();
26780
- state.lastTextPersist = state.lastTextRender;
26832
+ delete state.lastReasoningPersist;
26833
+ state.lastTextPersist = Date.now();
26781
26834
  }
26782
26835
  async planWithRetry(planner, input, session, turn, assistantMessage, context, timeoutMs, allowEmpty = false) {
26783
26836
  let lastError = "";
@@ -26786,7 +26839,7 @@ This completion is already terminal and was delivered automatically. Do not call
26786
26839
  release,
26787
26840
  timedOut
26788
26841
  } = this.registerTurnController(turn.id, timeoutMs);
26789
- this.streamingParts.delete(turn.id);
26842
+ this.deleteStreamingParts(turn.id);
26790
26843
  const onStreamEvent = (event) => this.applyStreamEvent(session, turn, assistantMessage, event);
26791
26844
  try {
26792
26845
  for (let attempt = 1;; attempt += 1) {
@@ -26813,7 +26866,7 @@ This completion is already terminal and was delivered automatically. Do not call
26813
26866
  lastError = "cancelled";
26814
26867
  break;
26815
26868
  }
26816
- this.prepareStreamingRetry(turn.id);
26869
+ this.prepareStreamingRetry(session.id, turn.id);
26817
26870
  const retry = plannerRetryState(error, attempt, true);
26818
26871
  const errorPayload = {
26819
26872
  turnId: turn.id,
@@ -26935,7 +26988,7 @@ This completion is already terminal and was delivered automatically. Do not call
26935
26988
  } : {}
26936
26989
  });
26937
26990
  this.modelCallsByTurn.delete(turn.id);
26938
- this.streamingParts.delete(turn.id);
26991
+ this.deleteStreamingParts(turn.id);
26939
26992
  this.fireHooks({
26940
26993
  id: turn.sessionId
26941
26994
  }, "turn.stop", undefined, {
@@ -28405,7 +28458,7 @@ function sameExistingPath(left, right) {
28405
28458
  return false;
28406
28459
  }
28407
28460
  }
28408
- var REASONING_MAX_BYTES2, STREAM_RENDER_INTERVAL_MS = 100, STREAM_PERSIST_INTERVAL_MS = 2000, LIVE_OUTPUT_MAX_BYTES, LIVE_OUTPUT_FLUSH_INTERVAL_MS = 150, LIVE_OUTPUT_INITIAL_DELAY_MS = 320, TOOL_HUMAN_RESULT_MAX_BYTES, LOOP_SUPERVISION_NO_PROGRESS_STEPS = 12, LOOP_SUPERVISION_STEER_INTERVAL = 5, LOOP_PATTERN_MAX_PERIOD = 8, PROGRESS_ACTION_TOOLS, AUTO_COMPACTION_CONTINUATION = "[internal continuation after context compaction: Continue the active user task from the compacted prior context. Do not repeat, regenerate, or explain the summary. Resume with the exact next useful action.]", WRAPUP_MODEL_TIMEOUT_MS = 15000, DEFAULT_SHUTDOWN_GRACE_PERIOD_MS = 2000, RUNTIME_LEASE_MS = 60000, RUNTIME_HEARTBEAT_MS = 15000, RESTART_TOOL_ERROR = "Interrupted by runtime restart; tool execution was not replayed.", STEP_LIMIT_WRAPUP_DIRECTIVE = "You have reached the maximum number of steps allowed for this turn, so tools are no longer available. Do not attempt to call any tool. In a few sentences, summarize what you accomplished, the key findings or evidence so far, any blockers, and the single most useful next step. This is your final message for this turn.", TIME_LIMIT_WRAPUP_DIRECTIVE = "You have reached the interactive wall-clock budget for this turn, so tools are no longer available. Do not attempt to call any tool. Concisely summarize completed work, proven evidence, active background jobs, remaining uncertainty, and the single best next action. This is your final message for this turn.", IneffectiveCompactionError, ModelCallDeadlineError, PlannerOutputValidationError;
28461
+ var REASONING_MAX_BYTES2, STREAM_PERSIST_INTERVAL_MS = 2000, LIVE_OUTPUT_MAX_BYTES, LIVE_OUTPUT_FLUSH_INTERVAL_MS = 150, LIVE_OUTPUT_INITIAL_DELAY_MS = 320, TOOL_HUMAN_RESULT_MAX_BYTES, LOOP_SUPERVISION_NO_PROGRESS_STEPS = 12, LOOP_SUPERVISION_STEER_INTERVAL = 5, LOOP_PATTERN_MAX_PERIOD = 8, PROGRESS_ACTION_TOOLS, AUTO_COMPACTION_CONTINUATION = "[internal continuation after context compaction: Continue the active user task from the compacted prior context. Do not repeat, regenerate, or explain the summary. Resume with the exact next useful action.]", WRAPUP_MODEL_TIMEOUT_MS = 15000, DEFAULT_SHUTDOWN_GRACE_PERIOD_MS = 2000, RUNTIME_LEASE_MS = 60000, RUNTIME_HEARTBEAT_MS = 15000, RESTART_TOOL_ERROR = "Interrupted by runtime restart; tool execution was not replayed.", STEP_LIMIT_WRAPUP_DIRECTIVE = "You have reached the maximum number of steps allowed for this turn, so tools are no longer available. Do not attempt to call any tool. In a few sentences, summarize what you accomplished, the key findings or evidence so far, any blockers, and the single most useful next step. This is your final message for this turn.", TIME_LIMIT_WRAPUP_DIRECTIVE = "You have reached the interactive wall-clock budget for this turn, so tools are no longer available. Do not attempt to call any tool. Concisely summarize completed work, proven evidence, active background jobs, remaining uncertainty, and the single best next action. This is your final message for this turn.", INTERNAL_META_STREAM_PREFIXES, IneffectiveCompactionError, ModelCallDeadlineError, PlannerOutputValidationError;
28409
28462
  var init_runtime = __esm(() => {
28410
28463
  init_sqlite_store();
28411
28464
  init_registry4();
@@ -28453,6 +28506,7 @@ var init_runtime = __esm(() => {
28453
28506
  LIVE_OUTPUT_MAX_BYTES = 2 * 1024;
28454
28507
  TOOL_HUMAN_RESULT_MAX_BYTES = 24 * 1024;
28455
28508
  PROGRESS_ACTION_TOOLS = new Set(["http_request", "subdomain_enum", "dir_enum", "port_scan", "nmap_scan", "fs_edit", "fs_write", "patch_apply", "code_write_script", "campaign_verify", "campaign_test", "callback_oast", "exploit_search"]);
28509
+ INTERNAL_META_STREAM_PREFIXES = ["the user", "user asked", "user wants", "user requested", "user said", "per my", "per the", "my communication", "my instruction", "my instructions", "my rules", "my task", "according to my", "according to the", "the tool search", "tool_search", "there's no dedicated tool", "there is no dedicated tool", "there's no specific tool", "there is no specific tool", "there's no matching tool", "there is no matching tool", "i have access to", "let me inspect the available tool", "let me check the available tool", "let me search the available tool", "i should report", "i will report", "i need to report", "i must report", "i can report", "we should report", "we will report", "we need to report", "we must report", "we can report"];
28456
28510
  IneffectiveCompactionError = class IneffectiveCompactionError extends Error {
28457
28511
  constructor(preTokens, postTokens) {
28458
28512
  super(`compaction did not reduce active context: ${preTokens} -> ${postTokens} estimated tokens`);
@@ -32644,6 +32698,17 @@ function createRuntimePort(runtime, options = {}) {
32644
32698
  return;
32645
32699
  if (evt.type === "snapshot.changed" && pending.some((item) => item.type === "snapshot.changed" && item.sessionId === evt.sessionId))
32646
32700
  return;
32701
+ const streamIdentity = transientStreamIdentity(evt);
32702
+ if (streamIdentity) {
32703
+ const existing = pending.length - 1;
32704
+ if (existing >= 0 && transientStreamIdentity(pending[existing]) === streamIdentity)
32705
+ pending[existing] = evt;
32706
+ else
32707
+ pending.push(evt);
32708
+ if (!flushTimer)
32709
+ flushTimer = setTimeout(flush, BATCH_DEBOUNCE_MS);
32710
+ return;
32711
+ }
32647
32712
  if (evt.type === "activity.changed") {
32648
32713
  const existing = pending.findIndex((item) => item.type === "activity.changed" && item.sessionId === evt.sessionId);
32649
32714
  if (existing >= 0)
@@ -33249,6 +33314,16 @@ function createRuntimePort(runtime, options = {}) {
33249
33314
  }
33250
33315
  };
33251
33316
  }
33317
+ function transientStreamIdentity(event) {
33318
+ if (event.type !== "event.appended")
33319
+ return;
33320
+ if (event.event.type !== "stream_text" && event.event.type !== "stream_reasoning")
33321
+ return;
33322
+ const payload = event.event.payload;
33323
+ if (typeof payload?.turnId !== "string" || typeof payload.partId !== "string")
33324
+ return;
33325
+ return `${event.sessionId}:${payload.turnId}:${event.event.type}:${payload.partId}`;
33326
+ }
33252
33327
  function storeChangeIdentity(change) {
33253
33328
  switch (change.kind) {
33254
33329
  case "part":
@@ -34292,19 +34367,6 @@ function createActions(store, setStore) {
34292
34367
  errorSet(message) {
34293
34368
  setStore("ui", "lastError", message);
34294
34369
  },
34295
- streamTextUpdated(partId, text) {
34296
- setStore(produce((s) => {
34297
- for (const message of s.snapshot.messages) {
34298
- const part = message.parts.find((item) => item.id === partId);
34299
- if (!part)
34300
- continue;
34301
- part.payload = {
34302
- text
34303
- };
34304
- return;
34305
- }
34306
- }));
34307
- },
34308
34370
  toolInputPreviewUpdated(preview) {
34309
34371
  setStore(produce((s) => {
34310
34372
  const index = s.snapshot.toolInputPreviews.findIndex((item) => item.id === preview.id);
@@ -34746,9 +34808,11 @@ async function handleTuiEvent(evt, ctx) {
34746
34808
  }
34747
34809
  switch (evt.type) {
34748
34810
  case "turn.started":
34811
+ ctx.onTurnStarted?.(evt.turnId);
34749
34812
  ctx.actions.turnStarted(evt.turnId, evt.startedAt);
34750
34813
  break;
34751
34814
  case "turn.finished":
34815
+ ctx.onTurnFinished?.(evt.turnId);
34752
34816
  ctx.actions.turnFinished(evt.turnId);
34753
34817
  await refreshSnapshot(evt.sessionId);
34754
34818
  queueMicrotask(() => {
@@ -34779,8 +34843,14 @@ async function handleTuiEvent(evt, ctx) {
34779
34843
  }
34780
34844
  if (evt.event.type === "stream_text") {
34781
34845
  const payload = evt.event.payload;
34782
- if (typeof payload?.partId === "string" && typeof payload.text === "string") {
34783
- ctx.actions.streamTextUpdated(payload.partId, payload.text);
34846
+ if (typeof payload?.turnId === "string" && typeof payload.partId === "string" && typeof payload.text === "string") {
34847
+ ctx.onStreamText?.(payload.partId, payload.text, payload.turnId);
34848
+ }
34849
+ }
34850
+ if (evt.event.type === "stream_reasoning") {
34851
+ const payload = evt.event.payload;
34852
+ if (typeof payload?.turnId === "string" && typeof payload.partId === "string" && typeof payload.rationale === "string") {
34853
+ ctx.onStreamReasoning?.(payload.partId, payload.rationale, payload.turnId);
34784
34854
  }
34785
34855
  }
34786
34856
  const detail = statusDetailFor(evt.event);
@@ -34986,6 +35056,243 @@ var init_runtime2 = __esm(() => {
34986
35056
  RuntimeContext = createContext();
34987
35057
  });
34988
35058
 
35059
+ // src/agent-tui/transcript/stream-engine.ts
35060
+ function createTranscriptStreamEngine() {
35061
+ const [state, setState] = createStore({
35062
+ text: {},
35063
+ reasoning: {},
35064
+ diagnostics: {
35065
+ revision: 0,
35066
+ flushes: 0,
35067
+ coalescedUpdates: 0,
35068
+ lastIntervalMs: BASE_FRAME_INTERVAL_MS,
35069
+ lastFlushAt: undefined
35070
+ }
35071
+ });
35072
+ const pending = new Map;
35073
+ let activeTurnId;
35074
+ let timer;
35075
+ let timerDueAt = 0;
35076
+ let lastFlushAt = 0;
35077
+ let updatesSinceFlush = 0;
35078
+ let coalescedUpdates = 0;
35079
+ let currentInterval = BASE_FRAME_INTERVAL_MS;
35080
+ let acceptingUpdates = false;
35081
+ let disposed = false;
35082
+ function key(kind, partId) {
35083
+ return `${kind}:${partId}`;
35084
+ }
35085
+ function intervalFor(contentLength, updateCount) {
35086
+ const sizePressure = Math.log2(1 + contentLength / 4096) * 8;
35087
+ const updatePressure = Math.max(0, updateCount - 3) * 2;
35088
+ return Math.round(Math.max(MIN_FRAME_INTERVAL_MS, Math.min(MAX_FRAME_INTERVAL_MS, BASE_FRAME_INTERVAL_MS + sizePressure + updatePressure)));
35089
+ }
35090
+ function schedule() {
35091
+ if (disposed || pending.size === 0)
35092
+ return;
35093
+ const now = Date.now();
35094
+ const maxLength = Math.max(0, ...[...pending.values()].map((entry) => entry.content.length));
35095
+ const interval = intervalFor(maxLength, updatesSinceFlush);
35096
+ currentInterval = interval;
35097
+ const elapsed = now - lastFlushAt;
35098
+ if (lastFlushAt === 0 || elapsed >= interval) {
35099
+ flush();
35100
+ return;
35101
+ }
35102
+ const dueAt = lastFlushAt + interval;
35103
+ if (timer && dueAt <= timerDueAt)
35104
+ return;
35105
+ if (timer)
35106
+ clearTimeout(timer);
35107
+ timerDueAt = dueAt;
35108
+ timer = setTimeout(flush, Math.max(0, dueAt - now));
35109
+ }
35110
+ function flush() {
35111
+ if (disposed)
35112
+ return;
35113
+ if (timer) {
35114
+ clearTimeout(timer);
35115
+ timer = undefined;
35116
+ }
35117
+ timerDueAt = 0;
35118
+ if (pending.size === 0)
35119
+ return;
35120
+ const entries = [...pending.values()];
35121
+ pending.clear();
35122
+ const now = Date.now();
35123
+ lastFlushAt = now;
35124
+ updatesSinceFlush = 0;
35125
+ setState(produce((draft) => {
35126
+ draft.diagnostics.revision += 1;
35127
+ draft.diagnostics.flushes += 1;
35128
+ draft.diagnostics.coalescedUpdates = coalescedUpdates;
35129
+ draft.diagnostics.lastIntervalMs = currentInterval;
35130
+ draft.diagnostics.lastFlushAt = now;
35131
+ const revision = draft.diagnostics.revision;
35132
+ for (const entry of entries) {
35133
+ const target = entry.kind === "text" ? draft.text : draft.reasoning;
35134
+ target[entry.partId] = {
35135
+ content: entry.content,
35136
+ revision
35137
+ };
35138
+ }
35139
+ }));
35140
+ }
35141
+ function clearVisible() {
35142
+ pending.clear();
35143
+ updatesSinceFlush = 0;
35144
+ if (timer) {
35145
+ clearTimeout(timer);
35146
+ timer = undefined;
35147
+ }
35148
+ timerDueAt = 0;
35149
+ setState(produce((draft) => {
35150
+ draft.text = {};
35151
+ draft.reasoning = {};
35152
+ }));
35153
+ }
35154
+ function update(kind, partId, content, turnId) {
35155
+ if (disposed || !acceptingUpdates || turnId !== activeTurnId)
35156
+ return;
35157
+ const pendingKey = key(kind, partId);
35158
+ const existing = pending.get(pendingKey);
35159
+ const visible = kind === "text" ? state.text[partId]?.content : state.reasoning[partId]?.content;
35160
+ if (existing?.content === content || !existing && visible === content)
35161
+ return;
35162
+ if (existing)
35163
+ coalescedUpdates += 1;
35164
+ pending.set(pendingKey, {
35165
+ kind,
35166
+ partId,
35167
+ content
35168
+ });
35169
+ updatesSinceFlush += 1;
35170
+ schedule();
35171
+ }
35172
+ function durableContent(messages, partId, kind) {
35173
+ for (const message of messages) {
35174
+ const part = message.parts.find((candidate2) => candidate2.id === partId);
35175
+ if (!part)
35176
+ continue;
35177
+ if (kind === "text") {
35178
+ if (typeof part.payload === "string")
35179
+ return part.payload;
35180
+ if (part.payload && typeof part.payload === "object" && "text" in part.payload) {
35181
+ const value = part.payload.text;
35182
+ return typeof value === "string" ? value : undefined;
35183
+ }
35184
+ return;
35185
+ }
35186
+ if (part.payload && typeof part.payload === "object") {
35187
+ const payload = part.payload;
35188
+ if (typeof payload.rationale === "string")
35189
+ return payload.rationale;
35190
+ if (typeof payload.text === "string")
35191
+ return payload.text;
35192
+ }
35193
+ return typeof part.payload === "string" ? part.payload : undefined;
35194
+ }
35195
+ return;
35196
+ }
35197
+ function reconciliationRemovals(kind, visible, messages) {
35198
+ const removals = [];
35199
+ for (const [partId, entry] of Object.entries(visible)) {
35200
+ const durable = durableContent(messages, partId, kind);
35201
+ if (durable === entry.content)
35202
+ removals.push(partId);
35203
+ }
35204
+ return removals;
35205
+ }
35206
+ function reconcile2(messages, runningTurnId) {
35207
+ if (disposed)
35208
+ return;
35209
+ if (!runningTurnId) {
35210
+ clearVisible();
35211
+ activeTurnId = undefined;
35212
+ acceptingUpdates = false;
35213
+ return;
35214
+ }
35215
+ if (activeTurnId && activeTurnId !== runningTurnId) {
35216
+ clearVisible();
35217
+ lastFlushAt = 0;
35218
+ currentInterval = BASE_FRAME_INTERVAL_MS;
35219
+ }
35220
+ if (activeTurnId !== runningTurnId)
35221
+ acceptingUpdates = true;
35222
+ activeTurnId = runningTurnId;
35223
+ const textRemovals = reconciliationRemovals("text", state.text, messages);
35224
+ const reasoningRemovals = reconciliationRemovals("reasoning", state.reasoning, messages);
35225
+ if (textRemovals.length === 0 && reasoningRemovals.length === 0)
35226
+ return;
35227
+ setState(produce((draft) => {
35228
+ for (const partId of textRemovals)
35229
+ delete draft.text[partId];
35230
+ for (const partId of reasoningRemovals)
35231
+ delete draft.reasoning[partId];
35232
+ }));
35233
+ }
35234
+ function beginTurn(turnId) {
35235
+ if (disposed)
35236
+ return;
35237
+ if (activeTurnId !== turnId) {
35238
+ clearVisible();
35239
+ lastFlushAt = 0;
35240
+ currentInterval = BASE_FRAME_INTERVAL_MS;
35241
+ }
35242
+ activeTurnId = turnId;
35243
+ acceptingUpdates = true;
35244
+ }
35245
+ function finishTurn(turnId) {
35246
+ if (disposed || activeTurnId !== turnId)
35247
+ return;
35248
+ flush();
35249
+ acceptingUpdates = false;
35250
+ }
35251
+ function reset2() {
35252
+ if (disposed)
35253
+ return;
35254
+ clearVisible();
35255
+ activeTurnId = undefined;
35256
+ acceptingUpdates = false;
35257
+ lastFlushAt = 0;
35258
+ coalescedUpdates = 0;
35259
+ currentInterval = BASE_FRAME_INTERVAL_MS;
35260
+ setState("diagnostics", {
35261
+ revision: 0,
35262
+ flushes: 0,
35263
+ coalescedUpdates: 0,
35264
+ lastIntervalMs: BASE_FRAME_INTERVAL_MS,
35265
+ lastFlushAt: undefined
35266
+ });
35267
+ }
35268
+ function dispose2() {
35269
+ if (disposed)
35270
+ return;
35271
+ clearVisible();
35272
+ disposed = true;
35273
+ }
35274
+ return {
35275
+ state,
35276
+ beginTurn,
35277
+ finishTurn,
35278
+ update,
35279
+ updateText(partId, content, turnId) {
35280
+ update("text", partId, content, turnId);
35281
+ },
35282
+ updateReasoning(partId, content, turnId) {
35283
+ update("reasoning", partId, content, turnId);
35284
+ },
35285
+ reconcile: reconcile2,
35286
+ reset: reset2,
35287
+ flush,
35288
+ dispose: dispose2
35289
+ };
35290
+ }
35291
+ var MIN_FRAME_INTERVAL_MS = 24, BASE_FRAME_INTERVAL_MS = 24, MAX_FRAME_INTERVAL_MS = 96;
35292
+ var init_stream_engine = __esm(() => {
35293
+ init_store2();
35294
+ });
35295
+
34989
35296
  // src/agent-tui/reasoning.ts
34990
35297
  function parseReasoning(text) {
34991
35298
  const trimmed = normalizeReasoningSummary(text);
@@ -36475,6 +36782,34 @@ var init_renderers2 = __esm(() => {
36475
36782
  TOOL_DETAIL_MAX_BYTES = 32 * 1024;
36476
36783
  });
36477
36784
 
36785
+ // src/agent-tui/transcript/projection.ts
36786
+ function createTranscriptProjection(store, width) {
36787
+ const historyMessages = createMemo(() => {
36788
+ const runningTurnId = store.snapshot.runningTurnId;
36789
+ return runningTurnId ? store.snapshot.messages.filter((message) => message.turnId !== runningTurnId) : store.snapshot.messages;
36790
+ });
36791
+ const activeMessages = createMemo(() => {
36792
+ const runningTurnId = store.snapshot.runningTurnId;
36793
+ return runningTurnId ? store.snapshot.messages.filter((message) => message.turnId === runningTurnId) : [];
36794
+ });
36795
+ const activeMessageIds = createMemo(() => new Set(activeMessages().map((message) => message.id)));
36796
+ const historyToolCalls = createMemo(() => {
36797
+ const runningTurnId = store.snapshot.runningTurnId;
36798
+ return runningTurnId ? store.snapshot.toolCalls.filter((toolCall) => toolCall.turnId !== runningTurnId && (!toolCall.messageId || !activeMessageIds().has(toolCall.messageId))) : store.snapshot.toolCalls;
36799
+ });
36800
+ const activeToolCalls = createMemo(() => {
36801
+ const runningTurnId = store.snapshot.runningTurnId;
36802
+ return runningTurnId ? store.snapshot.toolCalls.filter((toolCall) => toolCall.turnId === runningTurnId || (toolCall.messageId ? activeMessageIds().has(toolCall.messageId) : toolCall.turnId === undefined)) : [];
36803
+ });
36804
+ const historyRows = createMemo(() => projectMessagesToRows(historyMessages(), width(), undefined, historyToolCalls()));
36805
+ const activeRows = createMemo(() => projectMessagesToRows(activeMessages(), width(), store.snapshot.runningTurnId, activeToolCalls(), store.snapshot.toolInputPreviews));
36806
+ return createMemo(() => [...historyRows(), ...activeRows()]);
36807
+ }
36808
+ var init_projection = __esm(() => {
36809
+ init_solid();
36810
+ init_renderers2();
36811
+ });
36812
+
36478
36813
  // src/agent-tui/context/store.tsx
36479
36814
  function proxyRefreshQuery() {
36480
36815
  return {
@@ -36493,6 +36828,7 @@ function TuiStoreProvider(props) {
36493
36828
  setStore: _set,
36494
36829
  actions
36495
36830
  } = createFaraiStore(workspace);
36831
+ const transcript = createTranscriptStreamEngine();
36496
36832
  let statusTimer;
36497
36833
  const proxyRefreshes = new Map;
36498
36834
  const mcpRefreshes = new Map;
@@ -36514,7 +36850,7 @@ function TuiStoreProvider(props) {
36514
36850
  actions.updateNoticeSet(notice);
36515
36851
  });
36516
36852
  }
36517
- const timelineRows = createMemo(() => projectMessagesToRows(store.snapshot.messages, Math.max(1, dims().width - 4), store.snapshot.runningTurnId, store.snapshot.toolCalls, store.snapshot.toolInputPreviews));
36853
+ const timelineRows = createTranscriptProjection(store, () => Math.max(1, dims().width - 4));
36518
36854
  function setStatusDetail(detail, timeoutMs) {
36519
36855
  if (disposed)
36520
36856
  return;
@@ -36582,6 +36918,7 @@ function TuiStoreProvider(props) {
36582
36918
  if (disposed || store.activeSessionId !== sid || snapshotGenerations.get(sid) !== generation)
36583
36919
  return;
36584
36920
  actions.snapshotApplied(snapshot);
36921
+ transcript.reconcile(snapshot.messages, snapshot.runningTurnId);
36585
36922
  for (const entry2 of promptHistoryFromMessages(snapshot.messages)) {
36586
36923
  actions.promptHistoryAdd(entry2, "session");
36587
36924
  }
@@ -36611,12 +36948,16 @@ function TuiStoreProvider(props) {
36611
36948
  return;
36612
36949
  if (sessionId === store.activeSessionId)
36613
36950
  return;
36951
+ transcript.reset();
36614
36952
  actions.activeSessionSet(sessionId);
36615
36953
  const pendingSubmission = promptSubmissions.get(sessionId);
36616
36954
  if (pendingSubmission)
36617
36955
  pendingSubmission.generation = actions.promptSubmissionStarted();
36618
36956
  props.onActiveSessionChange?.(sessionId);
36619
36957
  port.setActiveSession(sessionId);
36958
+ const runningTurnId = port.getRunningTurnId?.(sessionId);
36959
+ if (runningTurnId)
36960
+ transcript.beginTurn(runningTurnId);
36620
36961
  try {
36621
36962
  await requestSnapshotRefresh(sessionId);
36622
36963
  } catch (error) {
@@ -37113,9 +37454,11 @@ function TuiStoreProvider(props) {
37113
37454
  setStatusDetail,
37114
37455
  refreshSnapshot: requestSnapshotRefresh,
37115
37456
  refreshSessions,
37116
- onSnapshot: () => {
37117
- return;
37118
- }
37457
+ onSnapshot: (snapshot) => transcript.reconcile(snapshot.messages, snapshot.runningTurnId),
37458
+ onTurnStarted: (turnId) => transcript.beginTurn(turnId),
37459
+ onTurnFinished: (turnId) => transcript.finishTurn(turnId),
37460
+ onStreamText: (partId, text, turnId) => transcript.updateText(partId, text, turnId),
37461
+ onStreamReasoning: (partId, rationale, turnId) => transcript.updateReasoning(partId, rationale, turnId)
37119
37462
  }, (error) => {
37120
37463
  if (!disposed)
37121
37464
  actions.errorSet(error instanceof Error ? error.message : String(error));
@@ -37123,8 +37466,9 @@ function TuiStoreProvider(props) {
37123
37466
  const off = port.event.on((event) => {
37124
37467
  if (disposed)
37125
37468
  return;
37126
- if (event.type === "store.changed" || event.type === "store.batch")
37469
+ if (event.type === "store.changed" || event.type === "store.batch" || event.type === "turn.started") {
37127
37470
  invalidateSnapshot(event.sessionId);
37471
+ }
37128
37472
  eventDispatcher.dispatch(event);
37129
37473
  });
37130
37474
  onCleanup(() => {
@@ -37135,6 +37479,7 @@ function TuiStoreProvider(props) {
37135
37479
  mcpOverlayGeneration += 1;
37136
37480
  off();
37137
37481
  eventDispatcher.dispose();
37482
+ transcript.dispose();
37138
37483
  if (statusTimer)
37139
37484
  clearTimeout(statusTimer);
37140
37485
  });
@@ -37180,6 +37525,7 @@ function TuiStoreProvider(props) {
37180
37525
  store,
37181
37526
  actions,
37182
37527
  timelineRows,
37528
+ transcript,
37183
37529
  submitPrompt,
37184
37530
  submitUserInput,
37185
37531
  answerUserInputQuestion,
@@ -37264,7 +37610,8 @@ var init_store4 = __esm(() => {
37264
37610
  init_solid();
37265
37611
  init_store3();
37266
37612
  init_runtime2();
37267
- init_renderers2();
37613
+ init_stream_engine();
37614
+ init_projection();
37268
37615
  StoreContext = createContext();
37269
37616
  });
37270
37617
 
@@ -39846,7 +40193,10 @@ function KeyboardController() {
39846
40193
  for (const message of [...tui.store.snapshot.messages].reverse()) {
39847
40194
  if (message.role !== "assistant")
39848
40195
  continue;
39849
- const parts = message.parts.map((part) => {
40196
+ const parts = message.parts.filter((part) => part.type === "text").map((part) => {
40197
+ const streamed = tui.transcript.state.text[part.id]?.content;
40198
+ if (streamed !== undefined)
40199
+ return streamed;
39850
40200
  if (typeof part.payload === "string")
39851
40201
  return part.payload;
39852
40202
  if (part.payload && typeof part.payload === "object" && "text" in part.payload) {
@@ -40244,11 +40594,13 @@ function AssistantMessage(props) {
40244
40594
  insertNode(_el$, _el$2);
40245
40595
  setProp(_el$, "style", {
40246
40596
  flexDirection: "column",
40597
+ flexShrink: 0,
40247
40598
  marginBottom: 1
40248
40599
  });
40249
40600
  insertNode(_el$2, _el$3);
40250
40601
  setProp(_el$2, "style", {
40251
40602
  flexDirection: "row",
40603
+ flexShrink: 0,
40252
40604
  minWidth: 0
40253
40605
  });
40254
40606
  insert(_el$2, createComponent2(TranscriptMarker, {
@@ -40264,23 +40616,23 @@ function AssistantMessage(props) {
40264
40616
  minWidth: 0
40265
40617
  });
40266
40618
  setProp(_el$4, "width", "100%");
40619
+ setProp(_el$4, "streaming", true);
40620
+ setProp(_el$4, "internalBlockMode", "top-level");
40267
40621
  setProp(_el$4, "tableOptions", {
40268
40622
  style: "columns",
40269
40623
  widthMode: "content",
40270
40624
  cellPaddingX: 1
40271
40625
  });
40272
40626
  effect((_p$) => {
40273
- var _v$ = props.row.text, _v$2 = props.row.streaming, _v$3 = syntax(), _v$4 = COLOR.markdownText;
40627
+ var _v$ = props.streamedText ?? props.row.text, _v$2 = syntax(), _v$3 = COLOR.markdownText;
40274
40628
  _v$ !== _p$.e && (_p$.e = setProp(_el$4, "content", _v$, _p$.e));
40275
- _v$2 !== _p$.t && (_p$.t = setProp(_el$4, "streaming", _v$2, _p$.t));
40276
- _v$3 !== _p$.a && (_p$.a = setProp(_el$4, "syntaxStyle", _v$3, _p$.a));
40277
- _v$4 !== _p$.o && (_p$.o = setProp(_el$4, "fg", _v$4, _p$.o));
40629
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$4, "syntaxStyle", _v$2, _p$.t));
40630
+ _v$3 !== _p$.a && (_p$.a = setProp(_el$4, "fg", _v$3, _p$.a));
40278
40631
  return _p$;
40279
40632
  }, {
40280
40633
  e: undefined,
40281
40634
  t: undefined,
40282
- a: undefined,
40283
- o: undefined
40635
+ a: undefined
40284
40636
  });
40285
40637
  return _el$;
40286
40638
  })();
@@ -40359,18 +40711,24 @@ function ReasoningRow(props) {
40359
40711
  const tui = useTuiStore();
40360
40712
  const dims = useTerminalDimensions();
40361
40713
  const expanded = () => Boolean(tui.store.ui.expandedCells[props.row.id]);
40362
- const label = () => truncateLine2(props.row.title === "reasoning" ? "thinking" : props.row.title, Math.max(1, dims().width - 4));
40714
+ const content = createMemo(() => props.streamedReasoning === undefined ? {
40715
+ title: props.row.title,
40716
+ body: props.row.body
40717
+ } : parseReasoning(props.streamedReasoning));
40718
+ const label = () => truncateLine2(content().title === "reasoning" ? "thinking" : content().title, Math.max(1, dims().width - 4));
40363
40719
  const toggleClick = createPrimaryClickGesture(() => tui.actions.cellExpandedToggle(props.row.id));
40364
40720
  return (() => {
40365
40721
  var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text");
40366
40722
  insertNode(_el$, _el$2);
40367
40723
  setProp(_el$, "style", {
40368
40724
  flexDirection: "column",
40725
+ flexShrink: 0,
40369
40726
  marginBottom: 1
40370
40727
  });
40371
40728
  insertNode(_el$2, _el$3);
40372
40729
  setProp(_el$2, "style", {
40373
- flexDirection: "row"
40730
+ flexDirection: "row",
40731
+ flexShrink: 0
40374
40732
  });
40375
40733
  spread(_el$2, toggleClick, true);
40376
40734
  insert(_el$2, createComponent2(TranscriptMarker, {
@@ -40384,24 +40742,24 @@ function ReasoningRow(props) {
40384
40742
  insert(_el$3, label);
40385
40743
  insert(_el$, createComponent2(Show, {
40386
40744
  get when() {
40387
- return memo2(() => !!expanded())() && props.row.body.trim();
40745
+ return memo2(() => !!expanded())() && content().body.trim();
40388
40746
  },
40389
40747
  get children() {
40390
40748
  return createComponent2(ExpandedPanel, {
40391
40749
  get children() {
40392
40750
  var _el$4 = createElement("markdown");
40751
+ setProp(_el$4, "streaming", true);
40752
+ setProp(_el$4, "internalBlockMode", "top-level");
40393
40753
  effect((_p$) => {
40394
- var _v$ = props.row.body, _v$2 = props.row.streaming, _v$3 = syntax(), _v$4 = COLOR.dim;
40754
+ var _v$ = content().body, _v$2 = syntax(), _v$3 = COLOR.dim;
40395
40755
  _v$ !== _p$.e && (_p$.e = setProp(_el$4, "content", _v$, _p$.e));
40396
- _v$2 !== _p$.t && (_p$.t = setProp(_el$4, "streaming", _v$2, _p$.t));
40397
- _v$3 !== _p$.a && (_p$.a = setProp(_el$4, "syntaxStyle", _v$3, _p$.a));
40398
- _v$4 !== _p$.o && (_p$.o = setProp(_el$4, "fg", _v$4, _p$.o));
40756
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$4, "syntaxStyle", _v$2, _p$.t));
40757
+ _v$3 !== _p$.a && (_p$.a = setProp(_el$4, "fg", _v$3, _p$.a));
40399
40758
  return _p$;
40400
40759
  }, {
40401
40760
  e: undefined,
40402
40761
  t: undefined,
40403
- a: undefined,
40404
- o: undefined
40762
+ a: undefined
40405
40763
  });
40406
40764
  return _el$4;
40407
40765
  }
@@ -40430,6 +40788,7 @@ var init_reasoning_cell = __esm(() => {
40430
40788
  init_expanded_panel();
40431
40789
  init_transcript_marker();
40432
40790
  init_mouse();
40791
+ init_reasoning();
40433
40792
  });
40434
40793
 
40435
40794
  // src/agent-tui/filetype.ts
@@ -42094,12 +42453,18 @@ function FaraiRow(props) {
42094
42453
  return createComponent2(AssistantMessage, {
42095
42454
  get row() {
42096
42455
  return props.row;
42456
+ },
42457
+ get streamedText() {
42458
+ return props.streamedText;
42097
42459
  }
42098
42460
  });
42099
42461
  case "thinking":
42100
42462
  return createComponent2(ReasoningRow, {
42101
42463
  get row() {
42102
42464
  return props.row;
42465
+ },
42466
+ get streamedReasoning() {
42467
+ return props.streamedReasoning;
42103
42468
  }
42104
42469
  });
42105
42470
  case "tool":
@@ -42225,7 +42590,16 @@ function Transcript() {
42225
42590
  byId
42226
42591
  };
42227
42592
  });
42228
- const rawRows = createMemo(() => tui.store.snapshot.messages.flatMap((message) => message.parts.map((part) => `${message.role}.${part.type} ${formatPayload(part.payload, 16000)}`)));
42593
+ const rawRows = createMemo(() => tui.store.snapshot.messages.flatMap((message) => message.parts.map((part) => {
42594
+ const streamText = tui.transcript.state.text[part.id]?.content;
42595
+ const streamReasoning = tui.transcript.state.reasoning[part.id]?.content;
42596
+ const payload = streamText !== undefined ? {
42597
+ text: streamText
42598
+ } : streamReasoning !== undefined ? {
42599
+ rationale: streamReasoning
42600
+ } : part.payload;
42601
+ return `${message.role}.${part.type} ${formatPayload(payload, 16000)}`;
42602
+ })));
42229
42603
  createEffect(() => {
42230
42604
  const request = tui.store.ui.messageNavigation;
42231
42605
  const userIds = transcriptRows().filter((row) => row.kind === "user").map((row) => row.id);
@@ -42242,6 +42616,7 @@ function Transcript() {
42242
42616
  typeof _ref$ === "function" ? use(_ref$, _el$) : scrollRef = _el$;
42243
42617
  setProp(_el$, "stickyScroll", true);
42244
42618
  setProp(_el$, "stickyStart", "bottom");
42619
+ setProp(_el$, "viewportCulling", true);
42245
42620
  setProp(_el$, "scrollbarOptions", {
42246
42621
  visible: false
42247
42622
  });
@@ -42307,6 +42682,12 @@ function Transcript() {
42307
42682
  children: (id2) => createComponent2(TranscriptRow, {
42308
42683
  get row() {
42309
42684
  return indexedRows().byId.get(id2);
42685
+ },
42686
+ get streamedText() {
42687
+ return tui.transcript.state.text[id2]?.content;
42688
+ },
42689
+ get streamedReasoning() {
42690
+ return tui.transcript.state.reasoning[id2]?.content;
42310
42691
  }
42311
42692
  })
42312
42693
  });
@@ -42422,12 +42803,19 @@ function TranscriptRow(props) {
42422
42803
  var _el$1 = createElement("box");
42423
42804
  setProp(_el$1, "style", {
42424
42805
  flexDirection: "column",
42806
+ flexShrink: 0,
42425
42807
  paddingLeft: isUser ? 0 : 1,
42426
42808
  paddingRight: isUser ? 0 : 1
42427
42809
  });
42428
42810
  insert(_el$1, createComponent2(FaraiRow, {
42429
42811
  get row() {
42430
42812
  return props.row;
42813
+ },
42814
+ get streamedText() {
42815
+ return props.streamedText;
42816
+ },
42817
+ get streamedReasoning() {
42818
+ return props.streamedReasoning;
42431
42819
  }
42432
42820
  }));
42433
42821
  effect((_$p) => setProp(_el$1, "id", props.row.id, _$p));
@@ -49967,5 +50355,5 @@ Examples:
49967
50355
  `);
49968
50356
  }
49969
50357
 
49970
- //# debugId=231A9E4A58F7AFCB64756E2164756E21
50358
+ //# debugId=90702708DD95C63C64756E2164756E21
49971
50359
  //# sourceMappingURL=index.js.map