cosmoremote 2.1.0 → 2.1.2
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/bridge.js +320 -149
- package/dist/bridge.js.map +1 -1
- package/dist/cli-conversations.d.ts +3 -1
- package/dist/cli-conversations.js +1 -0
- package/dist/cli-conversations.js.map +1 -1
- package/dist/detect.d.ts +9 -0
- package/dist/detect.js +48 -13
- package/dist/detect.js.map +1 -1
- package/dist/session.d.ts +18 -1
- package/dist/session.js +362 -18
- package/dist/session.js.map +1 -1
- package/package.json +1 -1
package/dist/session.js
CHANGED
|
@@ -37,6 +37,10 @@ class CLISession extends events_1.EventEmitter {
|
|
|
37
37
|
sessionId;
|
|
38
38
|
process = null;
|
|
39
39
|
cliBinary;
|
|
40
|
+
// Public so bridge.ts can compute the on-disk Claude session path
|
|
41
|
+
// (~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl) to verify whether a
|
|
42
|
+
// turn actually wrote conversation history before persisting the session
|
|
43
|
+
// id to session-contexts.json.
|
|
40
44
|
workingDir;
|
|
41
45
|
additionalDirs;
|
|
42
46
|
model;
|
|
@@ -45,6 +49,7 @@ class CLISession extends events_1.EventEmitter {
|
|
|
45
49
|
claudeSettingsPath = null;
|
|
46
50
|
isClaude;
|
|
47
51
|
isCodex;
|
|
52
|
+
isCursor;
|
|
48
53
|
// Per-turn state — reset on every sendPrompt
|
|
49
54
|
fullOutput = "";
|
|
50
55
|
cleanOutput = "";
|
|
@@ -65,11 +70,13 @@ class CLISession extends events_1.EventEmitter {
|
|
|
65
70
|
codexResponseDelivered = false;
|
|
66
71
|
stdoutLineBuffer = "";
|
|
67
72
|
stderrLineBuffer = "";
|
|
73
|
+
promptQueue = [];
|
|
68
74
|
// Persistent-process state (Claude only)
|
|
69
75
|
turnInFlight = false;
|
|
70
76
|
idleTimer = null;
|
|
71
77
|
codexThreadId = null;
|
|
72
78
|
claudeSessionId = null;
|
|
79
|
+
cursorSessionId = null;
|
|
73
80
|
// Optional persistent Codex adapter (experimental, opt-in via env var).
|
|
74
81
|
codexPersistent = null;
|
|
75
82
|
// True iff this session was constructed with a pre-existing claudeSessionId
|
|
@@ -86,8 +93,10 @@ class CLISession extends events_1.EventEmitter {
|
|
|
86
93
|
this.claudeSettings = opts.claudeSettings ?? null;
|
|
87
94
|
this.claudeSessionId = opts.initialClaudeSessionId ?? null;
|
|
88
95
|
this.codexThreadId = opts.initialCodexThreadId ?? null;
|
|
96
|
+
this.cursorSessionId = opts.initialCursorSessionId ?? null;
|
|
89
97
|
this.isClaude = this.cliBinary.includes("claude");
|
|
90
98
|
this.isCodex = this.cliBinary.includes("codex");
|
|
99
|
+
this.isCursor = this.cliBinary.includes("cursor");
|
|
91
100
|
// If we were given an existing Claude session id (continuation or imported
|
|
92
101
|
// terminal conversation), spawn with `--resume` so Claude reloads history.
|
|
93
102
|
this.claudeSessionResumable = this.isClaude && !!opts.initialClaudeSessionId;
|
|
@@ -105,9 +114,19 @@ class CLISession extends events_1.EventEmitter {
|
|
|
105
114
|
}
|
|
106
115
|
this.lastPromptContent = content;
|
|
107
116
|
this.lastPromptAt = now;
|
|
117
|
+
if (this.turnInFlight) {
|
|
118
|
+
this.enqueuePrompt(content, attachments);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
this.dispatchPrompt(content, attachments);
|
|
122
|
+
}
|
|
123
|
+
dispatchPrompt(content, attachments) {
|
|
108
124
|
if (this.isClaude) {
|
|
109
125
|
this.sendPromptClaudePersistent(content, attachments);
|
|
110
126
|
}
|
|
127
|
+
else if (this.isCursor) {
|
|
128
|
+
this.sendPromptCursorOneShot(content, attachments);
|
|
129
|
+
}
|
|
111
130
|
else if (this.isCodex && CODEX_PERSISTENT_ENABLED) {
|
|
112
131
|
this.sendPromptCodexPersistent(content, attachments);
|
|
113
132
|
}
|
|
@@ -115,9 +134,29 @@ class CLISession extends events_1.EventEmitter {
|
|
|
115
134
|
this.sendPromptOneShot(content, attachments);
|
|
116
135
|
}
|
|
117
136
|
}
|
|
137
|
+
enqueuePrompt(content, attachments) {
|
|
138
|
+
this.promptQueue.push({ content, attachments });
|
|
139
|
+
console.log(` [session:${this.sessionId.substring(0, 8)}] queued prompt position=${this.promptQueue.length}`);
|
|
140
|
+
this.emit("status", `Prompt queued (${this.promptQueue.length})`);
|
|
141
|
+
}
|
|
142
|
+
emitDoneAndContinue(result) {
|
|
143
|
+
this.emit("done", result);
|
|
144
|
+
if (this.promptQueue.length === 0)
|
|
145
|
+
return;
|
|
146
|
+
const next = this.promptQueue.shift();
|
|
147
|
+
if (!next)
|
|
148
|
+
return;
|
|
149
|
+
setImmediate(() => {
|
|
150
|
+
if (this.turnInFlight) {
|
|
151
|
+
this.promptQueue.unshift(next);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
this.dispatchPrompt(next.content, next.attachments);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
118
157
|
sendPromptCodexPersistent(content, attachments) {
|
|
119
158
|
if (this.turnInFlight) {
|
|
120
|
-
this.
|
|
159
|
+
this.enqueuePrompt(content, attachments);
|
|
121
160
|
return;
|
|
122
161
|
}
|
|
123
162
|
if (attachments.length > 0) {
|
|
@@ -159,7 +198,7 @@ class CLISession extends events_1.EventEmitter {
|
|
|
159
198
|
this.codexThreadId = this.codexPersistent.threadId;
|
|
160
199
|
}
|
|
161
200
|
this.flushOutput();
|
|
162
|
-
this.
|
|
201
|
+
this.emitDoneAndContinue({ code: result.code, fullOutput: this.cleanOutput });
|
|
163
202
|
this.scheduleIdleShutdown();
|
|
164
203
|
});
|
|
165
204
|
}
|
|
@@ -181,8 +220,7 @@ class CLISession extends events_1.EventEmitter {
|
|
|
181
220
|
// ---------------------------------------------------------------------------
|
|
182
221
|
sendPromptClaudePersistent(content, attachments) {
|
|
183
222
|
if (this.turnInFlight) {
|
|
184
|
-
|
|
185
|
-
this.emit("error", "Previous turn still running. Please wait for it to finish before sending another prompt.");
|
|
223
|
+
this.enqueuePrompt(content, attachments);
|
|
186
224
|
return;
|
|
187
225
|
}
|
|
188
226
|
this.resetTurnState();
|
|
@@ -280,7 +318,7 @@ class CLISession extends events_1.EventEmitter {
|
|
|
280
318
|
if (code !== 0 && !this.cleanOutput.trim() && finalOutput.trim()) {
|
|
281
319
|
this.emit("error", finalOutput);
|
|
282
320
|
}
|
|
283
|
-
this.
|
|
321
|
+
this.emitDoneAndContinue({ code: code ?? -1, fullOutput: finalOutput });
|
|
284
322
|
}
|
|
285
323
|
this.cleanupAttachmentFiles();
|
|
286
324
|
});
|
|
@@ -293,7 +331,7 @@ class CLISession extends events_1.EventEmitter {
|
|
|
293
331
|
if (wasInFlight) {
|
|
294
332
|
const finalOutput = this.rememberCliFailure(err.message) || err.message;
|
|
295
333
|
this.emit("error", finalOutput);
|
|
296
|
-
this.
|
|
334
|
+
this.emitDoneAndContinue({ code: -1, fullOutput: finalOutput });
|
|
297
335
|
}
|
|
298
336
|
this.cleanupAttachmentFiles();
|
|
299
337
|
});
|
|
@@ -397,7 +435,7 @@ class CLISession extends events_1.EventEmitter {
|
|
|
397
435
|
if (this.cleanOutput.length === 0 && typeof event.result === "string" && event.result) {
|
|
398
436
|
this.emit("output", event.result);
|
|
399
437
|
}
|
|
400
|
-
this.
|
|
438
|
+
this.emitDoneAndContinue({ code: 0, fullOutput: finalText });
|
|
401
439
|
this.scheduleIdleShutdown();
|
|
402
440
|
return;
|
|
403
441
|
}
|
|
@@ -446,6 +484,164 @@ class CLISession extends events_1.EventEmitter {
|
|
|
446
484
|
}
|
|
447
485
|
}
|
|
448
486
|
// ---------------------------------------------------------------------------
|
|
487
|
+
// Cursor Agent path: headless one-shot process per prompt, resumed by chat id.
|
|
488
|
+
// ---------------------------------------------------------------------------
|
|
489
|
+
sendPromptCursorOneShot(content, attachments) {
|
|
490
|
+
if (this.turnInFlight) {
|
|
491
|
+
this.enqueuePrompt(content, attachments);
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
this.resetTurnState();
|
|
495
|
+
this.turnInFlight = true;
|
|
496
|
+
this.startedAt = Date.now();
|
|
497
|
+
this.lastActivityAt = this.startedAt;
|
|
498
|
+
this.cancelIdleTimer();
|
|
499
|
+
const attachmentPaths = this.prepareAttachmentFiles(attachments);
|
|
500
|
+
const promptWithAttachments = this.buildPromptWithAttachments(content, attachmentPaths, true);
|
|
501
|
+
const args = [
|
|
502
|
+
"-p",
|
|
503
|
+
"--output-format", "stream-json",
|
|
504
|
+
"--stream-partial-output",
|
|
505
|
+
"--force",
|
|
506
|
+
"--sandbox", "disabled",
|
|
507
|
+
"--trust",
|
|
508
|
+
"--workspace", this.workingDir,
|
|
509
|
+
];
|
|
510
|
+
if (this.cursorSessionId)
|
|
511
|
+
args.push("--resume", this.cursorSessionId);
|
|
512
|
+
if (this.model)
|
|
513
|
+
args.push("--model", this.model);
|
|
514
|
+
args.push(promptWithAttachments);
|
|
515
|
+
console.log(` [session:${this.sessionId.substring(0, 8)}] spawning Cursor binary=${this.cliBinary} cwd=${this.workingDir} cursorSessionId=${this.cursorSessionId ?? "new"}`);
|
|
516
|
+
this.process = (0, child_process_1.spawn)(this.cliBinary, args, {
|
|
517
|
+
cwd: this.workingDir,
|
|
518
|
+
env: { ...process.env, FORCE_COLOR: "0" },
|
|
519
|
+
detached: true,
|
|
520
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
521
|
+
});
|
|
522
|
+
console.log(` [session:${this.sessionId.substring(0, 8)}] Cursor args: ${args.map((a) => JSON.stringify(a)).join(" ")} pid=${this.process.pid ?? "unknown"}`);
|
|
523
|
+
this.startTurnWatchdog();
|
|
524
|
+
this.process.stdout?.on("data", (chunk) => {
|
|
525
|
+
const text = chunk.toString();
|
|
526
|
+
this.fullOutput += text;
|
|
527
|
+
this.lastActivityAt = Date.now();
|
|
528
|
+
this.stdoutLineBuffer += text;
|
|
529
|
+
const lines = this.drainBufferedLines("stdout");
|
|
530
|
+
for (const line of lines) {
|
|
531
|
+
this.handleCursorStreamLine(line);
|
|
532
|
+
}
|
|
533
|
+
if (lines.length === 0 && !text.trim().startsWith("{")) {
|
|
534
|
+
this.cleanOutput += text;
|
|
535
|
+
this.bufferOutput(text);
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
this.process.stderr?.on("data", (chunk) => {
|
|
539
|
+
const text = chunk.toString().trim();
|
|
540
|
+
if (!text)
|
|
541
|
+
return;
|
|
542
|
+
this.lastActivityAt = Date.now();
|
|
543
|
+
console.log(` [session:${this.sessionId.substring(0, 8)}] cursor stderr: ${text.replace(/\s+/g, " ").slice(0, 220)}`);
|
|
544
|
+
const isNoise = /FORCE_COLOR|trust.*workspace|sandbox/i.test(text);
|
|
545
|
+
if (isNoise)
|
|
546
|
+
return;
|
|
547
|
+
this.rememberCliFailure(text);
|
|
548
|
+
this.stderrLineBuffer += `${text}\n`;
|
|
549
|
+
});
|
|
550
|
+
this.process.on("close", (code) => {
|
|
551
|
+
const elapsed = this.startedAt ? Date.now() - this.startedAt : -1;
|
|
552
|
+
console.log(` [session:${this.sessionId.substring(0, 8)}] Cursor close code=${code} elapsedMs=${elapsed} cleanLen=${this.cleanOutput.length}`);
|
|
553
|
+
const wasInFlight = this.turnInFlight;
|
|
554
|
+
this.clearTurnWatchdog();
|
|
555
|
+
if (this.killTimeout)
|
|
556
|
+
clearTimeout(this.killTimeout);
|
|
557
|
+
this.killTimeout = null;
|
|
558
|
+
this.flushOutput();
|
|
559
|
+
if (wasInFlight) {
|
|
560
|
+
const finalOutput = this.finalOutputForClose(code);
|
|
561
|
+
if (code !== 0 && !this.cleanOutput.trim() && finalOutput.trim()) {
|
|
562
|
+
this.emit("error", finalOutput);
|
|
563
|
+
}
|
|
564
|
+
this.turnInFlight = false;
|
|
565
|
+
this.emitDoneAndContinue({ code: code ?? -1, fullOutput: finalOutput });
|
|
566
|
+
}
|
|
567
|
+
this.cleanupAttachmentFiles();
|
|
568
|
+
this.process = null;
|
|
569
|
+
this.startedAt = null;
|
|
570
|
+
this.lastActivityAt = null;
|
|
571
|
+
this.pendingErrorText = null;
|
|
572
|
+
this.lastMeaningfulStderr = null;
|
|
573
|
+
});
|
|
574
|
+
this.process.on("error", (err) => {
|
|
575
|
+
const elapsed = this.startedAt ? Date.now() - this.startedAt : -1;
|
|
576
|
+
console.log(` [session:${this.sessionId.substring(0, 8)}] Cursor error after ${elapsed}ms: ${err.message}`);
|
|
577
|
+
this.clearTurnWatchdog();
|
|
578
|
+
if (this.killTimeout)
|
|
579
|
+
clearTimeout(this.killTimeout);
|
|
580
|
+
this.killTimeout = null;
|
|
581
|
+
this.emit("error", err.message);
|
|
582
|
+
this.cleanupAttachmentFiles();
|
|
583
|
+
this.process = null;
|
|
584
|
+
this.startedAt = null;
|
|
585
|
+
this.lastActivityAt = null;
|
|
586
|
+
this.pendingErrorText = null;
|
|
587
|
+
this.lastMeaningfulStderr = null;
|
|
588
|
+
this.turnInFlight = false;
|
|
589
|
+
this.emitDoneAndContinue({ code: -1, fullOutput: err.message });
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
handleCursorStreamLine(line) {
|
|
593
|
+
let event;
|
|
594
|
+
try {
|
|
595
|
+
event = JSON.parse(line);
|
|
596
|
+
}
|
|
597
|
+
catch {
|
|
598
|
+
if (line.trim()) {
|
|
599
|
+
this.cleanOutput += line;
|
|
600
|
+
this.bufferOutput(line);
|
|
601
|
+
}
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
const cursorSessionId = this.extractCursorSessionId(event);
|
|
605
|
+
if (cursorSessionId && this.cursorSessionId !== cursorSessionId) {
|
|
606
|
+
this.cursorSessionId = cursorSessionId;
|
|
607
|
+
console.log(` [session:${this.sessionId.substring(0, 8)}] cursor session_id=${cursorSessionId}`);
|
|
608
|
+
}
|
|
609
|
+
const tool = this.extractCursorTool(event);
|
|
610
|
+
if (tool) {
|
|
611
|
+
const label = this.formatToolLabel(tool.name, tool.input);
|
|
612
|
+
this.emit("status", label);
|
|
613
|
+
this.maybeEmitToolEvent({ name: tool.name, input: tool.input });
|
|
614
|
+
}
|
|
615
|
+
const status = this.formatCursorStatus(event);
|
|
616
|
+
if (status) {
|
|
617
|
+
this.emit("status", status);
|
|
618
|
+
}
|
|
619
|
+
const error = this.extractCursorError(event);
|
|
620
|
+
if (error) {
|
|
621
|
+
const message = this.rememberCliFailure(error) || error;
|
|
622
|
+
if (!this.cleanOutput.trim())
|
|
623
|
+
this.cleanOutput = message;
|
|
624
|
+
this.emit("error", message);
|
|
625
|
+
}
|
|
626
|
+
const text = this.extractCursorText(event);
|
|
627
|
+
if (text) {
|
|
628
|
+
this.cleanOutput += text;
|
|
629
|
+
this.bufferOutput(text);
|
|
630
|
+
}
|
|
631
|
+
if (this.isCursorTurnEnd(event)) {
|
|
632
|
+
const finalText = this.extractCursorFinalText(event) || this.cleanOutput;
|
|
633
|
+
console.log(` [session:${this.sessionId.substring(0, 8)}] Cursor turn complete len=${finalText.length}`);
|
|
634
|
+
this.clearTurnWatchdog();
|
|
635
|
+
this.turnInFlight = false;
|
|
636
|
+
this.cleanupAttachmentFiles();
|
|
637
|
+
this.flushOutput();
|
|
638
|
+
if (this.cleanOutput.length === 0 && finalText) {
|
|
639
|
+
this.emit("output", finalText);
|
|
640
|
+
}
|
|
641
|
+
this.emitDoneAndContinue({ code: 0, fullOutput: finalText });
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
// ---------------------------------------------------------------------------
|
|
449
645
|
// One-shot path (Codex today; reused by anything non-persistent).
|
|
450
646
|
// ---------------------------------------------------------------------------
|
|
451
647
|
sendPromptOneShot(content, attachments) {
|
|
@@ -556,7 +752,7 @@ class CLISession extends events_1.EventEmitter {
|
|
|
556
752
|
if (code !== 0 && !this.cleanOutput.trim() && finalOutput.trim()) {
|
|
557
753
|
this.emit("error", finalOutput);
|
|
558
754
|
}
|
|
559
|
-
this.
|
|
755
|
+
this.emitDoneAndContinue({ code: code ?? -1, fullOutput: finalOutput });
|
|
560
756
|
this.cleanupAttachmentFiles();
|
|
561
757
|
this.process = null;
|
|
562
758
|
this.startedAt = null;
|
|
@@ -643,6 +839,7 @@ class CLISession extends events_1.EventEmitter {
|
|
|
643
839
|
kill() {
|
|
644
840
|
this.cancelIdleTimer();
|
|
645
841
|
this.cleanupClaudeSettings();
|
|
842
|
+
this.promptQueue = [];
|
|
646
843
|
if (this.codexPersistent) {
|
|
647
844
|
void this.codexPersistent.interrupt().catch(() => { });
|
|
648
845
|
this.codexPersistent.kill();
|
|
@@ -662,8 +859,7 @@ class CLISession extends events_1.EventEmitter {
|
|
|
662
859
|
this.killProcessGroup("SIGKILL");
|
|
663
860
|
setTimeout(() => {
|
|
664
861
|
if (this.process && !this.process.killed) {
|
|
665
|
-
console.log(` [session:${this.sessionId.substring(0, 8)}] SIGKILL
|
|
666
|
-
this.process = null;
|
|
862
|
+
console.log(` [session:${this.sessionId.substring(0, 8)}] SIGKILL did not confirm exit for pid=${this.process.pid}; keeping handle for later close/error and retry logs`);
|
|
667
863
|
}
|
|
668
864
|
}, 500);
|
|
669
865
|
}
|
|
@@ -698,6 +894,9 @@ class CLISession extends events_1.EventEmitter {
|
|
|
698
894
|
get hasTurnInFlight() {
|
|
699
895
|
return this.turnInFlight;
|
|
700
896
|
}
|
|
897
|
+
get hasQueuedPrompts() {
|
|
898
|
+
return this.promptQueue.length > 0;
|
|
899
|
+
}
|
|
701
900
|
resetTurnState() {
|
|
702
901
|
this.fullOutput = "";
|
|
703
902
|
this.cleanOutput = "";
|
|
@@ -787,12 +986,17 @@ class CLISession extends events_1.EventEmitter {
|
|
|
787
986
|
let label = "";
|
|
788
987
|
switch (name.toLowerCase()) {
|
|
789
988
|
case "read":
|
|
989
|
+
case "read_file":
|
|
990
|
+
case "list_dir":
|
|
790
991
|
case "str_replace_editor":
|
|
791
992
|
case "str_replace_based_edit_tool": {
|
|
792
993
|
const cmd = input?.command;
|
|
793
|
-
const path = input?.path;
|
|
994
|
+
const path = input?.path ?? input?.target_file;
|
|
794
995
|
const file = path ? path.split("/").pop() : undefined;
|
|
795
|
-
if (
|
|
996
|
+
if (name.toLowerCase() === "list_dir") {
|
|
997
|
+
label = file ? `📁 Listing ${file}` : "📁 Listing directory";
|
|
998
|
+
}
|
|
999
|
+
else if (cmd === "view" || !cmd || name.toLowerCase() === "read" || name.toLowerCase() === "read_file") {
|
|
796
1000
|
label = file ? `📖 Reading ${file}` : "📖 Reading file";
|
|
797
1001
|
}
|
|
798
1002
|
else if (cmd === "create") {
|
|
@@ -804,15 +1008,18 @@ class CLISession extends events_1.EventEmitter {
|
|
|
804
1008
|
break;
|
|
805
1009
|
}
|
|
806
1010
|
case "bash":
|
|
807
|
-
case "execute_bash":
|
|
808
|
-
|
|
1011
|
+
case "execute_bash":
|
|
1012
|
+
case "run_terminal_cmd": {
|
|
1013
|
+
const cmd = input?.command ?? input?.cmd;
|
|
809
1014
|
const preview = cmd ? cmd.replace(/\s+/g, " ").slice(0, 40) : "";
|
|
810
1015
|
label = preview ? `⚙️ Running: ${preview}` : "⚙️ Running command";
|
|
811
1016
|
break;
|
|
812
1017
|
}
|
|
813
|
-
case "edit":
|
|
1018
|
+
case "edit":
|
|
1019
|
+
case "edit_file": {
|
|
814
1020
|
const file = input?.path?.split("/").pop()
|
|
815
|
-
?? input?.file_path?.split("/").pop()
|
|
1021
|
+
?? input?.file_path?.split("/").pop()
|
|
1022
|
+
?? input?.target_file?.split("/").pop();
|
|
816
1023
|
label = file ? `✏️ Editing ${file}` : "✏️ Editing file";
|
|
817
1024
|
break;
|
|
818
1025
|
}
|
|
@@ -826,8 +1033,11 @@ class CLISession extends events_1.EventEmitter {
|
|
|
826
1033
|
label = pattern ? `🔍 Finding: ${pattern}` : "🔍 Finding files";
|
|
827
1034
|
break;
|
|
828
1035
|
}
|
|
829
|
-
case "grep":
|
|
830
|
-
|
|
1036
|
+
case "grep":
|
|
1037
|
+
case "grep_search":
|
|
1038
|
+
case "file_search":
|
|
1039
|
+
case "codebase_search": {
|
|
1040
|
+
const pattern = (input?.pattern ?? input?.query)?.slice(0, 30);
|
|
831
1041
|
label = pattern ? `🔎 Searching: ${pattern}` : "🔎 Searching files";
|
|
832
1042
|
break;
|
|
833
1043
|
}
|
|
@@ -958,6 +1168,140 @@ class CLISession extends events_1.EventEmitter {
|
|
|
958
1168
|
return ".jpg";
|
|
959
1169
|
}
|
|
960
1170
|
}
|
|
1171
|
+
extractCursorSessionId(event) {
|
|
1172
|
+
const direct = event?.chatId ??
|
|
1173
|
+
event?.chat_id ??
|
|
1174
|
+
event?.sessionId ??
|
|
1175
|
+
event?.session_id ??
|
|
1176
|
+
event?.conversationId ??
|
|
1177
|
+
event?.conversation_id;
|
|
1178
|
+
if (typeof direct === "string" && direct.trim())
|
|
1179
|
+
return direct.trim();
|
|
1180
|
+
const type = typeof event?.type === "string" ? event.type.toLowerCase() : "";
|
|
1181
|
+
if ((type.includes("chat") || type.includes("session") || type.includes("conversation")) && typeof event?.id === "string") {
|
|
1182
|
+
return event.id.trim() || null;
|
|
1183
|
+
}
|
|
1184
|
+
return null;
|
|
1185
|
+
}
|
|
1186
|
+
extractCursorTool(event) {
|
|
1187
|
+
const type = typeof event?.type === "string" ? event.type.toLowerCase() : "";
|
|
1188
|
+
const item = event?.item;
|
|
1189
|
+
const itemType = typeof item?.type === "string" ? item.type.toLowerCase() : "";
|
|
1190
|
+
const name = event?.name ??
|
|
1191
|
+
event?.toolName ??
|
|
1192
|
+
event?.tool_name ??
|
|
1193
|
+
event?.tool?.name ??
|
|
1194
|
+
// cursor-agent shape: { type:"tool_call", tool_call:{ toolName, title, ... } }
|
|
1195
|
+
event?.tool_call?.toolName ??
|
|
1196
|
+
event?.tool_call?.name ??
|
|
1197
|
+
item?.name ??
|
|
1198
|
+
item?.toolName ??
|
|
1199
|
+
item?.tool_name;
|
|
1200
|
+
if (typeof name === "string" && name.trim() && (type.includes("tool") || itemType.includes("tool"))) {
|
|
1201
|
+
const input = event?.input ?? event?.args ?? event?.arguments ??
|
|
1202
|
+
event?.tool_call?.args ?? event?.tool_call?.input ??
|
|
1203
|
+
item?.input ?? item?.args ?? item?.arguments;
|
|
1204
|
+
return { name: name.trim(), input: typeof input === "object" && input ? input : undefined };
|
|
1205
|
+
}
|
|
1206
|
+
const blocks = Array.isArray(event?.message?.content) ? event.message.content : [];
|
|
1207
|
+
for (const block of blocks) {
|
|
1208
|
+
if (block?.type === "tool_use" && typeof block?.name === "string") {
|
|
1209
|
+
return { name: block.name, input: typeof block.input === "object" && block.input ? block.input : undefined };
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
return null;
|
|
1213
|
+
}
|
|
1214
|
+
extractCursorText(event) {
|
|
1215
|
+
const type = typeof event?.type === "string" ? event.type.toLowerCase() : "";
|
|
1216
|
+
const delta = event?.delta;
|
|
1217
|
+
if (typeof event?.text === "string" && (type.includes("delta") || type === "text" || type.includes("partial"))) {
|
|
1218
|
+
return event.text;
|
|
1219
|
+
}
|
|
1220
|
+
if (typeof event?.content === "string" && (type.includes("delta") || type === "text" || type.includes("partial"))) {
|
|
1221
|
+
return event.content;
|
|
1222
|
+
}
|
|
1223
|
+
if (typeof delta === "string")
|
|
1224
|
+
return delta;
|
|
1225
|
+
if (typeof delta?.text === "string")
|
|
1226
|
+
return delta.text;
|
|
1227
|
+
if (typeof delta?.content === "string")
|
|
1228
|
+
return delta.content;
|
|
1229
|
+
if (event?.type === "stream_event" && event.event?.type === "content_block_delta") {
|
|
1230
|
+
const streamDelta = event.event.delta;
|
|
1231
|
+
if (streamDelta?.type === "text_delta" && typeof streamDelta.text === "string")
|
|
1232
|
+
return streamDelta.text;
|
|
1233
|
+
}
|
|
1234
|
+
if (type === "assistant" || type === "message" || type === "assistant_message") {
|
|
1235
|
+
// cursor-agent (with --stream-partial-output) emits one `assistant` frame per
|
|
1236
|
+
// token chunk, each carrying an incremental DELTA at message.content[].text.
|
|
1237
|
+
// Do NOT gate on cleanOutput being empty — that dropped every chunk after the
|
|
1238
|
+
// first, so the phone saw first-chunk → silence → full answer. The turn-end
|
|
1239
|
+
// `result` handler only re-emits full text when nothing was streamed, so
|
|
1240
|
+
// streaming every delta here does not double-render.
|
|
1241
|
+
const content = event?.message?.content ?? event?.content;
|
|
1242
|
+
if (typeof content === "string")
|
|
1243
|
+
return content;
|
|
1244
|
+
if (Array.isArray(content)) {
|
|
1245
|
+
return content
|
|
1246
|
+
.map((block) => typeof block?.text === "string" ? block.text : typeof block?.content === "string" ? block.content : "")
|
|
1247
|
+
.filter(Boolean)
|
|
1248
|
+
.join("");
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
return null;
|
|
1252
|
+
}
|
|
1253
|
+
extractCursorFinalText(event) {
|
|
1254
|
+
const result = event?.result ?? event?.response ?? event?.message ?? event?.output;
|
|
1255
|
+
if (typeof result === "string" && result.trim())
|
|
1256
|
+
return result;
|
|
1257
|
+
if (typeof event?.text === "string" && event.text.trim())
|
|
1258
|
+
return event.text;
|
|
1259
|
+
if (typeof result?.content === "string" && result.content.trim())
|
|
1260
|
+
return result.content;
|
|
1261
|
+
if (Array.isArray(result?.content)) {
|
|
1262
|
+
const text = result.content
|
|
1263
|
+
.map((block) => typeof block?.text === "string" ? block.text : typeof block?.content === "string" ? block.content : "")
|
|
1264
|
+
.filter(Boolean)
|
|
1265
|
+
.join("");
|
|
1266
|
+
return text || null;
|
|
1267
|
+
}
|
|
1268
|
+
return null;
|
|
1269
|
+
}
|
|
1270
|
+
extractCursorError(event) {
|
|
1271
|
+
const type = typeof event?.type === "string" ? event.type.toLowerCase() : "";
|
|
1272
|
+
if (!type.includes("error") && !event?.error)
|
|
1273
|
+
return null;
|
|
1274
|
+
return this.extractCliFailureFromValue(event?.error) ??
|
|
1275
|
+
(typeof event?.message === "string" ? event.message : null) ??
|
|
1276
|
+
(typeof event?.detail === "string" ? event.detail : null);
|
|
1277
|
+
}
|
|
1278
|
+
isCursorTurnEnd(event) {
|
|
1279
|
+
const type = typeof event?.type === "string" ? event.type.toLowerCase() : "";
|
|
1280
|
+
const subtype = typeof event?.subtype === "string" ? event.subtype.toLowerCase() : "";
|
|
1281
|
+
return (type === "result" ||
|
|
1282
|
+
type === "done" ||
|
|
1283
|
+
type === "complete" ||
|
|
1284
|
+
type === "completed" ||
|
|
1285
|
+
type === "turn.completed" ||
|
|
1286
|
+
type === "response.completed" ||
|
|
1287
|
+
type === "chat.completed" ||
|
|
1288
|
+
type === "session.completed" ||
|
|
1289
|
+
subtype === "success");
|
|
1290
|
+
}
|
|
1291
|
+
formatCursorStatus(event) {
|
|
1292
|
+
const type = typeof event?.type === "string" ? event.type.toLowerCase() : "";
|
|
1293
|
+
if (!type)
|
|
1294
|
+
return null;
|
|
1295
|
+
if (type.includes("thinking") || type.includes("reasoning"))
|
|
1296
|
+
return "Thinking...";
|
|
1297
|
+
if (type.includes("model") || type.includes("request"))
|
|
1298
|
+
return "Calling model...";
|
|
1299
|
+
if (type.includes("completed") || type === "done" || type === "result")
|
|
1300
|
+
return "Turn complete";
|
|
1301
|
+
if (type.includes("started") || type.includes("created"))
|
|
1302
|
+
return "Starting conversation...";
|
|
1303
|
+
return null;
|
|
1304
|
+
}
|
|
961
1305
|
hasWorkspacePermissionError() {
|
|
962
1306
|
const haystack = [
|
|
963
1307
|
this.cleanOutput,
|