@hyperdrive.bot/paseo-server 0.3.27 → 0.3.28
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/server/server/agent/providers/claude/transport/pty-query.d.ts +56 -2
- package/dist/server/server/agent/providers/claude/transport/pty-query.js +160 -109
- package/dist/server/web-ui/_expo/static/js/web/{index-11f21e9e7b941cc98daf2ca7e3a78592.js → index-f8bb839b399b809d73376d962613fce2.js} +4 -4
- package/dist/server/web-ui/_expo/static/js/web/index-f8bb839b399b809d73376d962613fce2.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{index-11f21e9e7b941cc98daf2ca7e3a78592.js.gz → index-f8bb839b399b809d73376d962613fce2.js.gz} +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{index-11f21e9e7b941cc98daf2ca7e3a78592.js.map.br → index-f8bb839b399b809d73376d962613fce2.js.map.br} +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{index-11f21e9e7b941cc98daf2ca7e3a78592.js.map.gz → index-f8bb839b399b809d73376d962613fce2.js.map.gz} +0 -0
- package/dist/server/web-ui/index.html +1 -1
- package/dist/server/web-ui/index.html.br +0 -0
- package/dist/server/web-ui/index.html.gz +0 -0
- package/package.json +6 -6
- package/dist/server/web-ui/_expo/static/js/web/index-11f21e9e7b941cc98daf2ca7e3a78592.js.br +0 -0
|
@@ -193,14 +193,68 @@ export declare class PtyQuery implements AsyncGenerator<SDKMessage, void> {
|
|
|
193
193
|
* keys just leaves the composer as it was.
|
|
194
194
|
*/
|
|
195
195
|
private clearComposer;
|
|
196
|
-
|
|
196
|
+
/**
|
|
197
|
+
* Deliver a prompt and prove it, or say it failed. See docs/pty-delivery.md.
|
|
198
|
+
*
|
|
199
|
+
* One authority: a transcript user record matching the text. Nothing the terminal paints
|
|
200
|
+
* gates this - echoed characters, paste chips, boot noise and footer hints are renderings,
|
|
201
|
+
* and renderings changed with every CLI version while this code kept "discovering" new
|
|
202
|
+
* ones (bytes -> text -> raw probe -> paste chip, four incidents, same defect each time).
|
|
203
|
+
*
|
|
204
|
+
* Retries are safe because the composer is cleared first, and a message QUEUED behind a
|
|
205
|
+
* running turn is never retyped - that is the one way to send it twice.
|
|
206
|
+
*/
|
|
207
|
+
private deliverPrompt;
|
|
208
|
+
/**
|
|
209
|
+
* Press Enter to submit the composed prompt, and CONFIRM the turn actually started by
|
|
210
|
+
* watching for the USER LINE claude appends to <sid>.jsonl the instant it accepts a turn.
|
|
211
|
+
* During boot the TUI intermittently swallows the first Enter (a "what's new" notice /
|
|
212
|
+
* MCP-auth warning steals focus), so retry until it takes.
|
|
213
|
+
*
|
|
214
|
+
* The confirmation used to be `transcriptSize() > before`, which is any growth at all.
|
|
215
|
+
* That is not specific to the turn starting: claude writes `attachment`, `last-prompt`
|
|
216
|
+
* and other bookkeeping lines on its own schedule, so a swallowed Enter that happened to
|
|
217
|
+
* coincide with unrelated writes read as success. Observed in production 2026-08-01: a
|
|
218
|
+
* session logged 157 consecutive `attachment` lines and NOT ONE `user` line, yet
|
|
219
|
+
* submitTurn returned on the first attempt. The prompt stayed unsent in the TUI composer,
|
|
220
|
+
* paseo set turnInFlight and waited on a turn that had never begun, and the agent showed
|
|
221
|
+
* a spinner until the liveness backstop fired ten minutes later. The retry loop below was
|
|
222
|
+
* always right; the thing it was checking was not.
|
|
223
|
+
*/
|
|
224
|
+
/**
|
|
225
|
+
* Type the prompt and confirm the terminal actually received it, retrying against a
|
|
226
|
+
* deaf terminal with backoff.
|
|
227
|
+
*
|
|
228
|
+
* Returns true once an echo (or a transcript user line) confirms the keystrokes landed;
|
|
229
|
+
* false only after the terminal stayed deaf for the whole {@link READY_BUSY_MAX_WAIT_MS}
|
|
230
|
+
* ceiling. Retyping cannot double-deliver: a deaf terminal demonstrably discards input
|
|
231
|
+
* (zero echo), and every iteration first checks whether a user line landed anyway.
|
|
232
|
+
*/
|
|
197
233
|
private submitTurn;
|
|
198
234
|
/**
|
|
199
235
|
* True once a real `user` line has been appended past `offset`. Reads only the bytes
|
|
200
236
|
* added since then, so it stays cheap on a transcript that can reach tens of MB.
|
|
201
237
|
* `isMeta` user lines are claude's own bookkeeping and do not mean a turn started.
|
|
202
238
|
*/
|
|
203
|
-
|
|
239
|
+
/**
|
|
240
|
+
* Fingerprint used to match a transcript record against what we typed. Whitespace is
|
|
241
|
+
* stripped on both sides because the terminal is free to re-wrap and re-space anything
|
|
242
|
+
* it renders, while the transcript keeps the text verbatim - so the character sequence
|
|
243
|
+
* is the part that survives every rendering.
|
|
244
|
+
*/
|
|
245
|
+
private static fingerprint;
|
|
246
|
+
/**
|
|
247
|
+
* THE delivery authority: did claude append a user record matching what we sent?
|
|
248
|
+
*
|
|
249
|
+
* Content-matched on purpose. The old check accepted ANY user record past the watermark,
|
|
250
|
+
* which cannot tell "my message became a turn" apart from an unrelated record (a resume
|
|
251
|
+
* dialog answering itself, a queued earlier message). Matching the text answers the only
|
|
252
|
+
* question that matters, and answers it identically whether the terminal echoed the
|
|
253
|
+
* characters, collapsed them into a paste chip, or drew nothing at all.
|
|
254
|
+
*/
|
|
255
|
+
private receiptSince;
|
|
256
|
+
/** Text of every non-meta user record written past `offset`. */
|
|
257
|
+
private readUserRecordsSince;
|
|
204
258
|
private transcriptSize;
|
|
205
259
|
private onTranscriptMessage;
|
|
206
260
|
/**
|
|
@@ -86,7 +86,6 @@ const SLASH_COMMAND_ECHO_WAIT_MS = 3000;
|
|
|
86
86
|
* "idle-looking but deaf", and its absence also proves the keystrokes were discarded,
|
|
87
87
|
* which is exactly what makes retyping safe rather than a duplication risk.
|
|
88
88
|
*/
|
|
89
|
-
const ECHO_VERIFY_WINDOW_MS = 1500;
|
|
90
89
|
/**
|
|
91
90
|
* How much of the typed text must be found in the terminal output for it to count as an
|
|
92
91
|
* echo. Byte-counting was not enough: a freshly spawned claude under load keeps painting
|
|
@@ -95,18 +94,16 @@ const ECHO_VERIFY_WINDOW_MS = 1500;
|
|
|
95
94
|
* spawns failed that way in five minutes on a loaded box, 2026-08-03. Boot output never
|
|
96
95
|
* contains the user's message; a real input-box echo always does.
|
|
97
96
|
*/
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
*/
|
|
107
|
-
const PASTE_CHIP_MARKERS = ["pastedtext#", "pasteagaintoexpand"];
|
|
97
|
+
/** How much of the message identifies it in a transcript record. */
|
|
98
|
+
const RECEIPT_FINGERPRINT_CHARS = 64;
|
|
99
|
+
/** Below this length a fingerprint must match a record WHOLE, never as a substring. */
|
|
100
|
+
const RECEIPT_EXACT_MATCH_BELOW_CHARS = 12;
|
|
101
|
+
/** How long one delivery attempt waits for its transcript receipt before retrying. */
|
|
102
|
+
const RECEIPT_WINDOW_MS = 4000;
|
|
103
|
+
/** A queued message can only land when the running turn ends, so wait far longer for it. */
|
|
104
|
+
const QUEUED_RECEIPT_WINDOW_MS = 10 * 60000;
|
|
108
105
|
/** Backoff between retype attempts against a deaf terminal. */
|
|
109
|
-
const DEAF_RETRY_INITIAL_MS =
|
|
106
|
+
const DEAF_RETRY_INITIAL_MS = 2000;
|
|
110
107
|
const DEAF_RETRY_MAX_MS = 60000;
|
|
111
108
|
const TERMINAL_STOP_REASONS = new Set(["end_turn", "stop_sequence", "max_tokens"]);
|
|
112
109
|
/**
|
|
@@ -495,45 +492,28 @@ export class PtyQuery {
|
|
|
495
492
|
// handled by relaying the CLI's own rejection -- including its "did you mean"
|
|
496
493
|
// suggestion -- back to the sender, which corrects every rejection cause honestly
|
|
497
494
|
// instead of hiding one narrow cause invisibly.
|
|
498
|
-
const
|
|
495
|
+
const delivery = await this.deliverPrompt(text);
|
|
499
496
|
if (this.done)
|
|
500
497
|
break;
|
|
501
|
-
if (
|
|
502
|
-
// The terminal stayed deaf for the whole ceiling: keystrokes discarded, nothing
|
|
503
|
-
// delivered, and only the sender can decide what to do next.
|
|
504
|
-
this.recordLine("--- terminal deaf for the whole budget; message not delivered ---");
|
|
505
|
-
this.emit(this.buildSubmitFailedResult());
|
|
506
|
-
continue;
|
|
507
|
-
}
|
|
508
|
-
const started = await this.submitTurn(text);
|
|
509
|
-
if (!started && this.done)
|
|
510
|
-
break;
|
|
511
|
-
if (!started && this.rejectedCommandNotice) {
|
|
498
|
+
if (delivery === "rejected") {
|
|
512
499
|
// Not lost, refused: hand the CLI's own answer (which includes its "did you
|
|
513
500
|
// mean" suggestion) to the sender instead of a phantom running turn.
|
|
514
|
-
const notice = this.rejectedCommandNotice;
|
|
501
|
+
const notice = this.rejectedCommandNotice ?? "the command was not recognized.";
|
|
515
502
|
this.rejectedCommandNotice = null;
|
|
516
503
|
this.emit(this.buildCommandRejectedResult(notice));
|
|
517
504
|
continue;
|
|
518
505
|
}
|
|
519
|
-
if (
|
|
520
|
-
//
|
|
521
|
-
//
|
|
522
|
-
|
|
523
|
-
// delivered at 23:04:57. Telling the user it was lost is worse than saying
|
|
524
|
-
// nothing, because they resend and the agent gets it twice.
|
|
525
|
-
//
|
|
526
|
-
// A busy terminal has accepted the input, so treat the turn as started and let
|
|
527
|
-
// the stall backstop be the thing that notices if it never runs.
|
|
528
|
-
this.recordLine("--- submit queued behind a running turn ---");
|
|
529
|
-
}
|
|
530
|
-
else if (!started) {
|
|
531
|
-
// Idle terminal, five Enters, still no user line: the prompt genuinely did not
|
|
532
|
-
// land and only the user can resend it.
|
|
533
|
-
this.recordLine("--- submit failed: prompt never became a turn ---");
|
|
506
|
+
if (delivery === "undelivered") {
|
|
507
|
+
// No transcript receipt for the whole budget: the message never became a turn,
|
|
508
|
+
// nothing was queued, and only the sender can decide what to do next.
|
|
509
|
+
this.recordLine("--- no delivery receipt within the budget; message not delivered ---");
|
|
534
510
|
this.emit(this.buildSubmitFailedResult());
|
|
535
511
|
continue;
|
|
536
512
|
}
|
|
513
|
+
// "unconfirmed" is the slash-command case: exactly one Enter was sent and the CLI
|
|
514
|
+
// reacted, but a slash command may append no user record to confirm against.
|
|
515
|
+
// Treat it as started and let the stall backstop notice if it never runs -- never
|
|
516
|
+
// press Enter again, which would run the command twice.
|
|
537
517
|
// The turn is now claude's to answer; start the liveness clock. submitTurn() has
|
|
538
518
|
// confirmed claude accepted it (a real user line landed), so from here on silence
|
|
539
519
|
// means a stall, not a slow start.
|
|
@@ -817,51 +797,86 @@ export class PtyQuery {
|
|
|
817
797
|
this.logger.debug({ err }, "PtyQuery: could not clear the composer before retyping");
|
|
818
798
|
}
|
|
819
799
|
}
|
|
820
|
-
|
|
821
|
-
|
|
800
|
+
/**
|
|
801
|
+
* Deliver a prompt and prove it, or say it failed. See docs/pty-delivery.md.
|
|
802
|
+
*
|
|
803
|
+
* One authority: a transcript user record matching the text. Nothing the terminal paints
|
|
804
|
+
* gates this - echoed characters, paste chips, boot noise and footer hints are renderings,
|
|
805
|
+
* and renderings changed with every CLI version while this code kept "discovering" new
|
|
806
|
+
* ones (bytes -> text -> raw probe -> paste chip, four incidents, same defect each time).
|
|
807
|
+
*
|
|
808
|
+
* Retries are safe because the composer is cleared first, and a message QUEUED behind a
|
|
809
|
+
* running turn is never retyped - that is the one way to send it twice.
|
|
810
|
+
*/
|
|
811
|
+
async deliverPrompt(text) {
|
|
812
|
+
const fingerprint = PtyQuery.fingerprint(text);
|
|
813
|
+
const watermark = this.transcriptSize();
|
|
822
814
|
const deadline = monotonicNowMs() + READY_BUSY_MAX_WAIT_MS;
|
|
823
815
|
let backoff = DEAF_RETRY_INITIAL_MS;
|
|
824
816
|
let attempt = 0;
|
|
825
817
|
while (!this.done && monotonicNowMs() < deadline) {
|
|
826
818
|
attempt += 1;
|
|
827
|
-
|
|
828
|
-
//
|
|
829
|
-
//
|
|
830
|
-
//
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
this.
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
echoed = await pollUntil(() => sawEcho(probe.buf, needle), ECHO_VERIFY_WINDOW_MS, 100);
|
|
839
|
-
}
|
|
840
|
-
finally {
|
|
841
|
-
this.echoProbe = null;
|
|
842
|
-
}
|
|
843
|
-
if (echoed) {
|
|
819
|
+
const attemptStartedAt = monotonicNowMs();
|
|
820
|
+
// Empty the input box first: whatever an earlier attempt may have left there is
|
|
821
|
+
// undelivered by definition (no receipt), and typing on top of it is how one message
|
|
822
|
+
// became nine stacked paste blocks in production.
|
|
823
|
+
if (attempt > 1)
|
|
824
|
+
await this.clearComposer();
|
|
825
|
+
await this.transport.write(text);
|
|
826
|
+
const submitted = await this.submitTurn(text, watermark, fingerprint);
|
|
827
|
+
if (submitted === "rejected")
|
|
828
|
+
return "rejected";
|
|
829
|
+
if (submitted === "delivered") {
|
|
844
830
|
if (attempt > 1) {
|
|
845
|
-
this.recordLine(`---
|
|
831
|
+
this.recordLine(`--- delivered on attempt ${attempt} ---`);
|
|
846
832
|
}
|
|
847
|
-
|
|
848
|
-
return true;
|
|
833
|
+
return "delivered";
|
|
849
834
|
}
|
|
850
|
-
//
|
|
851
|
-
//
|
|
852
|
-
//
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
835
|
+
// No receipt. Is a turn actually RUNNING (so our text is queued behind it), or was
|
|
836
|
+
// the Enter simply swallowed? File growth cannot tell those apart: claude writes
|
|
837
|
+
// `attachment` and other bookkeeping records on its own schedule, and treating that
|
|
838
|
+
// as "queued" would sit here for ten minutes while the prompt was never submitted
|
|
839
|
+
// (the 2026-08-01 bug, in reverse). Real SDK messages arriving IS a running turn.
|
|
840
|
+
if (this.lastTranscriptAt > attemptStartedAt) {
|
|
841
|
+
this.recordLine("--- no receipt yet; queued behind a running turn ---");
|
|
842
|
+
const late = await pollUntil(() => this.receiptSince(watermark, fingerprint), QUEUED_RECEIPT_WINDOW_MS, 500);
|
|
843
|
+
if (late)
|
|
844
|
+
return "delivered";
|
|
845
|
+
}
|
|
846
|
+
this.logger.warn({ sessionId: this.sessionId, attempt, nextRetryMs: backoff }, "PtyQuery: no delivery receipt for the prompt; retrying");
|
|
847
|
+
this.recordLine(`--- no delivery receipt (attempt ${attempt}); retrying in ${Math.round(backoff / 1000)}s ---`);
|
|
858
848
|
await delay(backoff);
|
|
859
849
|
backoff = Math.min(backoff * 2, DEAF_RETRY_MAX_MS);
|
|
860
850
|
}
|
|
861
|
-
return
|
|
851
|
+
return "undelivered";
|
|
862
852
|
}
|
|
863
|
-
|
|
864
|
-
|
|
853
|
+
/**
|
|
854
|
+
* Press Enter to submit the composed prompt, and CONFIRM the turn actually started by
|
|
855
|
+
* watching for the USER LINE claude appends to <sid>.jsonl the instant it accepts a turn.
|
|
856
|
+
* During boot the TUI intermittently swallows the first Enter (a "what's new" notice /
|
|
857
|
+
* MCP-auth warning steals focus), so retry until it takes.
|
|
858
|
+
*
|
|
859
|
+
* The confirmation used to be `transcriptSize() > before`, which is any growth at all.
|
|
860
|
+
* That is not specific to the turn starting: claude writes `attachment`, `last-prompt`
|
|
861
|
+
* and other bookkeeping lines on its own schedule, so a swallowed Enter that happened to
|
|
862
|
+
* coincide with unrelated writes read as success. Observed in production 2026-08-01: a
|
|
863
|
+
* session logged 157 consecutive `attachment` lines and NOT ONE `user` line, yet
|
|
864
|
+
* submitTurn returned on the first attempt. The prompt stayed unsent in the TUI composer,
|
|
865
|
+
* paseo set turnInFlight and waited on a turn that had never begun, and the agent showed
|
|
866
|
+
* a spinner until the liveness backstop fired ten minutes later. The retry loop below was
|
|
867
|
+
* always right; the thing it was checking was not.
|
|
868
|
+
*/
|
|
869
|
+
/**
|
|
870
|
+
* Type the prompt and confirm the terminal actually received it, retrying against a
|
|
871
|
+
* deaf terminal with backoff.
|
|
872
|
+
*
|
|
873
|
+
* Returns true once an echo (or a transcript user line) confirms the keystrokes landed;
|
|
874
|
+
* false only after the terminal stayed deaf for the whole {@link READY_BUSY_MAX_WAIT_MS}
|
|
875
|
+
* ceiling. Retyping cannot double-deliver: a deaf terminal demonstrably discards input
|
|
876
|
+
* (zero echo), and every iteration first checks whether a user line landed anyway.
|
|
877
|
+
*/
|
|
878
|
+
async submitTurn(text, watermark, fingerprint) {
|
|
879
|
+
const before = watermark;
|
|
865
880
|
// A slash command gets EXACTLY ONE Enter. Never retry it.
|
|
866
881
|
//
|
|
867
882
|
// Typing "/x" opens the TUI's own command menu, and in that state each Enter runs the
|
|
@@ -885,7 +900,7 @@ export class PtyQuery {
|
|
|
885
900
|
if (text.trim().startsWith("/")) {
|
|
886
901
|
const bytesBefore = this.ptyBytesTotal;
|
|
887
902
|
await this.transport.writeRaw?.("\r");
|
|
888
|
-
const reacted = await pollUntil(() => this.
|
|
903
|
+
const reacted = await pollUntil(() => this.receiptSince(before, fingerprint) || this.ptyBytesTotal > bytesBefore, SLASH_COMMAND_ECHO_WAIT_MS, 150);
|
|
889
904
|
// The terminal responding is not the same as the command running. An unknown command
|
|
890
905
|
// is answered with `Unknown command: /X. Did you mean /y?` and NOTHING starts -- but
|
|
891
906
|
// that reply previously counted as acceptance, so the app showed a running turn that
|
|
@@ -899,42 +914,74 @@ export class PtyQuery {
|
|
|
899
914
|
if (m) {
|
|
900
915
|
this.rejectedCommandNotice = m[0].trim();
|
|
901
916
|
this.recordLine(`--- CLI rejected the command: ${this.rejectedCommandNotice} ---`);
|
|
902
|
-
return
|
|
917
|
+
return "rejected";
|
|
903
918
|
}
|
|
904
919
|
if (!reacted) {
|
|
905
920
|
this.logger.warn({ sessionId: this.sessionId, command: text.trim().split(/\s/, 1)[0] }, "PtyQuery: slash command produced no terminal response; not retrying to avoid double execution");
|
|
906
921
|
}
|
|
907
|
-
|
|
922
|
+
// A slash command may not append a user record at all (version-dependent), so the
|
|
923
|
+
// CLI's own reaction stands in for the receipt here. This reads a CLI RESPONSE, not
|
|
924
|
+
// a rendering, and it is never retried: a lost slash command is recoverable by
|
|
925
|
+
// resending, a duplicated one is not.
|
|
926
|
+
return reacted ? "delivered" : "unconfirmed";
|
|
908
927
|
}
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
return true;
|
|
928
|
+
await this.transport.writeRaw?.("\r");
|
|
929
|
+
if (await pollUntil(() => this.receiptSince(before, fingerprint), RECEIPT_WINDOW_MS, 150)) {
|
|
930
|
+
return "delivered";
|
|
913
931
|
}
|
|
914
|
-
//
|
|
915
|
-
//
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
this.logger[queued ? "info" : "warn"]({
|
|
919
|
-
sessionId: this.sessionId,
|
|
920
|
-
queuedBehindRunningTurn: queued,
|
|
921
|
-
lastLine: this.lastMeaningfulLine.slice(-120),
|
|
922
|
-
}, queued
|
|
923
|
-
? "PtyQuery: prompt queued behind a running turn, no user line yet"
|
|
924
|
-
: "PtyQuery: turn did not start after submit retries");
|
|
925
|
-
return false;
|
|
932
|
+
// No receipt from this attempt. The delivery loop decides what that means: it can see
|
|
933
|
+
// whether the transcript is moving (queued behind a running turn) and owns the retry.
|
|
934
|
+
this.logger.debug({ sessionId: this.sessionId, lastLine: this.lastMeaningfulLine.slice(-120) }, "PtyQuery: no receipt for this submit attempt");
|
|
935
|
+
return "unconfirmed";
|
|
926
936
|
}
|
|
927
937
|
/**
|
|
928
938
|
* True once a real `user` line has been appended past `offset`. Reads only the bytes
|
|
929
939
|
* added since then, so it stays cheap on a transcript that can reach tens of MB.
|
|
930
940
|
* `isMeta` user lines are claude's own bookkeeping and do not mean a turn started.
|
|
931
941
|
*/
|
|
932
|
-
|
|
942
|
+
/**
|
|
943
|
+
* Fingerprint used to match a transcript record against what we typed. Whitespace is
|
|
944
|
+
* stripped on both sides because the terminal is free to re-wrap and re-space anything
|
|
945
|
+
* it renders, while the transcript keeps the text verbatim - so the character sequence
|
|
946
|
+
* is the part that survives every rendering.
|
|
947
|
+
*/
|
|
948
|
+
static fingerprint(text) {
|
|
949
|
+
return text.replace(/\s+/g, "").slice(0, RECEIPT_FINGERPRINT_CHARS);
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* THE delivery authority: did claude append a user record matching what we sent?
|
|
953
|
+
*
|
|
954
|
+
* Content-matched on purpose. The old check accepted ANY user record past the watermark,
|
|
955
|
+
* which cannot tell "my message became a turn" apart from an unrelated record (a resume
|
|
956
|
+
* dialog answering itself, a queued earlier message). Matching the text answers the only
|
|
957
|
+
* question that matters, and answers it identically whether the terminal echoed the
|
|
958
|
+
* characters, collapsed them into a paste chip, or drew nothing at all.
|
|
959
|
+
*/
|
|
960
|
+
receiptSince(offset, fingerprint) {
|
|
961
|
+
for (const record of this.readUserRecordsSince(offset)) {
|
|
962
|
+
const body = PtyQuery.fingerprint(record);
|
|
963
|
+
if (!fingerprint)
|
|
964
|
+
return true;
|
|
965
|
+
// Short messages must match whole, or a two-letter fingerprint like "ok" would
|
|
966
|
+
// happily match an unrelated record that merely contains those letters.
|
|
967
|
+
if (fingerprint.length < RECEIPT_EXACT_MATCH_BELOW_CHARS) {
|
|
968
|
+
if (body === fingerprint)
|
|
969
|
+
return true;
|
|
970
|
+
continue;
|
|
971
|
+
}
|
|
972
|
+
if (body.includes(fingerprint))
|
|
973
|
+
return true;
|
|
974
|
+
}
|
|
975
|
+
return false;
|
|
976
|
+
}
|
|
977
|
+
/** Text of every non-meta user record written past `offset`. */
|
|
978
|
+
readUserRecordsSince(offset) {
|
|
933
979
|
let fd = null;
|
|
980
|
+
const out = [];
|
|
934
981
|
try {
|
|
935
982
|
const size = this.transcriptSize();
|
|
936
983
|
if (size <= offset)
|
|
937
|
-
return
|
|
984
|
+
return out;
|
|
938
985
|
fd = fs.openSync(this.transcriptPath, "r");
|
|
939
986
|
const length = size - offset;
|
|
940
987
|
const buffer = Buffer.alloc(length);
|
|
@@ -945,17 +992,18 @@ export class PtyQuery {
|
|
|
945
992
|
try {
|
|
946
993
|
// A trailing partial line simply fails to parse; the next poll sees it whole.
|
|
947
994
|
const parsed = JSON.parse(line);
|
|
948
|
-
if (parsed?.type
|
|
949
|
-
|
|
995
|
+
if (parsed?.type !== "user" || parsed.isMeta)
|
|
996
|
+
continue;
|
|
997
|
+
out.push(userRecordText(parsed.message?.content));
|
|
950
998
|
}
|
|
951
999
|
catch {
|
|
952
1000
|
/* partial or non-JSON line */
|
|
953
1001
|
}
|
|
954
1002
|
}
|
|
955
|
-
return
|
|
1003
|
+
return out;
|
|
956
1004
|
}
|
|
957
1005
|
catch {
|
|
958
|
-
return
|
|
1006
|
+
return out;
|
|
959
1007
|
}
|
|
960
1008
|
finally {
|
|
961
1009
|
if (fd !== null) {
|
|
@@ -1370,17 +1418,20 @@ function matchDialogOption(text, options) {
|
|
|
1370
1418
|
const byLabel = options.findIndex((o) => o.trim().toLowerCase() === lowered);
|
|
1371
1419
|
return byLabel >= 0 ? byLabel : null;
|
|
1372
1420
|
}
|
|
1373
|
-
/**
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
const
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1421
|
+
/** Flatten a transcript user record's content to plain text for fingerprint matching. */
|
|
1422
|
+
function userRecordText(content) {
|
|
1423
|
+
if (typeof content === "string")
|
|
1424
|
+
return content;
|
|
1425
|
+
if (!Array.isArray(content))
|
|
1426
|
+
return "";
|
|
1427
|
+
const parts = [];
|
|
1428
|
+
for (const block of content) {
|
|
1429
|
+
if (typeof block === "string")
|
|
1430
|
+
parts.push(block);
|
|
1431
|
+
else if (isRecord(block) && typeof block.text === "string")
|
|
1432
|
+
parts.push(block.text);
|
|
1433
|
+
}
|
|
1434
|
+
return parts.join(" ");
|
|
1384
1435
|
}
|
|
1385
1436
|
function isRecord(value) {
|
|
1386
1437
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
@@ -1103,7 +1103,7 @@ __d(function(g,r,_i,a,m,_e,d){"use strict";function e(e){return e&&e.__esModule?
|
|
|
1103
1103
|
__d(function(g,r,i,a,m,_e,d){"use strict";function e(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(_e,'__esModule',{value:!0}),Object.defineProperty(_e,"default",{enumerable:!0,get:function(){return N}}),Object.defineProperty(_e,"AppOwnership",{enumerable:!0,get:function(){return l.AppOwnership}}),Object.defineProperty(_e,"ExecutionEnvironment",{enumerable:!0,get:function(){return l.ExecutionEnvironment}}),Object.defineProperty(_e,"UserInterfaceIdiom",{enumerable:!0,get:function(){return l.UserInterfaceIdiom}});var n=e(r(d[0])),t=r(d[1]);r(d[2]);var u=e(r(d[3])),l=r(d[4]),o=e(r(d[5]));o.default||console.warn("No native ExponentConstants module found, are you sure the expo-constants's module is linked properly?");const s=(0,t.requireOptionalNativeModule)('ExpoUpdates');let f=null;if(s){let e;s.manifest?e=s.manifest:s.manifestString&&(e=JSON.parse(s.manifestString)),e&&Object.keys(e).length>0&&(f=e)}let c=null;if(u.default.EXDevLauncher){let e;u.default.EXDevLauncher.manifestString&&(e=JSON.parse(u.default.EXDevLauncher.manifestString)),e&&Object.keys(e).length>0&&(c=e)}let p=null;if(o.default&&o.default.manifest){const e=o.default.manifest;p='string'==typeof e?JSON.parse(e):e}let b=f??c??p;const E=o.default||{},{appOwnership:O}=E,x=(0,n.default)(E,["name","appOwnership"]),v=Object.assign({},x,{appOwnership:O??null});function _(e){return!h(e)}function h(e){return'metadata'in e}function S(e=!1){if(!b){const e=null===b?'null':'undefined';if(x.executionEnvironment,l.ExecutionEnvironment.Bare,x.executionEnvironment===l.ExecutionEnvironment.StoreClient||x.executionEnvironment===l.ExecutionEnvironment.Standalone)throw new t.CodedError('ERR_CONSTANTS_MANIFEST_UNAVAILABLE',`Constants.manifest is ${e}, must be an object.`)}return b}Object.defineProperties(v,{__unsafeNoWarnManifest:{get(){const e=S(!0);return e&&_(e)?e:null},enumerable:!1},__unsafeNoWarnManifest2:{get(){const e=S(!0);return e&&h(e)?e:null},enumerable:!1},manifest:{get(){const e=S();return e&&_(e)?e:null},enumerable:!0},manifest2:{get(){const e=S();return e&&h(e)?e:null},enumerable:!0},expoConfig:{get(){const e=S(!0);return e?s&&s.isEmbeddedLaunch?p:h(e)?e.extra?.expoClient??null:_(e)?e:null:null},enumerable:!0},expoGoConfig:{get(){const e=S(!0);return e?h(e)?e.extra?.expoGo??null:_(e)?e:null:null},enumerable:!0},easConfig:{get(){const e=S(!0);return e?h(e)?e.extra?.eas??null:_(e)?e:null:null},enumerable:!0},__rawManifest_TEST:{get:()=>b,set(e){b=e},enumerable:!1}});var N=v},1006,[35,4,25,1007,1008,1009]);
|
|
1104
1104
|
__d(function(g,r,i,a,m,_e,d){"use strict";Object.defineProperty(_e,'__esModule',{value:!0}),Object.defineProperty(_e,"default",{enumerable:!0,get:function(){return u}});var e,t=r(d[0]),u={UIManager:((e=t)&&e.__esModule?e:{default:e}).default}},1007,[159]);
|
|
1105
1105
|
__d(function(g,r,i,a,m,e,d){"use strict";var t,n,o;Object.defineProperty(e,'__esModule',{value:!0}),Object.defineProperty(e,"AppOwnership",{enumerable:!0,get:function(){return t}}),Object.defineProperty(e,"ExecutionEnvironment",{enumerable:!0,get:function(){return n}}),Object.defineProperty(e,"UserInterfaceIdiom",{enumerable:!0,get:function(){return o}}),(function(t){t.Expo="expo"})(t||(t={})),(function(t){t.Bare="bare",t.Standalone="standalone",t.StoreClient="storeClient"})(n||(n={})),(function(t){t.Handset="handset",t.Tablet="tablet",t.Desktop="desktop",t.TV="tv",t.Unsupported="unsupported"})(o||(o={}))},1008,[]);
|
|
1106
|
-
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return s}});var t=r(d[0]);const n=(Date.now()+'-'+Math.floor(1e9*Math.random())).toString();function o(){if('undefined'!=typeof navigator&&'string'==typeof navigator.userAgent){const t=navigator.userAgent.toLowerCase();if(t.includes('edge'))return'Edge';if(t.includes('edg'))return'Chromium Edge';if(t.includes('opr')&&'opr'in window&&window.opr)return'Opera';if(t.includes('chrome')&&'chrome'in window&&window.chrome)return'Chrome';if(t.includes('trident'))return'IE';if(t.includes('firefox'))return'Firefox';if(t.includes('safari'))return'Safari'}}var s={get appOwnership(){return null},get executionEnvironment(){return t.ExecutionEnvironment.Bare},get sessionId(){return n},get isHeadless(){return'undefined'==typeof navigator||/\bHeadlessChrome\//.test(navigator.userAgent)},get expoVersion(){return this.manifest.sdkVersion||null},get linkingUri(){return'undefined'!=typeof location?location.origin:''},get expoRuntimeVersion(){return this.expoVersion},get deviceName(){return o()},get systemFonts(){return[]},get statusBarHeight(){return 0},get deviceYearClass(){return null},get manifest(){return"{\"name\":\"Paseo\",\"slug\":\"paseo-hyperdrive\",\"version\":\"0.3.
|
|
1106
|
+
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return s}});var t=r(d[0]);const n=(Date.now()+'-'+Math.floor(1e9*Math.random())).toString();function o(){if('undefined'!=typeof navigator&&'string'==typeof navigator.userAgent){const t=navigator.userAgent.toLowerCase();if(t.includes('edge'))return'Edge';if(t.includes('edg'))return'Chromium Edge';if(t.includes('opr')&&'opr'in window&&window.opr)return'Opera';if(t.includes('chrome')&&'chrome'in window&&window.chrome)return'Chrome';if(t.includes('trident'))return'IE';if(t.includes('firefox'))return'Firefox';if(t.includes('safari'))return'Safari'}}var s={get appOwnership(){return null},get executionEnvironment(){return t.ExecutionEnvironment.Bare},get sessionId(){return n},get isHeadless(){return'undefined'==typeof navigator||/\bHeadlessChrome\//.test(navigator.userAgent)},get expoVersion(){return this.manifest.sdkVersion||null},get linkingUri(){return'undefined'!=typeof location?location.origin:''},get expoRuntimeVersion(){return this.expoVersion},get deviceName(){return o()},get systemFonts(){return[]},get statusBarHeight(){return 0},get deviceYearClass(){return null},get manifest(){return"{\"name\":\"Paseo\",\"slug\":\"paseo-hyperdrive\",\"version\":\"0.3.28\",\"orientation\":\"portrait\",\"icon\":\"./assets/images/icon.png\",\"scheme\":\"paseo\",\"userInterfaceStyle\":\"automatic\",\"newArchEnabled\":true,\"web\":{\"output\":\"single\",\"favicon\":\"./assets/images/favicon.png\",\"shortName\":\"Paseo\",\"orientation\":\"portrait\",\"name\":\"Paseo\"},\"autolinking\":{\"searchPaths\":[\"../../node_modules\",\"./node_modules\"]},\"experiments\":{\"typedRoutes\":true,\"reactCompiler\":true,\"autolinkingModuleResolution\":true},\"extra\":{\"router\":{},\"eas\":{\"build\":{\"experimental\":{\"ios\":{\"appExtensions\":[{\"bundleIdentifier\":\"bot.hyperdrive.paseo.AgentActivity\",\"targetName\":\"AgentActivity\"}]}}}}},\"sdkVersion\":\"54.0.0\",\"platforms\":[\"ios\",\"android\",\"web\"]}"},get manifest2(){return null},get experienceUrl(){return'undefined'!=typeof location?location.origin:''},get debugMode(){return!1},getWebViewUserAgentAsync:async()=>'undefined'!=typeof navigator?navigator.userAgent:null}},1009,[1008]);
|
|
1107
1107
|
__d(function(g,r,i,a,m,_e,d){"use strict";Object.defineProperty(_e,'__esModule',{value:!0}),Object.defineProperty(_e,"default",{enumerable:!0,get:function(){return u}});var e,t=r(d[0]),n=r(d[1]),o=(e=n)&&e.__esModule?e:{default:e};async function u(){if(!o.default.unregisterForNotificationsAsync)throw new t.UnavailabilityError('ExpoNotifications','unregisterForNotificationsAsync');return o.default.unregisterForNotificationsAsync()}},1010,[4,1011]);
|
|
1108
1108
|
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return n}}),r(d[0]);let t=!1;var n={addListener:()=>(t||(console.warn("[expo-notifications] Listening to push token changes is not yet fully supported on web. Adding a listener will have no effect."),t=!0),{remove:()=>{}}),removeListener:()=>{},removeAllListeners:()=>{},emit:()=>{},listenerCount:()=>0}},1011,[4]);
|
|
1109
1109
|
__d(function(g,r,i,a,m,_e,_d){"use strict";function e(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(_e,'__esModule',{value:!0}),Object.defineProperty(_e,"default",{enumerable:!0,get:function(){return u}});var t=(function(e){if(e&&e.__esModule)return e;var t={};return e&&Object.keys(e).forEach(function(o){var n=Object.getOwnPropertyDescriptor(e,o);Object.defineProperty(t,o,n.get?n:{enumerable:!0,get:function(){return e[o]}})}),t.default=e,t})(r(_d[0])),o=e(r(_d[1])),n=r(_d[2]),c=r(_d[3]),s=e(r(_d[4])),d=e(r(_d[5]));const p='https://exp.host/--/api/v2/';async function u(e={}){const s=e.devicePushToken||await(0,d.default)(),u=e.deviceId||await h(),R=e.projectId||o.default.easConfig?.projectId||o.default.expoConfig?.extra?.eas?.projectId;if(!R)throw new n.CodedError('ERR_NOTIFICATIONS_NO_EXPERIENCE_ID',"No \"projectId\" found. If \"projectId\" can't be inferred from the manifest (for instance, in bare workflow), you have to pass it in yourself.");const w=e.applicationId||t.applicationId;if(!w)throw new n.CodedError('ERR_NOTIFICATIONS_NO_APPLICATION_ID',"No \"applicationId\" found. If it can't be inferred from native configuration by expo-application, you have to pass it in yourself.");const O=e.type||y(s),_=e.development||await I(),v=e.baseUrl??p,N=e.url??`${v}push/getExpoPushToken`,x={type:O,deviceId:u.toLowerCase(),development:_,appId:w,deviceToken:E(s),projectId:R},T=await fetch(N,{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify(x)}).catch(e=>{throw new n.CodedError('ERR_NOTIFICATIONS_NETWORK_ERROR',`Error encountered while fetching Expo token: ${e}.`)});if(!T.ok){const e=T.statusText||T.status;let t;try{t=await T.text()}catch{}throw new n.CodedError('ERR_NOTIFICATIONS_SERVER_ERROR',`Error encountered while fetching Expo token, expected an OK response, received: ${e} (body: "${t}").`)}const b=l(await f(T));try{e.url||e.baseUrl?console.debug("[expo-notifications] Since the URL endpoint to register in has been customized in the options, expo-notifications won't try to auto-update the device push token on the server."):await(0,c.setAutoServerRegistrationEnabledAsync)(!0)}catch(e){console.warn('[expo-notifications] Could not enable automatically registering new device tokens with the Expo notification service',e)}return{type:'expo',data:b}}async function f(e){try{return await e.json()}catch{try{throw new n.CodedError('ERR_NOTIFICATIONS_SERVER_ERROR',`Expected a JSON response from server when fetching Expo token, received body: ${JSON.stringify(await e.text())}.`)}catch{throw new n.CodedError('ERR_NOTIFICATIONS_SERVER_ERROR',`Expected a JSON response from server when fetching Expo token, received response: ${JSON.stringify(e)}.`)}}}function l(e){if(!e||'object'!=typeof e||!e.data||'object'!=typeof e.data||!e.data.expoPushToken||'string'!=typeof e.data.expoPushToken)throw new n.CodedError('ERR_NOTIFICATIONS_SERVER_ERROR',`Malformed response from server, expected "{ data: { expoPushToken: string } }", received: ${JSON.stringify(e,null,2)}.`);return e.data.expoPushToken}async function h(){try{if(!s.default.getInstallationIdAsync)throw new n.UnavailabilityError('ExpoServerRegistrationModule','getInstallationIdAsync');return await s.default.getInstallationIdAsync()}catch(e){throw new n.CodedError('ERR_NOTIF_DEVICE_ID',`Could not have fetched installation ID of the application: ${e}.`)}}function E(e){return'string'==typeof e.data?e.data:JSON.stringify(e.data)}async function I(){return!1}function y(e){switch(e.type){case'ios':return'apns';case'android':return'fcm';default:return e.type}}},1012,[1013,1006,4,1016,1020,1005]);
|
|
@@ -15044,7 +15044,7 @@ __d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{v
|
|
|
15044
15044
|
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),Object.defineProperty(e,"decodeOfferFragmentPayload",{enumerable:!0,get:function(){return n.decodeOfferFragmentPayload}}),Object.defineProperty(e,"buildDaemonWebSocketUrl",{enumerable:!0,get:function(){return t.buildDaemonWebSocketUrl}}),Object.defineProperty(e,"deriveLabelFromEndpoint",{enumerable:!0,get:function(){return t.deriveLabelFromEndpoint}}),Object.defineProperty(e,"extractHostPortFromWebSocketUrl",{enumerable:!0,get:function(){return t.extractHostPortFromWebSocketUrl}}),Object.defineProperty(e,"normalizeHostPort",{enumerable:!0,get:function(){return t.normalizeHostPort}}),Object.defineProperty(e,"parseConnectionUri",{enumerable:!0,get:function(){return t.parseConnectionUri}}),Object.defineProperty(e,"parseHostPort",{enumerable:!0,get:function(){return t.parseHostPort}}),Object.defineProperty(e,"serializeConnectionUri",{enumerable:!0,get:function(){return t.serializeConnectionUri}}),Object.defineProperty(e,"serializeConnectionUriForStorage",{enumerable:!0,get:function(){return t.serializeConnectionUriForStorage}}),Object.defineProperty(e,"shouldUseTlsForDefaultHostedRelay",{enumerable:!0,get:function(){return t.shouldUseTlsForDefaultHostedRelay}}),e.buildRelayWebSocketUrl=function(n){return(0,t.buildRelayWebSocketUrl)(Object.assign({},n,{role:"client"}))};var t=r(d[0]),n=r(d[1])},3399,[3379,3400]);
|
|
15045
15045
|
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),Object.defineProperty(e,"ConnectionOfferV2Schema",{enumerable:!0,get:function(){return t}}),Object.defineProperty(e,"ConnectionOfferSchema",{enumerable:!0,get:function(){return o}}),e.decodeOfferFragmentPayload=l,e.parseConnectionOfferFromUrl=function(n){const t=f(n);if(!t)return null;const c=l(t);return o.parse(c)};var n=r(d[0]);const t=n.z.object({v:n.z.literal(2),serverId:n.z.string().min(1),daemonPublicKeyB64:n.z.string().min(1),relay:n.z.object({endpoint:n.z.string().min(1),useTls:n.z.boolean().optional()})}),o=t;function c(n){const t=n.replace(/-/g,"+").replace(/_/g,"/"),o=t.padEnd(t.length+(4-t.length%4)%4,"="),c=globalThis.atob(o),l=Uint8Array.from(c,n=>n.charCodeAt(0));return new TextDecoder("utf-8",{fatal:!0}).decode(l)}function l(n){const t=c(n);return JSON.parse(t)}const u="#offer=";function f(n){const t=n.trim();if(!t)return null;const o=t.indexOf(u);if(-1===o)return null;const c=t.slice(o+u.length).trim();return c.length>0?c:null}},3400,[3282]);
|
|
15046
15046
|
__d(function(g,r,i,a,m,_e,d){"use strict";function e(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(_e,'__esModule',{value:!0}),_e.resolveAppVersion=function(){const e=u(t.default?.version);if(e)return e;const o=u(n.default.expoConfig?.version);if(o)return o;const f=u(n.default.manifest?.version);if(f)return f;return null};var n=e(r(d[0])),t=e(r(d[1]));function u(e){if("string"!=typeof e)return null;const n=e.trim();return 0===n.length?null:n}},3401,[1006,3402]);
|
|
15047
|
-
__d(function(e,t,r,a,o,i,n){o.exports={name:"@hyperdrive.bot/paseo-app",version:"0.3.
|
|
15047
|
+
__d(function(e,t,r,a,o,i,n){o.exports={name:"@hyperdrive.bot/paseo-app",version:"0.3.28",private:!0,main:"index.ts",scripts:{start:"npm run start:expo","start:expo":"cross-env APP_VARIANT=development expo start","reset-project":"node ./scripts/reset-project.js","build:terminal-webview":"node ./scripts/build-terminal-webview-html.mjs","eas-build-post-install":"npm --prefix ../.. run build:app-deps && npm run build:terminal-webview",android:"npm run android:development","android:development":"npm --prefix ../.. run build:client && cross-env APP_VARIANT=development expo prebuild --platform android --non-interactive && cross-env APP_VARIANT=development expo run:android --variant=debug","android:production":"npm --prefix ../.. run build:client && cross-env APP_VARIANT=production expo prebuild --platform android --non-interactive && cross-env APP_VARIANT=production expo run:android --variant=release","android:release":"npm run android:production","android:clear":"node -e \"require('node:fs').rmSync('android', { recursive: true, force: true })\"",ios:"npm --prefix ../.. run build:client && expo run:ios","ios:release":"npm --prefix ../.. run build:client && expo run:ios --configuration Release",web:"npm --prefix ../.. run build:client && concurrently --kill-others --names protocol,client,expo --prefix-colors yellow,blue,magenta \"npm --prefix ../.. run watch:protocol\" \"npm --prefix ../.. run watch:client\" \"npm run web:expo\"","web:expo":"expo start --web",lint:"expo lint",typecheck:"tsgo --noEmit",test:"vitest run","test:browser":"vitest run --project browser","test:e2e":"playwright test --project='Desktop Chrome'","test:e2e:real":"cross-env E2E_FORK_PASEO_HOME_FROM=../../.dev/paseo-home playwright test --project=real-provider","test:e2e:ui":"playwright test --ui","test:coverage":"vitest run --project unit --coverage",build:"npm run build:web","build:web":"npm --prefix ../.. run build:app-deps && expo export --platform web --source-maps","profile:workspace-tabs":"node ./scripts/profile-workspace-tabs.mjs","deploy:web":"npm run build:web && wrangler pages deploy dist --project-name paseo-app --branch main"},dependencies:{"@bacons/apple-targets":"4.0.6","@datadog/browser-rum":"^6.23.0","@datadog/browser-rum-react":"^6.23.0","@datadog/mobile-react-native":"^2.7.0","@datadog/mobile-react-native-session-replay":"^2.14.8","@dnd-kit/core":"^6.3.1","@dnd-kit/sortable":"^10.0.0","@dnd-kit/utilities":"^3.2.2","@expo/image-utils":"0.8.8","@expo/plist":"0.4.9","@expo/prebuild-config":"54.0.8","@floating-ui/react-native":"^0.10.7","@gorhom/bottom-sheet":"^5.2.14","@gorhom/portal":"^1.0.14","@hyperdrive.bot/paseo-client":"*","@hyperdrive.bot/paseo-expo-two-way-audio":"*","@hyperdrive.bot/paseo-extension-sdk":"*","@hyperdrive.bot/paseo-highlight":"*","@react-native-async-storage/async-storage":"2.2.0","@react-native-masked-view/masked-view":"^0.3.2","@react-native/normalize-colors":"^0.81.5","@react-navigation/native":"^7.1.8","@sentry/electron":"^6.11.0","@sentry/react-native":"^6.20.0","@tanstack/react-query":"^5.90.11","@tanstack/react-virtual":"^3.13.21","@xterm/addon-clipboard":"^0.3.0-beta.213","@xterm/addon-fit":"^0.12.0-beta.213","@xterm/addon-image":"^0.10.0-beta.213","@xterm/addon-ligatures":"0.11.0-beta.213","@xterm/addon-search":"^0.17.0-beta.213","@xterm/addon-unicode11":"^0.10.0-beta.213","@xterm/addon-web-links":"^0.13.0-beta.213","@xterm/addon-webgl":"^0.20.0-beta.212","@xterm/xterm":"^6.1.0-beta.213",buffer:"^6.0.3",expo:"^54.0.18","expo-asset":"~12.0.12","expo-audio":"~1.0.13","expo-background-fetch":"~14.0.9","expo-build-properties":"^1.0.9","expo-camera":"~17.0.10","expo-clipboard":"~8.0.7","expo-constants":"~18.0.9","expo-crypto":"^15.0.8","expo-dev-client":"^6.0.15","expo-document-picker":"~14.0.8","expo-file-system":"~19.0.17","expo-haptics":"~15.0.7","expo-image":"~3.0.10","expo-image-manipulator":"~14.0.8","expo-image-picker":"^17.0.8","expo-keep-awake":"^15.0.7","expo-linking":"~8.0.8","expo-localization":"~17.0.9","expo-notifications":"^0.32.16","expo-router":"~6.0.13","expo-sharing":"^14.0.8","expo-speech":"~14.0.8","expo-speech-recognition":"^56.0.1","expo-splash-screen":"~31.0.10","expo-system-ui":"~6.0.7","expo-task-manager":"~14.0.9","fast-deep-equal":"^3.1.3",htmlparser2:"^12.0.0",i18next:"^26.3.0","lucide-react-native":"^0.546.0","markdown-it":"^10.0.0","mnemonic-id":"^3.2.7",qrcode:"^1.5.4",react:"19.1.0","react-dom":"19.1.0","react-i18next":"^17.0.8","react-native":"0.81.5","react-native-draggable-flatlist":"^4.0.3","react-native-edge-to-edge":"^1.7.0","react-native-gesture-handler":"~2.28.0","react-native-keyboard-controller":"^1.21.7","react-native-markdown-display":"^7.0.2","react-native-nitro-modules":"0.35.5","react-native-reanimated":"~4.3.1","react-native-safe-area-context":"~5.6.0","react-native-screens":"~4.16.0","react-native-svg":"^15.14.0","react-native-uitextview":"^2.2.0","react-native-unistyles":"^3.2.4","react-native-web":"~0.21.0","react-native-webview":"^13.16.0","react-native-worklets":"~0.8.3","tiny-invariant":"^1.3.3","use-sync-external-store":"^1.6.0",zod:"^4.4.3",zustand:"^5.0.9"},devDependencies:{"@playwright/test":"^1.56.1","@testing-library/dom":"^10.4.1","@testing-library/react":"^16.3.2","@types/chai":"^5.2.2","@types/markdown-it":"^14.1.2","@types/qrcode":"^1.5.6","@types/react":"~19.2.0","@types/ws":"^8.18.1","@vitest/browser":"^4.1.7","@vitest/browser-playwright":"^4.1.7","@vitest/coverage-v8":"^4.1.7","@xterm/headless":"^6.1.0-beta.213",dotenv:"^17.2.3",eslint:"^9.25.0","eslint-config-expo":"~10.0.0",jsdom:"^20.0.3","material-icon-theme":"^5.32.0",playwright:"^1.56.1","serve-sim":"^0.1.40",typescript:"~5.9.2",vitest:"^4.1.6",wrangler:"^4.105.0",ws:"^8.20.0"}}},3402,[]);
|
|
15048
15048
|
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.shouldUseDesktopDaemon=function(){return(0,n.isElectronRuntime)()},e.getDesktopDaemonStatus=async function(){return c(await(0,t.invokeDesktopCommand)("desktop_daemon_status"))},e.startDesktopDaemon=async function(){return c(await(0,t.invokeDesktopCommand)("start_desktop_daemon"))},e.stopDesktopDaemon=async function(n="manual_ipc"){return c(await(0,t.invokeDesktopCommand)("stop_desktop_daemon",{reason:n}))},e.restartDesktopDaemon=async function(){return c(await(0,t.invokeDesktopCommand)("restart_desktop_daemon"))},e.getDesktopDaemonLogs=async function(){return p(await(0,t.invokeDesktopCommand)("desktop_daemon_logs"))},e.getDesktopDaemonPairing=async function(){return k(await(0,t.invokeDesktopCommand)("desktop_daemon_pairing"))},e.getCliDaemonStatus=async function(){const n=await(0,t.invokeDesktopCommand)("cli_daemon_status");if("string"!=typeof n)throw new Error("Unexpected CLI daemon status response.");return n},e.listenToLocalTransportEvents=async function(t){const u=(0,n.getDesktopHost)()?.events?.on;if("function"!=typeof u)throw new Error("Desktop events API is unavailable.");const c=await u("local-daemon-transport-event",n=>{o(n)&&t({sessionId:s(n.sessionId)??"",kind:s(n.kind)??"error",text:s(n.text),binaryBase64:s(n.binaryBase64),code:l(n.code),reason:s(n.reason),error:s(n.error)})});return"function"==typeof c?c:()=>{}},e.openLocalTransportSession=async function(n){const o=await(0,t.invokeDesktopCommand)("open_local_daemon_transport",n);if("string"!=typeof o||0===o.trim().length)throw new Error("Unexpected local transport session response.");return o},e.sendLocalTransportMessage=async function(n){await(0,t.invokeDesktopCommand)("send_local_daemon_transport_message",Object.assign({sessionId:n.sessionId},n.text?{text:n.text}:{},n.binaryBase64?{binaryBase64:n.binaryBase64}:{}))},e.closeLocalTransportSession=async function(n){await(0,t.invokeDesktopCommand)("close_local_daemon_transport",{sessionId:n})},e.getCliInstallStatus=async function(){return f(await(0,t.invokeDesktopCommand)("get_cli_install_status"))},e.installCli=async function(){return f(await(0,t.invokeDesktopCommand)("install_cli"))},e.getSkillsStatus=async function(){return y(await(0,t.invokeDesktopCommand)("get_skills_status"))},e.installSkills=async function(){return y(await(0,t.invokeDesktopCommand)("install_skills"))},e.updateSkills=async function(){return y(await(0,t.invokeDesktopCommand)("update_skills"))},e.uninstallSkills=async function(){return y(await(0,t.invokeDesktopCommand)("uninstall_skills"))};var n=r(d[0]),t=r(d[1]);function o(n){return"object"==typeof n&&null!==n}function s(n){return"string"==typeof n&&n.trim().length>0?n:null}function l(n){return"number"==typeof n&&Number.isFinite(n)?n:null}function u(n){const t=s(n)?.toLowerCase();switch(t){case"starting":return"starting";case"running":return"running";case"errored":case"error":return"errored";default:return"stopped"}}function c(n){if(!o(n))throw new Error("Unexpected desktop daemon status response.");return{serverId:s(n.serverId)??"",status:u(n.status),listen:s(n.listen),hostname:s(n.hostname),pid:l(n.pid),home:s(n.home)??"",version:s(n.version),desktopManaged:!0===n.desktopManaged,error:s(n.error)}}function p(n){if(!o(n))throw new Error("Unexpected desktop daemon logs response.");return{logPath:s(n.logPath)??"",contents:"string"==typeof n.contents?n.contents:""}}function k(n){if(!o(n))throw new Error("Unexpected desktop daemon pairing response.");return{relayEnabled:!0===n.relayEnabled,url:s(n.url),qr:s(n.qr)}}function f(n){if(!o(n))throw new Error("Unexpected install status response.");return{installed:!0===n.installed}}function w(n){switch(n){case"not-installed":case"up-to-date":case"drift":return n;default:throw new Error(`Unexpected skills status state: ${String(n)}`)}}function _(n){if(!o(n))throw new Error("Unexpected skill op response.");const t=s(n.name);if(!t)throw new Error("Skill op missing name.");switch(n.kind){case"add":return{kind:"add",name:t};case"update":return{kind:"update",name:t};case"delete":return{kind:"delete",name:t};default:throw new Error(`Unexpected skill op kind: ${String(n.kind)}`)}}function y(n){if(!o(n))throw new Error("Unexpected skills status response.");const t=Array.isArray(n.ops)?n.ops.map(_):[];return{state:w(n.state),ops:t}}},3403,[3404,3406]);
|
|
15049
15049
|
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.getDesktopHost=n,e.isElectronRuntime=o,e.isElectronRuntimeMac=function(){if(!o())return!1;if("undefined"==typeof navigator)return!1;const t=n()?.platform?.toLowerCase();if("darwin"===t||"mac"===t||"macos"===t)return!0;const u=navigator.userAgent;return u.includes("Mac OS")||u.includes("Macintosh")},r(d[0]);var t=r(d[1]);function n(){return(0,t.getElectronHost)()}function o(){return null!==n()}},3404,[25,3405]);
|
|
15050
15050
|
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.getElectronHost=function(){if("undefined"==typeof window)return null;const t=window.paseoDesktop;if(!t||"object"!=typeof t)return null;return t}},3405,[]);
|
|
@@ -16539,5 +16539,5 @@ __d(function(g,r,_i,a,_m,_e,d){"use strict";var e,t=r(d[0]),n=this&&this.__creat
|
|
|
16539
16539
|
__r(975);
|
|
16540
16540
|
__r(341);
|
|
16541
16541
|
__r(0);
|
|
16542
|
-
//# sourceMappingURL=/_expo/static/js/web/index-
|
|
16543
|
-
//# debugId=
|
|
16542
|
+
//# sourceMappingURL=/_expo/static/js/web/index-f8bb839b399b809d73376d962613fce2.js.map
|
|
16543
|
+
//# debugId=2fd27551-57e7-48c2-a288-88c7228210fa
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -85,6 +85,6 @@
|
|
|
85
85
|
<body>
|
|
86
86
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
87
87
|
<div id="root"></div>
|
|
88
|
-
<script src="/_expo/static/js/web/index-
|
|
88
|
+
<script src="/_expo/static/js/web/index-f8bb839b399b809d73376d962613fce2.js" defer></script>
|
|
89
89
|
</body>
|
|
90
90
|
</html>
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperdrive.bot/paseo-server",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.28",
|
|
4
4
|
"description": "Paseo backend server",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/server",
|
|
@@ -65,11 +65,11 @@
|
|
|
65
65
|
"@agentclientprotocol/sdk": "^0.17.1",
|
|
66
66
|
"@anthropic-ai/claude-agent-sdk": "^0.3.195",
|
|
67
67
|
"@anthropic-ai/sdk": "^0.104.2",
|
|
68
|
-
"@hyperdrive.bot/paseo-client": "0.3.
|
|
69
|
-
"@hyperdrive.bot/paseo-extension-sdk": "0.3.
|
|
70
|
-
"@hyperdrive.bot/paseo-highlight": "0.3.
|
|
71
|
-
"@hyperdrive.bot/paseo-protocol": "0.3.
|
|
72
|
-
"@hyperdrive.bot/paseo-relay": "0.3.
|
|
68
|
+
"@hyperdrive.bot/paseo-client": "0.3.28",
|
|
69
|
+
"@hyperdrive.bot/paseo-extension-sdk": "0.3.28",
|
|
70
|
+
"@hyperdrive.bot/paseo-highlight": "0.3.28",
|
|
71
|
+
"@hyperdrive.bot/paseo-protocol": "0.3.28",
|
|
72
|
+
"@hyperdrive.bot/paseo-relay": "0.3.28",
|
|
73
73
|
"@isaacs/ttlcache": "^2.1.4",
|
|
74
74
|
"@modelcontextprotocol/sdk": "^1.20.1",
|
|
75
75
|
"@opencode-ai/sdk": "1.2.6",
|