agent-relay-server 0.11.9 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/public/index.html +379 -40
- package/runner/src/adapter.ts +30 -0
- package/src/routes.ts +10 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-relay-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "Lightweight HTTP message relay for inter-agent communication across machines",
|
|
5
5
|
"module": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"CONTRIBUTING.md"
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"agent-relay-sdk": "0.2.
|
|
35
|
+
"agent-relay-sdk": "0.2.6"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"prepack": "bun run build:dashboard >&2",
|
package/public/index.html
CHANGED
|
@@ -10591,6 +10591,7 @@ function isAgentStale(now, agent) {
|
|
|
10591
10591
|
function agentSupportsControlAction(agent, action) {
|
|
10592
10592
|
if (!agent || isBuiltInAgent(agent) || isChannelAgent(agent)) return false;
|
|
10593
10593
|
if (action === "resume") return false;
|
|
10594
|
+
if (action === "interrupt") return agent.providerCapabilities?.liveSession?.interrupt === true && agent.status === "busy";
|
|
10594
10595
|
if (agentType(agent) === "claude" && (action === "restart" || action === "shutdown" || action === "compact" || action === "clearContext") && !agentHasTmuxSession(agent)) return false;
|
|
10595
10596
|
const lifecycle = agent.providerCapabilities?.lifecycle;
|
|
10596
10597
|
if (lifecycle) {
|
|
@@ -124029,18 +124030,18 @@ function useFileListing(orchestratorId, selectedPath, git = false) {
|
|
|
124029
124030
|
}
|
|
124030
124031
|
function RawTextContent({ content, line, lineRef }) {
|
|
124031
124032
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
124032
|
-
className: "
|
|
124033
|
+
className: "w-full py-2 font-mono text-xs leading-5",
|
|
124033
124034
|
children: content.split("\n").map((text, index) => {
|
|
124034
124035
|
const lineNumber = index + 1;
|
|
124035
124036
|
const active = lineNumber === line;
|
|
124036
124037
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
124037
124038
|
ref: active ? lineRef : void 0,
|
|
124038
|
-
className: `grid grid-cols-[4rem_minmax(0,1fr)] px-2 ${active ? "bg-primary/15" : ""}`,
|
|
124039
|
+
className: `grid grid-cols-[2.5rem_minmax(0,1fr)] px-1 sm:grid-cols-[4rem_minmax(0,1fr)] sm:px-2 ${active ? "bg-primary/15" : ""}`,
|
|
124039
124040
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
124040
|
-
className: "select-none pr-
|
|
124041
|
+
className: "select-none pr-2 text-right text-muted-foreground sm:pr-3",
|
|
124041
124042
|
children: lineNumber
|
|
124042
124043
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", {
|
|
124043
|
-
className: "overflow-
|
|
124044
|
+
className: "overflow-hidden whitespace-pre-wrap break-words",
|
|
124044
124045
|
children: text || " "
|
|
124045
124046
|
})]
|
|
124046
124047
|
}, lineNumber);
|
|
@@ -124374,7 +124375,7 @@ function FileBrowser({ overlay = false }) {
|
|
|
124374
124375
|
className: overlay ? "flex h-full flex-col" : "flex h-[calc(100dvh-5rem)] flex-col gap-3 md:gap-4",
|
|
124375
124376
|
children: [
|
|
124376
124377
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
124377
|
-
className:
|
|
124378
|
+
className: `flex flex-wrap items-center gap-2 ${selectedPath ? "hidden md:flex" : ""}`,
|
|
124378
124379
|
children: [
|
|
124379
124380
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
124380
124381
|
className: "flex items-center gap-2 text-lg font-semibold",
|
|
@@ -124403,7 +124404,7 @@ function FileBrowser({ overlay = false }) {
|
|
|
124403
124404
|
]
|
|
124404
124405
|
}),
|
|
124405
124406
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
124406
|
-
className:
|
|
124407
|
+
className: `flex flex-wrap items-center gap-2 ${selectedPath ? "hidden md:flex" : ""}`,
|
|
124407
124408
|
children: [
|
|
124408
124409
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input, {
|
|
124409
124410
|
value: pathDraft,
|
|
@@ -124461,7 +124462,7 @@ function FileBrowser({ overlay = false }) {
|
|
|
124461
124462
|
]
|
|
124462
124463
|
}),
|
|
124463
124464
|
segments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
124464
|
-
className:
|
|
124465
|
+
className: `flex flex-wrap items-center gap-1 text-xs text-muted-foreground ${selectedPath ? "hidden md:flex" : ""}`,
|
|
124465
124466
|
children: segments.map((segment, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
|
|
124466
124467
|
type: "button",
|
|
124467
124468
|
className: "rounded px-1.5 py-0.5 font-mono hover:bg-accent hover:text-foreground",
|
|
@@ -124478,7 +124479,7 @@ function FileBrowser({ overlay = false }) {
|
|
|
124478
124479
|
children: visibleError
|
|
124479
124480
|
}),
|
|
124480
124481
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
124481
|
-
className:
|
|
124482
|
+
className: `grid min-h-0 flex-1 overflow-hidden md:rounded-md md:border md:border-border md:grid-cols-[22rem_minmax(0,1fr)] ${selectedPath ? "" : "rounded-md border border-border"}`,
|
|
124482
124483
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
124483
124484
|
className: `min-h-0 overflow-auto border-b border-border bg-card md:border-b-0 md:border-r ${selectedPath ? "hidden md:block" : ""}`,
|
|
124484
124485
|
children: [
|
|
@@ -124567,6 +124568,101 @@ function Textarea({ className, ...props }) {
|
|
|
124567
124568
|
});
|
|
124568
124569
|
}
|
|
124569
124570
|
//#endregion
|
|
124571
|
+
//#region src/lib/slash-commands.ts
|
|
124572
|
+
var RELAY_SLASH_COMMANDS = [
|
|
124573
|
+
{
|
|
124574
|
+
name: "/reply",
|
|
124575
|
+
source: "skill",
|
|
124576
|
+
description: "Reply to a relay message by id",
|
|
124577
|
+
argumentHint: "<messageId> <body>"
|
|
124578
|
+
},
|
|
124579
|
+
{
|
|
124580
|
+
name: "/message",
|
|
124581
|
+
source: "skill",
|
|
124582
|
+
description: "Send a relay message to an agent, label, tag, or broadcast",
|
|
124583
|
+
argumentHint: "<target> <body>"
|
|
124584
|
+
},
|
|
124585
|
+
{
|
|
124586
|
+
name: "/pair",
|
|
124587
|
+
source: "skill",
|
|
124588
|
+
description: "Start or manage a two-agent pair session"
|
|
124589
|
+
},
|
|
124590
|
+
{
|
|
124591
|
+
name: "/react",
|
|
124592
|
+
source: "skill",
|
|
124593
|
+
description: "Add a reaction to a relay message",
|
|
124594
|
+
argumentHint: "<messageId> <emoji>"
|
|
124595
|
+
},
|
|
124596
|
+
{
|
|
124597
|
+
name: "/read-message",
|
|
124598
|
+
source: "skill",
|
|
124599
|
+
description: "Fetch a full relay message by id",
|
|
124600
|
+
argumentHint: "<messageId>"
|
|
124601
|
+
},
|
|
124602
|
+
{
|
|
124603
|
+
name: "/send-claimable",
|
|
124604
|
+
source: "skill",
|
|
124605
|
+
description: "Enqueue a claimable work item for one agent to claim"
|
|
124606
|
+
},
|
|
124607
|
+
{
|
|
124608
|
+
name: "/status",
|
|
124609
|
+
source: "skill",
|
|
124610
|
+
description: "Show this agent's relay status"
|
|
124611
|
+
},
|
|
124612
|
+
{
|
|
124613
|
+
name: "/tags",
|
|
124614
|
+
source: "skill",
|
|
124615
|
+
description: "List or update this agent's relay tags"
|
|
124616
|
+
},
|
|
124617
|
+
{
|
|
124618
|
+
name: "/label",
|
|
124619
|
+
source: "skill",
|
|
124620
|
+
description: "Read, set, or clear this agent's relay label"
|
|
124621
|
+
},
|
|
124622
|
+
{
|
|
124623
|
+
name: "/disconnect",
|
|
124624
|
+
source: "skill",
|
|
124625
|
+
description: "End the current relay pair session"
|
|
124626
|
+
}
|
|
124627
|
+
];
|
|
124628
|
+
var CLAUDE_BUILTIN_SLASH_COMMANDS = [
|
|
124629
|
+
{
|
|
124630
|
+
name: "/clear",
|
|
124631
|
+
source: "builtin",
|
|
124632
|
+
description: "Clear conversation history and free up context"
|
|
124633
|
+
},
|
|
124634
|
+
{
|
|
124635
|
+
name: "/compact",
|
|
124636
|
+
source: "builtin",
|
|
124637
|
+
description: "Compact the conversation to reclaim context",
|
|
124638
|
+
argumentHint: "[instructions]"
|
|
124639
|
+
},
|
|
124640
|
+
{
|
|
124641
|
+
name: "/model",
|
|
124642
|
+
source: "builtin",
|
|
124643
|
+
description: "Switch the active model",
|
|
124644
|
+
argumentHint: "[model]"
|
|
124645
|
+
},
|
|
124646
|
+
{
|
|
124647
|
+
name: "/help",
|
|
124648
|
+
source: "builtin",
|
|
124649
|
+
description: "Show help"
|
|
124650
|
+
}
|
|
124651
|
+
];
|
|
124652
|
+
var CODEX_BUILTIN_SLASH_COMMANDS = [{
|
|
124653
|
+
name: "/compact",
|
|
124654
|
+
source: "builtin",
|
|
124655
|
+
description: "Compact the conversation to reclaim context"
|
|
124656
|
+
}, {
|
|
124657
|
+
name: "/new",
|
|
124658
|
+
source: "builtin",
|
|
124659
|
+
description: "Start a new thread"
|
|
124660
|
+
}];
|
|
124661
|
+
/** Slash-command catalog for a provider's chat-input autocomplete palette. */
|
|
124662
|
+
function providerSlashCommands(provider) {
|
|
124663
|
+
return [...provider === "claude" ? CLAUDE_BUILTIN_SLASH_COMMANDS : provider === "codex" ? CODEX_BUILTIN_SLASH_COMMANDS : [], ...RELAY_SLASH_COMMANDS];
|
|
124664
|
+
}
|
|
124665
|
+
//#endregion
|
|
124570
124666
|
//#region src/components/views/chat.tsx
|
|
124571
124667
|
function formatBytes(size) {
|
|
124572
124668
|
if (size < 1024) return `${size} B`;
|
|
@@ -125163,7 +125259,7 @@ function StatusMarker({ event }) {
|
|
|
125163
125259
|
]
|
|
125164
125260
|
});
|
|
125165
125261
|
}
|
|
125166
|
-
function BusyIndicator({ blockedLabel }) {
|
|
125262
|
+
function BusyIndicator({ blockedLabel, onInterrupt }) {
|
|
125167
125263
|
if (blockedLabel) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
125168
125264
|
className: "flex justify-start mb-3",
|
|
125169
125265
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
@@ -125178,16 +125274,78 @@ function BusyIndicator({ blockedLabel }) {
|
|
|
125178
125274
|
className: "flex justify-start mb-3",
|
|
125179
125275
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125180
125276
|
className: "flex items-center gap-2 rounded-2xl rounded-bl-sm bg-card ring-1 ring-foreground/10 px-4 py-2.5",
|
|
125181
|
-
children: [
|
|
125182
|
-
|
|
125277
|
+
children: [
|
|
125278
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125279
|
+
className: "flex items-center gap-1",
|
|
125280
|
+
children: [
|
|
125281
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "w-1.5 h-1.5 rounded-full bg-yellow-400 animate-[pulse_1.4s_ease-in-out_infinite]" }),
|
|
125282
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "w-1.5 h-1.5 rounded-full bg-yellow-400 animate-[pulse_1.4s_ease-in-out_0.2s_infinite]" }),
|
|
125283
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "w-1.5 h-1.5 rounded-full bg-yellow-400 animate-[pulse_1.4s_ease-in-out_0.4s_infinite]" })
|
|
125284
|
+
]
|
|
125285
|
+
}),
|
|
125286
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
125287
|
+
className: "text-xs text-muted-foreground",
|
|
125288
|
+
children: "working"
|
|
125289
|
+
}),
|
|
125290
|
+
onInterrupt && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
|
|
125291
|
+
onClick: onInterrupt,
|
|
125292
|
+
title: "Interrupt",
|
|
125293
|
+
className: "ml-1 flex items-center gap-1 rounded-full px-1.5 py-0.5 text-xs text-muted-foreground hover:text-red-300 hover:bg-red-500/10 transition-colors",
|
|
125294
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CircleStop, { className: "w-3.5 h-3.5" }), "Stop"]
|
|
125295
|
+
})
|
|
125296
|
+
]
|
|
125297
|
+
})
|
|
125298
|
+
});
|
|
125299
|
+
}
|
|
125300
|
+
function ActivityTrace({ steps }) {
|
|
125301
|
+
const [expanded, setExpanded] = (0, import_react.useState)(false);
|
|
125302
|
+
const last = steps[steps.length - 1];
|
|
125303
|
+
if (!last) return null;
|
|
125304
|
+
const summary = (last.label || last.text || "").replace(/\s+/g, " ").trim();
|
|
125305
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
125306
|
+
className: "flex justify-start mb-2",
|
|
125307
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125308
|
+
className: "max-w-[85%] md:max-w-[75%] min-w-0",
|
|
125309
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
|
|
125310
|
+
onClick: () => setExpanded((v) => !v),
|
|
125311
|
+
className: "flex items-center gap-1.5 text-xs text-muted-foreground/70 hover:text-muted-foreground transition-colors w-full text-left",
|
|
125183
125312
|
children: [
|
|
125184
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
125185
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
125186
|
-
|
|
125313
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Brain, { className: "w-3.5 h-3.5 shrink-0" }),
|
|
125314
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
125315
|
+
className: "truncate",
|
|
125316
|
+
children: expanded ? "Activity" : summary.length > 80 ? `${summary.slice(0, 79)}…` : summary || "thinking"
|
|
125317
|
+
}),
|
|
125318
|
+
steps.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
125319
|
+
className: "opacity-60 shrink-0",
|
|
125320
|
+
children: [
|
|
125321
|
+
"· ",
|
|
125322
|
+
steps.length,
|
|
125323
|
+
" steps"
|
|
125324
|
+
]
|
|
125325
|
+
}),
|
|
125326
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronRight, { className: cn$2("w-3 h-3 shrink-0 transition-transform", expanded && "rotate-90") })
|
|
125187
125327
|
]
|
|
125188
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("
|
|
125189
|
-
className: "
|
|
125190
|
-
children: "
|
|
125328
|
+
}), expanded && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
125329
|
+
className: "mt-1.5 space-y-1.5 border-l border-foreground/10 pl-2.5",
|
|
125330
|
+
children: steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
125331
|
+
className: "text-xs leading-relaxed",
|
|
125332
|
+
children: step.kind === "reasoning" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
125333
|
+
className: "italic text-muted-foreground/80 whitespace-pre-wrap break-words",
|
|
125334
|
+
children: step.text
|
|
125335
|
+
}) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
125336
|
+
className: "text-muted-foreground flex items-start gap-1.5",
|
|
125337
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Terminal, { className: "w-3 h-3 mt-0.5 shrink-0 opacity-70" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
125338
|
+
className: "min-w-0 break-words",
|
|
125339
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
125340
|
+
className: "font-medium",
|
|
125341
|
+
children: step.label || "tool"
|
|
125342
|
+
}), step.text ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
125343
|
+
className: "opacity-70",
|
|
125344
|
+
children: [" — ", step.text]
|
|
125345
|
+
}) : null]
|
|
125346
|
+
})]
|
|
125347
|
+
})
|
|
125348
|
+
}, step.id))
|
|
125191
125349
|
})]
|
|
125192
125350
|
})
|
|
125193
125351
|
});
|
|
@@ -125218,23 +125376,31 @@ function PermissionRequestBubble({ agentId, approval }) {
|
|
|
125218
125376
|
setSubmitting(null);
|
|
125219
125377
|
}
|
|
125220
125378
|
}
|
|
125379
|
+
const isPlan = approval.kind === "plan";
|
|
125221
125380
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
125222
125381
|
className: "flex justify-start mb-3",
|
|
125223
125382
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
125224
125383
|
className: "max-w-[92%] md:max-w-[78%] rounded-2xl rounded-bl-sm bg-card ring-1 ring-amber-500/35 px-3.5 py-3 text-sm",
|
|
125225
125384
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125226
125385
|
className: "flex items-start gap-2",
|
|
125227
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CircleAlert, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-400" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125386
|
+
children: [isPlan ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ListChecks, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-400" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CircleAlert, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-400" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
125228
125387
|
className: "min-w-0 flex-1",
|
|
125229
125388
|
children: [
|
|
125230
125389
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
125231
125390
|
className: "text-sm font-medium text-amber-100",
|
|
125232
125391
|
children: approval.title
|
|
125233
125392
|
}),
|
|
125234
|
-
approval.body && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("
|
|
125393
|
+
approval.body && (isPlan ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
125394
|
+
className: "mt-2 max-h-72 overflow-auto rounded-md bg-background/60 p-2.5 text-xs leading-relaxed",
|
|
125395
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FormattedBody, {
|
|
125396
|
+
text: approval.body,
|
|
125397
|
+
rawBody: approval.body,
|
|
125398
|
+
className: "leading-relaxed"
|
|
125399
|
+
})
|
|
125400
|
+
}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", {
|
|
125235
125401
|
className: "mt-2 max-h-44 overflow-auto whitespace-pre-wrap break-words rounded-md bg-background/80 p-2 font-mono text-xs leading-relaxed text-muted-foreground",
|
|
125236
125402
|
children: approval.body
|
|
125237
|
-
}),
|
|
125403
|
+
})),
|
|
125238
125404
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
125239
125405
|
className: "mt-2 flex flex-wrap gap-1.5",
|
|
125240
125406
|
children: choices.map((choice) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
|
|
@@ -125936,6 +126102,19 @@ var MessageBubble = (0, import_react.memo)(function MessageBubble({ msg, peer, o
|
|
|
125936
126102
|
})
|
|
125937
126103
|
});
|
|
125938
126104
|
});
|
|
126105
|
+
function sessionActivityStep(msg) {
|
|
126106
|
+
if (msg.kind !== "session") return null;
|
|
126107
|
+
const s = msg.payload?.session;
|
|
126108
|
+
if (!s || s.type !== "reasoning" && s.type !== "tool") return null;
|
|
126109
|
+
return {
|
|
126110
|
+
id: msg.id,
|
|
126111
|
+
kind: s.type,
|
|
126112
|
+
label: typeof s.label === "string" ? s.label : void 0,
|
|
126113
|
+
text: msg.body,
|
|
126114
|
+
ts: new Date(msg.createdAt).getTime(),
|
|
126115
|
+
turnId: typeof s.turnId === "string" ? s.turnId : void 0
|
|
126116
|
+
};
|
|
126117
|
+
}
|
|
125939
126118
|
function dateKey(ts) {
|
|
125940
126119
|
const d = new Date(ts);
|
|
125941
126120
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
@@ -126013,6 +126192,18 @@ function buildTimeline(messages, statusEvents, createdAt, importedHistory = [])
|
|
|
126013
126192
|
}
|
|
126014
126193
|
for (const msg of messages) {
|
|
126015
126194
|
if (isReactionEvent(msg)) continue;
|
|
126195
|
+
const step = sessionActivityStep(msg);
|
|
126196
|
+
if (step) {
|
|
126197
|
+
raw.push({
|
|
126198
|
+
ts: step.ts,
|
|
126199
|
+
entry: {
|
|
126200
|
+
type: "activity",
|
|
126201
|
+
steps: [step],
|
|
126202
|
+
timestamp: step.ts
|
|
126203
|
+
}
|
|
126204
|
+
});
|
|
126205
|
+
continue;
|
|
126206
|
+
}
|
|
126016
126207
|
raw.push({
|
|
126017
126208
|
ts: new Date(msg.createdAt).getTime(),
|
|
126018
126209
|
entry: {
|
|
@@ -126034,6 +126225,7 @@ function buildTimeline(messages, statusEvents, createdAt, importedHistory = [])
|
|
|
126034
126225
|
"import-boundary": 2,
|
|
126035
126226
|
"imported-message": 3,
|
|
126036
126227
|
message: 4,
|
|
126228
|
+
activity: 4,
|
|
126037
126229
|
status: 5
|
|
126038
126230
|
};
|
|
126039
126231
|
raw.sort((a, b) => {
|
|
@@ -126057,10 +126249,23 @@ function buildTimeline(messages, statusEvents, createdAt, importedHistory = [])
|
|
|
126057
126249
|
});
|
|
126058
126250
|
lastDate = dk;
|
|
126059
126251
|
}
|
|
126252
|
+
if (entry.type === "activity") {
|
|
126253
|
+
const prev = entries[entries.length - 1];
|
|
126254
|
+
if (prev && prev.type === "activity" && sameActivityTurn(prev, entry)) {
|
|
126255
|
+
prev.steps.push(...entry.steps);
|
|
126256
|
+
continue;
|
|
126257
|
+
}
|
|
126258
|
+
}
|
|
126060
126259
|
entries.push(entry);
|
|
126061
126260
|
}
|
|
126062
126261
|
return entries;
|
|
126063
126262
|
}
|
|
126263
|
+
function sameActivityTurn(a, b) {
|
|
126264
|
+
const at = a.steps[a.steps.length - 1]?.turnId;
|
|
126265
|
+
const bt = b.steps[0]?.turnId;
|
|
126266
|
+
if (at && bt) return at === bt;
|
|
126267
|
+
return !at && !bt;
|
|
126268
|
+
}
|
|
126064
126269
|
function ChatPanel({ threads, onBack, showBackButton }) {
|
|
126065
126270
|
const selectedInboxThread = useRelayStore((s) => s.selectedInboxThread);
|
|
126066
126271
|
const agentsById = useRelayStore((s) => s.agentsById);
|
|
@@ -126131,6 +126336,25 @@ function ChatPanel({ threads, onBack, showBackButton }) {
|
|
|
126131
126336
|
const canCompact = agentSupportsControlAction(agent, "compact");
|
|
126132
126337
|
const canClearContext = agentSupportsControlAction(agent, "clearContext");
|
|
126133
126338
|
const canShutdown = agentSupportsControlAction(agent, "shutdown");
|
|
126339
|
+
const canInterrupt = agentSupportsControlAction(agent, "interrupt");
|
|
126340
|
+
const interruptAgent = () => {
|
|
126341
|
+
if (agent) doAgentAction(agent, "interrupt");
|
|
126342
|
+
};
|
|
126343
|
+
const [slashIndex, setSlashIndex] = (0, import_react.useState)(0);
|
|
126344
|
+
const [slashClosed, setSlashClosed] = (0, import_react.useState)(false);
|
|
126345
|
+
const slashEnabled = agent?.providerCapabilities?.liveSession?.slashCommands === true;
|
|
126346
|
+
const slashProvider = agent?.providerCapabilities?.model?.provider ?? (typeof agent?.meta?.provider === "string" ? agent.meta.provider : void 0);
|
|
126347
|
+
const slashMatches = slashEnabled && !slashClosed && /^\/\S*$/.test(draft) ? providerSlashCommands(slashProvider).filter((c) => c.name.toLowerCase().startsWith(draft.toLowerCase())) : [];
|
|
126348
|
+
const slashActiveIndex = slashMatches.length ? Math.min(slashIndex, slashMatches.length - 1) : 0;
|
|
126349
|
+
function acceptSlash(cmd) {
|
|
126350
|
+
setReplyDraft(selectedInboxThread, `${cmd.name} `);
|
|
126351
|
+
setSlashIndex(0);
|
|
126352
|
+
}
|
|
126353
|
+
function handleDraftChange(value) {
|
|
126354
|
+
setReplyDraft(selectedInboxThread, value);
|
|
126355
|
+
if (slashClosed) setSlashClosed(false);
|
|
126356
|
+
setSlashIndex(0);
|
|
126357
|
+
}
|
|
126134
126358
|
const hasPendingUploads = pendingAttachments.some((item) => item.uploading);
|
|
126135
126359
|
const readyAttachments = pendingAttachments.filter((item) => item.artifact);
|
|
126136
126360
|
useBackNavProtection(draft.trim().length > 0 || pendingAttachments.length > 0, (0, import_react.useCallback)(() => {}, []));
|
|
@@ -126320,6 +126544,29 @@ function ChatPanel({ threads, onBack, showBackButton }) {
|
|
|
126320
126544
|
clearPendingAttachments();
|
|
126321
126545
|
}
|
|
126322
126546
|
function handleKeyDown(e) {
|
|
126547
|
+
if (slashMatches.length) {
|
|
126548
|
+
if (e.key === "ArrowDown") {
|
|
126549
|
+
e.preventDefault();
|
|
126550
|
+
setSlashIndex((i) => Math.min(i + 1, slashMatches.length - 1));
|
|
126551
|
+
return;
|
|
126552
|
+
}
|
|
126553
|
+
if (e.key === "ArrowUp") {
|
|
126554
|
+
e.preventDefault();
|
|
126555
|
+
setSlashIndex((i) => Math.max(i - 1, 0));
|
|
126556
|
+
return;
|
|
126557
|
+
}
|
|
126558
|
+
if (e.key === "Escape") {
|
|
126559
|
+
e.preventDefault();
|
|
126560
|
+
setSlashClosed(true);
|
|
126561
|
+
return;
|
|
126562
|
+
}
|
|
126563
|
+
if (e.key === "Enter" || e.key === "Tab") {
|
|
126564
|
+
e.preventDefault();
|
|
126565
|
+
const cmd = slashMatches[slashActiveIndex];
|
|
126566
|
+
if (cmd) acceptSlash(cmd);
|
|
126567
|
+
return;
|
|
126568
|
+
}
|
|
126569
|
+
}
|
|
126323
126570
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
126324
126571
|
e.preventDefault();
|
|
126325
126572
|
handleSend();
|
|
@@ -126406,6 +126653,14 @@ function ChatPanel({ threads, onBack, showBackButton }) {
|
|
|
126406
126653
|
onClick: () => void handleOpenTerminal(),
|
|
126407
126654
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Terminal, { className: "w-3.5 h-3.5" })
|
|
126408
126655
|
}),
|
|
126656
|
+
canInterrupt && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
|
|
126657
|
+
variant: "ghost",
|
|
126658
|
+
size: "icon-sm",
|
|
126659
|
+
className: "text-red-400 hover:text-red-300",
|
|
126660
|
+
title: "Interrupt",
|
|
126661
|
+
onClick: interruptAgent,
|
|
126662
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CircleStop, { className: "w-3.5 h-3.5" })
|
|
126663
|
+
}),
|
|
126409
126664
|
canCompact && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
|
|
126410
126665
|
variant: "ghost",
|
|
126411
126666
|
size: "icon-sm",
|
|
@@ -126489,6 +126744,10 @@ function ChatPanel({ threads, onBack, showBackButton }) {
|
|
|
126489
126744
|
onClick: () => void handleOpenTerminal(),
|
|
126490
126745
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Terminal, { className: "w-3.5 h-3.5" }), "Terminal"]
|
|
126491
126746
|
}),
|
|
126747
|
+
canInterrupt && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenuItem, {
|
|
126748
|
+
onClick: interruptAgent,
|
|
126749
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CircleStop, { className: "w-3.5 h-3.5" }), "Interrupt"]
|
|
126750
|
+
}),
|
|
126492
126751
|
canCompact && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenuItem, {
|
|
126493
126752
|
onClick: () => openConfirm("Compact Agent", `Compact context for ${displayName(agent)}?`, () => doAgentAction(agent, "compact")),
|
|
126494
126753
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Minimize2, { className: "w-3.5 h-3.5" }), "Compact"]
|
|
@@ -126550,6 +126809,7 @@ function ChatPanel({ threads, onBack, showBackButton }) {
|
|
|
126550
126809
|
onPreviewReferencedPath: previewReferencedFile,
|
|
126551
126810
|
onPreviewReferencedPathEnd: scheduleCloseReferencedFilePreview
|
|
126552
126811
|
}, entry.msg.id);
|
|
126812
|
+
if (entry.type === "activity") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ActivityTrace, { steps: entry.steps }, `act-${entry.steps[0]?.id ?? entry.timestamp}`);
|
|
126553
126813
|
if (entry.type === "import-boundary") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ImportedHistoryMarker, { history: entry.history }, `import-${entry.history.id}`);
|
|
126554
126814
|
if (entry.type === "imported-message") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ImportedMessageBubble, {
|
|
126555
126815
|
entry: entry.entry,
|
|
@@ -126564,11 +126824,14 @@ function ChatPanel({ threads, onBack, showBackButton }) {
|
|
|
126564
126824
|
agentId: agent.id,
|
|
126565
126825
|
approval: pendingApproval
|
|
126566
126826
|
}),
|
|
126567
|
-
agent?.status === "busy" && !pendingApproval && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BusyIndicator, {
|
|
126827
|
+
agent?.status === "busy" && !pendingApproval && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BusyIndicator, {
|
|
126828
|
+
blockedLabel: blockedState?.label,
|
|
126829
|
+
onInterrupt: canInterrupt ? interruptAgent : void 0
|
|
126830
|
+
})
|
|
126568
126831
|
] })
|
|
126569
126832
|
}),
|
|
126570
126833
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
126571
|
-
className: cn$2("shrink-0 border-t border-border px-3 md:px-4 py-2.5 md:py-3 safe-area-bottom", dragActive && "bg-primary/5 ring-1 ring-primary/40"),
|
|
126834
|
+
className: cn$2("relative shrink-0 border-t border-border px-3 md:px-4 py-2.5 md:py-3 safe-area-bottom", dragActive && "bg-primary/5 ring-1 ring-primary/40"),
|
|
126572
126835
|
onDragOver: (e) => {
|
|
126573
126836
|
e.preventDefault();
|
|
126574
126837
|
setDragActive(true);
|
|
@@ -126597,6 +126860,31 @@ function ChatPanel({ threads, onBack, showBackButton }) {
|
|
|
126597
126860
|
e.currentTarget.value = "";
|
|
126598
126861
|
}
|
|
126599
126862
|
}),
|
|
126863
|
+
slashMatches.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
126864
|
+
className: "absolute bottom-full left-3 right-3 md:left-4 md:right-4 mb-2 z-20 max-h-64 overflow-auto rounded-xl border border-border bg-popover shadow-lg",
|
|
126865
|
+
children: slashMatches.map((cmd, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
|
|
126866
|
+
onMouseDown: (e) => {
|
|
126867
|
+
e.preventDefault();
|
|
126868
|
+
acceptSlash(cmd);
|
|
126869
|
+
},
|
|
126870
|
+
onMouseEnter: () => setSlashIndex(i),
|
|
126871
|
+
className: cn$2("flex w-full items-baseline gap-2 px-3 py-1.5 text-left text-sm", i === slashActiveIndex ? "bg-accent" : "hover:bg-accent/50"),
|
|
126872
|
+
children: [
|
|
126873
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
126874
|
+
className: "font-medium shrink-0",
|
|
126875
|
+
children: cmd.name
|
|
126876
|
+
}),
|
|
126877
|
+
cmd.argumentHint && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
126878
|
+
className: "text-xs text-muted-foreground shrink-0",
|
|
126879
|
+
children: cmd.argumentHint
|
|
126880
|
+
}),
|
|
126881
|
+
cmd.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
126882
|
+
className: "ml-auto truncate text-xs text-muted-foreground/70",
|
|
126883
|
+
children: cmd.description
|
|
126884
|
+
})
|
|
126885
|
+
]
|
|
126886
|
+
}, cmd.name))
|
|
126887
|
+
}),
|
|
126600
126888
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
126601
126889
|
className: "hidden md:flex items-end gap-2",
|
|
126602
126890
|
children: [
|
|
@@ -126611,7 +126899,7 @@ function ChatPanel({ threads, onBack, showBackButton }) {
|
|
|
126611
126899
|
}),
|
|
126612
126900
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AutoGrowTextarea, {
|
|
126613
126901
|
value: draft,
|
|
126614
|
-
onChange: (e) =>
|
|
126902
|
+
onChange: (e) => handleDraftChange(e.target.value),
|
|
126615
126903
|
onKeyDown: handleKeyDown,
|
|
126616
126904
|
onPaste: handlePaste,
|
|
126617
126905
|
placeholder: `Message ${agent ? displayName(agent) : selectedInboxThread}…`,
|
|
@@ -126630,7 +126918,7 @@ function ChatPanel({ threads, onBack, showBackButton }) {
|
|
|
126630
126918
|
className: "md:hidden space-y-2",
|
|
126631
126919
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AutoGrowTextarea, {
|
|
126632
126920
|
value: draft,
|
|
126633
|
-
onChange: (e) =>
|
|
126921
|
+
onChange: (e) => handleDraftChange(e.target.value),
|
|
126634
126922
|
onKeyDown: handleKeyDown,
|
|
126635
126923
|
onPaste: handlePaste,
|
|
126636
126924
|
placeholder: `Message ${agent ? displayName(agent) : selectedInboxThread}…`,
|
|
@@ -157272,6 +157560,10 @@ if ("serviceWorker" in navigator) {
|
|
|
157272
157560
|
left: calc(var(--spacing) * 2.5);
|
|
157273
157561
|
}
|
|
157274
157562
|
|
|
157563
|
+
.left-3 {
|
|
157564
|
+
left: calc(var(--spacing) * 3);
|
|
157565
|
+
}
|
|
157566
|
+
|
|
157275
157567
|
.isolate {
|
|
157276
157568
|
isolation: isolate;
|
|
157277
157569
|
}
|
|
@@ -157703,6 +157995,10 @@ if ("serviceWorker" in navigator) {
|
|
|
157703
157995
|
max-height: calc(var(--spacing) * 44);
|
|
157704
157996
|
}
|
|
157705
157997
|
|
|
157998
|
+
.max-h-64 {
|
|
157999
|
+
max-height: calc(var(--spacing) * 64);
|
|
158000
|
+
}
|
|
158001
|
+
|
|
157706
158002
|
.max-h-72 {
|
|
157707
158003
|
max-height: calc(var(--spacing) * 72);
|
|
157708
158004
|
}
|
|
@@ -158015,10 +158311,6 @@ if ("serviceWorker" in navigator) {
|
|
|
158015
158311
|
min-width: 980px;
|
|
158016
158312
|
}
|
|
158017
158313
|
|
|
158018
|
-
.min-w-full {
|
|
158019
|
-
min-width: 100%;
|
|
158020
|
-
}
|
|
158021
|
-
|
|
158022
158314
|
.flex-1 {
|
|
158023
158315
|
flex: 1;
|
|
158024
158316
|
}
|
|
@@ -158081,6 +158373,10 @@ if ("serviceWorker" in navigator) {
|
|
|
158081
158373
|
rotate: 45deg;
|
|
158082
158374
|
}
|
|
158083
158375
|
|
|
158376
|
+
.rotate-90 {
|
|
158377
|
+
rotate: 90deg;
|
|
158378
|
+
}
|
|
158379
|
+
|
|
158084
158380
|
.rotate-\[-90deg\] {
|
|
158085
158381
|
rotate: -90deg;
|
|
158086
158382
|
}
|
|
@@ -158157,12 +158453,12 @@ if ("serviceWorker" in navigator) {
|
|
|
158157
158453
|
grid-template-columns: 1fr auto;
|
|
158158
158454
|
}
|
|
158159
158455
|
|
|
158160
|
-
.grid-cols-\[
|
|
158161
|
-
grid-template-columns:
|
|
158456
|
+
.grid-cols-\[2\.5rem_minmax\(0\,1fr\)\] {
|
|
158457
|
+
grid-template-columns: 2.5rem minmax(0, 1fr);
|
|
158162
158458
|
}
|
|
158163
158459
|
|
|
158164
|
-
.grid-cols-\[
|
|
158165
|
-
grid-template-columns:
|
|
158460
|
+
.grid-cols-\[4\.5rem_minmax\(0\,1fr\)\] {
|
|
158461
|
+
grid-template-columns: 4.5rem minmax(0, 1fr);
|
|
158166
158462
|
}
|
|
158167
158463
|
|
|
158168
158464
|
.grid-cols-\[minmax\(10rem\,1fr\)_8rem_8rem_7rem_7rem_5rem\] {
|
|
@@ -158181,6 +158477,10 @@ if ("serviceWorker" in navigator) {
|
|
|
158181
158477
|
flex-wrap: wrap;
|
|
158182
158478
|
}
|
|
158183
158479
|
|
|
158480
|
+
.items-baseline {
|
|
158481
|
+
align-items: baseline;
|
|
158482
|
+
}
|
|
158483
|
+
|
|
158184
158484
|
.items-center {
|
|
158185
158485
|
align-items: center;
|
|
158186
158486
|
}
|
|
@@ -158359,10 +158659,6 @@ if ("serviceWorker" in navigator) {
|
|
|
158359
158659
|
overflow: hidden;
|
|
158360
158660
|
}
|
|
158361
158661
|
|
|
158362
|
-
.overflow-visible {
|
|
158363
|
-
overflow: visible;
|
|
158364
|
-
}
|
|
158365
|
-
|
|
158366
158662
|
.overflow-x-auto {
|
|
158367
158663
|
overflow-x: auto;
|
|
158368
158664
|
}
|
|
@@ -159757,10 +160053,6 @@ if ("serviceWorker" in navigator) {
|
|
|
159757
160053
|
padding-right: calc(var(--spacing) * 2);
|
|
159758
160054
|
}
|
|
159759
160055
|
|
|
159760
|
-
.pr-3 {
|
|
159761
|
-
padding-right: calc(var(--spacing) * 3);
|
|
159762
|
-
}
|
|
159763
|
-
|
|
159764
160056
|
.pr-4 {
|
|
159765
160057
|
padding-right: calc(var(--spacing) * 4);
|
|
159766
160058
|
}
|
|
@@ -160301,6 +160593,10 @@ if ("serviceWorker" in navigator) {
|
|
|
160301
160593
|
text-transform: uppercase;
|
|
160302
160594
|
}
|
|
160303
160595
|
|
|
160596
|
+
.italic {
|
|
160597
|
+
font-style: italic;
|
|
160598
|
+
}
|
|
160599
|
+
|
|
160304
160600
|
.tabular-nums {
|
|
160305
160601
|
--tw-numeric-spacing: tabular-nums;
|
|
160306
160602
|
font-variant-numeric: var(--tw-ordinal, ) var(--tw-slashed-zero, ) var(--tw-numeric-figure, ) var(--tw-numeric-spacing, ) var(--tw-numeric-fraction, );
|
|
@@ -160823,6 +161119,16 @@ if ("serviceWorker" in navigator) {
|
|
|
160823
161119
|
}
|
|
160824
161120
|
}
|
|
160825
161121
|
|
|
161122
|
+
.hover\:bg-accent\/50:hover {
|
|
161123
|
+
background-color: var(--accent);
|
|
161124
|
+
}
|
|
161125
|
+
|
|
161126
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
161127
|
+
.hover\:bg-accent\/50:hover {
|
|
161128
|
+
background-color: color-mix(in oklab, var(--accent) 50%, transparent);
|
|
161129
|
+
}
|
|
161130
|
+
}
|
|
161131
|
+
|
|
160826
161132
|
.hover\:bg-background:hover {
|
|
160827
161133
|
background-color: var(--background);
|
|
160828
161134
|
}
|
|
@@ -161475,6 +161781,10 @@ if ("serviceWorker" in navigator) {
|
|
|
161475
161781
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
161476
161782
|
}
|
|
161477
161783
|
|
|
161784
|
+
.sm\:grid-cols-\[4rem_minmax\(0\,1fr\)\] {
|
|
161785
|
+
grid-template-columns: 4rem minmax(0, 1fr);
|
|
161786
|
+
}
|
|
161787
|
+
|
|
161478
161788
|
.sm\:flex-row {
|
|
161479
161789
|
flex-direction: row;
|
|
161480
161790
|
}
|
|
@@ -161483,6 +161793,14 @@ if ("serviceWorker" in navigator) {
|
|
|
161483
161793
|
justify-content: flex-end;
|
|
161484
161794
|
}
|
|
161485
161795
|
|
|
161796
|
+
.sm\:px-2 {
|
|
161797
|
+
padding-inline: calc(var(--spacing) * 2);
|
|
161798
|
+
}
|
|
161799
|
+
|
|
161800
|
+
.sm\:pr-3 {
|
|
161801
|
+
padding-right: calc(var(--spacing) * 3);
|
|
161802
|
+
}
|
|
161803
|
+
|
|
161486
161804
|
.sm\:opacity-0 {
|
|
161487
161805
|
opacity: 0;
|
|
161488
161806
|
}
|
|
@@ -161495,6 +161813,14 @@ if ("serviceWorker" in navigator) {
|
|
|
161495
161813
|
}
|
|
161496
161814
|
|
|
161497
161815
|
@media (min-width: 48rem) {
|
|
161816
|
+
.md\:right-4 {
|
|
161817
|
+
right: calc(var(--spacing) * 4);
|
|
161818
|
+
}
|
|
161819
|
+
|
|
161820
|
+
.md\:left-4 {
|
|
161821
|
+
left: calc(var(--spacing) * 4);
|
|
161822
|
+
}
|
|
161823
|
+
|
|
161498
161824
|
.md\:-m-6 {
|
|
161499
161825
|
margin: calc(var(--spacing) * -6);
|
|
161500
161826
|
}
|
|
@@ -161559,6 +161885,15 @@ if ("serviceWorker" in navigator) {
|
|
|
161559
161885
|
gap: calc(var(--spacing) * 4);
|
|
161560
161886
|
}
|
|
161561
161887
|
|
|
161888
|
+
.md\:rounded-md {
|
|
161889
|
+
border-radius: calc(var(--radius) * .8);
|
|
161890
|
+
}
|
|
161891
|
+
|
|
161892
|
+
.md\:border {
|
|
161893
|
+
border-style: var(--tw-border-style);
|
|
161894
|
+
border-width: 1px;
|
|
161895
|
+
}
|
|
161896
|
+
|
|
161562
161897
|
.md\:border-r {
|
|
161563
161898
|
border-right-style: var(--tw-border-style);
|
|
161564
161899
|
border-right-width: 1px;
|
|
@@ -161569,6 +161904,10 @@ if ("serviceWorker" in navigator) {
|
|
|
161569
161904
|
border-bottom-width: 0;
|
|
161570
161905
|
}
|
|
161571
161906
|
|
|
161907
|
+
.md\:border-border {
|
|
161908
|
+
border-color: var(--border);
|
|
161909
|
+
}
|
|
161910
|
+
|
|
161572
161911
|
.md\:p-6 {
|
|
161573
161912
|
padding: calc(var(--spacing) * 6);
|
|
161574
161913
|
}
|
package/runner/src/adapter.ts
CHANGED
|
@@ -27,6 +27,20 @@ export interface ProviderStatusEvent {
|
|
|
27
27
|
|
|
28
28
|
export type ProviderStatusUpdate = SemanticStatus | ProviderStatusEvent;
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* A session-mirror event surfaced by an adapter that learns about session
|
|
32
|
+
* activity through provider events rather than hooks/transcripts (e.g. the Codex
|
|
33
|
+
* app-server). The runner turns these into `kind: "session"` chat messages, the
|
|
34
|
+
* same lane Claude's transcript capture uses. Provider-independent boundary.
|
|
35
|
+
*/
|
|
36
|
+
export interface ProviderSessionEvent {
|
|
37
|
+
type: "prompt" | "response" | "reasoning" | "tool";
|
|
38
|
+
body: string;
|
|
39
|
+
origin?: "chat" | "terminal" | "provider";
|
|
40
|
+
turnId?: string;
|
|
41
|
+
label?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
30
44
|
export interface ProviderConfig {
|
|
31
45
|
command: string;
|
|
32
46
|
defaultArgs: string[];
|
|
@@ -36,6 +50,9 @@ export interface ProviderConfig {
|
|
|
36
50
|
defaultApprovalMode: string;
|
|
37
51
|
defaultTags: string[];
|
|
38
52
|
chatCaptureMode: "final" | "full";
|
|
53
|
+
// When false, the runner does not stream reasoning/tool steps into chat. Defaults
|
|
54
|
+
// to enabled (steps render discreetly, never as chat bubbles).
|
|
55
|
+
reasoningCapture?: boolean;
|
|
39
56
|
headless: {
|
|
40
57
|
tmuxPrefix: string;
|
|
41
58
|
shutdownTimeoutMs: number;
|
|
@@ -110,11 +127,24 @@ export interface ProviderAdapter {
|
|
|
110
127
|
shutdown(process: ManagedProcess, opts: { graceful: boolean; timeoutMs: number }): Promise<void>;
|
|
111
128
|
compact?(process: ManagedProcess): Promise<Record<string, unknown> | void>;
|
|
112
129
|
clearContext?(process: ManagedProcess): Promise<Record<string, unknown> | void>;
|
|
130
|
+
// Interrupt the in-flight turn without ending the session (ESC for Claude's
|
|
131
|
+
// tmux pane, turn/interrupt for the Codex app-server). Provider-independent at
|
|
132
|
+
// the runner boundary; each adapter does what its provider actually supports.
|
|
133
|
+
interrupt?(process: ManagedProcess): Promise<Record<string, unknown> | void>;
|
|
134
|
+
// Out-of-band activity probe for the busy-state reconciler: returns the real
|
|
135
|
+
// provider activity when the runner's claim state may have gone stale (e.g. the
|
|
136
|
+
// turn was interrupted from the web terminal so no Stop hook fired). "unknown"
|
|
137
|
+
// means the provider can't be cheaply probed and the reconciler should defer.
|
|
138
|
+
probeActivity?(process: ManagedProcess): Promise<"busy" | "idle" | "unknown">;
|
|
113
139
|
terminalAttachSpec?(process: ManagedProcess): Promise<TerminalAttachSpec>;
|
|
114
140
|
respondToPermissionDecision?(process: ManagedProcess, input: ProviderPermissionDecisionInput): Promise<Record<string, unknown> | void>;
|
|
115
141
|
deliverInitialPrompt?(process: ManagedProcess, prompt: string): Promise<void>;
|
|
116
142
|
deliver(process: ManagedProcess, messages: Message[]): Promise<void>;
|
|
117
143
|
onStatusChange(cb: (status: ProviderStatusUpdate) => void): void;
|
|
144
|
+
// Subscribe to session-mirror events from providers that emit them directly
|
|
145
|
+
// (Codex app-server item events). Claude mirrors via hooks/transcript instead,
|
|
146
|
+
// so it leaves this unimplemented.
|
|
147
|
+
onSessionEvent?(cb: (event: ProviderSessionEvent) => void): void;
|
|
118
148
|
// Headless providers with no tmux session (e.g. the Codex app-server) still
|
|
119
149
|
// warrant an automatic restart on unexpected exit. Returning true opts the
|
|
120
150
|
// provider into the runner's restart-with-backoff path.
|
package/src/routes.ts
CHANGED
|
@@ -320,7 +320,7 @@ const VALID_CHANNEL_BINDING_TARGET_TYPES = ["agent", "label", "tag", "capability
|
|
|
320
320
|
const VALID_WORKSPACE_MODES = ["isolated", "shared", "inherit"] as const;
|
|
321
321
|
const VALID_WORKSPACE_STATUSES = ["active", "ready", "conflict", "review_requested", "merge_planned", "merged", "abandoned", "cleanup_requested", "cleaned"] as const;
|
|
322
322
|
const VALID_CHANNEL_BINDING_MODES = ["exclusive", "broadcast"] as const;
|
|
323
|
-
const VALID_AGENT_ACTIONS = ["restart", "shutdown", "reconnect", "compact", "clearContext", "resume"] as const;
|
|
323
|
+
const VALID_AGENT_ACTIONS = ["restart", "shutdown", "reconnect", "compact", "clearContext", "resume", "interrupt"] as const;
|
|
324
324
|
const CLAUDE_RESUME_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
325
325
|
const VALID_AGENT_SPAWN_PROVIDERS = ["codex"] as const;
|
|
326
326
|
const VALID_CODEX_SPAWN_APPROVALS = ["open", "guarded", "read-only"] as const;
|
|
@@ -2147,11 +2147,12 @@ const deleteAgentById: Handler = (_req, params) => {
|
|
|
2147
2147
|
|
|
2148
2148
|
type AgentControlAction = (typeof VALID_AGENT_ACTIONS)[number];
|
|
2149
2149
|
|
|
2150
|
-
function agentControlActionCommandType(action: AgentControlAction): "agent.restart" | "agent.shutdown" | "agent.reconnect" | "agent.compact" | "agent.clearContext" {
|
|
2150
|
+
function agentControlActionCommandType(action: AgentControlAction): "agent.restart" | "agent.shutdown" | "agent.reconnect" | "agent.compact" | "agent.clearContext" | "agent.interrupt" {
|
|
2151
2151
|
if (action === "restart" || action === "resume") return "agent.restart";
|
|
2152
2152
|
if (action === "shutdown") return "agent.shutdown";
|
|
2153
2153
|
if (action === "compact") return "agent.compact";
|
|
2154
2154
|
if (action === "clearContext") return "agent.clearContext";
|
|
2155
|
+
if (action === "interrupt") return "agent.interrupt";
|
|
2155
2156
|
return "agent.reconnect";
|
|
2156
2157
|
}
|
|
2157
2158
|
|
|
@@ -2161,6 +2162,7 @@ function agentControlActionFromCommandType(type: string): AgentControlAction | n
|
|
|
2161
2162
|
if (type === "agent.reconnect") return "reconnect";
|
|
2162
2163
|
if (type === "agent.compact") return "compact";
|
|
2163
2164
|
if (type === "agent.clearContext") return "clearContext";
|
|
2165
|
+
if (type === "agent.interrupt") return "interrupt";
|
|
2164
2166
|
return null;
|
|
2165
2167
|
}
|
|
2166
2168
|
|
|
@@ -2170,6 +2172,7 @@ function agentControlActionRequestedTitle(action: AgentControlAction): string {
|
|
|
2170
2172
|
if (action === "shutdown") return "Agent shutdown requested";
|
|
2171
2173
|
if (action === "compact") return "Agent compaction requested";
|
|
2172
2174
|
if (action === "clearContext") return "Agent context clear requested";
|
|
2175
|
+
if (action === "interrupt") return "Agent interrupt requested";
|
|
2173
2176
|
return "Agent reconnect requested";
|
|
2174
2177
|
}
|
|
2175
2178
|
|
|
@@ -2179,6 +2182,7 @@ function agentControlActionCompletedTitle(action: AgentControlAction): string {
|
|
|
2179
2182
|
if (action === "shutdown") return "Agent shut down";
|
|
2180
2183
|
if (action === "compact") return "Agent compacted";
|
|
2181
2184
|
if (action === "clearContext") return "Agent context cleared";
|
|
2185
|
+
if (action === "interrupt") return "Agent interrupted";
|
|
2182
2186
|
return "Agent reconnected";
|
|
2183
2187
|
}
|
|
2184
2188
|
|
|
@@ -2187,6 +2191,7 @@ function agentControlActionIcon(action: AgentControlAction): string {
|
|
|
2187
2191
|
if (action === "compact") return "ti-compress";
|
|
2188
2192
|
if (action === "clearContext") return "ti-eraser";
|
|
2189
2193
|
if (action === "resume") return "ti-player-play";
|
|
2194
|
+
if (action === "interrupt") return "ti-player-stop";
|
|
2190
2195
|
return "ti-refresh";
|
|
2191
2196
|
}
|
|
2192
2197
|
|
|
@@ -2200,6 +2205,9 @@ function agentIsControlEligible(agent: AgentCard): boolean {
|
|
|
2200
2205
|
function agentCanReceiveControlAction(agent: AgentCard, action: AgentControlAction): boolean {
|
|
2201
2206
|
if (!agentIsControlEligible(agent)) return false;
|
|
2202
2207
|
if (action === "resume") return agentRuntimeProvider(agent) === "claude" && (agent.status === "offline" || agent.status === "stale");
|
|
2208
|
+
// Interrupt only makes sense while the provider is mid-turn, and only if the
|
|
2209
|
+
// provider advertises it can be interrupted from the dashboard.
|
|
2210
|
+
if (action === "interrupt") return agent.providerCapabilities?.liveSession?.interrupt === true && agent.status === "busy";
|
|
2203
2211
|
const lifecycle = agent.providerCapabilities?.lifecycle;
|
|
2204
2212
|
if (lifecycle) {
|
|
2205
2213
|
if (action === "restart") return lifecycle.restartHard === true;
|