viveworker 0.1.9 → 0.1.11
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.
- package/README.md +13 -13
- package/ntfy/server.yml.example +2 -2
- package/package.json +4 -3
- package/scripts/viveworker-bridge.mjs +470 -118
- package/scripts/viveworker.mjs +18 -4
- package/viveworker.env.example +3 -3
- package/web/i18n.js +107 -89
|
@@ -85,8 +85,16 @@ const state = await loadState(config.stateFile);
|
|
|
85
85
|
const migratedPairedDevicesStateChanged = migratePairedDevicesState({ config, state });
|
|
86
86
|
const restoredPendingPlanStateChanged = restorePendingPlanRequests({ config, runtime, state });
|
|
87
87
|
const restoredPendingUserInputStateChanged = restorePendingUserInputRequests({ config, runtime, state });
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
const initialHistoryItems = normalizeHistoryItems(state.recentHistoryItems ?? [], config.maxHistoryItems);
|
|
89
|
+
const initialTimelineEntries = normalizeTimelineEntries(state.recentTimelineEntries ?? [], config.maxTimelineEntries);
|
|
90
|
+
const normalizedHistoryStateChanged =
|
|
91
|
+
JSON.stringify(initialHistoryItems) !== JSON.stringify(Array.isArray(state.recentHistoryItems) ? state.recentHistoryItems : []);
|
|
92
|
+
const normalizedTimelineStateChanged =
|
|
93
|
+
JSON.stringify(initialTimelineEntries) !== JSON.stringify(Array.isArray(state.recentTimelineEntries) ? state.recentTimelineEntries : []);
|
|
94
|
+
runtime.recentHistoryItems = initialHistoryItems;
|
|
95
|
+
runtime.recentTimelineEntries = initialTimelineEntries;
|
|
96
|
+
state.recentHistoryItems = initialHistoryItems;
|
|
97
|
+
state.recentTimelineEntries = initialTimelineEntries;
|
|
90
98
|
const migratedRecentCodeEventsStateChanged = migrateRecentCodeEventsState({ config, runtime, state });
|
|
91
99
|
const restoredTimelineImagePathsStateChanged = await backfillPersistedTimelineImagePaths({ config, runtime, state });
|
|
92
100
|
runtime.historyFileState.offset = Number(state.historyFileOffset) || 0;
|
|
@@ -320,6 +328,17 @@ function fileEventDetailCopy(locale, fileEventType) {
|
|
|
320
328
|
}
|
|
321
329
|
}
|
|
322
330
|
|
|
331
|
+
function diffThreadDetailCopy(locale, sourceMode, count) {
|
|
332
|
+
switch (cleanText(sourceMode || "")) {
|
|
333
|
+
case "git_current":
|
|
334
|
+
return t(locale, "detail.diffThread.copy.current", { count });
|
|
335
|
+
case "non_git_latest":
|
|
336
|
+
return t(locale, "detail.diffThread.copy.latest", { count });
|
|
337
|
+
default:
|
|
338
|
+
return t(locale, "detail.diffThread.copy", { count });
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
323
342
|
function inferTimelineOutcome(kind, summary = "", messageText = "") {
|
|
324
343
|
const normalizedKind = cleanText(kind || "");
|
|
325
344
|
if (!normalizedKind) {
|
|
@@ -1756,6 +1775,205 @@ function resolveCurrentChangeOwner(change, candidates) {
|
|
|
1756
1775
|
return null;
|
|
1757
1776
|
}
|
|
1758
1777
|
|
|
1778
|
+
function normalizeCodeFallbackFileRef({ fileRef, cwd = "" }) {
|
|
1779
|
+
const normalizedFileRef = cleanTimelineFileRef(fileRef);
|
|
1780
|
+
if (!normalizedFileRef) {
|
|
1781
|
+
return "";
|
|
1782
|
+
}
|
|
1783
|
+
if (path.isAbsolute(normalizedFileRef)) {
|
|
1784
|
+
return resolvePath(normalizedFileRef);
|
|
1785
|
+
}
|
|
1786
|
+
const normalizedCwd = resolvePath(cleanText(cwd || ""));
|
|
1787
|
+
if (!normalizedCwd) {
|
|
1788
|
+
return normalizedFileRef;
|
|
1789
|
+
}
|
|
1790
|
+
return resolvePath(path.resolve(normalizedCwd, normalizedFileRef));
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
function buildNonGitCodeChangeEntries(item) {
|
|
1794
|
+
const changeType = normalizeTimelineFileEventType(item?.fileEventType || "");
|
|
1795
|
+
if (!["write", "create", "delete", "rename"].includes(changeType)) {
|
|
1796
|
+
return [];
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
const normalizedCwd = cleanText(item?.cwd || "");
|
|
1800
|
+
const normalizedFileRefs = normalizeTimelineFileRefs(item?.fileRefs ?? []).map((fileRef) =>
|
|
1801
|
+
normalizeCodeFallbackFileRef({ fileRef, cwd: normalizedCwd })
|
|
1802
|
+
);
|
|
1803
|
+
const normalizedPreviousFileRefs = normalizeTimelineFileRefs(item?.previousFileRefs ?? []).map((fileRef) =>
|
|
1804
|
+
normalizeCodeFallbackFileRef({ fileRef, cwd: normalizedCwd })
|
|
1805
|
+
);
|
|
1806
|
+
const sections = splitUnifiedDiffTextByFile(item?.diffText ?? "");
|
|
1807
|
+
const rawChanges = [];
|
|
1808
|
+
|
|
1809
|
+
if (changeType === "rename") {
|
|
1810
|
+
const pairCount = Math.max(normalizedFileRefs.length, normalizedPreviousFileRefs.length);
|
|
1811
|
+
for (let index = 0; index < pairCount; index += 1) {
|
|
1812
|
+
const newFileRef = cleanTimelineFileRef(normalizedFileRefs[index] || "");
|
|
1813
|
+
const oldFileRef = cleanTimelineFileRef(normalizedPreviousFileRefs[index] || "");
|
|
1814
|
+
if (!newFileRef && !oldFileRef) {
|
|
1815
|
+
continue;
|
|
1816
|
+
}
|
|
1817
|
+
rawChanges.push({
|
|
1818
|
+
changeType,
|
|
1819
|
+
fileRef: newFileRef || oldFileRef,
|
|
1820
|
+
oldFileRef,
|
|
1821
|
+
newFileRef: newFileRef || oldFileRef,
|
|
1822
|
+
});
|
|
1823
|
+
}
|
|
1824
|
+
} else {
|
|
1825
|
+
const targetRefs =
|
|
1826
|
+
normalizedFileRefs.length > 0
|
|
1827
|
+
? normalizedFileRefs
|
|
1828
|
+
: changeType === "delete"
|
|
1829
|
+
? normalizedPreviousFileRefs
|
|
1830
|
+
: [];
|
|
1831
|
+
for (const fileRef of targetRefs) {
|
|
1832
|
+
const normalizedFileRef = cleanTimelineFileRef(fileRef || "");
|
|
1833
|
+
if (!normalizedFileRef) {
|
|
1834
|
+
continue;
|
|
1835
|
+
}
|
|
1836
|
+
rawChanges.push({
|
|
1837
|
+
changeType,
|
|
1838
|
+
fileRef: normalizedFileRef,
|
|
1839
|
+
oldFileRef: changeType === "delete" ? normalizedFileRef : "",
|
|
1840
|
+
newFileRef: changeType === "delete" ? "" : normalizedFileRef,
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
const allowWholeItemDiff = sections.length === 0 && rawChanges.length <= 1;
|
|
1846
|
+
const baseDiffSource = normalizeTimelineDiffSource(item?.diffSource ?? "");
|
|
1847
|
+
const baseAddedLines = Math.max(0, Number(item?.diffAddedLines) || 0);
|
|
1848
|
+
const baseRemovedLines = Math.max(0, Number(item?.diffRemovedLines) || 0);
|
|
1849
|
+
const threadId = cleanText(item?.threadId || "");
|
|
1850
|
+
const threadLabel = cleanText(item?.threadLabel || "");
|
|
1851
|
+
const createdAtMs = Number(item?.createdAtMs) || 0;
|
|
1852
|
+
|
|
1853
|
+
return rawChanges.map((change) => {
|
|
1854
|
+
const section = findMatchingCurrentDiffSection(sections, change);
|
|
1855
|
+
const diffText = normalizeTimelineDiffText(section?.diffText || (allowWholeItemDiff ? item?.diffText ?? "" : ""));
|
|
1856
|
+
const counts = diffText
|
|
1857
|
+
? diffLineCounts(diffText)
|
|
1858
|
+
: {
|
|
1859
|
+
addedLines: allowWholeItemDiff ? baseAddedLines : 0,
|
|
1860
|
+
removedLines: allowWholeItemDiff ? baseRemovedLines : 0,
|
|
1861
|
+
};
|
|
1862
|
+
return {
|
|
1863
|
+
threadId,
|
|
1864
|
+
threadLabel,
|
|
1865
|
+
createdAtMs,
|
|
1866
|
+
changeType,
|
|
1867
|
+
fileRef: cleanTimelineFileRef(change.fileRef || change.newFileRef || change.oldFileRef || ""),
|
|
1868
|
+
oldFileRef: cleanTimelineFileRef(change.oldFileRef || ""),
|
|
1869
|
+
newFileRef: cleanTimelineFileRef(change.newFileRef || change.fileRef || ""),
|
|
1870
|
+
diffText,
|
|
1871
|
+
diffAvailable: item?.diffAvailable === true || Boolean(diffText),
|
|
1872
|
+
diffSource: baseDiffSource,
|
|
1873
|
+
diffAddedLines: counts.addedLines,
|
|
1874
|
+
diffRemovedLines: counts.removedLines,
|
|
1875
|
+
};
|
|
1876
|
+
});
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
function nonGitCodeChangeIdentity(change) {
|
|
1880
|
+
const anchorFileRef = cleanTimelineFileRef(change?.newFileRef || change?.fileRef || change?.oldFileRef || "");
|
|
1881
|
+
return anchorFileRef ? `file:${anchorFileRef}` : "";
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
function mergeDiffThreadSourceMode(currentMode, nextMode) {
|
|
1885
|
+
const normalizedCurrentMode = cleanText(currentMode || "");
|
|
1886
|
+
const normalizedNextMode = cleanText(nextMode || "");
|
|
1887
|
+
if (!normalizedCurrentMode) {
|
|
1888
|
+
return normalizedNextMode;
|
|
1889
|
+
}
|
|
1890
|
+
if (!normalizedNextMode || normalizedCurrentMode === normalizedNextMode) {
|
|
1891
|
+
return normalizedCurrentMode;
|
|
1892
|
+
}
|
|
1893
|
+
return "mixed";
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
function ensureDiffThreadGroup(groupsByThread, { threadId = "", threadLabel = "", fallbackKey = "", sourceMode = "" }) {
|
|
1897
|
+
const normalizedThreadId = cleanText(threadId || "");
|
|
1898
|
+
const normalizedThreadLabel = cleanText(threadLabel || "");
|
|
1899
|
+
const normalizedFallbackKey = cleanText(fallbackKey || "");
|
|
1900
|
+
const threadKey = normalizedThreadId || `unknown:${normalizedThreadLabel || normalizedFallbackKey || "thread"}`;
|
|
1901
|
+
let threadGroup = groupsByThread.get(threadKey);
|
|
1902
|
+
if (!threadGroup) {
|
|
1903
|
+
threadGroup = {
|
|
1904
|
+
kind: "diff_thread",
|
|
1905
|
+
token: diffThreadToken(normalizedThreadId, normalizedThreadLabel),
|
|
1906
|
+
threadId: normalizedThreadId,
|
|
1907
|
+
threadLabel: normalizedThreadLabel,
|
|
1908
|
+
sourceMode: cleanText(sourceMode || ""),
|
|
1909
|
+
filesByRef: new Map(),
|
|
1910
|
+
};
|
|
1911
|
+
groupsByThread.set(threadKey, threadGroup);
|
|
1912
|
+
return threadGroup;
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
if (!threadGroup.threadLabel && normalizedThreadLabel) {
|
|
1916
|
+
threadGroup.threadLabel = normalizedThreadLabel;
|
|
1917
|
+
}
|
|
1918
|
+
threadGroup.sourceMode = mergeDiffThreadSourceMode(threadGroup.sourceMode, sourceMode);
|
|
1919
|
+
return threadGroup;
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
function addChangeToDiffThreadGroup({ groupsByThread, threadId, threadLabel, fallbackKey = "", sourceMode = "", change, createdAtMs }) {
|
|
1923
|
+
const normalizedChangeType = normalizeTimelineFileEventType(change?.changeType || "");
|
|
1924
|
+
const normalizedFileRef = cleanTimelineFileRef(change?.fileRef || change?.newFileRef || change?.oldFileRef || "");
|
|
1925
|
+
const normalizedOldFileRef = cleanTimelineFileRef(change?.oldFileRef || "");
|
|
1926
|
+
const normalizedNewFileRef = cleanTimelineFileRef(change?.newFileRef || normalizedFileRef);
|
|
1927
|
+
if (!normalizedChangeType || !(normalizedFileRef || normalizedOldFileRef || normalizedNewFileRef)) {
|
|
1928
|
+
return;
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
const group = ensureDiffThreadGroup(groupsByThread, {
|
|
1932
|
+
threadId,
|
|
1933
|
+
threadLabel,
|
|
1934
|
+
fallbackKey: normalizedFileRef || normalizedNewFileRef || normalizedOldFileRef || fallbackKey,
|
|
1935
|
+
sourceMode,
|
|
1936
|
+
});
|
|
1937
|
+
|
|
1938
|
+
const fileGroupKey = `file:${normalizedNewFileRef || normalizedFileRef || normalizedOldFileRef}`;
|
|
1939
|
+
if (!fileGroupKey) {
|
|
1940
|
+
return;
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
const latestChangedAtMs = Number(createdAtMs) || 0;
|
|
1944
|
+
const fileGroup = {
|
|
1945
|
+
fileRef: normalizedFileRef || normalizedNewFileRef || normalizedOldFileRef,
|
|
1946
|
+
oldFileRef: normalizedOldFileRef,
|
|
1947
|
+
newFileRef: normalizedNewFileRef || normalizedFileRef,
|
|
1948
|
+
fileLabel:
|
|
1949
|
+
path.basename(
|
|
1950
|
+
normalizedChangeType === "delete"
|
|
1951
|
+
? normalizedOldFileRef || normalizedFileRef
|
|
1952
|
+
: normalizedNewFileRef || normalizedFileRef || normalizedOldFileRef
|
|
1953
|
+
) ||
|
|
1954
|
+
normalizedNewFileRef ||
|
|
1955
|
+
normalizedOldFileRef ||
|
|
1956
|
+
normalizedFileRef,
|
|
1957
|
+
changeType: normalizedChangeType,
|
|
1958
|
+
fileEventTypes: [normalizedChangeType],
|
|
1959
|
+
addedLines: Math.max(0, Number(change?.diffAddedLines) || 0),
|
|
1960
|
+
removedLines: Math.max(0, Number(change?.diffRemovedLines) || 0),
|
|
1961
|
+
latestChangedAtMs,
|
|
1962
|
+
sections: [
|
|
1963
|
+
{
|
|
1964
|
+
createdAtMs: latestChangedAtMs,
|
|
1965
|
+
diffText: normalizeTimelineDiffText(change?.diffText ?? ""),
|
|
1966
|
+
diffAvailable: change?.diffAvailable === true || Boolean(change?.diffText),
|
|
1967
|
+
diffSource: normalizeTimelineDiffSource(change?.diffSource ?? ""),
|
|
1968
|
+
addedLines: Math.max(0, Number(change?.diffAddedLines) || 0),
|
|
1969
|
+
removedLines: Math.max(0, Number(change?.diffRemovedLines) || 0),
|
|
1970
|
+
fileEventType: normalizedChangeType,
|
|
1971
|
+
},
|
|
1972
|
+
],
|
|
1973
|
+
};
|
|
1974
|
+
group.filesByRef.set(fileGroupKey, fileGroup);
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1759
1977
|
function handleSignal() {
|
|
1760
1978
|
runtime.stopping = true;
|
|
1761
1979
|
}
|
|
@@ -1791,7 +2009,11 @@ function normalizeHistoryItem(raw) {
|
|
|
1791
2009
|
|
|
1792
2010
|
const stableId = cleanText(raw.stableId ?? raw.id ?? "");
|
|
1793
2011
|
const kind = cleanText(raw.kind ?? "");
|
|
1794
|
-
const
|
|
2012
|
+
const threadId = cleanText(raw.threadId ?? extractConversationIdFromStableId(stableId) ?? "");
|
|
2013
|
+
const rawThreadLabel = cleanText(raw.threadLabel ?? "");
|
|
2014
|
+
const threadLabel = preferTitleOnlyJsonThreadLabel(rawThreadLabel, threadId, raw.messageText, raw.summary, raw.detailText, raw.message);
|
|
2015
|
+
const title =
|
|
2016
|
+
cleanText(raw.title ?? "") || (threadLabel ? formatTitle(kindTitle(DEFAULT_LOCALE, kind), threadLabel) : kindTitle(DEFAULT_LOCALE, kind));
|
|
1795
2017
|
const messageText = normalizeTimelineMessageText(raw.messageText ?? "");
|
|
1796
2018
|
const summary = normalizeNotificationText(raw.summary ?? "") || formatNotificationBody(messageText, 100) || "";
|
|
1797
2019
|
const createdAtMs = Number(raw.createdAtMs) || Date.now();
|
|
@@ -1805,9 +2027,9 @@ function normalizeHistoryItem(raw) {
|
|
|
1805
2027
|
stableId,
|
|
1806
2028
|
token: cleanText(raw.token ?? "") || historyToken(stableId),
|
|
1807
2029
|
kind,
|
|
1808
|
-
threadId
|
|
2030
|
+
threadId,
|
|
1809
2031
|
title,
|
|
1810
|
-
threadLabel
|
|
2032
|
+
threadLabel,
|
|
1811
2033
|
summary,
|
|
1812
2034
|
messageText,
|
|
1813
2035
|
imagePaths: normalizeTimelineImagePaths(raw.imagePaths ?? raw.localImagePaths ?? []),
|
|
@@ -1922,6 +2144,7 @@ function normalizeTimelineEntry(raw) {
|
|
|
1922
2144
|
return null;
|
|
1923
2145
|
}
|
|
1924
2146
|
|
|
2147
|
+
const threadId = cleanText(raw.threadId ?? extractConversationIdFromStableId(stableId) ?? "");
|
|
1925
2148
|
const messageText = normalizeTimelineMessageText(raw.messageText ?? "");
|
|
1926
2149
|
const fileEventType = normalizeTimelineFileEventType(raw.fileEventType ?? "");
|
|
1927
2150
|
const diffText = normalizeTimelineDiffText(raw.diffText ?? "");
|
|
@@ -1932,7 +2155,14 @@ function normalizeTimelineEntry(raw) {
|
|
|
1932
2155
|
formatNotificationBody(messageText, 180) ||
|
|
1933
2156
|
(kind === "file_event" ? "" : cleanText(raw.title ?? "")) ||
|
|
1934
2157
|
"";
|
|
1935
|
-
const threadLabel =
|
|
2158
|
+
const threadLabel = preferTitleOnlyJsonThreadLabel(
|
|
2159
|
+
cleanText(raw.threadLabel ?? ""),
|
|
2160
|
+
threadId,
|
|
2161
|
+
raw.messageText,
|
|
2162
|
+
raw.summary,
|
|
2163
|
+
raw.detailText,
|
|
2164
|
+
raw.message
|
|
2165
|
+
);
|
|
1936
2166
|
const title =
|
|
1937
2167
|
cleanText(raw.title ?? "") ||
|
|
1938
2168
|
(kind === "file_event" ? fileEventTitle(DEFAULT_LOCALE, fileEventType) : "") ||
|
|
@@ -1944,7 +2174,7 @@ function normalizeTimelineEntry(raw) {
|
|
|
1944
2174
|
stableId,
|
|
1945
2175
|
token: cleanText(raw.token ?? "") || historyToken(stableId),
|
|
1946
2176
|
kind,
|
|
1947
|
-
threadId
|
|
2177
|
+
threadId,
|
|
1948
2178
|
threadLabel,
|
|
1949
2179
|
title,
|
|
1950
2180
|
summary,
|
|
@@ -6752,6 +6982,90 @@ function getNativeThreadLabel({ runtime, conversationId, cwd }) {
|
|
|
6752
6982
|
return shortId(normalizedConversationId) || "Codex task";
|
|
6753
6983
|
}
|
|
6754
6984
|
|
|
6985
|
+
function threadStateArchiveStatus(threadState) {
|
|
6986
|
+
if (!isPlainObject(threadState)) {
|
|
6987
|
+
return "";
|
|
6988
|
+
}
|
|
6989
|
+
|
|
6990
|
+
const booleanCandidates = [
|
|
6991
|
+
threadState.archived,
|
|
6992
|
+
threadState.isArchived,
|
|
6993
|
+
threadState.hidden,
|
|
6994
|
+
threadState.isHidden,
|
|
6995
|
+
threadState.deleted,
|
|
6996
|
+
threadState.isDeleted,
|
|
6997
|
+
threadState.closed,
|
|
6998
|
+
threadState.isClosed,
|
|
6999
|
+
threadState.thread?.archived,
|
|
7000
|
+
threadState.thread?.isArchived,
|
|
7001
|
+
threadState.thread?.hidden,
|
|
7002
|
+
threadState.thread?.isHidden,
|
|
7003
|
+
threadState.thread?.deleted,
|
|
7004
|
+
threadState.thread?.isDeleted,
|
|
7005
|
+
threadState.metadata?.archived,
|
|
7006
|
+
threadState.metadata?.isArchived,
|
|
7007
|
+
threadState.metadata?.hidden,
|
|
7008
|
+
threadState.metadata?.isHidden,
|
|
7009
|
+
threadState.metadata?.deleted,
|
|
7010
|
+
threadState.metadata?.isDeleted,
|
|
7011
|
+
];
|
|
7012
|
+
if (booleanCandidates.some((value) => value === true)) {
|
|
7013
|
+
return "archived";
|
|
7014
|
+
}
|
|
7015
|
+
|
|
7016
|
+
const statusCandidates = [
|
|
7017
|
+
threadState.status,
|
|
7018
|
+
threadState.state,
|
|
7019
|
+
threadState.visibility,
|
|
7020
|
+
threadState.lifecycle,
|
|
7021
|
+
threadState.thread?.status,
|
|
7022
|
+
threadState.thread?.state,
|
|
7023
|
+
threadState.thread?.visibility,
|
|
7024
|
+
threadState.metadata?.status,
|
|
7025
|
+
threadState.metadata?.state,
|
|
7026
|
+
threadState.metadata?.visibility,
|
|
7027
|
+
]
|
|
7028
|
+
.map((value) => cleanText(value || "").toLowerCase())
|
|
7029
|
+
.filter(Boolean);
|
|
7030
|
+
|
|
7031
|
+
for (const status of statusCandidates) {
|
|
7032
|
+
if (["archived", "hidden", "deleted", "closed"].includes(status)) {
|
|
7033
|
+
return status;
|
|
7034
|
+
}
|
|
7035
|
+
}
|
|
7036
|
+
|
|
7037
|
+
return "";
|
|
7038
|
+
}
|
|
7039
|
+
|
|
7040
|
+
function shouldExcludeCodeThread(runtime, threadId) {
|
|
7041
|
+
const normalizedThreadId = cleanText(threadId || "");
|
|
7042
|
+
if (!normalizedThreadId) {
|
|
7043
|
+
return false;
|
|
7044
|
+
}
|
|
7045
|
+
|
|
7046
|
+
const threadState = runtime.threadStates.get(normalizedThreadId) ?? null;
|
|
7047
|
+
if (threadStateArchiveStatus(threadState)) {
|
|
7048
|
+
return true;
|
|
7049
|
+
}
|
|
7050
|
+
|
|
7051
|
+
const currentSessionHasThread = Array.isArray(runtime.knownFiles)
|
|
7052
|
+
? runtime.knownFiles.some((filePath) => extractThreadIdFromRolloutPath(filePath) === normalizedThreadId)
|
|
7053
|
+
: false;
|
|
7054
|
+
if (currentSessionHasThread) {
|
|
7055
|
+
return false;
|
|
7056
|
+
}
|
|
7057
|
+
|
|
7058
|
+
if (threadState) {
|
|
7059
|
+
return false;
|
|
7060
|
+
}
|
|
7061
|
+
|
|
7062
|
+
if (runtime.sessionIndex instanceof Map && runtime.sessionIndex.size > 0) {
|
|
7063
|
+
return runtime.sessionIndex.has(normalizedThreadId);
|
|
7064
|
+
}
|
|
7065
|
+
|
|
7066
|
+
return false;
|
|
7067
|
+
}
|
|
7068
|
+
|
|
6755
7069
|
async function findRolloutThreadCwd(runtime, conversationId) {
|
|
6756
7070
|
const normalizedConversationId = cleanText(conversationId || "");
|
|
6757
7071
|
if (!normalizedConversationId) {
|
|
@@ -8427,6 +8741,7 @@ async function buildDiffInboxItems(runtime, state, config, locale) {
|
|
|
8427
8741
|
token: group.token,
|
|
8428
8742
|
threadId: group.threadId,
|
|
8429
8743
|
threadLabel: group.threadLabel || "",
|
|
8744
|
+
sourceMode: cleanText(group.sourceMode || ""),
|
|
8430
8745
|
title: cleanText(group.threadLabel || "") || kindTitle(locale, "diff_thread"),
|
|
8431
8746
|
summary: t(locale, "diff.threadSummary", { count: group.changedFileCount }),
|
|
8432
8747
|
changedFileCount: group.changedFileCount,
|
|
@@ -8467,10 +8782,12 @@ async function buildDiffThreadGroups(runtime, state, config) {
|
|
|
8467
8782
|
.slice()
|
|
8468
8783
|
.sort((left, right) => Number(right.createdAtMs ?? 0) - Number(left.createdAtMs ?? 0));
|
|
8469
8784
|
const candidatesByRepoRoot = new Map();
|
|
8785
|
+
const nonGitRelevantItems = [];
|
|
8470
8786
|
|
|
8471
8787
|
for (const item of relevantItems) {
|
|
8472
8788
|
const repoRoot = await resolveCodeEventRepoRoot(item, repoRootCache);
|
|
8473
8789
|
if (!repoRoot) {
|
|
8790
|
+
nonGitRelevantItems.push(item);
|
|
8474
8791
|
continue;
|
|
8475
8792
|
}
|
|
8476
8793
|
const candidates = expandCodeEventOwnershipCandidates(item, repoRoot);
|
|
@@ -8492,112 +8809,42 @@ async function buildDiffThreadGroups(runtime, state, config) {
|
|
|
8492
8809
|
continue;
|
|
8493
8810
|
}
|
|
8494
8811
|
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
|
|
8503
|
-
|
|
8504
|
-
|
|
8505
|
-
|
|
8506
|
-
latestChangedAtMs: 0,
|
|
8507
|
-
latestChangedAtMsForSummary: 0,
|
|
8508
|
-
latestChangeType: "",
|
|
8509
|
-
latestChangeFileRefs: [],
|
|
8510
|
-
diffAddedLines: 0,
|
|
8511
|
-
diffRemovedLines: 0,
|
|
8512
|
-
filesByRef: new Map(),
|
|
8513
|
-
};
|
|
8514
|
-
groupsByThread.set(threadKey, threadGroup);
|
|
8515
|
-
} else if (!threadGroup.threadLabel && threadLabel) {
|
|
8516
|
-
threadGroup.threadLabel = threadLabel;
|
|
8517
|
-
}
|
|
8812
|
+
addChangeToDiffThreadGroup({
|
|
8813
|
+
groupsByThread,
|
|
8814
|
+
threadId: cleanText(owner.threadId || ""),
|
|
8815
|
+
threadLabel: cleanText(owner.threadLabel || ""),
|
|
8816
|
+
fallbackKey: cleanText(change.fileRef || change.newFileRef || change.oldFileRef || ""),
|
|
8817
|
+
sourceMode: "git_current",
|
|
8818
|
+
change,
|
|
8819
|
+
createdAtMs: Number(owner.createdAtMs) || 0,
|
|
8820
|
+
});
|
|
8821
|
+
}
|
|
8822
|
+
}
|
|
8518
8823
|
|
|
8519
|
-
|
|
8520
|
-
|
|
8521
|
-
|
|
8522
|
-
|
|
8523
|
-
|
|
8824
|
+
const latestNonGitChangesByIdentity = new Map();
|
|
8825
|
+
for (const item of nonGitRelevantItems) {
|
|
8826
|
+
const changes = buildNonGitCodeChangeEntries(item);
|
|
8827
|
+
for (const change of changes) {
|
|
8828
|
+
const identity = nonGitCodeChangeIdentity(change);
|
|
8829
|
+
if (!identity || latestNonGitChangesByIdentity.has(identity)) {
|
|
8524
8830
|
continue;
|
|
8525
8831
|
}
|
|
8526
|
-
|
|
8527
|
-
const latestOwnerChangeAtMs = Number(owner.createdAtMs) || 0;
|
|
8528
|
-
const normalizedFileRef = cleanTimelineFileRef(change.fileRef || change.newFileRef || change.oldFileRef || "");
|
|
8529
|
-
const normalizedOldFileRef = cleanTimelineFileRef(change.oldFileRef || "");
|
|
8530
|
-
const normalizedNewFileRef = cleanTimelineFileRef(change.newFileRef || normalizedFileRef);
|
|
8531
|
-
let fileGroup = threadGroup.filesByRef.get(fileGroupKey);
|
|
8532
|
-
if (!fileGroup) {
|
|
8533
|
-
fileGroup = {
|
|
8534
|
-
fileRef: normalizedFileRef,
|
|
8535
|
-
oldFileRef: normalizedOldFileRef,
|
|
8536
|
-
newFileRef: normalizedNewFileRef,
|
|
8537
|
-
fileLabel:
|
|
8538
|
-
path.basename(
|
|
8539
|
-
normalizeTimelineFileEventType(change.changeType) === "delete"
|
|
8540
|
-
? normalizedOldFileRef || normalizedFileRef
|
|
8541
|
-
: normalizedNewFileRef || normalizedFileRef
|
|
8542
|
-
) ||
|
|
8543
|
-
normalizedNewFileRef ||
|
|
8544
|
-
normalizedOldFileRef ||
|
|
8545
|
-
normalizedFileRef,
|
|
8546
|
-
changeType: normalizeTimelineFileEventType(change.changeType),
|
|
8547
|
-
fileEventTypes: new Set(),
|
|
8548
|
-
addedLines: 0,
|
|
8549
|
-
removedLines: 0,
|
|
8550
|
-
latestChangedAtMs: latestOwnerChangeAtMs,
|
|
8551
|
-
sections: [],
|
|
8552
|
-
};
|
|
8553
|
-
threadGroup.filesByRef.set(fileGroupKey, fileGroup);
|
|
8554
|
-
}
|
|
8555
|
-
|
|
8556
|
-
const changeType = normalizeTimelineFileEventType(change.changeType);
|
|
8557
|
-
if (changeType) {
|
|
8558
|
-
fileGroup.fileEventTypes.add(changeType);
|
|
8559
|
-
}
|
|
8560
|
-
fileGroup.changeType = changeType || fileGroup.changeType;
|
|
8561
|
-
fileGroup.addedLines = Math.max(0, Number(change.diffAddedLines) || 0);
|
|
8562
|
-
fileGroup.removedLines = Math.max(0, Number(change.diffRemovedLines) || 0);
|
|
8563
|
-
fileGroup.latestChangedAtMs = latestOwnerChangeAtMs;
|
|
8564
|
-
fileGroup.fileRef = normalizedFileRef;
|
|
8565
|
-
fileGroup.oldFileRef = normalizedOldFileRef;
|
|
8566
|
-
fileGroup.newFileRef = normalizedNewFileRef;
|
|
8567
|
-
fileGroup.sections = [
|
|
8568
|
-
{
|
|
8569
|
-
createdAtMs: latestOwnerChangeAtMs,
|
|
8570
|
-
diffText: normalizeTimelineDiffText(change.diffText ?? ""),
|
|
8571
|
-
diffAvailable: change.diffAvailable === true || Boolean(change.diffText),
|
|
8572
|
-
diffSource: normalizeTimelineDiffSource(change.diffSource ?? ""),
|
|
8573
|
-
addedLines: Math.max(0, Number(change.diffAddedLines) || 0),
|
|
8574
|
-
removedLines: Math.max(0, Number(change.diffRemovedLines) || 0),
|
|
8575
|
-
fileEventType: changeType,
|
|
8576
|
-
},
|
|
8577
|
-
];
|
|
8578
|
-
|
|
8579
|
-
threadGroup.latestChangedAtMs = Math.max(threadGroup.latestChangedAtMs, latestOwnerChangeAtMs);
|
|
8580
|
-
threadGroup.diffAddedLines += Math.max(0, Number(change.diffAddedLines) || 0);
|
|
8581
|
-
threadGroup.diffRemovedLines += Math.max(0, Number(change.diffRemovedLines) || 0);
|
|
8582
|
-
|
|
8583
|
-
if (latestOwnerChangeAtMs > threadGroup.latestChangedAtMsForSummary) {
|
|
8584
|
-
threadGroup.latestChangedAtMsForSummary = latestOwnerChangeAtMs;
|
|
8585
|
-
threadGroup.latestChangeType = changeType || "";
|
|
8586
|
-
threadGroup.latestChangeFileRefs = [normalizedFileRef || normalizedNewFileRef || normalizedOldFileRef];
|
|
8587
|
-
} else if (latestOwnerChangeAtMs === (threadGroup.latestChangedAtMsForSummary || 0)) {
|
|
8588
|
-
if (changeType && threadGroup.latestChangeType && threadGroup.latestChangeType !== changeType) {
|
|
8589
|
-
threadGroup.latestChangeType = "";
|
|
8590
|
-
} else if (!threadGroup.latestChangeType && changeType && threadGroup.latestChangeFileRefs.length === 0) {
|
|
8591
|
-
threadGroup.latestChangeType = changeType;
|
|
8592
|
-
}
|
|
8593
|
-
const latestFileRef = normalizedFileRef || normalizedNewFileRef || normalizedOldFileRef;
|
|
8594
|
-
if (latestFileRef && !threadGroup.latestChangeFileRefs.includes(latestFileRef)) {
|
|
8595
|
-
threadGroup.latestChangeFileRefs.push(latestFileRef);
|
|
8596
|
-
}
|
|
8597
|
-
}
|
|
8832
|
+
latestNonGitChangesByIdentity.set(identity, change);
|
|
8598
8833
|
}
|
|
8599
8834
|
}
|
|
8600
8835
|
|
|
8836
|
+
for (const change of latestNonGitChangesByIdentity.values()) {
|
|
8837
|
+
addChangeToDiffThreadGroup({
|
|
8838
|
+
groupsByThread,
|
|
8839
|
+
threadId: cleanText(change.threadId || ""),
|
|
8840
|
+
threadLabel: cleanText(change.threadLabel || ""),
|
|
8841
|
+
fallbackKey: cleanText(change.fileRef || change.newFileRef || change.oldFileRef || ""),
|
|
8842
|
+
sourceMode: "non_git_latest",
|
|
8843
|
+
change,
|
|
8844
|
+
createdAtMs: Number(change.createdAtMs) || 0,
|
|
8845
|
+
});
|
|
8846
|
+
}
|
|
8847
|
+
|
|
8601
8848
|
return [...groupsByThread.values()]
|
|
8602
8849
|
.map((group) => {
|
|
8603
8850
|
const files = [...group.filesByRef.values()]
|
|
@@ -8607,7 +8854,9 @@ async function buildDiffThreadGroups(runtime, state, config) {
|
|
|
8607
8854
|
newFileRef: fileGroup.newFileRef,
|
|
8608
8855
|
fileLabel: fileGroup.fileLabel,
|
|
8609
8856
|
changeType: normalizeTimelineFileEventType(fileGroup.changeType),
|
|
8610
|
-
fileEventTypes:
|
|
8857
|
+
fileEventTypes: Array.isArray(fileGroup.fileEventTypes)
|
|
8858
|
+
? fileGroup.fileEventTypes
|
|
8859
|
+
: [...fileGroup.fileEventTypes.values()],
|
|
8611
8860
|
addedLines: fileGroup.addedLines,
|
|
8612
8861
|
removedLines: fileGroup.removedLines,
|
|
8613
8862
|
latestChangedAtMs: fileGroup.latestChangedAtMs,
|
|
@@ -8615,20 +8864,33 @@ async function buildDiffThreadGroups(runtime, state, config) {
|
|
|
8615
8864
|
}))
|
|
8616
8865
|
.sort((left, right) => Number(right.latestChangedAtMs ?? 0) - Number(left.latestChangedAtMs ?? 0));
|
|
8617
8866
|
|
|
8867
|
+
const latestChangedAtMs = Math.max(0, ...files.map((fileGroup) => Number(fileGroup.latestChangedAtMs) || 0));
|
|
8868
|
+
const latestFiles = files.filter((fileGroup) => Number(fileGroup.latestChangedAtMs || 0) === latestChangedAtMs);
|
|
8869
|
+
const latestChangeTypes = [...new Set(latestFiles.map((fileGroup) => normalizeTimelineFileEventType(fileGroup.changeType)).filter(Boolean))];
|
|
8870
|
+
const latestChangeFileRefs = normalizeTimelineFileRefs(
|
|
8871
|
+
latestFiles
|
|
8872
|
+
.map((fileGroup) => cleanTimelineFileRef(fileGroup.fileRef || fileGroup.newFileRef || fileGroup.oldFileRef || ""))
|
|
8873
|
+
.filter(Boolean)
|
|
8874
|
+
);
|
|
8875
|
+
const diffAddedLines = files.reduce((sum, fileGroup) => sum + Math.max(0, Number(fileGroup.addedLines) || 0), 0);
|
|
8876
|
+
const diffRemovedLines = files.reduce((sum, fileGroup) => sum + Math.max(0, Number(fileGroup.removedLines) || 0), 0);
|
|
8877
|
+
|
|
8618
8878
|
return {
|
|
8619
8879
|
kind: "diff_thread",
|
|
8620
8880
|
token: group.token,
|
|
8621
8881
|
threadId: group.threadId,
|
|
8622
8882
|
threadLabel: group.threadLabel,
|
|
8883
|
+
sourceMode: cleanText(group.sourceMode || ""),
|
|
8623
8884
|
changedFileCount: files.length,
|
|
8624
|
-
latestChangedAtMs
|
|
8625
|
-
latestChangeType:
|
|
8626
|
-
latestChangeFileRefs
|
|
8627
|
-
diffAddedLines
|
|
8628
|
-
diffRemovedLines
|
|
8885
|
+
latestChangedAtMs,
|
|
8886
|
+
latestChangeType: latestChangeTypes.length === 1 ? latestChangeTypes[0] : "",
|
|
8887
|
+
latestChangeFileRefs,
|
|
8888
|
+
diffAddedLines,
|
|
8889
|
+
diffRemovedLines,
|
|
8629
8890
|
files,
|
|
8630
8891
|
};
|
|
8631
8892
|
})
|
|
8893
|
+
.filter((group) => !shouldExcludeCodeThread(runtime, group.threadId))
|
|
8632
8894
|
.filter((group) => group.changedFileCount > 0)
|
|
8633
8895
|
.sort((left, right) => Number(right.latestChangedAtMs ?? 0) - Number(left.latestChangedAtMs ?? 0));
|
|
8634
8896
|
}
|
|
@@ -9145,18 +9407,20 @@ function buildTimelineFileEventDetail(entry, locale) {
|
|
|
9145
9407
|
}
|
|
9146
9408
|
|
|
9147
9409
|
function buildDiffThreadDetail(group, locale) {
|
|
9410
|
+
const changedFileCount = Math.max(0, Number(group.changedFileCount) || 0);
|
|
9148
9411
|
return {
|
|
9149
9412
|
kind: "diff_thread",
|
|
9150
9413
|
token: group.token,
|
|
9151
9414
|
threadId: cleanText(group.threadId || ""),
|
|
9152
9415
|
title: cleanText(group.threadLabel || "") || kindTitle(locale, "diff_thread"),
|
|
9153
9416
|
threadLabel: group.threadLabel || "",
|
|
9417
|
+
sourceMode: cleanText(group.sourceMode || ""),
|
|
9154
9418
|
createdAtMs: Number(group.latestChangedAtMs) || 0,
|
|
9155
|
-
changedFileCount
|
|
9419
|
+
changedFileCount,
|
|
9156
9420
|
diffAddedLines: Math.max(0, Number(group.diffAddedLines) || 0),
|
|
9157
9421
|
diffRemovedLines: Math.max(0, Number(group.diffRemovedLines) || 0),
|
|
9158
9422
|
messageHtml: renderMessageHtml(
|
|
9159
|
-
|
|
9423
|
+
diffThreadDetailCopy(locale, group.sourceMode, changedFileCount),
|
|
9160
9424
|
`<p>${escapeHtml(t(locale, "detail.detailUnavailable"))}</p>`
|
|
9161
9425
|
),
|
|
9162
9426
|
files: Array.isArray(group.files)
|
|
@@ -12477,6 +12741,22 @@ function extractRolloutMessageText(content) {
|
|
|
12477
12741
|
);
|
|
12478
12742
|
}
|
|
12479
12743
|
|
|
12744
|
+
function extractTitleOnlyJsonTitleFromRolloutContent(content) {
|
|
12745
|
+
if (!Array.isArray(content)) {
|
|
12746
|
+
return "";
|
|
12747
|
+
}
|
|
12748
|
+
for (const entry of content) {
|
|
12749
|
+
if (!isPlainObject(entry) || (entry.type !== "input_text" && entry.type !== "output_text")) {
|
|
12750
|
+
continue;
|
|
12751
|
+
}
|
|
12752
|
+
const title = extractTitleOnlyJsonTitle(entry.text ?? "");
|
|
12753
|
+
if (title) {
|
|
12754
|
+
return title;
|
|
12755
|
+
}
|
|
12756
|
+
}
|
|
12757
|
+
return "";
|
|
12758
|
+
}
|
|
12759
|
+
|
|
12480
12760
|
function rolloutContentHasImages(content) {
|
|
12481
12761
|
if (!Array.isArray(content)) {
|
|
12482
12762
|
return false;
|
|
@@ -12592,6 +12872,22 @@ async function extractRolloutThreadMetadata(filePath) {
|
|
|
12592
12872
|
break;
|
|
12593
12873
|
}
|
|
12594
12874
|
}
|
|
12875
|
+
} else if (!metadata.titleCandidate && entry.payload?.type === "message" && entry.payload?.role === "assistant") {
|
|
12876
|
+
const titleCandidate = truncate(cleanText(extractTitleOnlyJsonTitleFromRolloutContent(entry.payload.content)), 90);
|
|
12877
|
+
if (titleCandidate) {
|
|
12878
|
+
metadata.titleCandidate = titleCandidate;
|
|
12879
|
+
if (metadata.threadId) {
|
|
12880
|
+
break;
|
|
12881
|
+
}
|
|
12882
|
+
}
|
|
12883
|
+
} else if (!metadata.titleCandidate && entry.type === "event_msg" && entry.payload?.type === "task_complete") {
|
|
12884
|
+
const titleCandidate = truncate(cleanText(extractTitleOnlyJsonTitle(entry.payload.last_agent_message ?? "")), 90);
|
|
12885
|
+
if (titleCandidate) {
|
|
12886
|
+
metadata.titleCandidate = titleCandidate;
|
|
12887
|
+
if (metadata.threadId) {
|
|
12888
|
+
break;
|
|
12889
|
+
}
|
|
12890
|
+
}
|
|
12595
12891
|
}
|
|
12596
12892
|
}
|
|
12597
12893
|
} catch {
|
|
@@ -12768,11 +13064,12 @@ function refreshResolvedThreadLabels({ config, runtime, state }) {
|
|
|
12768
13064
|
if (!conversationId) {
|
|
12769
13065
|
return item;
|
|
12770
13066
|
}
|
|
12771
|
-
const
|
|
13067
|
+
const nativeThreadLabel = getNativeThreadLabel({
|
|
12772
13068
|
runtime,
|
|
12773
13069
|
conversationId,
|
|
12774
13070
|
cwd: "",
|
|
12775
13071
|
});
|
|
13072
|
+
const threadLabel = preferTitleOnlyJsonThreadLabel(nativeThreadLabel, conversationId, item.messageText, item.summary);
|
|
12776
13073
|
const title = formatTitle(kindTitle(config.defaultLocale, item.kind), threadLabel);
|
|
12777
13074
|
if (threadLabel === item.threadLabel && title === item.title) {
|
|
12778
13075
|
return item;
|
|
@@ -12802,11 +13099,12 @@ function refreshResolvedThreadLabels({ config, runtime, state }) {
|
|
|
12802
13099
|
if (!threadId) {
|
|
12803
13100
|
return entry;
|
|
12804
13101
|
}
|
|
12805
|
-
const
|
|
13102
|
+
const nativeThreadLabel = getNativeThreadLabel({
|
|
12806
13103
|
runtime,
|
|
12807
13104
|
conversationId: threadId,
|
|
12808
13105
|
cwd: "",
|
|
12809
13106
|
});
|
|
13107
|
+
const threadLabel = preferTitleOnlyJsonThreadLabel(nativeThreadLabel, threadId, entry.messageText, entry.summary);
|
|
12810
13108
|
const title = threadLabel || kindTitle(config.defaultLocale, entry.kind);
|
|
12811
13109
|
if (threadLabel === entry.threadLabel && title === entry.title) {
|
|
12812
13110
|
return entry;
|
|
@@ -12836,11 +13134,12 @@ function refreshResolvedThreadLabels({ config, runtime, state }) {
|
|
|
12836
13134
|
if (!threadId) {
|
|
12837
13135
|
return entry;
|
|
12838
13136
|
}
|
|
12839
|
-
const
|
|
13137
|
+
const nativeThreadLabel = getNativeThreadLabel({
|
|
12840
13138
|
runtime,
|
|
12841
13139
|
conversationId: threadId,
|
|
12842
13140
|
cwd: "",
|
|
12843
13141
|
});
|
|
13142
|
+
const threadLabel = preferTitleOnlyJsonThreadLabel(nativeThreadLabel, threadId, entry.messageText, entry.summary);
|
|
12844
13143
|
const title = threadLabel || kindTitle(config.defaultLocale, entry.kind);
|
|
12845
13144
|
if (threadLabel === entry.threadLabel && title === entry.title) {
|
|
12846
13145
|
return entry;
|
|
@@ -12902,6 +13201,10 @@ function summarizeNotificationText(value) {
|
|
|
12902
13201
|
}
|
|
12903
13202
|
|
|
12904
13203
|
function normalizeLongText(value) {
|
|
13204
|
+
const titleOnlyJsonTitle = extractTitleOnlyJsonTitle(value);
|
|
13205
|
+
if (titleOnlyJsonTitle) {
|
|
13206
|
+
return titleOnlyJsonTitle;
|
|
13207
|
+
}
|
|
12905
13208
|
return String(stripEnvironmentContextBlocks(stripMarkdownLinks(value)) || "")
|
|
12906
13209
|
.replace(/\r\n/gu, "\n")
|
|
12907
13210
|
.replace(/[ \t]+\n/gu, "\n")
|
|
@@ -13013,6 +13316,53 @@ function cleanText(value) {
|
|
|
13013
13316
|
return String(value || "").replace(/\s+/gu, " ").trim();
|
|
13014
13317
|
}
|
|
13015
13318
|
|
|
13319
|
+
function extractTitleOnlyJsonTitle(value) {
|
|
13320
|
+
const rawText = String(value ?? "").trim();
|
|
13321
|
+
if (!rawText) {
|
|
13322
|
+
return "";
|
|
13323
|
+
}
|
|
13324
|
+
const parsed = safeJsonParse(rawText);
|
|
13325
|
+
if (!isPlainObject(parsed) || typeof parsed.title !== "string") {
|
|
13326
|
+
return "";
|
|
13327
|
+
}
|
|
13328
|
+
const meaningfulExtraKeys = Object.entries(parsed).filter(([key, entryValue]) => {
|
|
13329
|
+
if (key === "title") {
|
|
13330
|
+
return false;
|
|
13331
|
+
}
|
|
13332
|
+
if (entryValue == null) {
|
|
13333
|
+
return false;
|
|
13334
|
+
}
|
|
13335
|
+
if (typeof entryValue === "string" && cleanText(entryValue) === "") {
|
|
13336
|
+
return false;
|
|
13337
|
+
}
|
|
13338
|
+
if (Array.isArray(entryValue) && entryValue.length === 0) {
|
|
13339
|
+
return false;
|
|
13340
|
+
}
|
|
13341
|
+
if (isPlainObject(entryValue) && Object.keys(entryValue).length === 0) {
|
|
13342
|
+
return false;
|
|
13343
|
+
}
|
|
13344
|
+
return true;
|
|
13345
|
+
});
|
|
13346
|
+
if (meaningfulExtraKeys.length > 0) {
|
|
13347
|
+
return "";
|
|
13348
|
+
}
|
|
13349
|
+
return cleanText(parsed.title || "");
|
|
13350
|
+
}
|
|
13351
|
+
|
|
13352
|
+
function preferTitleOnlyJsonThreadLabel(rawThreadLabel, conversationId, ...candidates) {
|
|
13353
|
+
const preferredThreadLabel = sanitizeResolvedThreadLabel(rawThreadLabel, conversationId);
|
|
13354
|
+
if (preferredThreadLabel) {
|
|
13355
|
+
return preferredThreadLabel;
|
|
13356
|
+
}
|
|
13357
|
+
for (const candidate of candidates) {
|
|
13358
|
+
const titleOnlyJsonTitle = truncate(cleanText(extractTitleOnlyJsonTitle(candidate)), 90);
|
|
13359
|
+
if (titleOnlyJsonTitle) {
|
|
13360
|
+
return titleOnlyJsonTitle;
|
|
13361
|
+
}
|
|
13362
|
+
}
|
|
13363
|
+
return cleanText(rawThreadLabel || "");
|
|
13364
|
+
}
|
|
13365
|
+
|
|
13016
13366
|
function stripMarkdownLinks(value) {
|
|
13017
13367
|
return String(value || "").replace(/\[([^\]]+)\]\(([^)]+)\)/gu, "$1");
|
|
13018
13368
|
}
|
|
@@ -13227,6 +13577,8 @@ async function main() {
|
|
|
13227
13577
|
}
|
|
13228
13578
|
|
|
13229
13579
|
if (
|
|
13580
|
+
normalizedHistoryStateChanged ||
|
|
13581
|
+
normalizedTimelineStateChanged ||
|
|
13230
13582
|
migratedPairedDevicesStateChanged ||
|
|
13231
13583
|
restoredPendingPlanStateChanged ||
|
|
13232
13584
|
restoredTimelineImagePathsStateChanged ||
|