@hermespilot/link 0.8.3 → 0.8.5-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-JWKKE7ZJ.js → chunk-JJBIYFGY.js} +403 -29
- package/dist/cli/index.js +1 -1
- package/dist/http/app.d.ts +13 -5
- package/dist/http/app.js +1 -1
- package/package.json +1 -1
|
@@ -7721,7 +7721,7 @@ function isConversationMissingError(error) {
|
|
|
7721
7721
|
}
|
|
7722
7722
|
|
|
7723
7723
|
// src/constants.ts
|
|
7724
|
-
var LINK_VERSION = "0.8.
|
|
7724
|
+
var LINK_VERSION = "0.8.5-beta.0";
|
|
7725
7725
|
var LINK_COMMAND = "hermeslink";
|
|
7726
7726
|
var LINK_DEFAULT_PORT = 52379;
|
|
7727
7727
|
var LINK_RUNTIME_DIR_NAME = ".hermeslink";
|
|
@@ -14803,6 +14803,8 @@ function toSummary(manifest, snapshot, profile) {
|
|
|
14803
14803
|
id: manifest.id,
|
|
14804
14804
|
title: manifest.title,
|
|
14805
14805
|
title_source: manifest.title_source,
|
|
14806
|
+
category: manifestHasCronSignal(manifest, snapshot) ? "cron" : "chat",
|
|
14807
|
+
is_cron: manifestHasCronSignal(manifest, snapshot),
|
|
14806
14808
|
workspace_id: manifest.workspace_id ?? null,
|
|
14807
14809
|
created_at: manifest.created_at,
|
|
14808
14810
|
updated_at: manifest.updated_at,
|
|
@@ -14823,6 +14825,34 @@ function toSummary(manifest, snapshot, profile) {
|
|
|
14823
14825
|
} : null
|
|
14824
14826
|
};
|
|
14825
14827
|
}
|
|
14828
|
+
function manifestHasCronSignal(manifest, snapshot) {
|
|
14829
|
+
const sessionIds = [
|
|
14830
|
+
manifest.hermes_session_id,
|
|
14831
|
+
...manifest.hermes_session_ids ?? [],
|
|
14832
|
+
manifest.hermes_lineage?.root_session_id,
|
|
14833
|
+
manifest.hermes_lineage?.current_session_id,
|
|
14834
|
+
...manifest.hermes_lineage?.session_ids ?? []
|
|
14835
|
+
].map((value) => value?.trim()).filter((value) => Boolean(value));
|
|
14836
|
+
if (sessionIds.some((sessionId) => sessionId.startsWith("cron_"))) {
|
|
14837
|
+
return true;
|
|
14838
|
+
}
|
|
14839
|
+
if (snapshot?.messages.some((message) => {
|
|
14840
|
+
const raw = message.raw;
|
|
14841
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
14842
|
+
const format = raw.format;
|
|
14843
|
+
if (format === "hermes-link-cron-delivery") {
|
|
14844
|
+
return true;
|
|
14845
|
+
}
|
|
14846
|
+
}
|
|
14847
|
+
return Boolean(message.hermes?.cron_job_id || message.hermes?.cron_output_path);
|
|
14848
|
+
})) {
|
|
14849
|
+
return true;
|
|
14850
|
+
}
|
|
14851
|
+
return normalizeTitle(manifest.title) === "HermesLink \u5B9A\u65F6\u4EFB\u52A1";
|
|
14852
|
+
}
|
|
14853
|
+
function normalizeTitle(value) {
|
|
14854
|
+
return value.trim();
|
|
14855
|
+
}
|
|
14826
14856
|
function isDefaultConversationTitle(title) {
|
|
14827
14857
|
const normalized = title.trim().toLowerCase();
|
|
14828
14858
|
return !normalized || normalized === "untitled" || normalized === "new chat" || normalized === DEFAULT_CONVERSATION_TITLE.toLowerCase();
|
|
@@ -15902,6 +15932,9 @@ function collectHermesSessionIds(manifest, snapshot) {
|
|
|
15902
15932
|
return normalizeHermesSessionIds([
|
|
15903
15933
|
manifest.hermes_session_id,
|
|
15904
15934
|
...manifest.hermes_session_ids ?? [],
|
|
15935
|
+
manifest.hermes_lineage?.root_session_id,
|
|
15936
|
+
manifest.hermes_lineage?.current_session_id,
|
|
15937
|
+
...manifest.hermes_lineage?.session_ids ?? [],
|
|
15905
15938
|
...Object.values(manifest.profile_session_ids ?? {}),
|
|
15906
15939
|
...snapshot.runs.map((run) => run.hermes_session_id)
|
|
15907
15940
|
]);
|
|
@@ -15930,6 +15963,11 @@ function collectHermesSessionDeleteTargets(manifest, snapshot) {
|
|
|
15930
15963
|
for (const sessionId of manifest.hermes_session_ids ?? []) {
|
|
15931
15964
|
add(sessionId, defaultProfile);
|
|
15932
15965
|
}
|
|
15966
|
+
add(manifest.hermes_lineage?.root_session_id, defaultProfile);
|
|
15967
|
+
add(manifest.hermes_lineage?.current_session_id, defaultProfile);
|
|
15968
|
+
for (const sessionId of manifest.hermes_lineage?.session_ids ?? []) {
|
|
15969
|
+
add(sessionId, defaultProfile);
|
|
15970
|
+
}
|
|
15933
15971
|
for (const [profileName, sessionId] of Object.entries(
|
|
15934
15972
|
manifest.profile_session_ids ?? {}
|
|
15935
15973
|
)) {
|
|
@@ -19735,14 +19773,16 @@ var ConversationQueryCoordinator = class {
|
|
|
19735
19773
|
status,
|
|
19736
19774
|
limit,
|
|
19737
19775
|
cursor,
|
|
19738
|
-
workspace: options.workspace
|
|
19776
|
+
workspace: options.workspace,
|
|
19777
|
+
category: options.category
|
|
19739
19778
|
}),
|
|
19740
19779
|
listPage: (pageCursor) => listConversationStatsPage(this.deps.paths, {
|
|
19741
19780
|
status,
|
|
19742
19781
|
limit,
|
|
19743
19782
|
cursor: pageCursor,
|
|
19744
19783
|
workspace: options.workspace
|
|
19745
|
-
})
|
|
19784
|
+
}),
|
|
19785
|
+
category: options.category
|
|
19746
19786
|
});
|
|
19747
19787
|
}
|
|
19748
19788
|
async searchConversationPage(options = {}) {
|
|
@@ -19790,7 +19830,8 @@ var ConversationQueryCoordinator = class {
|
|
|
19790
19830
|
usedIndex = true;
|
|
19791
19831
|
const summaries = await this.summarizeIndexedConversations(
|
|
19792
19832
|
input.status,
|
|
19793
|
-
indexedPage.records
|
|
19833
|
+
indexedPage.records,
|
|
19834
|
+
input.category
|
|
19794
19835
|
);
|
|
19795
19836
|
for (const summary of summaries) {
|
|
19796
19837
|
if (!seenConversationIds.add(summary.id)) {
|
|
@@ -19830,15 +19871,18 @@ var ConversationQueryCoordinator = class {
|
|
|
19830
19871
|
}
|
|
19831
19872
|
};
|
|
19832
19873
|
}
|
|
19833
|
-
async listConversationsFromStore(status = "active",
|
|
19874
|
+
async listConversationsFromStore(status = "active", options = {}) {
|
|
19834
19875
|
const summaries = [];
|
|
19835
19876
|
for (const conversationId of await this.deps.store.listConversationIds()) {
|
|
19836
19877
|
const manifest = await this.deps.store.readManifest(conversationId).catch(() => null);
|
|
19837
|
-
if (!manifest || manifest.status !== status || !matchesWorkspaceFilter(manifest, workspace)) {
|
|
19878
|
+
if (!manifest || manifest.status !== status || !matchesWorkspaceFilter(manifest, options.workspace)) {
|
|
19838
19879
|
continue;
|
|
19839
19880
|
}
|
|
19840
19881
|
const snapshot = await this.deps.store.readSnapshot(conversationId).catch(() => emptySnapshot2());
|
|
19841
19882
|
const refreshed = await this.deps.metadata.refreshTitleFromHermes(manifest, { snapshot }).catch(() => manifest);
|
|
19883
|
+
if (!matchesConversationCategory(refreshed, snapshot, options.category)) {
|
|
19884
|
+
continue;
|
|
19885
|
+
}
|
|
19842
19886
|
summaries.push(await this.summarizeConversation(refreshed, snapshot));
|
|
19843
19887
|
}
|
|
19844
19888
|
return summaries.sort(
|
|
@@ -19846,10 +19890,10 @@ var ConversationQueryCoordinator = class {
|
|
|
19846
19890
|
);
|
|
19847
19891
|
}
|
|
19848
19892
|
async listConversationPageFromStore(input) {
|
|
19849
|
-
const all = await this.listConversationsFromStore(
|
|
19850
|
-
input.
|
|
19851
|
-
input.
|
|
19852
|
-
);
|
|
19893
|
+
const all = await this.listConversationsFromStore(input.status, {
|
|
19894
|
+
workspace: input.workspace,
|
|
19895
|
+
category: input.category
|
|
19896
|
+
});
|
|
19853
19897
|
const startIndex = input.cursor ? all.findIndex(
|
|
19854
19898
|
(summary) => isAfterConversationListCursor(summary, input.cursor)
|
|
19855
19899
|
) : 0;
|
|
@@ -19868,7 +19912,7 @@ var ConversationQueryCoordinator = class {
|
|
|
19868
19912
|
}
|
|
19869
19913
|
};
|
|
19870
19914
|
}
|
|
19871
|
-
async summarizeIndexedConversations(status, records) {
|
|
19915
|
+
async summarizeIndexedConversations(status, records, category = "all") {
|
|
19872
19916
|
const summaries = [];
|
|
19873
19917
|
for (const record of records) {
|
|
19874
19918
|
const manifest = await this.deps.store.readManifest(record.conversationId).catch(() => null);
|
|
@@ -19877,6 +19921,9 @@ var ConversationQueryCoordinator = class {
|
|
|
19877
19921
|
}
|
|
19878
19922
|
const snapshot = await this.deps.store.readSnapshot(record.conversationId).catch(() => emptySnapshot2());
|
|
19879
19923
|
const refreshed = await this.deps.metadata.refreshTitleFromHermes(manifest, { snapshot }).catch(() => manifest);
|
|
19924
|
+
if (!matchesConversationCategory(refreshed, snapshot, category)) {
|
|
19925
|
+
continue;
|
|
19926
|
+
}
|
|
19880
19927
|
summaries.push(await this.summarizeConversation(refreshed, snapshot));
|
|
19881
19928
|
}
|
|
19882
19929
|
return summaries;
|
|
@@ -20145,6 +20192,13 @@ function matchesWorkspaceFilter(manifest, filter) {
|
|
|
20145
20192
|
}
|
|
20146
20193
|
return workspaceId === filter.workspaceId;
|
|
20147
20194
|
}
|
|
20195
|
+
function matchesConversationCategory(manifest, snapshot, category = "all") {
|
|
20196
|
+
if (category === "all") {
|
|
20197
|
+
return true;
|
|
20198
|
+
}
|
|
20199
|
+
const isCron = manifestHasCronSignal(manifest, snapshot);
|
|
20200
|
+
return category === "cron" ? isCron : !isCron;
|
|
20201
|
+
}
|
|
20148
20202
|
function isAfterConversationListCursor(summary, cursor) {
|
|
20149
20203
|
return summary.updated_at < cursor.updatedAt || summary.updated_at === cursor.updatedAt && summary.id < cursor.conversationId;
|
|
20150
20204
|
}
|
|
@@ -21392,6 +21446,7 @@ async function syncHermesSessionsIntoConversations(paths, logger, options = {})
|
|
|
21392
21446
|
eligible_sessions: 0,
|
|
21393
21447
|
imported_count: 0,
|
|
21394
21448
|
reprojected_count: 0,
|
|
21449
|
+
hermes_archived_count: 0,
|
|
21395
21450
|
skipped_existing: 0,
|
|
21396
21451
|
skipped_hidden: 0,
|
|
21397
21452
|
skipped_empty_transcript: 0,
|
|
@@ -21417,6 +21472,21 @@ async function syncHermesSessionsIntoConversations(paths, logger, options = {})
|
|
|
21417
21472
|
continue;
|
|
21418
21473
|
}
|
|
21419
21474
|
if (isHiddenSession(session)) {
|
|
21475
|
+
if (isArchivedSession(session)) {
|
|
21476
|
+
const candidate = { profileName, profileDir, dbPath, session };
|
|
21477
|
+
result.hermes_archived_count += await archiveKnownHermesConversation({
|
|
21478
|
+
paths,
|
|
21479
|
+
store,
|
|
21480
|
+
knownHermesSessions,
|
|
21481
|
+
candidate
|
|
21482
|
+
}).catch((error) => {
|
|
21483
|
+
result.errors.push({
|
|
21484
|
+
profile: profileName,
|
|
21485
|
+
message: error instanceof Error ? error.message : String(error)
|
|
21486
|
+
});
|
|
21487
|
+
return 0;
|
|
21488
|
+
});
|
|
21489
|
+
}
|
|
21420
21490
|
result.skipped_hidden += 1;
|
|
21421
21491
|
continue;
|
|
21422
21492
|
}
|
|
@@ -21502,7 +21572,7 @@ async function syncHermesSessionsIntoConversations(paths, logger, options = {})
|
|
|
21502
21572
|
}
|
|
21503
21573
|
}
|
|
21504
21574
|
}
|
|
21505
|
-
if (result.imported_count > 0 || result.reprojected_count > 0 || result.errors.length > 0) {
|
|
21575
|
+
if (result.imported_count > 0 || result.reprojected_count > 0 || result.hermes_archived_count > 0 || result.errors.length > 0) {
|
|
21506
21576
|
void logger.info("hermes_session_sync_completed", { ...result });
|
|
21507
21577
|
} else {
|
|
21508
21578
|
void logger.debug("hermes_session_sync_completed", { ...result });
|
|
@@ -21613,15 +21683,6 @@ async function syncHermesConversationMessages(paths, logger, input) {
|
|
|
21613
21683
|
for (const candidate of candidates) {
|
|
21614
21684
|
try {
|
|
21615
21685
|
const candidateMessages = await readHermesLineageMessages(candidate);
|
|
21616
|
-
if (candidateMessages.length === 0) {
|
|
21617
|
-
continue;
|
|
21618
|
-
}
|
|
21619
|
-
const missingCandidateMessages = candidateMessages.filter(
|
|
21620
|
-
(message, index) => !isRepresentedHermesRow(representedRows, message, index)
|
|
21621
|
-
);
|
|
21622
|
-
if (missingCandidateMessages.length === 0) {
|
|
21623
|
-
continue;
|
|
21624
|
-
}
|
|
21625
21686
|
const profile = await resolveConversationProfileTarget(
|
|
21626
21687
|
paths,
|
|
21627
21688
|
candidate.profileName
|
|
@@ -21631,6 +21692,15 @@ async function syncHermesConversationMessages(paths, logger, input) {
|
|
|
21631
21692
|
profileUid: profile.profileUid,
|
|
21632
21693
|
profileName: profile.profileName
|
|
21633
21694
|
});
|
|
21695
|
+
if (candidateMessages.length === 0) {
|
|
21696
|
+
continue;
|
|
21697
|
+
}
|
|
21698
|
+
const missingCandidateMessages = candidateMessages.filter(
|
|
21699
|
+
(message, index) => !isRepresentedHermesRow(representedRows, message, index)
|
|
21700
|
+
);
|
|
21701
|
+
if (missingCandidateMessages.length === 0) {
|
|
21702
|
+
continue;
|
|
21703
|
+
}
|
|
21634
21704
|
projectedMessages.push(
|
|
21635
21705
|
...toLinkMessages({
|
|
21636
21706
|
conversationId: input.conversationId,
|
|
@@ -21664,13 +21734,36 @@ async function syncHermesConversationMessages(paths, logger, input) {
|
|
|
21664
21734
|
exactKeys.forEach((key) => knownExactKeys.add(key));
|
|
21665
21735
|
}
|
|
21666
21736
|
if (appendedMessages.length === 0) {
|
|
21737
|
+
let nextManifest2 = candidateProfiles.size > 0 || duplicateCleanup.removedCount > 0 ? await store.readManifest(input.conversationId) : null;
|
|
21738
|
+
const currentManifest = nextManifest2;
|
|
21739
|
+
if (nextManifest2) {
|
|
21740
|
+
for (const profile of candidateProfiles.values()) {
|
|
21741
|
+
nextManifest2 = mergeHermesLineageIntoManifest({
|
|
21742
|
+
manifest: nextManifest2,
|
|
21743
|
+
candidate: profile.candidate,
|
|
21744
|
+
snapshot,
|
|
21745
|
+
profileUid: profile.profileUid,
|
|
21746
|
+
profileName: profile.profileName,
|
|
21747
|
+
updatedAt: isoFromHermesTime(profile.candidate.session.last_active) ?? nextManifest2.updated_at
|
|
21748
|
+
});
|
|
21749
|
+
}
|
|
21750
|
+
if (duplicateCleanup.removedCount > 0) {
|
|
21751
|
+
nextManifest2 = {
|
|
21752
|
+
...nextManifest2,
|
|
21753
|
+
stats: buildConversationStats(nextManifest2, snapshot)
|
|
21754
|
+
};
|
|
21755
|
+
}
|
|
21756
|
+
}
|
|
21667
21757
|
if (duplicateCleanup.removedCount > 0) {
|
|
21668
21758
|
await store.writeSnapshot(input.conversationId, snapshot);
|
|
21669
|
-
|
|
21670
|
-
|
|
21671
|
-
const
|
|
21759
|
+
}
|
|
21760
|
+
if (nextManifest2 && currentManifest && (duplicateCleanup.removedCount > 0 || !manifestEquivalent(currentManifest, nextManifest2))) {
|
|
21761
|
+
const stats2 = buildConversationStats(nextManifest2, snapshot);
|
|
21762
|
+
nextManifest2 = { ...nextManifest2, stats: stats2 };
|
|
21672
21763
|
await store.writeManifest(nextManifest2);
|
|
21673
21764
|
await upsertConversationStats(paths, toStatsIndexRecord(nextManifest2, stats2));
|
|
21765
|
+
result.hermes_session_ids = manifestHermesSessionIds(nextManifest2);
|
|
21766
|
+
result.changed = true;
|
|
21674
21767
|
}
|
|
21675
21768
|
return result;
|
|
21676
21769
|
}
|
|
@@ -21711,6 +21804,7 @@ async function syncHermesConversationMessages(paths, logger, input) {
|
|
|
21711
21804
|
};
|
|
21712
21805
|
await store.writeManifest(nextManifest);
|
|
21713
21806
|
await upsertConversationStats(paths, toStatsIndexRecord(nextManifest, stats));
|
|
21807
|
+
result.hermes_session_ids = manifestHermesSessionIds(nextManifest);
|
|
21714
21808
|
const appendEvent = input.appendEvent ?? ((conversationId, event) => store.appendEvent(conversationId, event));
|
|
21715
21809
|
let lastEventSeq;
|
|
21716
21810
|
for (const message of hydratedAppendedMessages) {
|
|
@@ -21769,7 +21863,7 @@ async function importHermesSession(input) {
|
|
|
21769
21863
|
id: conversationId,
|
|
21770
21864
|
schema_version: 1,
|
|
21771
21865
|
kind: "direct",
|
|
21772
|
-
title:
|
|
21866
|
+
title: normalizeTitle2(title),
|
|
21773
21867
|
title_source: title ? "hermes" : "default",
|
|
21774
21868
|
status: "active",
|
|
21775
21869
|
hermes_session_id: sessionId,
|
|
@@ -22412,6 +22506,16 @@ function lineageSessionIds(candidate) {
|
|
|
22412
22506
|
candidate.session.id
|
|
22413
22507
|
]);
|
|
22414
22508
|
}
|
|
22509
|
+
function manifestHermesSessionIds(manifest) {
|
|
22510
|
+
return normalizeSessionIds([
|
|
22511
|
+
manifest.hermes_session_id,
|
|
22512
|
+
...manifest.hermes_session_ids ?? [],
|
|
22513
|
+
manifest.hermes_lineage?.root_session_id,
|
|
22514
|
+
manifest.hermes_lineage?.current_session_id,
|
|
22515
|
+
...manifest.hermes_lineage?.session_ids ?? [],
|
|
22516
|
+
...Object.values(manifest.profile_session_ids ?? {})
|
|
22517
|
+
]);
|
|
22518
|
+
}
|
|
22415
22519
|
function lineageTitle(candidate) {
|
|
22416
22520
|
const explicitLineageTitle = normalizeOptionalTitle(
|
|
22417
22521
|
candidate.session._lineage_title
|
|
@@ -22489,6 +22593,59 @@ async function readExistingHermesConversations(store, conversationIds) {
|
|
|
22489
22593
|
}
|
|
22490
22594
|
return conversations;
|
|
22491
22595
|
}
|
|
22596
|
+
async function archiveKnownHermesConversation(input) {
|
|
22597
|
+
const conversationIds = findKnownConversationIdsForCandidate(
|
|
22598
|
+
input.knownHermesSessions,
|
|
22599
|
+
input.candidate
|
|
22600
|
+
);
|
|
22601
|
+
if (conversationIds.length === 0) {
|
|
22602
|
+
return 0;
|
|
22603
|
+
}
|
|
22604
|
+
const archivedAt = readString12(input.candidate.session, "archived_at") ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
22605
|
+
let archivedCount = 0;
|
|
22606
|
+
for (const conversation of await readExistingHermesConversations(
|
|
22607
|
+
input.store,
|
|
22608
|
+
conversationIds
|
|
22609
|
+
)) {
|
|
22610
|
+
if (conversation.manifest.status !== "active") {
|
|
22611
|
+
continue;
|
|
22612
|
+
}
|
|
22613
|
+
const nextManifest = {
|
|
22614
|
+
...conversation.manifest,
|
|
22615
|
+
status: "archived",
|
|
22616
|
+
updated_at: archivedAt,
|
|
22617
|
+
archived_at: archivedAt,
|
|
22618
|
+
hermes_session_ids: normalizeSessionIds([
|
|
22619
|
+
conversation.manifest.hermes_session_id,
|
|
22620
|
+
...conversation.manifest.hermes_session_ids ?? [],
|
|
22621
|
+
...conversation.manifest.hermes_lineage?.session_ids ?? [],
|
|
22622
|
+
...lineageSessionIds(input.candidate)
|
|
22623
|
+
]),
|
|
22624
|
+
...lineageManifestPatch(input.candidate)
|
|
22625
|
+
};
|
|
22626
|
+
const stats = buildConversationStats(nextManifest, conversation.snapshot);
|
|
22627
|
+
const archivedManifest = { ...nextManifest, stats };
|
|
22628
|
+
await input.store.writeManifest(archivedManifest);
|
|
22629
|
+
await input.store.appendEvent(conversation.conversationId, {
|
|
22630
|
+
type: "conversation.archived",
|
|
22631
|
+
payload: {
|
|
22632
|
+
archived_at: archivedAt,
|
|
22633
|
+
source: "hermes",
|
|
22634
|
+
hermes_session_id: input.candidate.session.id
|
|
22635
|
+
}
|
|
22636
|
+
});
|
|
22637
|
+
const eventManifest = await input.store.readManifest(conversation.conversationId);
|
|
22638
|
+
const eventStats = buildConversationStats(eventManifest, conversation.snapshot);
|
|
22639
|
+
const indexedManifest = { ...eventManifest, stats: eventStats };
|
|
22640
|
+
await input.store.writeManifest(indexedManifest);
|
|
22641
|
+
await upsertConversationStats(
|
|
22642
|
+
input.paths,
|
|
22643
|
+
toStatsIndexRecord(indexedManifest, eventStats)
|
|
22644
|
+
);
|
|
22645
|
+
archivedCount += 1;
|
|
22646
|
+
}
|
|
22647
|
+
return archivedCount;
|
|
22648
|
+
}
|
|
22492
22649
|
function selectCanonicalHermesConversation(conversations) {
|
|
22493
22650
|
const ranked = [...conversations].sort((left, right) => {
|
|
22494
22651
|
const rightScore = canonicalConversationScore(right);
|
|
@@ -22601,7 +22758,7 @@ function mergeHermesLineageIntoManifest(input) {
|
|
|
22601
22758
|
};
|
|
22602
22759
|
const title = deriveHermesConversationTitle(input.candidate, input.snapshot);
|
|
22603
22760
|
if (title && canSyncHermesTitle(input.manifest)) {
|
|
22604
|
-
nextBase.title =
|
|
22761
|
+
nextBase.title = normalizeTitle2(title);
|
|
22605
22762
|
nextBase.title_source = "hermes";
|
|
22606
22763
|
}
|
|
22607
22764
|
const baseStats = buildConversationStats(nextBase, input.snapshot);
|
|
@@ -23640,7 +23797,7 @@ function senderForRole(input) {
|
|
|
23640
23797
|
function firstUserText(snapshot) {
|
|
23641
23798
|
return snapshot.messages.find((message) => message.role === "user")?.parts.find((part) => part.type === "text")?.text?.slice(0, 80);
|
|
23642
23799
|
}
|
|
23643
|
-
function
|
|
23800
|
+
function normalizeTitle2(value) {
|
|
23644
23801
|
const normalized = value?.replace(/\s+/gu, " ").trim();
|
|
23645
23802
|
return normalized || DEFAULT_CONVERSATION_TITLE;
|
|
23646
23803
|
}
|
|
@@ -23753,6 +23910,10 @@ function isHiddenSession(session) {
|
|
|
23753
23910
|
const visibility = readString12(session, "visibility")?.toLowerCase();
|
|
23754
23911
|
return Boolean(source && HIDDEN_SESSION_SOURCES.has(source)) || readBoolean2(session.hidden) || readBoolean2(session.archived) || Boolean(readString12(session, "archived_at")) || status === "hidden" || status === "archived" || visibility === "hidden" || visibility === "hide";
|
|
23755
23912
|
}
|
|
23913
|
+
function isArchivedSession(session) {
|
|
23914
|
+
const status = readString12(session, "status")?.toLowerCase();
|
|
23915
|
+
return readBoolean2(session.archived) || Boolean(readString12(session, "archived_at")) || status === "archived";
|
|
23916
|
+
}
|
|
23756
23917
|
function readTableColumns2(db, tableName) {
|
|
23757
23918
|
try {
|
|
23758
23919
|
const rows = db.prepare(`PRAGMA table_info(${quoteIdentifier(tableName)})`).all();
|
|
@@ -29546,6 +29707,123 @@ ${resolved}` : resolved;
|
|
|
29546
29707
|
return true;
|
|
29547
29708
|
});
|
|
29548
29709
|
}
|
|
29710
|
+
async repairStaleAutomaticContextCompression(conversationId) {
|
|
29711
|
+
const terminalRepaired = await this.deps.withConversationLock(
|
|
29712
|
+
conversationId,
|
|
29713
|
+
async () => {
|
|
29714
|
+
const snapshot = await this.deps.readSnapshot(conversationId);
|
|
29715
|
+
const cleared = [];
|
|
29716
|
+
for (const run of snapshot.runs) {
|
|
29717
|
+
const status = staleAutomaticContextCompressionStatus(run);
|
|
29718
|
+
if (!status) {
|
|
29719
|
+
continue;
|
|
29720
|
+
}
|
|
29721
|
+
const result = this.clearAutomaticContextCompressionInSnapshot({
|
|
29722
|
+
snapshot,
|
|
29723
|
+
run,
|
|
29724
|
+
status,
|
|
29725
|
+
completedAt: run.completed_at ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
29726
|
+
});
|
|
29727
|
+
if (result) {
|
|
29728
|
+
cleared.push({ run, ...result });
|
|
29729
|
+
}
|
|
29730
|
+
}
|
|
29731
|
+
if (cleared.length === 0) {
|
|
29732
|
+
return false;
|
|
29733
|
+
}
|
|
29734
|
+
await this.deps.writeSnapshot(conversationId, snapshot);
|
|
29735
|
+
for (const item of cleared) {
|
|
29736
|
+
await this.appendAutomaticContextCompressionClearedEvent({
|
|
29737
|
+
conversationId,
|
|
29738
|
+
run: item.run,
|
|
29739
|
+
marker: item.marker,
|
|
29740
|
+
metadata: item.metadata
|
|
29741
|
+
});
|
|
29742
|
+
}
|
|
29743
|
+
await this.deps.persistConversationStats(conversationId, snapshot);
|
|
29744
|
+
return true;
|
|
29745
|
+
}
|
|
29746
|
+
);
|
|
29747
|
+
const detachedRunningRepaired = await this.repairDetachedRunningAutomaticContextCompression(conversationId);
|
|
29748
|
+
return terminalRepaired || detachedRunningRepaired;
|
|
29749
|
+
}
|
|
29750
|
+
async repairDetachedRunningAutomaticContextCompression(conversationId) {
|
|
29751
|
+
const snapshot = await this.deps.readSnapshot(conversationId).catch(() => null);
|
|
29752
|
+
const candidates = snapshot?.runs.filter(
|
|
29753
|
+
(run) => isRunningAutomaticContextCompression(run) && !this.deps.activeRunControllers.has(run.id)
|
|
29754
|
+
) ?? [];
|
|
29755
|
+
let repaired = false;
|
|
29756
|
+
for (const run of candidates) {
|
|
29757
|
+
if (this.deps.activeRunControllers.has(run.id)) {
|
|
29758
|
+
continue;
|
|
29759
|
+
}
|
|
29760
|
+
const previousSessionId = run.context_compression?.previous_session_id?.trim() || run.hermes_session_id;
|
|
29761
|
+
const nextSessionId = await readHermesCompressionTip(
|
|
29762
|
+
previousSessionId,
|
|
29763
|
+
this.deps.paths,
|
|
29764
|
+
run.profile
|
|
29765
|
+
).catch(() => void 0);
|
|
29766
|
+
if (!nextSessionId || nextSessionId === previousSessionId) {
|
|
29767
|
+
continue;
|
|
29768
|
+
}
|
|
29769
|
+
const finished = await this.finishAutomaticContextCompressionFromEvent({
|
|
29770
|
+
conversationId,
|
|
29771
|
+
runId: run.id,
|
|
29772
|
+
previousSessionId,
|
|
29773
|
+
nextSessionId
|
|
29774
|
+
});
|
|
29775
|
+
if (!finished) {
|
|
29776
|
+
continue;
|
|
29777
|
+
}
|
|
29778
|
+
await this.completeRun(conversationId, run.id);
|
|
29779
|
+
repaired = true;
|
|
29780
|
+
}
|
|
29781
|
+
return repaired;
|
|
29782
|
+
}
|
|
29783
|
+
clearAutomaticContextCompressionInSnapshot(input) {
|
|
29784
|
+
if (input.run.context_compression?.status !== "compressing" || input.run.context_compression.source !== "auto") {
|
|
29785
|
+
return null;
|
|
29786
|
+
}
|
|
29787
|
+
const marker = findContextCompressionMarker(
|
|
29788
|
+
input.snapshot,
|
|
29789
|
+
input.run.context_compression.operation_id
|
|
29790
|
+
);
|
|
29791
|
+
if (!marker) {
|
|
29792
|
+
return null;
|
|
29793
|
+
}
|
|
29794
|
+
const metadata = {
|
|
29795
|
+
...input.run.context_compression,
|
|
29796
|
+
status: input.status,
|
|
29797
|
+
completed_at: input.completedAt
|
|
29798
|
+
};
|
|
29799
|
+
input.run.context_compression = metadata;
|
|
29800
|
+
updateContextCompressionMarker({
|
|
29801
|
+
message: marker,
|
|
29802
|
+
metadata,
|
|
29803
|
+
language: input.run.language === "en" ? "en" : "zh-CN"
|
|
29804
|
+
});
|
|
29805
|
+
return { marker, metadata };
|
|
29806
|
+
}
|
|
29807
|
+
async appendAutomaticContextCompressionClearedEvent(input) {
|
|
29808
|
+
await this.deps.appendEvent(input.conversationId, {
|
|
29809
|
+
type: contextCompressionClearedEventType(input.metadata.status),
|
|
29810
|
+
message_id: input.marker.id,
|
|
29811
|
+
run_id: input.run.id,
|
|
29812
|
+
payload: {
|
|
29813
|
+
message: input.marker,
|
|
29814
|
+
run: input.run,
|
|
29815
|
+
operation: input.metadata,
|
|
29816
|
+
automatic: true,
|
|
29817
|
+
session_rotated: false
|
|
29818
|
+
},
|
|
29819
|
+
...input.event ? {
|
|
29820
|
+
raw: {
|
|
29821
|
+
format: "hermes-run-event",
|
|
29822
|
+
payload: input.event.rawPayload
|
|
29823
|
+
}
|
|
29824
|
+
} : {}
|
|
29825
|
+
});
|
|
29826
|
+
}
|
|
29549
29827
|
async runHasAssistantOutput(conversationId, runId) {
|
|
29550
29828
|
const snapshot = await this.deps.readSnapshot(conversationId).catch(() => null);
|
|
29551
29829
|
const run = snapshot?.runs.find((item) => item.id === runId);
|
|
@@ -30109,6 +30387,12 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
30109
30387
|
);
|
|
30110
30388
|
}
|
|
30111
30389
|
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
30390
|
+
const clearedCompression = this.clearAutomaticContextCompressionInSnapshot({
|
|
30391
|
+
snapshot,
|
|
30392
|
+
run,
|
|
30393
|
+
status: "completed",
|
|
30394
|
+
completedAt
|
|
30395
|
+
});
|
|
30112
30396
|
const usage = readUsage(source?.payload);
|
|
30113
30397
|
const responseId = readResponseId(source?.payload);
|
|
30114
30398
|
const hermesRunId = readRunId(source?.payload);
|
|
@@ -30209,6 +30493,15 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
30209
30493
|
runId,
|
|
30210
30494
|
expiredInputRequests
|
|
30211
30495
|
);
|
|
30496
|
+
if (clearedCompression) {
|
|
30497
|
+
await this.appendAutomaticContextCompressionClearedEvent({
|
|
30498
|
+
conversationId,
|
|
30499
|
+
run,
|
|
30500
|
+
marker: clearedCompression.marker,
|
|
30501
|
+
metadata: clearedCompression.metadata,
|
|
30502
|
+
event: source
|
|
30503
|
+
});
|
|
30504
|
+
}
|
|
30212
30505
|
if (assistant) {
|
|
30213
30506
|
await this.deps.appendEvent(conversationId, {
|
|
30214
30507
|
type: "message.completed",
|
|
@@ -30285,6 +30578,12 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
30285
30578
|
}
|
|
30286
30579
|
run.status = "failed";
|
|
30287
30580
|
run.completed_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
30581
|
+
const clearedCompression = this.clearAutomaticContextCompressionInSnapshot({
|
|
30582
|
+
snapshot,
|
|
30583
|
+
run,
|
|
30584
|
+
status: "failed",
|
|
30585
|
+
completedAt: run.completed_at
|
|
30586
|
+
});
|
|
30288
30587
|
run.error_message = message;
|
|
30289
30588
|
run.error_detail = source ? readErrorMessage4(source.payload) ?? void 0 : void 0;
|
|
30290
30589
|
const language = run.language === "en" ? "en" : "zh-CN";
|
|
@@ -30335,6 +30634,15 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
30335
30634
|
runId,
|
|
30336
30635
|
expiredInputRequests
|
|
30337
30636
|
);
|
|
30637
|
+
if (clearedCompression) {
|
|
30638
|
+
await this.appendAutomaticContextCompressionClearedEvent({
|
|
30639
|
+
conversationId,
|
|
30640
|
+
run,
|
|
30641
|
+
marker: clearedCompression.marker,
|
|
30642
|
+
metadata: clearedCompression.metadata,
|
|
30643
|
+
event: source
|
|
30644
|
+
});
|
|
30645
|
+
}
|
|
30338
30646
|
const contextUsage = contextUsagePayloadForSnapshot(snapshot);
|
|
30339
30647
|
await this.deps.appendEvent(conversationId, {
|
|
30340
30648
|
type: "run.failed",
|
|
@@ -30563,6 +30871,12 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
30563
30871
|
const cancelledAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
30564
30872
|
run.status = "cancelled";
|
|
30565
30873
|
run.completed_at = cancelledAt;
|
|
30874
|
+
const clearedCompression = this.clearAutomaticContextCompressionInSnapshot({
|
|
30875
|
+
snapshot,
|
|
30876
|
+
run,
|
|
30877
|
+
status: "cancelled",
|
|
30878
|
+
completedAt: cancelledAt
|
|
30879
|
+
});
|
|
30566
30880
|
run.error_message = options.reason;
|
|
30567
30881
|
const cancelledByGoalPause = options.reason === "goal paused by app";
|
|
30568
30882
|
const assistant = snapshot.messages.find(
|
|
@@ -30604,6 +30918,14 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
30604
30918
|
runId,
|
|
30605
30919
|
cancelledInputRequests
|
|
30606
30920
|
);
|
|
30921
|
+
if (clearedCompression) {
|
|
30922
|
+
await this.appendAutomaticContextCompressionClearedEvent({
|
|
30923
|
+
conversationId,
|
|
30924
|
+
run,
|
|
30925
|
+
marker: clearedCompression.marker,
|
|
30926
|
+
metadata: clearedCompression.metadata
|
|
30927
|
+
});
|
|
30928
|
+
}
|
|
30607
30929
|
if (assistant) {
|
|
30608
30930
|
await this.deps.appendEvent(conversationId, {
|
|
30609
30931
|
type: "message.completed",
|
|
@@ -30856,6 +31178,36 @@ function contextCompressionGeneration(message) {
|
|
|
30856
31178
|
function readPreviousSessionId(payload) {
|
|
30857
31179
|
return readString21(payload, "old_session_id") ?? readString21(payload, "oldSessionId") ?? readString21(payload, "previous_session_id") ?? readString21(payload, "previousSessionId");
|
|
30858
31180
|
}
|
|
31181
|
+
function contextCompressionClearedEventType(status) {
|
|
31182
|
+
if (status === "completed") {
|
|
31183
|
+
return "context_compression.completed";
|
|
31184
|
+
}
|
|
31185
|
+
if (status === "cancelled") {
|
|
31186
|
+
return "context_compression.cancelled";
|
|
31187
|
+
}
|
|
31188
|
+
if (status === "timed_out") {
|
|
31189
|
+
return "context_compression.timed_out";
|
|
31190
|
+
}
|
|
31191
|
+
return "context_compression.failed";
|
|
31192
|
+
}
|
|
31193
|
+
function staleAutomaticContextCompressionStatus(run) {
|
|
31194
|
+
if (run.context_compression?.status !== "compressing" || run.context_compression.source !== "auto") {
|
|
31195
|
+
return null;
|
|
31196
|
+
}
|
|
31197
|
+
if (run.status === "completed") {
|
|
31198
|
+
return "completed";
|
|
31199
|
+
}
|
|
31200
|
+
if (run.status === "failed") {
|
|
31201
|
+
return "failed";
|
|
31202
|
+
}
|
|
31203
|
+
if (run.status === "cancelled") {
|
|
31204
|
+
return "cancelled";
|
|
31205
|
+
}
|
|
31206
|
+
return null;
|
|
31207
|
+
}
|
|
31208
|
+
function isRunningAutomaticContextCompression(run) {
|
|
31209
|
+
return run.status === "running" && run.context_compression?.status === "compressing" && run.context_compression.source === "auto";
|
|
31210
|
+
}
|
|
30859
31211
|
function readNextSessionId(payload) {
|
|
30860
31212
|
const info = toRecord19(payload.info);
|
|
30861
31213
|
return readString21(payload, "session_key") ?? readString21(payload, "sessionKey") ?? readString21(payload, "stored_session_id") ?? readString21(payload, "storedSessionId") ?? readString21(payload, "session_id") ?? readString21(payload, "sessionId") ?? readString21(info, "session_key") ?? readString21(info, "sessionKey");
|
|
@@ -32167,6 +32519,11 @@ var ConversationService = class {
|
|
|
32167
32519
|
}
|
|
32168
32520
|
);
|
|
32169
32521
|
}
|
|
32522
|
+
if (!options.beforeMessageId) {
|
|
32523
|
+
await this.runLifecycle.repairStaleAutomaticContextCompression(
|
|
32524
|
+
conversationId
|
|
32525
|
+
);
|
|
32526
|
+
}
|
|
32170
32527
|
return this.queries.getMessages(conversationId, options);
|
|
32171
32528
|
}
|
|
32172
32529
|
async setConversationModel(conversationId, input) {
|
|
@@ -32215,7 +32572,7 @@ var ConversationService = class {
|
|
|
32215
32572
|
);
|
|
32216
32573
|
}
|
|
32217
32574
|
const shouldApplyModelConfig = configuredModel != null && !runtimeMatchesConfiguredModel(currentRuntime, configuredModel);
|
|
32218
|
-
const modelConfigToApply =
|
|
32575
|
+
const modelConfigToApply = configuredModel ?? void 0;
|
|
32219
32576
|
const sessionConfig = await setTuiGatewaySessionModelConfig({
|
|
32220
32577
|
hermesSessionId: manifest.hermes_session_id,
|
|
32221
32578
|
profileName: currentRuntime.profile.name,
|
|
@@ -34553,7 +34910,8 @@ function registerConversationRoutes(router, options) {
|
|
|
34553
34910
|
const result = await conversations.listConversationPage({
|
|
34554
34911
|
limit: readLimit(ctx.query.limit),
|
|
34555
34912
|
cursor,
|
|
34556
|
-
workspace: readConversationWorkspaceFilter(ctx.query)
|
|
34913
|
+
workspace: readConversationWorkspaceFilter(ctx.query),
|
|
34914
|
+
category: readConversationCategoryFilter(ctx.query)
|
|
34557
34915
|
});
|
|
34558
34916
|
const localized = localizeConversationListPage(result, language);
|
|
34559
34917
|
ctx.body = {
|
|
@@ -35284,6 +35642,21 @@ function readConversationWorkspaceFilter(query) {
|
|
|
35284
35642
|
}
|
|
35285
35643
|
return { kind: "workspace", workspaceId: workspace };
|
|
35286
35644
|
}
|
|
35645
|
+
function readConversationCategoryFilter(query) {
|
|
35646
|
+
const category = readQueryString(query.category) ?? readQueryString(query.conversation_category) ?? readQueryString(query.conversationCategory);
|
|
35647
|
+
const normalized = category?.trim().toLowerCase();
|
|
35648
|
+
if (!normalized || normalized === "all") {
|
|
35649
|
+
return "all";
|
|
35650
|
+
}
|
|
35651
|
+
if (normalized === "chat" || normalized === "cron") {
|
|
35652
|
+
return normalized;
|
|
35653
|
+
}
|
|
35654
|
+
throw new LinkHttpError(
|
|
35655
|
+
400,
|
|
35656
|
+
"conversation_category_invalid",
|
|
35657
|
+
"conversation category must be all, chat or cron"
|
|
35658
|
+
);
|
|
35659
|
+
}
|
|
35287
35660
|
function readConversationWorkspaceId(body) {
|
|
35288
35661
|
if (Object.prototype.hasOwnProperty.call(body, "workspace_id") || Object.prototype.hasOwnProperty.call(body, "workspaceId")) {
|
|
35289
35662
|
return readString23(body, "workspace_id") ?? readString23(body, "workspaceId");
|
|
@@ -46206,6 +46579,7 @@ function registerSystemRoutes(router, options) {
|
|
|
46206
46579
|
conversation_delete: true,
|
|
46207
46580
|
conversation_bulk_delete: true,
|
|
46208
46581
|
conversation_clear_plan: true,
|
|
46582
|
+
conversation_categories: true,
|
|
46209
46583
|
conversation_cancel: true,
|
|
46210
46584
|
conversation_queue_controls: true,
|
|
46211
46585
|
conversation_queue_limit: MAX_CONVERSATION_QUEUED_RUNS,
|
package/dist/cli/index.js
CHANGED
package/dist/http/app.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ interface ConversationSummary {
|
|
|
12
12
|
title: string;
|
|
13
13
|
display_title?: string;
|
|
14
14
|
title_source?: ConversationTitleSource;
|
|
15
|
+
category?: ConversationCategory;
|
|
16
|
+
is_cron?: boolean;
|
|
15
17
|
workspace_id?: string | null;
|
|
16
18
|
created_at: string;
|
|
17
19
|
updated_at: string;
|
|
@@ -31,6 +33,7 @@ interface ConversationSummary {
|
|
|
31
33
|
content_preview: string;
|
|
32
34
|
} | null;
|
|
33
35
|
}
|
|
36
|
+
type ConversationCategory = "chat" | "cron";
|
|
34
37
|
interface ConversationListPageInfo {
|
|
35
38
|
limit: number;
|
|
36
39
|
has_more: boolean;
|
|
@@ -556,12 +559,21 @@ interface ConversationWorkspace {
|
|
|
556
559
|
updated_at: string;
|
|
557
560
|
}
|
|
558
561
|
|
|
562
|
+
type ConversationListCategory = 'all' | 'chat' | 'cron';
|
|
563
|
+
interface ConversationListPageOptions {
|
|
564
|
+
limit?: number;
|
|
565
|
+
cursor?: string;
|
|
566
|
+
workspace?: ConversationWorkspaceFilter;
|
|
567
|
+
category?: ConversationListCategory;
|
|
568
|
+
}
|
|
569
|
+
|
|
559
570
|
interface HermesSessionSyncResult {
|
|
560
571
|
scanned_profiles: number;
|
|
561
572
|
scanned_sessions: number;
|
|
562
573
|
eligible_sessions: number;
|
|
563
574
|
imported_count: number;
|
|
564
575
|
reprojected_count: number;
|
|
576
|
+
hermes_archived_count: number;
|
|
565
577
|
skipped_existing: number;
|
|
566
578
|
skipped_hidden: number;
|
|
567
579
|
skipped_empty_transcript: number;
|
|
@@ -618,11 +630,7 @@ declare class ConversationService {
|
|
|
618
630
|
private withConversationLock;
|
|
619
631
|
private restoreArchivedConversationForUserContinuationLocked;
|
|
620
632
|
listConversations(): Promise<ConversationSummary[]>;
|
|
621
|
-
listConversationPage(input?:
|
|
622
|
-
limit?: number;
|
|
623
|
-
cursor?: string;
|
|
624
|
-
workspace?: ConversationWorkspaceFilter;
|
|
625
|
-
}): Promise<ConversationListPage>;
|
|
633
|
+
listConversationPage(input?: ConversationListPageOptions): Promise<ConversationListPage>;
|
|
626
634
|
searchConversationPage(input?: {
|
|
627
635
|
limit?: number;
|
|
628
636
|
cursor?: string;
|
package/dist/http/app.js
CHANGED