@agentprojectcontext/apx 1.56.2 → 1.57.0
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 +1 -1
- package/src/core/stores/conversations.js +10 -0
- package/src/core/stores/messages.js +37 -6
- package/src/host/daemon/api/conversations.js +20 -2
- package/src/interfaces/web/dist/assets/index-CEI8DfVg.css +1 -0
- package/src/interfaces/web/dist/assets/index-DJ-ocXOR.js +651 -0
- package/src/interfaces/web/dist/assets/{index-DaE_memX.js.map → index-DJ-ocXOR.js.map} +1 -1
- package/src/interfaces/web/dist/index.html +2 -2
- package/src/interfaces/web/package-lock.json +6 -6
- package/src/interfaces/web/src/App.tsx +1 -1
- package/src/interfaces/web/src/components/chat/ChatList.tsx +86 -65
- package/src/interfaces/web/src/components/common/TabNav.tsx +1 -1
- package/src/interfaces/web/src/hooks/useChat.ts +39 -7
- package/src/interfaces/web/src/i18n/en.ts +9 -3
- package/src/interfaces/web/src/i18n/es.ts +9 -3
- package/src/interfaces/web/src/lib/api/conversations.ts +6 -0
- package/src/interfaces/web/src/screens/ProjectScreen.tsx +1 -1
- package/src/interfaces/web/src/screens/SettingsScreen.tsx +1 -1
- package/src/interfaces/web/src/screens/project/ChatTab.tsx +136 -36
- package/src/interfaces/web/src/types/daemon.ts +5 -0
- package/src/interfaces/web/dist/assets/index-CAUezTBY.css +0 -1
- package/src/interfaces/web/dist/assets/index-DaE_memX.js +0 -651
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
<link rel="apple-touch-icon" href="/favicon/dark/apple-touch-icon.png" media="(prefers-color-scheme: dark)" />
|
|
19
19
|
<link rel="manifest" href="/favicon/white/site.webmanifest" media="(prefers-color-scheme: light)" />
|
|
20
20
|
<link rel="manifest" href="/favicon/dark/site.webmanifest" media="(prefers-color-scheme: dark)" />
|
|
21
|
-
<script type="module" crossorigin src="/assets/index-
|
|
22
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
21
|
+
<script type="module" crossorigin src="/assets/index-DJ-ocXOR.js"></script>
|
|
22
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CEI8DfVg.css">
|
|
23
23
|
</head>
|
|
24
24
|
<body class="bg-background text-foreground antialiased">
|
|
25
25
|
<div id="root"></div>
|
|
@@ -2162,9 +2162,9 @@
|
|
|
2162
2162
|
}
|
|
2163
2163
|
},
|
|
2164
2164
|
"node_modules/baseline-browser-mapping": {
|
|
2165
|
-
"version": "2.10.
|
|
2166
|
-
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.
|
|
2167
|
-
"integrity": "sha512-
|
|
2165
|
+
"version": "2.10.42",
|
|
2166
|
+
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.42.tgz",
|
|
2167
|
+
"integrity": "sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==",
|
|
2168
2168
|
"dev": true,
|
|
2169
2169
|
"license": "Apache-2.0",
|
|
2170
2170
|
"bin": {
|
|
@@ -2580,9 +2580,9 @@
|
|
|
2580
2580
|
"license": "MIT"
|
|
2581
2581
|
},
|
|
2582
2582
|
"node_modules/electron-to-chromium": {
|
|
2583
|
-
"version": "1.5.
|
|
2584
|
-
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.
|
|
2585
|
-
"integrity": "sha512-
|
|
2583
|
+
"version": "1.5.387",
|
|
2584
|
+
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.387.tgz",
|
|
2585
|
+
"integrity": "sha512-TaxwufTFDufvPEoXdhwVrA3UdFWBeWGkYoJ1K8ldF1xe6gKfth6iRNS5lTQ5JPNOHdGQm8PT1QYKUqFLCiUefQ==",
|
|
2586
2586
|
"dev": true,
|
|
2587
2587
|
"license": "ISC"
|
|
2588
2588
|
},
|
|
@@ -84,7 +84,7 @@ function Shell() {
|
|
|
84
84
|
onOpenRoby={() => setRobyOpen(true)}
|
|
85
85
|
onOpenAddProject={openAdd}
|
|
86
86
|
/>
|
|
87
|
-
<main className="m-2
|
|
87
|
+
<main className="m-2 flex min-w-0 flex-1 flex-col overflow-hidden rounded-xl border border-border bg-card shadow-sm">
|
|
88
88
|
<TopBar onToggleTheme={toggle} isDark={theme === "dark"} pathname={location.pathname} />
|
|
89
89
|
<div className="flex-1 overflow-y-auto">
|
|
90
90
|
<Routes>
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
Monitor,
|
|
13
13
|
Plus,
|
|
14
14
|
Send,
|
|
15
|
-
Sparkles,
|
|
16
15
|
Timer,
|
|
17
16
|
User,
|
|
18
17
|
} from "lucide-react";
|
|
@@ -23,14 +22,15 @@ import { t } from "../../i18n";
|
|
|
23
22
|
import type { AgentEntry, ConversationListEntry, ThreadListEntry } from "../../types/daemon";
|
|
24
23
|
|
|
25
24
|
// Channel taxonomy — same channels the daemon writes ("web", "voice",
|
|
26
|
-
// "desktop", "telegram", …) folded into
|
|
27
|
-
// icon + a fixed display order so the sidebar is stable across reloads.
|
|
25
|
+
// "desktop", "telegram", …) folded into sidebar groups. Each group has an
|
|
26
|
+
// icon + a fixed display order so the sidebar is stable across reloads. Web is
|
|
27
|
+
// pinned first (it's where chats started from this UI land), then the remote
|
|
28
|
+
// channels, with the catch-all "other" last.
|
|
28
29
|
export type ChannelGroupKey =
|
|
29
|
-
| "
|
|
30
|
+
| "web"
|
|
30
31
|
| "telegram"
|
|
31
|
-
| "voice"
|
|
32
32
|
| "desktop"
|
|
33
|
-
| "
|
|
33
|
+
| "voice"
|
|
34
34
|
| "a2a"
|
|
35
35
|
| "schedule"
|
|
36
36
|
| "other";
|
|
@@ -42,14 +42,13 @@ interface GroupMeta {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
const GROUP_META: Record<ChannelGroupKey, GroupMeta> = {
|
|
45
|
-
|
|
45
|
+
web: { label: "Web", icon: MessageSquare, order: 0 },
|
|
46
46
|
telegram: { label: "Telegram", icon: Send, order: 1 },
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
other: { label: "Other", icon: FolderOpen, order: 7 },
|
|
47
|
+
desktop: { label: "Desktop", icon: Monitor, order: 2 },
|
|
48
|
+
voice: { label: "Voice", icon: Mic, order: 3 },
|
|
49
|
+
a2a: { label: "Agent ↔ Agent", icon: Bot, order: 4 },
|
|
50
|
+
schedule: { label: "Schedule", icon: Timer, order: 5 },
|
|
51
|
+
other: { label: "Other", icon: FolderOpen, order: 6 },
|
|
53
52
|
};
|
|
54
53
|
|
|
55
54
|
function channelGroup(channel?: string): ChannelGroupKey {
|
|
@@ -78,6 +77,14 @@ export function chatKeyToString(k: ChatKey): string {
|
|
|
78
77
|
return `thread:${k.channel}:${k.threadId}`;
|
|
79
78
|
}
|
|
80
79
|
|
|
80
|
+
/** Display metadata carried alongside a selection so the right-pane header can
|
|
81
|
+
* show "Created {date} · {channel} · {agent}" without a second fetch. */
|
|
82
|
+
export interface ChatSelectionMeta {
|
|
83
|
+
channel?: string;
|
|
84
|
+
createdAt?: string;
|
|
85
|
+
title?: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
81
88
|
interface Props {
|
|
82
89
|
pid: string;
|
|
83
90
|
agents: AgentEntry[];
|
|
@@ -85,8 +92,10 @@ interface Props {
|
|
|
85
92
|
superAgentSlug: string;
|
|
86
93
|
superAgentLabel: string;
|
|
87
94
|
selected: ChatKey;
|
|
88
|
-
onSelect: (key: ChatKey) => void;
|
|
89
|
-
|
|
95
|
+
onSelect: (key: ChatKey, meta?: ChatSelectionMeta) => void;
|
|
96
|
+
/** Start a fresh in-memory session with the chosen agent (super-agent or a
|
|
97
|
+
* project agent). It materialises in the Web group on the first message. */
|
|
98
|
+
onNewChat: (agentSlug: string) => void;
|
|
90
99
|
}
|
|
91
100
|
|
|
92
101
|
// Per-agent SWR fetcher. Lives in a child so adding/removing agents doesn't
|
|
@@ -125,6 +134,7 @@ export function ChatList({
|
|
|
125
134
|
const [agentFilter, setAgentFilter] = useState("");
|
|
126
135
|
const [collapsed, setCollapsed] = useState<Partial<Record<ChannelGroupKey, boolean>>>({});
|
|
127
136
|
const [byAgent, setByAgent] = useState<Record<string, ConversationListEntry[]>>({});
|
|
137
|
+
const [pickerOpen, setPickerOpen] = useState(false);
|
|
128
138
|
|
|
129
139
|
// Super-agent channel threads (telegram, web quick-chat, desktop …) come from
|
|
130
140
|
// the global message ledger, which is daemon-level and NOT project-scoped.
|
|
@@ -208,20 +218,16 @@ export function ChatList({
|
|
|
208
218
|
.sort((a, b) => GROUP_META[a.key].order - GROUP_META[b.key].order);
|
|
209
219
|
}, [filteredConvs, filteredThreads]);
|
|
210
220
|
|
|
211
|
-
//
|
|
212
|
-
//
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
out.push({ slug: a.slug, label: a.slug });
|
|
222
|
-
}
|
|
223
|
-
return out;
|
|
224
|
-
}, [agents, agentFilter, superAgentSlug, superAgentLabel]);
|
|
221
|
+
// Agents offered by the "+ New" picker: the super-agent (always available,
|
|
222
|
+
// listed first) followed by this project's own agents. Not affected by the
|
|
223
|
+
// sidebar filter — you can always start a fresh chat with any of them.
|
|
224
|
+
const newChatAgents = useMemo(
|
|
225
|
+
() => [
|
|
226
|
+
{ slug: superAgentSlug, label: superAgentLabel },
|
|
227
|
+
...agents.map((a) => ({ slug: a.slug, label: a.slug })),
|
|
228
|
+
],
|
|
229
|
+
[agents, superAgentSlug, superAgentLabel],
|
|
230
|
+
);
|
|
225
231
|
|
|
226
232
|
const agentOptions = useMemo(
|
|
227
233
|
() => [
|
|
@@ -232,7 +238,7 @@ export function ChatList({
|
|
|
232
238
|
[agents, superAgentSlug, superAgentLabel],
|
|
233
239
|
);
|
|
234
240
|
|
|
235
|
-
const totalCount = allConvs.length + (threadsQ.data?.length || 0)
|
|
241
|
+
const totalCount = allConvs.length + (threadsQ.data?.length || 0);
|
|
236
242
|
const anyLoaded =
|
|
237
243
|
Object.keys(byAgent).length > 0 || agents.length === 0 || !!threadsQ.data;
|
|
238
244
|
|
|
@@ -251,13 +257,48 @@ export function ChatList({
|
|
|
251
257
|
{t("project.chat.list.count", { n: totalCount })}
|
|
252
258
|
</p>
|
|
253
259
|
</div>
|
|
254
|
-
<
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
260
|
+
<div className="relative">
|
|
261
|
+
<button
|
|
262
|
+
type="button"
|
|
263
|
+
onClick={() => setPickerOpen((o) => !o)}
|
|
264
|
+
className="inline-flex items-center gap-1 rounded-md border border-border bg-accent/60 px-2 py-1 text-[11px] font-medium hover:bg-accent"
|
|
265
|
+
>
|
|
266
|
+
<Plus className="size-3" /> {t("project.chat.list.new")}
|
|
267
|
+
</button>
|
|
268
|
+
{pickerOpen && (
|
|
269
|
+
<>
|
|
270
|
+
{/* Click-away layer. */}
|
|
271
|
+
<button
|
|
272
|
+
type="button"
|
|
273
|
+
aria-hidden
|
|
274
|
+
tabIndex={-1}
|
|
275
|
+
className="fixed inset-0 z-10 cursor-default"
|
|
276
|
+
onClick={() => setPickerOpen(false)}
|
|
277
|
+
/>
|
|
278
|
+
<div className="absolute right-0 top-full z-20 mt-1 w-56 rounded-md border border-border bg-card p-1 shadow-lg">
|
|
279
|
+
<p className="px-2 py-1 text-[10px] font-semibold uppercase tracking-wider text-muted-fg">
|
|
280
|
+
{t("project.chat.list.pick_agent")}
|
|
281
|
+
</p>
|
|
282
|
+
<div className="max-h-64 overflow-y-auto">
|
|
283
|
+
{newChatAgents.map((a) => (
|
|
284
|
+
<button
|
|
285
|
+
key={`new-${a.slug}`}
|
|
286
|
+
type="button"
|
|
287
|
+
onClick={() => {
|
|
288
|
+
setPickerOpen(false);
|
|
289
|
+
onNewChat(a.slug);
|
|
290
|
+
}}
|
|
291
|
+
className="flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-xs hover:bg-accent/50"
|
|
292
|
+
>
|
|
293
|
+
<Bot className="size-3 shrink-0 text-muted-fg" />
|
|
294
|
+
<span className="truncate">{a.label}</span>
|
|
295
|
+
</button>
|
|
296
|
+
))}
|
|
297
|
+
</div>
|
|
298
|
+
</div>
|
|
299
|
+
</>
|
|
300
|
+
)}
|
|
301
|
+
</div>
|
|
261
302
|
</header>
|
|
262
303
|
|
|
263
304
|
<div className="space-y-2 border-b border-border p-2">
|
|
@@ -276,32 +317,6 @@ export function ChatList({
|
|
|
276
317
|
</div>
|
|
277
318
|
)}
|
|
278
319
|
|
|
279
|
-
{/* Live group — synthetic, always first. */}
|
|
280
|
-
{liveAgents.length > 0 && (
|
|
281
|
-
<ChannelGroup
|
|
282
|
-
keyName="live"
|
|
283
|
-
count={liveAgents.length}
|
|
284
|
-
collapsed={!!collapsed.live}
|
|
285
|
-
onToggle={() => setCollapsed((p) => ({ ...p, live: !p.live }))}
|
|
286
|
-
>
|
|
287
|
-
{liveAgents.map((a) => {
|
|
288
|
-
const isRoby = a.slug === superAgentSlug;
|
|
289
|
-
const active =
|
|
290
|
-
selected.kind === "live" && selected.agentSlug === a.slug;
|
|
291
|
-
return (
|
|
292
|
-
<ChatListItem
|
|
293
|
-
key={`live-${a.slug}`}
|
|
294
|
-
title={isRoby ? a.label : t("project.chat.list.live_with", { slug: a.slug })}
|
|
295
|
-
subtitle={t("project.chat.list.live_subtitle")}
|
|
296
|
-
badge={isRoby ? "super" : a.slug}
|
|
297
|
-
selected={active}
|
|
298
|
-
onClick={() => onSelect({ kind: "live", agentSlug: a.slug })}
|
|
299
|
-
/>
|
|
300
|
-
);
|
|
301
|
-
})}
|
|
302
|
-
</ChannelGroup>
|
|
303
|
-
)}
|
|
304
|
-
|
|
305
320
|
{/* Stored conversations + super-agent channel threads, grouped by channel. */}
|
|
306
321
|
{groups.map((g) => (
|
|
307
322
|
<ChannelGroup
|
|
@@ -327,7 +342,10 @@ export function ChatList({
|
|
|
327
342
|
timeAgo={th.last_ts}
|
|
328
343
|
selected={active}
|
|
329
344
|
onClick={() =>
|
|
330
|
-
onSelect(
|
|
345
|
+
onSelect(
|
|
346
|
+
{ kind: "thread", channel: th.channel, threadId: th.id },
|
|
347
|
+
{ channel: th.channel, createdAt: th.started_at, title: th.title },
|
|
348
|
+
)
|
|
331
349
|
}
|
|
332
350
|
/>
|
|
333
351
|
);
|
|
@@ -348,7 +366,10 @@ export function ChatList({
|
|
|
348
366
|
timeAgo={c.started_at}
|
|
349
367
|
selected={active}
|
|
350
368
|
onClick={() =>
|
|
351
|
-
onSelect(
|
|
369
|
+
onSelect(
|
|
370
|
+
{ kind: "conv", agentSlug: c.agent_slug, convId: c.id },
|
|
371
|
+
{ channel: c.channel, createdAt: c.started_at, title: c.title },
|
|
372
|
+
)
|
|
352
373
|
}
|
|
353
374
|
/>
|
|
354
375
|
);
|
|
@@ -65,7 +65,7 @@ export function TabNav({ sections, active, onChange, collapsed = false }: TabNav
|
|
|
65
65
|
<nav
|
|
66
66
|
className={cn(
|
|
67
67
|
"hidden md:flex shrink-0 flex-col gap-1 py-3 transition-all",
|
|
68
|
-
collapsed ? "w-12 items-center px-1" : "w-
|
|
68
|
+
collapsed ? "w-12 items-center px-1" : "w-44 px-2",
|
|
69
69
|
)}
|
|
70
70
|
>
|
|
71
71
|
{sections.map((section, si) => (
|
|
@@ -130,6 +130,44 @@ function isErrorResult(result: unknown): boolean {
|
|
|
130
130
|
return "error" in r && !!r.error;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
/** Reconstruct chat turns from a persisted channel thread (global ledger).
|
|
134
|
+
* User rows start a new turn; consecutive assistant/tool rows collapse into a
|
|
135
|
+
* single assistant bubble with interleaved text + tool parts — mirroring how a
|
|
136
|
+
* live streamed turn is shaped, so tool executions render the same on reload as
|
|
137
|
+
* they did in real time. Persisted rows carry no live status, so it's derived
|
|
138
|
+
* from the stored result (error → "error", else "done"). */
|
|
139
|
+
function threadToChatMsgs(messages: ConversationMessage[]): ChatMsg[] {
|
|
140
|
+
const out: ChatMsg[] = [];
|
|
141
|
+
let turn: ChatMsg | null = null;
|
|
142
|
+
let toolSeq = 0;
|
|
143
|
+
for (const m of messages) {
|
|
144
|
+
const ts = m.ts || new Date().toISOString();
|
|
145
|
+
if (m.role === "user") {
|
|
146
|
+
turn = null;
|
|
147
|
+
out.push({ role: "user", parts: userPart(m.content), ts });
|
|
148
|
+
} else if (m.role === "assistant" || m.role === "tool") {
|
|
149
|
+
if (!turn) {
|
|
150
|
+
turn = { role: "assistant", parts: [], ts };
|
|
151
|
+
out.push(turn);
|
|
152
|
+
}
|
|
153
|
+
if (m.role === "tool") {
|
|
154
|
+
turn.parts.push({
|
|
155
|
+
kind: "tool",
|
|
156
|
+
id: `hist-${toolSeq++}`,
|
|
157
|
+
tool: m.tool || "tool",
|
|
158
|
+
args: m.args,
|
|
159
|
+
result: m.result,
|
|
160
|
+
status: isErrorResult(m.result) ? "error" : "done",
|
|
161
|
+
});
|
|
162
|
+
} else if (m.content) {
|
|
163
|
+
turn.parts.push({ kind: "text", text: m.content });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// system/compact rows are context-only; not rendered in the thread viewer.
|
|
167
|
+
}
|
|
168
|
+
return out;
|
|
169
|
+
}
|
|
170
|
+
|
|
133
171
|
/**
|
|
134
172
|
* Pure reducer: apply one NDJSON stream event to an assistant turn and return
|
|
135
173
|
* the next turn. Every surface that consumes the super-agent stream (ChatTab,
|
|
@@ -398,13 +436,7 @@ export function useChat(pid: string, onError?: (msg: string) => void): UseChatRe
|
|
|
398
436
|
try {
|
|
399
437
|
const detail = await Conversations.thread(pid, channel, threadId);
|
|
400
438
|
if (seq !== loadSeqRef.current) return; // superseded by a newer pick
|
|
401
|
-
const loaded
|
|
402
|
-
.filter((m) => m.role === "user" || m.role === "assistant")
|
|
403
|
-
.map((m) => ({
|
|
404
|
-
role: m.role as "user" | "assistant",
|
|
405
|
-
parts: [{ kind: "text", text: m.content }],
|
|
406
|
-
ts: m.ts || new Date().toISOString(),
|
|
407
|
-
}));
|
|
439
|
+
const loaded = threadToChatMsgs(detail.messages ?? []);
|
|
408
440
|
// Ledger threads have no conversation file — sends continue as fresh
|
|
409
441
|
// web turns with this history as previousMessages.
|
|
410
442
|
convoRef.current = undefined;
|
|
@@ -319,6 +319,7 @@ export const en = {
|
|
|
319
319
|
chat: {
|
|
320
320
|
title: "Chat with agent",
|
|
321
321
|
subtitle: "Direct conversations with project agents. The super-agent does not intervene.",
|
|
322
|
+
live_title: "Chat with {agent}",
|
|
322
323
|
superagent_title: "Chat with {persona}",
|
|
323
324
|
superagent_subtitle: "Chat with {persona} — the APX super-agent. Can use tools (projects, tasks, mcps, agents).",
|
|
324
325
|
loaded_subtitle: "Loaded conversation with {slug}. Sending will append to this thread.",
|
|
@@ -327,7 +328,13 @@ export const en = {
|
|
|
327
328
|
placeholder: "Type something and press enter to send (shift+enter = new line)",
|
|
328
329
|
send: "Send",
|
|
329
330
|
stop: "Stop",
|
|
330
|
-
|
|
331
|
+
new_session: "New session",
|
|
332
|
+
delete: "Delete",
|
|
333
|
+
delete_confirm_title: "Delete chat",
|
|
334
|
+
delete_confirm_desc: "This can't be undone. This chat's history will be permanently deleted.",
|
|
335
|
+
deleted: "Chat deleted.",
|
|
336
|
+
meta_created: "Created {date} · {channel}",
|
|
337
|
+
meta_new: "New chat · {channel}",
|
|
331
338
|
copy: "copy",
|
|
332
339
|
copied: "Copied.",
|
|
333
340
|
stopped_marker: " [stopped]",
|
|
@@ -345,8 +352,7 @@ export const en = {
|
|
|
345
352
|
all_agents: "All agents",
|
|
346
353
|
empty: "No conversations yet. Start one from the right.",
|
|
347
354
|
count: "{n} total",
|
|
348
|
-
|
|
349
|
-
live_subtitle: "In-memory session",
|
|
355
|
+
pick_agent: "Pick an agent",
|
|
350
356
|
},
|
|
351
357
|
},
|
|
352
358
|
|
|
@@ -320,6 +320,7 @@ export const es = {
|
|
|
320
320
|
chat: {
|
|
321
321
|
title: "Chat con agente",
|
|
322
322
|
subtitle: "Chat directo con el agente del proyecto.",
|
|
323
|
+
live_title: "Chat con {agent}",
|
|
323
324
|
superagent_title: "Chat con {persona}",
|
|
324
325
|
superagent_subtitle: "Chat con {persona} — el super-agente APX. Puede usar tools (proyectos, tasks, mcps, agentes).",
|
|
325
326
|
loaded_subtitle: "Conversación cargada con {slug}. Lo que mandes se agrega a este chat.",
|
|
@@ -328,7 +329,13 @@ export const es = {
|
|
|
328
329
|
placeholder: "Escribí algo y enter para enviar (shift+enter = nueva línea)",
|
|
329
330
|
send: "Enviar",
|
|
330
331
|
stop: "Stop",
|
|
331
|
-
|
|
332
|
+
new_session: "Nueva sesión",
|
|
333
|
+
delete: "Borrar",
|
|
334
|
+
delete_confirm_title: "Borrar chat",
|
|
335
|
+
delete_confirm_desc: "Esta acción no se puede deshacer. Se elimina para siempre el historial de este chat.",
|
|
336
|
+
deleted: "Chat borrado.",
|
|
337
|
+
meta_created: "Creado {date} · {channel}",
|
|
338
|
+
meta_new: "Chat nuevo · {channel}",
|
|
332
339
|
copy: "copiar",
|
|
333
340
|
copied: "Copiado.",
|
|
334
341
|
stopped_marker: " [detenido]",
|
|
@@ -346,8 +353,7 @@ export const es = {
|
|
|
346
353
|
all_agents: "Todos los agentes",
|
|
347
354
|
empty: "No hay conversaciones todavía. Arrancá una desde la derecha.",
|
|
348
355
|
count: "{n} en total",
|
|
349
|
-
|
|
350
|
-
live_subtitle: "Sesión en memoria",
|
|
356
|
+
pick_agent: "Elegí un agente",
|
|
351
357
|
},
|
|
352
358
|
},
|
|
353
359
|
|
|
@@ -12,6 +12,12 @@ export const Conversations = {
|
|
|
12
12
|
http.get<ThreadListEntry[]>(`/projects/${pid}/super-agent/threads`),
|
|
13
13
|
thread: (pid: string, channel: string, id: string) =>
|
|
14
14
|
http.get<ThreadDetail>(`/projects/${pid}/super-agent/threads/${channel}/${id}`),
|
|
15
|
+
// Delete a persisted agent conversation (its `.md` file).
|
|
16
|
+
remove: (pid: string, slug: string, id: string) =>
|
|
17
|
+
http.del<{ ok: boolean }>(`/projects/${pid}/agents/${slug}/conversations/${id}`),
|
|
18
|
+
// Delete a super-agent channel thread (its channel+day ledger file).
|
|
19
|
+
removeThread: (pid: string, channel: string, id: string) =>
|
|
20
|
+
http.del<{ ok: boolean }>(`/projects/${pid}/super-agent/threads/${channel}/${id}`),
|
|
15
21
|
compact: (pid: string, slug: string, id?: string) =>
|
|
16
22
|
http.post<{ ok?: boolean }>(
|
|
17
23
|
id
|
|
@@ -141,7 +141,7 @@ export function ProjectScreen() {
|
|
|
141
141
|
onChange={onTabChange}
|
|
142
142
|
collapsed={collapsed}
|
|
143
143
|
onToggleCollapse={toggle}
|
|
144
|
-
contentClassName="w-full space-y-6
|
|
144
|
+
contentClassName="w-full space-y-6 py-6 pt-3 pr-6 pl-1"
|
|
145
145
|
testId={`project-tab-${active || "overview"}`}
|
|
146
146
|
>
|
|
147
147
|
<Routes>
|
|
@@ -101,7 +101,7 @@ export function SettingsScreen() {
|
|
|
101
101
|
onChange={(k) => navigate(k === "identity" ? "/settings" : `/settings/${pathFromTab(k as TabKey)}`)}
|
|
102
102
|
collapsed={collapsed}
|
|
103
103
|
onToggleCollapse={toggle}
|
|
104
|
-
contentClassName={`w-full ${WIDE_TABS.has(active) ? "" : "mx-auto max-w-3xl"} space-y-6
|
|
104
|
+
contentClassName={`w-full ${WIDE_TABS.has(active) ? "" : "mx-auto max-w-3xl"} space-y-6 py-6 pt-3 pr-6 pl-4`}
|
|
105
105
|
testId={`settings-tab-${active}`}
|
|
106
106
|
>
|
|
107
107
|
<Panel />
|