cursorconnect 0.1.6 → 0.1.7

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 (51) hide show
  1. package/bridge-runtime/.env.example +10 -2
  2. package/bridge-runtime/connector-version.json +1 -1
  3. package/bridge-runtime/dist/agent-completion-push.d.ts +42 -0
  4. package/bridge-runtime/dist/agent-completion-push.js +220 -0
  5. package/bridge-runtime/dist/agent-title-match.d.ts +8 -7
  6. package/bridge-runtime/dist/agent-title-match.js +11 -1
  7. package/bridge-runtime/dist/chat-display-store.d.ts +21 -9
  8. package/bridge-runtime/dist/chat-display-store.js +94 -23
  9. package/bridge-runtime/dist/chat-display.d.ts +2 -0
  10. package/bridge-runtime/dist/chat-display.js +197 -33
  11. package/bridge-runtime/dist/chat-history-mode.d.ts +5 -0
  12. package/bridge-runtime/dist/chat-history-mode.js +7 -0
  13. package/bridge-runtime/dist/command-executor.d.ts +2 -0
  14. package/bridge-runtime/dist/command-executor.js +44 -0
  15. package/bridge-runtime/dist/composer-title-index.d.ts +1 -0
  16. package/bridge-runtime/dist/composer-title-index.js +7 -7
  17. package/bridge-runtime/dist/debug-chats-page.d.ts +2 -0
  18. package/bridge-runtime/dist/debug-chats-page.js +491 -0
  19. package/bridge-runtime/dist/dom-transcript-store.d.ts +17 -0
  20. package/bridge-runtime/dist/dom-transcript-store.js +76 -0
  21. package/bridge-runtime/dist/extract-page.js +56 -85
  22. package/bridge-runtime/dist/history-limit.d.ts +2 -0
  23. package/bridge-runtime/dist/history-limit.js +2 -0
  24. package/bridge-runtime/dist/history-request.d.ts +8 -0
  25. package/bridge-runtime/dist/history-request.js +7 -0
  26. package/bridge-runtime/dist/index.js +1 -0
  27. package/bridge-runtime/dist/jsonl-index.d.ts +21 -3
  28. package/bridge-runtime/dist/jsonl-index.js +237 -73
  29. package/bridge-runtime/dist/jsonl-live-debug.d.ts +24 -0
  30. package/bridge-runtime/dist/jsonl-live-debug.js +175 -0
  31. package/bridge-runtime/dist/media-path.d.ts +2 -0
  32. package/bridge-runtime/dist/media-path.js +17 -0
  33. package/bridge-runtime/dist/message-filter.d.ts +2 -0
  34. package/bridge-runtime/dist/message-filter.js +21 -5
  35. package/bridge-runtime/dist/pairing-code.d.ts +2 -0
  36. package/bridge-runtime/dist/pairing-code.js +9 -2
  37. package/bridge-runtime/dist/relay-upstream.d.ts +2 -1
  38. package/bridge-runtime/dist/relay-upstream.js +4 -1
  39. package/bridge-runtime/dist/relay.d.ts +21 -0
  40. package/bridge-runtime/dist/relay.js +332 -28
  41. package/bridge-runtime/dist/types.d.ts +21 -0
  42. package/bridge-runtime/selectors.json +4 -5
  43. package/dist/index.js +79 -20
  44. package/dist/launch.js +23 -5
  45. package/dist/macos-autostart.js +87 -0
  46. package/dist/pairing-code.js +12 -3
  47. package/dist/print-pairing.js +2 -0
  48. package/dist/run-service.js +31 -0
  49. package/dist/startup-check.js +165 -0
  50. package/package.json +1 -1
  51. package/version-policy.json +1 -1
@@ -523,7 +523,7 @@ export function extractionFunction(containerSelectors, tabSelectors, inputSelect
523
523
  const t = text.trim().replace(/\s+/g, ' ');
524
524
  if (!t || t.length > 100)
525
525
  return undefined;
526
- if (/will resume when|background work finishes|background shell|resuming when|paused while|waiting for background/i.test(t)) {
526
+ if (/will resume when|background work finishes|background shell|resuming when|paused while|waiting for background|waiting for \d+ command/i.test(t)) {
527
527
  return t;
528
528
  }
529
529
  if (/^working\.?$/i.test(t))
@@ -543,10 +543,27 @@ export function extractionFunction(containerSelectors, tabSelectors, inputSelect
543
543
  return undefined;
544
544
  return hit;
545
545
  }
546
+ function isUserFacingAssistantProse(text) {
547
+ const t = text.trim();
548
+ if (!t)
549
+ return false;
550
+ if (/^##\s+\S/m.test(t))
551
+ return true;
552
+ if (/^\*\*[^*]{2,}\*\*/m.test(t) && t.length >= 60)
553
+ return true;
554
+ const tableRows = t.match(/^\|[^\n]+\|$/gm);
555
+ if (tableRows && tableRows.length >= 2)
556
+ return true;
557
+ if (/^[-*]\s+\S/m.test(t) && t.length >= 100)
558
+ return true;
559
+ return false;
560
+ }
546
561
  function isAssistantReflectionText(text) {
547
562
  const t = text.trim().replace(/\s+/g, ' ');
548
563
  if (!t || isNoiseChatText(t))
549
564
  return true;
565
+ if (isUserFacingAssistantProse(t))
566
+ return false;
550
567
  if (/^Thought\s*for\s*\d/i.test(t) && t.length < 160)
551
568
  return true;
552
569
  if (/^(Exploring|Grepped|Searched|Listed|Read |Ran |Edited |Loading|Planning|Using image)/i.test(t)) {
@@ -555,10 +572,6 @@ export function extractionFunction(containerSelectors, tabSelectors, inputSelect
555
572
  const toolHits = t.match(/\b(Explored|Grepped|Searched|Listed|Read )\b/gi);
556
573
  if (toolHits && toolHits.length >= 2)
557
574
  return true;
558
- if (t.length > 160 &&
559
- /\b(state\.messages|flat-index|mapKeyedChildren|humanEl|extract-page|userMessagesEquivalent)\b/i.test(t)) {
560
- return true;
561
- }
562
575
  return false;
563
576
  }
564
577
  function isMeaningfulAssistantText(text) {
@@ -659,14 +672,17 @@ export function extractionFunction(containerSelectors, tabSelectors, inputSelect
659
672
  return true;
660
673
  return false;
661
674
  }
662
- let flatOrder = 0;
675
+ let domSeqCounter = 0;
663
676
  flatEls.forEach((el) => {
664
677
  const idxRaw = el.getAttribute('data-flat-index') ?? '';
665
678
  const idxParsed = Number.parseInt(idxRaw, 10);
666
- const flatIndex = Number.isFinite(idxParsed) ? idxParsed : flatOrder++;
679
+ const domSeq = domSeqCounter++;
680
+ // Sort/display: Cursor data-flat-index is the real timeline; domSeq only tie-breaks.
681
+ const attrFlatIndex = Number.isFinite(idxParsed) ? idxParsed : undefined;
682
+ const flatIndex = attrFlatIndex ?? domSeq;
667
683
  const role = el.getAttribute('data-message-role') ?? '';
668
684
  const kind = el.getAttribute('data-message-kind') ?? '';
669
- const id = `${idxRaw || flatIndex}-${role || 'x'}-${kind || 'x'}`;
685
+ const id = `${attrFlatIndex ?? flatIndex}-${role || 'x'}-${kind || 'x'}`;
670
686
  if (isToolNoiseElement(el, kind)) {
671
687
  recordDrop('tool', el);
672
688
  return;
@@ -702,6 +718,7 @@ export function extractionFunction(containerSelectors, tabSelectors, inputSelect
702
718
  text: displayText,
703
719
  images: images.length ? images : undefined,
704
720
  flatIndex,
721
+ domSeq,
705
722
  });
706
723
  messageDebug.extracted++;
707
724
  return;
@@ -731,10 +748,10 @@ export function extractionFunction(containerSelectors, tabSelectors, inputSelect
731
748
  text,
732
749
  html: mdRoot.innerHTML || undefined,
733
750
  flatIndex,
751
+ domSeq,
734
752
  });
735
753
  messageDebug.extracted++;
736
754
  });
737
- messages.sort((a, b) => (a.flatIndex ?? 0) - (b.flatIndex ?? 0));
738
755
  function compareUserText(text) {
739
756
  return cleanUserText(text).replace(/\s+/g, ' ').trim();
740
757
  }
@@ -853,6 +870,30 @@ export function extractionFunction(containerSelectors, tabSelectors, inputSelect
853
870
  }
854
871
  }
855
872
  const pendingApprovals = [];
873
+ function isVisibleButton(btn) {
874
+ const st = getComputedStyle(btn);
875
+ if (st.display === 'none' || st.visibility === 'hidden' || Number(st.opacity) < 0.05) {
876
+ return false;
877
+ }
878
+ const r = btn.getBoundingClientRect();
879
+ return r.width >= 2 && r.height >= 2;
880
+ }
881
+ /** Real tool Accept/Run — only explicit approval row (not chat status / "Run in background"). */
882
+ function isToolApprovalButton(btn) {
883
+ return !!btn.closest('.ui-shell-tool-call__approval-row');
884
+ }
885
+ function isDeniedApprovalLabel(label) {
886
+ const t = label.trim().toLowerCase();
887
+ if (!t)
888
+ return true;
889
+ if (t === 'run in background' || t.includes('run in background'))
890
+ return true;
891
+ if (/waiting for \d+ command/.test(t))
892
+ return true;
893
+ if (/will resume when|background work finishes|background shell/.test(t))
894
+ return true;
895
+ return false;
896
+ }
856
897
  function isMenuTrigger(btn) {
857
898
  const p = btn.getAttribute('aria-haspopup');
858
899
  return p === 'menu' || p === 'true' || p === 'listbox';
@@ -922,7 +963,7 @@ export function extractionFunction(containerSelectors, tabSelectors, inputSelect
922
963
  continue;
923
964
  const actions = [];
924
965
  const runBtn = row.querySelector('button.ui-shell-tool-call__run-btn');
925
- if (runBtn && !isMenuTrigger(runBtn)) {
966
+ if (runBtn && isVisibleButton(runBtn) && !isMenuTrigger(runBtn)) {
926
967
  actions.push({
927
968
  label: cleanLabel(runBtn.textContent || '') || 'Run',
928
969
  type: 'approve',
@@ -930,95 +971,25 @@ export function extractionFunction(containerSelectors, tabSelectors, inputSelect
930
971
  });
931
972
  }
932
973
  const skipBtn = row.querySelector('button.ui-shell-tool-call__skip-btn');
933
- if (skipBtn && !isMenuTrigger(skipBtn)) {
974
+ if (skipBtn && isVisibleButton(skipBtn) && !isMenuTrigger(skipBtn)) {
934
975
  actions.push({
935
976
  label: cleanLabel(skipBtn.textContent || '') || 'Skip',
936
977
  type: 'reject',
937
978
  selectorPath: buildSelectorPath(skipBtn),
938
979
  });
939
980
  }
940
- if (!actions.some((a) => a.type === 'approve'))
981
+ const approveAction = actions.find((a) => a.type === 'approve' || a.type === 'approve_all');
982
+ if (!approveAction || isDeniedApprovalLabel(approveAction.label))
941
983
  continue;
942
984
  const cmd = card.querySelector('.ui-shell-tool-call__command')?.textContent?.trim();
985
+ if (cmd && isDeniedApprovalLabel(cmd))
986
+ continue;
943
987
  pendingApprovals.push({
944
988
  id: `tool:${buildSelectorPath(card)}`,
945
989
  description: (cmd || 'Pending approval').slice(0, 240),
946
990
  actions,
947
991
  });
948
992
  }
949
- if (pendingApprovals.length === 0) {
950
- const approveBtns = [];
951
- const rejectBtns = [];
952
- const seenA = new Set();
953
- const seenR = new Set();
954
- for (const sel of approveSelectors) {
955
- try {
956
- for (const btn of Array.from(container.querySelectorAll(sel))) {
957
- if (seenA.has(btn) || isMenuTrigger(btn))
958
- continue;
959
- const label = btn.textContent?.trim() || btn.getAttribute('aria-label') || '';
960
- if (label) {
961
- seenA.add(btn);
962
- approveBtns.push({ label, selector: buildSelectorPath(btn) });
963
- }
964
- }
965
- }
966
- catch {
967
- /* skip */
968
- }
969
- }
970
- if (approveBtns.length === 0) {
971
- for (const btn of Array.from(container.querySelectorAll('button'))) {
972
- if (seenA.has(btn) || isMenuTrigger(btn))
973
- continue;
974
- const text = `${btn.textContent || ''} ${btn.getAttribute('aria-label') || ''}`.toLowerCase();
975
- for (const pat of approveTextMatch) {
976
- if (text.includes(pat.toLowerCase())) {
977
- seenA.add(btn);
978
- approveBtns.push({
979
- label: btn.textContent?.trim() || pat,
980
- selector: buildSelectorPath(btn),
981
- });
982
- break;
983
- }
984
- }
985
- }
986
- }
987
- for (const sel of rejectSelectors) {
988
- try {
989
- for (const btn of Array.from(container.querySelectorAll(sel))) {
990
- if (seenR.has(btn) || isMenuTrigger(btn))
991
- continue;
992
- const label = btn.textContent?.trim() || btn.getAttribute('aria-label') || '';
993
- if (label) {
994
- seenR.add(btn);
995
- rejectBtns.push({ label, selector: buildSelectorPath(btn) });
996
- }
997
- }
998
- }
999
- catch {
1000
- /* skip */
1001
- }
1002
- }
1003
- if (approveBtns.length > 0 || rejectBtns.length > 0) {
1004
- const actions = [];
1005
- for (const b of approveBtns) {
1006
- actions.push({
1007
- label: b.label,
1008
- type: b.label.toLowerCase().includes('all') ? 'approve_all' : 'approve',
1009
- selectorPath: b.selector,
1010
- });
1011
- }
1012
- for (const b of rejectBtns) {
1013
- actions.push({ label: b.label, type: 'reject', selectorPath: b.selector });
1014
- }
1015
- pendingApprovals.push({
1016
- id: approveBtns.map((b) => b.label).join(','),
1017
- description: approveBtns[0]?.label || 'Pending approval',
1018
- actions,
1019
- });
1020
- }
1021
- }
1022
993
  let activeTab = tabs.find((t) => t.active);
1023
994
  if (!activeTab) {
1024
995
  const headerTitle = document.querySelector('span.auxiliary-bar-chat-title')?.textContent?.trim() ?? '';
@@ -0,0 +1,2 @@
1
+ /** Default JSONL tail for `agents:history` / app `fetchAgentHistory` (keep in sync). */
2
+ export declare const AGENT_HISTORY_DEFAULT_LIMIT = 80;
@@ -0,0 +1,2 @@
1
+ /** Default JSONL tail for `agents:history` / app `fetchAgentHistory` (keep in sync). */
2
+ export const AGENT_HISTORY_DEFAULT_LIMIT = 80;
@@ -0,0 +1,8 @@
1
+ export interface HistoryRequestOpts {
2
+ limit?: number;
3
+ offset?: number;
4
+ /** Ensure at least N tail rows (raises effective limit, capped at 500). */
5
+ minMessages?: number;
6
+ }
7
+ /** Resolve socket/HTTP `agents:history` tail size. */
8
+ export declare function resolveHistoryLimit(opts?: HistoryRequestOpts): number;
@@ -0,0 +1,7 @@
1
+ import { AGENT_HISTORY_DEFAULT_LIMIT } from './history-limit.js';
2
+ /** Resolve socket/HTTP `agents:history` tail size. */
3
+ export function resolveHistoryLimit(opts) {
4
+ const base = opts?.limit && opts.limit > 0 ? opts.limit : AGENT_HISTORY_DEFAULT_LIMIT;
5
+ const min = opts?.minMessages && opts.minMessages > 0 ? opts.minMessages : 0;
6
+ return Math.min(500, Math.max(base, min));
7
+ }
@@ -24,6 +24,7 @@ async function main() {
24
24
  const commandExecutor = new CommandExecutor(selectors);
25
25
  const cdpBridge = new CDPBridge(config);
26
26
  const jsonlIndex = new JsonlIndex(config.cursorProjectsDir);
27
+ jsonlIndex.setBridgeStateProvider(() => stateManager.getState());
27
28
  const messageDebugStore = new MessageDebugStore();
28
29
  const extractor = new DOMExtractor(selectors, (state, err) => {
29
30
  if (state?.messageDebug)
@@ -1,24 +1,38 @@
1
1
  import { EventEmitter } from 'events';
2
- import type { AgentsIndex, HistoryMessage } from './types.js';
2
+ import type { AgentsIndex, CursorState, HistoryMessage } from './types.js';
3
3
  export declare class JsonlIndex extends EventEmitter {
4
4
  private projectsDir;
5
5
  private debounceTimer;
6
6
  private subscribed;
7
7
  private mtimeByAgent;
8
8
  private pollTimer;
9
+ private readonly fileCache;
10
+ private readonly emitFileTimers;
11
+ private liveWatcher;
12
+ private liveWatchKey;
9
13
  /** Skip poll `agent:history` while serving explicit `agents:history` (avoids relay race). */
10
14
  readonly historyReplyInFlight: Set<string>;
15
+ private bridgeState;
11
16
  constructor(projectsDir: string);
17
+ setBridgeStateProvider(fn: () => Pick<CursorState, 'composerIdByTitle' | 'activeComposerId' | 'tabs'>): void;
18
+ private resolveOpts;
12
19
  start(): void;
13
20
  stop(): void;
14
- subscribe(agentId: string, title?: string): void;
21
+ subscribe(agentId: string, title?: string, opts?: {
22
+ emitHistory?: boolean;
23
+ }): void;
15
24
  unsubscribe(agentId: string): void;
25
+ /** Immediate `change` on subscribed `.jsonl` (no awaitWriteFinish delay). */
26
+ private refreshLiveWatcher;
16
27
  getSubscribedAgents(): ReadonlyMap<string, {
17
28
  title?: string;
18
29
  }>;
19
30
  private pollSubscribed;
20
31
  private emitHistoryForAgent;
21
- private emitHistoryForFile;
32
+ private scheduleEmitHistoryForFile;
33
+ private isLiveWatchedFile;
34
+ private loadMessagesForFile;
35
+ private emitHistoryForFileNow;
22
36
  private scheduleRebuild;
23
37
  rebuild(opts?: {
24
38
  broadcast?: boolean;
@@ -27,7 +41,11 @@ export declare class JsonlIndex extends EventEmitter {
27
41
  loadHistory(agentId: string, opts?: {
28
42
  title?: string;
29
43
  composerIdByTitle?: Record<string, string>;
44
+ activeComposerId?: string;
45
+ activeTabTitle?: string;
30
46
  limit?: number;
47
+ /** Skip newest `offset` rows before taking `limit` tail (0 = newest). */
48
+ offset?: number;
31
49
  }): Promise<{
32
50
  agentId: string;
33
51
  messages: HistoryMessage[];