@agentprojectcontext/apx 1.32.2 → 1.33.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 +1 -1
- package/skills/apc-context/SKILL.md +2 -5
- package/src/core/agent/prompts/action-discipline.md +12 -5
- package/src/core/agent/prompts/channels/telegram.md +9 -5
- package/src/core/apc/parser.js +1 -1
- package/src/core/apc/scaffold.js +3 -1
- package/src/core/apc/skill-sync.js +3 -1
- package/src/core/engines/gemini.js +28 -11
- package/src/core/engines/index.js +11 -1
- package/src/core/stores/code-sessions.js +4 -1
- package/src/host/daemon/api/artifacts.js +25 -0
- package/src/host/daemon/api/code.js +14 -1
- package/src/host/daemon/api/engines.js +31 -1
- package/src/host/daemon/api/exec.js +17 -2
- package/src/host/daemon/plugins/telegram/dispatch.js +573 -0
- package/src/host/daemon/plugins/telegram/helpers.js +130 -0
- package/src/host/daemon/plugins/telegram/index.js +19 -694
- package/src/interfaces/web/dist/assets/index-Aaiw8BZN.css +1 -0
- package/src/interfaces/web/dist/assets/index-DPqtjDjh.js +602 -0
- package/src/interfaces/web/dist/assets/index-DPqtjDjh.js.map +1 -0
- package/src/interfaces/web/dist/index.html +2 -2
- package/src/interfaces/web/package-lock.json +3 -3
- package/src/interfaces/web/src/App.tsx +3 -1
- package/src/interfaces/web/src/components/ModelCombobox.tsx +42 -7
- package/src/interfaces/web/src/components/UiSelect.tsx +12 -2
- package/src/interfaces/web/src/components/code/CodeArtifactsTab.tsx +253 -111
- package/src/interfaces/web/src/components/code/CodeChangesTab.tsx +10 -8
- package/src/interfaces/web/src/components/code/CodeComposer.tsx +20 -17
- package/src/interfaces/web/src/components/code/CodeContextTab.tsx +43 -18
- package/src/interfaces/web/src/components/code/CodeFileTree.tsx +212 -0
- package/src/interfaces/web/src/components/code/CodeFileViewer.tsx +121 -0
- package/src/interfaces/web/src/components/code/CodeSessionList.tsx +30 -26
- package/src/interfaces/web/src/components/code/CodeSidePanel.tsx +23 -19
- package/src/interfaces/web/src/components/code/CodeTerminal.tsx +140 -0
- package/src/interfaces/web/src/components/common/TabLayout.tsx +3 -3
- package/src/interfaces/web/src/components/ui/chat-input.tsx +17 -6
- package/src/interfaces/web/src/hooks/useChat.ts +1 -0
- package/src/interfaces/web/src/hooks/useNavCollapseCtx.tsx +25 -1
- package/src/interfaces/web/src/i18n/es.ts +1 -1
- package/src/interfaces/web/src/lib/api/agents.ts +1 -1
- package/src/interfaces/web/src/lib/api/artifacts.ts +10 -0
- package/src/interfaces/web/src/lib/api/code.ts +4 -2
- package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +423 -79
- package/src/interfaces/web/src/screens/project/ChatTab.tsx +7 -10
- package/src/core/util/text-similarity.js +0 -52
- package/src/interfaces/web/dist/assets/index-34U_Mp1M.css +0 -1
- package/src/interfaces/web/dist/assets/index-BkybwwRn.js +0 -570
- package/src/interfaces/web/dist/assets/index-BkybwwRn.js.map +0 -1
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { useState, useRef, useEffect } from "react";
|
|
2
|
+
import { Terminal as TerminalIcon, Eraser, X } from "lucide-react";
|
|
3
|
+
import { cn } from "../../lib/cn";
|
|
4
|
+
import { Tip } from "../ui/tip";
|
|
5
|
+
import { http } from "../../lib/http";
|
|
6
|
+
|
|
7
|
+
interface Line {
|
|
8
|
+
type: "cmd" | "out" | "err";
|
|
9
|
+
text: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function CodeTerminal({
|
|
13
|
+
pid,
|
|
14
|
+
className,
|
|
15
|
+
initCmd,
|
|
16
|
+
onClose,
|
|
17
|
+
}: {
|
|
18
|
+
pid: string;
|
|
19
|
+
className?: string;
|
|
20
|
+
initCmd?: string;
|
|
21
|
+
/** Click handler for the header × button. Closes the terminal panel. */
|
|
22
|
+
onClose?: () => void;
|
|
23
|
+
}) {
|
|
24
|
+
const [lines, setLines] = useState<Line[]>([]);
|
|
25
|
+
const [input, setInput] = useState("");
|
|
26
|
+
const [busy, setBusy] = useState(false);
|
|
27
|
+
const [history, setHistory] = useState<string[]>([]);
|
|
28
|
+
const [histIdx, setHistIdx] = useState(-1);
|
|
29
|
+
const bottomRef = useRef<HTMLDivElement>(null);
|
|
30
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
bottomRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
34
|
+
}, [lines]);
|
|
35
|
+
|
|
36
|
+
// Pre-fill input when a command is pushed from parent (e.g. artifact run).
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (!initCmd) return;
|
|
39
|
+
setInput(initCmd);
|
|
40
|
+
setTimeout(() => inputRef.current?.focus(), 50);
|
|
41
|
+
}, [initCmd]);
|
|
42
|
+
|
|
43
|
+
const run = async (cmd: string) => {
|
|
44
|
+
const trimmed = cmd.trim();
|
|
45
|
+
if (!trimmed) return;
|
|
46
|
+
setHistory((h) => [trimmed, ...h.slice(0, 49)]);
|
|
47
|
+
setHistIdx(-1);
|
|
48
|
+
setLines((l) => [...l, { type: "cmd", text: `$ ${trimmed}` }]);
|
|
49
|
+
setBusy(true);
|
|
50
|
+
try {
|
|
51
|
+
const r = await http.post<{ ok: boolean; stdout: string; stderr: string; exit_code: number; cwd: string }>(
|
|
52
|
+
"/run",
|
|
53
|
+
{ cmd: trimmed, project: pid },
|
|
54
|
+
);
|
|
55
|
+
if (r.stdout) setLines((l) => [...l, { type: "out", text: r.stdout }]);
|
|
56
|
+
if (r.stderr) setLines((l) => [...l, { type: "err", text: r.stderr }]);
|
|
57
|
+
} catch (e) {
|
|
58
|
+
setLines((l) => [...l, { type: "err", text: String((e as Error).message) }]);
|
|
59
|
+
} finally {
|
|
60
|
+
setBusy(false);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const onKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
65
|
+
if (e.key === "Enter") {
|
|
66
|
+
void run(input);
|
|
67
|
+
setInput("");
|
|
68
|
+
} else if (e.key === "ArrowUp") {
|
|
69
|
+
e.preventDefault();
|
|
70
|
+
const next = Math.min(histIdx + 1, history.length - 1);
|
|
71
|
+
setHistIdx(next);
|
|
72
|
+
setInput(history[next] ?? "");
|
|
73
|
+
} else if (e.key === "ArrowDown") {
|
|
74
|
+
e.preventDefault();
|
|
75
|
+
const next = Math.max(histIdx - 1, -1);
|
|
76
|
+
setHistIdx(next);
|
|
77
|
+
setInput(next === -1 ? "" : (history[next] ?? ""));
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<div className={cn("flex h-full min-h-0 flex-col bg-card/60", className)} data-testid="code-terminal">
|
|
83
|
+
<div className="flex shrink-0 items-center gap-2 border-b border-border px-3 py-1">
|
|
84
|
+
<TerminalIcon className="size-3 text-muted-foreground" />
|
|
85
|
+
<span className="flex-1 text-[11px] text-muted-foreground">Terminal</span>
|
|
86
|
+
<Tip content="Limpiar">
|
|
87
|
+
<button
|
|
88
|
+
type="button"
|
|
89
|
+
onClick={() => setLines([])}
|
|
90
|
+
className="rounded p-0.5 text-muted-foreground hover:bg-accent hover:text-foreground"
|
|
91
|
+
>
|
|
92
|
+
<Eraser className="size-3" />
|
|
93
|
+
</button>
|
|
94
|
+
</Tip>
|
|
95
|
+
<Tip content="Cerrar terminal">
|
|
96
|
+
<button
|
|
97
|
+
type="button"
|
|
98
|
+
onClick={() => onClose?.()}
|
|
99
|
+
className="rounded p-0.5 text-muted-foreground hover:bg-accent hover:text-foreground"
|
|
100
|
+
>
|
|
101
|
+
<X className="size-3" />
|
|
102
|
+
</button>
|
|
103
|
+
</Tip>
|
|
104
|
+
</div>
|
|
105
|
+
<div
|
|
106
|
+
className="min-h-0 flex-1 overflow-y-auto px-3 py-1 font-mono text-[11px] leading-snug cursor-text"
|
|
107
|
+
onClick={() => inputRef.current?.focus()}
|
|
108
|
+
>
|
|
109
|
+
{lines.map((l, i) => (
|
|
110
|
+
<div
|
|
111
|
+
key={i}
|
|
112
|
+
className={cn(
|
|
113
|
+
"whitespace-pre-wrap break-all",
|
|
114
|
+
l.type === "cmd" && "text-emerald-400",
|
|
115
|
+
l.type === "err" && "text-rose-400",
|
|
116
|
+
l.type === "out" && "text-foreground/90",
|
|
117
|
+
)}
|
|
118
|
+
>
|
|
119
|
+
{l.text}
|
|
120
|
+
</div>
|
|
121
|
+
))}
|
|
122
|
+
<div ref={bottomRef} />
|
|
123
|
+
</div>
|
|
124
|
+
<div className="flex shrink-0 items-center border-t border-border px-3 py-1">
|
|
125
|
+
<span className="mr-2 text-[11px] text-emerald-400 font-mono">$</span>
|
|
126
|
+
<input
|
|
127
|
+
ref={inputRef}
|
|
128
|
+
value={input}
|
|
129
|
+
onChange={(e) => setInput(e.target.value)}
|
|
130
|
+
onKeyDown={onKey}
|
|
131
|
+
disabled={busy}
|
|
132
|
+
placeholder={busy ? "ejecutando…" : "comando…"}
|
|
133
|
+
className="flex-1 bg-transparent font-mono text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 disabled:opacity-50"
|
|
134
|
+
spellCheck={false}
|
|
135
|
+
autoComplete="off"
|
|
136
|
+
/>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
@@ -35,13 +35,13 @@ export function TabLayout({
|
|
|
35
35
|
return (
|
|
36
36
|
<div className="flex h-full">
|
|
37
37
|
<TabNav sections={sections} active={active} onChange={onChange} collapsed={collapsed} />
|
|
38
|
-
<div className="flex min-w-0 flex-1 flex-col overflow-
|
|
38
|
+
<div className="flex min-w-0 flex-1 flex-col overflow-hidden">
|
|
39
39
|
{actions ? (
|
|
40
|
-
<div className="flex items-center justify-end gap-2 px-6 pt-3">
|
|
40
|
+
<div className="flex shrink-0 items-center justify-end gap-2 px-6 pt-3">
|
|
41
41
|
{actions}
|
|
42
42
|
</div>
|
|
43
43
|
) : null}
|
|
44
|
-
<div className={cn(contentClassName)} data-testid={testId}>
|
|
44
|
+
<div className={cn("flex-1 min-h-0 overflow-y-auto", contentClassName)} data-testid={testId}>
|
|
45
45
|
{children}
|
|
46
46
|
</div>
|
|
47
47
|
</div>
|
|
@@ -49,12 +49,23 @@ export function ChatInput({
|
|
|
49
49
|
React.useLayoutEffect(() => {
|
|
50
50
|
const el = ref.current
|
|
51
51
|
if (!el) return
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
const resize = () => {
|
|
53
|
+
el.style.height = "auto"
|
|
54
|
+
// Force a reflow before reading scrollHeight so the "auto" reset takes
|
|
55
|
+
// effect — without this, scrollHeight can return the stale prior height.
|
|
56
|
+
void el.offsetHeight
|
|
57
|
+
const lineHeight = parseFloat(getComputedStyle(el).lineHeight) || 20
|
|
58
|
+
const min = lineHeight * minRows
|
|
59
|
+
const max = lineHeight * maxRows
|
|
60
|
+
el.style.height = `${Math.min(Math.max(el.scrollHeight, min), max)}px`
|
|
61
|
+
el.style.overflowY = el.scrollHeight > max ? "auto" : "hidden"
|
|
62
|
+
}
|
|
63
|
+
resize()
|
|
64
|
+
// Re-run after the next paint to catch cases where the parent layout
|
|
65
|
+
// wasn't ready on the initial sync pass (e.g. inside a resizable panel
|
|
66
|
+
// that's just been mounted).
|
|
67
|
+
const raf = requestAnimationFrame(resize)
|
|
68
|
+
return () => cancelAnimationFrame(raf)
|
|
58
69
|
}, [value, minRows, maxRows])
|
|
59
70
|
|
|
60
71
|
const canSend = value.trim().length > 0 && !disabled
|
|
@@ -263,6 +263,7 @@ export function useChat(pid: string, onError?: (msg: string) => void): UseChatRe
|
|
|
263
263
|
const out = await Agents.chat(pid, opts.agentSlug, {
|
|
264
264
|
prompt: trimmed,
|
|
265
265
|
conversation_id: convoRef.current,
|
|
266
|
+
model: opts.model || undefined,
|
|
266
267
|
});
|
|
267
268
|
convoRef.current = out.conversation_id;
|
|
268
269
|
patchLast((m) => ({
|
|
@@ -12,17 +12,27 @@ const CollapseSetCtx = createContext<((s: CollapseState) => void) | null>(null);
|
|
|
12
12
|
const LabelReadCtx = createContext<string>("");
|
|
13
13
|
const LabelSetCtx = createContext<((s: string) => void) | null>(null);
|
|
14
14
|
|
|
15
|
+
// ── Page actions (buttons screens can inject into the TopBar) ─────────────────
|
|
16
|
+
|
|
17
|
+
const ActionsReadCtx = createContext<ReactNode>(null);
|
|
18
|
+
const ActionsSetCtx = createContext<((a: ReactNode) => void) | null>(null);
|
|
19
|
+
|
|
15
20
|
// ── Combined provider (one wrapper in Shell) ──────────────────────────────────
|
|
16
21
|
|
|
17
22
|
export function NavCollapseProvider({ children }: { children: ReactNode }) {
|
|
18
23
|
const [collapse, setCollapse] = useState<CollapseState>(null);
|
|
19
24
|
const [label, setLabel] = useState("");
|
|
25
|
+
const [actions, setActions] = useState<ReactNode>(null);
|
|
20
26
|
return (
|
|
21
27
|
<CollapseSetCtx.Provider value={setCollapse}>
|
|
22
28
|
<CollapseReadCtx.Provider value={collapse}>
|
|
23
29
|
<LabelSetCtx.Provider value={setLabel}>
|
|
24
30
|
<LabelReadCtx.Provider value={label}>
|
|
25
|
-
{
|
|
31
|
+
<ActionsSetCtx.Provider value={setActions}>
|
|
32
|
+
<ActionsReadCtx.Provider value={actions}>
|
|
33
|
+
{children}
|
|
34
|
+
</ActionsReadCtx.Provider>
|
|
35
|
+
</ActionsSetCtx.Provider>
|
|
26
36
|
</LabelReadCtx.Provider>
|
|
27
37
|
</LabelSetCtx.Provider>
|
|
28
38
|
</CollapseReadCtx.Provider>
|
|
@@ -57,3 +67,17 @@ export function useSetPageLabel(label: string) {
|
|
|
57
67
|
return () => set?.("");
|
|
58
68
|
}, [label, set]);
|
|
59
69
|
}
|
|
70
|
+
|
|
71
|
+
// ── Page actions hooks ────────────────────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
export function usePageActions() {
|
|
74
|
+
return useContext(ActionsReadCtx);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function useSetPageActions(actions: ReactNode) {
|
|
78
|
+
const set = useContext(ActionsSetCtx);
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
set?.(actions);
|
|
81
|
+
return () => set?.(null);
|
|
82
|
+
}, [actions, set]);
|
|
83
|
+
}
|
|
@@ -286,7 +286,7 @@ export const es = {
|
|
|
286
286
|
|
|
287
287
|
chat: {
|
|
288
288
|
title: "Chat con agente",
|
|
289
|
-
subtitle: "
|
|
289
|
+
subtitle: "Chat directo con el agente del proyecto.",
|
|
290
290
|
superagent_title: "Chat con {persona}",
|
|
291
291
|
superagent_subtitle: "Chat con {persona} — el super-agente APX. Puede usar tools (proyectos, tasks, mcps, agentes).",
|
|
292
292
|
empty: "Mandá un mensaje para arrancar la conversación.",
|
|
@@ -10,7 +10,7 @@ export const Agents = {
|
|
|
10
10
|
http.patch<AgentEntry>(`/projects/${pid}/agents/${encodeURIComponent(slug)}`, body),
|
|
11
11
|
remove: (pid: string, slug: string) =>
|
|
12
12
|
http.del<{ ok: boolean }>(`/projects/${pid}/agents/${encodeURIComponent(slug)}`),
|
|
13
|
-
chat: (pid: string, slug: string, body: { prompt: string; conversation_id?: string }) =>
|
|
13
|
+
chat: (pid: string, slug: string, body: { prompt: string; conversation_id?: string; model?: string }) =>
|
|
14
14
|
http.post<{ conversation_id: string; text: string; usage?: unknown; engine: string }>(
|
|
15
15
|
`/projects/${pid}/agents/${encodeURIComponent(slug)}/chat`,
|
|
16
16
|
body,
|
|
@@ -44,4 +44,14 @@ export const Artifacts = {
|
|
|
44
44
|
http.del<void>(
|
|
45
45
|
`/projects/${encodeURIComponent(pid)}/artifacts/${encodeURIComponent(name)}`,
|
|
46
46
|
),
|
|
47
|
+
write: (pid: string, name: string, content: string) =>
|
|
48
|
+
http.patch<{ ok: boolean; name: string }>(
|
|
49
|
+
`/projects/${encodeURIComponent(pid)}/artifacts/${encodeURIComponent(name)}`,
|
|
50
|
+
{ content },
|
|
51
|
+
),
|
|
52
|
+
rename: (pid: string, name: string, newName: string) =>
|
|
53
|
+
http.patch<{ ok: boolean; name: string }>(
|
|
54
|
+
`/projects/${encodeURIComponent(pid)}/artifacts/${encodeURIComponent(name)}`,
|
|
55
|
+
{ newName },
|
|
56
|
+
),
|
|
47
57
|
};
|
|
@@ -39,6 +39,7 @@ export interface CodeSessionRow {
|
|
|
39
39
|
title: string;
|
|
40
40
|
mode: CodeMode;
|
|
41
41
|
model: string | null;
|
|
42
|
+
agentSlug: string | null;
|
|
42
43
|
createdAt: string;
|
|
43
44
|
updatedAt: string;
|
|
44
45
|
messageCount: number;
|
|
@@ -53,6 +54,7 @@ export interface CodeSession {
|
|
|
53
54
|
createdAt: string;
|
|
54
55
|
updatedAt: string;
|
|
55
56
|
model: string | null;
|
|
57
|
+
agentSlug: string | null;
|
|
56
58
|
mode: CodeMode;
|
|
57
59
|
git: { baselineCommit: string | null; baselineTree: string } | null;
|
|
58
60
|
messages: CodeTurn[];
|
|
@@ -89,13 +91,13 @@ export const Code = {
|
|
|
89
91
|
|
|
90
92
|
create: (
|
|
91
93
|
pid: string | number,
|
|
92
|
-
body: { title?: string; model?: string | null; mode?: CodeMode } = {},
|
|
94
|
+
body: { title?: string; model?: string | null; mode?: CodeMode; agentSlug?: string | null } = {},
|
|
93
95
|
) => http.post<CodeSession>(base(pid), body),
|
|
94
96
|
|
|
95
97
|
update: (
|
|
96
98
|
pid: string | number,
|
|
97
99
|
sid: string,
|
|
98
|
-
patch: { title?: string; model?: string | null; mode?: CodeMode },
|
|
100
|
+
patch: { title?: string; model?: string | null; mode?: CodeMode; agentSlug?: string | null },
|
|
99
101
|
) => http.patch<CodeSession>(`${base(pid)}/${sid}`, patch),
|
|
100
102
|
|
|
101
103
|
remove: (pid: string | number, sid: string) =>
|