@tryarcanist/cli 0.1.44 → 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 +336 -274
  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);
@@ -1149,6 +1209,7 @@ var ERROR_CODES = [
1149
1209
  "max_duration_exceeded",
1150
1210
  "spawn_timeout",
1151
1211
  "spawn_modal_error",
1212
+ "spawn_provider_error",
1152
1213
  "spawn_deadline_no_object",
1153
1214
  "spawn_deadline_no_bridge",
1154
1215
  "spawn_preconnect",
@@ -1181,8 +1242,9 @@ var ERROR_CODE_LABELS = {
1181
1242
  stale_prompt: "Prompt became inactive",
1182
1243
  max_duration_exceeded: "Prompt exceeded maximum duration",
1183
1244
  spawn_timeout: "Sandbox spawn timed out",
1184
- spawn_modal_error: "Sandbox spawn failed (Modal API error)",
1185
- spawn_deadline_no_object: "Sandbox spawn timed out before Modal responded",
1245
+ spawn_modal_error: "Sandbox spawn failed (legacy provider error)",
1246
+ spawn_provider_error: "Sandbox spawn failed (provider API error)",
1247
+ spawn_deadline_no_object: "Sandbox spawn timed out before provider responded",
1186
1248
  spawn_deadline_no_bridge: "Sandbox spawn timed out before bridge connected",
1187
1249
  spawn_preconnect: "Sandbox failed before connecting",
1188
1250
  sandbox_terminated: "Sandbox terminated",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryarcanist/cli",
3
- "version": "0.1.44",
3
+ "version": "0.1.46",
4
4
  "description": "CLI for Arcanist — create and manage coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {