@meistrari/remy-cli 1.12.0 → 1.13.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 +2 -0
  2. package/dist/remy.js +599 -46
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -46,6 +46,8 @@ Use Up/Down to select a session in the dashboard, Left/Right to load the adjacen
46
46
 
47
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
48
 
49
+ Subagent work appears as named **Subagent · <name>** activity rather than messages from Remy. Resolved descendants stay grouped under their actual parent as a compact `Parent › Child` path; missing or contradictory ancestry remains explicitly **Unattributed subagent** instead of being assigned to the active main turn. Press `Ctrl+O` to read retained child messages, complete tool results, reasoning summaries, and child IDs. Child input does not enter your prompt history, child output does not replace Remy's main live preview, and parent or main completion does not end a still-running descendant. Child-specific Stop controls are not available.
50
+
49
51
  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
52
 
51
53
  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.
package/dist/remy.js CHANGED
@@ -33925,15 +33925,16 @@ var sessionWorkspaceGitRevisionEventSchema = exports_external2.object({
33925
33925
  trigger: exports_external2.enum(["turn-ended", "boot-reconcile"])
33926
33926
  }).passthrough()
33927
33927
  }).passthrough();
33928
- var agentTurnEndedEventSchema = exports_external2.object({
33929
- type: exports_external2.literal("agent.turn.ended"),
33930
- turnId: exports_external2.string().min(1),
33931
- actor: exports_external2.object({ type: exports_external2.enum(["main", "subagent"]) }).passthrough(),
33932
- payload: exports_external2.object({ status: exports_external2.enum(["completed", "failed", "cancelled", "interrupted"]) }).passthrough()
33928
+ var agentTurnEndedEventSchema = wrappedTurnEndedAgentEventSchema.pick({
33929
+ type: true,
33930
+ turnId: true,
33931
+ actor: true,
33932
+ payload: true
33933
33933
  }).passthrough();
33934
- var agentMessageDeltaEventSchema = exports_external2.object({
33935
- type: exports_external2.literal("agent.message.delta"),
33936
- payload: exports_external2.object({ role: exports_external2.string(), delta: exports_external2.string() }).passthrough()
33934
+ var agentMessageDeltaEventSchema = wrappedMessageDeltaAgentEventSchema.pick({
33935
+ type: true,
33936
+ actor: true,
33937
+ payload: true
33937
33938
  }).passthrough();
33938
33939
  var publicMessageContentSegmentSchema = exports_external2.discriminatedUnion("type", [
33939
33940
  exports_external2.strictObject({ type: exports_external2.literal("text"), text: exports_external2.string() }),
@@ -33985,6 +33986,13 @@ function createSessionViewState({ detail, activeMessageId }) {
33985
33986
  messageTurns: {},
33986
33987
  retainedTurnStarts: {},
33987
33988
  retainedTurnEnds: {},
33989
+ childLineage: {
33990
+ nextOrder: 0,
33991
+ runtimeEpochs: {},
33992
+ seenRuntimeBoundaryEventIds: {},
33993
+ facts: [],
33994
+ owners: []
33995
+ },
33988
33996
  context: {
33989
33997
  snapshot: { status: "unknown" },
33990
33998
  through: 0
@@ -34038,7 +34046,7 @@ function sessionTurnOutcome({ state, sessionMessageId }) {
34038
34046
  }
34039
34047
  function projectEphemeralEvent({ state, event }) {
34040
34048
  const messageDelta = agentMessageDeltaEventSchema.safeParse(event);
34041
- if (messageDelta.success && messageDelta.data.payload.role === "assistant") {
34049
+ if (messageDelta.success && messageDelta.data.actor.type === "main" && messageDelta.data.payload.role === "assistant") {
34042
34050
  return {
34043
34051
  ...state,
34044
34052
  previews: { ...state.previews, assistantText: state.previews.assistantText + messageDelta.data.payload.delta }
@@ -34059,6 +34067,7 @@ function projectRetainedEvent({
34059
34067
  const workspaceGitRevision = sessionWorkspaceGitRevisionEventSchema.safeParse(event);
34060
34068
  const turnEnded = agentTurnEndedEventSchema.safeParse(event);
34061
34069
  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 });
34070
+ projected = recordAgentLineageEvent({ state: projected, event, retainedEventId });
34062
34071
  if (artifact) {
34063
34072
  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 };
34064
34073
  const timelineArtifact = artifact.kind === "file" ? {
@@ -34171,19 +34180,22 @@ function projectSessionWorkspaceGitRevision({ state, event, occurredAt, retained
34171
34180
  });
34172
34181
  }
34173
34182
  function projectAgentTurnEnded({ state, event, occurredAt, retainedEventId }) {
34174
- const retainedTurnEnds = { ...state.retainedTurnEnds, [event.turnId]: { actorType: event.actor.type, outcome: event.payload.status } };
34175
- const messageTurns = event.actor.type === "main" ? Object.fromEntries(Object.entries(state.messageTurns).map(([messageId, turn]) => [messageId, turn.turnId === event.turnId ? { ...turn, outcome: event.payload.status } : turn])) : state.messageTurns;
34176
- return appendActivity({
34177
- state: { ...state, retainedTurnEnds, messageTurns },
34178
- retainedEventId,
34179
- occurredAt,
34180
- card: 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}.` }
34183
+ const stateWithTurnOutcome = event.actor.type === "main" ? {
34184
+ ...state,
34185
+ retainedTurnEnds: { ...state.retainedTurnEnds, [event.turnId]: { actorType: event.actor.type, outcome: event.payload.status } },
34186
+ messageTurns: Object.fromEntries(Object.entries(state.messageTurns).map(([messageId, turn]) => [messageId, turn.turnId === event.turnId ? { ...turn, outcome: event.payload.status } : turn]))
34187
+ } : state;
34188
+ 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({
34189
+ identity: event.actor,
34190
+ activityTitle: event.payload.status === "failed" ? "Turn failed" : "Turn ended",
34191
+ 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}.` }
34181
34192
  });
34193
+ return appendActivity({ state: stateWithTurnOutcome, retainedEventId, occurredAt, card });
34182
34194
  }
34183
34195
  function projectDurableAgentEvent({ state, event, occurredAt, retainedEventId }) {
34184
34196
  const agentEvent = parseDurableAgentEvent(event);
34185
34197
  if (agentEvent.type === "agent.message.ended")
34186
- return projectAgentMessageEnded({ state, event: agentEvent, occurredAt });
34198
+ return projectAgentMessageEnded({ state, event: agentEvent, occurredAt, retainedEventId });
34187
34199
  if (agentEvent.type === "agent.context.updated")
34188
34200
  return state;
34189
34201
  if (agentEvent.type === "agent.work.observed" && agentEvent.actor.type === "main")
@@ -34195,6 +34207,9 @@ function projectDurableAgentEvent({ state, event, occurredAt, retainedEventId })
34195
34207
  } : state;
34196
34208
  return appendActivity({ state: stateWithTurnStart, retainedEventId, occurredAt, card: toAgentActivityCard(agentEvent) });
34197
34209
  }
34210
+ function hasOwnContextIdentity(identities, identity) {
34211
+ return Object.prototype.hasOwnProperty.call(identities, identity);
34212
+ }
34198
34213
  function projectAgentWorkObserved({
34199
34214
  state,
34200
34215
  event,
@@ -34266,23 +34281,45 @@ function patchedWorkItemId(observation) {
34266
34281
  return observation.itemId;
34267
34282
  return null;
34268
34283
  }
34269
- function projectAgentMessageEnded({ state, event, occurredAt }) {
34284
+ function projectAgentMessageEnded({ state, event, occurredAt, retainedEventId }) {
34270
34285
  const messageId = event.payload.messageId;
34286
+ const text = event.payload.content.map(publicMessageContentSegmentText).join("");
34287
+ if (event.actor.type === "subagent") {
34288
+ const childId = terminalSafeSingleLine(event.actor.subagentId);
34289
+ const card = createChildActivityCard({
34290
+ identity: event.actor,
34291
+ activityTitle: event.payload.role === "assistant" ? "Message completed" : `${capitalize(event.payload.role)} message`,
34292
+ titleIncludesActivity: false,
34293
+ card: {
34294
+ kind: event.payload.role === "assistant" ? "command-output" : "progress",
34295
+ weight: "signal",
34296
+ summary: `${event.payload.role === "assistant" ? "Message completed." : `${capitalize(event.payload.role)} message retained.`} \xB7 ${childId}`,
34297
+ detail: stripAnsi(text) || "[Empty message]"
34298
+ }
34299
+ });
34300
+ return appendActivity({ state, retainedEventId, occurredAt, card });
34301
+ }
34271
34302
  if (state.transcript.some((item) => item.kind === "message" && item.messageId === messageId))
34272
34303
  return state;
34273
34304
  const role2 = event.payload.role;
34274
34305
  const author = role2 === "assistant" ? { label: "Remy", role: "remy" } : role2 === "system" ? { label: "System", role: "system" } : { label: event.payload.commandId ? state.commandAuthorLabels[event.payload.commandId] ?? "Human" : "Human", role: "human" };
34275
- const text = event.payload.content.map(publicMessageContentSegmentText).join("") || (author.role === "remy" ? "[Remy sent an empty message]" : "[Empty message]");
34306
+ const displayedText = text || (author.role === "remy" ? "[Remy sent an empty message]" : "[Empty message]");
34276
34307
  return {
34277
34308
  ...state,
34278
34309
  ...author.role === "remy" ? { previews: { ...state.previews, assistantText: "" } } : {},
34279
- transcript: [...state.transcript, { kind: "message", messageId, occurredAt, author, text, attachments: [] }]
34310
+ transcript: [...state.transcript, { kind: "message", messageId, occurredAt, author, text: displayedText, attachments: [] }]
34280
34311
  };
34281
34312
  }
34282
34313
  function appendActivity({ state, retainedEventId, occurredAt, card }) {
34283
34314
  return { ...state, transcript: [...state.transcript, { kind: "activity", activityId: `activity:${retainedEventId}`, occurredAt, card }] };
34284
34315
  }
34285
34316
  function toAgentActivityCard(event) {
34317
+ const identity = childActivityIdentity(event);
34318
+ if (identity)
34319
+ return toChildAgentActivityCard({ event, identity });
34320
+ return toMainAgentActivityCard(event);
34321
+ }
34322
+ function toMainAgentActivityCard(event) {
34286
34323
  switch (event.type) {
34287
34324
  case "agent.session.started":
34288
34325
  return { kind: "lifecycle", weight: "noise", title: "Remy session started", summary: "Session is ready." };
@@ -34347,6 +34384,444 @@ function toAgentActivityCard(event) {
34347
34384
  throw new SessionProjectionProtocolError("Cannot project an unknown retained agent event.");
34348
34385
  }
34349
34386
  }
34387
+ function toChildAgentActivityCard({ event, identity }) {
34388
+ switch (event.type) {
34389
+ case "agent.turn.started":
34390
+ return createChildActivityCard({ identity, activityTitle: "Turn started", card: { kind: "lifecycle", weight: "noise", summary: "Turn started." } });
34391
+ case "agent.turn.ended":
34392
+ return createChildActivityCard({
34393
+ identity,
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
+ });
34397
+ case "agent.work.observed":
34398
+ return createChildActivityCard({ identity, activityTitle: "Work progress", card: { kind: "progress", weight: "noise", summary: "Subagent updated its work plan.", detail: jsonDetail(event.payload.observations) } });
34399
+ case "agent.message.started":
34400
+ return createChildActivityCard({ identity, activityTitle: "Message started", card: { kind: "lifecycle", weight: "noise", summary: "Subagent is composing a message." } });
34401
+ case "agent.reasoning.started":
34402
+ return createChildActivityCard({ identity, activityTitle: "Reasoning", card: { kind: "reasoning", weight: "noise", summary: "Subagent started a reasoning summary." } });
34403
+ case "agent.reasoning.ended": {
34404
+ const summary = stripAnsi(event.payload.summary ?? "").trim();
34405
+ return createChildActivityCard({
34406
+ identity,
34407
+ activityTitle: "Reasoning",
34408
+ card: summary.length > 0 ? { kind: "reasoning", weight: "signal", summary } : { kind: "reasoning", weight: "noise", summary: "Subagent completed a reasoning summary." }
34409
+ });
34410
+ }
34411
+ case "agent.tool.call.started":
34412
+ return createChildActivityCard({
34413
+ identity,
34414
+ activityTitle: terminalSafeSingleLine(event.payload.toolName),
34415
+ card: {
34416
+ kind: "tool",
34417
+ weight: "signal",
34418
+ summary: "Tool started.",
34419
+ ...event.payload.input === undefined ? {} : { detail: jsonDetail(event.payload.input), detailFormat: "code" }
34420
+ }
34421
+ });
34422
+ case "agent.tool.call.completed": {
34423
+ const failed = event.payload.status === "failed" || event.payload.status === "cancelled";
34424
+ const detail = event.payload.output === undefined ? undefined : toolOutputDetail(event.payload.output);
34425
+ return createChildActivityCard({
34426
+ identity,
34427
+ activityTitle: failed ? "Tool failed" : "Tool completed",
34428
+ card: failed ? { kind: "failure", weight: "signal", summary: providerText(event.payload.error?.message, `Tool ${event.payload.status}.`), ...detail === undefined ? {} : { detail, detailFormat: "code" } } : { kind: "command-output", weight: "signal", summary: "Tool completed.", ...detail === undefined ? {} : { detail, detailFormat: "code" } }
34429
+ });
34430
+ }
34431
+ case "agent.subagent.started":
34432
+ return createChildActivityCard({ identity, activityTitle: "Started", card: { kind: "progress", weight: "signal", summary: providerText(event.payload.name, event.payload.subagentId) } });
34433
+ case "agent.subagent.progress":
34434
+ return createChildActivityCard({ identity, activityTitle: "Progress", card: { kind: "progress", weight: "signal", summary: providerText(event.payload.summary, "Subagent is working.") } });
34435
+ case "agent.subagent.ended":
34436
+ return createChildActivityCard({
34437
+ identity,
34438
+ activityTitle: event.payload.status === "failed" ? "Failed" : "Ended",
34439
+ card: event.payload.status === "failed" ? { kind: "failure", weight: "signal", summary: providerText(event.payload.error?.message, "Subagent failed.") } : { kind: "progress", weight: "signal", summary: providerText(event.payload.summary, `Subagent ${event.payload.status}.`) }
34440
+ });
34441
+ case "agent.usage":
34442
+ return createChildActivityCard({ identity, activityTitle: "Usage updated", card: { kind: "progress", weight: "noise", summary: "Subagent reported usage.", detail: jsonDetail(event.payload.usage) } });
34443
+ case "agent.context.compaction.started":
34444
+ return createChildActivityCard({ identity, activityTitle: "Context compaction started", card: { kind: "reasoning", weight: "noise", summary: "Subagent is compacting context." } });
34445
+ case "agent.context.compaction.completed":
34446
+ return createChildActivityCard({
34447
+ identity,
34448
+ activityTitle: event.payload.status === "failed" ? "Context compaction failed" : "Context compaction completed",
34449
+ card: event.payload.status === "failed" ? { kind: "failure", weight: "signal", summary: providerText(event.payload.error?.message, "Context compaction failed.") } : { kind: "reasoning", weight: "noise", summary: "Subagent compacted context." }
34450
+ });
34451
+ case "agent.user-input.requested":
34452
+ return createChildActivityCard({ identity, activityTitle: "Needs input", card: { kind: "approval-question", weight: "signal", summary: providerText(event.payload.prompt, "Subagent needs input."), detail: jsonDetail(event.payload.questions) } });
34453
+ case "agent.session.started":
34454
+ case "agent.session.configured":
34455
+ case "agent.session.skills.updated":
34456
+ case "agent.session.state.changed":
34457
+ case "agent.session.ended":
34458
+ case "agent.user-input.resolved":
34459
+ case "agent.error":
34460
+ throw new SessionProjectionProtocolError(`Cannot attribute non-child session event type ${event.type} to a subagent.`);
34461
+ case "agent.message.delta":
34462
+ case "agent.reasoning.summary.delta":
34463
+ case "agent.tool.output.delta":
34464
+ throw new SessionProjectionProtocolError(`Cannot project ephemeral session event type ${event.type} as retained history.`);
34465
+ default:
34466
+ throw new SessionProjectionProtocolError("Cannot attribute an unknown retained event to a subagent.");
34467
+ }
34468
+ }
34469
+ function createChildActivityCard({
34470
+ identity,
34471
+ activityTitle,
34472
+ titleIncludesActivity = true,
34473
+ card
34474
+ }) {
34475
+ const subagentId = terminalSafeSingleLine(identity.subagentId);
34476
+ const actorId = terminalSafeSingleLine(identity.actorId);
34477
+ const displayName = terminalSafeSingleLine(identity.name ?? "") || subagentId;
34478
+ const identityDetail = [
34479
+ `Subagent ID: ${subagentId}`,
34480
+ ...actorId !== subagentId ? [`Actor ID: ${actorId}`] : []
34481
+ ].join(`
34482
+ `);
34483
+ const detail = card.detail === undefined ? identityDetail : `${identityDetail}
34484
+
34485
+ ${card.detail}`;
34486
+ return {
34487
+ ...card,
34488
+ title: `Subagent \xB7 ${displayName}${activityTitle === undefined || !titleIncludesActivity ? "" : ` \xB7 ${activityTitle}`}`,
34489
+ detail,
34490
+ attribution: {
34491
+ status: "unresolved",
34492
+ identity,
34493
+ ...activityTitle === undefined ? {} : { activityTitle }
34494
+ }
34495
+ };
34496
+ }
34497
+ function childActivityIdentity(event) {
34498
+ if (event.type === "agent.subagent.started" || event.type === "agent.subagent.progress" || event.type === "agent.subagent.ended") {
34499
+ if (event.actor.type === "subagent") {
34500
+ if (!subagentLifecycleIdentityMatches({ actor: event.actor, payload: event.payload }))
34501
+ throw new SessionProjectionProtocolError(`Retained session event type ${event.type} carried contradictory subagent actor and payload identity.`);
34502
+ return {
34503
+ ...event.actor,
34504
+ ...event.actor.name === undefined && event.payload.name !== undefined ? { name: event.payload.name } : {}
34505
+ };
34506
+ }
34507
+ return {
34508
+ type: "subagent",
34509
+ actorId: event.payload.actorId,
34510
+ subagentId: event.payload.subagentId,
34511
+ parentActorId: event.payload.parentActorId,
34512
+ origin: event.payload.origin,
34513
+ ...event.payload.parentToolCallId === undefined ? {} : { parentToolCallId: event.payload.parentToolCallId },
34514
+ ...event.payload.name === undefined ? {} : { name: event.payload.name }
34515
+ };
34516
+ }
34517
+ if ("actor" in event && event.actor.type === "subagent") {
34518
+ return event.actor;
34519
+ }
34520
+ return;
34521
+ }
34522
+ function subagentLifecycleIdentityMatches({ actor, payload }) {
34523
+ return actor.actorId === payload.actorId && actor.subagentId === payload.subagentId && actor.parentActorId === payload.parentActorId && actor.parentToolCallId === payload.parentToolCallId && subagentOriginsMatch(actor.origin, payload.origin);
34524
+ }
34525
+ function subagentOriginsMatch(left, right) {
34526
+ if (left.type === "tool_call")
34527
+ return right.type === "tool_call" && left.toolCallId === right.toolCallId;
34528
+ return right.type === "provider_task" && left.taskId === right.taskId;
34529
+ }
34530
+ function recordAgentLineageEvent({
34531
+ state,
34532
+ event,
34533
+ retainedEventId
34534
+ }) {
34535
+ if (!event.type.startsWith("agent."))
34536
+ return state;
34537
+ let parsed;
34538
+ try {
34539
+ parsed = parseDurableAgentEvent(event);
34540
+ } catch (error93) {
34541
+ if (error93 instanceof SessionProjectionProtocolError)
34542
+ return state;
34543
+ throw error93;
34544
+ }
34545
+ const runtimeBase = lineageRuntimeBase(parsed);
34546
+ let childLineage = state.childLineage;
34547
+ if (parsed.type === "agent.session.started" || parsed.type === "agent.session.ended") {
34548
+ const boundaryKey = `${runtimeBase}:${lengthPrefixed(parsed.eventId)}`;
34549
+ if (hasOwnContextIdentity(childLineage.seenRuntimeBoundaryEventIds, boundaryKey))
34550
+ return state;
34551
+ childLineage = {
34552
+ ...childLineage,
34553
+ runtimeEpochs: parsed.type === "agent.session.started" ? { ...childLineage.runtimeEpochs, [runtimeBase]: (childLineage.runtimeEpochs[runtimeBase] ?? 0) + 1 } : childLineage.runtimeEpochs,
34554
+ seenRuntimeBoundaryEventIds: {
34555
+ ...childLineage.seenRuntimeBoundaryEventIds,
34556
+ [boundaryKey]: true
34557
+ }
34558
+ };
34559
+ return { ...state, childLineage };
34560
+ }
34561
+ if (!("turnId" in parsed))
34562
+ return state;
34563
+ const runtimeScope = lineageRuntimeScope({
34564
+ runtimeBase,
34565
+ epoch: childLineage.runtimeEpochs[runtimeBase] ?? 0
34566
+ });
34567
+ const order = childLineage.nextOrder;
34568
+ const identity = childActivityIdentity(parsed);
34569
+ const fact = identity ? {
34570
+ retainedEventId,
34571
+ order,
34572
+ runtimeScope,
34573
+ turnId: parsed.turnId,
34574
+ eventType: parsed.type,
34575
+ identity
34576
+ } : parsed.type === "agent.tool.call.started" && parsed.actor.type === "main" ? {
34577
+ retainedEventId,
34578
+ order,
34579
+ runtimeScope,
34580
+ turnId: parsed.turnId,
34581
+ eventType: parsed.type,
34582
+ toolCallId: parsed.payload.toolCallId
34583
+ } : undefined;
34584
+ if (!fact)
34585
+ return state;
34586
+ const nextState = {
34587
+ ...state,
34588
+ childLineage: {
34589
+ ...childLineage,
34590
+ nextOrder: order + 1,
34591
+ facts: [...childLineage.facts, fact]
34592
+ }
34593
+ };
34594
+ if (!isChildLineageFact(fact) || isSubagentLifecycleType(fact.eventType))
34595
+ return reconcileChildLineage(nextState);
34596
+ const resolution = resolveBodyFactAgainstOwners(childLineage.owners, fact);
34597
+ if (resolution.status === "graph-change")
34598
+ return reconcileChildLineage(nextState);
34599
+ if (resolution.status === "unresolved")
34600
+ return nextState;
34601
+ return stampResolvedAttribution({ state: nextState, retainedEventId, fact, owner: resolution.owner });
34602
+ }
34603
+ function resolveBodyFactAgainstOwners(owners, fact) {
34604
+ const candidates = owners.filter((owner) => owner.runtimeScope === fact.runtimeScope && childIdentityMatches(owner.identity, fact.identity));
34605
+ const turnCandidates = candidates.filter((owner) => owner.childTurnIds.has(fact.turnId));
34606
+ if (turnCandidates.length === 1)
34607
+ return { status: "known", owner: turnCandidates[0] };
34608
+ if (candidates.length === 1)
34609
+ return { status: "graph-change" };
34610
+ return { status: "unresolved" };
34611
+ }
34612
+ function stampResolvedAttribution({ state, retainedEventId, fact, owner }) {
34613
+ const activityId = `activity:${retainedEventId}`;
34614
+ for (let index = state.transcript.length - 1;index >= 0; index -= 1) {
34615
+ const item = state.transcript[index];
34616
+ if (item.kind !== "activity" || item.activityId !== activityId)
34617
+ continue;
34618
+ if (!item.card.attribution)
34619
+ return state;
34620
+ const attribution = {
34621
+ status: "resolved",
34622
+ identity: fact.identity,
34623
+ ...item.card.attribution.activityTitle === undefined ? {} : { activityTitle: item.card.attribution.activityTitle },
34624
+ ownerKey: owner.key,
34625
+ owningMainTurnId: owner.owningMainTurnId,
34626
+ path: ownerPath(owner)
34627
+ };
34628
+ if (childAttributionEqual(item.card.attribution, attribution))
34629
+ return state;
34630
+ const transcript = [...state.transcript];
34631
+ transcript[index] = { ...item, card: { ...item.card, attribution } };
34632
+ return { ...state, transcript };
34633
+ }
34634
+ return state;
34635
+ }
34636
+ function lineageRuntimeBase(event) {
34637
+ return `${lengthPrefixed(event.sessionId)}:${lengthPrefixed(event.providerSessionId)}`;
34638
+ }
34639
+ function lineageRuntimeScope({ runtimeBase, epoch }) {
34640
+ return `${runtimeBase}:${epoch}`;
34641
+ }
34642
+ function lengthPrefixed(value) {
34643
+ return `${value.length}:${value}`;
34644
+ }
34645
+ function isChildLineageFact(fact) {
34646
+ return "identity" in fact;
34647
+ }
34648
+ function isSubagentLifecycleType(type) {
34649
+ return type === "agent.subagent.started" || type === "agent.subagent.progress" || type === "agent.subagent.ended";
34650
+ }
34651
+ function childIdentityMatches(left, right) {
34652
+ return left.actorId === right.actorId && left.subagentId === right.subagentId && left.parentActorId === right.parentActorId && left.parentToolCallId === right.parentToolCallId && subagentOriginsMatch(left.origin, right.origin);
34653
+ }
34654
+ function childIdentityCanAnchor(identity) {
34655
+ return identity.actorId !== identity.parentActorId && (identity.origin.type !== "tool_call" || identity.parentToolCallId === undefined || identity.parentToolCallId === identity.origin.toolCallId);
34656
+ }
34657
+ function reconcileChildLineage(state) {
34658
+ const facts = state.childLineage.facts;
34659
+ const childFacts = facts.filter(isChildLineageFact);
34660
+ const mainToolTurns = new Map;
34661
+ for (const fact of facts) {
34662
+ if (isChildLineageFact(fact))
34663
+ continue;
34664
+ const key = `${fact.runtimeScope}:${lengthPrefixed(fact.toolCallId)}`;
34665
+ const turns = mainToolTurns.get(key) ?? new Set;
34666
+ turns.add(fact.turnId);
34667
+ mainToolTurns.set(key, turns);
34668
+ }
34669
+ const owners = [];
34670
+ const ownerByFactOrder = new Map;
34671
+ const mainTurnForToolOrigin = (fact) => {
34672
+ if (fact.identity.origin.type !== "tool_call")
34673
+ return;
34674
+ const turns = mainToolTurns.get(`${fact.runtimeScope}:${lengthPrefixed(fact.identity.origin.toolCallId)}`);
34675
+ return turns?.size === 1 ? [...turns][0] : undefined;
34676
+ };
34677
+ const createOwner = ({
34678
+ fact,
34679
+ owningMainTurnId,
34680
+ parent
34681
+ }) => {
34682
+ if (!childIdentityCanAnchor(fact.identity))
34683
+ return;
34684
+ if (parent && ownerHasActorAncestor(parent, fact.identity.actorId))
34685
+ return;
34686
+ 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);
34687
+ if (samePlacement.some((owner2) => !childIdentityMatches(owner2.identity, fact.identity)))
34688
+ return;
34689
+ const existing = samePlacement.find((owner2) => childIdentityMatches(owner2.identity, fact.identity));
34690
+ if (existing)
34691
+ return existing;
34692
+ const owner = {
34693
+ key: `${fact.runtimeScope}:${lengthPrefixed(owningMainTurnId)}:${fact.order}`,
34694
+ identity: fact.identity,
34695
+ runtimeScope: fact.runtimeScope,
34696
+ owningMainTurnId,
34697
+ parent,
34698
+ childTurnIds: new Set
34699
+ };
34700
+ owners.push(owner);
34701
+ return owner;
34702
+ };
34703
+ for (const fact of childFacts) {
34704
+ if (fact.eventType !== "agent.subagent.started" || fact.identity.parentActorId !== "main")
34705
+ continue;
34706
+ const toolTurn = mainTurnForToolOrigin(fact);
34707
+ if (fact.identity.origin.type === "tool_call" && toolTurn === undefined) {
34708
+ const turns = mainToolTurns.get(`${fact.runtimeScope}:${lengthPrefixed(fact.identity.origin.toolCallId)}`);
34709
+ if (turns && turns.size > 1)
34710
+ continue;
34711
+ }
34712
+ if (toolTurn !== undefined && toolTurn !== fact.turnId)
34713
+ continue;
34714
+ const owner = createOwner({ fact, owningMainTurnId: fact.turnId, parent: null });
34715
+ if (owner)
34716
+ ownerByFactOrder.set(fact.order, owner);
34717
+ }
34718
+ for (const fact of childFacts) {
34719
+ if (isSubagentLifecycleType(fact.eventType) && fact.eventType !== "agent.subagent.started" && fact.identity.parentActorId === "main") {
34720
+ const toolTurn = mainTurnForToolOrigin(fact);
34721
+ if (toolTurn === undefined)
34722
+ continue;
34723
+ 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 });
34724
+ if (owner) {
34725
+ ownerByFactOrder.set(fact.order, owner);
34726
+ owner.childTurnIds.add(fact.turnId);
34727
+ }
34728
+ }
34729
+ }
34730
+ let changed = true;
34731
+ while (changed) {
34732
+ changed = false;
34733
+ for (const fact of childFacts) {
34734
+ if (ownerByFactOrder.has(fact.order))
34735
+ continue;
34736
+ const candidates = owners.filter((owner2) => owner2.runtimeScope === fact.runtimeScope && childIdentityMatches(owner2.identity, fact.identity));
34737
+ const turnCandidates = candidates.filter((owner2) => owner2.childTurnIds.has(fact.turnId));
34738
+ const owner = turnCandidates.length === 1 ? turnCandidates[0] : candidates.length === 1 ? candidates[0] : undefined;
34739
+ if (!owner || fact.eventType === "agent.subagent.started")
34740
+ continue;
34741
+ ownerByFactOrder.set(fact.order, owner);
34742
+ owner.childTurnIds.add(fact.turnId);
34743
+ changed = true;
34744
+ }
34745
+ for (const fact of childFacts) {
34746
+ if (ownerByFactOrder.has(fact.order) || fact.eventType !== "agent.subagent.started" || fact.identity.parentActorId === "main" || !childIdentityCanAnchor(fact.identity)) {
34747
+ continue;
34748
+ }
34749
+ const parentCandidates = owners.filter((owner2) => owner2.runtimeScope === fact.runtimeScope && owner2.identity.actorId === fact.identity.parentActorId);
34750
+ const turnParents = parentCandidates.filter((owner2) => owner2.childTurnIds.has(fact.turnId));
34751
+ const parent = turnParents.length === 1 ? turnParents[0] : parentCandidates.length === 1 ? parentCandidates[0] : undefined;
34752
+ if (!parent)
34753
+ continue;
34754
+ const owner = createOwner({ fact, owningMainTurnId: parent.owningMainTurnId, parent });
34755
+ if (!owner)
34756
+ continue;
34757
+ ownerByFactOrder.set(fact.order, owner);
34758
+ changed = true;
34759
+ }
34760
+ }
34761
+ const resolvedByRetainedEventId = new Map;
34762
+ for (const fact of childFacts) {
34763
+ const owner = ownerByFactOrder.get(fact.order);
34764
+ if (owner)
34765
+ resolvedByRetainedEventId.set(fact.retainedEventId, { fact, owner });
34766
+ }
34767
+ let transcriptChanged = false;
34768
+ const transcript = state.transcript.map((item) => {
34769
+ if (item.kind !== "activity" || !item.card.attribution)
34770
+ return item;
34771
+ const retainedEventId = item.activityId.slice("activity:".length);
34772
+ const resolved = resolvedByRetainedEventId.get(retainedEventId);
34773
+ const attribution = resolved ? {
34774
+ status: "resolved",
34775
+ identity: resolved.fact.identity,
34776
+ ...item.card.attribution.activityTitle === undefined ? {} : { activityTitle: item.card.attribution.activityTitle },
34777
+ ownerKey: resolved.owner.key,
34778
+ owningMainTurnId: resolved.owner.owningMainTurnId,
34779
+ path: ownerPath(resolved.owner)
34780
+ } : {
34781
+ status: "unresolved",
34782
+ identity: item.card.attribution.identity,
34783
+ ...item.card.attribution.activityTitle === undefined ? {} : { activityTitle: item.card.attribution.activityTitle }
34784
+ };
34785
+ if (childAttributionEqual(item.card.attribution, attribution))
34786
+ return item;
34787
+ transcriptChanged = true;
34788
+ return { ...item, card: { ...item.card, attribution } };
34789
+ });
34790
+ const nextState = { ...state, childLineage: { ...state.childLineage, owners } };
34791
+ return transcriptChanged ? { ...nextState, transcript } : nextState;
34792
+ }
34793
+ function ownerHasActorAncestor(parent, actorId) {
34794
+ let candidate = parent;
34795
+ while (candidate) {
34796
+ if (candidate.identity.actorId === actorId)
34797
+ return true;
34798
+ candidate = candidate.parent;
34799
+ }
34800
+ return false;
34801
+ }
34802
+ function ownerPath(owner) {
34803
+ const path = [];
34804
+ let candidate = owner;
34805
+ while (candidate) {
34806
+ path.unshift(candidate.identity);
34807
+ candidate = candidate.parent;
34808
+ }
34809
+ return path;
34810
+ }
34811
+ function childAttributionEqual(left, right) {
34812
+ if (left.status !== right.status || left.activityTitle !== right.activityTitle || !childIdentityMatches(left.identity, right.identity)) {
34813
+ return false;
34814
+ }
34815
+ if (left.status === "unresolved" || right.status === "unresolved")
34816
+ return true;
34817
+ 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]));
34818
+ }
34819
+ function terminalSafeSingleLine(value) {
34820
+ return stripAnsi(value).replace(/\s+/gu, " ").trim();
34821
+ }
34822
+ function capitalize(value) {
34823
+ return value.length === 0 ? value : `${value[0].toUpperCase()}${value.slice(1)}`;
34824
+ }
34350
34825
  function parseDurableAgentEvent(event) {
34351
34826
  if (!event.type.startsWith("agent."))
34352
34827
  throw new SessionProjectionProtocolError(`Cannot project retained session event type ${event.type}.`);
@@ -34383,6 +34858,9 @@ var terminalControlPattern = /[\u0000-\u0008\u000B-\u001F\u007F-\u009F]/g;
34383
34858
  function stripAnsi(value) {
34384
34859
  return value.replace(ansiEscapePattern, "").replace(terminalControlPattern, "");
34385
34860
  }
34861
+ function terminalSafeText(value) {
34862
+ return stripAnsi(value);
34863
+ }
34386
34864
  function formatReasoningPreview(item) {
34387
34865
  if (item.status === "absent")
34388
34866
  return "";
@@ -37593,16 +38071,20 @@ function activityGlyph({ kind, inFlight }) {
37593
38071
  return { glyph: "\xB7", color: PALETTE.dimText };
37594
38072
  return { glyph: "\u2713", color: PALETTE.dimText };
37595
38073
  }
37596
- function dedupeActivitySteps(items) {
37597
- const runs = [];
37598
- for (const item of items) {
37599
- const last = runs[runs.length - 1];
37600
- if (last && last.item.card.kind === item.card.kind && last.item.card.title === item.card.title && last.item.card.summary === item.card.summary)
37601
- last.count += 1;
37602
- else
37603
- runs.push({ item, count: 1 });
38074
+ function activityCardsHaveEqualDisclosure(left, right) {
38075
+ 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);
38076
+ }
38077
+ function canonicalValuesEqual(left, right) {
38078
+ if (Object.is(left, right))
38079
+ return true;
38080
+ if (Array.isArray(left) || Array.isArray(right)) {
38081
+ return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((value, index) => canonicalValuesEqual(value, right[index]));
37604
38082
  }
37605
- return runs;
38083
+ if (typeof left !== "object" || left === null || typeof right !== "object" || right === null)
38084
+ return false;
38085
+ const leftEntries = Object.entries(left);
38086
+ const rightEntries = Object.entries(right);
38087
+ return leftEntries.length === rightEntries.length && leftEntries.every(([key, value], index) => rightEntries[index]?.[0] === key && canonicalValuesEqual(value, rightEntries[index]?.[1]));
37606
38088
  }
37607
38089
  function renderActivityGroup({ items, activityExpanded }) {
37608
38090
  if (items.length === 0)
@@ -37624,22 +38106,82 @@ function renderActivityGroup({ items, activityExpanded }) {
37624
38106
  ], `
37625
38107
  `);
37626
38108
  }
37627
- const steps = dedupeActivitySteps(items).map(({ item, count }) => {
37628
- const inFlight = item.card.kind === "tool" && items.indexOf(item) === lastInFlightIndex;
37629
- const { glyph, color } = activityGlyph({ kind: item.card.kind, inFlight });
37630
- const showSummary = item.card.summary.length > 0 && item.card.summary !== item.card.title && !boilerplateActivitySummaries.has(item.card.summary);
37631
- const detail = item.card.detail === undefined ? undefined : item.card.detailFormat === "code" ? renderCodeSnippet({ text: item.card.detail, indent: " " }) : new StyledText5([dim4(fg6(PALETTE.dimText)(` ${item.card.detail}`))]);
37632
- const step = new StyledText5([
37633
- fg6(color)(` ${glyph} `),
37634
- fg6(PALETTE.bodyText)(item.card.title),
37635
- ...count > 1 ? [dim4(fg6(PALETTE.dimText)(` \xD7${count}`))] : [],
37636
- ...showSummary ? [dim4(fg6(PALETTE.dimText)(`
37637
- ${item.card.summary}`))] : []
37638
- ]);
37639
- return detail ? joinStyled([step, detail], `
38109
+ return joinStyled([summary, ...renderExpandedActivityHierarchy({ items, lastInFlightIndex })], `
38110
+ `);
38111
+ }
38112
+ function renderExpandedActivityHierarchy({ items, lastInFlightIndex }) {
38113
+ const rendered = [];
38114
+ let activeOwnerKey;
38115
+ let index = 0;
38116
+ while (index < items.length) {
38117
+ const item = items[index];
38118
+ const attribution = item.card.attribution;
38119
+ if (attribution?.status === "resolved") {
38120
+ if (activeOwnerKey !== attribution.ownerKey) {
38121
+ rendered.push(new StyledText5([
38122
+ dim4(fg6(PALETTE.dimText)(" ")),
38123
+ fg6(PALETTE.bodyText)(`Subagent \xB7 ${lineagePathLabel(attribution.path)}`)
38124
+ ]));
38125
+ }
38126
+ activeOwnerKey = attribution.ownerKey;
38127
+ } else {
38128
+ activeOwnerKey = undefined;
38129
+ }
38130
+ let count = 1;
38131
+ while (items[index + count] && activityCardsHaveEqualDisclosure(item.card, items[index + count].card)) {
38132
+ count += 1;
38133
+ }
38134
+ rendered.push(renderExpandedActivityStep({
38135
+ item,
38136
+ count,
38137
+ inFlight: item.card.kind === "tool" && index === lastInFlightIndex,
38138
+ nested: attribution?.status === "resolved"
38139
+ }));
38140
+ index += count;
38141
+ }
38142
+ return rendered;
38143
+ }
38144
+ function renderExpandedActivityStep({ item, count, inFlight, nested }) {
38145
+ const { glyph, color } = activityGlyph({ kind: item.card.kind, inFlight });
38146
+ const showSummary = item.card.summary.length > 0 && item.card.summary !== item.card.title && !boilerplateActivitySummaries.has(item.card.summary);
38147
+ const disclosure = renderActivityDisclosure(item.card);
38148
+ const title = expandedActivityTitle(item.card);
38149
+ const indent = nested ? " " : " ";
38150
+ const step = new StyledText5([
38151
+ fg6(color)(`${indent}${glyph} `),
38152
+ fg6(PALETTE.bodyText)(title),
38153
+ ...count > 1 ? [dim4(fg6(PALETTE.dimText)(` \xD7${count}`))] : [],
38154
+ ...showSummary ? [dim4(fg6(PALETTE.dimText)(`
38155
+ ${indent} ${item.card.summary}`))] : []
38156
+ ]);
38157
+ return disclosure ? joinStyled([step, disclosure], `
37640
38158
  `) : step;
37641
- });
37642
- return joinStyled([summary, ...steps], `
38159
+ }
38160
+ function expandedActivityTitle(card) {
38161
+ if (!card.attribution)
38162
+ return card.title;
38163
+ if (card.attribution.status === "resolved")
38164
+ return card.attribution.activityTitle ?? "Activity";
38165
+ const name = childIdentityLabel(card.attribution.identity);
38166
+ return `Unattributed subagent \xB7 ${name}${card.attribution.activityTitle ? ` \xB7 ${card.attribution.activityTitle}` : ""}`;
38167
+ }
38168
+ function lineagePathLabel(path) {
38169
+ return path.map(childIdentityLabel).join(" \u203A ");
38170
+ }
38171
+ function childIdentityLabel(identity) {
38172
+ const safeName = terminalSafeText(identity.name ?? "").replace(/\s+/gu, " ").trim();
38173
+ if (safeName.length > 0)
38174
+ return safeName;
38175
+ return terminalSafeText(identity.subagentId).replace(/\s+/gu, " ").trim();
38176
+ }
38177
+ function renderActivityDisclosure(card) {
38178
+ const details = [];
38179
+ if (card.detail !== undefined) {
38180
+ const safeDetail = terminalSafeText(card.detail);
38181
+ details.push(card.detailFormat === "code" ? renderCodeSnippet({ text: safeDetail, indent: " " }) : new StyledText5([dim4(fg6(PALETTE.dimText)(` ${safeDetail}`))]));
38182
+ }
38183
+ return details.length === 0 ? undefined : joinStyled(details, `
38184
+
37643
38185
  `);
37644
38186
  }
37645
38187
  function renderActivitySummary({ items, includeTimestamp }) {
@@ -37652,9 +38194,20 @@ function renderCollapsedActivityStep({ item, inFlight }) {
37652
38194
  const { glyph, color } = activityGlyph({ kind: item.card.kind, inFlight });
37653
38195
  return new StyledText5([
37654
38196
  fg6(color)(` ${glyph} `),
37655
- dim4(fg6(PALETTE.dimText)(item.card.title))
38197
+ dim4(fg6(PALETTE.dimText)(collapsedActivityTitle(item.card)))
37656
38198
  ]);
37657
38199
  }
38200
+ function collapsedActivityTitle(card) {
38201
+ const attribution = card.attribution;
38202
+ if (!attribution)
38203
+ return card.title;
38204
+ if (attribution.status === "unresolved") {
38205
+ const name = childIdentityLabel(attribution.identity);
38206
+ return `Unattributed subagent \xB7 ${name}${attribution.activityTitle ? ` \xB7 ${attribution.activityTitle}` : ""}`;
38207
+ }
38208
+ const activityTitle = attribution.activityTitle ? ` \xB7 ${attribution.activityTitle}` : "";
38209
+ return `${lineagePathLabel(attribution.path)}${activityTitle}`;
38210
+ }
37658
38211
  function renderSubmittedTimeline({ state, admittedSubmissions }) {
37659
38212
  const submitted = admittedSubmissions.filter((submission) => submission.status === "submitted" && !state.transcript.some((item) => item.kind === "message" && item.messageId === submission.messageId));
37660
38213
  const messages = submitted.map((submission) => {
@@ -37844,7 +38397,7 @@ var compactMarkRows = 9;
37844
38397
  var compactMinWidth = 48;
37845
38398
  var compactMinHeight = 20;
37846
38399
  var markBrightnessGain = 4.2;
37847
- var remyCliVersion = "1.12.0";
38400
+ var remyCliVersion = "1.13.0";
37848
38401
  async function showRemySplash({
37849
38402
  createRenderer = createRemyRenderer,
37850
38403
  durationMs = splashDurationMs,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/remy-cli",
3
- "version": "1.12.0",
3
+ "version": "1.13.0",
4
4
  "description": "Remy, the Coding Agent terminal client.",
5
5
  "type": "module",
6
6
  "bin": {