@meistrari/remy-cli 1.8.0 → 1.10.0

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 (3) hide show
  1. package/README.md +4 -2
  2. package/dist/remy.js +294 -55
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -36,7 +36,7 @@ The dashboard is the starting point for all interactive work. It refreshes the v
36
36
 
37
37
  ### Start work
38
38
 
39
- Press `n` in the dashboard. The new-session flow asks what you want done, selects a GitHub installation when needed, suggests repositories based on your request, and lets you review those suggestions. Before creating the session, choose the agent model and reasoning effort. Reasoning starts at `high` by default. Remy then opens the session and streams its activity.
39
+ Press `n` in the dashboard. The new-session flow asks what you want done, selects a GitHub installation when needed, suggests repositories based on your request, and lets you review those suggestions. Before creating the session, choose the agent model and reasoning effort. The wizard starts with `gpt-6-astra` and `medium` reasoning by default. Remy then opens the session and streams its activity.
40
40
 
41
41
  Use `Tab` while writing the request to complete a local file path. Remy attaches selected paths when you confirm the request. On macOS, `Ctrl+V` adds a PNG from the system clipboard to the request; Remy attaches it on confirmation. Remy uploads files before creating the session.
42
42
 
@@ -44,6 +44,8 @@ Use `Tab` while writing the request to complete a local file path. Remy attaches
44
44
 
45
45
  Use Up/Down to select a session in the dashboard, Left/Right to load the adjacent page, and `Enter` to open it. After the dashboard list or repository-review list has focus, Vim keys work too: `j`/`k` move, `h`/`l` go to the previous/next dashboard page (or clear/mark a repository), `G` goes to the last row, and `gg` goes to the first row. Press `Shift+L` in the dashboard to start a logout confirmation. In a session, type a follow-up and press `Enter` to send it.
46
46
 
47
+ The **Live reasoning preview** shows the newest main-agent summary as it arrives. It is a short, best-effort preview—not private chain-of-thought. Long previews show **Preview shortened.** Opening or reconnecting partway through a summary may show no preview; the final summary still appears in activity history. Disconnecting or finishing the item clears the live preview.
48
+
47
49
  Published files and Tela Pages appear in the timeline. A Tela Page row includes its title and canonical URL so you can open it directly from the terminal.
48
50
 
49
51
  When Remy creates a plan, the session shows its checklist in the timeline and keeps `Plan <done>/<total>` with the current item pinned above the composer. Updates change the same checklist instead of producing repeated rows, and an unfinished plan remains visible after reconnecting or between turns. The collapsed timeline shows up to five plan items; press `Ctrl+O` for the complete checklist and activity detail.
@@ -74,7 +76,7 @@ The interactive dashboard is the recommended path. Use these commands when a scr
74
76
  remy new --repository owner/repository "Add a health check endpoint"
75
77
  ```
76
78
 
77
- Repeat `--repository` to select more repositories from the same GitHub installation. If you do not select a repository, supply `--installation <github-installation-id>`. Repeat `--attach <path>` to upload files. Direct creation uses `high` reasoning unless `--reasoning-effort` overrides it. `remy new` creates the session from the prompt and flags, then opens it when the terminal is interactive.
79
+ Repeat `--repository` to select more repositories from the same GitHub installation. If you do not select a repository, supply `--installation <github-installation-id>`. Repeat `--attach <path>` to upload files. Direct creation uses `medium` reasoning unless `--reasoning-effort` overrides it; omit `--model` to use the server's `gpt-6-astra` default. `remy new` creates the session from the prompt and flags, then opens it when the terminal is interactive.
78
80
 
79
81
  ### Open a known session
80
82
 
package/dist/remy.js CHANGED
@@ -32083,11 +32083,13 @@ var agentCommandSchema = zod_default2.union([
32083
32083
  ]);
32084
32084
  // ../../packages/agents-protocol/src/agent-model.ts
32085
32085
  var codexModelIds = [
32086
+ "gpt-6-astra",
32086
32087
  "gpt-5.6-sol",
32088
+ "gpt-5.6-terra",
32089
+ "gpt-5.6-luna",
32087
32090
  "gpt-5.5",
32088
32091
  "gpt-5.4",
32089
32092
  "gpt-5.4-mini",
32090
- "gpt-5.3-codex",
32091
32093
  "gpt-5.2"
32092
32094
  ];
32093
32095
  var claudeModelIds = [
@@ -32553,41 +32555,55 @@ var agentEventSchema = zod_default2.discriminatedUnion("type", [
32553
32555
  // ../../packages/agents-protocol/src/agent-presentation.ts
32554
32556
  var CODEX_CONTEXT_WINDOW = 272000;
32555
32557
  var codexModelPresentation = {
32558
+ "gpt-6-astra": {
32559
+ label: "GPT 6.0 Astra",
32560
+ description: "Our most capable model for complex, demanding work.",
32561
+ relativeCost: 5,
32562
+ maxInputTokens: CODEX_CONTEXT_WINDOW,
32563
+ isMultiModal: true
32564
+ },
32556
32565
  "gpt-5.6-sol": {
32557
- label: "GPT-5.6-Sol",
32558
- description: "Latest frontier agentic coding model.",
32566
+ label: "GPT 5.6 Sol",
32567
+ description: "Reliable agentic workhorse for everyday tasks.",
32559
32568
  relativeCost: 5,
32560
32569
  maxInputTokens: CODEX_CONTEXT_WINDOW,
32561
32570
  isMultiModal: true
32562
32571
  },
32572
+ "gpt-5.6-terra": {
32573
+ label: "GPT 5.6 Terra",
32574
+ description: "Balanced agentic coding model for everyday work.",
32575
+ relativeCost: 3,
32576
+ maxInputTokens: CODEX_CONTEXT_WINDOW,
32577
+ isMultiModal: true
32578
+ },
32579
+ "gpt-5.6-luna": {
32580
+ label: "GPT 5.6 Luna",
32581
+ description: "Fast and affordable agentic coding model.",
32582
+ relativeCost: 2,
32583
+ maxInputTokens: CODEX_CONTEXT_WINDOW,
32584
+ isMultiModal: true
32585
+ },
32563
32586
  "gpt-5.5": {
32564
- label: "GPT-5.5",
32565
- description: "Frontier model for complex coding, research, and real-world work.",
32587
+ label: "GPT 5.5",
32588
+ description: "Proven previous-generation model for coding and general work.",
32566
32589
  relativeCost: 4,
32567
32590
  maxInputTokens: CODEX_CONTEXT_WINDOW,
32568
32591
  isMultiModal: true
32569
32592
  },
32570
32593
  "gpt-5.4": {
32571
- label: "GPT-5.4",
32594
+ label: "GPT 5.4",
32572
32595
  description: "Strong model for everyday coding.",
32573
32596
  relativeCost: 3,
32574
32597
  maxInputTokens: CODEX_CONTEXT_WINDOW,
32575
32598
  isMultiModal: true
32576
32599
  },
32577
32600
  "gpt-5.4-mini": {
32578
- label: "GPT-5.4-Mini",
32601
+ label: "GPT 5.4 Mini",
32579
32602
  description: "Small, fast, and cost-efficient model for simpler coding tasks.",
32580
32603
  relativeCost: 2,
32581
32604
  maxInputTokens: CODEX_CONTEXT_WINDOW,
32582
32605
  isMultiModal: true
32583
32606
  },
32584
- "gpt-5.3-codex": {
32585
- label: "GPT-5.3-Codex",
32586
- description: "Code-tuned model for focused engineering tasks.",
32587
- relativeCost: 3,
32588
- maxInputTokens: CODEX_CONTEXT_WINDOW,
32589
- isMultiModal: true
32590
- },
32591
32607
  "gpt-5.2": {
32592
32608
  label: "GPT-5.2",
32593
32609
  description: "Optimized for professional work and long-running agents.",
@@ -32991,7 +33007,8 @@ async function* streamSessionEvents({
32991
33007
  sessionId,
32992
33008
  lastRetainedEventId,
32993
33009
  maxReconnects = 0,
32994
- signal
33010
+ signal,
33011
+ onSynchronized
32995
33012
  }) {
32996
33013
  let retainedCursor = lastRetainedEventId;
32997
33014
  let reconnects = 0;
@@ -33005,7 +33022,7 @@ async function* streamSessionEvents({
33005
33022
  });
33006
33023
  if (!response.body)
33007
33024
  throw new CodingAgentProtocolError("Session event stream response did not include a readable body.");
33008
- for await (const frame of parseSessionEventStream(response.body)) {
33025
+ for await (const frame of parseSessionEventStream(response.body, { onSynchronized })) {
33009
33026
  if (frame.kind === "retained")
33010
33027
  retainedCursor = frame.id;
33011
33028
  yield frame;
@@ -33015,21 +33032,33 @@ async function* streamSessionEvents({
33015
33032
  reconnects += 1;
33016
33033
  }
33017
33034
  }
33018
- async function* parseSessionEventStream(stream) {
33019
- for await (const message of parseServerSentEvents(stream)) {
33035
+ async function* parseSessionEventStream(stream, { onSynchronized } = {}) {
33036
+ for await (const message of parseServerSentEvents(stream, { onSynchronized })) {
33020
33037
  if (message.event !== undefined && message.event !== "session-event")
33021
33038
  continue;
33022
33039
  yield parseSessionEventMessage(message);
33023
33040
  }
33024
33041
  }
33025
- async function* parseServerSentEvents(stream) {
33042
+ async function* parseServerSentEvents(stream, { onSynchronized }) {
33026
33043
  const decoder2 = new TextDecoder;
33027
33044
  const reader = stream.getReader();
33028
33045
  let buffer = "";
33029
33046
  let messageId;
33030
33047
  let eventName;
33031
33048
  let dataLines = [];
33032
- function dispatch() {
33049
+ let frameLineCount = 0;
33050
+ let hasSynchronizationComment = false;
33051
+ let synchronized = false;
33052
+ function dispatch(completeFrame) {
33053
+ const isSynchronizationFrame = completeFrame && frameLineCount === 1 && hasSynchronizationComment;
33054
+ frameLineCount = 0;
33055
+ hasSynchronizationComment = false;
33056
+ if (isSynchronizationFrame) {
33057
+ if (synchronized)
33058
+ throw new CodingAgentProtocolError("Session event stream contained more than one synchronization marker.");
33059
+ synchronized = true;
33060
+ onSynchronized?.();
33061
+ }
33033
33062
  if (dataLines.length === 0) {
33034
33063
  messageId = undefined;
33035
33064
  eventName = undefined;
@@ -33048,7 +33077,10 @@ async function* parseServerSentEvents(stream) {
33048
33077
  }
33049
33078
  function applyLine(line) {
33050
33079
  if (line === "")
33051
- return dispatch();
33080
+ return dispatch(true);
33081
+ frameLineCount += 1;
33082
+ if (line === ": synchronized")
33083
+ hasSynchronizationComment = true;
33052
33084
  if (line.startsWith(":"))
33053
33085
  return null;
33054
33086
  const separatorIndex = line.indexOf(":");
@@ -33086,7 +33118,7 @@ async function* parseServerSentEvents(stream) {
33086
33118
  if (message)
33087
33119
  yield message;
33088
33120
  }
33089
- const finalMessage = dispatch();
33121
+ const finalMessage = dispatch(false);
33090
33122
  if (finalMessage)
33091
33123
  yield finalMessage;
33092
33124
  } finally {
@@ -33696,8 +33728,8 @@ function newSessionReasoningEffortLabel(effort) {
33696
33728
  var newSessionModels = codexModelIds;
33697
33729
  var newSessionReasoningEfforts = selectableAgentReasoningEfforts;
33698
33730
  var defaultNewSessionAgent = {
33699
- model: codexModelIds[0],
33700
- reasoningEffort: "high"
33731
+ model: "gpt-6-astra",
33732
+ reasoningEffort: "medium"
33701
33733
  };
33702
33734
 
33703
33735
  // src/sessions/session-controller.ts
@@ -33860,10 +33892,6 @@ var agentMessageDeltaEventSchema = exports_external2.object({
33860
33892
  type: exports_external2.literal("agent.message.delta"),
33861
33893
  payload: exports_external2.object({ role: exports_external2.string(), delta: exports_external2.string() }).passthrough()
33862
33894
  }).passthrough();
33863
- var agentReasoningSummaryDeltaEventSchema = exports_external2.object({
33864
- type: exports_external2.literal("agent.reasoning.summary.delta"),
33865
- payload: exports_external2.object({ text: exports_external2.string() }).passthrough()
33866
- }).passthrough();
33867
33895
  var publicMessageContentSegmentSchema = exports_external2.discriminatedUnion("type", [
33868
33896
  exports_external2.strictObject({ type: exports_external2.literal("text"), text: exports_external2.string() }),
33869
33897
  exports_external2.strictObject({
@@ -33966,13 +33994,6 @@ function projectEphemeralEvent({ state, event }) {
33966
33994
  previews: { ...state.previews, assistantText: state.previews.assistantText + messageDelta.data.payload.delta }
33967
33995
  };
33968
33996
  }
33969
- const reasoningDelta = agentReasoningSummaryDeltaEventSchema.safeParse(event);
33970
- if (reasoningDelta.success) {
33971
- return {
33972
- ...state,
33973
- previews: { ...state.previews, reasoningText: state.previews.reasoningText + reasoningDelta.data.payload.text }
33974
- };
33975
- }
33976
33997
  return state;
33977
33998
  }
33978
33999
  function projectRetainedEvent({
@@ -34120,8 +34141,7 @@ function projectDurableAgentEvent({ state, event, occurredAt, retainedEventId })
34120
34141
  retainedTurnStarts: { ...state.retainedTurnStarts, [agentEvent.turnId]: { actorType: agentEvent.actor.type, startedAt: occurredAt } },
34121
34142
  messageTurns: Object.fromEntries(Object.entries(state.messageTurns).map(([messageId, turn]) => [messageId, turn.turnId === agentEvent.turnId ? { ...turn, startedAt: occurredAt } : turn]))
34122
34143
  } : state;
34123
- const stateWithCompletedReasoning = agentEvent.type === "agent.reasoning.ended" ? { ...stateWithTurnStart, previews: { ...stateWithTurnStart.previews, reasoningText: "" } } : stateWithTurnStart;
34124
- return appendActivity({ state: stateWithCompletedReasoning, retainedEventId, occurredAt, card: toAgentActivityCard(agentEvent) });
34144
+ return appendActivity({ state: stateWithTurnStart, retainedEventId, occurredAt, card: toAgentActivityCard(agentEvent) });
34125
34145
  }
34126
34146
  function projectAgentWorkObserved({
34127
34147
  state,
@@ -34311,6 +34331,16 @@ var terminalControlPattern = /[\u0000-\u0008\u000B-\u001F\u007F-\u009F]/g;
34311
34331
  function stripAnsi(value) {
34312
34332
  return value.replace(ansiEscapePattern, "").replace(terminalControlPattern, "");
34313
34333
  }
34334
+ function formatReasoningPreview(item) {
34335
+ if (item.status === "absent")
34336
+ return "";
34337
+ const preview = stripAnsi(item.text).trim();
34338
+ if (!item.truncated)
34339
+ return preview;
34340
+ return preview.length > 0 ? `${preview}
34341
+
34342
+ Preview shortened.` : "Preview shortened.";
34343
+ }
34314
34344
  function providerText(raw, fallback) {
34315
34345
  const cleaned = stripAnsi(raw ?? "").trim();
34316
34346
  return cleaned.length > 0 ? cleaned : fallback;
@@ -34328,6 +34358,127 @@ function toSessionPullRequest(detail) {
34328
34358
  return pullRequest ? { number: pullRequest.number, status: pullRequest.status, draft: pullRequest.draft } : null;
34329
34359
  }
34330
34360
 
34361
+ // src/sessions/reasoning-preview.ts
34362
+ var maximumPrefixLength = 2000;
34363
+ var absentItem = { status: "absent" };
34364
+ function initialReasoningPreviewState() {
34365
+ return {
34366
+ synchronized: false,
34367
+ sequenceFloor: -1,
34368
+ runtime: "active",
34369
+ lastSessionStartedEventId: null,
34370
+ item: absentItem
34371
+ };
34372
+ }
34373
+ function reduceReasoningPreview(state, input) {
34374
+ if (input.type === "synchronized")
34375
+ return state.synchronized ? state : { ...state, synchronized: true };
34376
+ const event = input.event;
34377
+ if (event.type === "agent.session.started") {
34378
+ if (event.eventId === state.lastSessionStartedEventId)
34379
+ return state;
34380
+ return {
34381
+ ...state,
34382
+ sequenceFloor: event.sequence,
34383
+ runtime: "active",
34384
+ lastSessionStartedEventId: event.eventId,
34385
+ item: absentItem
34386
+ };
34387
+ }
34388
+ if (event.type === "agent.session.ended") {
34389
+ return {
34390
+ ...state,
34391
+ sequenceFloor: Math.max(state.sequenceFloor, event.sequence),
34392
+ runtime: "ended",
34393
+ item: absentItem
34394
+ };
34395
+ }
34396
+ if (!isMainReasoningOrTurnEvent(event) || state.runtime === "ended")
34397
+ return state;
34398
+ const matchingRetainedEnd = state.item.status === "active" && state.item.turnId === event.turnId && (event.type === "agent.turn.ended" || event.type === "agent.reasoning.ended" && state.item.reasoningId === event.payload.reasoningId);
34399
+ if (event.sequence <= state.sequenceFloor && !matchingRetainedEnd)
34400
+ return state;
34401
+ const advancedState = { ...state, sequenceFloor: Math.max(state.sequenceFloor, event.sequence) };
34402
+ switch (event.type) {
34403
+ case "agent.reasoning.started": {
34404
+ if (!state.synchronized)
34405
+ return advancedState;
34406
+ if (state.item.status === "active" && sameReasoningItem({ item: state.item, turnId: event.turnId, reasoningId: event.payload.reasoningId })) {
34407
+ return advancedState;
34408
+ }
34409
+ return {
34410
+ ...advancedState,
34411
+ item: {
34412
+ status: "active",
34413
+ turnId: event.turnId,
34414
+ reasoningId: event.payload.reasoningId,
34415
+ text: "",
34416
+ truncated: false
34417
+ }
34418
+ };
34419
+ }
34420
+ case "agent.reasoning.summary.delta": {
34421
+ if (state.item.status !== "active" || !sameReasoningItem({ item: state.item, turnId: event.turnId, reasoningId: event.payload.reasoningId })) {
34422
+ return advancedState;
34423
+ }
34424
+ return {
34425
+ ...advancedState,
34426
+ item: appendBoundedPrefix(state.item, event.payload.text)
34427
+ };
34428
+ }
34429
+ case "agent.reasoning.ended": {
34430
+ if (state.item.status !== "active" || !sameReasoningItem({ item: state.item, turnId: event.turnId, reasoningId: event.payload.reasoningId })) {
34431
+ return advancedState;
34432
+ }
34433
+ return { ...advancedState, item: absentItem };
34434
+ }
34435
+ case "agent.turn.started": {
34436
+ if (state.item.status === "active" && state.item.turnId !== event.turnId)
34437
+ return { ...advancedState, item: absentItem };
34438
+ return advancedState;
34439
+ }
34440
+ case "agent.turn.ended": {
34441
+ if (state.item.status === "active" && state.item.turnId === event.turnId)
34442
+ return { ...advancedState, item: absentItem };
34443
+ return advancedState;
34444
+ }
34445
+ }
34446
+ }
34447
+ function isMainReasoningOrTurnEvent(event) {
34448
+ if (event.type !== "agent.reasoning.started" && event.type !== "agent.reasoning.summary.delta" && event.type !== "agent.reasoning.ended" && event.type !== "agent.turn.started" && event.type !== "agent.turn.ended") {
34449
+ return false;
34450
+ }
34451
+ return event.actor.type === "main";
34452
+ }
34453
+ function sameReasoningItem({
34454
+ item,
34455
+ turnId,
34456
+ reasoningId
34457
+ }) {
34458
+ return item.turnId === turnId && item.reasoningId === reasoningId;
34459
+ }
34460
+ function appendBoundedPrefix(item, text) {
34461
+ if (item.truncated)
34462
+ return item;
34463
+ const availableLength = maximumPrefixLength - item.text.length;
34464
+ let retainedLength = Math.min(availableLength, text.length);
34465
+ const reachesSizeBound = item.text.length + retainedLength === maximumPrefixLength;
34466
+ if (reachesSizeBound && retainedLength > 0 && isHighSurrogate(text.charCodeAt(retainedLength - 1)))
34467
+ retainedLength -= 1;
34468
+ const discardedInput = retainedLength < text.length;
34469
+ if (retainedLength === 0) {
34470
+ return discardedInput && !item.truncated ? { ...item, truncated: true } : item;
34471
+ }
34472
+ return {
34473
+ ...item,
34474
+ text: item.text + text.slice(0, retainedLength),
34475
+ truncated: item.truncated || discardedInput
34476
+ };
34477
+ }
34478
+ function isHighSurrogate(codeUnit) {
34479
+ return codeUnit >= 55296 && codeUnit <= 56319;
34480
+ }
34481
+
34331
34482
  // src/sessions/session-controller.ts
34332
34483
  var remoteSessionReconnectPolicy = {
34333
34484
  initialBackoffMs: 1000,
@@ -34366,6 +34517,9 @@ function createRemoteSessionController(dependencies) {
34366
34517
  let stopped = false;
34367
34518
  let state;
34368
34519
  let cachedLastRetainedEventId;
34520
+ let reasoningPreviewState = initialReasoningPreviewState();
34521
+ let reasoningConnectionGeneration = 0;
34522
+ let reasoningHistorySequenceFloor = -1;
34369
34523
  async function start(input) {
34370
34524
  stopped = false;
34371
34525
  ready = new Promise((resolve) => {
@@ -34379,14 +34533,17 @@ function createRemoteSessionController(dependencies) {
34379
34533
  });
34380
34534
  resolveReady();
34381
34535
  publishState();
34382
- if (input.mode === "cold-resume")
34383
- await hydrateRetainedHistoryFromBeginning();
34536
+ if (input.mode === "cold-resume") {
34537
+ const hydrationGeneration = beginReasoningAttempt();
34538
+ await hydrateRetainedHistoryFromBeginning(hydrationGeneration);
34539
+ }
34384
34540
  await streamWithControllerReconnects();
34385
34541
  }
34386
34542
  function stop() {
34387
34543
  if (stopped)
34388
34544
  return;
34389
34545
  stopped = true;
34546
+ invalidateReasoningAttempt({ publish: true });
34390
34547
  abortController.abort(new Error("Remote session controller stopped."));
34391
34548
  }
34392
34549
  function subscribe(listener) {
@@ -34421,10 +34578,14 @@ function createRemoteSessionController(dependencies) {
34421
34578
  publishState();
34422
34579
  }
34423
34580
  function updateDetail(detail) {
34581
+ if (detail.status !== "open")
34582
+ invalidateReasoningAttempt({ publish: false });
34424
34583
  state = updateSessionDetail({ state: getState(), detail });
34584
+ if (detail.status !== "open")
34585
+ state = withoutReasoningPreview(state);
34425
34586
  publishState();
34426
34587
  }
34427
- async function hydrateRetainedHistoryFromBeginning() {
34588
+ async function hydrateRetainedHistoryFromBeginning(generation) {
34428
34589
  let after;
34429
34590
  while (true) {
34430
34591
  if (stopped)
@@ -34436,10 +34597,13 @@ function createRemoteSessionController(dependencies) {
34436
34597
  });
34437
34598
  for (const item of page.data) {
34438
34599
  await reduceFrameAndPersist({
34439
- kind: "retained",
34440
- id: String(item.history_sequence),
34441
- event: "session-event",
34442
- data: item
34600
+ frame: {
34601
+ kind: "retained",
34602
+ id: String(item.history_sequence),
34603
+ event: "session-event",
34604
+ data: item
34605
+ },
34606
+ generation
34443
34607
  });
34444
34608
  }
34445
34609
  if (!page.has_more || !page.next_cursor)
@@ -34453,27 +34617,33 @@ function createRemoteSessionController(dependencies) {
34453
34617
  while (true) {
34454
34618
  if (stopped)
34455
34619
  return;
34620
+ const generation = beginReasoningAttempt();
34456
34621
  state = markSessionConnected(getState());
34457
34622
  publishState();
34458
34623
  try {
34459
34624
  const stream = await dependencies.openEventStream({
34460
34625
  sessionId: dependencies.sessionId,
34461
34626
  lastRetainedEventId: cachedLastRetainedEventId,
34462
- signal: abortController.signal
34627
+ signal: abortController.signal,
34628
+ onSynchronized: () => synchronizeReasoningAttempt(generation)
34463
34629
  });
34464
34630
  for await (const frame of stream) {
34465
34631
  if (stopped)
34466
34632
  return;
34467
- await reduceFrameAndPersist(frame);
34633
+ await reduceFrameAndPersist({ frame, generation });
34468
34634
  }
34635
+ endReasoningAttempt(generation);
34469
34636
  } catch (error93) {
34637
+ endReasoningAttempt(generation);
34470
34638
  if (stopped)
34471
34639
  return;
34472
34640
  if (error93 instanceof Error && error93.name === "SessionProjectionProtocolError")
34473
34641
  throw error93;
34642
+ const refreshGeneration = reasoningConnectionGeneration;
34474
34643
  const detail = await dependencies.getSession({ sessionId: dependencies.sessionId });
34475
- state = updateSessionDetail({ state: getState(), detail });
34476
- publishState();
34644
+ if (stopped || refreshGeneration !== reasoningConnectionGeneration)
34645
+ return;
34646
+ updateDetail(detail);
34477
34647
  }
34478
34648
  if (stopped)
34479
34649
  return;
@@ -34499,17 +34669,86 @@ function createRemoteSessionController(dependencies) {
34499
34669
  backoffMs = Math.min(backoffMs * 2, reconnectPolicy.maxBackoffMs);
34500
34670
  }
34501
34671
  }
34502
- async function reduceFrameAndPersist(frame) {
34503
- const nextState = projectRemoteSessionEvent({ state: getState(), frame });
34672
+ async function reduceFrameAndPersist({ frame, generation }) {
34673
+ const previousState = getState();
34674
+ const projectedState = projectRemoteSessionEvent({ state: previousState, frame });
34675
+ state = projectReasoningPreview({ previousState, projectedState, frame, generation });
34504
34676
  if (frame.kind === "retained") {
34505
- state = nextState;
34506
34677
  await writeCache();
34507
- cachedLastRetainedEventId = nextState.lastRetainedEventId;
34678
+ cachedLastRetainedEventId = projectedState.lastRetainedEventId;
34508
34679
  }
34509
- state = nextState;
34510
34680
  resolveCompletedWaiters();
34681
+ if (stopped)
34682
+ return;
34511
34683
  publishState(frame);
34512
34684
  }
34685
+ function projectReasoningPreview({
34686
+ previousState,
34687
+ projectedState,
34688
+ frame,
34689
+ generation
34690
+ }) {
34691
+ if (generation !== reasoningConnectionGeneration || projectedState.aggregateStatus !== "open")
34692
+ return withoutReasoningPreview(projectedState);
34693
+ if (frame.kind === "retained") {
34694
+ if (previousState.seenRetainedEventIds[frame.id])
34695
+ return projectedState;
34696
+ if (frame.data.history_sequence <= reasoningHistorySequenceFloor)
34697
+ return projectedState;
34698
+ reasoningHistorySequenceFloor = frame.data.history_sequence;
34699
+ } else if (frame.data.event.type !== "agent.reasoning.summary.delta") {
34700
+ return projectedState;
34701
+ }
34702
+ if (frame.data.event.type === "agent.message.ended")
34703
+ return projectedState;
34704
+ const parsed = wrappedAgentEventSchema.safeParse(frame.data.event);
34705
+ if (!parsed.success)
34706
+ return projectedState;
34707
+ reasoningPreviewState = reduceReasoningPreview(reasoningPreviewState, { type: "event", event: parsed.data });
34708
+ return {
34709
+ ...projectedState,
34710
+ previews: {
34711
+ ...projectedState.previews,
34712
+ reasoningText: formatReasoningPreview(reasoningPreviewState.item)
34713
+ }
34714
+ };
34715
+ }
34716
+ function beginReasoningAttempt() {
34717
+ reasoningConnectionGeneration += 1;
34718
+ reasoningPreviewState = initialReasoningPreviewState();
34719
+ reasoningHistorySequenceFloor = -1;
34720
+ if (state)
34721
+ state = withoutReasoningPreview(state);
34722
+ return reasoningConnectionGeneration;
34723
+ }
34724
+ function synchronizeReasoningAttempt(generation) {
34725
+ if (stopped || generation !== reasoningConnectionGeneration || getState().aggregateStatus !== "open")
34726
+ return;
34727
+ reasoningPreviewState = reduceReasoningPreview(reasoningPreviewState, { type: "synchronized" });
34728
+ }
34729
+ function endReasoningAttempt(generation) {
34730
+ if (generation !== reasoningConnectionGeneration)
34731
+ return;
34732
+ invalidateReasoningAttempt({ publish: true });
34733
+ }
34734
+ function invalidateReasoningAttempt({ publish }) {
34735
+ reasoningConnectionGeneration += 1;
34736
+ reasoningPreviewState = initialReasoningPreviewState();
34737
+ reasoningHistorySequenceFloor = -1;
34738
+ if (!state || state.previews.reasoningText.length === 0)
34739
+ return;
34740
+ state = withoutReasoningPreview(state);
34741
+ if (publish)
34742
+ publishState();
34743
+ }
34744
+ function withoutReasoningPreview(currentState) {
34745
+ if (currentState.previews.reasoningText.length === 0)
34746
+ return currentState;
34747
+ return {
34748
+ ...currentState,
34749
+ previews: { ...currentState.previews, reasoningText: "" }
34750
+ };
34751
+ }
34513
34752
  async function writeCache() {
34514
34753
  const currentState = getState();
34515
34754
  const cache = {
@@ -37545,7 +37784,7 @@ var compactMarkRows = 9;
37545
37784
  var compactMinWidth = 48;
37546
37785
  var compactMinHeight = 20;
37547
37786
  var markBrightnessGain = 4.2;
37548
- var remyCliVersion = "1.8.0";
37787
+ var remyCliVersion = "1.10.0";
37549
37788
  async function showRemySplash({
37550
37789
  createRenderer = createRemyRenderer,
37551
37790
  durationMs = splashDurationMs,
@@ -38798,7 +39037,7 @@ async function runAttachedSession({
38798
39037
  environment: dependencies.environment,
38799
39038
  getSession: async ({ sessionId: id }) => await operations.getSession({ client: operations.client, sessionId: id }),
38800
39039
  listSessionEvents: async ({ sessionId: id, limit, after }) => await operations.listSessionEvents({ client: operations.client, sessionId: id, limit, after }),
38801
- openEventStream: ({ sessionId: id, lastRetainedEventId, signal }) => operations.streamSessionEvents({ client: operations.client, sessionId: id, lastRetainedEventId, signal })
39040
+ openEventStream: ({ sessionId: id, lastRetainedEventId, signal, onSynchronized }) => operations.streamSessionEvents({ client: operations.client, sessionId: id, lastRetainedEventId, signal, onSynchronized })
38802
39041
  });
38803
39042
  const interactive = !noTui && !json3 && isInteractiveTerminal(dependencies);
38804
39043
  const shouldPrintJson = !interactive || json3;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/remy-cli",
3
- "version": "1.8.0",
3
+ "version": "1.10.0",
4
4
  "description": "Remy, the Coding Agent terminal client.",
5
5
  "type": "module",
6
6
  "bin": {