@agentprojectcontext/apx 1.32.0 → 1.33.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 (230) hide show
  1. package/package.json +6 -1
  2. package/skills/apc-context/SKILL.md +5 -2
  3. package/skills/apx/SKILL.md +3 -3
  4. package/skills/apx-agency-agents/SKILL.md +5 -5
  5. package/skills/apx-agent/SKILL.md +7 -7
  6. package/skills/apx-mcp/SKILL.md +6 -4
  7. package/skills/apx-mcp-builder/SKILL.md +4 -7
  8. package/skills/apx-project/SKILL.md +4 -5
  9. package/skills/apx-routine/SKILL.md +14 -12
  10. package/skills/apx-runtime/SKILL.md +5 -3
  11. package/skills/apx-sessions/SKILL.md +5 -5
  12. package/skills/apx-skill-builder/SKILL.md +10 -6
  13. package/skills/apx-task/SKILL.md +8 -8
  14. package/skills/apx-telegram/SKILL.md +23 -7
  15. package/skills/apx-voice/SKILL.md +8 -6
  16. package/src/core/{agent-system.js → agent/build-agent-system.js} +10 -12
  17. package/src/core/agent/index.js +0 -2
  18. package/src/core/{agent-memory.js → agent/memory.js} +2 -2
  19. package/src/core/agent/model-router.js +21 -43
  20. package/src/core/agent/prompt-builder.js +17 -63
  21. package/src/core/agent/prompts/action-discipline.md +24 -0
  22. package/src/core/agent/prompts/channels/code.md +8 -12
  23. package/src/core/agent/prompts/channels/desktop.md +6 -4
  24. package/src/core/agent/prompts/channels/routine.md +10 -1
  25. package/src/core/agent/prompts/channels/telegram.md +10 -1
  26. package/src/core/agent/prompts/channels/web_code.md +20 -0
  27. package/src/core/agent/prompts/modes/voice.md +2 -2
  28. package/src/core/agent/prompts/super-agent-base.md +2 -2
  29. package/src/core/agent/run-agent.js +37 -35
  30. package/src/core/agent/runtime-bridge.js +42 -0
  31. package/src/core/agent/self-memory.js +19 -9
  32. package/src/core/agent/skills/catalog.js +65 -0
  33. package/src/core/agent/skills/index.js +6 -0
  34. package/src/{host/daemon/skills-loader.js → core/agent/skills/loader.js} +3 -3
  35. package/src/core/agent/skills/rag.js +91 -0
  36. package/src/core/agent/skills/trigger.js +71 -0
  37. package/src/{host/daemon → core/agent}/super-agent.js +5 -5
  38. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/add-project.js +3 -4
  39. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/call-agent.js +2 -2
  40. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/call-mcp.js +1 -2
  41. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/call-runtime.js +10 -11
  42. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/create-task.js +1 -1
  43. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/discover-tools.js +1 -1
  44. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/edit-file.js +1 -2
  45. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/import-agent.js +4 -5
  46. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-agents.js +1 -1
  47. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-skills.js +7 -2
  48. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-tasks.js +1 -1
  49. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-vault-agents.js +1 -1
  50. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/load-skill.js +1 -1
  51. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/read-agent-memory.js +1 -1
  52. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/read-self-memory.js +1 -1
  53. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/remember.js +1 -1
  54. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/run-shell.js +1 -2
  55. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/search-messages.js +1 -1
  56. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/search-sessions.js +1 -1
  57. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/send-telegram.js +0 -2
  58. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/set-identity.js +1 -3
  59. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/set-permission-mode.js +1 -3
  60. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/tail-messages.js +1 -1
  61. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/transcribe-audio.js +1 -1
  62. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/write-file.js +1 -2
  63. package/src/core/agent/tools/helpers.js +74 -0
  64. package/src/{host/daemon/super-agent-tools → core/agent/tools}/registry-bridge.js +3 -3
  65. package/src/{host/daemon/super-agent-tools/index.js → core/agent/tools/registry.js} +31 -32
  66. package/src/core/apc/agents-vault.js +37 -0
  67. package/src/core/{scaffold.js → apc/scaffold.js} +4 -5
  68. package/src/core/{config.js → config/index.js} +21 -27
  69. package/src/core/config/paths.js +32 -0
  70. package/src/core/constants/actors.js +8 -0
  71. package/src/core/constants/channels.js +19 -0
  72. package/src/core/constants/index.js +5 -0
  73. package/src/core/constants/permissions.js +17 -0
  74. package/src/core/constants/roles.js +9 -0
  75. package/src/core/engines/_streaming.js +63 -0
  76. package/src/core/engines/anthropic.js +11 -22
  77. package/src/core/engines/ollama.js +7 -16
  78. package/src/core/identity/index.js +8 -0
  79. package/src/core/{identity.js → identity/self.js} +5 -5
  80. package/src/core/{telegram-identity.js → identity/telegram.js} +1 -1
  81. package/src/core/logging.js +1 -1
  82. package/src/core/mascot.js +1 -1
  83. package/src/core/memory/active-threads.js +10 -10
  84. package/src/core/memory/broker.js +9 -9
  85. package/src/core/memory/compactor.js +2 -2
  86. package/src/core/memory/index.js +2 -2
  87. package/src/core/memory/indexer.js +1 -1
  88. package/src/core/{code-sessions-store.js → stores/code-sessions.js} +7 -8
  89. package/src/core/{messages-store.js → stores/messages.js} +6 -4
  90. package/src/core/stores/routine-memory.js +71 -0
  91. package/src/core/{routines-store.js → stores/routines.js} +1 -3
  92. package/src/core/stores/runtime-sessions.js +99 -0
  93. package/src/core/{tasks-store.js → stores/tasks.js} +3 -8
  94. package/src/core/update-check.js +1 -1
  95. package/src/core/util/ids.js +14 -0
  96. package/src/core/util/index.js +2 -0
  97. package/src/core/util/time.js +9 -0
  98. package/src/core/voice/tts.js +1 -1
  99. package/src/host/daemon/api/admin-config.js +4 -3
  100. package/src/host/daemon/api/admin.js +1 -1
  101. package/src/host/daemon/api/agents.js +4 -25
  102. package/src/host/daemon/api/artifacts.js +26 -1
  103. package/src/host/daemon/api/code.js +62 -17
  104. package/src/host/daemon/api/confirm.js +1 -1
  105. package/src/host/daemon/api/connections.js +2 -2
  106. package/src/host/daemon/api/conversations.js +2 -2
  107. package/src/host/daemon/api/deck.js +1 -1
  108. package/src/host/daemon/api/desktop.js +1 -1
  109. package/src/host/daemon/api/embeddings.js +4 -4
  110. package/src/host/daemon/api/engines.js +2 -2
  111. package/src/host/daemon/api/exec.js +20 -5
  112. package/src/host/daemon/api/identity.js +1 -1
  113. package/src/host/daemon/api/mcps.js +1 -1
  114. package/src/host/daemon/api/messages.js +1 -1
  115. package/src/host/daemon/api/runtimes.js +9 -8
  116. package/src/host/daemon/api/sessions-search.js +1 -1
  117. package/src/host/daemon/api/sessions.js +2 -2
  118. package/src/host/daemon/api/shared.js +5 -4
  119. package/src/host/daemon/api/skills.js +30 -0
  120. package/src/host/daemon/api/super-agent.js +29 -9
  121. package/src/host/daemon/api/tasks.js +2 -2
  122. package/src/host/daemon/api/telegram.js +1 -1
  123. package/src/host/daemon/api/tools.js +6 -6
  124. package/src/host/daemon/api/tts.js +2 -2
  125. package/src/host/daemon/api/voice.js +14 -12
  126. package/src/host/daemon/api.js +2 -0
  127. package/src/host/daemon/compact.js +1 -1
  128. package/src/host/daemon/db.js +4 -4
  129. package/src/host/daemon/desktop-ws.js +1 -1
  130. package/src/host/daemon/index.js +4 -4
  131. package/src/host/daemon/plugins/{desktop.js → desktop/index.js} +11 -6
  132. package/src/host/daemon/plugins/index.js +2 -2
  133. package/src/host/daemon/plugins/{telegram.js → telegram/index.js} +52 -193
  134. package/src/host/daemon/plugins/telegram/media.js +162 -0
  135. package/src/host/daemon/projects-helpers.js +54 -0
  136. package/src/host/daemon/routines.js +28 -12
  137. package/src/host/daemon/smoke.js +2 -2
  138. package/src/host/daemon/token-store.js +1 -1
  139. package/src/host/daemon/transcription.js +2 -2
  140. package/src/host/daemon/wakeup.js +2 -2
  141. package/src/interfaces/cli/commands/agent.js +3 -3
  142. package/src/interfaces/cli/commands/command.js +1 -1
  143. package/src/interfaces/cli/commands/config.js +3 -2
  144. package/src/interfaces/cli/commands/desktop.js +1 -1
  145. package/src/interfaces/cli/commands/exec.js +2 -1
  146. package/src/interfaces/cli/commands/identity.js +2 -2
  147. package/src/interfaces/cli/commands/init.js +1 -1
  148. package/src/interfaces/cli/commands/mcp.js +1 -1
  149. package/src/interfaces/cli/commands/memory.js +2 -2
  150. package/src/interfaces/cli/commands/model.js +16 -6
  151. package/src/interfaces/cli/commands/project.js +1 -1
  152. package/src/interfaces/cli/commands/routine.js +58 -0
  153. package/src/interfaces/cli/commands/search.js +1 -1
  154. package/src/interfaces/cli/commands/session.js +4 -4
  155. package/src/interfaces/cli/commands/setup.js +4 -3
  156. package/src/interfaces/cli/commands/skills.js +25 -4
  157. package/src/interfaces/cli/commands/status.js +1 -1
  158. package/src/interfaces/cli/commands/sys.js +11 -4
  159. package/src/interfaces/cli/commands/update.js +1 -1
  160. package/src/interfaces/cli/index.js +4 -4
  161. package/src/interfaces/cli/postinstall.js +2 -2
  162. package/src/interfaces/mcp-server/index.js +1 -1
  163. package/src/interfaces/tui/component/prompt/index.tsx +3 -1
  164. package/src/interfaces/tui/context/sdk-apx.tsx +47 -7
  165. package/src/interfaces/tui/context/sync-apx.tsx +20 -2
  166. package/src/interfaces/tui/context/sync.tsx +2 -1
  167. package/src/interfaces/tui/routes/session/index.tsx +151 -136
  168. package/src/interfaces/tui/routes/session/sidebar-apx.tsx +37 -15
  169. package/src/interfaces/tui/run.ts +2 -0
  170. package/src/interfaces/web/dist/assets/index-7dVT2O1S.css +1 -0
  171. package/src/interfaces/web/dist/assets/index-DWsE_8Nz.js +602 -0
  172. package/src/interfaces/web/dist/assets/index-DWsE_8Nz.js.map +1 -0
  173. package/src/interfaces/web/dist/index.html +2 -2
  174. package/src/interfaces/web/package-lock.json +6 -6
  175. package/src/interfaces/web/src/App.tsx +53 -32
  176. package/src/interfaces/web/src/components/RobyBubble.tsx +12 -6
  177. package/src/interfaces/web/src/components/UiSelect.tsx +13 -3
  178. package/src/interfaces/web/src/components/chat/SkillPicker.tsx +77 -0
  179. package/src/interfaces/web/src/components/code/CodeArtifactsTab.tsx +253 -111
  180. package/src/interfaces/web/src/components/code/CodeChangesTab.tsx +10 -8
  181. package/src/interfaces/web/src/components/code/CodeComposer.tsx +20 -17
  182. package/src/interfaces/web/src/components/code/CodeContextTab.tsx +43 -18
  183. package/src/interfaces/web/src/components/code/CodeFileTree.tsx +212 -0
  184. package/src/interfaces/web/src/components/code/CodeFileViewer.tsx +121 -0
  185. package/src/interfaces/web/src/components/code/CodeProjectPicker.tsx +1 -1
  186. package/src/interfaces/web/src/components/code/CodeSessionList.tsx +30 -26
  187. package/src/interfaces/web/src/components/code/CodeSidePanel.tsx +40 -21
  188. package/src/interfaces/web/src/components/code/CodeTerminal.tsx +140 -0
  189. package/src/interfaces/web/src/components/common/TabLayout.tsx +11 -7
  190. package/src/interfaces/web/src/components/common/TabNav.tsx +3 -3
  191. package/src/interfaces/web/src/components/layout/ProjectSidebar.tsx +4 -2
  192. package/src/interfaces/web/src/components/ui/chat-input.tsx +17 -6
  193. package/src/interfaces/web/src/hooks/useChat.ts +48 -2
  194. package/src/interfaces/web/src/hooks/useNavCollapseCtx.tsx +83 -0
  195. package/src/interfaces/web/src/hooks/usePersonaName.ts +11 -0
  196. package/src/interfaces/web/src/i18n/en.ts +7 -7
  197. package/src/interfaces/web/src/i18n/es.ts +8 -8
  198. package/src/interfaces/web/src/lib/api/agents.ts +1 -1
  199. package/src/interfaces/web/src/lib/api/artifacts.ts +10 -0
  200. package/src/interfaces/web/src/lib/api/code.ts +4 -2
  201. package/src/interfaces/web/src/lib/api/skills.ts +25 -0
  202. package/src/interfaces/web/src/lib/api.ts +1 -0
  203. package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +430 -86
  204. package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +5 -18
  205. package/src/interfaces/web/src/screens/modules/DesktopScreen.tsx +1 -8
  206. package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +39 -40
  207. package/src/interfaces/web/src/screens/project/ChatTab.tsx +16 -16
  208. package/src/skills/apc-context/SKILL.md +159 -0
  209. package/src/core/agent/ghost-guard.js +0 -24
  210. package/src/core/agent/prompts/channels/terminal.md +0 -16
  211. package/src/host/daemon/apc-runtime-context.js +0 -124
  212. package/src/host/daemon/super-agent-tools/helpers.js +0 -124
  213. package/src/host/daemon/tool-call-parser.js +0 -2
  214. package/src/interfaces/web/dist/assets/index-63P_ji1a.js +0 -571
  215. package/src/interfaces/web/dist/assets/index-63P_ji1a.js.map +0 -1
  216. package/src/interfaces/web/dist/assets/index-DLWy6dYz.css +0 -1
  217. /package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/ask-questions.js +0 -0
  218. /package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-files.js +0 -0
  219. /package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-mcps.js +0 -0
  220. /package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-projects.js +0 -0
  221. /package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/read-file.js +0 -0
  222. /package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/search-files.js +0 -0
  223. /package/src/core/agent/{pseudo-tools.js → tools/pseudo-tools.js} +0 -0
  224. /package/src/core/agent/{tool-call-parser.js → tools/tool-call-parser.js} +0 -0
  225. /package/src/core/{parser.js → apc/parser.js} +0 -0
  226. /package/src/core/{apc-skill-sync.js → apc/skill-sync.js} +0 -0
  227. /package/src/core/{artifacts-store.js → stores/artifacts.js} +0 -0
  228. /package/src/{host/daemon → core/stores}/engine-sessions.js +0 -0
  229. /package/src/core/{session-store.js → stores/sessions.js} +0 -0
  230. /package/src/host/daemon/plugins/{telegram-ask.js → telegram/ask.js} +0 -0
@@ -3,8 +3,9 @@
3
3
  // collapse toggle (and optional page actions) followed by the routed content.
4
4
  // The page title/subtitle live in the top breadcrumb now, not here.
5
5
  import { type ReactNode } from "react";
6
- import { TabNav, NavToggle, type TabSection } from "./TabNav";
6
+ import { TabNav, type TabSection } from "./TabNav";
7
7
  import { cn } from "../../lib/cn";
8
+ import { useRegisterNavCollapse } from "../../hooks/useNavCollapseCtx";
8
9
 
9
10
  interface Props {
10
11
  sections: TabSection[];
@@ -29,15 +30,18 @@ export function TabLayout({
29
30
  testId,
30
31
  children,
31
32
  }: Props) {
33
+ useRegisterNavCollapse(collapsed, onToggleCollapse);
34
+
32
35
  return (
33
36
  <div className="flex h-full">
34
37
  <TabNav sections={sections} active={active} onChange={onChange} collapsed={collapsed} />
35
- <div className="flex min-w-0 flex-1 flex-col overflow-y-auto">
36
- <div className="flex items-center justify-between px-6 pt-3">
37
- <NavToggle collapsed={collapsed} onToggle={onToggleCollapse} />
38
- {actions ? <div className="flex gap-2">{actions}</div> : null}
39
- </div>
40
- <div className={cn(contentClassName)} data-testid={testId}>
38
+ <div className="flex min-w-0 flex-1 flex-col overflow-hidden">
39
+ {actions ? (
40
+ <div className="flex shrink-0 items-center justify-end gap-2 px-6 pt-3">
41
+ {actions}
42
+ </div>
43
+ ) : null}
44
+ <div className={cn("flex-1 min-h-0 overflow-y-auto", contentClassName)} data-testid={testId}>
41
45
  {children}
42
46
  </div>
43
47
  </div>
@@ -1,7 +1,7 @@
1
1
  // Left-rail tab nav used inside Settings + per-project screens. Mirrors
2
2
  // the panda.project sectioned-nav pattern: optional section title above
3
3
  // each group, icon + label rows, active state in the section's tone.
4
- import { useState, useEffect, Fragment, type ElementType } from "react";
4
+ import { useState, useEffect, useCallback, Fragment, type ElementType } from "react";
5
5
  import { PanelLeft } from "lucide-react";
6
6
  import { cn } from "../../lib/cn";
7
7
  import { Tip } from "../ui/tip";
@@ -34,12 +34,12 @@ export function useNavCollapse(storageKey: string) {
34
34
  try { setCollapsed(localStorage.getItem(storageKey) === "true"); } catch { /* ignore */ }
35
35
  }, [storageKey]);
36
36
 
37
- const toggle = () =>
37
+ const toggle = useCallback(() =>
38
38
  setCollapsed((v) => {
39
39
  const next = !v;
40
40
  try { localStorage.setItem(storageKey, String(next)); } catch { /* quota */ }
41
41
  return next;
42
- });
42
+ }), [storageKey]);
43
43
 
44
44
  return { collapsed, toggle };
45
45
  }
@@ -9,6 +9,7 @@ import { ProjectAvatar } from "./ProjectAvatar";
9
9
  import { Tip } from "../ui/tip";
10
10
  import { useProjects } from "../../hooks/useProjects";
11
11
  import { t } from "../../i18n";
12
+ import { usePersonaName } from "../../hooks/usePersonaName";
12
13
 
13
14
  interface Props {
14
15
  onSelect: (href: string) => void;
@@ -37,6 +38,7 @@ export function ProjectSidebar({ onSelect, onOpenRoby }: Props) {
37
38
  const { projects, isLoading } = useProjects();
38
39
  const location = useLocation();
39
40
  const MODULES = buildModules();
41
+ const persona = usePersonaName();
40
42
 
41
43
  const isActive = (href: string) =>
42
44
  location.pathname === href || location.pathname.startsWith(`${href}/`);
@@ -122,12 +124,12 @@ export function ProjectSidebar({ onSelect, onOpenRoby }: Props) {
122
124
  />
123
125
  {/* Roby launcher — subtle (not a loud floating bubble), pinned under the
124
126
  gear so it doesn't overlap the chat composer. */}
125
- <Tip content={t("roby.talk")} side="right">
127
+ <Tip content={t("superagent.talk", { persona })} side="right">
126
128
  <button
127
129
  type="button"
128
130
  onClick={onOpenRoby}
129
131
  data-testid="nav-roby"
130
- aria-label={t("roby.talk")}
132
+ aria-label={t("superagent.talk", { persona })}
131
133
  className="mt-1 flex size-10 items-center justify-center rounded-xl border border-border/60 bg-muted/30 text-muted-fg transition-colors hover:bg-accent hover:text-foreground"
132
134
  >
133
135
  <Bot size={18} />
@@ -49,12 +49,23 @@ export function ChatInput({
49
49
  React.useLayoutEffect(() => {
50
50
  const el = ref.current
51
51
  if (!el) return
52
- el.style.height = "auto"
53
- const lineHeight = parseFloat(getComputedStyle(el).lineHeight) || 20
54
- const min = lineHeight * minRows
55
- const max = lineHeight * maxRows
56
- el.style.height = `${Math.min(Math.max(el.scrollHeight, min), max)}px`
57
- el.style.overflowY = el.scrollHeight > max ? "auto" : "hidden"
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
@@ -49,7 +49,7 @@ export interface UseChatResult {
49
49
  streaming: boolean;
50
50
  }
51
51
 
52
- /** Concatenate the text parts of a message (for clipboard / history). */
52
+ /** Concatenate the text parts of a message (for clipboard). */
53
53
  export function textOf(msg: ChatMsg): string {
54
54
  return msg.parts
55
55
  .filter((p): p is TextPart => p.kind === "text")
@@ -58,6 +58,51 @@ export function textOf(msg: ChatMsg): string {
58
58
  .trim();
59
59
  }
60
60
 
61
+ /** Compact line summarising an ask_questions tool call. Surfaced into the
62
+ * history string we send to the super-agent so the model can see it ALREADY
63
+ * asked and not re-ask the same questions on the next turn. Without this,
64
+ * ask_questions calls are invisible in history and the model loops. */
65
+ function summarizeAskQuestions(part: ToolPart): string | null {
66
+ const raw = (part.args as { questions?: unknown } | undefined)?.questions;
67
+ if (!Array.isArray(raw) || raw.length === 0) return null;
68
+ const lines = raw
69
+ .map((q) => {
70
+ if (typeof q === "string") return `- ${q}`;
71
+ if (!q || typeof q !== "object") return null;
72
+ const qq = q as { question?: unknown; options?: unknown };
73
+ if (typeof qq.question !== "string") return null;
74
+ const opts = Array.isArray(qq.options) ? qq.options : [];
75
+ const optStr = opts
76
+ .map((o) =>
77
+ typeof o === "string"
78
+ ? o
79
+ : o && typeof o === "object" && typeof (o as { label?: unknown }).label === "string"
80
+ ? ((o as { label: string }).label)
81
+ : "",
82
+ )
83
+ .filter((s) => s)
84
+ .join(", ");
85
+ return optStr ? `- ${qq.question} (opciones: ${optStr})` : `- ${qq.question}`;
86
+ })
87
+ .filter((s): s is string => !!s);
88
+ if (lines.length === 0) return null;
89
+ return `[ask_questions]\n${lines.join("\n")}`;
90
+ }
91
+
92
+ /** History view of a message — text parts plus ask_questions summaries.
93
+ * Used when sending `previousMessages` to the super-agent. */
94
+ export function historyTextOf(msg: ChatMsg): string {
95
+ const chunks: string[] = [];
96
+ for (const p of msg.parts) {
97
+ if (p.kind === "text" && p.text) chunks.push(p.text);
98
+ else if (p.kind === "tool" && p.tool === "ask_questions") {
99
+ const s = summarizeAskQuestions(p);
100
+ if (s) chunks.push(s);
101
+ }
102
+ }
103
+ return chunks.join("\n\n").trim();
104
+ }
105
+
61
106
  const userPart = (text: string): ChatPart[] => [{ kind: "text", text }];
62
107
 
63
108
  function isErrorResult(result: unknown): boolean {
@@ -202,7 +247,7 @@ export function useChat(pid: string, onError?: (msg: string) => void): UseChatRe
202
247
  const nowIso = () => new Date().toISOString();
203
248
  const history: ConversationMessage[] = msgs.map((m) => ({
204
249
  role: m.role,
205
- content: textOf(m),
250
+ content: historyTextOf(m),
206
251
  }));
207
252
 
208
253
  setMsgs((curr) => [
@@ -218,6 +263,7 @@ export function useChat(pid: string, onError?: (msg: string) => void): UseChatRe
218
263
  const out = await Agents.chat(pid, opts.agentSlug, {
219
264
  prompt: trimmed,
220
265
  conversation_id: convoRef.current,
266
+ model: opts.model || undefined,
221
267
  });
222
268
  convoRef.current = out.conversation_id;
223
269
  patchLast((m) => ({
@@ -0,0 +1,83 @@
1
+ import { createContext, useContext, useState, useEffect, type ReactNode } from "react";
2
+
3
+ // ── Nav collapse ─────────────────────────────────────────────────────────────
4
+
5
+ type CollapseState = { collapsed: boolean; toggle: () => void } | null;
6
+
7
+ const CollapseReadCtx = createContext<CollapseState>(null);
8
+ const CollapseSetCtx = createContext<((s: CollapseState) => void) | null>(null);
9
+
10
+ // ── Page label (extra breadcrumb segment pushed by leaf screens) ──────────────
11
+
12
+ const LabelReadCtx = createContext<string>("");
13
+ const LabelSetCtx = createContext<((s: string) => void) | null>(null);
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
+
20
+ // ── Combined provider (one wrapper in Shell) ──────────────────────────────────
21
+
22
+ export function NavCollapseProvider({ children }: { children: ReactNode }) {
23
+ const [collapse, setCollapse] = useState<CollapseState>(null);
24
+ const [label, setLabel] = useState("");
25
+ const [actions, setActions] = useState<ReactNode>(null);
26
+ return (
27
+ <CollapseSetCtx.Provider value={setCollapse}>
28
+ <CollapseReadCtx.Provider value={collapse}>
29
+ <LabelSetCtx.Provider value={setLabel}>
30
+ <LabelReadCtx.Provider value={label}>
31
+ <ActionsSetCtx.Provider value={setActions}>
32
+ <ActionsReadCtx.Provider value={actions}>
33
+ {children}
34
+ </ActionsReadCtx.Provider>
35
+ </ActionsSetCtx.Provider>
36
+ </LabelReadCtx.Provider>
37
+ </LabelSetCtx.Provider>
38
+ </CollapseReadCtx.Provider>
39
+ </CollapseSetCtx.Provider>
40
+ );
41
+ }
42
+
43
+ // ── Nav collapse hooks ────────────────────────────────────────────────────────
44
+
45
+ export function useNavCollapseCtx() {
46
+ return useContext(CollapseReadCtx);
47
+ }
48
+
49
+ export function useRegisterNavCollapse(collapsed: boolean, toggle: () => void) {
50
+ const setState = useContext(CollapseSetCtx);
51
+ useEffect(() => {
52
+ setState?.({ collapsed, toggle });
53
+ return () => setState?.(null);
54
+ }, [collapsed, toggle, setState]);
55
+ }
56
+
57
+ // ── Page label hooks ──────────────────────────────────────────────────────────
58
+
59
+ export function usePageLabel() {
60
+ return useContext(LabelReadCtx);
61
+ }
62
+
63
+ export function useSetPageLabel(label: string) {
64
+ const set = useContext(LabelSetCtx);
65
+ useEffect(() => {
66
+ set?.(label);
67
+ return () => set?.("");
68
+ }, [label, set]);
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
+ }
@@ -0,0 +1,11 @@
1
+ // Resolve the super-agent display name (the persona shown to the user).
2
+ // Reads identity.json via useIdentity(); falls back to "APX" while loading
3
+ // or when identity is empty. UI strings should always interpolate this hook
4
+ // instead of hardcoding a persona name (it would otherwise drift from what
5
+ // the daemon-side resolveAgentName() reports across CLI / Telegram / etc.).
6
+ import { useIdentity } from "./useIdentity";
7
+
8
+ export function usePersonaName(): string {
9
+ const { identity } = useIdentity();
10
+ return (identity as { agent_name?: string })?.agent_name?.trim() || "APX";
11
+ }
@@ -286,8 +286,8 @@ export const en = {
286
286
  chat: {
287
287
  title: "Chat with agent",
288
288
  subtitle: "Direct conversations with project agents. The super-agent does not intervene.",
289
- roby_title: "Chat with Roby",
290
- roby_subtitle: "Chat with Roby — the APX super-agent. Can use tools (projects, tasks, mcps, agents).",
289
+ superagent_title: "Chat with {persona}",
290
+ superagent_subtitle: "Chat with {persona} — the APX super-agent. Can use tools (projects, tasks, mcps, agents).",
291
291
  empty: "Send a message to start the conversation.",
292
292
  placeholder: "Type something and press enter to send (shift+enter = new line)",
293
293
  send: "Send",
@@ -710,13 +710,13 @@ export const en = {
710
710
  delete_btn: "Delete",
711
711
  },
712
712
 
713
- roby: {
714
- title: "Roby",
713
+ superagent: {
714
+ title: "{persona}",
715
715
  badge: "super-agent · APX",
716
716
  desc: "Quick chat with your super-agent. Has access to tools (projects, tasks, mcps, agents); for a longer persistent thread, open Chats.",
717
- empty: "Send Roby a message to get started.",
718
- thinking: "Roby is thinking…",
719
- talk: "Talk to Roby",
717
+ empty: "Send {persona} a message to get started.",
718
+ thinking: "{persona} is thinking…",
719
+ talk: "Talk to {persona}",
720
720
  new_chat: "New chat",
721
721
  placeholder: "Type and press enter to send (shift+enter = new line)…",
722
722
  },
@@ -286,9 +286,9 @@ export const es = {
286
286
 
287
287
  chat: {
288
288
  title: "Chat con agente",
289
- subtitle: "Conversaciones directas con agentes del proyecto. El super-agent no interviene.",
290
- roby_title: "Chat con Roby",
291
- roby_subtitle: "Chat con Roby — el super-agente APX. Puede usar tools (proyectos, tasks, mcps, agentes).",
289
+ subtitle: "Chat directo con el agente del proyecto.",
290
+ superagent_title: "Chat con {persona}",
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.",
293
293
  placeholder: "Escribí algo y enter para enviar (shift+enter = nueva línea)",
294
294
  send: "Enviar",
@@ -711,13 +711,13 @@ export const es = {
711
711
  delete_btn: "Borrar",
712
712
  },
713
713
 
714
- roby: {
715
- title: "Roby",
714
+ superagent: {
715
+ title: "{persona}",
716
716
  badge: "super-agent · APX",
717
717
  desc: "Conversación rápida con tu super-agente. Tiene acceso a tools (proyectos, tasks, mcps, agentes); para un hilo más largo y persistente, abrí Chats.",
718
- empty: "Mandale un mensaje a Roby para arrancar.",
719
- thinking: "Roby está pensando…",
720
- talk: "Hablar con Roby",
718
+ empty: "Mandale un mensaje a {persona} para arrancar.",
719
+ thinking: "{persona} está pensando…",
720
+ talk: "Hablar con {persona}",
721
721
  new_chat: "Nuevo chat",
722
722
  placeholder: "Escribí y enter para enviar (shift+enter = nueva línea)…",
723
723
  },
@@ -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) =>
@@ -0,0 +1,25 @@
1
+ import { http } from "../http";
2
+
3
+ export type SkillEntry = {
4
+ slug: string;
5
+ source: "bundled" | "user" | "project" | string;
6
+ description: string;
7
+ };
8
+
9
+ export type SkillsList = {
10
+ count: number;
11
+ skills: SkillEntry[];
12
+ };
13
+
14
+ export const Skills = {
15
+ /**
16
+ * List installed skills (bundled + user + optional project-scoped). The
17
+ * description is already condensed for one-line rendering.
18
+ */
19
+ list: (projectPath?: string) =>
20
+ http.get<SkillsList>(
21
+ projectPath
22
+ ? `/skills?project_path=${encodeURIComponent(projectPath)}`
23
+ : "/skills",
24
+ ),
25
+ };
@@ -22,6 +22,7 @@ export * from "./api/voice";
22
22
  export * from "./api/deck";
23
23
  export * from "./api/code";
24
24
  export * from "./api/artifacts";
25
+ export * from "./api/skills";
25
26
 
26
27
  // Re-export the daemon types so older imports of "../lib/api" still work.
27
28
  export type {