@meistrari/remy-cli 1.15.0 → 1.16.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 +3 -0
  2. package/dist/remy.js +149 -43
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -54,6 +54,8 @@ Published files and Tela Pages appear in the timeline. File artifacts include th
54
54
 
55
55
  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.
56
56
 
57
+ Enter `/compact` to admit a durable main-thread context compaction control without sending a human message. Remy reports request admission immediately and shows the eventual completed, no-op, failed, or unknown provider result in retained activity. Compaction waits for a safe point and does not interrupt active work or jump ahead of an earlier Next message.
58
+
57
59
  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. Published file artifacts show their filename and muted, full web preview URL; open the URL in a browser to select that artifact in its session. Tela Page rows retain their canonical Page URL. Remy does not fetch or print artifact bytes. When the conversation has focus, press `Tab` to return to the composer.
58
60
 
59
61
  | Control | Result |
@@ -61,6 +63,7 @@ Drag to select visible text in any Remy view; Remy copies it to the local clipbo
61
63
  | `Esc` | Interrupt the active turn; the session stays open. |
62
64
  | `/complete` | Complete an open session after its active turn stops. |
63
65
  | `/cancel` | Cancel the current session, including an active turn. |
66
+ | `/compact` | Admit a main-thread context compaction request without sending a message; the provider result appears in retained activity. |
64
67
  | `/sessions` | Return to the dashboard. |
65
68
  | `/new` | Start another new-session flow. |
66
69
  | `/logout` | Sign out of Remy after confirmation. |
package/dist/remy.js CHANGED
@@ -32214,6 +32214,10 @@ var interruptAgentCommandSchema = runningCommandBaseSchema.extend({
32214
32214
  turnId: zod_default2.string().min(1).optional(),
32215
32215
  reason: zod_default2.string().min(1).optional()
32216
32216
  }).strict();
32217
+ var compactContextAgentCommandSchema = runningCommandBaseSchema.extend({
32218
+ type: zod_default2.literal("agent.compact-context"),
32219
+ commandId: commandIdSchema
32220
+ }).strict();
32217
32221
  var stopAgentCommandSchema = runningCommandBaseSchema.extend({
32218
32222
  type: zod_default2.literal("agent.stop"),
32219
32223
  reason: zod_default2.string().min(1).optional()
@@ -32227,6 +32231,7 @@ var respondUserInputAgentCommandSchema = runningCommandBaseSchema.extend({
32227
32231
  var agentCommandSchema = zod_default2.union([
32228
32232
  sendPromptCommandSchema,
32229
32233
  interruptAgentCommandSchema,
32234
+ compactContextAgentCommandSchema,
32230
32235
  stopAgentCommandSchema,
32231
32236
  respondUserInputAgentCommandSchema
32232
32237
  ]);
@@ -32420,6 +32425,12 @@ var agentTurnEndStatusSchema = zod_default2.enum(["completed", "failed", "cancel
32420
32425
  var agentSubagentStatusSchema = zod_default2.enum(["completed", "failed", "cancelled"]);
32421
32426
  var agentContextCompactionTriggerSchema = zod_default2.enum(["manual", "auto", "unknown"]);
32422
32427
  var agentContextCompactionStatusSchema = zod_default2.enum(["completed", "failed"]);
32428
+ var agentContextCompactionRequestResultSchema = zod_default2.discriminatedUnion("status", [
32429
+ zod_default2.object({ status: zod_default2.literal("completed") }).strict(),
32430
+ zod_default2.object({ status: zod_default2.literal("no_op"), reason: zod_default2.string() }).strict(),
32431
+ zod_default2.object({ status: zod_default2.literal("failed"), error: agentErrorSchema }).strict(),
32432
+ zod_default2.object({ status: zod_default2.literal("unknown") }).strict()
32433
+ ]);
32423
32434
  var agentErrorSourceSchema = zod_default2.enum(["provider", "runtime", "transport", "unknown"]);
32424
32435
  var agentSkillScopeSchema = zod_default2.enum(["user", "repository", "system", "admin"]);
32425
32436
  var agentSkillSchema = zod_default2.object({
@@ -32672,6 +32683,13 @@ var contextCompactionCompletedAgentEventSchema = agentTurnEventBaseSchema.extend
32672
32683
  error: agentErrorSchema.optional()
32673
32684
  }).strict()
32674
32685
  }).strict();
32686
+ var contextCompactionRequestCompletedAgentEventSchema = agentEventBaseSchema.extend({
32687
+ type: zod_default2.literal("context.compaction.request.completed"),
32688
+ payload: zod_default2.object({
32689
+ commandId: zod_default2.string().min(1),
32690
+ result: agentContextCompactionRequestResultSchema
32691
+ }).strict()
32692
+ }).strict();
32675
32693
  var userInputRequestedAgentEventSchema = agentTurnEventBaseSchema.extend({
32676
32694
  type: zod_default2.literal("user-input.requested"),
32677
32695
  payload: zod_default2.object({
@@ -32724,6 +32742,7 @@ var agentEventSchema = zod_default2.discriminatedUnion("type", [
32724
32742
  contextUpdatedAgentEventSchema,
32725
32743
  contextCompactionStartedAgentEventSchema,
32726
32744
  contextCompactionCompletedAgentEventSchema,
32745
+ contextCompactionRequestCompletedAgentEventSchema,
32727
32746
  userInputRequestedAgentEventSchema,
32728
32747
  userInputResolvedAgentEventSchema,
32729
32748
  errorAgentEventSchema
@@ -32912,6 +32931,13 @@ var interruptSessionResponseSchema = exports_external2.strictObject({
32912
32931
  type: exports_external2.literal("agent.interrupt")
32913
32932
  })
32914
32933
  });
32934
+ var compactSessionContextResponseSchema = exports_external2.strictObject({
32935
+ command: exports_external2.strictObject({
32936
+ id: exports_external2.string(),
32937
+ sequence: exports_external2.number().int().positive(),
32938
+ type: exports_external2.literal("agent.compact-context")
32939
+ })
32940
+ });
32915
32941
  var withdrawSessionMessageResponseSchema = exports_external2.strictObject({
32916
32942
  command: exports_external2.strictObject({
32917
32943
  id: exports_external2.string(),
@@ -33082,6 +33108,24 @@ async function interruptSession({
33082
33108
  throw new CodingAgentProtocolError("Session interrupt response did not match the public API contract.", { cause: parsed.error });
33083
33109
  return parsed.data.command;
33084
33110
  }
33111
+ async function compactSessionContext({
33112
+ client,
33113
+ sessionId,
33114
+ idempotencyKey
33115
+ }) {
33116
+ const response = await client.request(`/v1/sessions/${encodeURIComponent(sessionId)}/compact`, {
33117
+ method: "POST",
33118
+ headers: {
33119
+ "content-type": "application/json",
33120
+ "Idempotency-Key": idempotencyKey
33121
+ },
33122
+ body: "{}"
33123
+ });
33124
+ const parsed = compactSessionContextResponseSchema.safeParse(await parseJson2(response, "Session context compaction response was not valid JSON."));
33125
+ if (!parsed.success)
33126
+ throw new CodingAgentProtocolError("Session context compaction response did not match the public API contract.", { cause: parsed.error });
33127
+ return parsed.data.command;
33128
+ }
33085
33129
  async function completeSession({ client, sessionId }) {
33086
33130
  const response = await client.request(`/v1/sessions/${encodeURIComponent(sessionId)}/complete`, { method: "PUT" });
33087
33131
  const parsed = sessionDetailResponseSchema.safeParse(await parseJson2(response, "Session completion response was not valid JSON."));
@@ -34022,6 +34066,9 @@ var wrappedContextCompactionStartedAgentEventSchema = contextCompactionStartedAg
34022
34066
  var wrappedContextCompactionCompletedAgentEventSchema = contextCompactionCompletedAgentEventSchema.extend({
34023
34067
  type: zod_default2.literal("agent.context.compaction.completed")
34024
34068
  }).strict();
34069
+ var wrappedContextCompactionRequestCompletedAgentEventSchema = contextCompactionRequestCompletedAgentEventSchema.extend({
34070
+ type: zod_default2.literal("agent.context.compaction.request.completed")
34071
+ }).strict();
34025
34072
  var wrappedUserInputRequestedAgentEventSchema = userInputRequestedAgentEventSchema.extend({
34026
34073
  type: zod_default2.literal("agent.user-input.requested")
34027
34074
  }).strict();
@@ -34056,11 +34103,31 @@ var wrappedAgentEventSchema = zod_default2.discriminatedUnion("type", [
34056
34103
  wrappedContextUpdatedAgentEventSchema,
34057
34104
  wrappedContextCompactionStartedAgentEventSchema,
34058
34105
  wrappedContextCompactionCompletedAgentEventSchema,
34106
+ wrappedContextCompactionRequestCompletedAgentEventSchema,
34059
34107
  wrappedUserInputRequestedAgentEventSchema,
34060
34108
  wrappedUserInputResolvedAgentEventSchema,
34061
34109
  wrappedErrorAgentEventSchema
34062
34110
  ]);
34063
34111
 
34112
+ // ../../packages/agents-protocol/src/agent-identity.ts
34113
+ function normalizeAgentSubagentIdentity(identity) {
34114
+ return {
34115
+ actorId: identity.actorId,
34116
+ subagentId: identity.subagentId,
34117
+ parentActorId: identity.parentActorId,
34118
+ origin: identity.origin,
34119
+ parentToolCallId: identity.parentToolCallId ?? (identity.origin.type === "tool_call" ? identity.origin.toolCallId : undefined)
34120
+ };
34121
+ }
34122
+ function agentSubagentIdentitiesEqual(left, right) {
34123
+ const normalizedLeft = normalizeAgentSubagentIdentity(left);
34124
+ const normalizedRight = normalizeAgentSubagentIdentity(right);
34125
+ return normalizedLeft.actorId === normalizedRight.actorId && normalizedLeft.subagentId === normalizedRight.subagentId && normalizedLeft.parentActorId === normalizedRight.parentActorId && normalizedLeft.parentToolCallId === normalizedRight.parentToolCallId && (normalizedLeft.origin.type === "tool_call" ? normalizedRight.origin.type === "tool_call" && normalizedLeft.origin.toolCallId === normalizedRight.origin.toolCallId : normalizedRight.origin.type === "provider_task" && normalizedLeft.origin.taskId === normalizedRight.origin.taskId);
34126
+ }
34127
+ function agentRuntimeIdentityKey(event) {
34128
+ return `${event.sessionId.length}:${event.sessionId}:${event.providerSessionId.length}:${event.providerSessionId}`;
34129
+ }
34130
+
34064
34131
  // src/sessions/projection.ts
34065
34132
  var sessionMessageCreatedEventSchema = exports_external2.object({
34066
34133
  type: exports_external2.literal("session.message.created"),
@@ -34389,11 +34456,7 @@ function projectAgentTurnEnded({ state, event, occurredAt, retainedEventId }) {
34389
34456
  retainedTurnEnds: { ...state.retainedTurnEnds, [event.turnId]: { actorType: event.actor.type, outcome: event.payload.status } },
34390
34457
  messageTurns: Object.fromEntries(Object.entries(state.messageTurns).map(([messageId, turn]) => [messageId, turn.turnId === event.turnId ? { ...turn, outcome: event.payload.status } : turn]))
34391
34458
  } : state;
34392
- const card = event.actor.type === "main" ? event.payload.status === "failed" ? { kind: "failure", weight: "signal", title: "Remy turn failed", summary: "Turn failed." } : { kind: "lifecycle", weight: "noise", title: "Remy turn ended", summary: `Turn ${event.payload.status}.` } : createChildActivityCard({
34393
- identity: event.actor,
34394
- activityTitle: event.payload.status === "failed" ? "Turn failed" : "Turn ended",
34395
- card: event.payload.status === "failed" ? { kind: "failure", weight: "signal", summary: providerText(event.payload.error?.message, "Turn failed.") } : { kind: "lifecycle", weight: "noise", summary: `Turn ${event.payload.status}.` }
34396
- });
34459
+ const card = toAgentActivityCard(event);
34397
34460
  return appendActivity({ state: stateWithTurnOutcome, retainedEventId, occurredAt, card });
34398
34461
  }
34399
34462
  function projectDurableAgentEvent({ state, event, occurredAt, retainedEventId }) {
@@ -34538,7 +34601,7 @@ function toMainAgentActivityCard(event) {
34538
34601
  case "agent.turn.started":
34539
34602
  return { kind: "lifecycle", weight: "noise", title: "Remy turn started", summary: "Turn started." };
34540
34603
  case "agent.turn.ended":
34541
- return event.payload.status === "failed" ? { kind: "failure", weight: "signal", title: "Remy turn failed", summary: providerText(event.payload.error?.message, "Turn failed.") } : { kind: "lifecycle", weight: "noise", title: "Remy turn ended", summary: `Turn ${event.payload.status}.` };
34604
+ return event.payload.status === "failed" ? { kind: "failure", weight: "signal", title: "Remy turn failed", summary: "Turn failed." } : { kind: "lifecycle", weight: "noise", title: "Remy turn ended", summary: `Turn ${event.payload.status}.` };
34542
34605
  case "agent.work.observed":
34543
34606
  return { kind: "progress", weight: "noise", title: "Work progress", summary: "Remy updated its work plan.", detail: jsonDetail(event.payload.observations) };
34544
34607
  case "agent.message.started":
@@ -34574,6 +34637,16 @@ function toMainAgentActivityCard(event) {
34574
34637
  return { kind: "reasoning", weight: "noise", title: "Context compaction started", summary: "Remy is compacting context." };
34575
34638
  case "agent.context.compaction.completed":
34576
34639
  return event.payload.status === "failed" ? { kind: "failure", weight: "signal", title: "Context compaction failed", summary: providerText(event.payload.error?.message, "Context compaction failed.") } : { kind: "reasoning", weight: "noise", title: "Context compaction completed", summary: "Remy compacted context." };
34640
+ case "agent.context.compaction.request.completed": {
34641
+ const result = event.payload.result;
34642
+ if (result.status === "completed")
34643
+ return { kind: "progress", weight: "signal", title: "Context compaction completed", summary: "The provider completed manual context compaction." };
34644
+ if (result.status === "no_op")
34645
+ return { kind: "progress", weight: "signal", title: "Context not compacted", summary: providerText(result.reason, "The provider did not compact context.") };
34646
+ if (result.status === "failed")
34647
+ return { kind: "failure", weight: "signal", title: "Context compaction failed", summary: providerText(result.error.message, "Context compaction failed.") };
34648
+ return { kind: "progress", weight: "signal", title: "Context compaction result unknown", summary: "The provider result could not be correlated." };
34649
+ }
34577
34650
  case "agent.user-input.requested":
34578
34651
  return { kind: "approval-question", weight: "signal", title: "Remy needs input", summary: providerText(event.payload.prompt, "Remy needs input."), detail: jsonDetail(event.payload.questions) };
34579
34652
  case "agent.user-input.resolved":
@@ -34659,6 +34732,7 @@ function toChildAgentActivityCard({ event, identity }) {
34659
34732
  case "agent.session.skills.updated":
34660
34733
  case "agent.session.state.changed":
34661
34734
  case "agent.session.ended":
34735
+ case "agent.context.compaction.request.completed":
34662
34736
  case "agent.user-input.resolved":
34663
34737
  case "agent.error":
34664
34738
  throw new SessionProjectionProtocolError(`Cannot attribute non-child session event type ${event.type} to a subagent.`);
@@ -34701,7 +34775,7 @@ ${card.detail}`;
34701
34775
  function childActivityIdentity(event) {
34702
34776
  if (event.type === "agent.subagent.started" || event.type === "agent.subagent.progress" || event.type === "agent.subagent.ended") {
34703
34777
  if (event.actor.type === "subagent") {
34704
- if (!subagentLifecycleIdentityMatches({ actor: event.actor, payload: event.payload }))
34778
+ if (!agentSubagentIdentitiesEqual(event.actor, event.payload))
34705
34779
  throw new SessionProjectionProtocolError(`Retained session event type ${event.type} carried contradictory subagent actor and payload identity.`);
34706
34780
  return {
34707
34781
  ...event.actor,
@@ -34723,14 +34797,6 @@ function childActivityIdentity(event) {
34723
34797
  }
34724
34798
  return;
34725
34799
  }
34726
- function subagentLifecycleIdentityMatches({ actor, payload }) {
34727
- return actor.actorId === payload.actorId && actor.subagentId === payload.subagentId && actor.parentActorId === payload.parentActorId && actor.parentToolCallId === payload.parentToolCallId && subagentOriginsMatch(actor.origin, payload.origin);
34728
- }
34729
- function subagentOriginsMatch(left, right) {
34730
- if (left.type === "tool_call")
34731
- return right.type === "tool_call" && left.toolCallId === right.toolCallId;
34732
- return right.type === "provider_task" && left.taskId === right.taskId;
34733
- }
34734
34800
  function recordAgentLineageEvent({
34735
34801
  state,
34736
34802
  event,
@@ -34746,7 +34812,7 @@ function recordAgentLineageEvent({
34746
34812
  return state;
34747
34813
  throw error93;
34748
34814
  }
34749
- const runtimeBase = lineageRuntimeBase(parsed);
34815
+ const runtimeBase = agentRuntimeIdentityKey(parsed);
34750
34816
  let childLineage = state.childLineage;
34751
34817
  if (parsed.type === "agent.session.started" || parsed.type === "agent.session.ended") {
34752
34818
  const boundaryKey = `${runtimeBase}:${lengthPrefixed(parsed.eventId)}`;
@@ -34805,7 +34871,7 @@ function recordAgentLineageEvent({
34805
34871
  return stampResolvedAttribution({ state: nextState, retainedEventId, fact, owner: resolution.owner });
34806
34872
  }
34807
34873
  function resolveBodyFactAgainstOwners(owners, fact) {
34808
- const candidates = owners.filter((owner) => owner.runtimeScope === fact.runtimeScope && childIdentityMatches(owner.identity, fact.identity));
34874
+ const candidates = owners.filter((owner) => owner.runtimeScope === fact.runtimeScope && agentSubagentIdentitiesEqual(owner.identity, fact.identity));
34809
34875
  const turnCandidates = candidates.filter((owner) => owner.childTurnIds.has(fact.turnId));
34810
34876
  if (turnCandidates.length === 1)
34811
34877
  return { status: "known", owner: turnCandidates[0] };
@@ -34837,9 +34903,6 @@ function stampResolvedAttribution({ state, retainedEventId, fact, owner }) {
34837
34903
  }
34838
34904
  return state;
34839
34905
  }
34840
- function lineageRuntimeBase(event) {
34841
- return `${lengthPrefixed(event.sessionId)}:${lengthPrefixed(event.providerSessionId)}`;
34842
- }
34843
34906
  function lineageRuntimeScope({ runtimeBase, epoch }) {
34844
34907
  return `${runtimeBase}:${epoch}`;
34845
34908
  }
@@ -34852,9 +34915,6 @@ function isChildLineageFact(fact) {
34852
34915
  function isSubagentLifecycleType(type) {
34853
34916
  return type === "agent.subagent.started" || type === "agent.subagent.progress" || type === "agent.subagent.ended";
34854
34917
  }
34855
- function childIdentityMatches(left, right) {
34856
- return left.actorId === right.actorId && left.subagentId === right.subagentId && left.parentActorId === right.parentActorId && left.parentToolCallId === right.parentToolCallId && subagentOriginsMatch(left.origin, right.origin);
34857
- }
34858
34918
  function childIdentityCanAnchor(identity) {
34859
34919
  return identity.actorId !== identity.parentActorId && (identity.origin.type !== "tool_call" || identity.parentToolCallId === undefined || identity.parentToolCallId === identity.origin.toolCallId);
34860
34920
  }
@@ -34888,9 +34948,9 @@ function reconcileChildLineage(state) {
34888
34948
  if (parent && ownerHasActorAncestor(parent, fact.identity.actorId))
34889
34949
  return;
34890
34950
  const samePlacement = owners.filter((owner2) => owner2.runtimeScope === fact.runtimeScope && owner2.owningMainTurnId === owningMainTurnId && owner2.parent === parent && owner2.identity.actorId === fact.identity.actorId && owner2.identity.subagentId === fact.identity.subagentId);
34891
- if (samePlacement.some((owner2) => !childIdentityMatches(owner2.identity, fact.identity)))
34951
+ if (samePlacement.some((owner2) => !agentSubagentIdentitiesEqual(owner2.identity, fact.identity)))
34892
34952
  return;
34893
- const existing = samePlacement.find((owner2) => childIdentityMatches(owner2.identity, fact.identity));
34953
+ const existing = samePlacement.find((owner2) => agentSubagentIdentitiesEqual(owner2.identity, fact.identity));
34894
34954
  if (existing)
34895
34955
  return existing;
34896
34956
  const owner = {
@@ -34924,7 +34984,7 @@ function reconcileChildLineage(state) {
34924
34984
  const toolTurn = mainTurnForToolOrigin(fact);
34925
34985
  if (toolTurn === undefined)
34926
34986
  continue;
34927
- const owner = owners.find((candidate) => candidate.runtimeScope === fact.runtimeScope && candidate.parent === null && candidate.owningMainTurnId === toolTurn && childIdentityMatches(candidate.identity, fact.identity)) ?? createOwner({ fact, owningMainTurnId: toolTurn, parent: null });
34987
+ const owner = owners.find((candidate) => candidate.runtimeScope === fact.runtimeScope && candidate.parent === null && candidate.owningMainTurnId === toolTurn && agentSubagentIdentitiesEqual(candidate.identity, fact.identity)) ?? createOwner({ fact, owningMainTurnId: toolTurn, parent: null });
34928
34988
  if (owner) {
34929
34989
  ownerByFactOrder.set(fact.order, owner);
34930
34990
  owner.childTurnIds.add(fact.turnId);
@@ -34937,7 +34997,7 @@ function reconcileChildLineage(state) {
34937
34997
  for (const fact of childFacts) {
34938
34998
  if (ownerByFactOrder.has(fact.order))
34939
34999
  continue;
34940
- const candidates = owners.filter((owner2) => owner2.runtimeScope === fact.runtimeScope && childIdentityMatches(owner2.identity, fact.identity));
35000
+ const candidates = owners.filter((owner2) => owner2.runtimeScope === fact.runtimeScope && agentSubagentIdentitiesEqual(owner2.identity, fact.identity));
34941
35001
  const turnCandidates = candidates.filter((owner2) => owner2.childTurnIds.has(fact.turnId));
34942
35002
  const owner = turnCandidates.length === 1 ? turnCandidates[0] : candidates.length === 1 ? candidates[0] : undefined;
34943
35003
  if (!owner || fact.eventType === "agent.subagent.started")
@@ -35013,12 +35073,16 @@ function ownerPath(owner) {
35013
35073
  return path;
35014
35074
  }
35015
35075
  function childAttributionEqual(left, right) {
35016
- if (left.status !== right.status || left.activityTitle !== right.activityTitle || !childIdentityMatches(left.identity, right.identity)) {
35076
+ if (left === right)
35077
+ return true;
35078
+ if (!left || !right)
35079
+ return false;
35080
+ if (left.status !== right.status || left.activityTitle !== right.activityTitle || left.identity.name !== right.identity.name || !agentSubagentIdentitiesEqual(left.identity, right.identity)) {
35017
35081
  return false;
35018
35082
  }
35019
35083
  if (left.status === "unresolved" || right.status === "unresolved")
35020
35084
  return true;
35021
- return left.ownerKey === right.ownerKey && left.owningMainTurnId === right.owningMainTurnId && left.path.length === right.path.length && left.path.every((identity, index) => childIdentityMatches(identity, right.path[index]));
35085
+ return left.ownerKey === right.ownerKey && left.owningMainTurnId === right.owningMainTurnId && left.path.length === right.path.length && left.path.every((identity, index) => identity.name === right.path[index].name && agentSubagentIdentitiesEqual(identity, right.path[index]));
35022
35086
  }
35023
35087
  function terminalSafeSingleLine(value) {
35024
35088
  return stripAnsi(value).replace(/\s+/gu, " ").trim();
@@ -37990,6 +38054,7 @@ var composerSlashCommands = [
37990
38054
  { value: "/sessions", description: "Back to the session list" },
37991
38055
  { value: "/complete", description: "Finish this session" },
37992
38056
  { value: "/cancel", description: "Cancel this session" },
38057
+ { value: "/compact", description: "Compact main-thread context" },
37993
38058
  { value: "/new", description: "Start a new session" },
37994
38059
  { value: "/logout", description: "Sign out of Remy" },
37995
38060
  { value: "/help", description: "Show what you can do here" },
@@ -38001,6 +38066,7 @@ async function createSessionTui({
38001
38066
  controller,
38002
38067
  submitMessage,
38003
38068
  requestInterrupt,
38069
+ requestCompact,
38004
38070
  requestComplete,
38005
38071
  requestCancel,
38006
38072
  repositoryLabel,
@@ -38242,6 +38308,7 @@ async function createSessionTui({
38242
38308
  let admittedSubmissions = [];
38243
38309
  let stopState = { kind: "idle" };
38244
38310
  let lifecycleRequestState = { kind: "idle" };
38311
+ let contextCompactionRequestState = { kind: "idle" };
38245
38312
  let helpVisible = false;
38246
38313
  let logoutConfirmationOpen = false;
38247
38314
  let latestState = controller.getState();
@@ -38592,6 +38659,12 @@ async function createSessionTui({
38592
38659
  handleLifecycleRequest("cancel");
38593
38660
  return true;
38594
38661
  }
38662
+ if (command === "/compact") {
38663
+ composer.setText("");
38664
+ composerDraft = { ...composerDraft, text: "" };
38665
+ await handleCompactionRequest();
38666
+ return true;
38667
+ }
38595
38668
  if (command === "/logout") {
38596
38669
  composer.setText("");
38597
38670
  composerDraft = { ...composerDraft, text: "" };
@@ -38679,6 +38752,39 @@ async function createSessionTui({
38679
38752
  }
38680
38753
  render();
38681
38754
  }
38755
+ async function handleCompactionRequest() {
38756
+ if (contextCompactionRequestState.kind === "requesting")
38757
+ return;
38758
+ if (latestState.aggregateStatus !== "open") {
38759
+ composerFeedback = "This session is already terminal.";
38760
+ render();
38761
+ return;
38762
+ }
38763
+ const idempotencyKey = contextCompactionRequestState.kind === "delivery-unknown" ? contextCompactionRequestState.idempotencyKey : crypto.randomUUID();
38764
+ contextCompactionRequestState = { kind: "requesting", idempotencyKey };
38765
+ composerFeedback = "Requesting context compaction\u2026";
38766
+ render();
38767
+ try {
38768
+ await requestCompact({ idempotencyKey });
38769
+ if (!destroyed) {
38770
+ contextCompactionRequestState = { kind: "admitted" };
38771
+ composerFeedback = "Context compaction request admitted. Result will appear in the timeline.";
38772
+ }
38773
+ } catch (error93) {
38774
+ if (!destroyed) {
38775
+ const message = error93 instanceof Error ? error93.message : String(error93);
38776
+ if (isKnownClientRejection(error93)) {
38777
+ contextCompactionRequestState = { kind: "idle" };
38778
+ composerFeedback = `Could not request context compaction: ${message}`;
38779
+ } else {
38780
+ contextCompactionRequestState = { kind: "delivery-unknown", idempotencyKey };
38781
+ composerFeedback = `Context compaction admission is unknown: ${message}. Run /compact again to retry the same request.`;
38782
+ }
38783
+ }
38784
+ }
38785
+ if (!destroyed)
38786
+ render();
38787
+ }
38682
38788
  async function handleLifecycleRequest(operation) {
38683
38789
  if (lifecycleRequestState.kind === "pending")
38684
38790
  return;
@@ -38805,6 +38911,7 @@ var helpEntries = [
38805
38911
  { group: "command", token: "/sessions", description: "back to the session list" },
38806
38912
  { group: "command", token: "/complete", description: "finish this session" },
38807
38913
  { group: "command", token: "/cancel", description: "cancel this session" },
38914
+ { group: "command", token: "/compact", description: "compact main-thread context" },
38808
38915
  { group: "command", token: "/logout", description: "sign out of Remy" },
38809
38916
  { group: "command", token: "/exit", description: "leave Remy" },
38810
38917
  { group: "key", token: "esc", description: "stop Remy\u2019s current turn" },
@@ -39022,19 +39129,7 @@ function activityGlyph({ kind, inFlight }) {
39022
39129
  return { glyph: "\u2713", color: PALETTE.dimText };
39023
39130
  }
39024
39131
  function activityCardsHaveEqualDisclosure(left, right) {
39025
- return left.kind === right.kind && left.title === right.title && left.summary === right.summary && left.detail === right.detail && left.detailFormat === right.detailFormat && canonicalValuesEqual(left.attribution, right.attribution);
39026
- }
39027
- function canonicalValuesEqual(left, right) {
39028
- if (Object.is(left, right))
39029
- return true;
39030
- if (Array.isArray(left) || Array.isArray(right)) {
39031
- return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((value, index) => canonicalValuesEqual(value, right[index]));
39032
- }
39033
- if (typeof left !== "object" || left === null || typeof right !== "object" || right === null)
39034
- return false;
39035
- const leftEntries = Object.entries(left);
39036
- const rightEntries = Object.entries(right);
39037
- return leftEntries.length === rightEntries.length && leftEntries.every(([key, value], index) => rightEntries[index]?.[0] === key && canonicalValuesEqual(value, rightEntries[index]?.[1]));
39132
+ return left.kind === right.kind && left.title === right.title && left.summary === right.summary && left.detail === right.detail && left.detailFormat === right.detailFormat && childAttributionEqual(left.attribution, right.attribution);
39038
39133
  }
39039
39134
  function renderActivityGroup({ items, activityExpanded }) {
39040
39135
  if (items.length === 0)
@@ -39235,6 +39330,12 @@ function renderComposerStatus({ admittedSubmissions }) {
39235
39330
 
39236
39331
  `);
39237
39332
  }
39333
+ function isKnownClientRejection(error93) {
39334
+ if (typeof error93 !== "object" || error93 === null || !("status" in error93))
39335
+ return false;
39336
+ const status = error93.status;
39337
+ return typeof status === "number" && status >= 400 && status < 500;
39338
+ }
39238
39339
  async function createDefaultRenderer3() {
39239
39340
  return await createRemyRenderer();
39240
39341
  }
@@ -39495,7 +39596,7 @@ var compactMarkRows = 9;
39495
39596
  var compactMinWidth = 48;
39496
39597
  var compactMinHeight = 20;
39497
39598
  var markBrightnessGain = 4.2;
39498
- var remyCliVersion = "1.15.0";
39599
+ var remyCliVersion = "1.16.0";
39499
39600
  async function showRemySplash({
39500
39601
  createRenderer = createRemyRenderer,
39501
39602
  durationMs = splashDurationMs,
@@ -40898,6 +40999,9 @@ async function runAttachedSession({
40898
40999
  requestInterrupt: async ({ idempotencyKey }) => {
40899
41000
  await operations.interruptSession({ client: operations.client, sessionId, idempotencyKey });
40900
41001
  },
41002
+ requestCompact: async ({ idempotencyKey }) => {
41003
+ await operations.compactSessionContext({ client: operations.client, sessionId, idempotencyKey });
41004
+ },
40901
41005
  requestComplete: async () => {
40902
41006
  const detail = await operations.completeSession({ client: operations.client, sessionId });
40903
41007
  controller.updateDetail(detail);
@@ -41024,6 +41128,7 @@ async function createSessionOperations(dependencies, { onAuthenticated } = {}) {
41024
41128
  appendSessionMessage: dependencies.appendSessionMessage ?? appendSessionMessage,
41025
41129
  cancelSession: dependencies.cancelSession ?? cancelSession,
41026
41130
  completeSession: dependencies.completeSession ?? completeSession,
41131
+ compactSessionContext: dependencies.compactSessionContext ?? compactSessionContext,
41027
41132
  interruptSession: dependencies.interruptSession ?? interruptSession,
41028
41133
  getSession: dependencies.getSession ?? getSession,
41029
41134
  listSessionEvents: dependencies.listSessionEvents ?? listSessionEvents,
@@ -41044,6 +41149,7 @@ async function createSessionOperations(dependencies, { onAuthenticated } = {}) {
41044
41149
  appendSessionMessage: dependencies.appendSessionMessage ?? appendSessionMessage,
41045
41150
  cancelSession: dependencies.cancelSession ?? cancelSession,
41046
41151
  completeSession: dependencies.completeSession ?? completeSession,
41152
+ compactSessionContext: dependencies.compactSessionContext ?? compactSessionContext,
41047
41153
  interruptSession: dependencies.interruptSession ?? interruptSession,
41048
41154
  getSession: dependencies.getSession ?? getSession,
41049
41155
  listSessionEvents: dependencies.listSessionEvents ?? listSessionEvents,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/remy-cli",
3
- "version": "1.15.0",
3
+ "version": "1.16.0",
4
4
  "description": "Remy, the Coding Agent terminal client.",
5
5
  "type": "module",
6
6
  "bin": {