@hyperdrive.bot/paseo-server 0.3.19 → 0.3.26
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/provider-registry.d.ts +30 -0
- package/dist/server/server/agent/provider-registry.js +36 -0
- package/dist/server/server/agent/providers/claude/agent.d.ts +18 -0
- package/dist/server/server/agent/providers/claude/agent.js +104 -2
- package/dist/server/server/agent/providers/claude/pty-session-launcher.d.ts +3 -0
- package/dist/server/server/agent/providers/claude/pty-session-launcher.js +1 -0
- package/dist/server/server/agent/providers/claude/transport/pty-query.d.ts +64 -0
- package/dist/server/server/agent/providers/claude/transport/pty-query.js +282 -2
- package/dist/server/server/workspace/all-sessions-route.js +97 -17
- package/dist/server/web-ui/_expo/static/js/web/{index-16f1edcbe9fa1e0c322b20c78461f39d.js → index-045767cf5362f85583f2649254d4471a.js} +5 -5
- package/dist/server/web-ui/_expo/static/js/web/index-045767cf5362f85583f2649254d4471a.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-045767cf5362f85583f2649254d4471a.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{index-16f1edcbe9fa1e0c322b20c78461f39d.js.map.br → index-045767cf5362f85583f2649254d4471a.js.map.br} +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{index-16f1edcbe9fa1e0c322b20c78461f39d.js.map.gz → index-045767cf5362f85583f2649254d4471a.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-16f1edcbe9fa1e0c322b20c78461f39d.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-16f1edcbe9fa1e0c322b20c78461f39d.js.gz +0 -0
|
@@ -8,6 +8,36 @@ export declare function resolveClaudeTransport(setting: "sdk" | "pty" | "auto" |
|
|
|
8
8
|
requireNodePty?: () => unknown;
|
|
9
9
|
probeClaudeBinary?: () => void;
|
|
10
10
|
}): "sdk" | "pty";
|
|
11
|
+
/**
|
|
12
|
+
* Internal agents (session-digest and auto-title generation) are headless,
|
|
13
|
+
* one-shot, `persistSession: false` runs. They have no TUI to drive: they are
|
|
14
|
+
* handed a prompt and asked for one structured answer.
|
|
15
|
+
*
|
|
16
|
+
* `resolveClaudeTransport` returns "pty" whenever node-pty and the `claude`
|
|
17
|
+
* binary are both present, which on a developer box is always, so these runs
|
|
18
|
+
* were being given a full interactive terminal and their prompt written through
|
|
19
|
+
* the PTY's bracketed-paste path. That path can refuse the write when the TUI
|
|
20
|
+
* is not in an input-accepting state, and the caller sees:
|
|
21
|
+
*
|
|
22
|
+
* "Your message did not reach the agent: the terminal would not accept it."
|
|
23
|
+
*
|
|
24
|
+
* Measured on 2026-08-04: 187 of 191 structured-generation failures (98%) were
|
|
25
|
+
* exactly that error, across claude/pool/pool-tu and across haiku, opus-5 and
|
|
26
|
+
* opus-4-8, so it is neither provider- nor model-specific. Each failure burned
|
|
27
|
+
* a provider attempt until the digest's 120s budget expired, losing ~46% of all
|
|
28
|
+
* digests (116 DigestGenerationTimeoutError against 113 successful digests that
|
|
29
|
+
* day). Every lost digest also silently costs a Jarvis judgement, because the
|
|
30
|
+
* judge only wakes on the `onSettled` hook that a completed digest fires.
|
|
31
|
+
*
|
|
32
|
+
* Two cheaper explanations were tested and rejected before this one: prompt
|
|
33
|
+
* size (2 KB / 8 KB / 32 KB pastes all delivered fine) and a spawn/readiness
|
|
34
|
+
* race (three spawn-then-immediately-send probes all delivered fine).
|
|
35
|
+
*
|
|
36
|
+
* So internal agents default to the SDK transport, which needs no terminal. An
|
|
37
|
+
* EXPLICIT `transport` setting still wins: this only redirects the "auto" case,
|
|
38
|
+
* so anyone deliberately pinning "pty" keeps it.
|
|
39
|
+
*/
|
|
40
|
+
export declare function resolveInternalAgentTransportSetting(internal: boolean | undefined, setting: "sdk" | "pty" | "auto" | undefined): "sdk" | "pty" | "auto" | undefined;
|
|
11
41
|
export declare const NODE_PTY_UNAVAILABLE_REASON = "node-pty native module not built. Run 'npm rebuild node-pty' in packages/server.";
|
|
12
42
|
export declare function isNodePtyAvailable(deps?: {
|
|
13
43
|
requireNodePty?: () => unknown;
|
|
@@ -38,6 +38,42 @@ export function resolveClaudeTransport(setting, deps) {
|
|
|
38
38
|
return "sdk";
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Internal agents (session-digest and auto-title generation) are headless,
|
|
43
|
+
* one-shot, `persistSession: false` runs. They have no TUI to drive: they are
|
|
44
|
+
* handed a prompt and asked for one structured answer.
|
|
45
|
+
*
|
|
46
|
+
* `resolveClaudeTransport` returns "pty" whenever node-pty and the `claude`
|
|
47
|
+
* binary are both present, which on a developer box is always, so these runs
|
|
48
|
+
* were being given a full interactive terminal and their prompt written through
|
|
49
|
+
* the PTY's bracketed-paste path. That path can refuse the write when the TUI
|
|
50
|
+
* is not in an input-accepting state, and the caller sees:
|
|
51
|
+
*
|
|
52
|
+
* "Your message did not reach the agent: the terminal would not accept it."
|
|
53
|
+
*
|
|
54
|
+
* Measured on 2026-08-04: 187 of 191 structured-generation failures (98%) were
|
|
55
|
+
* exactly that error, across claude/pool/pool-tu and across haiku, opus-5 and
|
|
56
|
+
* opus-4-8, so it is neither provider- nor model-specific. Each failure burned
|
|
57
|
+
* a provider attempt until the digest's 120s budget expired, losing ~46% of all
|
|
58
|
+
* digests (116 DigestGenerationTimeoutError against 113 successful digests that
|
|
59
|
+
* day). Every lost digest also silently costs a Jarvis judgement, because the
|
|
60
|
+
* judge only wakes on the `onSettled` hook that a completed digest fires.
|
|
61
|
+
*
|
|
62
|
+
* Two cheaper explanations were tested and rejected before this one: prompt
|
|
63
|
+
* size (2 KB / 8 KB / 32 KB pastes all delivered fine) and a spawn/readiness
|
|
64
|
+
* race (three spawn-then-immediately-send probes all delivered fine).
|
|
65
|
+
*
|
|
66
|
+
* So internal agents default to the SDK transport, which needs no terminal. An
|
|
67
|
+
* EXPLICIT `transport` setting still wins: this only redirects the "auto" case,
|
|
68
|
+
* so anyone deliberately pinning "pty" keeps it.
|
|
69
|
+
*/
|
|
70
|
+
export function resolveInternalAgentTransportSetting(internal, setting) {
|
|
71
|
+
if (!internal)
|
|
72
|
+
return setting;
|
|
73
|
+
if (setting === "sdk" || setting === "pty")
|
|
74
|
+
return setting;
|
|
75
|
+
return "sdk";
|
|
76
|
+
}
|
|
41
77
|
export const NODE_PTY_UNAVAILABLE_REASON = "node-pty native module not built. Run 'npm rebuild node-pty' in packages/server.";
|
|
42
78
|
export function isNodePtyAvailable(deps) {
|
|
43
79
|
const tryRequire = deps?.requireNodePty ?? (() => requireFromHere("node-pty"));
|
|
@@ -101,6 +101,8 @@ export declare class ClaudeAgentSession implements AgentSession {
|
|
|
101
101
|
private toolUseIndexToId;
|
|
102
102
|
private toolUseInputBuffers;
|
|
103
103
|
private pendingPermissions;
|
|
104
|
+
/** Permission id currently standing in for a PTY TUI dialog, if any. */
|
|
105
|
+
private ptyDialogRequestId;
|
|
104
106
|
private activeForegroundTurnId;
|
|
105
107
|
private autonomousTurn;
|
|
106
108
|
private readonly subscribers;
|
|
@@ -280,6 +282,22 @@ export declare class ClaudeAgentSession implements AgentSession {
|
|
|
280
282
|
private handleSystemMessage;
|
|
281
283
|
private readMissingResumedConversationError;
|
|
282
284
|
private convertUsage;
|
|
285
|
+
/**
|
|
286
|
+
* Raise (and retract) a permission request for a dialog the PTY's TUI is showing.
|
|
287
|
+
*
|
|
288
|
+
* The SDK path gets these through `canUseTool`, which is how the app renders a real
|
|
289
|
+
* question card with its options. The PTY path has no such hook - the TUI owns the
|
|
290
|
+
* dialog - so before this the app only saw an inert `AskUserQuestion` tool chip and the
|
|
291
|
+
* session sat waiting with nothing to tap (observed live: 11h51m). Surfacing the same
|
|
292
|
+
* request shape reuses the app's existing question UI, and the answer is played back
|
|
293
|
+
* into the terminal as the dialog's own keystrokes.
|
|
294
|
+
*
|
|
295
|
+
* Scoped to questions on purpose: a plan dialog's options are the CLI's and version-
|
|
296
|
+
* dependent, so guessing an index there could approve an implementation nobody chose.
|
|
297
|
+
*/
|
|
298
|
+
private handlePtyDialogChange;
|
|
299
|
+
/** Map the app's chosen answer back to the dialog's option index and press the keys. */
|
|
300
|
+
private answerPtyDialogFromPermission;
|
|
283
301
|
private handlePermissionRequest;
|
|
284
302
|
private enqueueTimeline;
|
|
285
303
|
private flushPendingToolCalls;
|
|
@@ -30,7 +30,7 @@ import { getAgentStreamEventTurnId, } from "../../agent-sdk-types.js";
|
|
|
30
30
|
import { importSessionFromPersistence } from "../../provider-session-import.js";
|
|
31
31
|
import { SETTING_APPLIES_NEXT_TURN_NOTICE } from "../../provider-notices.js";
|
|
32
32
|
import { checkProviderLaunchAvailable, createProviderEnv, createProviderEnvSpec, resolveProviderLaunch, } from "../../provider-launch-config.js";
|
|
33
|
-
import { isNodePtyAvailable, NODE_PTY_UNAVAILABLE_REASON, resolveClaudeTransport, } from "../../provider-registry.js";
|
|
33
|
+
import { isNodePtyAvailable, NODE_PTY_UNAVAILABLE_REASON, resolveClaudeTransport, resolveInternalAgentTransportSetting, } from "../../provider-registry.js";
|
|
34
34
|
import { isCommandAvailable } from "../../../../executable-resolution/executable-resolution.js";
|
|
35
35
|
import { withTimeout } from "../../../../utils/promise-timeout.js";
|
|
36
36
|
import { terminateWithTreeKill } from "../../../../utils/tree-kill.js";
|
|
@@ -714,6 +714,33 @@ function isPermissionUpdate(value) {
|
|
|
714
714
|
const destination = value.destination;
|
|
715
715
|
return Array.isArray(rules) && typeof behavior === "string" && typeof destination === "string";
|
|
716
716
|
}
|
|
717
|
+
/**
|
|
718
|
+
* The app answers a question by echoing the tool input back with the chosen label(s). The
|
|
719
|
+
* exact key varies by question text, so collect every string leaf rather than guessing one
|
|
720
|
+
* path; the caller matches these against the dialog's on-screen options.
|
|
721
|
+
*/
|
|
722
|
+
function collectAnswerLabels(input) {
|
|
723
|
+
const labels = [];
|
|
724
|
+
const visit = (value, depth) => {
|
|
725
|
+
if (depth > 4)
|
|
726
|
+
return;
|
|
727
|
+
if (typeof value === "string") {
|
|
728
|
+
labels.push(value);
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
if (Array.isArray(value)) {
|
|
732
|
+
for (const item of value)
|
|
733
|
+
visit(item, depth + 1);
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
if (isMetadata(value)) {
|
|
737
|
+
for (const item of Object.values(value))
|
|
738
|
+
visit(item, depth + 1);
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
visit(input, 0);
|
|
742
|
+
return labels;
|
|
743
|
+
}
|
|
717
744
|
function resolvePermissionKind(toolName, input) {
|
|
718
745
|
if (toolName === "ExitPlanMode")
|
|
719
746
|
return "plan";
|
|
@@ -1469,6 +1496,8 @@ export class ClaudeAgentSession {
|
|
|
1469
1496
|
this.toolUseIndexToId = new Map();
|
|
1470
1497
|
this.toolUseInputBuffers = new Map();
|
|
1471
1498
|
this.pendingPermissions = new Map();
|
|
1499
|
+
/** Permission id currently standing in for a PTY TUI dialog, if any. */
|
|
1500
|
+
this.ptyDialogRequestId = null;
|
|
1472
1501
|
this.activeForegroundTurnId = null;
|
|
1473
1502
|
this.autonomousTurn = null;
|
|
1474
1503
|
this.subscribers = new Set();
|
|
@@ -2314,7 +2343,7 @@ export class ClaudeAgentSession {
|
|
|
2314
2343
|
ensureTransport() {
|
|
2315
2344
|
if (this.resolvedTransport)
|
|
2316
2345
|
return this.resolvedTransport;
|
|
2317
|
-
this.resolvedTransport = resolveClaudeTransport(this.runtimeSettings?.transport);
|
|
2346
|
+
this.resolvedTransport = resolveClaudeTransport(resolveInternalAgentTransportSetting(this.config.internal, this.runtimeSettings?.transport));
|
|
2318
2347
|
this.fireTransportSpawnedTelemetry(this.resolvedTransport);
|
|
2319
2348
|
return this.resolvedTransport;
|
|
2320
2349
|
}
|
|
@@ -2501,6 +2530,7 @@ export class ClaudeAgentSession {
|
|
|
2501
2530
|
? this.normalizeMcpServers(this.config.mcpServers)
|
|
2502
2531
|
: undefined,
|
|
2503
2532
|
logger: this.logger,
|
|
2533
|
+
onDialogChange: (dialog) => this.handlePtyDialogChange(dialog),
|
|
2504
2534
|
});
|
|
2505
2535
|
this.activeTransport = ptySession.transport;
|
|
2506
2536
|
this.query = ptySession.query;
|
|
@@ -3580,6 +3610,78 @@ export class ClaudeAgentSession {
|
|
|
3580
3610
|
convertUsage(message, modelUsage) {
|
|
3581
3611
|
return this.contextUsage.buildResultUsage(message, modelUsage);
|
|
3582
3612
|
}
|
|
3613
|
+
/**
|
|
3614
|
+
* Raise (and retract) a permission request for a dialog the PTY's TUI is showing.
|
|
3615
|
+
*
|
|
3616
|
+
* The SDK path gets these through `canUseTool`, which is how the app renders a real
|
|
3617
|
+
* question card with its options. The PTY path has no such hook - the TUI owns the
|
|
3618
|
+
* dialog - so before this the app only saw an inert `AskUserQuestion` tool chip and the
|
|
3619
|
+
* session sat waiting with nothing to tap (observed live: 11h51m). Surfacing the same
|
|
3620
|
+
* request shape reuses the app's existing question UI, and the answer is played back
|
|
3621
|
+
* into the terminal as the dialog's own keystrokes.
|
|
3622
|
+
*
|
|
3623
|
+
* Scoped to questions on purpose: a plan dialog's options are the CLI's and version-
|
|
3624
|
+
* dependent, so guessing an index there could approve an implementation nobody chose.
|
|
3625
|
+
*/
|
|
3626
|
+
handlePtyDialogChange(dialog) {
|
|
3627
|
+
if (!dialog) {
|
|
3628
|
+
const pendingId = this.ptyDialogRequestId;
|
|
3629
|
+
this.ptyDialogRequestId = null;
|
|
3630
|
+
if (!pendingId)
|
|
3631
|
+
return;
|
|
3632
|
+
const pending = this.pendingPermissions.get(pendingId);
|
|
3633
|
+
if (!pending)
|
|
3634
|
+
return;
|
|
3635
|
+
// Answered in the terminal (or by keystrokes): retract the card, don't leave it up.
|
|
3636
|
+
this.pendingPermissions.delete(pendingId);
|
|
3637
|
+
pending.cleanup?.();
|
|
3638
|
+
this.pushEvent({
|
|
3639
|
+
type: "permission_resolved",
|
|
3640
|
+
provider: "claude",
|
|
3641
|
+
requestId: pendingId,
|
|
3642
|
+
resolution: { behavior: "allow" },
|
|
3643
|
+
});
|
|
3644
|
+
return;
|
|
3645
|
+
}
|
|
3646
|
+
if (dialog.name !== "AskUserQuestion" || this.ptyDialogRequestId)
|
|
3647
|
+
return;
|
|
3648
|
+
const requestId = `permission-${randomUUID()}`;
|
|
3649
|
+
this.ptyDialogRequestId = requestId;
|
|
3650
|
+
const request = {
|
|
3651
|
+
id: requestId,
|
|
3652
|
+
provider: "claude",
|
|
3653
|
+
name: dialog.name,
|
|
3654
|
+
kind: "question",
|
|
3655
|
+
input: normalizeClaudeAskUserQuestionRequestInput(dialog.name, dialog.input),
|
|
3656
|
+
metadata: { toolUseId: dialog.toolUseId },
|
|
3657
|
+
};
|
|
3658
|
+
const options = dialog.options;
|
|
3659
|
+
this.pendingPermissions.set(requestId, {
|
|
3660
|
+
request,
|
|
3661
|
+
// There is no SDK promise to settle here: "resolving" means playing the answer into
|
|
3662
|
+
// the terminal. An answer that names no known option is left to the user to type.
|
|
3663
|
+
resolve: (result) => {
|
|
3664
|
+
void this.answerPtyDialogFromPermission(result, options);
|
|
3665
|
+
},
|
|
3666
|
+
reject: () => { },
|
|
3667
|
+
});
|
|
3668
|
+
this.pushEvent({ type: "permission_requested", provider: "claude", request });
|
|
3669
|
+
}
|
|
3670
|
+
/** Map the app's chosen answer back to the dialog's option index and press the keys. */
|
|
3671
|
+
async answerPtyDialogFromPermission(result, options) {
|
|
3672
|
+
if (result.behavior !== "allow")
|
|
3673
|
+
return;
|
|
3674
|
+
const query = this.query;
|
|
3675
|
+
if (typeof query?.answerDialogByIndex !== "function")
|
|
3676
|
+
return;
|
|
3677
|
+
const chosen = collectAnswerLabels(result.updatedInput);
|
|
3678
|
+
const index = options.findIndex((option) => chosen.some((label) => label.trim().toLowerCase() === option.trim().toLowerCase()));
|
|
3679
|
+
if (index < 0) {
|
|
3680
|
+
this.logger.warn({ chosen, options }, "PTY dialog answer matched no on-screen option; leaving the dialog up");
|
|
3681
|
+
return;
|
|
3682
|
+
}
|
|
3683
|
+
await query.answerDialogByIndex(index);
|
|
3684
|
+
}
|
|
3583
3685
|
enqueueTimeline(item) {
|
|
3584
3686
|
this.pushEvent({ type: "timeline", item, provider: "claude" });
|
|
3585
3687
|
}
|
|
@@ -2,6 +2,7 @@ import type { Logger } from "pino";
|
|
|
2
2
|
import type { Query, SDKUserMessage } from "@anthropic-ai/claude-agent-sdk";
|
|
3
3
|
import { type ProviderRuntimeSettings } from "../../provider-launch-config.js";
|
|
4
4
|
import { PtyTransport } from "./transport/pty.js";
|
|
5
|
+
import { type PtyInteractiveDialog } from "./transport/pty-query.js";
|
|
5
6
|
export interface CreatePtySessionOptions {
|
|
6
7
|
binary: string;
|
|
7
8
|
cwd: string;
|
|
@@ -22,6 +23,8 @@ export interface CreatePtySessionOptions {
|
|
|
22
23
|
cols: number;
|
|
23
24
|
rows: number;
|
|
24
25
|
};
|
|
26
|
+
/** Surfaces TUI question/plan dialogs so the provider can raise a permission request. */
|
|
27
|
+
onDialogChange?: (dialog: PtyInteractiveDialog | null) => void;
|
|
25
28
|
}
|
|
26
29
|
export interface PtySession {
|
|
27
30
|
transport: PtyTransport;
|
|
@@ -55,6 +55,7 @@ export async function createPtySession(opts) {
|
|
|
55
55
|
transcriptPath,
|
|
56
56
|
sessionId: opts.sessionId,
|
|
57
57
|
logger: opts.logger,
|
|
58
|
+
...(opts.onDialogChange ? { onDialogChange: opts.onDialogChange } : {}),
|
|
58
59
|
});
|
|
59
60
|
opts.logger.debug({ pid: transport.context().pid, sessionId: opts.sessionId, transcriptPath }, "PTY claude session spawned");
|
|
60
61
|
return { transport, query, transcriptPath, systemPromptFilePath };
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import type { Logger } from "pino";
|
|
2
2
|
import type { ModelInfo, SDKMessage, SDKSystemMessage, SDKUserMessage } from "@anthropic-ai/claude-agent-sdk";
|
|
3
3
|
import type { PtyTransport } from "./pty.js";
|
|
4
|
+
/** An interactive TUI dialog the user has to answer for the turn to continue. */
|
|
5
|
+
export interface PtyInteractiveDialog {
|
|
6
|
+
toolUseId: string;
|
|
7
|
+
/** "AskUserQuestion" | "ExitPlanMode" */
|
|
8
|
+
name: string;
|
|
9
|
+
/** The tool_use input verbatim, so the app renders the real questions and options. */
|
|
10
|
+
input: Record<string, unknown>;
|
|
11
|
+
/** Option labels of the FIRST question, in on-screen order; empty when unknown. */
|
|
12
|
+
options: string[];
|
|
13
|
+
}
|
|
4
14
|
export interface PtyQueryOptions {
|
|
5
15
|
transport: PtyTransport;
|
|
6
16
|
/** The shared input channel (createAsyncMessageInput().iterable) fed by startTurn(). */
|
|
@@ -15,6 +25,12 @@ export interface PtyQueryOptions {
|
|
|
15
25
|
* {@link TURN_STALL_TIMEOUT_MS}. Pass `0` to disable the liveness backstop.
|
|
16
26
|
*/
|
|
17
27
|
stallTimeoutMs?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Called when the TUI starts (dialog) or finishes (null) waiting on an interactive
|
|
30
|
+
* question. The provider turns this into a permission request so the app can render
|
|
31
|
+
* the real options instead of an inert tool chip nobody can answer.
|
|
32
|
+
*/
|
|
33
|
+
onDialogChange?: (dialog: PtyInteractiveDialog | null) => void;
|
|
18
34
|
}
|
|
19
35
|
export declare class PtyQuery implements AsyncGenerator<SDKMessage, void> {
|
|
20
36
|
private readonly transport;
|
|
@@ -23,6 +39,7 @@ export declare class PtyQuery implements AsyncGenerator<SDKMessage, void> {
|
|
|
23
39
|
private readonly transcriptPath;
|
|
24
40
|
private readonly logger;
|
|
25
41
|
private readonly reader;
|
|
42
|
+
private readonly onDialogChange;
|
|
26
43
|
private readonly outQueue;
|
|
27
44
|
private readonly outResolvers;
|
|
28
45
|
private done;
|
|
@@ -62,6 +79,8 @@ export declare class PtyQuery implements AsyncGenerator<SDKMessage, void> {
|
|
|
62
79
|
private resumeDialogAt;
|
|
63
80
|
/** Set when the CLI rejected the just-submitted command; carries the CLI's own reply. */
|
|
64
81
|
private rejectedCommandNotice;
|
|
82
|
+
/** Interactive TUI dialog awaiting the user (AskUserQuestion / plan approval), if any. */
|
|
83
|
+
private pendingDialog;
|
|
65
84
|
/**
|
|
66
85
|
* Raw capture window for echo verification. The rolling tail deliberately drops lines
|
|
67
86
|
* with fewer than three letters as spinner noise, which also swallows the echo of a
|
|
@@ -89,6 +108,43 @@ export declare class PtyQuery implements AsyncGenerator<SDKMessage, void> {
|
|
|
89
108
|
*/
|
|
90
109
|
/** The CLI refused the command; relay its own reply so the sender can correct and resend. */
|
|
91
110
|
private buildCommandRejectedResult;
|
|
111
|
+
/**
|
|
112
|
+
* Write each inline base64 image block to a temp file so the terminal agent can Read it
|
|
113
|
+
* by path. A staging failure is logged and skipped rather than thrown: the text part of
|
|
114
|
+
* the message (if any) must still be delivered.
|
|
115
|
+
*/
|
|
116
|
+
private stageInlineImages;
|
|
117
|
+
/**
|
|
118
|
+
* The message had no text and no stageable image, so there is literally nothing to type.
|
|
119
|
+
* Emitted instead of dropping the message, because the manager has already marked the
|
|
120
|
+
* agent running by the time the input loop sees it: silence here is a forever-spinner.
|
|
121
|
+
*/
|
|
122
|
+
private buildNothingDeliverableResult;
|
|
123
|
+
/** Tell the provider the dialog state changed; never let a listener throw into the loop. */
|
|
124
|
+
private notifyDialogChange;
|
|
125
|
+
/** The dialog the TUI is waiting on, if any. */
|
|
126
|
+
interactiveDialog(): PtyInteractiveDialog | null;
|
|
127
|
+
/**
|
|
128
|
+
* Answer the pending dialog by option index (0-based), the way the app's question card
|
|
129
|
+
* does. Returns false when there is no dialog or the keystrokes never registered.
|
|
130
|
+
*/
|
|
131
|
+
answerDialogByIndex(index: number): Promise<boolean>;
|
|
132
|
+
/**
|
|
133
|
+
* Try to answer the pending TUI dialog with the user's reply. A reply that names an
|
|
134
|
+
* option (its 1-based number or its label) becomes Down-arrow presses + Enter, the same
|
|
135
|
+
* proven mechanism as the resume-dialog answerer. Anything else gets the question echoed
|
|
136
|
+
* back with the options, and nothing is typed - an unanswerable message must never turn
|
|
137
|
+
* into a silent decline.
|
|
138
|
+
*/
|
|
139
|
+
private answerPendingDialog;
|
|
140
|
+
/**
|
|
141
|
+
* Drive the dialog's own keys: Down to the option, Enter to confirm. Confirmation is the
|
|
142
|
+
* tool_result landing in the transcript (which clears pendingDialog) - an optimistic flag
|
|
143
|
+
* here would record intent as fact.
|
|
144
|
+
*/
|
|
145
|
+
private sendDialogAnswer;
|
|
146
|
+
/** The agent is waiting on a dialog; tell the sender what it asks and how to answer. */
|
|
147
|
+
private buildDialogPendingResult;
|
|
92
148
|
private buildSubmitFailedResult;
|
|
93
149
|
/**
|
|
94
150
|
* Press Enter to submit the composed prompt, and CONFIRM the turn actually started by
|
|
@@ -141,6 +197,14 @@ export declare class PtyQuery implements AsyncGenerator<SDKMessage, void> {
|
|
|
141
197
|
private sawUserLineSince;
|
|
142
198
|
private transcriptSize;
|
|
143
199
|
private onTranscriptMessage;
|
|
200
|
+
/**
|
|
201
|
+
* Track interactive TUI dialogs (AskUserQuestion / plan-mode approval) from the
|
|
202
|
+
* transcript. While one is pending the terminal is WAITING, not stalled: the stall
|
|
203
|
+
* backstop must stand down, no ESC may be sent (it declines the dialog - the source of
|
|
204
|
+
* every phantom "User declined" this week), and an incoming reply is translated to
|
|
205
|
+
* dialog keystrokes instead of typed text.
|
|
206
|
+
*/
|
|
207
|
+
private trackInteractiveDialog;
|
|
144
208
|
private scheduleTurnEndCheck;
|
|
145
209
|
private maybeCompleteTurn;
|
|
146
210
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
|
+
import * as os from "node:os";
|
|
3
4
|
import * as path from "node:path";
|
|
4
5
|
import { TranscriptSdkReader } from "./transcript-sdk-reader.js";
|
|
5
6
|
/**
|
|
@@ -245,6 +246,8 @@ export class PtyQuery {
|
|
|
245
246
|
this.resumeDialogAt = 0;
|
|
246
247
|
/** Set when the CLI rejected the just-submitted command; carries the CLI's own reply. */
|
|
247
248
|
this.rejectedCommandNotice = null;
|
|
249
|
+
/** Interactive TUI dialog awaiting the user (AskUserQuestion / plan approval), if any. */
|
|
250
|
+
this.pendingDialog = null;
|
|
248
251
|
/**
|
|
249
252
|
* Raw capture window for echo verification. The rolling tail deliberately drops lines
|
|
250
253
|
* with fewer than three letters as spinner noise, which also swallows the echo of a
|
|
@@ -260,6 +263,7 @@ export class PtyQuery {
|
|
|
260
263
|
this.transcriptPath = opts.transcriptPath;
|
|
261
264
|
this.logger = opts.logger;
|
|
262
265
|
this.stallTimeoutMs = opts.stallTimeoutMs ?? TURN_STALL_TIMEOUT_MS;
|
|
266
|
+
this.onDialogChange = opts.onDialogChange ?? null;
|
|
263
267
|
this.reader = new TranscriptSdkReader({
|
|
264
268
|
transcriptPath: opts.transcriptPath,
|
|
265
269
|
onMessage: (m) => this.onTranscriptMessage(m),
|
|
@@ -414,6 +418,10 @@ export class PtyQuery {
|
|
|
414
418
|
if (this.done)
|
|
415
419
|
return;
|
|
416
420
|
this.done = true;
|
|
421
|
+
const hadDialog = this.pendingDialog !== null;
|
|
422
|
+
this.pendingDialog = null;
|
|
423
|
+
if (hadDialog)
|
|
424
|
+
this.notifyDialogChange();
|
|
417
425
|
this.cancelReadiness?.();
|
|
418
426
|
if (this.turnEndTimer)
|
|
419
427
|
clearTimeout(this.turnEndTimer);
|
|
@@ -444,9 +452,30 @@ export class PtyQuery {
|
|
|
444
452
|
for await (const userMessage of this.input) {
|
|
445
453
|
if (this.done)
|
|
446
454
|
break;
|
|
447
|
-
|
|
448
|
-
|
|
455
|
+
// Inline images cannot be typed into a terminal, but the CLI agent can Read a
|
|
456
|
+
// file: stage each image to disk and reference it by path, the same serialization
|
|
457
|
+
// job toSdkUserMessage does for the SDK path.
|
|
458
|
+
const imageRefs = this.stageInlineImages(userMessage)
|
|
459
|
+
.map((p) => `[Attached image: ${p}]`)
|
|
460
|
+
.join(" ");
|
|
461
|
+
const text = [extractUserText(userMessage), imageRefs].filter(Boolean).join(" ").trim();
|
|
462
|
+
if (!text) {
|
|
463
|
+
// An empty prompt used to be dropped silently right here, AFTER the manager had
|
|
464
|
+
// already flipped the agent to "running": the sender stared at a spinner forever
|
|
465
|
+
// (observed live 2026-08-04, an image-only message from a 0.3.10 app whose
|
|
466
|
+
// composer also stripped the attachment). Nothing-deliverable is an answerable
|
|
467
|
+
// outcome, so answer it.
|
|
468
|
+
this.recordLine("--- message had no deliverable content; notifying sender ---");
|
|
469
|
+
this.emit(this.buildNothingDeliverableResult());
|
|
449
470
|
continue;
|
|
471
|
+
}
|
|
472
|
+
if (this.pendingDialog) {
|
|
473
|
+
// The TUI is showing a question/plan dialog: typed text would be eaten by it and
|
|
474
|
+
// pre-typing ESC would decline it. Either answer it (a reply that names an
|
|
475
|
+
// option) or tell the sender what is being asked - never type across it.
|
|
476
|
+
await this.answerPendingDialog(text);
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
450
479
|
this.turnStartedAt = monotonicNowMs();
|
|
451
480
|
// Clear modals BEFORE typing, not after: text typed into either dialog is
|
|
452
481
|
// consumed by it, so recovering at submit time is already too late.
|
|
@@ -526,6 +555,149 @@ export class PtyQuery {
|
|
|
526
555
|
errors: [`The agent's CLI rejected the command: ${notice} Nothing was run.`],
|
|
527
556
|
};
|
|
528
557
|
}
|
|
558
|
+
/**
|
|
559
|
+
* Write each inline base64 image block to a temp file so the terminal agent can Read it
|
|
560
|
+
* by path. A staging failure is logged and skipped rather than thrown: the text part of
|
|
561
|
+
* the message (if any) must still be delivered.
|
|
562
|
+
*/
|
|
563
|
+
stageInlineImages(message) {
|
|
564
|
+
const content = message.message?.content;
|
|
565
|
+
if (!Array.isArray(content))
|
|
566
|
+
return [];
|
|
567
|
+
const staged = [];
|
|
568
|
+
for (const block of content) {
|
|
569
|
+
if (!block || typeof block !== "object")
|
|
570
|
+
continue;
|
|
571
|
+
const image = block;
|
|
572
|
+
if (image.type !== "image")
|
|
573
|
+
continue;
|
|
574
|
+
const source = image.source;
|
|
575
|
+
if (!source || source.type !== "base64" || !source.data)
|
|
576
|
+
continue;
|
|
577
|
+
try {
|
|
578
|
+
const dir = path.join(os.tmpdir(), "paseo-pty-images", this.sessionId);
|
|
579
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
580
|
+
const ext = IMAGE_MEDIA_TYPE_EXT[source.media_type ?? ""] ?? "png";
|
|
581
|
+
const file = path.join(dir, `image-${randomUUID().slice(0, 8)}.${ext}`);
|
|
582
|
+
fs.writeFileSync(file, Buffer.from(source.data, "base64"));
|
|
583
|
+
staged.push(file);
|
|
584
|
+
}
|
|
585
|
+
catch (err) {
|
|
586
|
+
this.logger.warn({ err }, "PtyQuery: failed to stage an inline image for delivery");
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
return staged;
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* The message had no text and no stageable image, so there is literally nothing to type.
|
|
593
|
+
* Emitted instead of dropping the message, because the manager has already marked the
|
|
594
|
+
* agent running by the time the input loop sees it: silence here is a forever-spinner.
|
|
595
|
+
*/
|
|
596
|
+
buildNothingDeliverableResult() {
|
|
597
|
+
const base = this.buildResult("submit_failed");
|
|
598
|
+
return {
|
|
599
|
+
...base,
|
|
600
|
+
subtype: "error_during_execution",
|
|
601
|
+
is_error: true,
|
|
602
|
+
errors: [
|
|
603
|
+
`Your message had nothing the terminal transport could deliver: no text, and no ` +
|
|
604
|
+
`image it could stage to disk. Nothing was sent, so please resend it with text. ` +
|
|
605
|
+
`The session itself is still running.`,
|
|
606
|
+
],
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
/** Tell the provider the dialog state changed; never let a listener throw into the loop. */
|
|
610
|
+
notifyDialogChange() {
|
|
611
|
+
if (!this.onDialogChange)
|
|
612
|
+
return;
|
|
613
|
+
try {
|
|
614
|
+
this.onDialogChange(this.pendingDialog ? { ...this.pendingDialog } : null);
|
|
615
|
+
}
|
|
616
|
+
catch (err) {
|
|
617
|
+
this.logger.warn({ err }, "PtyQuery: dialog listener threw");
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
/** The dialog the TUI is waiting on, if any. */
|
|
621
|
+
interactiveDialog() {
|
|
622
|
+
return this.pendingDialog ? { ...this.pendingDialog } : null;
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Answer the pending dialog by option index (0-based), the way the app's question card
|
|
626
|
+
* does. Returns false when there is no dialog or the keystrokes never registered.
|
|
627
|
+
*/
|
|
628
|
+
async answerDialogByIndex(index) {
|
|
629
|
+
if (!this.pendingDialog || index < 0)
|
|
630
|
+
return false;
|
|
631
|
+
return await this.sendDialogAnswer(this.pendingDialog, index);
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Try to answer the pending TUI dialog with the user's reply. A reply that names an
|
|
635
|
+
* option (its 1-based number or its label) becomes Down-arrow presses + Enter, the same
|
|
636
|
+
* proven mechanism as the resume-dialog answerer. Anything else gets the question echoed
|
|
637
|
+
* back with the options, and nothing is typed - an unanswerable message must never turn
|
|
638
|
+
* into a silent decline.
|
|
639
|
+
*/
|
|
640
|
+
async answerPendingDialog(text) {
|
|
641
|
+
const dialog = this.pendingDialog;
|
|
642
|
+
if (!dialog)
|
|
643
|
+
return;
|
|
644
|
+
const index = matchDialogOption(text, dialog.options);
|
|
645
|
+
if (index === null) {
|
|
646
|
+
this.recordLine(`--- reply does not name a ${dialog.name} option; echoing question ---`);
|
|
647
|
+
this.emit(this.buildDialogPendingResult(dialog));
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
const answered = await this.sendDialogAnswer(dialog, index);
|
|
651
|
+
if (!answered) {
|
|
652
|
+
this.emit(this.buildDialogPendingResult(dialog, true));
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Drive the dialog's own keys: Down to the option, Enter to confirm. Confirmation is the
|
|
657
|
+
* tool_result landing in the transcript (which clears pendingDialog) - an optimistic flag
|
|
658
|
+
* here would record intent as fact.
|
|
659
|
+
*/
|
|
660
|
+
async sendDialogAnswer(dialog, index) {
|
|
661
|
+
this.recordLine(`--- answering ${dialog.name} with option ${index + 1} ---`);
|
|
662
|
+
for (let i = 0; i < index && !this.done; i++) {
|
|
663
|
+
await this.transport.writeRaw?.("\u001b[B");
|
|
664
|
+
await delay(DIALOG_KEY_DELAY_MS);
|
|
665
|
+
}
|
|
666
|
+
await this.transport.writeRaw?.("\r");
|
|
667
|
+
const deadline = monotonicNowMs() + DIALOG_ANSWER_CONFIRM_MS;
|
|
668
|
+
while (this.pendingDialog && !this.done && monotonicNowMs() < deadline) {
|
|
669
|
+
await delay(250);
|
|
670
|
+
}
|
|
671
|
+
if (this.pendingDialog) {
|
|
672
|
+
this.recordLine(`--- ${dialog.name} answer did not register ---`);
|
|
673
|
+
return false;
|
|
674
|
+
}
|
|
675
|
+
// Answer accepted: the turn is live again, restart the liveness clock.
|
|
676
|
+
this.turnStartedAt = monotonicNowMs();
|
|
677
|
+
this.turnInFlight = true;
|
|
678
|
+
this.armStallTimer();
|
|
679
|
+
this.startHeartbeat();
|
|
680
|
+
return true;
|
|
681
|
+
}
|
|
682
|
+
/** The agent is waiting on a dialog; tell the sender what it asks and how to answer. */
|
|
683
|
+
buildDialogPendingResult(dialog, answerFailed = false) {
|
|
684
|
+
const base = this.buildResult("submit_failed");
|
|
685
|
+
const optionLines = dialog.options.length > 0
|
|
686
|
+
? ` Options: ${dialog.options.map((o, i) => `${i + 1}) ${o}`).join(" ")}.`
|
|
687
|
+
: "";
|
|
688
|
+
const preamble = answerFailed
|
|
689
|
+
? `The answer keystrokes did not register in the terminal, so the dialog is still up.`
|
|
690
|
+
: `Your message was NOT delivered because the agent is showing a ${dialog.name === "ExitPlanMode" ? "plan-approval" : "question"} dialog and is waiting for an answer.`;
|
|
691
|
+
return {
|
|
692
|
+
...base,
|
|
693
|
+
subtype: "error_during_execution",
|
|
694
|
+
is_error: true,
|
|
695
|
+
errors: [
|
|
696
|
+
`${preamble}${optionLines} Reply with just the option number (or its exact text) ` +
|
|
697
|
+
`to answer it.`,
|
|
698
|
+
],
|
|
699
|
+
};
|
|
700
|
+
}
|
|
529
701
|
buildSubmitFailedResult() {
|
|
530
702
|
const base = this.buildResult("submit_failed");
|
|
531
703
|
const excerpt = this.terminalTail.slice(-TERMINAL_TAIL_EXCERPT_CHARS).trim();
|
|
@@ -565,6 +737,12 @@ export class PtyQuery {
|
|
|
565
737
|
async clearInterruptPrompt() {
|
|
566
738
|
if (!this.interruptPromptAt)
|
|
567
739
|
return;
|
|
740
|
+
if (this.pendingDialog) {
|
|
741
|
+
// ESC here would DECLINE the pending question/plan dialog - that is where every
|
|
742
|
+
// phantom "User declined to answer" came from. The dialog branch of the input loop
|
|
743
|
+
// owns this state; never clear modals across it.
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
568
746
|
if (monotonicNowMs() - this.interruptPromptAt > INTERRUPT_PROMPT_TTL_MS) {
|
|
569
747
|
this.interruptPromptAt = 0;
|
|
570
748
|
return;
|
|
@@ -775,6 +953,7 @@ export class PtyQuery {
|
|
|
775
953
|
// ---- transcript → SDKMessage stream + turn-end ----------------------------
|
|
776
954
|
onTranscriptMessage(message) {
|
|
777
955
|
this.emit(message);
|
|
956
|
+
this.trackInteractiveDialog(message);
|
|
778
957
|
if (message.type === "assistant") {
|
|
779
958
|
const beta = message.message;
|
|
780
959
|
this.lastStopReason = beta.stop_reason ?? null;
|
|
@@ -788,6 +967,42 @@ export class PtyQuery {
|
|
|
788
967
|
this.armStallTimer();
|
|
789
968
|
this.scheduleTurnEndCheck();
|
|
790
969
|
}
|
|
970
|
+
/**
|
|
971
|
+
* Track interactive TUI dialogs (AskUserQuestion / plan-mode approval) from the
|
|
972
|
+
* transcript. While one is pending the terminal is WAITING, not stalled: the stall
|
|
973
|
+
* backstop must stand down, no ESC may be sent (it declines the dialog - the source of
|
|
974
|
+
* every phantom "User declined" this week), and an incoming reply is translated to
|
|
975
|
+
* dialog keystrokes instead of typed text.
|
|
976
|
+
*/
|
|
977
|
+
trackInteractiveDialog(message) {
|
|
978
|
+
const content = message.message?.content;
|
|
979
|
+
if (!Array.isArray(content))
|
|
980
|
+
return;
|
|
981
|
+
for (const block of content) {
|
|
982
|
+
if (!block || typeof block !== "object")
|
|
983
|
+
continue;
|
|
984
|
+
const b = block;
|
|
985
|
+
if (message.type === "assistant" &&
|
|
986
|
+
b.type === "tool_use" &&
|
|
987
|
+
b.id &&
|
|
988
|
+
(b.name === "AskUserQuestion" || b.name === "ExitPlanMode")) {
|
|
989
|
+
this.pendingDialog = {
|
|
990
|
+
toolUseId: b.id,
|
|
991
|
+
name: b.name,
|
|
992
|
+
input: isRecord(b.input) ? b.input : {},
|
|
993
|
+
options: extractDialogOptions(b.name, b.input),
|
|
994
|
+
};
|
|
995
|
+
this.recordLine(`--- interactive dialog pending: ${b.name} ---`);
|
|
996
|
+
this.notifyDialogChange();
|
|
997
|
+
}
|
|
998
|
+
const pending = this.pendingDialog;
|
|
999
|
+
if (pending && b.type === "tool_result" && b.tool_use_id === pending.toolUseId) {
|
|
1000
|
+
this.recordLine(`--- interactive dialog answered: ${pending.name} ---`);
|
|
1001
|
+
this.pendingDialog = null;
|
|
1002
|
+
this.notifyDialogChange();
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
791
1006
|
scheduleTurnEndCheck() {
|
|
792
1007
|
if (this.turnEndTimer)
|
|
793
1008
|
clearTimeout(this.turnEndTimer);
|
|
@@ -835,6 +1050,14 @@ export class PtyQuery {
|
|
|
835
1050
|
this.stallTimer = null;
|
|
836
1051
|
if (this.done || !this.turnInFlight)
|
|
837
1052
|
return;
|
|
1053
|
+
if (this.pendingDialog) {
|
|
1054
|
+
// Waiting on a human is not a stall. Observed live 2026-08-04: an ExitPlanMode
|
|
1055
|
+
// approval sat pending for 2.5 hours and the backstop declared the turn failed,
|
|
1056
|
+
// which read as a wedge and got a healthy process killed.
|
|
1057
|
+
this.recordLine(`--- stall budget elapsed but ${this.pendingDialog.name} is pending ---`);
|
|
1058
|
+
this.armStallTimer();
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
838
1061
|
this.turnInFlight = false;
|
|
839
1062
|
this.stopHeartbeat();
|
|
840
1063
|
const silentForMs = this.stallTimeoutMs;
|
|
@@ -1074,6 +1297,63 @@ export class PtyQuery {
|
|
|
1074
1297
|
}
|
|
1075
1298
|
}
|
|
1076
1299
|
/** Extract the plain-text prompt from a user SDK message for typing into the PTY. */
|
|
1300
|
+
/** Delay between Down-arrow presses when answering a dialog; the TUI animates selection. */
|
|
1301
|
+
const DIALOG_KEY_DELAY_MS = 150;
|
|
1302
|
+
/** How long to wait for the dialog's tool_result to land after answering keystrokes. */
|
|
1303
|
+
const DIALOG_ANSWER_CONFIRM_MS = 8000;
|
|
1304
|
+
/**
|
|
1305
|
+
* Options the TUI dialog offers, in on-screen order. AskUserQuestion carries them in its
|
|
1306
|
+
* input; the plan-approval dialog's options are the CLI's own and version-dependent, so
|
|
1307
|
+
* they stay empty and replies are matched by number only.
|
|
1308
|
+
*/
|
|
1309
|
+
function extractDialogOptions(name, input) {
|
|
1310
|
+
if (name !== "AskUserQuestion")
|
|
1311
|
+
return [];
|
|
1312
|
+
const questions = input?.questions;
|
|
1313
|
+
if (!Array.isArray(questions) || questions.length === 0)
|
|
1314
|
+
return [];
|
|
1315
|
+
const options = questions[0]?.options;
|
|
1316
|
+
if (!Array.isArray(options))
|
|
1317
|
+
return [];
|
|
1318
|
+
const labels = [];
|
|
1319
|
+
for (const option of options) {
|
|
1320
|
+
if (typeof option === "string")
|
|
1321
|
+
labels.push(option);
|
|
1322
|
+
else if (option && typeof option === "object") {
|
|
1323
|
+
const label = option.label;
|
|
1324
|
+
if (typeof label === "string")
|
|
1325
|
+
labels.push(label);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
return labels;
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* Resolve the user's reply to a 0-based dialog option index, or null when it names none.
|
|
1332
|
+
* "2" picks the second option; an exact case-insensitive label match works too. Free text
|
|
1333
|
+
* stays null on purpose: guessing would answer a question the user did not answer.
|
|
1334
|
+
*/
|
|
1335
|
+
function matchDialogOption(text, options) {
|
|
1336
|
+
const reply = text.trim();
|
|
1337
|
+
if (/^[1-9]$/.test(reply)) {
|
|
1338
|
+
const index = Number(reply) - 1;
|
|
1339
|
+
if (options.length === 0 || index < options.length)
|
|
1340
|
+
return index;
|
|
1341
|
+
return null;
|
|
1342
|
+
}
|
|
1343
|
+
const lowered = reply.toLowerCase();
|
|
1344
|
+
const byLabel = options.findIndex((o) => o.trim().toLowerCase() === lowered);
|
|
1345
|
+
return byLabel >= 0 ? byLabel : null;
|
|
1346
|
+
}
|
|
1347
|
+
function isRecord(value) {
|
|
1348
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
1349
|
+
}
|
|
1350
|
+
/** Extensions for the media types the app can send; anything unknown stages as .png. */
|
|
1351
|
+
const IMAGE_MEDIA_TYPE_EXT = {
|
|
1352
|
+
"image/jpeg": "jpg",
|
|
1353
|
+
"image/png": "png",
|
|
1354
|
+
"image/gif": "gif",
|
|
1355
|
+
"image/webp": "webp",
|
|
1356
|
+
};
|
|
1077
1357
|
function extractUserText(message) {
|
|
1078
1358
|
const content = message.message?.content;
|
|
1079
1359
|
if (typeof content === "string")
|
|
@@ -1,7 +1,48 @@
|
|
|
1
1
|
import * as fsp from "node:fs/promises";
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
+
import { createCwdResolver } from "../search/resolve-cwd.js";
|
|
4
5
|
const HEADER_READ_BYTES = 64 * 1024;
|
|
6
|
+
/** Take any field this record supplies that the header does not have yet. */
|
|
7
|
+
function absorbRecord(header, obj) {
|
|
8
|
+
if (header.title === null && typeof obj["custom-title"] === "string") {
|
|
9
|
+
header.title = obj["custom-title"];
|
|
10
|
+
}
|
|
11
|
+
if (header.cwd === null && typeof obj.cwd === "string" && obj.cwd.length > 0) {
|
|
12
|
+
header.cwd = obj.cwd;
|
|
13
|
+
}
|
|
14
|
+
if (header.agentName === null && typeof obj.agentName === "string") {
|
|
15
|
+
header.agentName = obj.agentName;
|
|
16
|
+
}
|
|
17
|
+
if (header.lastActivity === null && typeof obj.lastActivity === "string") {
|
|
18
|
+
header.lastActivity = obj.lastActivity;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** Nothing further down the file can add anything — stop scanning. */
|
|
22
|
+
function isHeaderComplete(header) {
|
|
23
|
+
return (header.title !== null &&
|
|
24
|
+
header.cwd !== null &&
|
|
25
|
+
header.agentName !== null &&
|
|
26
|
+
header.lastActivity !== null);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Recover the fields the palette needs from the head of a transcript.
|
|
30
|
+
*
|
|
31
|
+
* The FIRST record still decides whether this file is a transcript at all — if
|
|
32
|
+
* it does not parse, the file is treated as corrupt and skipped, exactly as
|
|
33
|
+
* before. What changed is that a parseable first record no longer ends the
|
|
34
|
+
* search: later records in the read window are scanned for any field the first
|
|
35
|
+
* one left unset, `cwd` above all.
|
|
36
|
+
*
|
|
37
|
+
* Why it matters: the opening record is often session meta that carries no
|
|
38
|
+
* `cwd`, and the caller then fell through to `recoverCwdFromDirName`, which
|
|
39
|
+
* FABRICATES a path. The slug is lossy — `-home-claudiao-super-repo` decodes
|
|
40
|
+
* to `/home/claudiao/super/repo`, a directory that exists nowhere — so every
|
|
41
|
+
* palette row offered to resume into a directory that was not there, and
|
|
42
|
+
* resuming into a missing directory hydrates an empty chat. The authoritative
|
|
43
|
+
* value was sitting a few records further down the same buffer we had already
|
|
44
|
+
* read.
|
|
45
|
+
*/
|
|
5
46
|
async function readHeader(filePath) {
|
|
6
47
|
const fh = await fsp.open(filePath, "r");
|
|
7
48
|
try {
|
|
@@ -9,19 +50,37 @@ async function readHeader(filePath) {
|
|
|
9
50
|
const { bytesRead } = await fh.read(buf, 0, HEADER_READ_BYTES, 0);
|
|
10
51
|
if (bytesRead === 0)
|
|
11
52
|
return null;
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
53
|
+
const text = buf.subarray(0, bytesRead).toString("utf-8");
|
|
54
|
+
// A trailing fragment with no newline is a COMPLETE record only when the
|
|
55
|
+
// read reached EOF; mid-window it is truncated and must not be parsed.
|
|
56
|
+
const readHitEof = bytesRead < HEADER_READ_BYTES;
|
|
57
|
+
const header = { title: null, cwd: null, agentName: null, lastActivity: null };
|
|
58
|
+
let isFirstRecord = true;
|
|
59
|
+
let cursor = 0;
|
|
60
|
+
while (cursor < text.length) {
|
|
61
|
+
const nl = text.indexOf("\n", cursor);
|
|
62
|
+
if (nl === -1 && !readHitEof)
|
|
63
|
+
break;
|
|
64
|
+
const line = (nl === -1 ? text.slice(cursor) : text.slice(cursor, nl)).trim();
|
|
65
|
+
cursor = nl === -1 ? text.length : nl + 1;
|
|
66
|
+
if (line.length === 0)
|
|
67
|
+
continue;
|
|
68
|
+
let obj;
|
|
69
|
+
try {
|
|
70
|
+
obj = JSON.parse(line);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// The opening record must parse; anything later is best-effort.
|
|
74
|
+
if (isFirstRecord)
|
|
75
|
+
return null;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
isFirstRecord = false;
|
|
79
|
+
absorbRecord(header, obj);
|
|
80
|
+
if (isHeaderComplete(header))
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
return isFirstRecord ? null : header;
|
|
25
84
|
}
|
|
26
85
|
finally {
|
|
27
86
|
await fh.close();
|
|
@@ -51,13 +110,19 @@ async function loadSession(transcriptDir, dirSlug, fileName, logger) {
|
|
|
51
110
|
return null;
|
|
52
111
|
try {
|
|
53
112
|
const header = await readHeader(filePath);
|
|
54
|
-
|
|
113
|
+
// No parseable opening record — not a transcript we can offer to resume.
|
|
114
|
+
// This used to happen via a thrown JSON.parse caught below; `readHeader`
|
|
115
|
+
// now reports it by returning null, so the skip has to be explicit or a
|
|
116
|
+
// corrupt file would be listed with a fabricated cwd and an mtime date.
|
|
117
|
+
if (header === null)
|
|
118
|
+
return null;
|
|
119
|
+
const lastActivity = header.lastActivity ?? new Date(stat.mtimeMs).toISOString();
|
|
55
120
|
return {
|
|
56
121
|
sid,
|
|
57
|
-
title: header
|
|
122
|
+
title: header.title,
|
|
58
123
|
lastActivity,
|
|
59
|
-
cwd: header
|
|
60
|
-
agentName: header
|
|
124
|
+
cwd: header.cwd ?? recoverCwdFromDirName(dirSlug),
|
|
125
|
+
agentName: header.agentName,
|
|
61
126
|
};
|
|
62
127
|
}
|
|
63
128
|
catch (err) {
|
|
@@ -108,7 +173,22 @@ export async function collectAllSessions(projectsRoot, logger) {
|
|
|
108
173
|
throw err;
|
|
109
174
|
}
|
|
110
175
|
const perDir = await Promise.all(topLevel.map((dirSlug) => loadDir(projectsRoot, dirSlug, logger)));
|
|
176
|
+
// History moves between machines; paths do not. Roughly half of this corpus
|
|
177
|
+
// carries a `/Users/<mac-user>/…` cwd that means nothing on a Linux box, and
|
|
178
|
+
// resuming into a directory that is not there hydrates an empty chat. Reuse
|
|
179
|
+
// the same resolver the search path uses so a palette row and a search hit
|
|
180
|
+
// for the SAME session agree on where to resume. It only ever remaps onto a
|
|
181
|
+
// directory that EXISTS and leaves anything unresolvable untouched, so the
|
|
182
|
+
// UI keeps showing the truth rather than a plausible lie. The resolver
|
|
183
|
+
// caches per call — thousands of sessions share a handful of cwds, so this
|
|
184
|
+
// collapses into one stat per distinct directory.
|
|
185
|
+
const resolveCwd = createCwdResolver();
|
|
111
186
|
const sessions = perDir.flat();
|
|
187
|
+
await Promise.all(sessions.map(async (entry) => {
|
|
188
|
+
// Assigned in place: `loadSession` just built these and nothing else
|
|
189
|
+
// holds a reference yet, so there is no copy worth allocating.
|
|
190
|
+
entry.cwd = (await resolveCwd(entry.cwd)).cwd;
|
|
191
|
+
}));
|
|
112
192
|
sessions.sort((a, b) => {
|
|
113
193
|
if (a.lastActivity < b.lastActivity)
|
|
114
194
|
return 1;
|
|
@@ -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.26\",\"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]);
|
|
@@ -14906,7 +14906,7 @@ __d(function(g,r,i,a,m,_e,_d){"use strict";const e=["color","size","strokeWidth"
|
|
|
14906
14906
|
* This source code is licensed under the ISC license.
|
|
14907
14907
|
* See the LICENSE file in the root directory of this source tree.
|
|
14908
14908
|
*/function t(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(_e,'__esModule',{value:!0}),Object.defineProperty(_e,"default",{enumerable:!0,get:function(){return d}});var n=t(r(_d[0])),o=r(_d[1]),c=(function(e){if(e&&e.__esModule)return e;var t={};return e&&Object.keys(e).forEach(function(n){var o=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,o.get?o:{enumerable:!0,get:function(){return e[n]}})}),t.default=e,t})(r(_d[2])),u=r(_d[3]),s=t(u);const d=(0,o.forwardRef)((t,d)=>{let{color:l="currentColor",size:f=24,strokeWidth:b=2,absoluteStrokeWidth:h,children:O,iconNode:j}=t,p=(0,n.default)(t,e);const _=Object.assign({stroke:l,strokeWidth:h?24*Number(b)/Number(f):b},p);return(0,o.createElement)(c.Svg,Object.assign({ref:d},s.default,{width:f,height:f},_),[...j.map(([e,t])=>{const n=e.charAt(0).toUpperCase()+e.slice(1);return(0,o.createElement)(c[n],Object.assign({},u.childDefaultAttributes,_,t))}),...(Array.isArray(O)?O:[O])||[]])})},3263,[35,36,1604,1625]);
|
|
14909
|
-
__d(function(g,_r,i,_a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.useCommandCenter=function(){const c=(0,t.c)(107),{t:w}=(0,o.useTranslation)(),y=(0,r.usePathname)(),{overrides:C}=(0,v.useKeyboardShortcutOverrides)(),k=(0,s.useKeyboardShortcutsStore)(V),X=(0,s.useKeyboardShortcutsStore)(J),Y=(0,n.useRef)(null),Z=(0,n.useRef)(!1),ee=(0,n.useRef)(k),te=(0,n.useRef)(0);let ne;c[0]===Symbol.for("react.memo_cache_sentinel")?(ne=[],c[0]=ne):ne=c[0];const re=(0,n.useRef)(ne),oe=(0,n.useRef)(G),se=(0,n.useRef)(z),[ie,ce]=(0,n.useState)(""),[ue,ae]=(0,n.useState)(0),le=(0,n.useSyncExternalStore)(_.subscribeCommandContributions,_.getCommandContributions,_.getCommandContributions),de=(0,K.useActiveServerId)(),fe=k?de:null,{agents:me}=(0,l.useAggregatedAgents)();let ge;c[1]!==fe||c[2]!==k?(ge={serverId:fe,enabled:k},c[1]=fe,c[2]=k,c[3]=ge):ge=c[3];const{sessions:he}=(0,u.useAllSessionsList)(ge);let pe;c[4]!==fe||c[5]!==k||c[6]!==ie?(pe={serverId:fe,query:ie,enabled:k},c[4]=fe,c[5]=k,c[6]=ie,c[7]=pe):pe=c[7];const{results:we,capable:ye,isLoading:ve}=(0,a.useRecallSearch)(pe);let Ce;e:{if(!k||0===me.length){Ce=D;break e}let t;if(c[8]!==me||c[9]!==ie){let n;c[11]!==ie?(n=t=>x(t,ie),c[11]=ie,c[12]=n):n=c[12],t=me.filter(n),t.sort(q),c[8]=me,c[9]=ie,c[10]=t}else t=c[10];Ce=t}const ke=Ce,Ae=ye&&ie.trim().length>=2&&(we.length>0||ve);let Se;e:{if(!k||0===he.length||Ae){Se=F;break e}let t,n;if(c[13]!==me){t=new Set;for(const n of me)t.add(n.id);c[13]=me,c[14]=t}else t=c[14];if(c[15]!==he||c[16]!==t||c[17]!==ie){let r;c[19]!==t||c[20]!==ie?(r=n=>!t.has(n.sid)&&W(n,ie),c[19]=t,c[20]=ie,c[21]=r):r=c[21],n=he.filter(r),n.sort($),c[15]=he,c[16]=t,c[17]=ie,c[18]=n}else n=c[18];Se=n}const be=Se;let Ee;e:{if(!k||!Ae||0===we.length){Ee=O;break e}let t,n;if(c[22]!==me){t=new Set;for(const n of me)t.add(n.id);c[22]=me,c[23]=t}else t=c[23];if(c[24]!==t||c[25]!==we){let r;c[27]!==t?(r=n=>!t.has(n.sid),c[27]=t,c[28]=r):r=c[28],n=we.filter(r),c[24]=t,c[25]=we,c[26]=n}else n=c[26];Ee=n}const Ie=Ee;let Ke;c[29]===Symbol.for("react.memo_cache_sentinel")?(Ke=(0,p.buildSettingsRoute)(),c[29]=Ke):Ke=c[29];const Re=Ke;let Le;c[30]===Symbol.for("react.memo_cache_sentinel")?(Le=(0,p.buildOpenProjectRoute)(),c[30]=Le):Le=c[30];const Te=Le;let Pe;e:{if(!k){Pe=M;break e}let t;if(c[31]!==le||c[32]!==C||c[33]!==ie||c[34]!==w){let n,r;c[36]!==ie||c[37]!==w?(n=t=>!("home"===t.routeKind&&!Te)&&N(ie,t,w(t.titleKey)),c[36]=ie,c[37]=w,c[38]=n):n=c[38],c[39]!==C||c[40]!==w?(r=t=>{let n;return"settings"===t.routeKind?n=Re:"home"===t.routeKind&&(n=Te),{kind:"action",id:t.id,title:w(t.titleKey),icon:t.icon,route:n,shortcutKeys:U(t.actionId,C)}},c[39]=C,c[40]=w,c[41]=r):r=c[41];const o=B.filter(n).map(r),s=ie.trim().toLowerCase();t=[...o,...le.filter(t=>0===s.length||t.title.toLowerCase().includes(s)||t.keywords.some(t=>t.includes(s))).map(Q)],c[31]=le,c[32]=C,c[33]=ie,c[34]=w,c[35]=t}else t=c[35];Pe=t}const _e=Pe;let De;e:{if(!k){De=j;break e}let t;if(c[42]!==_e||c[43]!==ke||c[44]!==Ie||c[45]!==be){t=[];for(const n of _e)t.push({kind:"action",action:n});for(const n of ke)t.push({kind:"agent",agent:n});for(const n of be)t.push({kind:"session",session:n});for(const n of Ie)t.push({kind:"recallMatch",match:n});c[42]=_e,c[43]=ke,c[44]=Ie,c[45]=be,c[46]=t}else t=c[46];De=t}const Fe=De;let Oe;c[47]!==X?(Oe=()=>{X(!1)},c[47]=X,c[48]=Oe):Oe=c[48];const Me=Oe;let je;c[49]!==X?(je=t=>{Z.current=!0,(0,h.clearCommandCenterFocusRestoreElement)(),X(!1),(0,A.navigateToAgent)({serverId:t.serverId,agentId:t.id})},c[49]=X,c[50]=je):je=c[50];const xe=je,We=(0,f.useOpenProjectPicker)();let qe;c[51]!==We||c[52]!==X?(qe=t=>{(0,h.clearCommandCenterFocusRestoreElement)(),X(!1),t.run?t.run():"new-agent"!==t.id?t.route&&(Z.current=!0,r.router.push(t.route)):We()},c[51]=We,c[52]=X,c[53]=qe):qe=c[53];const Be=qe,Ne=(0,R.useHostRuntimeClient)(fe??""),Ue=(0,T.useToast)();let He;c[54]!==fe||c[55]!==y||c[56]!==X||c[57]!==Ue?(He=(t,n)=>{Z.current=!0,(0,h.clearCommandCenterFocusRestoreElement)(),X(!1);const r=fe;if(!r)return void Ue.error("Couldn't open session: no active server.");const o=n?.trim();if(o){const n=E.useSessionStore.getState().sessions[r]?.workspaces,s=(0,b.resolveWorkspaceIdByExecutionDirectory)({workspaces:n?.values(),workspaceDirectory:o});if(s)return void(0,S.navigateToPreparedWorkspaceTab)({serverId:r,workspaceId:s,target:{kind:"agent",agentId:t},currentPathname:y,pin:!0})}(0,A.navigateToAgent)({serverId:r,agentId:t,currentPathname:y})},c[54]=fe,c[55]=y,c[56]=X,c[57]=Ue,c[58]=He):He=c[58];const Qe=He;let $e;c[59]!==fe||c[60]!==y||c[61]!==Ne||c[62]!==X||c[63]!==Ue?($e=t=>{Z.current=!0,(0,h.clearCommandCenterFocusRestoreElement)(),X(!1);const n=fe;Ne&&n?Ne.createAgent({config:{provider:"claude",cwd:t.cwd},resumeSessionId:t.sid}).then(t=>(0,A.navigateToAgent)({serverId:n,agentId:t.id,currentPathname:y})).catch(t=>{const n=t instanceof Error?t.message:String(t);Ue.error(`Couldn't open session: ${n}`)}):Ue.error("Couldn't open session: daemon not connected.")},c[59]=fe,c[60]=y,c[61]=Ne,c[62]=X,c[63]=Ue,c[64]=$e):$e=c[64];const ze=$e;let Ge;c[65]!==ze||c[66]!==Qe?(Ge=t=>{"open-agent"!==(0,P.recallSelectionKind)(t.source)?ze({sid:t.sid,title:null,lastActivity:t.timestamp,cwd:t.cwd,agentName:null}):Qe(t.sid,t.cwd)},c[65]=ze,c[66]=Qe,c[67]=Ge):Ge=c[67];const Je=Ge;let Ve;c[68]!==Be||c[69]!==xe||c[70]!==Je||c[71]!==ze?(Ve=t=>{"action"!==t.kind?"session"!==t.kind?"recallMatch"!==t.kind?xe(t.agent):Je(t.match):ze(t.session):Be(t.action)},c[68]=Be,c[69]=xe,c[70]=Je,c[71]=ze,c[72]=Ve):Ve=c[72];const Xe=Ve;let Ye,Ze,et,tt,nt,rt,ot,st,it,ct,ut,at,lt;c[73]!==ue?(Ye=()=>{te.current=ue},Ze=[ue],c[73]=ue,c[74]=Ye,c[75]=Ze):(Ye=c[74],Ze=c[75]);(0,n.useEffect)(Ye,Ze),c[76]!==Fe?(et=()=>{re.current=Fe},tt=[Fe],c[76]=Fe,c[77]=et,c[78]=tt):(et=c[77],tt=c[78]);(0,n.useEffect)(et,tt),c[79]!==Me?(nt=()=>{oe.current=Me},rt=[Me],c[79]=Me,c[80]=nt,c[81]=rt):(nt=c[80],rt=c[81]);(0,n.useEffect)(nt,rt),c[82]!==Xe?(ot=()=>{se.current=Xe},st=[Xe],c[82]=Xe,c[83]=ot,c[84]=st):(ot=c[83],st=c[84]);(0,n.useEffect)(ot,st),c[85]!==k?(it=()=>{const t=ee.current;if(ee.current=k,!k){if(ce(""),ae(0),t&&!Z.current&&L.isWeb){const t=(0,h.takeCommandCenterFocusRestoreElement)(),n=()=>Boolean(t)&&"undefined"!=typeof document&&document.activeElement===t;return(0,I.focusWithRetries)({focus:()=>t?.focus(),isFocused:n,onTimeout:H})}return}Z.current=!1;const n=setTimeout(()=>{Y.current?.focus()},0);return()=>clearTimeout(n)},ct=[k],c[85]=k,c[86]=it,c[87]=ct):(it=c[86],ct=c[87]);(0,n.useEffect)(it,ct),c[88]!==ue||c[89]!==Fe.length||c[90]!==k?(ut=()=>{k&&ue>=Fe.length&&ae(Fe.length>0?Fe.length-1:0)},at=[ue,Fe.length,k],c[88]=ue,c[89]=Fe.length,c[90]=k,c[91]=ut,c[92]=at):(ut=c[91],at=c[92]);(0,n.useEffect)(ut,at),c[93]!==k?(lt=t=>{if(!k)return!1;const n=re.current;if("Escape"===t)return oe.current(),!0;if("Enter"===t){if(0===n.length)return!1;const t=Math.max(0,Math.min(te.current,n.length-1));return se.current(n[t]),!0}return("ArrowDown"===t||"ArrowUp"===t)&&(0!==n.length&&(ae(r=>{const o=r+("ArrowDown"===t?1:-1);return o<0?n.length-1:o>=n.length?0:o}),!0))},c[93]=k,c[94]=lt):lt=c[94];const dt=lt;let ft,mt,gt;c[95]!==dt||c[96]!==k?(ft=()=>{if(!k||!L.isWeb)return;const t=t=>{"ArrowDown"!==t.key&&"ArrowUp"!==t.key&&"Enter"!==t.key&&"Escape"!==t.key||dt(t.key)&&t.preventDefault()};return window.addEventListener("keydown",t,!0),()=>window.removeEventListener("keydown",t,!0)},mt=[k,dt],c[95]=dt,c[96]=k,c[97]=ft,c[98]=mt):(ft=c[97],mt=c[98]);(0,n.useEffect)(ft,mt),c[99]!==ue||c[100]!==Me||c[101]!==dt||c[102]!==Xe||c[103]!==Fe||c[104]!==k||c[105]!==ie?(gt={open:k,inputRef:Y,query:ie,setQuery:ce,activeIndex:ue,setActiveIndex:ae,items:Fe,handleClose:Me,handleSelectItem:Xe,handleKeyEvent:dt},c[99]=ue,c[100]=Me,c[101]=dt,c[102]=Xe,c[103]=Fe,c[104]=k,c[105]=ie,c[106]=gt):gt=c[106];return gt};var t=_r(d[0]),n=_r(d[1]),r=_r(d[2]),o=_r(d[3]),s=_r(d[4]),c=_r(d[5]),u=_r(d[6]),a=_r(d[7]),l=_r(d[8]),f=_r(d[9]),h=_r(d[10]),p=_r(d[11]),w=_r(d[12]),y=_r(d[13]),v=_r(d[14]),C=_r(d[15]),k=_r(d[16]),A=_r(d[17]),S=_r(d[18]),b=_r(d[19]),E=_r(d[20]),I=_r(d[21]),K=_r(d[22]),R=_r(d[23]),L=_r(d[24]),T=_r(d[25]),P=_r(d[26]),_=_r(d[27]);const D=[],F=[],O=[],M=[],j=[];function x(t,n){if(!n)return!0;const r=n.toLowerCase(),o=(t.title??"New agent").toLowerCase(),s=t.cwd.toLowerCase();return o.includes(r)||s.includes(r)}function W(t,n){if(!n)return!0;const r=n.toLowerCase(),o=(t.title??"").toLowerCase(),s=(t.cwd??"").toLowerCase(),c=t.sid.toLowerCase();return o.includes(r)||s.includes(r)||c.includes(r)}function q(t,n){const r=(t.pendingPermissionCount??0)>0?1:0,o=(n.pendingPermissionCount??0)>0?1:0;if(r!==o)return o-r;const s=t.requiresAttention?1:0,c=n.requiresAttention?1:0;if(s!==c)return c-s;const u="running"===t.status?1:0,a="running"===n.status?1:0;return u!==a?a-u:n.lastActivityAt.getTime()-t.lastActivityAt.getTime()}const B=[{id:"new-agent",titleKey:"shell.commandCenter.openProject",icon:"plus",actionId:"new-agent",keywords:["open","project","folder","workspace","repo"],routeKind:"none"},{id:"home",titleKey:"shell.commandCenter.home",icon:"home",keywords:["home","start","import","session","pair","device","providers"],routeKind:"home"},{id:"settings",titleKey:"sidebar.actions.settings",icon:"settings",keywords:["settings","preferences","config","configuration"],routeKind:"settings"}];function N(t,n,r){const o=t.trim().toLowerCase();return!o||(!!r.toLowerCase().includes(o)||n.keywords.some(t=>t.includes(o)))}function U(t,n){if(!t)return;const r={isMac:"mac"===(0,C.getShortcutOs)(),isDesktop:(0,k.getIsElectronRuntime)()},o=(0,y.getBindingIdForAction)(t,r);if(!o)return;const s=n[o];if(s)return(0,w.chordStringToShortcutKeys)(s);const c=(0,y.getDefaultKeysForAction)(t,r);return c?[c]:void 0}function H(){c.keyboardActionDispatcher.dispatch({id:"message-input.focus",scope:"message-input"})}function Q(t){return{kind:"action",id:t.id,title:t.title,run:t.run}}function $(t,n){return t.lastActivity<n.lastActivity?1:t.lastActivity>n.lastActivity?-1:0}function z(){}function G(){}function J(t){return t.setCommandCenterOpen}function V(t){return t.commandCenterOpen}},3264,[349,36,1143,1573,3265,3269,3270,3446,3447,3452,3479,3477,3480,3481,3482,3483,3457,3484,3486,3495,3414,3496,3497,3271,3407,3499,3503,3504]);
|
|
14909
|
+
__d(function(g,_r,i,_a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.useCommandCenter=function(){const c=(0,t.c)(107),{t:w}=(0,o.useTranslation)(),y=(0,r.usePathname)(),{overrides:C}=(0,v.useKeyboardShortcutOverrides)(),k=(0,s.useKeyboardShortcutsStore)(V),X=(0,s.useKeyboardShortcutsStore)(J),Y=(0,n.useRef)(null),Z=(0,n.useRef)(!1),ee=(0,n.useRef)(k),te=(0,n.useRef)(0);let ne;c[0]===Symbol.for("react.memo_cache_sentinel")?(ne=[],c[0]=ne):ne=c[0];const re=(0,n.useRef)(ne),oe=(0,n.useRef)(G),se=(0,n.useRef)(z),[ie,ce]=(0,n.useState)(""),[ue,ae]=(0,n.useState)(0),le=(0,n.useSyncExternalStore)(_.subscribeCommandContributions,_.getCommandContributions,_.getCommandContributions),de=(0,K.useActiveServerId)(),fe=k?de:null,{agents:me}=(0,l.useAggregatedAgents)();let ge;c[1]!==fe||c[2]!==k?(ge={serverId:fe,enabled:k},c[1]=fe,c[2]=k,c[3]=ge):ge=c[3];const{sessions:he}=(0,u.useAllSessionsList)(ge);let pe;c[4]!==fe||c[5]!==k||c[6]!==ie?(pe={serverId:fe,query:ie,enabled:k},c[4]=fe,c[5]=k,c[6]=ie,c[7]=pe):pe=c[7];const{results:we,capable:ye,isLoading:ve}=(0,a.useRecallSearch)(pe);let Ce;e:{if(!k||0===me.length){Ce=D;break e}let t;if(c[8]!==me||c[9]!==ie){let n;c[11]!==ie?(n=t=>x(t,ie),c[11]=ie,c[12]=n):n=c[12],t=me.filter(n),t.sort(q),c[8]=me,c[9]=ie,c[10]=t}else t=c[10];Ce=t}const ke=Ce,Ae=ye&&ie.trim().length>=2&&(we.length>0||ve);let Se;e:{if(!k||0===he.length||Ae){Se=F;break e}let t,n;if(c[13]!==me){t=new Set;for(const n of me)t.add(n.id);c[13]=me,c[14]=t}else t=c[14];if(c[15]!==he||c[16]!==t||c[17]!==ie){let r;c[19]!==t||c[20]!==ie?(r=n=>!t.has(n.sid)&&W(n,ie),c[19]=t,c[20]=ie,c[21]=r):r=c[21],n=he.filter(r),n.sort($),c[15]=he,c[16]=t,c[17]=ie,c[18]=n}else n=c[18];Se=n}const be=Se;let Ee;e:{if(!k||!Ae||0===we.length){Ee=O;break e}let t,n;if(c[22]!==me){t=new Set;for(const n of me)t.add(n.id);c[22]=me,c[23]=t}else t=c[23];if(c[24]!==t||c[25]!==we){let r;c[27]!==t?(r=n=>!t.has(n.sid),c[27]=t,c[28]=r):r=c[28],n=we.filter(r),c[24]=t,c[25]=we,c[26]=n}else n=c[26];Ee=n}const Ie=Ee;let Ke;c[29]===Symbol.for("react.memo_cache_sentinel")?(Ke=(0,p.buildSettingsRoute)(),c[29]=Ke):Ke=c[29];const Re=Ke;let Le;c[30]===Symbol.for("react.memo_cache_sentinel")?(Le=(0,p.buildOpenProjectRoute)(),c[30]=Le):Le=c[30];const Te=Le;let Pe;e:{if(!k){Pe=M;break e}let t;if(c[31]!==le||c[32]!==C||c[33]!==ie||c[34]!==w){let n,r;c[36]!==ie||c[37]!==w?(n=t=>!("home"===t.routeKind&&!Te)&&N(ie,t,w(t.titleKey)),c[36]=ie,c[37]=w,c[38]=n):n=c[38],c[39]!==C||c[40]!==w?(r=t=>{let n;return"settings"===t.routeKind?n=Re:"home"===t.routeKind&&(n=Te),{kind:"action",id:t.id,title:w(t.titleKey),icon:t.icon,route:n,shortcutKeys:U(t.actionId,C)}},c[39]=C,c[40]=w,c[41]=r):r=c[41];const o=B.filter(n).map(r),s=ie.trim().toLowerCase();t=[...o,...le.filter(t=>0===s.length||t.title.toLowerCase().includes(s)||t.keywords.some(t=>t.includes(s))).map(Q)],c[31]=le,c[32]=C,c[33]=ie,c[34]=w,c[35]=t}else t=c[35];Pe=t}const _e=Pe;let De;e:{if(!k){De=j;break e}let t;if(c[42]!==_e||c[43]!==ke||c[44]!==Ie||c[45]!==be){t=[];for(const n of _e)t.push({kind:"action",action:n});for(const n of ke)t.push({kind:"agent",agent:n});for(const n of Ie)t.push({kind:"recallMatch",match:n});for(const n of be)t.push({kind:"session",session:n});c[42]=_e,c[43]=ke,c[44]=Ie,c[45]=be,c[46]=t}else t=c[46];De=t}const Fe=De;let Oe;c[47]!==X?(Oe=()=>{X(!1)},c[47]=X,c[48]=Oe):Oe=c[48];const Me=Oe;let je;c[49]!==X?(je=t=>{Z.current=!0,(0,h.clearCommandCenterFocusRestoreElement)(),X(!1),(0,A.navigateToAgent)({serverId:t.serverId,agentId:t.id})},c[49]=X,c[50]=je):je=c[50];const xe=je,We=(0,f.useOpenProjectPicker)();let qe;c[51]!==We||c[52]!==X?(qe=t=>{(0,h.clearCommandCenterFocusRestoreElement)(),X(!1),t.run?t.run():"new-agent"!==t.id?t.route&&(Z.current=!0,r.router.push(t.route)):We()},c[51]=We,c[52]=X,c[53]=qe):qe=c[53];const Be=qe,Ne=(0,R.useHostRuntimeClient)(fe??""),Ue=(0,T.useToast)();let He;c[54]!==fe||c[55]!==y||c[56]!==X||c[57]!==Ue?(He=(t,n)=>{Z.current=!0,(0,h.clearCommandCenterFocusRestoreElement)(),X(!1);const r=fe;if(!r)return void Ue.error("Couldn't open session: no active server.");const o=n?.trim();if(o){const n=E.useSessionStore.getState().sessions[r]?.workspaces,s=(0,b.resolveWorkspaceIdByExecutionDirectory)({workspaces:n?.values(),workspaceDirectory:o});if(s)return void(0,S.navigateToPreparedWorkspaceTab)({serverId:r,workspaceId:s,target:{kind:"agent",agentId:t},currentPathname:y,pin:!0})}(0,A.navigateToAgent)({serverId:r,agentId:t,currentPathname:y})},c[54]=fe,c[55]=y,c[56]=X,c[57]=Ue,c[58]=He):He=c[58];const Qe=He;let $e;c[59]!==fe||c[60]!==y||c[61]!==Ne||c[62]!==X||c[63]!==Ue?($e=t=>{Z.current=!0,(0,h.clearCommandCenterFocusRestoreElement)(),X(!1);const n=fe;Ne&&n?Ne.createAgent({config:{provider:"claude",cwd:t.cwd},resumeSessionId:t.sid}).then(t=>(0,A.navigateToAgent)({serverId:n,agentId:t.id,currentPathname:y})).catch(t=>{const n=t instanceof Error?t.message:String(t);Ue.error(`Couldn't open session: ${n}`)}):Ue.error("Couldn't open session: daemon not connected.")},c[59]=fe,c[60]=y,c[61]=Ne,c[62]=X,c[63]=Ue,c[64]=$e):$e=c[64];const ze=$e;let Ge;c[65]!==ze||c[66]!==Qe?(Ge=t=>{"open-agent"!==(0,P.recallSelectionKind)(t.source)?ze({sid:t.sid,title:null,lastActivity:t.timestamp,cwd:t.cwd,agentName:null}):Qe(t.sid,t.cwd)},c[65]=ze,c[66]=Qe,c[67]=Ge):Ge=c[67];const Je=Ge;let Ve;c[68]!==Be||c[69]!==xe||c[70]!==Je||c[71]!==ze?(Ve=t=>{"action"!==t.kind?"session"!==t.kind?"recallMatch"!==t.kind?xe(t.agent):Je(t.match):ze(t.session):Be(t.action)},c[68]=Be,c[69]=xe,c[70]=Je,c[71]=ze,c[72]=Ve):Ve=c[72];const Xe=Ve;let Ye,Ze,et,tt,nt,rt,ot,st,it,ct,ut,at,lt;c[73]!==ue?(Ye=()=>{te.current=ue},Ze=[ue],c[73]=ue,c[74]=Ye,c[75]=Ze):(Ye=c[74],Ze=c[75]);(0,n.useEffect)(Ye,Ze),c[76]!==Fe?(et=()=>{re.current=Fe},tt=[Fe],c[76]=Fe,c[77]=et,c[78]=tt):(et=c[77],tt=c[78]);(0,n.useEffect)(et,tt),c[79]!==Me?(nt=()=>{oe.current=Me},rt=[Me],c[79]=Me,c[80]=nt,c[81]=rt):(nt=c[80],rt=c[81]);(0,n.useEffect)(nt,rt),c[82]!==Xe?(ot=()=>{se.current=Xe},st=[Xe],c[82]=Xe,c[83]=ot,c[84]=st):(ot=c[83],st=c[84]);(0,n.useEffect)(ot,st),c[85]!==k?(it=()=>{const t=ee.current;if(ee.current=k,!k){if(ce(""),ae(0),t&&!Z.current&&L.isWeb){const t=(0,h.takeCommandCenterFocusRestoreElement)(),n=()=>Boolean(t)&&"undefined"!=typeof document&&document.activeElement===t;return(0,I.focusWithRetries)({focus:()=>t?.focus(),isFocused:n,onTimeout:H})}return}Z.current=!1;const n=setTimeout(()=>{Y.current?.focus()},0);return()=>clearTimeout(n)},ct=[k],c[85]=k,c[86]=it,c[87]=ct):(it=c[86],ct=c[87]);(0,n.useEffect)(it,ct),c[88]!==ue||c[89]!==Fe.length||c[90]!==k?(ut=()=>{k&&ue>=Fe.length&&ae(Fe.length>0?Fe.length-1:0)},at=[ue,Fe.length,k],c[88]=ue,c[89]=Fe.length,c[90]=k,c[91]=ut,c[92]=at):(ut=c[91],at=c[92]);(0,n.useEffect)(ut,at),c[93]!==k?(lt=t=>{if(!k)return!1;const n=re.current;if("Escape"===t)return oe.current(),!0;if("Enter"===t){if(0===n.length)return!1;const t=Math.max(0,Math.min(te.current,n.length-1));return se.current(n[t]),!0}return("ArrowDown"===t||"ArrowUp"===t)&&(0!==n.length&&(ae(r=>{const o=r+("ArrowDown"===t?1:-1);return o<0?n.length-1:o>=n.length?0:o}),!0))},c[93]=k,c[94]=lt):lt=c[94];const dt=lt;let ft,mt,gt;c[95]!==dt||c[96]!==k?(ft=()=>{if(!k||!L.isWeb)return;const t=t=>{"ArrowDown"!==t.key&&"ArrowUp"!==t.key&&"Enter"!==t.key&&"Escape"!==t.key||dt(t.key)&&t.preventDefault()};return window.addEventListener("keydown",t,!0),()=>window.removeEventListener("keydown",t,!0)},mt=[k,dt],c[95]=dt,c[96]=k,c[97]=ft,c[98]=mt):(ft=c[97],mt=c[98]);(0,n.useEffect)(ft,mt),c[99]!==ue||c[100]!==Me||c[101]!==dt||c[102]!==Xe||c[103]!==Fe||c[104]!==k||c[105]!==ie?(gt={open:k,inputRef:Y,query:ie,setQuery:ce,activeIndex:ue,setActiveIndex:ae,items:Fe,handleClose:Me,handleSelectItem:Xe,handleKeyEvent:dt},c[99]=ue,c[100]=Me,c[101]=dt,c[102]=Xe,c[103]=Fe,c[104]=k,c[105]=ie,c[106]=gt):gt=c[106];return gt};var t=_r(d[0]),n=_r(d[1]),r=_r(d[2]),o=_r(d[3]),s=_r(d[4]),c=_r(d[5]),u=_r(d[6]),a=_r(d[7]),l=_r(d[8]),f=_r(d[9]),h=_r(d[10]),p=_r(d[11]),w=_r(d[12]),y=_r(d[13]),v=_r(d[14]),C=_r(d[15]),k=_r(d[16]),A=_r(d[17]),S=_r(d[18]),b=_r(d[19]),E=_r(d[20]),I=_r(d[21]),K=_r(d[22]),R=_r(d[23]),L=_r(d[24]),T=_r(d[25]),P=_r(d[26]),_=_r(d[27]);const D=[],F=[],O=[],M=[],j=[];function x(t,n){if(!n)return!0;const r=n.toLowerCase(),o=(t.title??"New agent").toLowerCase(),s=t.cwd.toLowerCase();return o.includes(r)||s.includes(r)}function W(t,n){if(!n)return!0;const r=n.toLowerCase(),o=(t.title??"").toLowerCase(),s=(t.cwd??"").toLowerCase(),c=t.sid.toLowerCase();return o.includes(r)||s.includes(r)||c.includes(r)}function q(t,n){const r=(t.pendingPermissionCount??0)>0?1:0,o=(n.pendingPermissionCount??0)>0?1:0;if(r!==o)return o-r;const s=t.requiresAttention?1:0,c=n.requiresAttention?1:0;if(s!==c)return c-s;const u="running"===t.status?1:0,a="running"===n.status?1:0;return u!==a?a-u:n.lastActivityAt.getTime()-t.lastActivityAt.getTime()}const B=[{id:"new-agent",titleKey:"shell.commandCenter.openProject",icon:"plus",actionId:"new-agent",keywords:["open","project","folder","workspace","repo"],routeKind:"none"},{id:"home",titleKey:"shell.commandCenter.home",icon:"home",keywords:["home","start","import","session","pair","device","providers"],routeKind:"home"},{id:"settings",titleKey:"sidebar.actions.settings",icon:"settings",keywords:["settings","preferences","config","configuration"],routeKind:"settings"}];function N(t,n,r){const o=t.trim().toLowerCase();return!o||(!!r.toLowerCase().includes(o)||n.keywords.some(t=>t.includes(o)))}function U(t,n){if(!t)return;const r={isMac:"mac"===(0,C.getShortcutOs)(),isDesktop:(0,k.getIsElectronRuntime)()},o=(0,y.getBindingIdForAction)(t,r);if(!o)return;const s=n[o];if(s)return(0,w.chordStringToShortcutKeys)(s);const c=(0,y.getDefaultKeysForAction)(t,r);return c?[c]:void 0}function H(){c.keyboardActionDispatcher.dispatch({id:"message-input.focus",scope:"message-input"})}function Q(t){return{kind:"action",id:t.id,title:t.title,run:t.run}}function $(t,n){return t.lastActivity<n.lastActivity?1:t.lastActivity>n.lastActivity?-1:0}function z(){}function G(){}function J(t){return t.setCommandCenterOpen}function V(t){return t.commandCenterOpen}},3264,[349,36,1143,1573,3265,3269,3270,3446,3447,3452,3479,3477,3480,3481,3482,3483,3457,3484,3486,3495,3414,3496,3497,3271,3407,3499,3503,3504]);
|
|
14910
14910
|
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),Object.defineProperty(e,"useKeyboardShortcutsStore",{enumerable:!0,get:function(){return s}});var t=r(d[0]);let o=null;function n(t,n){const{altDown:s,cmdOrCtrlDown:c}=n(),u=s||c;o&&(clearTimeout(o),o=null),u?o=setTimeout(()=>{t({showShortcutBadges:!0})},150):t({showShortcutBadges:!1})}const s=(0,t.create)((t,o)=>({commandCenterOpen:!1,shortcutsDialogOpen:!1,capturingShortcut:!1,altDown:!1,cmdOrCtrlDown:!1,showShortcutBadges:!1,sidebarShortcutWorkspaceTargets:[],setCommandCenterOpen:o=>t({commandCenterOpen:o}),setShortcutsDialogOpen:o=>t({shortcutsDialogOpen:o}),setCapturingShortcut:o=>t({capturingShortcut:o}),setAltDown:s=>{t({altDown:s}),n(t,o)},setCmdOrCtrlDown:s=>{t({cmdOrCtrlDown:s}),n(t,o)},setSidebarShortcutWorkspaceTargets:o=>t({sidebarShortcutWorkspaceTargets:o}),resetModifiers:()=>{t({altDown:!1,cmdOrCtrlDown:!1}),n(t,o)}}))},3265,[3266]);
|
|
14911
14911
|
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0});var t=r(d[0]);Object.keys(t).forEach(function(n){'default'===n||Object.prototype.hasOwnProperty.call(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[n]}})});var n=r(d[1]);Object.keys(n).forEach(function(t){'default'===t||Object.prototype.hasOwnProperty.call(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:function(){return n[t]}})})},3266,[3267,3268]);
|
|
14912
14912
|
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),Object.defineProperty(e,"createStore",{enumerable:!0,get:function(){return n}});const t=t=>{let n;const c=new Set,o=(t,o)=>{const s="function"==typeof t?t(n):t;if(!Object.is(s,n)){const t=n;n=(null!=o?o:"object"!=typeof s||null===s)?s:Object.assign({},n,s),c.forEach(c=>c(n,t))}},s=()=>n,u={setState:o,getState:s,getInitialState:()=>l,subscribe:t=>(c.add(t),()=>c.delete(t))},l=n=t(o,s,u);return u},n=n=>n?t(n):t},3267,[]);
|
|
@@ -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.26",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-045767cf5362f85583f2649254d4471a.js.map
|
|
16543
|
+
//# debugId=810a9939-800e-468d-a63e-6ad656f9655c
|
|
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-045767cf5362f85583f2649254d4471a.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.26",
|
|
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.26",
|
|
69
|
+
"@hyperdrive.bot/paseo-extension-sdk": "0.3.26",
|
|
70
|
+
"@hyperdrive.bot/paseo-highlight": "0.3.26",
|
|
71
|
+
"@hyperdrive.bot/paseo-protocol": "0.3.26",
|
|
72
|
+
"@hyperdrive.bot/paseo-relay": "0.3.26",
|
|
73
73
|
"@isaacs/ttlcache": "^2.1.4",
|
|
74
74
|
"@modelcontextprotocol/sdk": "^1.20.1",
|
|
75
75
|
"@opencode-ai/sdk": "1.2.6",
|
|
Binary file
|