@meistrari/remy-cli 1.7.0 → 1.9.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 -0
  2. package/dist/remy.js +336 -81
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -44,6 +44,10 @@ 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
+
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.
50
+
47
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.
48
52
 
49
53
  Drag to select visible text in any Remy view; Remy copies it to the local clipboard and emits OSC 52 for terminal or remote-session clipboard support, then clears the selection highlight. When the conversation has focus, press `Tab` to return to the composer.
package/dist/remy.js CHANGED
@@ -32607,6 +32607,38 @@ var selectableAgentReasoningEfforts = ["low", "medium", "high", "xhigh"];
32607
32607
  // ../../packages/coding-agent-client/src/sessions.ts
32608
32608
  var reasoningEffortSchema = exports_external2.enum(selectableAgentReasoningEfforts);
32609
32609
  var sessionStatusSchema = exports_external2.enum(["open", "completed", "failed", "cancelled"]);
32610
+ var sessionArtifactPresentationSchema = exports_external2.discriminatedUnion("type", [
32611
+ exports_external2.strictObject({ type: exports_external2.literal("image"), caption: exports_external2.string(), alt_text: exports_external2.string() }),
32612
+ exports_external2.strictObject({ type: exports_external2.literal("video"), title: exports_external2.string(), description: exports_external2.string() }),
32613
+ exports_external2.strictObject({ type: exports_external2.literal("file"), title: exports_external2.string(), description: exports_external2.string() })
32614
+ ]);
32615
+ var sessionFileArtifactEventSchema = exports_external2.strictObject({
32616
+ kind: exports_external2.literal("file"),
32617
+ artifact_id: exports_external2.string(),
32618
+ filename: exports_external2.string(),
32619
+ media_type: exports_external2.string(),
32620
+ byte_size: exports_external2.number().int().positive(),
32621
+ presentation: sessionArtifactPresentationSchema
32622
+ });
32623
+ var sessionTelaPageArtifactEventSchema = exports_external2.strictObject({
32624
+ kind: exports_external2.literal("tela_page"),
32625
+ artifact_id: exports_external2.string(),
32626
+ site: exports_external2.string(),
32627
+ title: exports_external2.string(),
32628
+ summary: exports_external2.string().nullable(),
32629
+ surface: exports_external2.enum(["document", "deployed"]),
32630
+ outcome: exports_external2.enum(["created", "updated", "unchanged"]),
32631
+ url: exports_external2.url(),
32632
+ published_at: exports_external2.iso.datetime()
32633
+ });
32634
+ var sessionArtifactEventSchema = exports_external2.discriminatedUnion("kind", [
32635
+ sessionFileArtifactEventSchema,
32636
+ sessionTelaPageArtifactEventSchema
32637
+ ]);
32638
+ var sessionArtifactSchema = exports_external2.discriminatedUnion("kind", [
32639
+ sessionFileArtifactEventSchema.extend({ download_url: exports_external2.string() }),
32640
+ sessionTelaPageArtifactEventSchema.extend({ preview_url: exports_external2.string() })
32641
+ ]);
32610
32642
  var sessionMessageSchema = exports_external2.strictObject({
32611
32643
  id: exports_external2.string(),
32612
32644
  sequence: exports_external2.number().int().positive(),
@@ -32677,15 +32709,6 @@ var interruptSessionResponseSchema = exports_external2.strictObject({
32677
32709
  type: exports_external2.literal("agent.interrupt")
32678
32710
  })
32679
32711
  });
32680
- var sessionPullRequestSchema = exports_external2.strictObject({
32681
- id: exports_external2.string(),
32682
- repository_id: exports_external2.string(),
32683
- number: exports_external2.number().int().positive(),
32684
- title: exports_external2.string(),
32685
- url: exports_external2.string(),
32686
- status: exports_external2.enum(["open", "closed", "merged"]),
32687
- draft: exports_external2.boolean()
32688
- });
32689
32712
  var sessionDetailResponseSchema = exports_external2.object({
32690
32713
  id: exports_external2.string(),
32691
32714
  status: sessionStatusSchema,
@@ -32704,18 +32727,9 @@ var sessionDetailResponseSchema = exports_external2.object({
32704
32727
  id: exports_external2.string(),
32705
32728
  full_name: exports_external2.string()
32706
32729
  })),
32707
- connections: exports_external2.array(sessionConnectionSchema).max(1)
32730
+ connections: exports_external2.array(sessionConnectionSchema).max(1),
32731
+ artifacts: exports_external2.array(sessionArtifactSchema)
32708
32732
  }).passthrough();
32709
- var sessionLifecycleResponseSchema = exports_external2.object({
32710
- id: exports_external2.string(),
32711
- status: sessionStatusSchema,
32712
- repositories: exports_external2.array(exports_external2.strictObject({
32713
- id: exports_external2.string(),
32714
- full_name: exports_external2.string()
32715
- })),
32716
- pull_requests: exports_external2.array(sessionPullRequestSchema),
32717
- connections: exports_external2.array(sessionConnectionSchema).max(1)
32718
- });
32719
32733
  var sessionSourceSchema = exports_external2.discriminatedUnion("provider", [
32720
32734
  exports_external2.object({
32721
32735
  provider: exports_external2.literal("slack"),
@@ -32757,6 +32771,10 @@ var sessionSummarySchema = exports_external2.object({
32757
32771
  created_at: exports_external2.string(),
32758
32772
  updated_at: exports_external2.string().nullable()
32759
32773
  });
32774
+ var telaPagePreviewGrantSchema = exports_external2.strictObject({
32775
+ url: exports_external2.url(),
32776
+ expires_at: exports_external2.iso.datetime()
32777
+ });
32760
32778
  var sessionListResponseSchema = exports_external2.strictObject({
32761
32779
  data: exports_external2.array(sessionSummarySchema),
32762
32780
  has_more: exports_external2.boolean(),
@@ -32848,14 +32866,14 @@ async function interruptSession({
32848
32866
  }
32849
32867
  async function completeSession({ client, sessionId }) {
32850
32868
  const response = await client.request(`/v1/sessions/${encodeURIComponent(sessionId)}/complete`, { method: "PUT" });
32851
- const parsed = sessionLifecycleResponseSchema.safeParse(await parseJson2(response, "Session completion response was not valid JSON."));
32869
+ const parsed = sessionDetailResponseSchema.safeParse(await parseJson2(response, "Session completion response was not valid JSON."));
32852
32870
  if (!parsed.success)
32853
32871
  throw new CodingAgentProtocolError("Session completion response did not match the public API contract.", { cause: parsed.error });
32854
32872
  return parsed.data;
32855
32873
  }
32856
32874
  async function cancelSession({ client, sessionId }) {
32857
32875
  const response = await client.request(`/v1/sessions/${encodeURIComponent(sessionId)}/cancel`, { method: "PUT" });
32858
- const parsed = sessionLifecycleResponseSchema.safeParse(await parseJson2(response, "Session cancellation response was not valid JSON."));
32876
+ const parsed = sessionDetailResponseSchema.safeParse(await parseJson2(response, "Session cancellation response was not valid JSON."));
32859
32877
  if (!parsed.success)
32860
32878
  throw new CodingAgentProtocolError("Session cancellation response did not match the public API contract.", { cause: parsed.error });
32861
32879
  return parsed.data;
@@ -32933,19 +32951,13 @@ function parseAppendSessionMessageResponse(body) {
32933
32951
  // ../../packages/coding-agent-client/src/session-events.ts
32934
32952
  var retainedEventBodySchema = exports_external2.object({ type: exports_external2.string().min(1) }).passthrough();
32935
32953
  var ephemeralEventBodySchema = exports_external2.object({ type: exports_external2.string().min(1) }).passthrough();
32936
- var publishedAttachmentSchema = exports_external2.object({
32937
- artifact_id: exports_external2.string(),
32938
- filename: exports_external2.string(),
32939
- media_type: exports_external2.string(),
32940
- byte_size: exports_external2.number().int().nonnegative()
32941
- }).passthrough();
32942
32954
  var retainedSessionEventSchema = exports_external2.object({
32943
32955
  id: exports_external2.string(),
32944
32956
  history_sequence: exports_external2.number().int().positive(),
32945
32957
  occurred_at: exports_external2.iso.datetime(),
32946
32958
  recorded_at: exports_external2.iso.datetime(),
32947
32959
  event: retainedEventBodySchema,
32948
- attachment: publishedAttachmentSchema.optional()
32960
+ artifact: sessionArtifactEventSchema.optional()
32949
32961
  }).strict();
32950
32962
  var ephemeralSessionEventSchema = exports_external2.object({
32951
32963
  occurred_at: exports_external2.iso.datetime(),
@@ -32979,7 +32991,8 @@ async function* streamSessionEvents({
32979
32991
  sessionId,
32980
32992
  lastRetainedEventId,
32981
32993
  maxReconnects = 0,
32982
- signal
32994
+ signal,
32995
+ onSynchronized
32983
32996
  }) {
32984
32997
  let retainedCursor = lastRetainedEventId;
32985
32998
  let reconnects = 0;
@@ -32993,7 +33006,7 @@ async function* streamSessionEvents({
32993
33006
  });
32994
33007
  if (!response.body)
32995
33008
  throw new CodingAgentProtocolError("Session event stream response did not include a readable body.");
32996
- for await (const frame of parseSessionEventStream(response.body)) {
33009
+ for await (const frame of parseSessionEventStream(response.body, { onSynchronized })) {
32997
33010
  if (frame.kind === "retained")
32998
33011
  retainedCursor = frame.id;
32999
33012
  yield frame;
@@ -33003,21 +33016,33 @@ async function* streamSessionEvents({
33003
33016
  reconnects += 1;
33004
33017
  }
33005
33018
  }
33006
- async function* parseSessionEventStream(stream) {
33007
- for await (const message of parseServerSentEvents(stream)) {
33019
+ async function* parseSessionEventStream(stream, { onSynchronized } = {}) {
33020
+ for await (const message of parseServerSentEvents(stream, { onSynchronized })) {
33008
33021
  if (message.event !== undefined && message.event !== "session-event")
33009
33022
  continue;
33010
33023
  yield parseSessionEventMessage(message);
33011
33024
  }
33012
33025
  }
33013
- async function* parseServerSentEvents(stream) {
33026
+ async function* parseServerSentEvents(stream, { onSynchronized }) {
33014
33027
  const decoder2 = new TextDecoder;
33015
33028
  const reader = stream.getReader();
33016
33029
  let buffer = "";
33017
33030
  let messageId;
33018
33031
  let eventName;
33019
33032
  let dataLines = [];
33020
- function dispatch() {
33033
+ let frameLineCount = 0;
33034
+ let hasSynchronizationComment = false;
33035
+ let synchronized = false;
33036
+ function dispatch(completeFrame) {
33037
+ const isSynchronizationFrame = completeFrame && frameLineCount === 1 && hasSynchronizationComment;
33038
+ frameLineCount = 0;
33039
+ hasSynchronizationComment = false;
33040
+ if (isSynchronizationFrame) {
33041
+ if (synchronized)
33042
+ throw new CodingAgentProtocolError("Session event stream contained more than one synchronization marker.");
33043
+ synchronized = true;
33044
+ onSynchronized?.();
33045
+ }
33021
33046
  if (dataLines.length === 0) {
33022
33047
  messageId = undefined;
33023
33048
  eventName = undefined;
@@ -33036,7 +33061,10 @@ async function* parseServerSentEvents(stream) {
33036
33061
  }
33037
33062
  function applyLine(line) {
33038
33063
  if (line === "")
33039
- return dispatch();
33064
+ return dispatch(true);
33065
+ frameLineCount += 1;
33066
+ if (line === ": synchronized")
33067
+ hasSynchronizationComment = true;
33040
33068
  if (line.startsWith(":"))
33041
33069
  return null;
33042
33070
  const separatorIndex = line.indexOf(":");
@@ -33074,7 +33102,7 @@ async function* parseServerSentEvents(stream) {
33074
33102
  if (message)
33075
33103
  yield message;
33076
33104
  }
33077
- const finalMessage = dispatch();
33105
+ const finalMessage = dispatch(false);
33078
33106
  if (finalMessage)
33079
33107
  yield finalMessage;
33080
33108
  } finally {
@@ -33848,10 +33876,6 @@ var agentMessageDeltaEventSchema = exports_external2.object({
33848
33876
  type: exports_external2.literal("agent.message.delta"),
33849
33877
  payload: exports_external2.object({ role: exports_external2.string(), delta: exports_external2.string() }).passthrough()
33850
33878
  }).passthrough();
33851
- var agentReasoningSummaryDeltaEventSchema = exports_external2.object({
33852
- type: exports_external2.literal("agent.reasoning.summary.delta"),
33853
- payload: exports_external2.object({ text: exports_external2.string() }).passthrough()
33854
- }).passthrough();
33855
33879
  var publicMessageContentSegmentSchema = exports_external2.discriminatedUnion("type", [
33856
33880
  exports_external2.strictObject({ type: exports_external2.literal("text"), text: exports_external2.string() }),
33857
33881
  exports_external2.strictObject({
@@ -33930,7 +33954,7 @@ function projectRemoteSessionEvent({ state, frame }) {
33930
33954
  event: frame.data.event,
33931
33955
  occurredAt: frame.data.occurred_at,
33932
33956
  retainedEventId: frame.id,
33933
- attachment: frame.data.attachment
33957
+ artifact: frame.data.artifact
33934
33958
  });
33935
33959
  return {
33936
33960
  ...projected,
@@ -33954,13 +33978,6 @@ function projectEphemeralEvent({ state, event }) {
33954
33978
  previews: { ...state.previews, assistantText: state.previews.assistantText + messageDelta.data.payload.delta }
33955
33979
  };
33956
33980
  }
33957
- const reasoningDelta = agentReasoningSummaryDeltaEventSchema.safeParse(event);
33958
- if (reasoningDelta.success) {
33959
- return {
33960
- ...state,
33961
- previews: { ...state.previews, reasoningText: state.previews.reasoningText + reasoningDelta.data.payload.text }
33962
- };
33963
- }
33964
33981
  return state;
33965
33982
  }
33966
33983
  function projectRetainedEvent({
@@ -33968,7 +33985,7 @@ function projectRetainedEvent({
33968
33985
  event,
33969
33986
  occurredAt,
33970
33987
  retainedEventId,
33971
- attachment
33988
+ artifact
33972
33989
  }) {
33973
33990
  const messageCreated = sessionMessageCreatedEventSchema.safeParse(event);
33974
33991
  const turnAssociated = sessionMessageTurnAssociatedEventSchema.safeParse(event);
@@ -33976,17 +33993,28 @@ function projectRetainedEvent({
33976
33993
  const workspaceGitRevision = sessionWorkspaceGitRevisionEventSchema.safeParse(event);
33977
33994
  const turnEnded = agentTurnEndedEventSchema.safeParse(event);
33978
33995
  let projected = messageCreated.success ? projectSessionMessageCreated({ state, event: messageCreated.data, occurredAt }) : turnAssociated.success ? projectSessionMessageTurnAssociated({ state, event: turnAssociated.data }) : workspaceGitInitialized.success ? projectSessionWorkspaceGitInitialized({ state, event: workspaceGitInitialized.data, occurredAt, retainedEventId }) : workspaceGitRevision.success ? projectSessionWorkspaceGitRevision({ state, event: workspaceGitRevision.data, occurredAt, retainedEventId }) : turnEnded.success ? projectAgentTurnEnded({ state, event: turnEnded.data, occurredAt, retainedEventId }) : projectDurableAgentEvent({ state, event, occurredAt, retainedEventId });
33979
- if (attachment) {
33996
+ if (artifact) {
33997
+ const publication = artifact.kind === "file" ? { artifactId: artifact.artifact_id, kind: artifact.kind, title: artifact.filename } : { artifactId: artifact.artifact_id, kind: artifact.kind, title: artifact.title, url: artifact.url };
33998
+ const timelineArtifact = artifact.kind === "file" ? {
33999
+ kind: "artifact",
34000
+ artifactKind: artifact.kind,
34001
+ artifactId: artifact.artifact_id,
34002
+ occurredAt,
34003
+ title: artifact.filename,
34004
+ mediaType: artifact.media_type
34005
+ } : {
34006
+ kind: "artifact",
34007
+ artifactKind: artifact.kind,
34008
+ artifactId: artifact.artifact_id,
34009
+ occurredAt,
34010
+ title: artifact.title,
34011
+ site: artifact.site,
34012
+ url: artifact.url
34013
+ };
33980
34014
  projected = {
33981
34015
  ...projected,
33982
- publications: [...projected.publications, { artifactId: attachment.artifact_id, filename: attachment.filename }],
33983
- transcript: [...projected.transcript, {
33984
- kind: "artifact",
33985
- artifactId: attachment.artifact_id,
33986
- occurredAt,
33987
- filename: attachment.filename,
33988
- mediaType: attachment.media_type
33989
- }]
34016
+ publications: [...projected.publications, publication],
34017
+ transcript: [...projected.transcript, timelineArtifact]
33990
34018
  };
33991
34019
  }
33992
34020
  return projected;
@@ -34097,8 +34125,7 @@ function projectDurableAgentEvent({ state, event, occurredAt, retainedEventId })
34097
34125
  retainedTurnStarts: { ...state.retainedTurnStarts, [agentEvent.turnId]: { actorType: agentEvent.actor.type, startedAt: occurredAt } },
34098
34126
  messageTurns: Object.fromEntries(Object.entries(state.messageTurns).map(([messageId, turn]) => [messageId, turn.turnId === agentEvent.turnId ? { ...turn, startedAt: occurredAt } : turn]))
34099
34127
  } : state;
34100
- const stateWithCompletedReasoning = agentEvent.type === "agent.reasoning.ended" ? { ...stateWithTurnStart, previews: { ...stateWithTurnStart.previews, reasoningText: "" } } : stateWithTurnStart;
34101
- return appendActivity({ state: stateWithCompletedReasoning, retainedEventId, occurredAt, card: toAgentActivityCard(agentEvent) });
34128
+ return appendActivity({ state: stateWithTurnStart, retainedEventId, occurredAt, card: toAgentActivityCard(agentEvent) });
34102
34129
  }
34103
34130
  function projectAgentWorkObserved({
34104
34131
  state,
@@ -34288,6 +34315,16 @@ var terminalControlPattern = /[\u0000-\u0008\u000B-\u001F\u007F-\u009F]/g;
34288
34315
  function stripAnsi(value) {
34289
34316
  return value.replace(ansiEscapePattern, "").replace(terminalControlPattern, "");
34290
34317
  }
34318
+ function formatReasoningPreview(item) {
34319
+ if (item.status === "absent")
34320
+ return "";
34321
+ const preview = stripAnsi(item.text).trim();
34322
+ if (!item.truncated)
34323
+ return preview;
34324
+ return preview.length > 0 ? `${preview}
34325
+
34326
+ Preview shortened.` : "Preview shortened.";
34327
+ }
34291
34328
  function providerText(raw, fallback) {
34292
34329
  const cleaned = stripAnsi(raw ?? "").trim();
34293
34330
  return cleaned.length > 0 ? cleaned : fallback;
@@ -34305,6 +34342,127 @@ function toSessionPullRequest(detail) {
34305
34342
  return pullRequest ? { number: pullRequest.number, status: pullRequest.status, draft: pullRequest.draft } : null;
34306
34343
  }
34307
34344
 
34345
+ // src/sessions/reasoning-preview.ts
34346
+ var maximumPrefixLength = 2000;
34347
+ var absentItem = { status: "absent" };
34348
+ function initialReasoningPreviewState() {
34349
+ return {
34350
+ synchronized: false,
34351
+ sequenceFloor: -1,
34352
+ runtime: "active",
34353
+ lastSessionStartedEventId: null,
34354
+ item: absentItem
34355
+ };
34356
+ }
34357
+ function reduceReasoningPreview(state, input) {
34358
+ if (input.type === "synchronized")
34359
+ return state.synchronized ? state : { ...state, synchronized: true };
34360
+ const event = input.event;
34361
+ if (event.type === "agent.session.started") {
34362
+ if (event.eventId === state.lastSessionStartedEventId)
34363
+ return state;
34364
+ return {
34365
+ ...state,
34366
+ sequenceFloor: event.sequence,
34367
+ runtime: "active",
34368
+ lastSessionStartedEventId: event.eventId,
34369
+ item: absentItem
34370
+ };
34371
+ }
34372
+ if (event.type === "agent.session.ended") {
34373
+ return {
34374
+ ...state,
34375
+ sequenceFloor: Math.max(state.sequenceFloor, event.sequence),
34376
+ runtime: "ended",
34377
+ item: absentItem
34378
+ };
34379
+ }
34380
+ if (!isMainReasoningOrTurnEvent(event) || state.runtime === "ended")
34381
+ return state;
34382
+ 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);
34383
+ if (event.sequence <= state.sequenceFloor && !matchingRetainedEnd)
34384
+ return state;
34385
+ const advancedState = { ...state, sequenceFloor: Math.max(state.sequenceFloor, event.sequence) };
34386
+ switch (event.type) {
34387
+ case "agent.reasoning.started": {
34388
+ if (!state.synchronized)
34389
+ return advancedState;
34390
+ if (state.item.status === "active" && sameReasoningItem({ item: state.item, turnId: event.turnId, reasoningId: event.payload.reasoningId })) {
34391
+ return advancedState;
34392
+ }
34393
+ return {
34394
+ ...advancedState,
34395
+ item: {
34396
+ status: "active",
34397
+ turnId: event.turnId,
34398
+ reasoningId: event.payload.reasoningId,
34399
+ text: "",
34400
+ truncated: false
34401
+ }
34402
+ };
34403
+ }
34404
+ case "agent.reasoning.summary.delta": {
34405
+ if (state.item.status !== "active" || !sameReasoningItem({ item: state.item, turnId: event.turnId, reasoningId: event.payload.reasoningId })) {
34406
+ return advancedState;
34407
+ }
34408
+ return {
34409
+ ...advancedState,
34410
+ item: appendBoundedPrefix(state.item, event.payload.text)
34411
+ };
34412
+ }
34413
+ case "agent.reasoning.ended": {
34414
+ if (state.item.status !== "active" || !sameReasoningItem({ item: state.item, turnId: event.turnId, reasoningId: event.payload.reasoningId })) {
34415
+ return advancedState;
34416
+ }
34417
+ return { ...advancedState, item: absentItem };
34418
+ }
34419
+ case "agent.turn.started": {
34420
+ if (state.item.status === "active" && state.item.turnId !== event.turnId)
34421
+ return { ...advancedState, item: absentItem };
34422
+ return advancedState;
34423
+ }
34424
+ case "agent.turn.ended": {
34425
+ if (state.item.status === "active" && state.item.turnId === event.turnId)
34426
+ return { ...advancedState, item: absentItem };
34427
+ return advancedState;
34428
+ }
34429
+ }
34430
+ }
34431
+ function isMainReasoningOrTurnEvent(event) {
34432
+ 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") {
34433
+ return false;
34434
+ }
34435
+ return event.actor.type === "main";
34436
+ }
34437
+ function sameReasoningItem({
34438
+ item,
34439
+ turnId,
34440
+ reasoningId
34441
+ }) {
34442
+ return item.turnId === turnId && item.reasoningId === reasoningId;
34443
+ }
34444
+ function appendBoundedPrefix(item, text) {
34445
+ if (item.truncated)
34446
+ return item;
34447
+ const availableLength = maximumPrefixLength - item.text.length;
34448
+ let retainedLength = Math.min(availableLength, text.length);
34449
+ const reachesSizeBound = item.text.length + retainedLength === maximumPrefixLength;
34450
+ if (reachesSizeBound && retainedLength > 0 && isHighSurrogate(text.charCodeAt(retainedLength - 1)))
34451
+ retainedLength -= 1;
34452
+ const discardedInput = retainedLength < text.length;
34453
+ if (retainedLength === 0) {
34454
+ return discardedInput && !item.truncated ? { ...item, truncated: true } : item;
34455
+ }
34456
+ return {
34457
+ ...item,
34458
+ text: item.text + text.slice(0, retainedLength),
34459
+ truncated: item.truncated || discardedInput
34460
+ };
34461
+ }
34462
+ function isHighSurrogate(codeUnit) {
34463
+ return codeUnit >= 55296 && codeUnit <= 56319;
34464
+ }
34465
+
34308
34466
  // src/sessions/session-controller.ts
34309
34467
  var remoteSessionReconnectPolicy = {
34310
34468
  initialBackoffMs: 1000,
@@ -34343,6 +34501,9 @@ function createRemoteSessionController(dependencies) {
34343
34501
  let stopped = false;
34344
34502
  let state;
34345
34503
  let cachedLastRetainedEventId;
34504
+ let reasoningPreviewState = initialReasoningPreviewState();
34505
+ let reasoningConnectionGeneration = 0;
34506
+ let reasoningHistorySequenceFloor = -1;
34346
34507
  async function start(input) {
34347
34508
  stopped = false;
34348
34509
  ready = new Promise((resolve) => {
@@ -34356,14 +34517,17 @@ function createRemoteSessionController(dependencies) {
34356
34517
  });
34357
34518
  resolveReady();
34358
34519
  publishState();
34359
- if (input.mode === "cold-resume")
34360
- await hydrateRetainedHistoryFromBeginning();
34520
+ if (input.mode === "cold-resume") {
34521
+ const hydrationGeneration = beginReasoningAttempt();
34522
+ await hydrateRetainedHistoryFromBeginning(hydrationGeneration);
34523
+ }
34361
34524
  await streamWithControllerReconnects();
34362
34525
  }
34363
34526
  function stop() {
34364
34527
  if (stopped)
34365
34528
  return;
34366
34529
  stopped = true;
34530
+ invalidateReasoningAttempt({ publish: true });
34367
34531
  abortController.abort(new Error("Remote session controller stopped."));
34368
34532
  }
34369
34533
  function subscribe(listener) {
@@ -34398,10 +34562,14 @@ function createRemoteSessionController(dependencies) {
34398
34562
  publishState();
34399
34563
  }
34400
34564
  function updateDetail(detail) {
34565
+ if (detail.status !== "open")
34566
+ invalidateReasoningAttempt({ publish: false });
34401
34567
  state = updateSessionDetail({ state: getState(), detail });
34568
+ if (detail.status !== "open")
34569
+ state = withoutReasoningPreview(state);
34402
34570
  publishState();
34403
34571
  }
34404
- async function hydrateRetainedHistoryFromBeginning() {
34572
+ async function hydrateRetainedHistoryFromBeginning(generation) {
34405
34573
  let after;
34406
34574
  while (true) {
34407
34575
  if (stopped)
@@ -34413,10 +34581,13 @@ function createRemoteSessionController(dependencies) {
34413
34581
  });
34414
34582
  for (const item of page.data) {
34415
34583
  await reduceFrameAndPersist({
34416
- kind: "retained",
34417
- id: String(item.history_sequence),
34418
- event: "session-event",
34419
- data: item
34584
+ frame: {
34585
+ kind: "retained",
34586
+ id: String(item.history_sequence),
34587
+ event: "session-event",
34588
+ data: item
34589
+ },
34590
+ generation
34420
34591
  });
34421
34592
  }
34422
34593
  if (!page.has_more || !page.next_cursor)
@@ -34430,27 +34601,33 @@ function createRemoteSessionController(dependencies) {
34430
34601
  while (true) {
34431
34602
  if (stopped)
34432
34603
  return;
34604
+ const generation = beginReasoningAttempt();
34433
34605
  state = markSessionConnected(getState());
34434
34606
  publishState();
34435
34607
  try {
34436
34608
  const stream = await dependencies.openEventStream({
34437
34609
  sessionId: dependencies.sessionId,
34438
34610
  lastRetainedEventId: cachedLastRetainedEventId,
34439
- signal: abortController.signal
34611
+ signal: abortController.signal,
34612
+ onSynchronized: () => synchronizeReasoningAttempt(generation)
34440
34613
  });
34441
34614
  for await (const frame of stream) {
34442
34615
  if (stopped)
34443
34616
  return;
34444
- await reduceFrameAndPersist(frame);
34617
+ await reduceFrameAndPersist({ frame, generation });
34445
34618
  }
34619
+ endReasoningAttempt(generation);
34446
34620
  } catch (error93) {
34621
+ endReasoningAttempt(generation);
34447
34622
  if (stopped)
34448
34623
  return;
34449
34624
  if (error93 instanceof Error && error93.name === "SessionProjectionProtocolError")
34450
34625
  throw error93;
34626
+ const refreshGeneration = reasoningConnectionGeneration;
34451
34627
  const detail = await dependencies.getSession({ sessionId: dependencies.sessionId });
34452
- state = updateSessionDetail({ state: getState(), detail });
34453
- publishState();
34628
+ if (stopped || refreshGeneration !== reasoningConnectionGeneration)
34629
+ return;
34630
+ updateDetail(detail);
34454
34631
  }
34455
34632
  if (stopped)
34456
34633
  return;
@@ -34476,17 +34653,86 @@ function createRemoteSessionController(dependencies) {
34476
34653
  backoffMs = Math.min(backoffMs * 2, reconnectPolicy.maxBackoffMs);
34477
34654
  }
34478
34655
  }
34479
- async function reduceFrameAndPersist(frame) {
34480
- const nextState = projectRemoteSessionEvent({ state: getState(), frame });
34656
+ async function reduceFrameAndPersist({ frame, generation }) {
34657
+ const previousState = getState();
34658
+ const projectedState = projectRemoteSessionEvent({ state: previousState, frame });
34659
+ state = projectReasoningPreview({ previousState, projectedState, frame, generation });
34481
34660
  if (frame.kind === "retained") {
34482
- state = nextState;
34483
34661
  await writeCache();
34484
- cachedLastRetainedEventId = nextState.lastRetainedEventId;
34662
+ cachedLastRetainedEventId = projectedState.lastRetainedEventId;
34485
34663
  }
34486
- state = nextState;
34487
34664
  resolveCompletedWaiters();
34665
+ if (stopped)
34666
+ return;
34488
34667
  publishState(frame);
34489
34668
  }
34669
+ function projectReasoningPreview({
34670
+ previousState,
34671
+ projectedState,
34672
+ frame,
34673
+ generation
34674
+ }) {
34675
+ if (generation !== reasoningConnectionGeneration || projectedState.aggregateStatus !== "open")
34676
+ return withoutReasoningPreview(projectedState);
34677
+ if (frame.kind === "retained") {
34678
+ if (previousState.seenRetainedEventIds[frame.id])
34679
+ return projectedState;
34680
+ if (frame.data.history_sequence <= reasoningHistorySequenceFloor)
34681
+ return projectedState;
34682
+ reasoningHistorySequenceFloor = frame.data.history_sequence;
34683
+ } else if (frame.data.event.type !== "agent.reasoning.summary.delta") {
34684
+ return projectedState;
34685
+ }
34686
+ if (frame.data.event.type === "agent.message.ended")
34687
+ return projectedState;
34688
+ const parsed = wrappedAgentEventSchema.safeParse(frame.data.event);
34689
+ if (!parsed.success)
34690
+ return projectedState;
34691
+ reasoningPreviewState = reduceReasoningPreview(reasoningPreviewState, { type: "event", event: parsed.data });
34692
+ return {
34693
+ ...projectedState,
34694
+ previews: {
34695
+ ...projectedState.previews,
34696
+ reasoningText: formatReasoningPreview(reasoningPreviewState.item)
34697
+ }
34698
+ };
34699
+ }
34700
+ function beginReasoningAttempt() {
34701
+ reasoningConnectionGeneration += 1;
34702
+ reasoningPreviewState = initialReasoningPreviewState();
34703
+ reasoningHistorySequenceFloor = -1;
34704
+ if (state)
34705
+ state = withoutReasoningPreview(state);
34706
+ return reasoningConnectionGeneration;
34707
+ }
34708
+ function synchronizeReasoningAttempt(generation) {
34709
+ if (stopped || generation !== reasoningConnectionGeneration || getState().aggregateStatus !== "open")
34710
+ return;
34711
+ reasoningPreviewState = reduceReasoningPreview(reasoningPreviewState, { type: "synchronized" });
34712
+ }
34713
+ function endReasoningAttempt(generation) {
34714
+ if (generation !== reasoningConnectionGeneration)
34715
+ return;
34716
+ invalidateReasoningAttempt({ publish: true });
34717
+ }
34718
+ function invalidateReasoningAttempt({ publish }) {
34719
+ reasoningConnectionGeneration += 1;
34720
+ reasoningPreviewState = initialReasoningPreviewState();
34721
+ reasoningHistorySequenceFloor = -1;
34722
+ if (!state || state.previews.reasoningText.length === 0)
34723
+ return;
34724
+ state = withoutReasoningPreview(state);
34725
+ if (publish)
34726
+ publishState();
34727
+ }
34728
+ function withoutReasoningPreview(currentState) {
34729
+ if (currentState.previews.reasoningText.length === 0)
34730
+ return currentState;
34731
+ return {
34732
+ ...currentState,
34733
+ previews: { ...currentState.previews, reasoningText: "" }
34734
+ };
34735
+ }
34490
34736
  async function writeCache() {
34491
34737
  const currentState = getState();
34492
34738
  const cache = {
@@ -37200,10 +37446,18 @@ function renderTimelineItem(item, activityExpanded) {
37200
37446
  }
37201
37447
  if (item.kind === "plan")
37202
37448
  return renderPlan({ item, expanded: activityExpanded });
37449
+ if (item.artifactKind === "tela_page") {
37450
+ return new StyledText5([
37451
+ renderTimestampChunk(item.occurredAt),
37452
+ bold4(fg6(PALETTE.tool)("Tela Page: ")),
37453
+ fg6(PALETTE.bodyText)(item.title),
37454
+ dim4(fg6(PALETTE.dimText)(` \xB7 ${item.url}`))
37455
+ ]);
37456
+ }
37203
37457
  return new StyledText5([
37204
37458
  renderTimestampChunk(item.occurredAt),
37205
37459
  bold4(fg6(PALETTE.tool)("Artifact: ")),
37206
- fg6(PALETTE.bodyText)(item.filename)
37460
+ fg6(PALETTE.bodyText)(item.title)
37207
37461
  ]);
37208
37462
  }
37209
37463
  var collapsedPlanItemLimit = 5;
@@ -37514,7 +37768,7 @@ var compactMarkRows = 9;
37514
37768
  var compactMinWidth = 48;
37515
37769
  var compactMinHeight = 20;
37516
37770
  var markBrightnessGain = 4.2;
37517
- var remyCliVersion = "1.7.0";
37771
+ var remyCliVersion = "1.9.0";
37518
37772
  async function showRemySplash({
37519
37773
  createRenderer = createRemyRenderer,
37520
37774
  durationMs = splashDurationMs,
@@ -38643,7 +38897,8 @@ async function attachCreatedSession({
38643
38897
  status: created.session.status,
38644
38898
  repositories: sessionRepositories.map((repository) => ({ id: repository.id, full_name: repository.fullName })),
38645
38899
  pull_requests: [],
38646
- connections: created.connections
38900
+ connections: created.connections,
38901
+ artifacts: []
38647
38902
  }
38648
38903
  },
38649
38904
  noTui,
@@ -38766,7 +39021,7 @@ async function runAttachedSession({
38766
39021
  environment: dependencies.environment,
38767
39022
  getSession: async ({ sessionId: id }) => await operations.getSession({ client: operations.client, sessionId: id }),
38768
39023
  listSessionEvents: async ({ sessionId: id, limit, after }) => await operations.listSessionEvents({ client: operations.client, sessionId: id, limit, after }),
38769
- openEventStream: ({ sessionId: id, lastRetainedEventId, signal }) => operations.streamSessionEvents({ client: operations.client, sessionId: id, lastRetainedEventId, signal })
39024
+ openEventStream: ({ sessionId: id, lastRetainedEventId, signal, onSynchronized }) => operations.streamSessionEvents({ client: operations.client, sessionId: id, lastRetainedEventId, signal, onSynchronized })
38770
39025
  });
38771
39026
  const interactive = !noTui && !json3 && isInteractiveTerminal(dependencies);
38772
39027
  const shouldPrintJson = !interactive || json3;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/remy-cli",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
4
4
  "description": "Remy, the Coding Agent terminal client.",
5
5
  "type": "module",
6
6
  "bin": {