@tryarcanist/cli 0.1.45 → 0.1.46

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.js +332 -272
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -717,6 +717,13 @@ function shouldAppendTextDelta(existingText, incomingText) {
717
717
  if (incomingText.length < DUPLICATE_TEXT_DELTA_MIN_CHARS) return true;
718
718
  return !existingText.endsWith(incomingText);
719
719
  }
720
+ function createStreamCoalescerState() {
721
+ return {
722
+ streamableIndexById: /* @__PURE__ */ new Map(),
723
+ segmentOrdinalByStreamId: /* @__PURE__ */ new Map(),
724
+ concatByStreamId: /* @__PURE__ */ new Map()
725
+ };
726
+ }
720
727
  function streamableKey(type, streamId) {
721
728
  return `${type}:${streamId}`;
722
729
  }
@@ -735,9 +742,51 @@ function resolveTextValue(data) {
735
742
  function resolvePromptId(data) {
736
743
  return typeof data?.promptId === "string" ? data.promptId : void 0;
737
744
  }
745
+ function coalesceStreamDelta(events, state, type, streamId, text, promptId) {
746
+ const key = streamableKey(type, streamId);
747
+ const existingText = state.concatByStreamId.get(key) ?? "";
748
+ const existingIdx = state.streamableIndexById.get(key);
749
+ if (existingIdx !== void 0 && existingIdx === events.length - 1) {
750
+ const existing = events[existingIdx];
751
+ if (existing.type === type && shouldAppendTextDelta(existingText, text)) {
752
+ existing.text += text;
753
+ state.concatByStreamId.set(key, existingText + text);
754
+ if (!existing.promptId && promptId) existing.promptId = promptId;
755
+ return true;
756
+ }
757
+ return false;
758
+ }
759
+ if (existingText && !shouldAppendTextDelta(existingText, text)) return false;
760
+ const previousOrdinal = state.segmentOrdinalByStreamId.get(key);
761
+ const segmentOrdinal = previousOrdinal === void 0 ? 0 : previousOrdinal + 1;
762
+ state.segmentOrdinalByStreamId.set(key, segmentOrdinal);
763
+ state.streamableIndexById.set(key, events.length);
764
+ state.concatByStreamId.set(key, existingText + text);
765
+ if (type === "text") {
766
+ events.push({
767
+ type: "text",
768
+ id: resolveSegmentId(streamId, segmentOrdinal),
769
+ ...segmentOrdinal > 0 ? { streamId } : {},
770
+ text,
771
+ ...promptId ? { promptId } : {}
772
+ });
773
+ } else {
774
+ events.push({
775
+ type: "reasoning",
776
+ id: resolveSegmentId(streamId, segmentOrdinal),
777
+ ...segmentOrdinal > 0 ? { streamId } : {},
778
+ text,
779
+ ...promptId ? { promptId } : {}
780
+ });
781
+ }
782
+ return true;
783
+ }
738
784
  function mergeToolCall(previous, incoming) {
739
785
  return {
786
+ ...previous,
740
787
  ...incoming,
788
+ tool: incoming.tool ?? previous.tool,
789
+ summary: incoming.summary ?? previous.summary,
741
790
  ...incoming.promptId === void 0 && previous.promptId !== void 0 ? { promptId: previous.promptId } : {},
742
791
  ...incoming.input === void 0 && previous.input !== void 0 ? { input: previous.input } : {},
743
792
  ...incoming.toolStatus === void 0 && previous.toolStatus !== void 0 ? { toolStatus: previous.toolStatus } : {},
@@ -751,291 +800,302 @@ function mergeToolCall(previous, incoming) {
751
800
  function normalizeToolStatus(value) {
752
801
  return value === "running" || value === "completed" || value === "error" ? value : void 0;
753
802
  }
754
- function flattenSessionEvents(raw) {
755
- const merged = [];
756
- const streamableIndexById = /* @__PURE__ */ new Map();
757
- const segmentOrdinalByStreamId = /* @__PURE__ */ new Map();
758
- const concatByStreamId = /* @__PURE__ */ new Map();
759
- const toolCallIndexById = /* @__PURE__ */ new Map();
760
- const questionIndexById = /* @__PURE__ */ new Map();
761
- for (const event of raw) {
762
- const normalized = normalizeRawSessionEvent(event);
763
- const { type } = normalized;
764
- const data = normalized.data;
765
- if (type === "sandbox_compaction_start") {
766
- merged.push({
767
- type: "compaction_start",
768
- id: `cs-${data?.timestamp ?? merged.length}`,
769
- ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {},
770
- ...typeof data?.contextTokens === "number" ? { contextTokens: data.contextTokens } : {}
771
- });
772
- continue;
773
- }
774
- if (type === "sandbox_compaction_complete") {
775
- merged.push({
776
- type: "compaction_complete",
777
- id: `cc-${data?.timestamp ?? merged.length}`,
778
- ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {},
779
- ...typeof data?.contextTokensBefore === "number" ? { contextTokensBefore: data.contextTokensBefore } : {},
780
- ...typeof data?.contextTokensAfter === "number" ? { contextTokensAfter: data.contextTokensAfter } : {}
781
- });
782
- continue;
783
- }
784
- if (type === "sandbox_context_fill_warning") {
785
- merged.push({
786
- type: "context_fill_warning",
787
- id: `cfw-${data?.timestamp ?? merged.length}`,
788
- ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {},
789
- fillPercent: typeof data?.fillPercent === "number" ? data.fillPercent : Number(data?.fillPercent ?? 0),
790
- ...typeof data?.contextTokens === "number" ? { contextTokens: data.contextTokens } : {},
791
- ...typeof data?.contextWindow === "number" ? { contextWindow: data.contextWindow } : {}
792
- });
793
- continue;
794
- }
795
- if (type === "sandbox_tool_truncated") {
796
- merged.push({
797
- type: "tool_truncated",
798
- id: typeof data?.callId === "string" ? data.callId : String(merged.length),
799
- tool: typeof data?.tool === "string" ? data.tool : "",
800
- ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {}
801
- });
802
- continue;
803
- }
804
- if (type === "retry_status") {
805
- merged.push({
806
- type: "retry_status",
807
- id: `rs-${data?.timestamp ?? merged.length}`,
808
- ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {},
809
- attempt: typeof data?.attempt === "number" ? data.attempt : Number(data?.attempt ?? 0),
810
- message: typeof data?.message === "string" ? data.message : "Retrying...",
811
- ...typeof data?.nextRetryAt === "string" ? { nextRetryAt: data.nextRetryAt } : {},
812
- ...typeof data?.provider === "string" ? { provider: data.provider } : {},
813
- ...typeof data?.errorCode === "string" ? { errorCode: data.errorCode } : {},
814
- ...typeof data?.scope === "string" ? { scope: data.scope } : {},
815
- ...typeof data?.maxAttempts === "number" ? { maxAttempts: data.maxAttempts } : {},
816
- ...typeof data?.reason === "string" ? { reason: data.reason } : {},
817
- ...typeof data?.retryAfterMs === "number" ? { retryAfterMs: data.retryAfterMs } : {}
818
- });
819
- continue;
820
- }
821
- if (type === "branch_changed") {
822
- merged.push({
823
- type: "branch_changed",
824
- id: `bc-${data?.timestamp ?? merged.length}`,
825
- branch: typeof data?.branch === "string" ? data.branch : "",
826
- ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {}
827
- });
828
- continue;
829
- }
830
- if (type === "agent_timeline") {
831
- merged.push({
832
- type: "agent_timeline",
833
- id: `atl-${merged.length}-${typeof data?.eventType === "string" ? data.eventType : "unknown"}`,
834
- eventType: typeof data?.eventType === "string" ? data.eventType : "unknown",
835
- source: typeof data?.source === "string" ? data.source : "observed",
836
- observer: typeof data?.observer === "string" ? data.observer : "unknown",
837
- summary: typeof data?.summary === "string" ? data.summary : "Observed agent timeline event.",
838
- ...typeof data?.status === "string" ? { status: data.status } : {},
839
- ...isRecord(data?.metadata) ? { metadata: data.metadata } : {},
840
- ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {}
841
- });
842
- continue;
843
- }
844
- if (type === "session_error") {
845
- merged.push({
846
- type: "session_error",
847
- id: resolveEventId(data, "err", merged.length),
848
- error: typeof data?.error === "string" ? data.error : "Unknown error",
849
- ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {},
850
- ...typeof data?.code === "string" ? { code: data.code } : {}
851
- });
852
- continue;
853
- }
854
- if (type === "session_resumed_cold") {
855
- merged.push({
856
- type: "session_resumed_cold",
857
- id: resolveEventId(data, "rescold", merged.length),
858
- ...typeof data?.reason === "string" ? { reason: data.reason } : {},
859
- ...typeof data?.lostSnapshotImageId === "string" ? { lostSnapshotImageId: data.lostSnapshotImageId } : data?.lostSnapshotImageId === null ? { lostSnapshotImageId: null } : {},
860
- ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {}
861
- });
862
- continue;
863
- }
864
- if (type === "raw_opencode") {
865
- const partType = typeof data?.partType === "string" ? data.partType : void 0;
866
- const eventType = typeof data?.eventType === "string" ? data.eventType : void 0;
867
- if (partType === "text") continue;
868
- if (eventType && RAW_OPENCODE_NOISE.has(eventType)) continue;
869
- merged.push({
870
- type: "raw_opencode",
871
- id: resolveEventId(data, "raw", merged.length),
872
- ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {},
873
- ...partType ? { partType } : {},
874
- ...eventType ? { eventType } : {},
875
- data: data ?? {}
876
- });
877
- continue;
878
- }
879
- if (type === "reasoning") {
880
- const streamId = resolveEventId(data, "reasoning", merged.length);
881
- const key = streamableKey("reasoning", streamId);
882
- const text = resolveTextValue(data);
883
- const promptId = resolvePromptId(data);
884
- const existingText = concatByStreamId.get(key) ?? "";
885
- const existingIdx = streamableIndexById.get(key);
886
- if (existingIdx !== void 0 && existingIdx === merged.length - 1) {
887
- const existing = merged[existingIdx];
888
- if (existing.type === "reasoning" && shouldAppendTextDelta(existingText, text)) {
889
- existing.text += text;
890
- concatByStreamId.set(key, existingText + text);
891
- if (!existing.promptId && promptId) existing.promptId = promptId;
892
- }
893
- } else {
894
- if (existingText && !shouldAppendTextDelta(existingText, text)) continue;
895
- const previousOrdinal = segmentOrdinalByStreamId.get(key);
896
- const segmentOrdinal = previousOrdinal === void 0 ? 0 : previousOrdinal + 1;
897
- segmentOrdinalByStreamId.set(key, segmentOrdinal);
898
- streamableIndexById.set(key, merged.length);
899
- concatByStreamId.set(key, existingText + text);
900
- merged.push({
901
- type: "reasoning",
902
- id: resolveSegmentId(streamId, segmentOrdinal),
903
- ...segmentOrdinal > 0 ? { streamId } : {},
904
- text,
905
- ...promptId ? { promptId } : {}
906
- });
907
- }
908
- continue;
909
- }
910
- if (type === "patch") {
911
- const files = Array.isArray(data?.files) ? data.files.filter((item) => typeof item === "string") : [];
912
- if (files.length > 0) {
913
- merged.push({ type: "patch", id: `patch-${merged.length}`, files });
914
- if (resolvePromptId(data)) {
915
- const last = merged[merged.length - 1];
916
- if (last?.type === "patch") last.promptId = resolvePromptId(data);
917
- }
918
- }
919
- continue;
920
- }
921
- if (type === "todo_update") {
922
- const todos = Array.isArray(data?.todos) ? data.todos : [];
923
- if (todos.length > 0) {
924
- for (let index = merged.length - 1; index >= 0; index--) {
925
- const existing = merged[index];
926
- if (existing.type === "tool_call" && existing.tool.toLowerCase() === "todowrite") {
927
- merged[index] = { ...existing, input: { ...existing.input, todos } };
928
- break;
929
- }
930
- }
931
- }
932
- continue;
933
- }
934
- if (type === "answer") {
935
- const questionId = typeof data?.id === "string" ? data.id : void 0;
936
- if (!questionId) continue;
937
- const existingIdx = questionIndexById.get(questionId);
938
- if (existingIdx !== void 0) {
939
- const existing = merged[existingIdx];
940
- if (existing?.type === "question") {
941
- merged[existingIdx] = {
942
- ...existing,
943
- answer: data?.answer == null ? null : String(data.answer)
944
- };
945
- }
946
- }
947
- continue;
948
- }
949
- if (type !== "text" && type !== "tool_call" && type !== "tool_update" && type !== "question") {
950
- continue;
951
- }
952
- if (type === "text") {
953
- const streamId = resolveEventId(data, "text", merged.length);
954
- const key = streamableKey("text", streamId);
955
- const text = resolveTextValue(data);
956
- const promptId = resolvePromptId(data);
957
- const existingText = concatByStreamId.get(key) ?? "";
958
- const existingIdx = streamableIndexById.get(key);
959
- if (existingIdx !== void 0 && existingIdx === merged.length - 1) {
960
- const existing = merged[existingIdx];
961
- if (existing.type === "text" && shouldAppendTextDelta(existingText, text)) {
962
- existing.text += text;
963
- concatByStreamId.set(key, existingText + text);
964
- if (!existing.promptId && promptId) existing.promptId = promptId;
965
- }
966
- } else {
967
- if (existingText && !shouldAppendTextDelta(existingText, text)) continue;
968
- const previousOrdinal = segmentOrdinalByStreamId.get(key);
969
- const segmentOrdinal = previousOrdinal === void 0 ? 0 : previousOrdinal + 1;
970
- segmentOrdinalByStreamId.set(key, segmentOrdinal);
971
- streamableIndexById.set(key, merged.length);
972
- concatByStreamId.set(key, existingText + text);
973
- merged.push({
974
- type: "text",
975
- id: resolveSegmentId(streamId, segmentOrdinal),
976
- ...segmentOrdinal > 0 ? { streamId } : {},
977
- text,
978
- ...promptId ? { promptId } : {}
979
- });
980
- }
981
- continue;
982
- }
983
- if (type === "tool_call") {
984
- const id = resolveEventId(data, "tool", merged.length);
803
+ function applyToolCallUpdate(previous, data) {
804
+ const toolStatus = normalizeToolStatus(data?.status);
805
+ return {
806
+ ...previous,
807
+ ...toolStatus ? { toolStatus } : {},
808
+ ...typeof data?.outputEstimatedTokens === "number" ? { outputEstimatedTokens: data.outputEstimatedTokens } : {},
809
+ ...typeof data?.outputChars === "number" ? { outputChars: data.outputChars } : {},
810
+ ...typeof data?.truncated === "boolean" ? { truncated: data.truncated } : {}
811
+ };
812
+ }
813
+ function createFlattenState() {
814
+ return {
815
+ merged: [],
816
+ streams: createStreamCoalescerState(),
817
+ toolCallIndexById: /* @__PURE__ */ new Map(),
818
+ questionIndexById: /* @__PURE__ */ new Map()
819
+ };
820
+ }
821
+ function pushEvent(state, event) {
822
+ if (event) state.merged.push(event);
823
+ }
824
+ function projectCompactionStart(data, index) {
825
+ return {
826
+ type: "compaction_start",
827
+ id: `cs-${data?.timestamp ?? index}`,
828
+ ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {},
829
+ ...typeof data?.contextTokens === "number" ? { contextTokens: data.contextTokens } : {}
830
+ };
831
+ }
832
+ function projectCompactionComplete(data, index) {
833
+ return {
834
+ type: "compaction_complete",
835
+ id: `cc-${data?.timestamp ?? index}`,
836
+ ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {},
837
+ ...typeof data?.contextTokensBefore === "number" ? { contextTokensBefore: data.contextTokensBefore } : {},
838
+ ...typeof data?.contextTokensAfter === "number" ? { contextTokensAfter: data.contextTokensAfter } : {}
839
+ };
840
+ }
841
+ function projectContextFillWarning(data, index) {
842
+ return {
843
+ type: "context_fill_warning",
844
+ id: `cfw-${data?.timestamp ?? index}`,
845
+ ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {},
846
+ fillPercent: typeof data?.fillPercent === "number" ? data.fillPercent : Number(data?.fillPercent ?? 0),
847
+ ...typeof data?.contextTokens === "number" ? { contextTokens: data.contextTokens } : {},
848
+ ...typeof data?.contextWindow === "number" ? { contextWindow: data.contextWindow } : {}
849
+ };
850
+ }
851
+ function projectToolTruncated(data, index) {
852
+ return {
853
+ type: "tool_truncated",
854
+ id: typeof data?.callId === "string" ? data.callId : String(index),
855
+ tool: typeof data?.tool === "string" ? data.tool : "",
856
+ ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {}
857
+ };
858
+ }
859
+ function projectRetryStatus(data, index) {
860
+ return {
861
+ type: "retry_status",
862
+ id: `rs-${data?.timestamp ?? index}`,
863
+ ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {},
864
+ attempt: typeof data?.attempt === "number" ? data.attempt : Number(data?.attempt ?? 0),
865
+ message: typeof data?.message === "string" ? data.message : "Retrying...",
866
+ ...typeof data?.nextRetryAt === "string" ? { nextRetryAt: data.nextRetryAt } : {},
867
+ ...typeof data?.provider === "string" ? { provider: data.provider } : {},
868
+ ...typeof data?.errorCode === "string" ? { errorCode: data.errorCode } : {},
869
+ ...typeof data?.scope === "string" ? { scope: data.scope } : {},
870
+ ...typeof data?.maxAttempts === "number" ? { maxAttempts: data.maxAttempts } : {},
871
+ ...typeof data?.reason === "string" ? { reason: data.reason } : {},
872
+ ...typeof data?.retryAfterMs === "number" ? { retryAfterMs: data.retryAfterMs } : {}
873
+ };
874
+ }
875
+ function projectBranchChanged(data, index) {
876
+ return {
877
+ type: "branch_changed",
878
+ id: `bc-${data?.timestamp ?? index}`,
879
+ branch: typeof data?.branch === "string" ? data.branch : "",
880
+ ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {}
881
+ };
882
+ }
883
+ function projectAgentTimeline(data, index) {
884
+ return {
885
+ type: "agent_timeline",
886
+ id: `atl-${index}-${typeof data?.eventType === "string" ? data.eventType : "unknown"}`,
887
+ eventType: typeof data?.eventType === "string" ? data.eventType : "unknown",
888
+ source: typeof data?.source === "string" ? data.source : "observed",
889
+ observer: typeof data?.observer === "string" ? data.observer : "unknown",
890
+ summary: typeof data?.summary === "string" ? data.summary : "Observed agent timeline event.",
891
+ ...typeof data?.status === "string" ? { status: data.status } : {},
892
+ ...isRecord(data?.metadata) ? { metadata: data.metadata } : {},
893
+ ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {}
894
+ };
895
+ }
896
+ function projectSessionError(data, index) {
897
+ return {
898
+ type: "session_error",
899
+ id: resolveEventId(data, "err", index),
900
+ error: typeof data?.error === "string" ? data.error : "Unknown error",
901
+ ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {},
902
+ ...typeof data?.code === "string" ? { code: data.code } : {}
903
+ };
904
+ }
905
+ function projectSessionResumedCold(data, index) {
906
+ return {
907
+ type: "session_resumed_cold",
908
+ id: resolveEventId(data, "rescold", index),
909
+ ...typeof data?.reason === "string" ? { reason: data.reason } : {},
910
+ ...typeof data?.lostSnapshotImageId === "string" ? { lostSnapshotImageId: data.lostSnapshotImageId } : data?.lostSnapshotImageId === null ? { lostSnapshotImageId: null } : {},
911
+ ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {}
912
+ };
913
+ }
914
+ function projectRawOpencode(data, index) {
915
+ const partType = typeof data?.partType === "string" ? data.partType : void 0;
916
+ const eventType = typeof data?.eventType === "string" ? data.eventType : void 0;
917
+ if (partType === "text") return null;
918
+ if (eventType && RAW_OPENCODE_NOISE.has(eventType)) return null;
919
+ return {
920
+ type: "raw_opencode",
921
+ id: resolveEventId(data, "raw", index),
922
+ ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {},
923
+ ...partType ? { partType } : {},
924
+ ...eventType ? { eventType } : {},
925
+ data: data ?? {}
926
+ };
927
+ }
928
+ function projectPatch(data, index) {
929
+ const files = Array.isArray(data?.files) ? data.files.filter((item) => typeof item === "string") : [];
930
+ if (files.length === 0) return null;
931
+ return {
932
+ type: "patch",
933
+ id: `patch-${index}`,
934
+ files,
935
+ ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {}
936
+ };
937
+ }
938
+ function projectStream(type, data, state) {
939
+ coalesceStreamDelta(
940
+ state.merged,
941
+ state.streams,
942
+ type,
943
+ resolveEventId(data, type, state.merged.length),
944
+ resolveTextValue(data),
945
+ resolvePromptId(data)
946
+ );
947
+ }
948
+ function projectToolCall(data, state) {
949
+ const id = resolveEventId(data, "tool", state.merged.length);
950
+ const existingIdx = state.toolCallIndexById.get(id);
951
+ if (existingIdx !== void 0) {
952
+ const previous = state.merged[existingIdx];
953
+ if (previous.type === "tool_call") {
954
+ const toolStatus = normalizeToolStatus(data?.toolStatus);
955
+ const input = isRecord(data?.input) ? data.input : void 0;
985
956
  const nextEntry = {
986
957
  type: "tool_call",
987
958
  id,
988
- tool: typeof data?.tool === "string" ? data.tool : typeof data?.toolName === "string" ? data.toolName : "unknown",
989
- summary: typeof data?.summary === "string" ? data.summary : "",
959
+ ...typeof data?.tool === "string" ? { tool: data.tool } : typeof data?.toolName === "string" ? { tool: data.toolName } : {},
960
+ ...typeof data?.summary === "string" && data.summary.length > 0 ? { summary: data.summary } : {},
990
961
  ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {},
991
- ...isRecord(data?.input) ? { input: data.input } : {},
992
- ...normalizeToolStatus(data?.toolStatus) ? { toolStatus: normalizeToolStatus(data?.toolStatus) } : {},
962
+ ...input && Object.keys(input).length > 0 ? { input } : {},
963
+ ...toolStatus ? { toolStatus } : {},
993
964
  ...typeof data?.truncated === "boolean" ? { truncated: data.truncated } : {},
994
965
  ...typeof data?.inputEstimatedTokens === "number" ? { inputEstimatedTokens: data.inputEstimatedTokens } : {},
995
966
  ...typeof data?.outputEstimatedTokens === "number" ? { outputEstimatedTokens: data.outputEstimatedTokens } : {},
996
967
  ...typeof data?.outputChars === "number" ? { outputChars: data.outputChars } : {}
997
968
  };
998
- const existingIdx = toolCallIndexById.get(id);
999
- if (existingIdx !== void 0) {
1000
- const previous = merged[existingIdx];
1001
- if (previous.type === "tool_call") {
1002
- merged[existingIdx] = mergeToolCall(previous, nextEntry);
1003
- }
1004
- } else {
1005
- toolCallIndexById.set(id, merged.length);
1006
- merged.push(nextEntry);
1007
- }
1008
- continue;
969
+ state.merged[existingIdx] = mergeToolCall(previous, nextEntry);
1009
970
  }
1010
- if (type === "tool_update") {
1011
- const id = typeof data?.id === "string" ? data.id : "";
1012
- const existingIdx = toolCallIndexById.get(id);
1013
- if (existingIdx !== void 0) {
1014
- const previous = merged[existingIdx];
1015
- if (previous.type === "tool_call") {
1016
- merged[existingIdx] = {
1017
- ...previous,
1018
- ...normalizeToolStatus(data?.status) ? { toolStatus: normalizeToolStatus(data?.status) } : {},
1019
- ...typeof data?.outputEstimatedTokens === "number" ? { outputEstimatedTokens: data.outputEstimatedTokens } : {},
1020
- ...typeof data?.outputChars === "number" ? { outputChars: data.outputChars } : {},
1021
- ...typeof data?.truncated === "boolean" ? { truncated: data.truncated } : {}
1022
- };
1023
- }
1024
- }
1025
- continue;
1026
- }
1027
- const question = {
1028
- type: "question",
1029
- id: resolveEventId(data, "question", merged.length),
1030
- question: typeof data?.question === "string" ? data.question : "",
1031
- answer: data?.answer == null ? null : String(data.answer),
971
+ } else {
972
+ const toolStatus = normalizeToolStatus(data?.toolStatus);
973
+ const nextEntry = {
974
+ type: "tool_call",
975
+ id,
976
+ tool: typeof data?.tool === "string" ? data.tool : typeof data?.toolName === "string" ? data.toolName : "unknown",
977
+ summary: typeof data?.summary === "string" ? data.summary : "",
1032
978
  ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {},
1033
- ...Array.isArray(data?.options) ? { options: data.options } : {}
979
+ ...isRecord(data?.input) ? { input: data.input } : {},
980
+ ...toolStatus ? { toolStatus } : {},
981
+ ...typeof data?.truncated === "boolean" ? { truncated: data.truncated } : {},
982
+ ...typeof data?.inputEstimatedTokens === "number" ? { inputEstimatedTokens: data.inputEstimatedTokens } : {},
983
+ ...typeof data?.outputEstimatedTokens === "number" ? { outputEstimatedTokens: data.outputEstimatedTokens } : {},
984
+ ...typeof data?.outputChars === "number" ? { outputChars: data.outputChars } : {}
1034
985
  };
1035
- questionIndexById.set(question.id, merged.length);
1036
- merged.push(question);
986
+ state.toolCallIndexById.set(id, state.merged.length);
987
+ state.merged.push(nextEntry);
988
+ }
989
+ }
990
+ function projectToolUpdate(data, state) {
991
+ const id = typeof data?.id === "string" ? data.id : "";
992
+ const existingIdx = state.toolCallIndexById.get(id);
993
+ if (existingIdx === void 0) return;
994
+ const previous = state.merged[existingIdx];
995
+ if (previous.type === "tool_call") {
996
+ state.merged[existingIdx] = applyToolCallUpdate(previous, data);
997
+ }
998
+ }
999
+ function projectQuestion(data, state) {
1000
+ const question = {
1001
+ type: "question",
1002
+ id: resolveEventId(data, "question", state.merged.length),
1003
+ question: typeof data?.question === "string" ? data.question : "",
1004
+ answer: data?.answer == null ? null : String(data.answer),
1005
+ ...resolvePromptId(data) ? { promptId: resolvePromptId(data) } : {},
1006
+ ...Array.isArray(data?.options) ? { options: data.options } : {}
1007
+ };
1008
+ state.questionIndexById.set(question.id, state.merged.length);
1009
+ state.merged.push(question);
1010
+ }
1011
+ function applyAnswer(data, state) {
1012
+ const questionId = typeof data?.id === "string" ? data.id : void 0;
1013
+ if (!questionId) return;
1014
+ const existingIdx = state.questionIndexById.get(questionId);
1015
+ if (existingIdx === void 0) return;
1016
+ const existing = state.merged[existingIdx];
1017
+ if (existing?.type === "question") {
1018
+ state.merged[existingIdx] = {
1019
+ ...existing,
1020
+ answer: data?.answer == null ? null : String(data.answer)
1021
+ };
1022
+ }
1023
+ }
1024
+ function applyTodoUpdate(data, state) {
1025
+ const todos = Array.isArray(data?.todos) ? data.todos : [];
1026
+ if (todos.length === 0) return;
1027
+ for (let index = state.merged.length - 1; index >= 0; index--) {
1028
+ const existing = state.merged[index];
1029
+ if (existing.type === "tool_call" && existing.tool.toLowerCase() === "todowrite") {
1030
+ state.merged[index] = { ...existing, input: { ...existing.input, todos } };
1031
+ break;
1032
+ }
1033
+ }
1034
+ }
1035
+ function flattenSessionEvents(raw) {
1036
+ const state = createFlattenState();
1037
+ for (const event of raw) {
1038
+ const normalized = normalizeRawSessionEvent(event);
1039
+ const { type } = normalized;
1040
+ const data = normalized.data;
1041
+ switch (type) {
1042
+ case "sandbox_compaction_start":
1043
+ pushEvent(state, projectCompactionStart(data, state.merged.length));
1044
+ break;
1045
+ case "sandbox_compaction_complete":
1046
+ pushEvent(state, projectCompactionComplete(data, state.merged.length));
1047
+ break;
1048
+ case "sandbox_context_fill_warning":
1049
+ pushEvent(state, projectContextFillWarning(data, state.merged.length));
1050
+ break;
1051
+ case "sandbox_tool_truncated":
1052
+ pushEvent(state, projectToolTruncated(data, state.merged.length));
1053
+ break;
1054
+ case "retry_status":
1055
+ pushEvent(state, projectRetryStatus(data, state.merged.length));
1056
+ break;
1057
+ case "branch_changed":
1058
+ pushEvent(state, projectBranchChanged(data, state.merged.length));
1059
+ break;
1060
+ case "agent_timeline":
1061
+ pushEvent(state, projectAgentTimeline(data, state.merged.length));
1062
+ break;
1063
+ case "session_error":
1064
+ pushEvent(state, projectSessionError(data, state.merged.length));
1065
+ break;
1066
+ case "session_resumed_cold":
1067
+ pushEvent(state, projectSessionResumedCold(data, state.merged.length));
1068
+ break;
1069
+ case "raw_opencode":
1070
+ pushEvent(state, projectRawOpencode(data, state.merged.length));
1071
+ break;
1072
+ case "reasoning":
1073
+ projectStream("reasoning", data, state);
1074
+ break;
1075
+ case "text":
1076
+ projectStream("text", data, state);
1077
+ break;
1078
+ case "patch":
1079
+ pushEvent(state, projectPatch(data, state.merged.length));
1080
+ break;
1081
+ case "todo_update":
1082
+ applyTodoUpdate(data, state);
1083
+ break;
1084
+ case "answer":
1085
+ applyAnswer(data, state);
1086
+ break;
1087
+ case "tool_call":
1088
+ projectToolCall(data, state);
1089
+ break;
1090
+ case "tool_update":
1091
+ projectToolUpdate(data, state);
1092
+ break;
1093
+ case "question":
1094
+ projectQuestion(data, state);
1095
+ break;
1096
+ }
1037
1097
  }
1038
- return merged;
1098
+ return state.merged;
1039
1099
  }
1040
1100
  function partitionEventsByPrompt(allEvents, promptIds) {
1041
1101
  const promptSet = new Set(promptIds);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryarcanist/cli",
3
- "version": "0.1.45",
3
+ "version": "0.1.46",
4
4
  "description": "CLI for Arcanist — create and manage coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {