@agentprojectcontext/apx 1.31.2 → 1.32.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.
Files changed (32) hide show
  1. package/package.json +1 -1
  2. package/src/core/agent/constants.js +5 -0
  3. package/src/core/agent/run-agent.js +29 -1
  4. package/src/host/daemon/api/artifacts.js +117 -0
  5. package/src/host/daemon/api/code.js +12 -0
  6. package/src/host/daemon/plugins/desktop.js +34 -0
  7. package/src/host/daemon/plugins/telegram-ask.js +309 -0
  8. package/src/host/daemon/plugins/telegram.js +330 -2
  9. package/src/host/daemon/super-agent-tools/tools/ask-questions.js +96 -13
  10. package/src/interfaces/cli/commands/artifact.js +99 -0
  11. package/src/interfaces/cli/index.js +4 -0
  12. package/src/interfaces/cli/terminal-chat/renderer.js +22 -2
  13. package/src/interfaces/web/dist/assets/index-63P_ji1a.js +571 -0
  14. package/src/interfaces/web/dist/assets/index-63P_ji1a.js.map +1 -0
  15. package/src/interfaces/web/dist/assets/index-DLWy6dYz.css +1 -0
  16. package/src/interfaces/web/dist/index.html +2 -2
  17. package/src/interfaces/web/package-lock.json +6 -6
  18. package/src/interfaces/web/src/components/chat/AskQuestionsCard.tsx +72 -0
  19. package/src/interfaces/web/src/components/chat/InlineAskPanel.tsx +399 -0
  20. package/src/interfaces/web/src/components/chat/MessageBubble.tsx +16 -3
  21. package/src/interfaces/web/src/components/chat/MessageList.tsx +2 -1
  22. package/src/interfaces/web/src/components/code/CodeArtifactsTab.tsx +230 -0
  23. package/src/interfaces/web/src/components/code/CodeSidePanel.tsx +12 -4
  24. package/src/interfaces/web/src/i18n/en.ts +20 -0
  25. package/src/interfaces/web/src/i18n/es.ts +20 -0
  26. package/src/interfaces/web/src/lib/api/artifacts.ts +47 -0
  27. package/src/interfaces/web/src/lib/api.ts +1 -0
  28. package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +23 -2
  29. package/src/interfaces/web/src/screens/project/ChatTab.tsx +15 -0
  30. package/src/interfaces/web/dist/assets/index-BDUsA6L6.css +0 -1
  31. package/src/interfaces/web/dist/assets/index-BV615I9p.js +0 -548
  32. package/src/interfaces/web/dist/assets/index-BV615I9p.js.map +0 -1
@@ -1,20 +1,22 @@
1
- import { Gauge, GitCompare } from "lucide-react";
1
+ import { Gauge, GitCompare, Package } from "lucide-react";
2
2
  import { Tabs, TabsList, TabsTrigger, TabsContent } from "../ui/tabs";
3
3
  import { t } from "../../i18n";
4
4
  import { CodeContextTab } from "./CodeContextTab";
5
5
  import { CodeChangesTab } from "./CodeChangesTab";
6
+ import { CodeArtifactsTab } from "./CodeArtifactsTab";
6
7
  import type { CodeChanges, CodeTurn } from "../../lib/api/code";
7
8
 
8
9
  interface Props {
10
+ pid: string;
9
11
  turns: CodeTurn[];
10
12
  changes: CodeChanges | undefined;
11
13
  changesLoading: boolean;
12
14
  onRefreshChanges: () => void;
13
15
  }
14
16
 
15
- // Right-hand panel: Context (token metrics + breakdown) and Changes (diffs vs
16
- // the session's git baseline), mirroring OpenCode's session side panel.
17
- export function CodeSidePanel({ turns, changes, changesLoading, onRefreshChanges }: Props) {
17
+ // Right-hand panel: Context (token metrics), Changes (diffs vs the session's
18
+ // git baseline), and Artifacts (managed files under <project>/artifacts/).
19
+ export function CodeSidePanel({ pid, turns, changes, changesLoading, onRefreshChanges }: Props) {
18
20
  const changeCount = changes?.files.length || 0;
19
21
  return (
20
22
  <Tabs defaultValue="context" className="flex h-full flex-col gap-0" data-testid="code-side-panel">
@@ -31,6 +33,9 @@ export function CodeSidePanel({ turns, changes, changesLoading, onRefreshChanges
31
33
  </span>
32
34
  )}
33
35
  </TabsTrigger>
36
+ <TabsTrigger value="artifacts" className="flex-1">
37
+ <Package className="size-3.5" /> {t("code_module.tab_artifacts")}
38
+ </TabsTrigger>
34
39
  </TabsList>
35
40
  </div>
36
41
  <TabsContent value="context" className="min-h-0 flex-1 overflow-y-auto">
@@ -39,6 +44,9 @@ export function CodeSidePanel({ turns, changes, changesLoading, onRefreshChanges
39
44
  <TabsContent value="changes" className="min-h-0 flex-1 overflow-hidden">
40
45
  <CodeChangesTab changes={changes} loading={changesLoading} onRefresh={onRefreshChanges} />
41
46
  </TabsContent>
47
+ <TabsContent value="artifacts" className="min-h-0 flex-1 overflow-hidden">
48
+ <CodeArtifactsTab pid={pid} />
49
+ </TabsContent>
42
50
  </Tabs>
43
51
  );
44
52
  }
@@ -726,6 +726,18 @@ export const en = {
726
726
  message: "That route does not exist.",
727
727
  },
728
728
 
729
+ ask_panel: {
730
+ answers_header: "Answers",
731
+ other: "Other",
732
+ other_placeholder: "Write your own answer here",
733
+ text_placeholder: "Type your answer…",
734
+ back: "Back",
735
+ skip: "Skip",
736
+ next: "Next",
737
+ submit: "Send",
738
+ status_waiting: "Waiting for your answer…",
739
+ status_received: "Answers received",
740
+ },
729
741
  code_module: {
730
742
  title: "Code",
731
743
  badge: "super-agent",
@@ -747,6 +759,14 @@ export const en = {
747
759
  mode_plan_hint: "Plan — read-only, proposes changes without touching files",
748
760
  tab_context: "Context",
749
761
  tab_changes: "Changes",
762
+ tab_artifacts: "Artifacts",
763
+ artifacts_none: "No artifacts yet. Ask the agent to create a script under `artifacts/<name>`.",
764
+ artifacts_count: "{n} artifact(s)",
765
+ artifacts_copy_path: "Copy path",
766
+ artifacts_run: "Run",
767
+ artifacts_run_hint: "Run it from your terminal:",
768
+ artifacts_delete: "Delete",
769
+ artifacts_delete_confirm: "Delete this artifact? The file will be removed from disk.",
750
770
  ctx_model: "Model",
751
771
  ctx_tokens: "Tokens",
752
772
  ctx_input: "Input",
@@ -727,6 +727,18 @@ export const es = {
727
727
  message: "Esa ruta no existe.",
728
728
  },
729
729
 
730
+ ask_panel: {
731
+ answers_header: "Respuestas",
732
+ other: "Otro",
733
+ other_placeholder: "Escribí tu propia respuesta acá",
734
+ text_placeholder: "Escribí tu respuesta…",
735
+ back: "Atrás",
736
+ skip: "Omitir",
737
+ next: "Siguiente",
738
+ submit: "Enviar",
739
+ status_waiting: "Esperando respuesta…",
740
+ status_received: "Respuestas recibidas",
741
+ },
730
742
  code_module: {
731
743
  title: "Code",
732
744
  badge: "super-agent",
@@ -748,6 +760,14 @@ export const es = {
748
760
  mode_plan_hint: "Plan — solo lectura, propone cambios sin tocar archivos",
749
761
  tab_context: "Contexto",
750
762
  tab_changes: "Cambios",
763
+ tab_artifacts: "Artifacts",
764
+ artifacts_none: "Todavía no hay artifacts. Pedile al agente que cree un script en `artifacts/<nombre>`.",
765
+ artifacts_count: "{n} artifact(s)",
766
+ artifacts_copy_path: "Copiar path",
767
+ artifacts_run: "Run",
768
+ artifacts_run_hint: "Para ejecutarlo desde la terminal:",
769
+ artifacts_delete: "Eliminar",
770
+ artifacts_delete_confirm: "¿Eliminar este artifact? El archivo se borra del disco.",
751
771
  ctx_model: "Modelo",
752
772
  ctx_tokens: "Tokens",
753
773
  ctx_input: "Entrada",
@@ -0,0 +1,47 @@
1
+ import { http } from "../http";
2
+
3
+ export interface ArtifactEntry {
4
+ name: string;
5
+ path: string;
6
+ size: number;
7
+ modified: string;
8
+ }
9
+
10
+ export interface ArtifactContent {
11
+ name: string;
12
+ path: string;
13
+ content: string;
14
+ }
15
+
16
+ // Shape of POST /projects/:pid/artifacts/:name/run. On success the daemon
17
+ // returns the captured stdout/stderr and exit metadata; on 4xx an error
18
+ // payload is thrown by the http client instead.
19
+ export interface ArtifactRunResult {
20
+ ok: boolean;
21
+ exitCode?: number | null;
22
+ signal?: string | null;
23
+ timedOut?: boolean;
24
+ truncated?: boolean;
25
+ stdout?: string;
26
+ stderr?: string;
27
+ durationMs?: number;
28
+ error?: string;
29
+ }
30
+
31
+ export const Artifacts = {
32
+ list: (pid: string) =>
33
+ http.get<ArtifactEntry[]>(`/projects/${encodeURIComponent(pid)}/artifacts`),
34
+ read: (pid: string, name: string) =>
35
+ http.get<ArtifactContent>(
36
+ `/projects/${encodeURIComponent(pid)}/artifacts/${encodeURIComponent(name)}`,
37
+ ),
38
+ run: (pid: string, name: string, args: string[] = []) =>
39
+ http.post<ArtifactRunResult>(
40
+ `/projects/${encodeURIComponent(pid)}/artifacts/${encodeURIComponent(name)}/run`,
41
+ { args },
42
+ ),
43
+ remove: (pid: string, name: string) =>
44
+ http.del<void>(
45
+ `/projects/${encodeURIComponent(pid)}/artifacts/${encodeURIComponent(name)}`,
46
+ ),
47
+ };
@@ -21,6 +21,7 @@ export * from "./api/filesystem";
21
21
  export * from "./api/voice";
22
22
  export * from "./api/deck";
23
23
  export * from "./api/code";
24
+ export * from "./api/artifacts";
24
25
 
25
26
  // Re-export the daemon types so older imports of "../lib/api" still work.
26
27
  export type {
@@ -8,6 +8,7 @@ import { CodeProjectPicker } from "../../components/code/CodeProjectPicker";
8
8
  import { CodeSessionList } from "../../components/code/CodeSessionList";
9
9
  import { CodeComposer } from "../../components/code/CodeComposer";
10
10
  import { CodeSidePanel } from "../../components/code/CodeSidePanel";
11
+ import { InlineAskPanel, pendingAskQuestions } from "../../components/chat/InlineAskPanel";
11
12
  import { useToast } from "../../components/Toast";
12
13
  import { t } from "../../i18n";
13
14
  import { applyStreamEvent, textOf, type ChatMsg } from "../../hooks/useChat";
@@ -151,8 +152,8 @@ export function CodeScreen() {
151
152
  return copy;
152
153
  });
153
154
 
154
- const send = async () => {
155
- const prompt = draft.trim();
155
+ const send = async (overridePrompt?: string) => {
156
+ const prompt = (overridePrompt ?? draft).trim();
156
157
  if (!prompt || busy || !pid || !sid) return;
157
158
  const now = new Date().toISOString();
158
159
  setMsgs((curr) => [
@@ -209,6 +210,16 @@ export function CodeScreen() {
209
210
  const hasProjects = !projects.isLoading && projectList.length > 0;
210
211
  const turns: CodeTurn[] = useMemo(() => msgs as unknown as CodeTurn[], [msgs]);
211
212
 
213
+ // Detect unanswered ask_questions in the last assistant turn. Local "dismissed"
214
+ // ref keys off the turn id so the panel re-appears for a fresh batch.
215
+ const [dismissedKey, setDismissedKey] = useState<string | null>(null);
216
+ const pending = !busy ? pendingAskQuestions(msgs) : null;
217
+ const askVisible = pending && pending.turnKey !== dismissedKey;
218
+
219
+ const submitAnswers = (compiled: string) => {
220
+ void send(compiled);
221
+ };
222
+
212
223
  return (
213
224
  <div className="flex h-full min-h-0 flex-col overflow-hidden p-4" data-testid="screen-code">
214
225
  <header className="mb-3 flex items-center justify-between gap-3">
@@ -263,6 +274,15 @@ export function CodeScreen() {
263
274
  </div>
264
275
  )}
265
276
  </div>
277
+ {askVisible && pending && (
278
+ <InlineAskPanel
279
+ turnKey={pending.turnKey}
280
+ questions={pending.questions}
281
+ onSubmit={submitAnswers}
282
+ onDismiss={() => setDismissedKey(pending.turnKey)}
283
+ disabled={busy}
284
+ />
285
+ )}
266
286
  <div className="border-t border-border bg-card/60 p-3" data-testid="code-input">
267
287
  <CodeComposer
268
288
  value={draft}
@@ -282,6 +302,7 @@ export function CodeScreen() {
282
302
  {/* Right: context + changes */}
283
303
  <aside className="hidden w-80 shrink-0 flex-col border-l border-border lg:flex">
284
304
  <CodeSidePanel
305
+ pid={pid}
285
306
  turns={turns}
286
307
  changes={changes.data}
287
308
  changesLoading={changes.isLoading}
@@ -8,6 +8,7 @@ import { UiSelect } from "../../components/UiSelect";
8
8
  import { Composer } from "../../components/chat/Composer";
9
9
  import { MessageList } from "../../components/chat/MessageList";
10
10
  import { ContextBar } from "../../components/chat/ContextBar";
11
+ import { InlineAskPanel, pendingAskQuestions } from "../../components/chat/InlineAskPanel";
11
12
  import { useChat } from "../../hooks/useChat";
12
13
  import { useToast } from "../../components/Toast";
13
14
  import { t } from "../../i18n";
@@ -25,6 +26,7 @@ export function ChatTab({ pid }: { pid: string }) {
25
26
  const [activeSlug, setActiveSlug] = useState(params.get("agent") || "");
26
27
  const [creating, setCreating] = useState(false);
27
28
  const [model, setModel] = useState("");
29
+ const [dismissedAskKey, setDismissedAskKey] = useState<string | null>(null);
28
30
  const { msgs, send: sendChat, stop, clear, streaming } = useChat(pid, (m) => toast.error(m));
29
31
 
30
32
  const agentList = agents.data || [];
@@ -117,6 +119,19 @@ export function ChatTab({ pid }: { pid: string }) {
117
119
  )}
118
120
  </div>
119
121
  <ContextBar msgs={msgs} />
122
+ {(() => {
123
+ const pending = !streaming ? pendingAskQuestions(msgs) : null;
124
+ if (!pending || pending.turnKey === dismissedAskKey) return null;
125
+ return (
126
+ <InlineAskPanel
127
+ turnKey={pending.turnKey}
128
+ questions={pending.questions}
129
+ onSubmit={(compiled) => void send(compiled)}
130
+ onDismiss={() => setDismissedAskKey(pending.turnKey)}
131
+ disabled={streaming}
132
+ />
133
+ );
134
+ })()}
120
135
  <Composer
121
136
  onSend={send}
122
137
  onStop={stop}