metheus-governance-mcp-cli 0.2.264 → 0.2.265

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.
@@ -478,7 +478,7 @@ export function dedupeProcessableArchiveComments(records) {
478
478
  return deduped;
479
479
  }
480
480
 
481
- function isArchiveRecordAfterState(record, routeState) {
481
+ export function isArchiveRecordAfterState(record, routeState) {
482
482
  const state = safeObject(routeState);
483
483
  const lastCommentID = String(state.last_processed_comment_id || "").trim();
484
484
  const lastCreatedAt = String(state.last_processed_created_at || "").trim();
@@ -8,6 +8,7 @@ import {
8
8
  compareArchiveCommentRecords,
9
9
  dedupeProcessableArchiveComments,
10
10
  findRecentTelegramMessageEnvelope,
11
+ isArchiveRecordAfterState,
11
12
  isTelegramLocalInboundEnvelopeForRoute,
12
13
  isInboundArchiveKind,
13
14
  normalizeTelegramMessageEnvelope,
@@ -6305,11 +6306,18 @@ export function selectRunnerPendingWork({
6305
6306
  .map((record) => normalizeArchiveCommentRecord(record, parseArchivedChatComment))
6306
6307
  .filter((record) => record.id && record.parsedArchive)
6307
6308
  .sort(compareArchiveCommentRecords);
6309
+ const importedCommentRecords = ensureArray(importOutcome?.importedComments)
6310
+ .map((record) => normalizeArchiveCommentRecord(record, parseArchivedChatComment))
6311
+ .filter((record) => record.id && record.parsedArchive)
6312
+ .sort(compareArchiveCommentRecords);
6308
6313
  const inboundComments = orderedComments.filter((record) => isInboundArchiveKind(record.parsedArchive.kind));
6309
6314
  const importedRecords = dedupeProcessableArchiveComments(
6310
- ensureArray(importOutcome?.importedCommentIDs)
6311
- .map((commentID) => orderedComments.find((record) => record.id === commentID))
6312
- .filter(Boolean),
6315
+ [
6316
+ ...ensureArray(importOutcome?.importedCommentIDs)
6317
+ .map((commentID) => orderedComments.find((record) => record.id === commentID))
6318
+ .filter(Boolean),
6319
+ ...importedCommentRecords,
6320
+ ],
6313
6321
  );
6314
6322
  const hasCursor = Boolean(
6315
6323
  String(safeObject(refreshedState).active_comment_id || "").trim()
@@ -6324,6 +6332,18 @@ export function selectRunnerPendingWork({
6324
6332
  (record) => normalizeArchiveCommentRecord(record, parseArchivedChatComment),
6325
6333
  pendingSelectionOptions,
6326
6334
  );
6335
+ const laggingImportedRecords = importedRecords.filter((record) => (
6336
+ !orderedComments.some((orderedRecord) => String(orderedRecord.id || "").trim() === String(record.id || "").trim())
6337
+ ));
6338
+ const importedPendingAfterCursor = dedupeProcessableArchiveComments(
6339
+ laggingImportedRecords.filter((record) => isArchiveRecordAfterState(record, {
6340
+ ...safeObject(refreshedState),
6341
+ last_processed_comment_id: String(safeObject(refreshedState).active_comment_id || "").trim()
6342
+ || safeObject(refreshedState).last_processed_comment_id,
6343
+ last_processed_created_at: String(safeObject(refreshedState).active_comment_created_at || "").trim()
6344
+ || safeObject(refreshedState).last_processed_created_at,
6345
+ })),
6346
+ );
6327
6347
  const pending = importedRecords.length > 0 && !hasCursor
6328
6348
  ? applyPendingAgeSelection({
6329
6349
  ordered: orderedComments,
@@ -6331,6 +6351,13 @@ export function selectRunnerPendingWork({
6331
6351
  shouldPrime: false,
6332
6352
  pending: importedRecords,
6333
6353
  }, pendingSelectionOptions)
6354
+ : importedPendingAfterCursor.length > 0 && ensureArray(fullPending.pending).length === 0
6355
+ ? applyPendingAgeSelection({
6356
+ ordered: orderedComments,
6357
+ latest: inboundComments.length ? inboundComments[inboundComments.length - 1] : null,
6358
+ shouldPrime: false,
6359
+ pending: importedPendingAfterCursor,
6360
+ }, pendingSelectionOptions)
6334
6361
  : fullPending;
6335
6362
  return {
6336
6363
  orderedComments,
@@ -767,6 +767,7 @@ async function archiveRunnerTelegramInboundUpdates({
767
767
  }) {
768
768
  let handledUpdateID = 0;
769
769
  const importedCommentIDs = [];
770
+ const importedComments = [];
770
771
  const retainedSharedUpdates = [];
771
772
  for (let index = 0; index < mergedSharedUpdates.length; index += 1) {
772
773
  const update = mergedSharedUpdates[index];
@@ -799,17 +800,19 @@ async function archiveRunnerTelegramInboundUpdates({
799
800
  continue;
800
801
  }
801
802
  let createdComment;
803
+ let archiveBody = "";
802
804
  try {
805
+ archiveBody = formatTelegramInboundArchiveComment({
806
+ ...update,
807
+ archiveSourceOrigin: "telegram_archive_context",
808
+ });
803
809
  createdComment = await createThreadComment({
804
810
  siteBaseURL: runtime.baseURL,
805
811
  token: runtime.token,
806
812
  timeoutSeconds: runtime.timeoutSeconds,
807
813
  threadID: archiveThread.threadID,
808
814
  actorUserID: runtime.actor.user_id,
809
- body: formatTelegramInboundArchiveComment({
810
- ...update,
811
- archiveSourceOrigin: "telegram_archive_context",
812
- }),
815
+ body: archiveBody,
813
816
  });
814
817
  } catch (err) {
815
818
  releaseRunnerInboundArchiveMessageReservation(reservation.reservationKey);
@@ -822,6 +825,19 @@ async function archiveRunnerTelegramInboundUpdates({
822
825
  if (String(createdComment.id || "").trim()) {
823
826
  importedCommentIDs.push(String(createdComment.id || "").trim());
824
827
  }
828
+ importedComments.push({
829
+ ...safeObject(createdComment),
830
+ id: String(createdComment?.id || "").trim(),
831
+ body: archiveBody,
832
+ created_at: String(createdComment?.created_at || createdComment?.createdAt || new Date().toISOString()).trim(),
833
+ updated_at: String(
834
+ createdComment?.updated_at
835
+ || createdComment?.updatedAt
836
+ || createdComment?.created_at
837
+ || createdComment?.createdAt
838
+ || new Date().toISOString()
839
+ ).trim(),
840
+ });
825
841
  if (boolFromRaw(archivePolicy.dedupeInbound, true)) {
826
842
  existingKeys.add(dedupeKey);
827
843
  }
@@ -829,6 +845,7 @@ async function archiveRunnerTelegramInboundUpdates({
829
845
  return {
830
846
  handledUpdateID,
831
847
  importedCommentIDs,
848
+ importedComments,
832
849
  retainedSharedUpdates,
833
850
  };
834
851
  }
@@ -1168,6 +1185,7 @@ export async function archiveLocalTelegramMessagesForRoute({
1168
1185
  const {
1169
1186
  handledUpdateID,
1170
1187
  importedCommentIDs,
1188
+ importedComments,
1171
1189
  retainedSharedUpdates,
1172
1190
  } = await archiveRunnerTelegramInboundUpdates({
1173
1191
  mergedSharedUpdates,
@@ -1191,6 +1209,7 @@ export async function archiveLocalTelegramMessagesForRoute({
1191
1209
 
1192
1210
  return {
1193
1211
  importedCommentIDs,
1212
+ importedComments,
1194
1213
  importedCount: importedCommentIDs.length,
1195
1214
  lastUpdateID: Math.max(lastUpdateID, handledUpdateID),
1196
1215
  };
@@ -598,8 +598,8 @@ export async function runSelftestRunnerScenarios(push, deps) {
598
598
  );
599
599
  }
600
600
 
601
- try {
602
- const duplicateComments = [
601
+ try {
602
+ const duplicateComments = [
603
603
  {
604
604
  id: "dup-comment-earliest",
605
605
  createdAt: "2026-03-18T00:00:01.000Z",
@@ -636,15 +636,68 @@ export async function runSelftestRunnerScenarios(push, deps) {
636
636
  `pending=${pendingWork.pending.pending.map((item) => item.id).join(",") || "(none)"}`,
637
637
  );
638
638
  } catch (err) {
639
- push(
640
- "runner_pending_selection_does_not_requeue_imported_duplicate_after_cursor",
641
- false,
642
- String(err?.message || err),
643
- );
644
- }
645
-
646
- try {
647
- const selected = selectProjectChatDestination(
639
+ push(
640
+ "runner_pending_selection_does_not_requeue_imported_duplicate_after_cursor",
641
+ false,
642
+ String(err?.message || err),
643
+ );
644
+ }
645
+
646
+ try {
647
+ const pendingWork = selectRunnerPendingWork({
648
+ comments: [
649
+ {
650
+ id: "cursor-comment",
651
+ createdAt: "2026-03-18T00:00:01.000Z",
652
+ updatedAt: "2026-03-18T00:00:01.000Z",
653
+ parsedArchive: { kind: "telegram_message", chatID: "-1001", messageID: 353, body: "@RyoAI_bot first copy" },
654
+ },
655
+ ],
656
+ importOutcome: {
657
+ importedCommentIDs: ["fresh-import-comment"],
658
+ importedComments: [
659
+ {
660
+ id: "fresh-import-comment",
661
+ created_at: "2026-03-18T00:00:05.000Z",
662
+ updated_at: "2026-03-18T00:00:05.000Z",
663
+ body: "[Telegram message]\nchat_id: -1001\nmessage_id: 354\n\nfresh imported mention",
664
+ },
665
+ ],
666
+ },
667
+ refreshedState: {
668
+ last_processed_comment_id: "cursor-comment",
669
+ last_processed_created_at: "2026-03-18T00:00:01.000Z",
670
+ },
671
+ mode: "start",
672
+ parseArchivedChatComment,
673
+ deps: {
674
+ normalizeArchiveCommentRecord: (record, parser) => ({
675
+ id: String(record?.id || "").trim(),
676
+ body: String(record?.body || "").trim(),
677
+ createdAt: String(record?.created_at || record?.createdAt || record?.updated_at || record?.updatedAt || "").trim(),
678
+ updatedAt: String(record?.updated_at || record?.updatedAt || "").trim(),
679
+ parsedArchive: typeof parser === "function" ? parser(String(record?.body || "").trim()) : null,
680
+ }),
681
+ applyPendingAgeSelection: (selection) => selection,
682
+ },
683
+ pendingSelectionOptions: {},
684
+ });
685
+ push(
686
+ "runner_pending_selection_uses_fresh_imported_comment_when_thread_read_lags",
687
+ pendingWork.pending.pending.length === 1
688
+ && String(pendingWork.pending.pending[0]?.id || "").trim() === "fresh-import-comment",
689
+ `pending=${pendingWork.pending.pending.map((item) => item.id).join(",") || "(none)"}`,
690
+ );
691
+ } catch (err) {
692
+ push(
693
+ "runner_pending_selection_uses_fresh_imported_comment_when_thread_read_lags",
694
+ false,
695
+ String(err?.message || err),
696
+ );
697
+ }
698
+
699
+ try {
700
+ const selected = selectProjectChatDestination(
648
701
  [
649
702
  { id: "dest-1", provider: "telegram", label: "Main Room", chat_id: "-1001", is_active: true },
650
703
  { id: "dest-2", provider: "slack", label: "Slack", chat_id: "C123", is_active: true },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.264",
3
+ "version": "0.2.265",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [