@oh-my-pi/pi-coding-agent 14.6.4 → 14.6.5
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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
4
|
-
"version": "14.6.
|
|
4
|
+
"version": "14.6.5",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://github.com/can1357/oh-my-pi",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -46,12 +46,12 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@agentclientprotocol/sdk": "0.20.0",
|
|
48
48
|
"@mozilla/readability": "^0.6.0",
|
|
49
|
-
"@oh-my-pi/omp-stats": "14.6.
|
|
50
|
-
"@oh-my-pi/pi-agent-core": "14.6.
|
|
51
|
-
"@oh-my-pi/pi-ai": "14.6.
|
|
52
|
-
"@oh-my-pi/pi-natives": "14.6.
|
|
53
|
-
"@oh-my-pi/pi-tui": "14.6.
|
|
54
|
-
"@oh-my-pi/pi-utils": "14.6.
|
|
49
|
+
"@oh-my-pi/omp-stats": "14.6.5",
|
|
50
|
+
"@oh-my-pi/pi-agent-core": "14.6.5",
|
|
51
|
+
"@oh-my-pi/pi-ai": "14.6.5",
|
|
52
|
+
"@oh-my-pi/pi-natives": "14.6.5",
|
|
53
|
+
"@oh-my-pi/pi-tui": "14.6.5",
|
|
54
|
+
"@oh-my-pi/pi-utils": "14.6.5",
|
|
55
55
|
"@puppeteer/browsers": "^2.13.0",
|
|
56
56
|
"@sinclair/typebox": "^0.34.49",
|
|
57
57
|
"@xterm/headless": "^6.0.0",
|
|
@@ -344,8 +344,11 @@ export class InputController {
|
|
|
344
344
|
// (a user-role `message_start` event) leaves any draft the user has
|
|
345
345
|
// typed since queuing intact. Same protection as #783, applied to
|
|
346
346
|
// the streaming/queue path.
|
|
347
|
-
this.ctx.
|
|
348
|
-
|
|
347
|
+
await this.ctx.withLocalSubmission(
|
|
348
|
+
text,
|
|
349
|
+
() => this.ctx.session.prompt(text, { streamingBehavior: "steer", images }),
|
|
350
|
+
{ imageCount: images?.length ?? 0 },
|
|
351
|
+
);
|
|
349
352
|
this.ctx.updatePendingMessagesDisplay();
|
|
350
353
|
this.ctx.ui.requestRender();
|
|
351
354
|
return;
|
|
@@ -440,7 +443,9 @@ export class InputController {
|
|
|
440
443
|
if (this.ctx.session.isStreaming) {
|
|
441
444
|
this.ctx.editor.addToHistory(text);
|
|
442
445
|
this.ctx.editor.setText("");
|
|
443
|
-
await this.ctx.
|
|
446
|
+
await this.ctx.withLocalSubmission(text, () =>
|
|
447
|
+
this.ctx.session.prompt(text, { streamingBehavior: "followUp" }),
|
|
448
|
+
);
|
|
444
449
|
this.ctx.updatePendingMessagesDisplay();
|
|
445
450
|
this.ctx.ui.requestRender();
|
|
446
451
|
return;
|
|
@@ -449,7 +454,7 @@ export class InputController {
|
|
|
449
454
|
// Not streaming — just submit normally
|
|
450
455
|
this.ctx.editor.addToHistory(text);
|
|
451
456
|
this.ctx.editor.setText("");
|
|
452
|
-
await this.ctx.session.prompt(text);
|
|
457
|
+
await this.ctx.withLocalSubmission(text, () => this.ctx.session.prompt(text));
|
|
453
458
|
}
|
|
454
459
|
|
|
455
460
|
restoreQueuedMessagesToEditor(options?: { abort?: boolean; currentText?: string }): number {
|
|
@@ -183,6 +183,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
183
183
|
optimisticUserMessageSignature: string | undefined = undefined;
|
|
184
184
|
locallySubmittedUserSignatures: Set<string> = new Set();
|
|
185
185
|
#pendingSubmittedInput: SubmittedUserInput | undefined;
|
|
186
|
+
#pendingSubmissionDispose: (() => void) | undefined;
|
|
186
187
|
lastSigintTime = 0;
|
|
187
188
|
lastEscapeTime = 0;
|
|
188
189
|
shutdownRequested = false;
|
|
@@ -567,6 +568,30 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
567
568
|
);
|
|
568
569
|
}
|
|
569
570
|
|
|
571
|
+
recordLocalSubmission(text: string, imageCount = 0): () => void {
|
|
572
|
+
if (this.isKnownSlashCommand(text)) {
|
|
573
|
+
return () => {};
|
|
574
|
+
}
|
|
575
|
+
const signature = `${text}\u0000${imageCount}`;
|
|
576
|
+
this.locallySubmittedUserSignatures.add(signature);
|
|
577
|
+
let disposed = false;
|
|
578
|
+
return () => {
|
|
579
|
+
if (disposed) return;
|
|
580
|
+
disposed = true;
|
|
581
|
+
this.locallySubmittedUserSignatures.delete(signature);
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
async withLocalSubmission<T>(text: string, fn: () => Promise<T>, options?: { imageCount?: number }): Promise<T> {
|
|
586
|
+
const dispose = this.recordLocalSubmission(text, options?.imageCount ?? 0);
|
|
587
|
+
try {
|
|
588
|
+
return await fn();
|
|
589
|
+
} catch (err) {
|
|
590
|
+
dispose();
|
|
591
|
+
throw err;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
570
595
|
startPendingSubmission(input: { text: string; images?: ImageContent[] }): SubmittedUserInput {
|
|
571
596
|
const submission: SubmittedUserInput = {
|
|
572
597
|
text: input.text,
|
|
@@ -575,8 +600,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
575
600
|
started: false,
|
|
576
601
|
};
|
|
577
602
|
this.#pendingSubmittedInput = submission;
|
|
578
|
-
|
|
579
|
-
this.
|
|
603
|
+
const imageCount = submission.images?.length ?? 0;
|
|
604
|
+
this.optimisticUserMessageSignature = `${submission.text}\u0000${imageCount}`;
|
|
605
|
+
this.#pendingSubmissionDispose = this.recordLocalSubmission(submission.text, imageCount);
|
|
580
606
|
this.addMessageToChat({
|
|
581
607
|
role: "user",
|
|
582
608
|
content: [{ type: "text", text: submission.text }, ...(submission.images ?? [])],
|
|
@@ -598,7 +624,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
598
624
|
submission.cancelled = true;
|
|
599
625
|
this.#pendingSubmittedInput = undefined;
|
|
600
626
|
this.optimisticUserMessageSignature = undefined;
|
|
601
|
-
this
|
|
627
|
+
this.#pendingSubmissionDispose?.();
|
|
628
|
+
this.#pendingSubmissionDispose = undefined;
|
|
602
629
|
this.#pendingWorkingMessage = undefined;
|
|
603
630
|
if (this.loadingAnimation) {
|
|
604
631
|
this.loadingAnimation.stop();
|
|
@@ -624,6 +651,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
624
651
|
finishPendingSubmission(input: SubmittedUserInput): void {
|
|
625
652
|
if (this.#pendingSubmittedInput === input) {
|
|
626
653
|
this.#pendingSubmittedInput = undefined;
|
|
654
|
+
this.#pendingSubmissionDispose = undefined;
|
|
627
655
|
}
|
|
628
656
|
}
|
|
629
657
|
|
|
@@ -1223,6 +1251,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1223
1251
|
showError(message: string): void {
|
|
1224
1252
|
this.#pendingSubmittedInput = undefined;
|
|
1225
1253
|
this.optimisticUserMessageSignature = undefined;
|
|
1254
|
+
this.#pendingSubmissionDispose?.();
|
|
1255
|
+
this.#pendingSubmissionDispose = undefined;
|
|
1226
1256
|
this.#pendingWorkingMessage = undefined;
|
|
1227
1257
|
if (this.loadingAnimation) {
|
|
1228
1258
|
this.loadingAnimation.stop();
|
package/src/modes/types.ts
CHANGED
|
@@ -151,6 +151,19 @@ export interface InteractiveModeContext {
|
|
|
151
151
|
cancelPendingSubmission(): boolean;
|
|
152
152
|
markPendingSubmissionStarted(input: SubmittedUserInput): boolean;
|
|
153
153
|
finishPendingSubmission(input: SubmittedUserInput): void;
|
|
154
|
+
/**
|
|
155
|
+
* Marks a locally-initiated user submission so the eventual `message_start`
|
|
156
|
+
* event for that user message does not clobber the editor draft (see #783).
|
|
157
|
+
* Returns a dispose function that removes the signature; call it on
|
|
158
|
+
* delivery failure so a retry can be re-marked cleanly.
|
|
159
|
+
*/
|
|
160
|
+
recordLocalSubmission(text: string, imageCount?: number): () => void;
|
|
161
|
+
/**
|
|
162
|
+
* Wraps `fn` in a `recordLocalSubmission` marker that is automatically
|
|
163
|
+
* removed if `fn` rejects. Use this for the common case where a thrown
|
|
164
|
+
* delivery error should leave the signature set untouched.
|
|
165
|
+
*/
|
|
166
|
+
withLocalSubmission<T>(text: string, fn: () => Promise<T>, options?: { imageCount?: number }): Promise<T>;
|
|
154
167
|
isKnownSlashCommand(text: string): boolean;
|
|
155
168
|
addMessageToChat(message: AgentMessage, options?: { populateHistory?: boolean }): void;
|
|
156
169
|
renderSessionContext(
|
|
@@ -534,6 +534,16 @@ export class UiHelpers {
|
|
|
534
534
|
this.ctx.showStatus("Queued message for after compaction");
|
|
535
535
|
}
|
|
536
536
|
|
|
537
|
+
async #deliverQueuedMessage(message: CompactionQueuedMessage): Promise<void> {
|
|
538
|
+
if (this.ctx.isKnownSlashCommand(message.text)) {
|
|
539
|
+
await this.ctx.session.prompt(message.text);
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
await this.ctx.withLocalSubmission(message.text, () =>
|
|
543
|
+
message.mode === "followUp" ? this.ctx.session.followUp(message.text) : this.ctx.session.steer(message.text),
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
|
|
537
547
|
isKnownSlashCommand(text: string): boolean {
|
|
538
548
|
if (!text.startsWith("/")) return false;
|
|
539
549
|
const spaceIndex = text.indexOf(" ");
|
|
@@ -576,13 +586,7 @@ export class UiHelpers {
|
|
|
576
586
|
try {
|
|
577
587
|
if (options?.willRetry) {
|
|
578
588
|
for (const message of queuedMessages) {
|
|
579
|
-
|
|
580
|
-
await this.ctx.session.prompt(message.text);
|
|
581
|
-
} else if (message.mode === "followUp") {
|
|
582
|
-
await this.ctx.session.followUp(message.text);
|
|
583
|
-
} else {
|
|
584
|
-
await this.ctx.session.steer(message.text);
|
|
585
|
-
}
|
|
589
|
+
await this.#deliverQueuedMessage(message);
|
|
586
590
|
}
|
|
587
591
|
this.ctx.updatePendingMessagesDisplay();
|
|
588
592
|
return;
|
|
@@ -607,7 +611,10 @@ export class UiHelpers {
|
|
|
607
611
|
const rest = queuedMessages.slice(firstPromptIndex + 1);
|
|
608
612
|
|
|
609
613
|
for (const message of preCommands) {
|
|
610
|
-
|
|
614
|
+
// preCommands are all slash commands; #deliverQueuedMessage handles
|
|
615
|
+
// that branch (no local-submission marking needed since slash
|
|
616
|
+
// commands don't generate a matching user message_start).
|
|
617
|
+
await this.#deliverQueuedMessage(message);
|
|
611
618
|
}
|
|
612
619
|
|
|
613
620
|
// Pass streamingBehavior so that if the session is still streaming when
|
|
@@ -619,22 +626,22 @@ export class UiHelpers {
|
|
|
619
626
|
// deferred, the message lands in the same queue every other consumer
|
|
620
627
|
// (Alt+Up dequeue, post-stream drain) already drains, instead of being
|
|
621
628
|
// stranded in compactionQueuedMessages with no drainer.
|
|
629
|
+
//
|
|
630
|
+
// firstPrompt is fire-and-forget — its rejection is funneled through
|
|
631
|
+
// `restoreQueue` rather than rethrown, so we use the primitive
|
|
632
|
+
// recordLocalSubmission and dispose manually in the catch.
|
|
633
|
+
const disposeFirstPrompt = this.ctx.recordLocalSubmission(firstPrompt.text);
|
|
622
634
|
const promptPromise = this.ctx.session
|
|
623
635
|
.prompt(firstPrompt.text, {
|
|
624
636
|
streamingBehavior: firstPrompt.mode === "followUp" ? "followUp" : "steer",
|
|
625
637
|
})
|
|
626
638
|
.catch((error: unknown) => {
|
|
639
|
+
disposeFirstPrompt();
|
|
627
640
|
restoreQueue(error);
|
|
628
641
|
});
|
|
629
642
|
|
|
630
643
|
for (const message of rest) {
|
|
631
|
-
|
|
632
|
-
await this.ctx.session.prompt(message.text);
|
|
633
|
-
} else if (message.mode === "followUp") {
|
|
634
|
-
await this.ctx.session.followUp(message.text);
|
|
635
|
-
} else {
|
|
636
|
-
await this.ctx.session.steer(message.text);
|
|
637
|
-
}
|
|
644
|
+
await this.#deliverQueuedMessage(message);
|
|
638
645
|
}
|
|
639
646
|
this.ctx.updatePendingMessagesDisplay();
|
|
640
647
|
void promptPromise;
|