skydive-cli 0.1.0-beta.297 → 0.1.0-beta.314
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/README.md +29 -1
- package/dist/js/bin.mjs +32 -12
- package/dist/js/{boot-DYzw1U0y.mjs → boot-DEX6CwVM.mjs} +449 -55
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -209,7 +209,10 @@ This is off by default (default-deny) and you opt in explicitly:
|
|
|
209
209
|
|
|
210
210
|
- `skydive chat --share-machine` enables it at launch, or
|
|
211
211
|
- `ctrl+t` toggles it from the chat screen (the status bar shows `local access`
|
|
212
|
-
when on, `revoke access` when off)
|
|
212
|
+
when on, `revoke access` when off), or
|
|
213
|
+
- set `"shareMachineDefault": true` in the CLI config file to share on every
|
|
214
|
+
launch (see [Configuration](#configuration); `--no-share-machine` overrides
|
|
215
|
+
once).
|
|
213
216
|
|
|
214
217
|
What to know before enabling it:
|
|
215
218
|
|
|
@@ -234,6 +237,10 @@ What to know before enabling it:
|
|
|
234
237
|
sent, the target agent is granted access (the flag is the consent — there is
|
|
235
238
|
no prompt to approve), and the connection is torn down when the run ends.
|
|
236
239
|
For access that outlives a single run, use `skydive portal open` (below).
|
|
240
|
+
When sharing comes from `shareMachineDefault` rather than the explicit
|
|
241
|
+
flag, no new grant is minted — the machine connects and already-granted
|
|
242
|
+
agents can run, but an ungranted agent still needs `skydive portal grant`
|
|
243
|
+
(or an approved request).
|
|
237
244
|
|
|
238
245
|
### Headless machine access (`skydive portal`)
|
|
239
246
|
|
|
@@ -262,6 +269,27 @@ Two unattended patterns:
|
|
|
262
269
|
`skydive chat -p "<prompt>" --agent <name>` one-shots from another shell \u2014
|
|
263
270
|
cheaper when firing many prompts, since each one skips the connect/grant.
|
|
264
271
|
|
|
272
|
+
## Configuration
|
|
273
|
+
|
|
274
|
+
Persistent state lives in a per-user JSON file managed by
|
|
275
|
+
[`conf`](https://github.com/sindresorhus/conf):
|
|
276
|
+
`~/Library/Preferences/skydive/config.json` on macOS,
|
|
277
|
+
`~/.config/skydive/config.json` on Linux (profile name follows
|
|
278
|
+
`SKYDIVE_CONFIG_NAME`). Most keys are written by flows — `auth login`, the
|
|
279
|
+
device-login flow, the in-TUI theme picker — but the file is plain JSON and
|
|
280
|
+
read fresh on every invocation, so preference keys can be set by editing it
|
|
281
|
+
directly. There is deliberately no `skydive config` command.
|
|
282
|
+
|
|
283
|
+
Hand-editable keys:
|
|
284
|
+
|
|
285
|
+
| Key | Type | Effect |
|
|
286
|
+
| --------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
287
|
+
| `shareMachineDefault` | boolean | When `true`, `skydive chat` (TUI and `-p`) shares this machine over the portal on launch, as if `--share-machine` were passed. An explicit `--share-machine`/`--no-share-machine` overrides it per invocation. Default `false`. |
|
|
288
|
+
|
|
289
|
+
The remaining keys (`apiKey`, `apiUrl`, `sessionToken`, `appUrl`, `themeDark`,
|
|
290
|
+
`themeLight`, …) are credentials or flow-managed state — leave them to the
|
|
291
|
+
CLI.
|
|
292
|
+
|
|
265
293
|
## Environment variables
|
|
266
294
|
|
|
267
295
|
| Variable | Effect |
|
package/dist/js/bin.mjs
CHANGED
|
@@ -16,7 +16,7 @@ import zlib from "node:zlib";
|
|
|
16
16
|
import os from "node:os";
|
|
17
17
|
|
|
18
18
|
//#region package.json
|
|
19
|
-
var version$1 = "0.1.0-beta.
|
|
19
|
+
var version$1 = "0.1.0-beta.314";
|
|
20
20
|
|
|
21
21
|
//#endregion
|
|
22
22
|
//#region src/types.ts
|
|
@@ -157,6 +157,17 @@ function getSavedTheme(mode) {
|
|
|
157
157
|
function saveTheme(mode, themeId) {
|
|
158
158
|
store.set(mode === "dark" ? "themeDark" : "themeLight", themeId);
|
|
159
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* Whether `skydive chat` should enable portal machine sharing on launch
|
|
162
|
+
* without `--share-machine`. Set by hand-editing `shareMachineDefault` in
|
|
163
|
+
* config.json — deliberately no CLI command (kept off the API surface).
|
|
164
|
+
* Sharing only makes the machine reachable — agents still need a
|
|
165
|
+
* (persistent) grant to run anything, so this default skips the per-session
|
|
166
|
+
* enable step, not the consent step.
|
|
167
|
+
*/
|
|
168
|
+
function getShareMachineDefault() {
|
|
169
|
+
return store.get("shareMachineDefault") ?? false;
|
|
170
|
+
}
|
|
160
171
|
|
|
161
172
|
//#endregion
|
|
162
173
|
//#region src/api-client.ts
|
|
@@ -2562,8 +2573,7 @@ const chatCommand = {
|
|
|
2562
2573
|
describe: "For -p: continue an existing conversation by id instead of starting a new one."
|
|
2563
2574
|
}).option("share-machine", {
|
|
2564
2575
|
type: "boolean",
|
|
2565
|
-
|
|
2566
|
-
describe: "Share this machine with the agent over the portal so it can run commands here (default-deny; in the TUI you approve per agent, with -p the flag grants the target agent for the run)"
|
|
2576
|
+
describe: "Share this machine with the agent over the portal so it can run commands here (default-deny; in the TUI you approve per agent, with -p the flag grants the target agent for the run). Defaults to `shareMachineDefault` in the CLI config file; --no-share-machine disables for this invocation."
|
|
2567
2577
|
}).option("theme", {
|
|
2568
2578
|
type: "string",
|
|
2569
2579
|
describe: `Pin a colorscheme (disables automatic light/dark switching). One of: ${themes.map((t) => t.id).join(", ")}. Defaults to following the terminal background; also settable via SKYDIVE_THEME.`
|
|
@@ -2607,11 +2617,11 @@ const chatCommand = {
|
|
|
2607
2617
|
printError(`Unknown theme "${themeId}". Valid themes: ${themes.map((t) => t.id).join(", ")}`);
|
|
2608
2618
|
process.exit(1);
|
|
2609
2619
|
}
|
|
2610
|
-
const { runChat } = await import("./boot-
|
|
2620
|
+
const { runChat } = await import("./boot-DEX6CwVM.mjs");
|
|
2611
2621
|
await runChat({
|
|
2612
2622
|
appUrl,
|
|
2613
2623
|
sessionToken: session.value.sessionToken,
|
|
2614
|
-
shareMachine: argv
|
|
2624
|
+
shareMachine: resolveShareMachine(argv),
|
|
2615
2625
|
promptHistoryPath: getPromptHistoryPath(),
|
|
2616
2626
|
theme: themeId,
|
|
2617
2627
|
notifications: argv.notify,
|
|
@@ -2619,6 +2629,14 @@ const chatCommand = {
|
|
|
2619
2629
|
});
|
|
2620
2630
|
}
|
|
2621
2631
|
};
|
|
2632
|
+
/**
|
|
2633
|
+
* Whether this invocation shares the machine: an explicit
|
|
2634
|
+
* --share-machine/--no-share-machine wins; otherwise fall back to the
|
|
2635
|
+
* persisted `shareMachineDefault` config value.
|
|
2636
|
+
*/
|
|
2637
|
+
function resolveShareMachine(argv) {
|
|
2638
|
+
return argv["share-machine"] ?? getShareMachineDefault();
|
|
2639
|
+
}
|
|
2622
2640
|
async function runPrintMode({ argv, appUrl }) {
|
|
2623
2641
|
const session = resolveSession({ appUrl });
|
|
2624
2642
|
if (session.isErr()) {
|
|
@@ -2639,7 +2657,7 @@ async function runPrintMode({ argv, appUrl }) {
|
|
|
2639
2657
|
}
|
|
2640
2658
|
}
|
|
2641
2659
|
let machineShare = null;
|
|
2642
|
-
if (argv
|
|
2660
|
+
if (resolveShareMachine(argv)) {
|
|
2643
2661
|
const { PortalClient } = await import("./client-_OL8-XGH.mjs").then((n) => n.n);
|
|
2644
2662
|
let signalConnected;
|
|
2645
2663
|
const connected = new Promise((resolve) => {
|
|
@@ -2657,7 +2675,7 @@ async function runPrintMode({ argv, appUrl }) {
|
|
|
2657
2675
|
machineShare.enable();
|
|
2658
2676
|
if (await Promise.race([connected.then(() => false), new Promise((resolve) => setTimeout(() => resolve(true), 3e4).unref())])) {
|
|
2659
2677
|
machineShare.dispose();
|
|
2660
|
-
printError("Could not connect the portal within 30s — machine sharing is unavailable (network, or the portal kill switch is off). Re-run
|
|
2678
|
+
printError("Could not connect the portal within 30s — machine sharing is unavailable (network, or the portal kill switch is off). Re-run with --no-share-machine (or unset `shareMachineDefault` in the CLI config), or check `skydive portal status`.");
|
|
2661
2679
|
process.exit(1);
|
|
2662
2680
|
}
|
|
2663
2681
|
}
|
|
@@ -2669,7 +2687,8 @@ async function runPrintMode({ argv, appUrl }) {
|
|
|
2669
2687
|
agentSelector: argv.agent ?? null,
|
|
2670
2688
|
conversationId: argv.conversation ?? null,
|
|
2671
2689
|
json: argv.json,
|
|
2672
|
-
machineShare
|
|
2690
|
+
machineShare,
|
|
2691
|
+
grantTargetAgent: argv["share-machine"] === true
|
|
2673
2692
|
});
|
|
2674
2693
|
if (argv.json) output(argv, result);
|
|
2675
2694
|
} catch (error) {
|
|
@@ -3042,7 +3061,7 @@ var print_exports = /* @__PURE__ */ __exportAll({
|
|
|
3042
3061
|
* disambiguate: an `--agent` selector must match exactly one agent, and
|
|
3043
3062
|
* when it's omitted we only auto-pick if the account has exactly one.
|
|
3044
3063
|
*/
|
|
3045
|
-
async function runPrint({ appUrl, sessionToken, prompt, agentSelector, conversationId, json, machineShare }) {
|
|
3064
|
+
async function runPrint({ appUrl, sessionToken, prompt, agentSelector, conversationId, json, machineShare, grantTargetAgent }) {
|
|
3046
3065
|
const client = createRestClient({
|
|
3047
3066
|
appUrl,
|
|
3048
3067
|
sessionToken
|
|
@@ -3052,8 +3071,9 @@ async function runPrint({ appUrl, sessionToken, prompt, agentSelector, conversat
|
|
|
3052
3071
|
onPage: null
|
|
3053
3072
|
}), agentSelector);
|
|
3054
3073
|
if (machineShare) {
|
|
3055
|
-
if (!machineShare.isGranted(agent.id)) await machineShare.grantAgent(agent.id);
|
|
3056
|
-
console.error(`portal: shared this machine with ${agent.name} for this run (grant persists until revoked)`);
|
|
3074
|
+
if (!machineShare.isGranted(agent.id) && grantTargetAgent) await machineShare.grantAgent(agent.id);
|
|
3075
|
+
if (machineShare.isGranted(agent.id)) console.error(`portal: shared this machine with ${agent.name} for this run (grant persists until revoked)`);
|
|
3076
|
+
else console.error(`portal: this machine is shared (shareMachineDefault), but ${agent.name} has no grant — approve its request, or run \`skydive portal grant --agent ${agent.name}\`.`);
|
|
3057
3077
|
}
|
|
3058
3078
|
let send;
|
|
3059
3079
|
try {
|
|
@@ -3390,7 +3410,7 @@ const switchCommand = {
|
|
|
3390
3410
|
printError("The workspace picker needs the Bun runtime and it could not be set up automatically. Pass a workspace slug instead, or install Bun and retry.");
|
|
3391
3411
|
process.exit(1);
|
|
3392
3412
|
}
|
|
3393
|
-
const { runWorkspacePicker } = await import("./boot-
|
|
3413
|
+
const { runWorkspacePicker } = await import("./boot-DEX6CwVM.mjs");
|
|
3394
3414
|
await runWorkspacePicker(session);
|
|
3395
3415
|
return;
|
|
3396
3416
|
}
|
|
@@ -1096,7 +1096,8 @@ function AgentCreateScreen() {
|
|
|
1096
1096
|
agent,
|
|
1097
1097
|
conversation: {
|
|
1098
1098
|
kind: "new",
|
|
1099
|
-
agentId: agent.id
|
|
1099
|
+
agentId: agent.id,
|
|
1100
|
+
nonce: crypto.randomUUID()
|
|
1100
1101
|
}
|
|
1101
1102
|
});
|
|
1102
1103
|
} catch (err) {
|
|
@@ -1406,7 +1407,8 @@ function ConversationList({ agent, conversations, complete, onPick }) {
|
|
|
1406
1407
|
agent,
|
|
1407
1408
|
conversation: {
|
|
1408
1409
|
kind: "new",
|
|
1409
|
-
agentId: agent.id
|
|
1410
|
+
agentId: agent.id,
|
|
1411
|
+
nonce: crypto.randomUUID()
|
|
1410
1412
|
}
|
|
1411
1413
|
});
|
|
1412
1414
|
else onPick({
|
|
@@ -2905,6 +2907,19 @@ function RenderItem({ item }) {
|
|
|
2905
2907
|
barColor: null,
|
|
2906
2908
|
children: /* @__PURE__ */ jsx(CardItem, { item })
|
|
2907
2909
|
});
|
|
2910
|
+
case "info": return /* @__PURE__ */ jsx(Row, {
|
|
2911
|
+
barColor: null,
|
|
2912
|
+
children: /* @__PURE__ */ jsxs("box", {
|
|
2913
|
+
style: { flexDirection: "column" },
|
|
2914
|
+
children: [/* @__PURE__ */ jsx("text", {
|
|
2915
|
+
fg: theme.muted,
|
|
2916
|
+
children: /* @__PURE__ */ jsx("b", { children: item.title })
|
|
2917
|
+
}), /* @__PURE__ */ jsx("text", {
|
|
2918
|
+
fg: theme.muted,
|
|
2919
|
+
children: item.text
|
|
2920
|
+
})]
|
|
2921
|
+
})
|
|
2922
|
+
});
|
|
2908
2923
|
case "recap": return /* @__PURE__ */ jsx(Row, {
|
|
2909
2924
|
barColor: null,
|
|
2910
2925
|
children: /* @__PURE__ */ jsxs("text", {
|
|
@@ -3454,7 +3469,7 @@ function mapToolState(raw) {
|
|
|
3454
3469
|
function formatChatFooterStatus({ runKind, ctrlCArmed, isShared, hasPendingSteer, hasActionableCard }) {
|
|
3455
3470
|
const connectHint = hasActionableCard ? "ctrl+r connect · " : "";
|
|
3456
3471
|
switch (runKind) {
|
|
3457
|
-
case "idle": return ctrlCArmed ? "press ctrl+c again to quit" : `${connectHint}↵ send · esc back · ctrl+c quit · ctrl+t ${isShared ? "revoke access" : "local access"} · ? keybinds`;
|
|
3472
|
+
case "idle": return ctrlCArmed ? "press ctrl+c again to quit" : `${connectHint}↵ send · / commands · esc back · ctrl+c quit · ctrl+t ${isShared ? "revoke access" : "local access"} · ? keybinds`;
|
|
3458
3473
|
case "sending": return "sending…";
|
|
3459
3474
|
case "streaming": return hasPendingSteer ? `${connectHint}↵ steer · esc cancel steer · ctrl+c cancel run` : `${connectHint}↵ steer · ctrl+c cancel`;
|
|
3460
3475
|
}
|
|
@@ -3464,6 +3479,31 @@ function formatModelHint(model) {
|
|
|
3464
3479
|
return `ctrl+p · ${model ?? "default"}`;
|
|
3465
3480
|
}
|
|
3466
3481
|
|
|
3482
|
+
//#endregion
|
|
3483
|
+
//#region src/chat/tui/chat/status-info.ts
|
|
3484
|
+
/**
|
|
3485
|
+
* Body text for the `/status` transcript block: the session facts the status
|
|
3486
|
+
* bar and footer can't fit, one aligned `label value` row each. Pure so the
|
|
3487
|
+
* layout is testable without a renderer.
|
|
3488
|
+
*/
|
|
3489
|
+
function formatStatusInfo({ appUrl, workspaceName, agentName, conversationId, model, share }) {
|
|
3490
|
+
const rows = [
|
|
3491
|
+
["app", appUrl ?? "unknown"],
|
|
3492
|
+
["workspace", workspaceName ?? "unknown"],
|
|
3493
|
+
["agent", agentName],
|
|
3494
|
+
["conversation", conversationId ?? "new — created when you send the first message"],
|
|
3495
|
+
["model", model ?? "default"],
|
|
3496
|
+
["local access", formatShare(share)]
|
|
3497
|
+
];
|
|
3498
|
+
const width = Math.max(...rows.map(([label]) => label.length));
|
|
3499
|
+
return rows.map(([label, value]) => `${label.padEnd(width)} ${value}`).join("\n");
|
|
3500
|
+
}
|
|
3501
|
+
function formatShare(share) {
|
|
3502
|
+
if (share.status === "off") return "off — ctrl+t to share this machine";
|
|
3503
|
+
if (share.status !== "connected") return share.status;
|
|
3504
|
+
return share.grantedToAgent ? "shared — this agent can run commands here" : "shared — this agent is not granted";
|
|
3505
|
+
}
|
|
3506
|
+
|
|
3467
3507
|
//#endregion
|
|
3468
3508
|
//#region src/chat/notification-preview.ts
|
|
3469
3509
|
const MAX_NOTIFICATION_PREVIEW_CHARS = 240;
|
|
@@ -3750,12 +3790,37 @@ const keybindGroups = [
|
|
|
3750
3790
|
keys: "pgup / pgdn",
|
|
3751
3791
|
action: "scroll transcript"
|
|
3752
3792
|
},
|
|
3793
|
+
{
|
|
3794
|
+
keys: "/",
|
|
3795
|
+
action: "open the command menu (start of message)"
|
|
3796
|
+
},
|
|
3753
3797
|
{
|
|
3754
3798
|
keys: "?",
|
|
3755
3799
|
action: "open this help (composer empty)"
|
|
3756
3800
|
}
|
|
3757
3801
|
]
|
|
3758
3802
|
},
|
|
3803
|
+
{
|
|
3804
|
+
title: "command menu (type / to open)",
|
|
3805
|
+
binds: [
|
|
3806
|
+
{
|
|
3807
|
+
keys: "↑ / ↓",
|
|
3808
|
+
action: "move selection"
|
|
3809
|
+
},
|
|
3810
|
+
{
|
|
3811
|
+
keys: "tab",
|
|
3812
|
+
action: "complete the command"
|
|
3813
|
+
},
|
|
3814
|
+
{
|
|
3815
|
+
keys: "↵",
|
|
3816
|
+
action: "run (or complete) the selected command"
|
|
3817
|
+
},
|
|
3818
|
+
{
|
|
3819
|
+
keys: "esc",
|
|
3820
|
+
action: "dismiss the menu"
|
|
3821
|
+
}
|
|
3822
|
+
]
|
|
3823
|
+
},
|
|
3759
3824
|
{
|
|
3760
3825
|
title: "agent picker",
|
|
3761
3826
|
binds: [
|
|
@@ -4006,6 +4071,182 @@ function HelpModal({ onClose }) {
|
|
|
4006
4071
|
});
|
|
4007
4072
|
}
|
|
4008
4073
|
|
|
4074
|
+
//#endregion
|
|
4075
|
+
//#region src/chat/tui/autocomplete/completion-menu.tsx
|
|
4076
|
+
const MAX_COMPLETION_ROWS = 6;
|
|
4077
|
+
/**
|
|
4078
|
+
* Rows the menu occupies in the chat screen's fixed layout budget: the
|
|
4079
|
+
* windowed items (min 1 for the empty row) plus the border. The chat screen
|
|
4080
|
+
* subtracts this from the transcript height, mirroring the composer and
|
|
4081
|
+
* staged-attachment panels.
|
|
4082
|
+
*/
|
|
4083
|
+
function completionMenuHeight(itemCount) {
|
|
4084
|
+
return Math.min(Math.max(itemCount, 1), MAX_COMPLETION_ROWS) + 2;
|
|
4085
|
+
}
|
|
4086
|
+
/**
|
|
4087
|
+
* The inline autocomplete dropdown, rendered directly above the composer
|
|
4088
|
+
* (never as a screen overlay — the composer must stay mounted and focused).
|
|
4089
|
+
* Pure presentation: the chat screen owns the open/highlight state and the
|
|
4090
|
+
* keyboard handling.
|
|
4091
|
+
*/
|
|
4092
|
+
function CompletionMenu({ items, highlight }) {
|
|
4093
|
+
const start = windowStart(highlight, items.length, MAX_COMPLETION_ROWS);
|
|
4094
|
+
const windowed = items.slice(start, start + MAX_COMPLETION_ROWS);
|
|
4095
|
+
return /* @__PURE__ */ jsx("box", {
|
|
4096
|
+
style: {
|
|
4097
|
+
border: true,
|
|
4098
|
+
borderColor: theme.faint,
|
|
4099
|
+
paddingLeft: 1,
|
|
4100
|
+
paddingRight: 1,
|
|
4101
|
+
flexShrink: 0,
|
|
4102
|
+
marginTop: 1,
|
|
4103
|
+
flexDirection: "column"
|
|
4104
|
+
},
|
|
4105
|
+
children: windowed.length === 0 ? /* @__PURE__ */ jsx("text", {
|
|
4106
|
+
fg: theme.muted,
|
|
4107
|
+
children: "no matching command — ↵ sends as a message"
|
|
4108
|
+
}) : windowed.map((item, i) => {
|
|
4109
|
+
const selected = start + i === highlight;
|
|
4110
|
+
return /* @__PURE__ */ jsxs("text", {
|
|
4111
|
+
fg: selected ? theme.accent : theme.fg,
|
|
4112
|
+
children: [
|
|
4113
|
+
selected ? "› " : " ",
|
|
4114
|
+
item.label,
|
|
4115
|
+
item.hint ? /* @__PURE__ */ jsx("span", {
|
|
4116
|
+
fg: theme.dim,
|
|
4117
|
+
children: ` ${item.hint}`
|
|
4118
|
+
}) : null,
|
|
4119
|
+
/* @__PURE__ */ jsx("span", {
|
|
4120
|
+
fg: theme.muted,
|
|
4121
|
+
children: ` ${item.description}`
|
|
4122
|
+
})
|
|
4123
|
+
]
|
|
4124
|
+
}, item.id);
|
|
4125
|
+
})
|
|
4126
|
+
});
|
|
4127
|
+
}
|
|
4128
|
+
|
|
4129
|
+
//#endregion
|
|
4130
|
+
//#region src/chat/slash-commands.ts
|
|
4131
|
+
const slashCommands = [
|
|
4132
|
+
{
|
|
4133
|
+
name: "sandbox",
|
|
4134
|
+
aliases: ["sbx"],
|
|
4135
|
+
argsHint: "[command]",
|
|
4136
|
+
description: "run in the agent's sandbox (no command: open a terminal)",
|
|
4137
|
+
action: { kind: "sandbox" }
|
|
4138
|
+
},
|
|
4139
|
+
{
|
|
4140
|
+
name: "new",
|
|
4141
|
+
aliases: [],
|
|
4142
|
+
argsHint: null,
|
|
4143
|
+
description: "start a new conversation with this agent",
|
|
4144
|
+
action: { kind: "new-conversation" }
|
|
4145
|
+
},
|
|
4146
|
+
{
|
|
4147
|
+
name: "model",
|
|
4148
|
+
aliases: [],
|
|
4149
|
+
argsHint: null,
|
|
4150
|
+
description: "switch the model for the next run",
|
|
4151
|
+
action: { kind: "model-picker" }
|
|
4152
|
+
},
|
|
4153
|
+
{
|
|
4154
|
+
name: "theme",
|
|
4155
|
+
aliases: [],
|
|
4156
|
+
argsHint: null,
|
|
4157
|
+
description: "switch the color theme",
|
|
4158
|
+
action: { kind: "theme-picker" }
|
|
4159
|
+
},
|
|
4160
|
+
{
|
|
4161
|
+
name: "status",
|
|
4162
|
+
aliases: [],
|
|
4163
|
+
argsHint: null,
|
|
4164
|
+
description: "show connection and session status",
|
|
4165
|
+
action: { kind: "status" }
|
|
4166
|
+
},
|
|
4167
|
+
{
|
|
4168
|
+
name: "share",
|
|
4169
|
+
aliases: [],
|
|
4170
|
+
argsHint: null,
|
|
4171
|
+
description: "local access: grant / revoke this machine",
|
|
4172
|
+
action: { kind: "share" }
|
|
4173
|
+
},
|
|
4174
|
+
{
|
|
4175
|
+
name: "browser",
|
|
4176
|
+
aliases: [],
|
|
4177
|
+
argsHint: null,
|
|
4178
|
+
description: "open this conversation in the browser",
|
|
4179
|
+
action: { kind: "browser" }
|
|
4180
|
+
},
|
|
4181
|
+
{
|
|
4182
|
+
name: "connect",
|
|
4183
|
+
aliases: [],
|
|
4184
|
+
argsHint: null,
|
|
4185
|
+
description: "run the newest connect card (auth cards)",
|
|
4186
|
+
action: { kind: "connect" }
|
|
4187
|
+
},
|
|
4188
|
+
{
|
|
4189
|
+
name: "help",
|
|
4190
|
+
aliases: [],
|
|
4191
|
+
argsHint: null,
|
|
4192
|
+
description: "show keybinds and commands",
|
|
4193
|
+
action: { kind: "help" }
|
|
4194
|
+
},
|
|
4195
|
+
{
|
|
4196
|
+
name: "exit",
|
|
4197
|
+
aliases: ["quit"],
|
|
4198
|
+
argsHint: null,
|
|
4199
|
+
description: "quit the chat",
|
|
4200
|
+
action: { kind: "quit" }
|
|
4201
|
+
}
|
|
4202
|
+
];
|
|
4203
|
+
/**
|
|
4204
|
+
* Match a submitted line against the registry: `/<name>` or `/<name> <rest>`,
|
|
4205
|
+
* case-insensitive on the name. Returns the command plus the trimmed rest,
|
|
4206
|
+
* or null when the line isn't a known slash command.
|
|
4207
|
+
*
|
|
4208
|
+
* An argument-less command only matches as the exact line — `/help me write
|
|
4209
|
+
* a plan` stays an ordinary message rather than swallowing the user's text
|
|
4210
|
+
* into the help modal. Only `sandbox` accepts a rest.
|
|
4211
|
+
*/
|
|
4212
|
+
function matchSlashCommand(line) {
|
|
4213
|
+
if (!line.startsWith("/")) return null;
|
|
4214
|
+
const sp = line.indexOf(" ");
|
|
4215
|
+
const name = (sp === -1 ? line.slice(1) : line.slice(1, sp)).toLowerCase();
|
|
4216
|
+
const command = slashCommands.find((c) => c.name === name || c.aliases.includes(name));
|
|
4217
|
+
if (!command) return null;
|
|
4218
|
+
const rest = sp === -1 ? "" : line.slice(sp + 1).trim();
|
|
4219
|
+
if (command.argsHint === null && rest !== "") return null;
|
|
4220
|
+
return {
|
|
4221
|
+
command,
|
|
4222
|
+
rest
|
|
4223
|
+
};
|
|
4224
|
+
}
|
|
4225
|
+
/**
|
|
4226
|
+
* The in-progress command token the completion menu should open for: the
|
|
4227
|
+
* composer text is a lone `/<partial>` with no whitespace yet (whitespace
|
|
4228
|
+
* means the name is settled and the user is typing arguments — or prose).
|
|
4229
|
+
* Returns the partial name ('' right after `/`), or null when no menu
|
|
4230
|
+
* should show.
|
|
4231
|
+
*/
|
|
4232
|
+
function detectCommandTrigger(text) {
|
|
4233
|
+
if (!text.startsWith("/")) return null;
|
|
4234
|
+
if (/\s/.test(text)) return null;
|
|
4235
|
+
return text.slice(1);
|
|
4236
|
+
}
|
|
4237
|
+
/**
|
|
4238
|
+
* Menu ordering: prefix matches (registry order) ahead of substring matches,
|
|
4239
|
+
* checked against names and aliases. Empty query lists everything.
|
|
4240
|
+
*/
|
|
4241
|
+
function filterSlashCommands(query) {
|
|
4242
|
+
const q = query.trim().toLowerCase();
|
|
4243
|
+
if (!q) return [...slashCommands];
|
|
4244
|
+
const names = (c) => [c.name, ...c.aliases];
|
|
4245
|
+
const prefix = slashCommands.filter((c) => names(c).some((n) => n.startsWith(q)));
|
|
4246
|
+
const substring = slashCommands.filter((c) => !prefix.includes(c) && names(c).some((n) => n.includes(q)));
|
|
4247
|
+
return [...prefix, ...substring];
|
|
4248
|
+
}
|
|
4249
|
+
|
|
4009
4250
|
//#endregion
|
|
4010
4251
|
//#region src/chat/tui/screens/model-picker.tsx
|
|
4011
4252
|
/**
|
|
@@ -4832,28 +5073,35 @@ function routeInput(raw) {
|
|
|
4832
5073
|
command
|
|
4833
5074
|
} : { kind: "noop" };
|
|
4834
5075
|
}
|
|
4835
|
-
const
|
|
4836
|
-
if (
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
5076
|
+
const match = matchSlashCommand(line);
|
|
5077
|
+
if (match) {
|
|
5078
|
+
const { action } = match.command;
|
|
5079
|
+
switch (action.kind) {
|
|
5080
|
+
case "sandbox": return match.rest ? {
|
|
5081
|
+
kind: "sandbox-exec",
|
|
5082
|
+
command: match.rest
|
|
5083
|
+
} : { kind: "sandbox-pty" };
|
|
5084
|
+
case "new-conversation":
|
|
5085
|
+
case "model-picker":
|
|
5086
|
+
case "theme-picker":
|
|
5087
|
+
case "status":
|
|
5088
|
+
case "share":
|
|
5089
|
+
case "browser":
|
|
5090
|
+
case "connect":
|
|
5091
|
+
case "help":
|
|
5092
|
+
case "quit": return { kind: action.kind };
|
|
5093
|
+
default: {
|
|
5094
|
+
const exhaustive = action;
|
|
5095
|
+
throw new Error(`unhandled slash action: ${JSON.stringify(exhaustive)}`);
|
|
5096
|
+
}
|
|
5097
|
+
}
|
|
5098
|
+
}
|
|
4840
5099
|
if (line.trim().toLowerCase() === "exit") return { kind: "quit" };
|
|
4841
5100
|
return {
|
|
4842
5101
|
kind: "message",
|
|
4843
5102
|
content: raw
|
|
4844
5103
|
};
|
|
4845
5104
|
}
|
|
4846
|
-
/**
|
|
4847
|
-
* If `line` is `/<name>` or `/<name> <rest>` for one of `names`, return the
|
|
4848
|
-
* trimmed rest (possibly ''); otherwise null. Case-insensitive on the name.
|
|
4849
|
-
*/
|
|
4850
|
-
function matchSlash(line, names) {
|
|
4851
|
-
if (!line.startsWith("/")) return null;
|
|
4852
|
-
const sp = line.indexOf(" ");
|
|
4853
|
-
const name = (sp === -1 ? line.slice(1) : line.slice(1, sp)).toLowerCase();
|
|
4854
|
-
if (!names.includes(name)) return null;
|
|
4855
|
-
return sp === -1 ? "" : line.slice(sp + 1).trim();
|
|
4856
|
-
}
|
|
4857
5105
|
/** Max characters of `!` output we forward to the agent as context. */
|
|
4858
5106
|
const SHELL_CONTEXT_OUTPUT_CAP = 16e3;
|
|
4859
5107
|
/**
|
|
@@ -5139,6 +5387,7 @@ function ChatScreen({ agent, conversation }) {
|
|
|
5139
5387
|
const sessionToken = useStore((s) => s.sessionToken);
|
|
5140
5388
|
const goTo = useStore((s) => s.goTo);
|
|
5141
5389
|
const setChatTitle = useStore((s) => s.setChatTitle);
|
|
5390
|
+
const workspaceName = useStore((s) => s.workspaceName);
|
|
5142
5391
|
const portal = useStore((s) => s.portal);
|
|
5143
5392
|
const portalClient = useStore((s) => s.portalClient);
|
|
5144
5393
|
const setPortalPrompt = useStore((s) => s.setPortalPrompt);
|
|
@@ -5162,6 +5411,12 @@ function ChatScreen({ agent, conversation }) {
|
|
|
5162
5411
|
const uploadsRef = useRef(/* @__PURE__ */ new Map());
|
|
5163
5412
|
const [credPrompt, setCredPrompt] = useState(null);
|
|
5164
5413
|
const credPromptOpen = credPrompt !== null;
|
|
5414
|
+
const [menuDismissed, setMenuDismissed] = useState(false);
|
|
5415
|
+
const [menuHighlight, setMenuHighlight] = useState(0);
|
|
5416
|
+
const commandQuery = grantPrompt || credPrompt ? null : detectCommandTrigger(input);
|
|
5417
|
+
const menuCommands = commandQuery === null ? [] : filterSlashCommands(commandQuery);
|
|
5418
|
+
const menuOpen = commandQuery !== null && !menuDismissed;
|
|
5419
|
+
const menuHighlightClamped = Math.min(menuHighlight, Math.max(0, menuCommands.length - 1));
|
|
5165
5420
|
const runRef = useRef(run);
|
|
5166
5421
|
runRef.current = run;
|
|
5167
5422
|
const itemsRef = useRef(items);
|
|
@@ -5191,7 +5446,8 @@ function ChatScreen({ agent, conversation }) {
|
|
|
5191
5446
|
const pendingRows = pendingVisible ? attachments.length + 2 : 0;
|
|
5192
5447
|
const credPromptHeight = 4 + (credPrompt && (credPrompt.error || credPrompt.submitting) ? 1 : 0);
|
|
5193
5448
|
const bottomBoxHeight = credPrompt ? credPromptHeight : composerBoxHeight;
|
|
5194
|
-
const
|
|
5449
|
+
const menuRows = menuOpen ? completionMenuHeight(menuCommands.length) : 0;
|
|
5450
|
+
const scrollHeight = Math.max(3, height - 5 - bottomBoxHeight - pendingRows - menuRows);
|
|
5195
5451
|
useEffect(() => {
|
|
5196
5452
|
const composer = composerRef.current;
|
|
5197
5453
|
if (!composer) return;
|
|
@@ -5524,6 +5780,12 @@ function ChatScreen({ agent, conversation }) {
|
|
|
5524
5780
|
portalClient,
|
|
5525
5781
|
portal.status
|
|
5526
5782
|
]);
|
|
5783
|
+
const runNewestCard = useCallback(() => {
|
|
5784
|
+
const target = [...itemsRef.current].reverse().find(isActionableCard);
|
|
5785
|
+
if (!target) return false;
|
|
5786
|
+
runCardAction(target);
|
|
5787
|
+
return true;
|
|
5788
|
+
}, [runCardAction]);
|
|
5527
5789
|
const handleCredInput = useCallback((displayed) => {
|
|
5528
5790
|
setCredPrompt((prev) => {
|
|
5529
5791
|
if (!prev || prev.submitting) return prev;
|
|
@@ -5700,42 +5962,12 @@ function ChatScreen({ agent, conversation }) {
|
|
|
5700
5962
|
agentHost,
|
|
5701
5963
|
renderer
|
|
5702
5964
|
]);
|
|
5703
|
-
const submit = useCallback(() => {
|
|
5704
|
-
const content = input.trim();
|
|
5705
|
-
const staged = attachments.filter((a) => a.status !== "error");
|
|
5706
|
-
if (!content && staged.length === 0) return;
|
|
5707
|
-
const routed = routeInput(content);
|
|
5708
|
-
if (routed.kind !== "message") {
|
|
5709
|
-
history.append(content);
|
|
5710
|
-
setInput("");
|
|
5711
|
-
composerRef.current?.clear();
|
|
5712
|
-
setComposerRows(1);
|
|
5713
|
-
if (routed.kind === "local-shell") runLocalShell(routed.command);
|
|
5714
|
-
else if (routed.kind === "sandbox-exec") runSandboxExec(routed.command);
|
|
5715
|
-
else if (routed.kind === "sandbox-pty") runSandboxPty();
|
|
5716
|
-
else if (routed.kind === "quit") quit();
|
|
5717
|
-
return;
|
|
5718
|
-
}
|
|
5719
|
-
history.append(content);
|
|
5720
|
-
setInput("");
|
|
5721
|
-
composerRef.current?.clear();
|
|
5722
|
-
setComposerRows(1);
|
|
5723
|
-
setAttachments([]);
|
|
5724
|
-
sendContent(content, staged);
|
|
5725
|
-
}, [
|
|
5726
|
-
input,
|
|
5727
|
-
attachments,
|
|
5728
|
-
sendContent,
|
|
5729
|
-
history,
|
|
5730
|
-
runLocalShell,
|
|
5731
|
-
runSandboxExec,
|
|
5732
|
-
runSandboxPty,
|
|
5733
|
-
quit
|
|
5734
|
-
]);
|
|
5735
5965
|
const handleComposerChange = useCallback(() => {
|
|
5736
5966
|
const composer = composerRef.current;
|
|
5737
5967
|
if (!composer) return;
|
|
5738
5968
|
setInput(composer.plainText);
|
|
5969
|
+
setMenuHighlight(0);
|
|
5970
|
+
if (detectCommandTrigger(composer.plainText) === null) setMenuDismissed(false);
|
|
5739
5971
|
setComposerRows(Math.min(Math.max(composer.editorView.getTotalVirtualLineCount(), 1), maxComposerRows));
|
|
5740
5972
|
}, []);
|
|
5741
5973
|
/**
|
|
@@ -5915,6 +6147,126 @@ function ChatScreen({ agent, conversation }) {
|
|
|
5915
6147
|
setPortalPrompt(null);
|
|
5916
6148
|
useStore.getState().showToast({ message: "kept this machine private" });
|
|
5917
6149
|
}, [agent.id, setPortalPrompt]);
|
|
6150
|
+
const executeLine = useCallback((content, staged) => {
|
|
6151
|
+
if (!content && staged.length === 0) return;
|
|
6152
|
+
const routed = routeInput(content);
|
|
6153
|
+
if (routed.kind !== "message") {
|
|
6154
|
+
history.append(content);
|
|
6155
|
+
setInput("");
|
|
6156
|
+
composerRef.current?.clear();
|
|
6157
|
+
setComposerRows(1);
|
|
6158
|
+
switch (routed.kind) {
|
|
6159
|
+
case "local-shell":
|
|
6160
|
+
runLocalShell(routed.command);
|
|
6161
|
+
break;
|
|
6162
|
+
case "sandbox-exec":
|
|
6163
|
+
runSandboxExec(routed.command);
|
|
6164
|
+
break;
|
|
6165
|
+
case "sandbox-pty":
|
|
6166
|
+
runSandboxPty();
|
|
6167
|
+
break;
|
|
6168
|
+
case "new-conversation":
|
|
6169
|
+
if (runRef.current.kind !== "idle") {
|
|
6170
|
+
useStore.getState().showToast({ message: "finish or cancel the run first (ctrl+c)" });
|
|
6171
|
+
break;
|
|
6172
|
+
}
|
|
6173
|
+
goTo({
|
|
6174
|
+
kind: "chat",
|
|
6175
|
+
agent,
|
|
6176
|
+
conversation: {
|
|
6177
|
+
kind: "new",
|
|
6178
|
+
agentId: agent.id,
|
|
6179
|
+
nonce: crypto.randomUUID()
|
|
6180
|
+
}
|
|
6181
|
+
});
|
|
6182
|
+
break;
|
|
6183
|
+
case "model-picker":
|
|
6184
|
+
setModelPickerOpen(true);
|
|
6185
|
+
break;
|
|
6186
|
+
case "theme-picker":
|
|
6187
|
+
setThemePickerOpen(true);
|
|
6188
|
+
break;
|
|
6189
|
+
case "status":
|
|
6190
|
+
setItems((prev) => [...prev, {
|
|
6191
|
+
kind: "info",
|
|
6192
|
+
id: crypto.randomUUID(),
|
|
6193
|
+
title: "status",
|
|
6194
|
+
text: formatStatusInfo({
|
|
6195
|
+
appUrl,
|
|
6196
|
+
workspaceName,
|
|
6197
|
+
agentName: agent.name,
|
|
6198
|
+
conversationId,
|
|
6199
|
+
model,
|
|
6200
|
+
share: {
|
|
6201
|
+
status: portal.status,
|
|
6202
|
+
grantedToAgent: portal.grantedAgentIds.includes(agent.id)
|
|
6203
|
+
}
|
|
6204
|
+
})
|
|
6205
|
+
}]);
|
|
6206
|
+
break;
|
|
6207
|
+
case "share":
|
|
6208
|
+
toggleShare();
|
|
6209
|
+
break;
|
|
6210
|
+
case "browser":
|
|
6211
|
+
openInBrowser();
|
|
6212
|
+
break;
|
|
6213
|
+
case "connect":
|
|
6214
|
+
if (!runNewestCard()) useStore.getState().showToast({ message: "no connect card waiting for action" });
|
|
6215
|
+
break;
|
|
6216
|
+
case "help":
|
|
6217
|
+
setHelpOpen(true);
|
|
6218
|
+
break;
|
|
6219
|
+
case "quit":
|
|
6220
|
+
quit();
|
|
6221
|
+
break;
|
|
6222
|
+
case "noop": break;
|
|
6223
|
+
default: {
|
|
6224
|
+
const exhaustive = routed;
|
|
6225
|
+
throw new Error(`unhandled route: ${JSON.stringify(exhaustive)}`);
|
|
6226
|
+
}
|
|
6227
|
+
}
|
|
6228
|
+
return;
|
|
6229
|
+
}
|
|
6230
|
+
history.append(content);
|
|
6231
|
+
setInput("");
|
|
6232
|
+
composerRef.current?.clear();
|
|
6233
|
+
setComposerRows(1);
|
|
6234
|
+
setAttachments([]);
|
|
6235
|
+
sendContent(content, staged);
|
|
6236
|
+
}, [
|
|
6237
|
+
history,
|
|
6238
|
+
runLocalShell,
|
|
6239
|
+
runSandboxExec,
|
|
6240
|
+
runSandboxPty,
|
|
6241
|
+
toggleShare,
|
|
6242
|
+
openInBrowser,
|
|
6243
|
+
quit,
|
|
6244
|
+
sendContent,
|
|
6245
|
+
goTo,
|
|
6246
|
+
agent,
|
|
6247
|
+
appUrl,
|
|
6248
|
+
workspaceName,
|
|
6249
|
+
conversationId,
|
|
6250
|
+
model,
|
|
6251
|
+
portal.status,
|
|
6252
|
+
portal.grantedAgentIds,
|
|
6253
|
+
runNewestCard
|
|
6254
|
+
]);
|
|
6255
|
+
const submit = useCallback(() => {
|
|
6256
|
+
const staged = attachments.filter((a) => a.status !== "error");
|
|
6257
|
+
executeLine(input.trim(), staged);
|
|
6258
|
+
}, [
|
|
6259
|
+
executeLine,
|
|
6260
|
+
input,
|
|
6261
|
+
attachments
|
|
6262
|
+
]);
|
|
6263
|
+
const acceptCompletion = useCallback((command, execute) => {
|
|
6264
|
+
if (execute && command.argsHint === null) {
|
|
6265
|
+
executeLine(`/${command.name}`, []);
|
|
6266
|
+
return;
|
|
6267
|
+
}
|
|
6268
|
+
applyComposerText(`/${command.name} `, "end");
|
|
6269
|
+
}, [executeLine, applyComposerText]);
|
|
5918
6270
|
useEffect(() => {
|
|
5919
6271
|
if (!portalClient || portal.status !== "connected") return;
|
|
5920
6272
|
if (portal.grantedAgentIds.includes(agent.id)) return;
|
|
@@ -5960,6 +6312,38 @@ function ChatScreen({ agent, conversation }) {
|
|
|
5960
6312
|
}
|
|
5961
6313
|
if (!(key.name === "c" && key.ctrl || key.name === "pageup" || key.name === "pgup" || key.name === "pagedown" || key.name === "pgdn")) return;
|
|
5962
6314
|
}
|
|
6315
|
+
if (menuOpen) {
|
|
6316
|
+
if (key.name === "escape") {
|
|
6317
|
+
setMenuDismissed(true);
|
|
6318
|
+
return;
|
|
6319
|
+
}
|
|
6320
|
+
const selected = menuCommands[menuHighlightClamped];
|
|
6321
|
+
if (selected) {
|
|
6322
|
+
if (key.name === "up") {
|
|
6323
|
+
setMenuHighlight((h) => Math.max(0, Math.min(h, menuCommands.length - 1) - 1));
|
|
6324
|
+
key.preventDefault();
|
|
6325
|
+
return;
|
|
6326
|
+
}
|
|
6327
|
+
if (key.name === "down") {
|
|
6328
|
+
setMenuHighlight((h) => Math.min(menuCommands.length - 1, h + 1));
|
|
6329
|
+
key.preventDefault();
|
|
6330
|
+
return;
|
|
6331
|
+
}
|
|
6332
|
+
if (key.name === "tab") {
|
|
6333
|
+
acceptCompletion(selected, false);
|
|
6334
|
+
key.preventDefault();
|
|
6335
|
+
return;
|
|
6336
|
+
}
|
|
6337
|
+
if ((key.name === "return" || key.name === "kpenter") && !key.shift) {
|
|
6338
|
+
const query = (commandQuery ?? "").toLowerCase();
|
|
6339
|
+
if (!(query === selected.name || selected.aliases.includes(query)) || selected.argsHint === null) {
|
|
6340
|
+
acceptCompletion(selected, true);
|
|
6341
|
+
key.preventDefault();
|
|
6342
|
+
return;
|
|
6343
|
+
}
|
|
6344
|
+
}
|
|
6345
|
+
}
|
|
6346
|
+
}
|
|
5963
6347
|
const composer = composerRef.current;
|
|
5964
6348
|
if (composer && !grantPrompt && !credPrompt && (key.name === "up" || key.name === "down")) {
|
|
5965
6349
|
if (key.name === "up") {
|
|
@@ -5973,6 +6357,7 @@ function ChatScreen({ agent, conversation }) {
|
|
|
5973
6357
|
const text = history.previous(composer.plainText);
|
|
5974
6358
|
if (text === null) return;
|
|
5975
6359
|
applyComposerText(text, "start");
|
|
6360
|
+
if (detectCommandTrigger(text) !== null) setMenuDismissed(true);
|
|
5976
6361
|
key.preventDefault();
|
|
5977
6362
|
return;
|
|
5978
6363
|
}
|
|
@@ -5987,6 +6372,7 @@ function ChatScreen({ agent, conversation }) {
|
|
|
5987
6372
|
const text = history.next(composer.plainText);
|
|
5988
6373
|
if (text === null) return;
|
|
5989
6374
|
applyComposerText(text, "end");
|
|
6375
|
+
if (detectCommandTrigger(text) !== null) setMenuDismissed(true);
|
|
5990
6376
|
key.preventDefault();
|
|
5991
6377
|
return;
|
|
5992
6378
|
}
|
|
@@ -5995,8 +6381,7 @@ function ChatScreen({ agent, conversation }) {
|
|
|
5995
6381
|
return;
|
|
5996
6382
|
}
|
|
5997
6383
|
if (key.name === "r" && key.ctrl) {
|
|
5998
|
-
|
|
5999
|
-
if (target) runCardAction(target);
|
|
6384
|
+
runNewestCard();
|
|
6000
6385
|
return;
|
|
6001
6386
|
}
|
|
6002
6387
|
if (key.name === "t" && key.ctrl) {
|
|
@@ -6161,6 +6546,15 @@ function ChatScreen({ agent, conversation }) {
|
|
|
6161
6546
|
children: "ctrl+x remove last"
|
|
6162
6547
|
})]
|
|
6163
6548
|
}) : null,
|
|
6549
|
+
menuOpen ? /* @__PURE__ */ jsx(CompletionMenu, {
|
|
6550
|
+
items: menuCommands.map((c) => ({
|
|
6551
|
+
id: c.name,
|
|
6552
|
+
label: `/${c.name}`,
|
|
6553
|
+
hint: c.argsHint,
|
|
6554
|
+
description: c.description
|
|
6555
|
+
})),
|
|
6556
|
+
highlight: menuHighlightClamped
|
|
6557
|
+
}) : null,
|
|
6164
6558
|
credPrompt ? /* @__PURE__ */ jsx(CredentialPromptBox, {
|
|
6165
6559
|
prompt: credPrompt,
|
|
6166
6560
|
height: credPromptHeight,
|
|
@@ -6531,7 +6925,7 @@ function App({ appUrl, sessionToken, shareMachine, promptHistoryPath, notificati
|
|
|
6531
6925
|
screen.kind === "chat" && rest && /* @__PURE__ */ jsx(ChatScreen, {
|
|
6532
6926
|
agent: screen.agent,
|
|
6533
6927
|
conversation: screen.conversation
|
|
6534
|
-
})
|
|
6928
|
+
}, "id" in screen.conversation ? screen.conversation.id : screen.conversation.nonce)
|
|
6535
6929
|
]
|
|
6536
6930
|
})
|
|
6537
6931
|
]
|