replicas-cli 0.2.455 → 0.2.457

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 (2) hide show
  1. package/dist/index.mjs +449 -91
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -7730,29 +7730,51 @@ function getClaudePartialMessageStreamId(event) {
7730
7730
  if (event.type !== CLAUDE_PARTIAL_MESSAGE_EVENT_TYPE) return null;
7731
7731
  return typeof event.payload.streamId === "string" && event.payload.streamId ? event.payload.streamId : null;
7732
7732
  }
7733
- function getEventSignature(event) {
7734
- const streamId = getClaudePartialMessageStreamId(event);
7735
- if (streamId) {
7736
- return `${CLAUDE_PARTIAL_MESSAGE_EVENT_TYPE}:${streamId}`;
7737
- }
7738
- const normalize = (input) => {
7739
- if (Array.isArray(input)) return input.map(normalize);
7740
- if (input && typeof input === "object") {
7741
- const entries = Object.entries(input).sort(([a], [b]) => a.localeCompare(b)).map(([key, val]) => [key, normalize(val)]);
7742
- return Object.fromEntries(entries);
7743
- }
7744
- return input;
7745
- };
7746
- try {
7747
- return JSON.stringify(normalize(event));
7748
- } catch {
7749
- return String(event);
7750
- }
7751
- }
7733
+ var ACCEPTED_USER_MESSAGE_SOURCE = "replicas-chat-turn-accepted";
7752
7734
  var USER_MESSAGE_ID_PAYLOAD_KEY = "replicasMessageId";
7753
7735
  var CODEX_ASP_ITEM_ID_PAYLOAD_KEY = "codexAspItemId";
7736
+ var CODEX_ASP_TRANSCRIPT_UPDATED_EVENT_TYPE = "codex-asp-transcript-updated";
7754
7737
  var CODEX_QUOTA_STATUS_EVENT_TYPE = "codex-quota-status";
7755
7738
  var CHAT_INTERRUPTED_EVENT_TYPE = "replicas-interrupted";
7739
+ var CHAT_GOAL_EVENT_TYPE = "chat-goal";
7740
+ var CHAT_GOAL_STATUSES = [
7741
+ "active",
7742
+ "paused",
7743
+ "blocked",
7744
+ "usageLimited",
7745
+ "budgetLimited",
7746
+ "complete"
7747
+ ];
7748
+ function isChatGoalStatus(value) {
7749
+ return typeof value === "string" && CHAT_GOAL_STATUSES.some((status) => status === value);
7750
+ }
7751
+ function coerceChatGoalPayload(payload) {
7752
+ const goal = payload.goal;
7753
+ if (goal === null || goal === void 0) return null;
7754
+ if (typeof goal !== "object") return null;
7755
+ const candidate = goal;
7756
+ const threadId = candidate.threadId;
7757
+ const objective = candidate.objective;
7758
+ const status = candidate.status;
7759
+ const tokensUsed = candidate.tokensUsed;
7760
+ const timeUsedSeconds = candidate.timeUsedSeconds;
7761
+ const createdAt = candidate.createdAt;
7762
+ const updatedAt = candidate.updatedAt;
7763
+ const tokenBudget = candidate.tokenBudget ?? null;
7764
+ if (typeof threadId !== "string" || typeof objective !== "string" || !isChatGoalStatus(status) || typeof tokensUsed !== "number" || typeof timeUsedSeconds !== "number" || typeof createdAt !== "string" || typeof updatedAt !== "string" || tokenBudget !== null && typeof tokenBudget !== "number") {
7765
+ return null;
7766
+ }
7767
+ return {
7768
+ threadId,
7769
+ objective,
7770
+ status,
7771
+ tokenBudget,
7772
+ tokensUsed,
7773
+ timeUsedSeconds,
7774
+ createdAt,
7775
+ updatedAt
7776
+ };
7777
+ }
7756
7778
 
7757
7779
  // ../shared/src/credentials/types.ts
7758
7780
  var CREDENTIAL_SCOPE = {
@@ -10056,7 +10078,7 @@ function formatTurnElapsed(ms) {
10056
10078
  }
10057
10079
 
10058
10080
  // ../shared/src/cli-version.ts
10059
- var CLI_VERSION = "0.2.455";
10081
+ var CLI_VERSION = "0.2.457";
10060
10082
 
10061
10083
  // ../shared/src/version.ts
10062
10084
  function compareVersions(v1, v2) {
@@ -24651,6 +24673,90 @@ function normalizeCodexAspTranscriptStatus(status, failed = false) {
24651
24673
  if (status === "completed") return "completed";
24652
24674
  return "in_progress";
24653
24675
  }
24676
+ function isCodexAspTranscript(value) {
24677
+ if (!isRecord(value)) return false;
24678
+ return typeof value.threadId === "string" && typeof value.updatedAt === "string" && Array.isArray(value.turns);
24679
+ }
24680
+ function isCodexAspTranscriptDelta(value) {
24681
+ if (!isRecord(value)) return false;
24682
+ if (typeof value.threadId !== "string" || typeof value.updatedAt !== "string" || !Array.isArray(value.turns) || value.reset !== void 0 && value.reset !== null && !isCodexAspTranscript(value.reset)) {
24683
+ return false;
24684
+ }
24685
+ return value.turns.every((turn) => isRecord(turn) && typeof turn.id === "string" && typeof turn.status === "string" && typeof turn.startedAt === "string" && (turn.completedAt === null || typeof turn.completedAt === "string") && Array.isArray(turn.items) && turn.items.every((item) => isRecord(item) && typeof item.id === "string" && (item.item === void 0 || isRecord(item.item) && typeof item.item.type === "string" && typeof item.item.id === "string" && typeof item.item.timestamp === "string") && (item.appendText === void 0 || typeof item.appendText === "string") && (item.appendOutput === void 0 || typeof item.appendOutput === "string")));
24686
+ }
24687
+ function appendCodexAspTranscriptField(current, append) {
24688
+ return append === void 0 ? current : `${current ?? ""}${append}`;
24689
+ }
24690
+ function patchCodexAspTranscriptItem(current, delta) {
24691
+ const base = delta.item ?? current;
24692
+ if (!base) {
24693
+ return null;
24694
+ }
24695
+ if (base.type === "agentMessage" && delta.appendText !== void 0) {
24696
+ return {
24697
+ ...base,
24698
+ text: appendCodexAspTranscriptField(base.text, delta.appendText) ?? ""
24699
+ };
24700
+ }
24701
+ if ((base.type === "commandExecution" || base.type === "fileChange") && delta.appendOutput !== void 0) {
24702
+ return {
24703
+ ...base,
24704
+ output: appendCodexAspTranscriptField(base.output, delta.appendOutput)
24705
+ };
24706
+ }
24707
+ return base;
24708
+ }
24709
+ function applyCodexAspTranscriptDelta(current, delta) {
24710
+ if (!delta) return current ?? null;
24711
+ if (delta.reset === null) {
24712
+ return null;
24713
+ }
24714
+ const base = delta.reset ?? (current?.threadId === delta.threadId ? current : {
24715
+ threadId: delta.threadId,
24716
+ updatedAt: delta.updatedAt,
24717
+ turns: []
24718
+ });
24719
+ const turns = base.turns.map((turn) => ({
24720
+ ...turn,
24721
+ items: [...turn.items]
24722
+ }));
24723
+ for (const turnDelta of delta.turns) {
24724
+ let turn = turns.find((candidate) => candidate.id === turnDelta.id);
24725
+ if (!turn) {
24726
+ turn = {
24727
+ id: turnDelta.id,
24728
+ status: turnDelta.status,
24729
+ startedAt: turnDelta.startedAt,
24730
+ completedAt: turnDelta.completedAt,
24731
+ items: []
24732
+ };
24733
+ turns.push(turn);
24734
+ } else {
24735
+ turn.status = turnDelta.status;
24736
+ turn.startedAt = turnDelta.startedAt;
24737
+ turn.completedAt = turnDelta.completedAt;
24738
+ }
24739
+ for (const itemDelta of turnDelta.items) {
24740
+ const itemIndex = turn.items.findIndex((item) => item.id === itemDelta.id);
24741
+ const patched = patchCodexAspTranscriptItem(turn.items[itemIndex], itemDelta);
24742
+ if (!patched) continue;
24743
+ if (itemIndex === -1) {
24744
+ turn.items.push(patched);
24745
+ } else {
24746
+ turn.items[itemIndex] = patched;
24747
+ }
24748
+ }
24749
+ }
24750
+ turns.sort((a, b) => Date.parse(a.startedAt) - Date.parse(b.startedAt));
24751
+ for (const turn of turns) {
24752
+ turn.items.sort((a, b) => (a.sequence ?? Number.MAX_SAFE_INTEGER) - (b.sequence ?? Number.MAX_SAFE_INTEGER) || Date.parse(a.timestamp) - Date.parse(b.timestamp));
24753
+ }
24754
+ return {
24755
+ threadId: delta.threadId,
24756
+ updatedAt: delta.updatedAt,
24757
+ turns
24758
+ };
24759
+ }
24654
24760
 
24655
24761
  // ../shared/src/routes/workspaces.ts
24656
24762
  var WORKSPACE_STATUSES = ["active", "sleeping", "archived", "preparing", "error"];
@@ -24970,8 +25076,25 @@ function parseSkillName(tool, input) {
24970
25076
  const skillName = parsedInput.skill ?? parsedInput.name;
24971
25077
  return typeof skillName === "string" && skillName ? skillName : null;
24972
25078
  }
25079
+ function parseSkillFilePath(input) {
25080
+ const parsedInput = typeof input === "string" ? safeJsonParse(input, null) : input;
25081
+ if (!isRecord(parsedInput)) return null;
25082
+ const path6 = [parsedInput.path, parsedInput.filePath, parsedInput.file_path].find((value) => typeof value === "string" && value.length > 0);
25083
+ if (!path6) return null;
25084
+ const parts = path6.split(/[\\/]+/).filter(Boolean);
25085
+ if (parts.length < 2) return null;
25086
+ if (parts[parts.length - 1].toLowerCase() !== "skill.md") return null;
25087
+ const skillName = parts[parts.length - 2];
25088
+ return skillName && skillName.toLowerCase() !== "skills" ? skillName : null;
25089
+ }
25090
+ function skillNameFromToolCall(tool, input) {
25091
+ const directSkillName = parseSkillName(tool, input);
25092
+ if (directSkillName) return directSkillName;
25093
+ if (typeof tool !== "string" || !["read", "read_file", "readfile"].includes(tool.toLowerCase())) return null;
25094
+ return parseSkillFilePath(input);
25095
+ }
24973
25096
  function createCallDisplayMessage(message) {
24974
- const skillName = message.server.toLowerCase() === "skills" ? message.tool : parseSkillName(message.tool, message.input);
25097
+ const skillName = message.server.toLowerCase() === "skills" ? message.tool : skillNameFromToolCall(message.tool, message.input);
24975
25098
  if (skillName === null) return { ...message, type: "tool_call" };
24976
25099
  return {
24977
25100
  id: message.id,
@@ -24997,6 +25120,49 @@ function areUserMessagesWithinMatchWindow(a, b) {
24997
25120
  return a.content === b.content && Math.abs(parseTimestampMs(a.timestamp) - parseTimestampMs(b.timestamp)) <= USER_MESSAGE_MATCH_GRACE_PERIOD_MS;
24998
25121
  }
24999
25122
 
25123
+ // ../shared/src/agent-event-utils.ts
25124
+ function getUserMessage(event) {
25125
+ return event.type === "event_msg" && event.payload.type === "user_message" && typeof event.payload.message === "string" ? event.payload.message : null;
25126
+ }
25127
+ function getUserMessageId(event) {
25128
+ const messageId = event.payload[USER_MESSAGE_ID_PAYLOAD_KEY];
25129
+ return typeof messageId === "string" ? messageId : null;
25130
+ }
25131
+ function getUserMessageItemId(event) {
25132
+ const itemId = event.payload[CODEX_ASP_ITEM_ID_PAYLOAD_KEY];
25133
+ return typeof itemId === "string" ? itemId : null;
25134
+ }
25135
+ function areSameUserMessageEvents(a, b) {
25136
+ const aMessage = getUserMessage(a);
25137
+ const bMessage = getUserMessage(b);
25138
+ if (!aMessage || aMessage !== bMessage) return false;
25139
+ const aMessageId = getUserMessageId(a);
25140
+ const bMessageId = getUserMessageId(b);
25141
+ if (aMessageId || bMessageId) return aMessageId === bMessageId;
25142
+ const aItemId = getUserMessageItemId(a);
25143
+ const bItemId = getUserMessageItemId(b);
25144
+ if (aItemId || bItemId) return aItemId === bItemId;
25145
+ return areUserMessagesWithinMatchWindow(
25146
+ { content: aMessage, timestamp: a.timestamp },
25147
+ { content: bMessage, timestamp: b.timestamp }
25148
+ );
25149
+ }
25150
+ function mergeAgentEvents(primary, supplemental, options) {
25151
+ const merged = [...primary];
25152
+ const { areDuplicates, mergeEvent } = options;
25153
+ for (const event of supplemental) {
25154
+ const existingIndex = merged.findIndex((existing) => areDuplicates(existing, event));
25155
+ if (existingIndex === -1) {
25156
+ merged.push(event);
25157
+ continue;
25158
+ }
25159
+ if (mergeEvent) {
25160
+ merged[existingIndex] = mergeEvent(merged[existingIndex], event);
25161
+ }
25162
+ }
25163
+ return merged;
25164
+ }
25165
+
25000
25166
  // ../shared/src/display-message/parsers/codex-parser.ts
25001
25167
  function getStatusFromExitCode(exitCode) {
25002
25168
  return exitCode === 0 ? "completed" : "failed";
@@ -25573,6 +25739,15 @@ function parsePiEvents(events) {
25573
25739
  for (const event of events) {
25574
25740
  if (event.type === "event_msg" && event.payload.type === "user_message" && typeof event.payload.message === "string") {
25575
25741
  messages.push({ id: `pi-user-${event.timestamp}-${messages.length}`, type: "user", content: event.payload.message, timestamp: event.timestamp });
25742
+ if (typeof event.payload.skillName === "string" && event.payload.skillName) {
25743
+ messages.push({
25744
+ id: `pi-skill-${event.timestamp}-${messages.length}`,
25745
+ type: "skill",
25746
+ skillName: event.payload.skillName,
25747
+ status: "completed",
25748
+ timestamp: event.timestamp
25749
+ });
25750
+ }
25576
25751
  continue;
25577
25752
  }
25578
25753
  if (event.type === "pi-error") {
@@ -26401,6 +26576,16 @@ function messageForItem(item) {
26401
26576
  timestamp: item.timestamp
26402
26577
  };
26403
26578
  }
26579
+ function skillMessagesForItem(item) {
26580
+ if (item.type !== "userMessage" || !item.skillNames) return [];
26581
+ return item.skillNames.filter((skillName) => skillName.trim().length > 0).map((skillName, index) => ({
26582
+ id: `skill-${item.id}-${index}`,
26583
+ type: "skill",
26584
+ skillName,
26585
+ status: "completed",
26586
+ timestamp: item.timestamp
26587
+ }));
26588
+ }
26404
26589
  function stableString(value) {
26405
26590
  if (value === void 0) return "";
26406
26591
  if (typeof value !== "object" || value === null) return String(value);
@@ -26458,6 +26643,7 @@ function parseCodexAspTranscript(transcript) {
26458
26643
  if (message) {
26459
26644
  messages.push(message);
26460
26645
  }
26646
+ messages.push(...skillMessagesForItem(item));
26461
26647
  }
26462
26648
  return messages;
26463
26649
  }
@@ -33766,7 +33952,31 @@ function useGenerateWorkspaceName() {
33766
33952
  // ../shared/src/hooks/useWorkspaceEngine.ts
33767
33953
  import { useCallback as useCallback4 } from "react";
33768
33954
  import { useQuery as useQuery2, useMutation as useMutation2 } from "@tanstack/react-query";
33769
- function upsertChat(chats, chat) {
33955
+
33956
+ // ../shared/src/workspace-chat.ts
33957
+ import { queryOptions } from "@tanstack/react-query";
33958
+ var workspaceChatKeys = {
33959
+ chats: (workspaceId) => ["workspace-chats", workspaceId],
33960
+ allChats: (workspaceId) => ["workspace-all-chats", workspaceId],
33961
+ histories: (workspaceId) => ["chat-history", workspaceId],
33962
+ history: (workspaceId, chatId) => ["chat-history", workspaceId, chatId],
33963
+ queues: (workspaceId) => ["chat-queue", workspaceId],
33964
+ queue: (workspaceId, chatId) => ["chat-queue", workspaceId, chatId]
33965
+ };
33966
+ function workspaceChatsQueryOptions({
33967
+ workspaceId,
33968
+ fetchChats,
33969
+ enabled = true,
33970
+ staleTime = 6e4
33971
+ }) {
33972
+ return queryOptions({
33973
+ queryKey: workspaceChatKeys.chats(workspaceId),
33974
+ queryFn: fetchChats,
33975
+ enabled: Boolean(workspaceId) && enabled,
33976
+ staleTime
33977
+ });
33978
+ }
33979
+ function upsertWorkspaceChat(chats, chat) {
33770
33980
  const existingIndex = chats.findIndex((item) => item.id === chat.id);
33771
33981
  if (existingIndex === -1) {
33772
33982
  return [chat, ...chats].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
@@ -33775,7 +33985,7 @@ function upsertChat(chats, chat) {
33775
33985
  next[existingIndex] = chat;
33776
33986
  return next.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
33777
33987
  }
33778
- function patchChat(chats, chatId, patch) {
33988
+ function patchWorkspaceChat(chats, chatId, patch) {
33779
33989
  let changed = false;
33780
33990
  const next = chats.map((chat) => {
33781
33991
  if (chat.id !== chatId) return chat;
@@ -33784,21 +33994,229 @@ function patchChat(chats, chatId, patch) {
33784
33994
  });
33785
33995
  return changed ? next.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt)) : chats;
33786
33996
  }
33997
+ function isAcceptedUserMessage(event) {
33998
+ return getUserMessage(event) !== null && event.payload.source === ACCEPTED_USER_MESSAGE_SOURCE;
33999
+ }
34000
+ function areDuplicateUserMessages(a, b) {
34001
+ if (areSameUserMessageEvents(a, b)) return true;
34002
+ const aMessage = getUserMessage(a);
34003
+ const bMessage = getUserMessage(b);
34004
+ if (!aMessage || aMessage !== bMessage) return false;
34005
+ const aAccepted = isAcceptedUserMessage(a);
34006
+ const bAccepted = isAcceptedUserMessage(b);
34007
+ return aAccepted !== bAccepted ? areUserMessagesWithinMatchWindow(
34008
+ { content: aMessage, timestamp: a.timestamp },
34009
+ { content: bMessage, timestamp: b.timestamp }
34010
+ ) : false;
34011
+ }
34012
+ function areDuplicateWorkspaceChatEvents(a, b) {
34013
+ const aPartialStreamId = getClaudePartialMessageStreamId(a);
34014
+ const bPartialStreamId = getClaudePartialMessageStreamId(b);
34015
+ return aPartialStreamId !== null && aPartialStreamId === bPartialStreamId || JSON.stringify(a) === JSON.stringify(b) || areDuplicateUserMessages(a, b);
34016
+ }
34017
+ function mergeWorkspaceChatHistoryEvents(primary, supplemental) {
34018
+ return mergeAgentEvents(primary, supplemental, {
34019
+ areDuplicates: areDuplicateWorkspaceChatEvents,
34020
+ mergeEvent: (current, candidate) => getClaudePartialMessageStreamId(candidate) !== null ? candidate : current
34021
+ });
34022
+ }
34023
+ function isSameChatSender(a, b) {
34024
+ return a.senderUserId === b.senderUserId && a.recordedAt === b.recordedAt;
34025
+ }
34026
+ function codexAspTranscriptUpdateFromEvent(event) {
34027
+ if (event.type !== CODEX_ASP_TRANSCRIPT_UPDATED_EVENT_TYPE) return null;
34028
+ const { payload } = event;
34029
+ const updatedAt = payload.updatedAt;
34030
+ const transcript = payload.transcript;
34031
+ const transcriptDelta = payload.transcriptDelta;
34032
+ if (typeof updatedAt !== "string" || transcript !== null && !isCodexAspTranscript(transcript) || transcriptDelta !== void 0 && !isCodexAspTranscriptDelta(transcriptDelta)) {
34033
+ return null;
34034
+ }
34035
+ const threadId = typeof payload.threadId === "string" ? payload.threadId : void 0;
34036
+ return {
34037
+ updatedAt,
34038
+ transcript,
34039
+ ...transcriptDelta ? { transcriptDelta } : {},
34040
+ ...threadId ? { threadId } : {}
34041
+ };
34042
+ }
34043
+ function patchCachedChats(queryClient2, workspaceId, chatId, patch) {
34044
+ for (const queryKey of [
34045
+ workspaceChatKeys.chats(workspaceId),
34046
+ workspaceChatKeys.allChats(workspaceId)
34047
+ ]) {
34048
+ queryClient2.setQueryData(
34049
+ queryKey,
34050
+ (current) => current ? {
34051
+ ...current,
34052
+ chats: patchWorkspaceChat(current.chats, chatId, patch)
34053
+ } : current
34054
+ );
34055
+ }
34056
+ }
34057
+ function applyWorkspaceChatEvent(queryClient2, workspaceId, event, options = {}) {
34058
+ if (event.type === "chat.created" || event.type === "chat.updated") {
34059
+ const { chat } = event.payload;
34060
+ queryClient2.setQueryData(
34061
+ workspaceChatKeys.allChats(workspaceId),
34062
+ (current) => current ? { ...current, chats: upsertWorkspaceChat(current.chats, chat) } : current
34063
+ );
34064
+ if (chat.parentChatId === null && !options.skipRootChatUpsert) {
34065
+ queryClient2.setQueryData(
34066
+ workspaceChatKeys.chats(workspaceId),
34067
+ (current) => ({
34068
+ ...current ?? {},
34069
+ chats: upsertWorkspaceChat(current?.chats ?? [], chat),
34070
+ deletedChats: (current?.deletedChats ?? []).filter((item) => item.id !== chat.id)
34071
+ })
34072
+ );
34073
+ }
34074
+ return "chat-list-changed";
34075
+ }
34076
+ if (event.type === "chat.deleted") {
34077
+ queryClient2.setQueryData(workspaceChatKeys.chats(workspaceId), (current) => {
34078
+ if (!current) return current;
34079
+ const deletedChat = event.payload.chat;
34080
+ return {
34081
+ ...current,
34082
+ chats: current.chats.filter((chat) => chat.id !== event.payload.chatId),
34083
+ deletedChats: deletedChat?.deletedAt ? [
34084
+ deletedChat,
34085
+ ...(current.deletedChats ?? []).filter((chat) => chat.id !== deletedChat.id)
34086
+ ] : (current.deletedChats ?? []).filter((chat) => chat.id !== event.payload.chatId)
34087
+ };
34088
+ });
34089
+ queryClient2.setQueryData(
34090
+ workspaceChatKeys.allChats(workspaceId),
34091
+ (current) => current ? {
34092
+ ...current,
34093
+ chats: current.chats.filter((chat) => chat.id !== event.payload.chatId)
34094
+ } : current
34095
+ );
34096
+ queryClient2.removeQueries({
34097
+ queryKey: workspaceChatKeys.history(workspaceId, event.payload.chatId)
34098
+ });
34099
+ return "chat-list-changed";
34100
+ }
34101
+ if (event.type === "chat.turn.accepted") {
34102
+ if (event.payload.queued) {
34103
+ queryClient2.invalidateQueries({
34104
+ queryKey: workspaceChatKeys.queue(workspaceId, event.payload.chatId)
34105
+ });
34106
+ return "queue-changed";
34107
+ }
34108
+ const sender = event.payload.sender;
34109
+ const acceptedEvent = event.payload.event;
34110
+ if (sender || acceptedEvent) {
34111
+ queryClient2.setQueryData(
34112
+ workspaceChatKeys.history(workspaceId, event.payload.chatId),
34113
+ (current) => ({
34114
+ ...current ?? { thread_id: null },
34115
+ events: acceptedEvent ? mergeWorkspaceChatHistoryEvents(current?.events ?? [], [acceptedEvent]) : current?.events ?? [],
34116
+ senders: sender ? [
34117
+ ...(current?.senders ?? []).filter(
34118
+ (candidate) => !isSameChatSender(candidate, sender)
34119
+ ),
34120
+ sender
34121
+ ] : current?.senders
34122
+ })
34123
+ );
34124
+ }
34125
+ patchCachedChats(queryClient2, workspaceId, event.payload.chatId, {
34126
+ processing: true,
34127
+ awaitingInput: false,
34128
+ updatedAt: event.ts
34129
+ });
34130
+ return "chat-list-changed";
34131
+ }
34132
+ if (event.type === "chat.turn.started") {
34133
+ queryClient2.setQueryData(
34134
+ workspaceChatKeys.queue(workspaceId, event.payload.chatId),
34135
+ (current) => current ? {
34136
+ ...current,
34137
+ queue: current.queue.filter((message) => message.id !== event.payload.messageId)
34138
+ } : current
34139
+ );
34140
+ queryClient2.invalidateQueries({
34141
+ queryKey: workspaceChatKeys.queue(workspaceId, event.payload.chatId)
34142
+ });
34143
+ patchCachedChats(queryClient2, workspaceId, event.payload.chatId, {
34144
+ processing: true,
34145
+ awaitingInput: false,
34146
+ updatedAt: event.ts
34147
+ });
34148
+ const history = queryClient2.getQueryData(
34149
+ workspaceChatKeys.history(workspaceId, event.payload.chatId)
34150
+ );
34151
+ if (!history?.events.some((candidate) => getUserMessageId(candidate) === event.payload.messageId)) {
34152
+ queryClient2.invalidateQueries({
34153
+ queryKey: workspaceChatKeys.history(workspaceId, event.payload.chatId)
34154
+ });
34155
+ }
34156
+ return "chat-list-changed";
34157
+ }
34158
+ if (event.type === "chat.turn.delta") {
34159
+ const historyKey = workspaceChatKeys.history(workspaceId, event.payload.chatId);
34160
+ const transcriptUpdate = codexAspTranscriptUpdateFromEvent(event.payload.event);
34161
+ if (transcriptUpdate) {
34162
+ queryClient2.setQueryData(historyKey, (current) => {
34163
+ const transcript = transcriptUpdate.transcriptDelta ? applyCodexAspTranscriptDelta(
34164
+ current?.codexAspTranscript ?? transcriptUpdate.transcript ?? null,
34165
+ transcriptUpdate.transcriptDelta
34166
+ ) : transcriptUpdate.transcript ?? current?.codexAspTranscript ?? null;
34167
+ return {
34168
+ ...current ?? { events: [] },
34169
+ thread_id: current?.thread_id ?? transcript?.threadId ?? transcriptUpdate.threadId ?? null,
34170
+ codexAspTranscript: transcript
34171
+ };
34172
+ });
34173
+ return "turn-delta";
34174
+ }
34175
+ queryClient2.setQueryData(historyKey, (current) => ({
34176
+ ...current ?? { thread_id: null },
34177
+ events: mergeWorkspaceChatHistoryEvents(current?.events ?? [], [event.payload.event]),
34178
+ goal: event.payload.event.type === CHAT_GOAL_EVENT_TYPE ? coerceChatGoalPayload(event.payload.event.payload) : current?.goal
34179
+ }));
34180
+ return "turn-delta";
34181
+ }
34182
+ if (event.type === "chat.turn.completed" || event.type === "chat.interrupted") {
34183
+ patchCachedChats(queryClient2, workspaceId, event.payload.chatId, {
34184
+ processing: getTerminalChatProcessing(event),
34185
+ awaitingInput: false,
34186
+ isAuthRetrying: false,
34187
+ updatedAt: event.ts
34188
+ });
34189
+ queryClient2.invalidateQueries({
34190
+ queryKey: workspaceChatKeys.history(workspaceId, event.payload.chatId)
34191
+ });
34192
+ queryClient2.invalidateQueries({
34193
+ queryKey: workspaceChatKeys.queue(workspaceId, event.payload.chatId)
34194
+ });
34195
+ queryClient2.invalidateQueries({
34196
+ queryKey: workspaceChatKeys.allChats(workspaceId)
34197
+ });
34198
+ return "turn-completed";
34199
+ }
34200
+ return null;
34201
+ }
34202
+
34203
+ // ../shared/src/hooks/useWorkspaceEngine.ts
33787
34204
  function useWorkspaceChats(workspaceId) {
33788
34205
  const auth = useReplicasAuth();
33789
34206
  const orgFetch = useOrgFetch();
33790
- return useQuery2({
33791
- queryKey: ["workspace-chats", workspaceId],
33792
- queryFn: () => orgFetch(`/v1/workspaces/${workspaceId}/chats`),
33793
- enabled: !!workspaceId,
33794
- staleTime: 6e4
33795
- }, auth.queryClient);
34207
+ return useQuery2(
34208
+ workspaceChatsQueryOptions({
34209
+ workspaceId,
34210
+ fetchChats: () => orgFetch(`/v1/workspaces/${workspaceId}/chats`)
34211
+ }),
34212
+ auth.queryClient
34213
+ );
33796
34214
  }
33797
34215
  function useChatHistory(workspaceId, chatId) {
33798
34216
  const auth = useReplicasAuth();
33799
34217
  const orgFetch = useOrgFetch();
33800
34218
  return useQuery2({
33801
- queryKey: ["chat-history", workspaceId, chatId],
34219
+ queryKey: workspaceChatKeys.history(workspaceId, chatId),
33802
34220
  queryFn: () => orgFetch(`/v1/workspaces/${workspaceId}/chats/${chatId}/history`),
33803
34221
  enabled: !!workspaceId && !!chatId
33804
34222
  }, auth.queryClient);
@@ -33875,67 +34293,7 @@ function useWorkspaceEvents(workspaceId, enabled = true) {
33875
34293
  const qc = auth.queryClient;
33876
34294
  const handleEvent = useCallback4((event) => {
33877
34295
  if (!workspaceId) return;
33878
- const chatsKey = ["workspace-chats", workspaceId];
33879
- if (event.type === "chat.created" || event.type === "chat.updated") {
33880
- if (event.payload.chat.parentChatId === null) {
33881
- qc.setQueryData(chatsKey, (current) => {
33882
- if (!current) return { chats: [event.payload.chat] };
33883
- return { chats: upsertChat(current.chats, event.payload.chat) };
33884
- });
33885
- }
33886
- return;
33887
- }
33888
- if (event.type === "chat.deleted") {
33889
- qc.setQueryData(chatsKey, (current) => {
33890
- if (!current) return current;
33891
- return { chats: current.chats.filter((chat) => chat.id !== event.payload.chatId) };
33892
- });
33893
- qc.removeQueries({ queryKey: ["chat-history", workspaceId, event.payload.chatId] });
33894
- return;
33895
- }
33896
- if (event.type === "chat.turn.accepted") {
33897
- if (!event.payload.queued) {
33898
- qc.setQueryData(chatsKey, (current) => {
33899
- if (!current) return current;
33900
- return { chats: patchChat(current.chats, event.payload.chatId, { processing: true, updatedAt: event.ts }) };
33901
- });
33902
- }
33903
- return;
33904
- }
33905
- if (event.type === "chat.turn.started") {
33906
- qc.setQueryData(chatsKey, (current) => {
33907
- if (!current) return current;
33908
- return { chats: patchChat(current.chats, event.payload.chatId, { processing: true, updatedAt: event.ts }) };
33909
- });
33910
- return;
33911
- }
33912
- if (event.type === "chat.turn.completed" || event.type === "chat.interrupted") {
33913
- qc.setQueryData(chatsKey, (current) => {
33914
- if (!current) return current;
33915
- return { chats: patchChat(current.chats, event.payload.chatId, { processing: getTerminalChatProcessing(event), updatedAt: event.ts }) };
33916
- });
33917
- qc.invalidateQueries({ queryKey: ["chat-history", workspaceId, event.payload.chatId] });
33918
- return;
33919
- }
33920
- if (event.type === "chat.turn.delta") {
33921
- qc.setQueryData(
33922
- ["chat-history", workspaceId, event.payload.chatId],
33923
- (current) => {
33924
- const incomingSignature = getEventSignature(event.payload.event);
33925
- if (!current) {
33926
- return { thread_id: null, events: [event.payload.event] };
33927
- }
33928
- const existingIndex = current.events.findIndex((e) => getEventSignature(e) === incomingSignature);
33929
- if (existingIndex !== -1) {
33930
- const events = current.events.slice();
33931
- events[existingIndex] = event.payload.event;
33932
- return { ...current, events };
33933
- }
33934
- return { ...current, events: [...current.events, event.payload.event] };
33935
- }
33936
- );
33937
- return;
33938
- }
34296
+ if (applyWorkspaceChatEvent(qc, workspaceId, event)) return;
33939
34297
  if (event.type === "repo.status.changed") {
33940
34298
  qc.setQueryData(["workspace-status", workspaceId, false], (current) => {
33941
34299
  if (!current) return current;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-cli",
3
- "version": "0.2.455",
3
+ "version": "0.2.457",
4
4
  "description": "CLI for managing Replicas workspaces - SSH into cloud dev environments with automatic port forwarding",
5
5
  "main": "dist/index.mjs",
6
6
  "bin": {