@agentprojectcontext/apx 1.77.2 → 1.78.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/README.md +23 -9
- package/package.json +12 -12
- package/src/core/agent/build-agent-system.js +3 -2
- package/src/core/agent/constants.js +3 -2
- package/src/core/agent/loop/greeting-guard.js +55 -0
- package/src/core/agent/loop/side-effects.js +52 -0
- package/src/core/agent/prompt-builder.js +49 -8
- package/src/core/agent/run-agent.js +13 -48
- package/src/core/agent/security.js +1 -1
- package/src/core/agent/self-memory.js +4 -2
- package/src/core/agent/skills/index-store.js +2 -2
- package/src/core/agent/skills/loader.js +3 -22
- package/src/core/agent/super-agent.js +0 -2
- package/src/core/agent/tools/handlers/call-runtime.js +4 -4
- package/src/core/agent/tools/handlers/search-sessions.js +1 -1
- package/src/core/agent/tools/handlers/set-permission-mode.js +11 -4
- package/src/core/agent/tools/names.js +58 -0
- package/src/core/apc/context-copy.js +1 -1
- package/src/core/apc/frontmatter.js +85 -0
- package/src/core/apc/parser.js +9 -21
- package/src/core/apc/paths.js +8 -8
- package/src/core/apc/scaffold.js +5 -4
- package/src/core/channels/telegram/config.js +201 -0
- package/src/core/config/index.js +25 -183
- package/src/core/config/paths.js +54 -1
- package/src/core/confirmation/adapters/web.js +1 -1
- package/src/core/deck/manifest.js +12 -12
- package/src/core/desktop/autostart.js +3 -2
- package/src/core/desktop/process.js +4 -4
- package/src/core/engines/presets.js +2 -2
- package/src/core/http-tools/browser.js +1 -1
- package/src/core/http-tools/catalog.js +551 -0
- package/src/core/http-tools/fetch.js +1 -1
- package/src/core/http-tools/glob.js +3 -3
- package/src/core/http-tools/grep.js +2 -9
- package/src/core/http-tools/inline-handlers.js +59 -0
- package/src/core/http-tools/registry.js +31 -615
- package/src/core/http-tools/search.js +2 -2
- package/src/core/identity/self.js +6 -7
- package/src/core/identity/telegram.js +3 -3
- package/src/core/logging.js +6 -5
- package/src/core/mcp/sources.js +12 -16
- package/src/core/memory/indexer.js +2 -5
- package/src/core/profiles/lifecycle.js +0 -2
- package/src/core/profiles/store.js +2 -7
- package/src/core/routines/runner.js +1 -1
- package/src/core/runtime-skills/apx-agency-agents/SKILL.md +2 -2
- package/src/core/runtime-skills/apx-profile/SKILL.md +3 -3
- package/src/core/runtime-skills/apx-project/SKILL.md +1 -1
- package/src/core/runtime-skills/apx-task/SKILL.md +8 -8
- package/src/core/runtime-skills/apx-telegram/SKILL.md +4 -4
- package/src/core/runtime-skills/apx-voice/SKILL.md +4 -4
- package/src/core/runtimes/detect.js +2 -2
- package/src/core/sessions/index.js +800 -0
- package/src/core/stores/code-sessions.js +3 -14
- package/src/core/stores/engine-sessions.js +0 -4
- package/src/core/stores/messages.js +0 -5
- package/src/core/stores/organization.js +9 -11
- package/src/core/stores/routines.js +6 -11
- package/src/core/stores/runtime-callbacks.js +2 -1
- package/src/core/util/json-file.js +128 -0
- package/src/core/vars/sources.js +14 -21
- package/src/core/voice/stt-models.js +1 -1
- package/src/core/voice/tts.js +5 -4
- package/src/host/daemon/api/admin-config.js +5 -5
- package/src/host/daemon/api/admin.js +8 -7
- package/src/host/daemon/api/agents.js +16 -16
- package/src/host/daemon/api/artifact-preview.js +8 -8
- package/src/host/daemon/api/artifacts.js +7 -7
- package/src/host/daemon/api/code.js +8 -8
- package/src/host/daemon/api/config.js +6 -6
- package/src/host/daemon/api/confirm.js +2 -2
- package/src/host/daemon/api/connections.js +2 -2
- package/src/host/daemon/api/conversations.js +12 -12
- package/src/host/daemon/api/deck.js +6 -6
- package/src/host/daemon/api/desktop.js +8 -8
- package/src/host/daemon/api/embeddings.js +4 -4
- package/src/host/daemon/api/engines.js +8 -7
- package/src/host/daemon/api/exec.js +5 -4
- package/src/host/daemon/api/files-project.js +6 -6
- package/src/host/daemon/api/health.js +2 -2
- package/src/host/daemon/api/identity.js +3 -3
- package/src/host/daemon/api/inbox.js +2 -2
- package/src/host/daemon/api/integrations.js +23 -19
- package/src/host/daemon/api/mcps.js +16 -12
- package/src/host/daemon/api/messages.js +5 -5
- package/src/host/daemon/api/organization.js +8 -8
- package/src/host/daemon/api/pairing.js +10 -7
- package/src/host/daemon/api/plugins.js +3 -3
- package/src/host/daemon/api/prefix.js +20 -0
- package/src/host/daemon/api/profiles.js +9 -9
- package/src/host/daemon/api/projects.js +5 -5
- package/src/host/daemon/api/routines.js +8 -8
- package/src/host/daemon/api/run.js +2 -2
- package/src/host/daemon/api/runtimes.js +6 -6
- package/src/host/daemon/api/sessions-search.js +18 -11
- package/src/host/daemon/api/sessions.js +6 -6
- package/src/host/daemon/api/shared.js +59 -13
- package/src/host/daemon/api/skills.js +12 -12
- package/src/host/daemon/api/super-agent.js +4 -4
- package/src/host/daemon/api/tasks.js +11 -11
- package/src/host/daemon/api/telegram.js +19 -20
- package/src/host/daemon/api/tools.js +7 -7
- package/src/host/daemon/api/top-level.js +7 -7
- package/src/host/daemon/api/transcribe.js +6 -6
- package/src/host/daemon/api/tts.js +3 -3
- package/src/host/daemon/api/vars.js +11 -8
- package/src/host/daemon/api/voice.js +6 -6
- package/src/host/daemon/api/web.js +28 -32
- package/src/host/daemon/api.js +77 -48
- package/src/host/daemon/db.js +1 -1
- package/src/host/daemon/desktop-ws.js +7 -3
- package/src/host/daemon/index.js +11 -5
- package/src/host/daemon/plugins/desktop/index.js +1 -1
- package/src/host/daemon/plugins/telegram/index.js +1 -1
- package/src/host/daemon/stt-venv.js +20 -81
- package/src/host/daemon/wakeup.js +1 -1
- package/src/interfaces/acp/index.js +3 -3
- package/src/interfaces/acp/session.js +2 -2
- package/src/interfaces/cli/commands/a2a.js +2 -2
- package/src/interfaces/cli/commands/agent.js +3 -3
- package/src/interfaces/cli/commands/artifact.js +13 -13
- package/src/interfaces/cli/commands/chat.js +3 -3
- package/src/interfaces/cli/commands/command.js +2 -2
- package/src/interfaces/cli/commands/config.js +4 -4
- package/src/interfaces/cli/commands/daemon.js +17 -18
- package/src/interfaces/cli/commands/desktop.js +7 -14
- package/src/interfaces/cli/commands/exec.js +2 -2
- package/src/interfaces/cli/commands/log.js +3 -4
- package/src/interfaces/cli/commands/mcp.js +9 -9
- package/src/interfaces/cli/commands/memory.js +2 -2
- package/src/interfaces/cli/commands/messages.js +5 -5
- package/src/interfaces/cli/commands/model.js +0 -18
- package/src/interfaces/cli/commands/obsidian.js +5 -5
- package/src/interfaces/cli/commands/org.js +5 -5
- package/src/interfaces/cli/commands/pair.js +6 -6
- package/src/interfaces/cli/commands/plugins.js +2 -2
- package/src/interfaces/cli/commands/profile.js +10 -10
- package/src/interfaces/cli/commands/project-config.js +6 -6
- package/src/interfaces/cli/commands/project.js +10 -10
- package/src/interfaces/cli/commands/routine.js +8 -9
- package/src/interfaces/cli/commands/runtime.js +2 -2
- package/src/interfaces/cli/commands/search.js +0 -1
- package/src/interfaces/cli/commands/session.js +12 -40
- package/src/interfaces/cli/commands/sessions.js +21 -798
- package/src/interfaces/cli/commands/setup.js +16 -6
- package/src/interfaces/cli/commands/skills.js +1 -2
- package/src/interfaces/cli/commands/status.js +3 -3
- package/src/interfaces/cli/commands/task.js +8 -8
- package/src/interfaces/cli/commands/telegram.js +21 -21
- package/src/interfaces/cli/commands/voice.js +6 -6
- package/src/interfaces/cli/help/index.js +2245 -0
- package/src/interfaces/cli/http.js +9 -4
- package/src/interfaces/cli/index.js +14 -2857
- package/src/interfaces/cli/routes/acp.js +13 -0
- package/src/interfaces/cli/routes/agent.js +27 -0
- package/src/interfaces/cli/routes/artifact.js +28 -0
- package/src/interfaces/cli/routes/chat.js +11 -0
- package/src/interfaces/cli/routes/code.js +11 -0
- package/src/interfaces/cli/routes/command.js +21 -0
- package/src/interfaces/cli/routes/config.js +16 -0
- package/src/interfaces/cli/routes/connections.js +11 -0
- package/src/interfaces/cli/routes/conversations.js +21 -0
- package/src/interfaces/cli/routes/daemon.js +19 -0
- package/src/interfaces/cli/routes/desktop.js +20 -0
- package/src/interfaces/cli/routes/env.js +13 -0
- package/src/interfaces/cli/routes/exec.js +11 -0
- package/src/interfaces/cli/routes/identity.js +11 -0
- package/src/interfaces/cli/routes/index.js +71 -0
- package/src/interfaces/cli/routes/init.js +11 -0
- package/src/interfaces/cli/routes/log.js +20 -0
- package/src/interfaces/cli/routes/mcp.js +22 -0
- package/src/interfaces/cli/routes/memory.js +11 -0
- package/src/interfaces/cli/routes/messages.js +16 -0
- package/src/interfaces/cli/routes/model.js +11 -0
- package/src/interfaces/cli/routes/obsidian.js +17 -0
- package/src/interfaces/cli/routes/org.js +34 -0
- package/src/interfaces/cli/routes/overlay.js +10 -0
- package/src/interfaces/cli/routes/pair.js +17 -0
- package/src/interfaces/cli/routes/panel.js +16 -0
- package/src/interfaces/cli/routes/permission.js +11 -0
- package/src/interfaces/cli/routes/plugins.js +21 -0
- package/src/interfaces/cli/routes/profile.js +27 -0
- package/src/interfaces/cli/routes/project.js +41 -0
- package/src/interfaces/cli/routes/restart.js +15 -0
- package/src/interfaces/cli/routes/routine.js +28 -0
- package/src/interfaces/cli/routes/run.js +11 -0
- package/src/interfaces/cli/routes/search.js +11 -0
- package/src/interfaces/cli/routes/send.js +11 -0
- package/src/interfaces/cli/routes/session.js +26 -0
- package/src/interfaces/cli/routes/sessions.js +15 -0
- package/src/interfaces/cli/routes/setup.js +18 -0
- package/src/interfaces/cli/routes/skills.js +20 -0
- package/src/interfaces/cli/routes/status.js +12 -0
- package/src/interfaces/cli/routes/task.js +26 -0
- package/src/interfaces/cli/routes/telegram.js +39 -0
- package/src/interfaces/cli/routes/update.js +24 -0
- package/src/interfaces/cli/routes/voice.js +17 -0
- package/src/interfaces/desktop/main.js +6 -7
- package/src/interfaces/desktop/renderer.js +0 -5
- package/src/interfaces/mcp-server/index.js +15 -14
- package/src/interfaces/tui/_shims/globals.d.ts +8 -0
- package/src/interfaces/tui/tsconfig.json +4 -1
- package/src/interfaces/web/README.md +6 -6
- package/src/interfaces/web/dist/assets/index-CBR_-QyA.js +824 -0
- package/src/interfaces/web/dist/assets/index-CBR_-QyA.js.map +1 -0
- package/src/interfaces/web/dist/index.html +1 -1
- package/src/interfaces/web/src/components/AddProjectDialog.tsx +1 -1
- package/src/interfaces/web/src/components/agents/AgentFormFields.tsx +1 -1
- package/src/interfaces/web/src/components/chat/ChatList.tsx +2 -2
- package/src/interfaces/web/src/components/chat/SkillPicker.tsx +1 -1
- package/src/interfaces/web/src/components/code/CodeFileTree.tsx +1 -1
- package/src/interfaces/web/src/components/code/CodeTerminal.tsx +1 -1
- package/src/interfaces/web/src/components/desktop/DesktopStatusCard.tsx +1 -1
- package/src/interfaces/web/src/components/files/FileBrowser.tsx +2 -2
- package/src/interfaces/web/src/components/memory/MemoryBrowser.tsx +2 -2
- package/src/interfaces/web/src/components/routines/ExecutionsList.tsx +1 -1
- package/src/interfaces/web/src/components/routines/RoutineEditor.tsx +2 -2
- package/src/interfaces/web/src/components/settings/DesktopSettingsPanel.tsx +1 -1
- package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +1 -1
- package/src/interfaces/web/src/components/settings/SkillsInspectorPanel.tsx +1 -1
- package/src/interfaces/web/src/components/settings/SkillsManager.tsx +2 -2
- package/src/interfaces/web/src/components/tasks/TaskDetailPanel.tsx +1 -1
- package/src/interfaces/web/src/hooks/useDaemonStatus.ts +1 -1
- package/src/interfaces/web/src/hooks/useDevices.ts +1 -1
- package/src/interfaces/web/src/hooks/useEngines.ts +1 -1
- package/src/interfaces/web/src/hooks/useGlobalConfig.ts +2 -2
- package/src/interfaces/web/src/hooks/useIdentity.ts +1 -1
- package/src/interfaces/web/src/hooks/useInbox.ts +1 -1
- package/src/interfaces/web/src/hooks/useProfiles.ts +3 -3
- package/src/interfaces/web/src/hooks/useProjects.ts +1 -1
- package/src/interfaces/web/src/hooks/useTelegram.ts +3 -3
- package/src/interfaces/web/src/hooks/useTokenBootstrap.ts +30 -7
- package/src/interfaces/web/src/lib/api/admin.ts +11 -11
- package/src/interfaces/web/src/lib/api/agents.ts +14 -14
- package/src/interfaces/web/src/lib/api/artifacts.ts +11 -11
- package/src/interfaces/web/src/lib/api/code.ts +1 -1
- package/src/interfaces/web/src/lib/api/conversations.ts +8 -8
- package/src/interfaces/web/src/lib/api/deck.ts +3 -3
- package/src/interfaces/web/src/lib/api/desktop.ts +8 -8
- package/src/interfaces/web/src/lib/api/embeddings.ts +3 -3
- package/src/interfaces/web/src/lib/api/engines.ts +3 -3
- package/src/interfaces/web/src/lib/api/filesystem.ts +2 -2
- package/src/interfaces/web/src/lib/api/health.ts +1 -1
- package/src/interfaces/web/src/lib/api/identity.ts +2 -2
- package/src/interfaces/web/src/lib/api/inbox.ts +1 -1
- package/src/interfaces/web/src/lib/api/integrations.ts +8 -8
- package/src/interfaces/web/src/lib/api/mcps.ts +6 -6
- package/src/interfaces/web/src/lib/api/messages.ts +3 -3
- package/src/interfaces/web/src/lib/api/organization.ts +7 -7
- package/src/interfaces/web/src/lib/api/profiles.ts +8 -8
- package/src/interfaces/web/src/lib/api/projectFiles.ts +5 -5
- package/src/interfaces/web/src/lib/api/projects.ts +12 -12
- package/src/interfaces/web/src/lib/api/routines.ts +7 -7
- package/src/interfaces/web/src/lib/api/sessions.ts +2 -2
- package/src/interfaces/web/src/lib/api/skills.ts +11 -11
- package/src/interfaces/web/src/lib/api/super_agent.ts +3 -3
- package/src/interfaces/web/src/lib/api/tasks.ts +12 -12
- package/src/interfaces/web/src/lib/api/telegram.ts +14 -14
- package/src/interfaces/web/src/lib/api/tools.ts +1 -1
- package/src/interfaces/web/src/lib/api/vars.ts +4 -4
- package/src/interfaces/web/src/lib/api/voice.ts +6 -6
- package/src/interfaces/web/src/screens/base/AgentDefaultsTab.tsx +1 -1
- package/src/interfaces/web/src/screens/base/GlobalTasksTab.tsx +1 -1
- package/src/interfaces/web/src/screens/base/LogsTab.tsx +3 -3
- package/src/interfaces/web/src/screens/base/SessionsTab.tsx +1 -1
- package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +1 -1
- package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +1 -1
- package/src/interfaces/web/src/screens/modules/DesktopScreen.tsx +1 -1
- package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +1 -1
- package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +8 -8
- package/src/interfaces/web/src/screens/project/AgentsTab.tsx +2 -2
- package/src/interfaces/web/src/screens/project/ChatTab.tsx +3 -3
- package/src/interfaces/web/src/screens/project/ConfigTab.tsx +1 -1
- package/src/interfaces/web/src/screens/project/McpsTab.tsx +3 -3
- package/src/interfaces/web/src/screens/project/Overview.tsx +6 -6
- package/src/interfaces/web/src/screens/project/RoutinesTab.tsx +2 -2
- package/src/interfaces/web/src/screens/project/StructureTab.tsx +1 -1
- package/src/interfaces/web/src/screens/project/TasksTab.tsx +1 -1
- package/src/interfaces/web/src/screens/project/VarsTab.tsx +1 -1
- package/src/interfaces/web/vite.config.ts +13 -18
- package/src/interfaces/web/dist/assets/index-BWWpTTSd.js +0 -824
- package/src/interfaces/web/dist/assets/index-BWWpTTSd.js.map +0 -1
- package/src/skills/apc-context/SKILL.md +0 -159
- /package/src/{host/daemon → core}/runtimes/_spawn.js +0 -0
- /package/src/{host/daemon → core}/runtimes/aider.js +0 -0
- /package/src/{host/daemon → core}/runtimes/antigravity.js +0 -0
- /package/src/{host/daemon → core}/runtimes/claude-code.js +0 -0
- /package/src/{host/daemon → core}/runtimes/codex.js +0 -0
- /package/src/{host/daemon → core}/runtimes/cursor-agent.js +0 -0
- /package/src/{host/daemon → core}/runtimes/gemini-cli.js +0 -0
- /package/src/{host/daemon → core}/runtimes/index.js +0 -0
- /package/src/{host/daemon → core}/runtimes/opencode.js +0 -0
- /package/src/{host/daemon → core}/runtimes/qwen-code.js +0 -0
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<p align="center">
|
|
12
12
|
<a href="https://agentprojectcontext.github.io/apx/"><img src="https://img.shields.io/badge/Website-agentprojectcontext.github.io-3fb950?style=flat-square&logo=googlechrome&logoColor=white" alt="Website"></a>
|
|
13
13
|
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-3fb950?style=flat-square" alt="License: MIT"></a>
|
|
14
|
-
<img src="https://img.shields.io/badge/Node.js-
|
|
14
|
+
<img src="https://img.shields.io/badge/Node.js-22%2B-3fb950?style=flat-square&logo=nodedotjs&logoColor=white" alt="Node.js 22+">
|
|
15
15
|
<a href="https://github.com/agentprojectcontext/agentprojectcontext"><img src="https://img.shields.io/badge/Protocol-APC-3fb950?style=flat-square" alt="APC protocol"></a>
|
|
16
16
|
</p>
|
|
17
17
|
|
|
@@ -49,7 +49,7 @@ APX is opinionated about storage: the filesystem is the source of truth. Project
|
|
|
49
49
|
█ ‿ █
|
|
50
50
|
▀███████▀
|
|
51
51
|
</pre>
|
|
52
|
-
<sub>
|
|
52
|
+
<sub>The <b>super-agent</b> — it greets you across the <code>apx</code> CLI and the web admin.</sub>
|
|
53
53
|
</div>
|
|
54
54
|
|
|
55
55
|
## Quick start
|
|
@@ -77,7 +77,8 @@ apx messages tail
|
|
|
77
77
|
## Examples
|
|
78
78
|
|
|
79
79
|
Real commands — copy one, point it at an agent like `sofia`, and APX routes it to the right
|
|
80
|
-
runtime. The session and
|
|
80
|
+
runtime. The session transcript and its summary land in `~/.apx/`; only curated
|
|
81
|
+
memory and the agent definition live in `.apc/`.
|
|
81
82
|
|
|
82
83
|
| What | Command |
|
|
83
84
|
|------|---------|
|
|
@@ -90,7 +91,7 @@ runtime. The session and memory land in `.apc/`.
|
|
|
90
91
|
|
|
91
92
|
## Use cases
|
|
92
93
|
|
|
93
|
-
- **Review PRs across any runtime** — point an agent at your repo; APX routes to Claude Code and falls back to Codex or OpenCode if one isn't installed. The session and its summary land in
|
|
94
|
+
- **Review PRs across any runtime** — point an agent at your repo; APX routes to Claude Code and falls back to Codex or OpenCode if one isn't installed. The session and its summary land in `~/.apx/`.
|
|
94
95
|
- **Operate your agents from Telegram** — talk to project agents from your phone. Identity roles gate who can do what, and every message is logged per channel for a full audit trail.
|
|
95
96
|
- **Memory that lives in your repo** — curated, per-agent memory is plain markdown, committed and reviewable alongside your code. No vendor database, no hidden state, no lock-in.
|
|
96
97
|
- **Run the same prompt across engines** — send one prompt through Anthropic, OpenAI, Gemini or a local Ollama model with `apx exec`, configured per project or globally.
|
|
@@ -101,7 +102,7 @@ runtime. The session and memory land in `.apc/`.
|
|
|
101
102
|
npm install -g @agentprojectcontext/apx
|
|
102
103
|
```
|
|
103
104
|
|
|
104
|
-
Requires Node.js
|
|
105
|
+
Requires Node.js 22+. The daemon starts automatically on first `apx` call.
|
|
105
106
|
|
|
106
107
|
## Web admin
|
|
107
108
|
|
|
@@ -178,12 +179,21 @@ Activity belongs to APX runtime state, not `.apc/`. Message storage is local to
|
|
|
178
179
|
JSONL messages include `type` (`user`, `agent`, `tool`, or `system`) plus `actor_id`, so chat views
|
|
179
180
|
can distinguish Telegram users from APX agents and future subagents.
|
|
180
181
|
|
|
182
|
+
A **channel** is the surface a turn arrived on. The canonical list lives in
|
|
183
|
+
`src/core/constants/channels.js`; `voice` is a *mode*, not a channel.
|
|
184
|
+
|
|
181
185
|
| Channel | What it captures |
|
|
182
186
|
|---------|-----------------|
|
|
183
|
-
| `
|
|
184
|
-
| `
|
|
185
|
-
| `
|
|
186
|
-
| `
|
|
187
|
+
| `cli` | `apx exec` / `apx run` from the terminal |
|
|
188
|
+
| `telegram` | Telegram bot messages |
|
|
189
|
+
| `api` | Direct daemon HTTP calls |
|
|
190
|
+
| `web` | The admin panel's main chat |
|
|
191
|
+
| `web_sidebar` | The panel's side assistant |
|
|
192
|
+
| `web_code` | The panel's coding surface |
|
|
193
|
+
| `code` | `apx code` sessions |
|
|
194
|
+
| `deck` | The tablet/phone dashboard |
|
|
195
|
+
| `desktop` | The floating voice capsule (always voice mode) |
|
|
196
|
+
| `routine` | Scheduled routine runs |
|
|
187
197
|
|
|
188
198
|
## Runtimes
|
|
189
199
|
|
|
@@ -193,6 +203,10 @@ can distinguish Telegram users from APX agents and future subagents.
|
|
|
193
203
|
| `codex` | OpenAI Codex CLI via non-interactive `codex exec --sandbox workspace-write --skip-git-repo-check` |
|
|
194
204
|
| `opencode` | OpenCode CLI |
|
|
195
205
|
| `aider` | Aider CLI |
|
|
206
|
+
| `cursor-agent` | Cursor's headless agent |
|
|
207
|
+
| `gemini-cli` | Google Gemini CLI |
|
|
208
|
+
| `qwen-code` | Qwen Code CLI |
|
|
209
|
+
| `antigravity` | Antigravity CLI |
|
|
196
210
|
|
|
197
211
|
Global APX skill installation also writes named helper skills for `codex-cli`, `claude-code`,
|
|
198
212
|
`opencode-cli`, and `openrouter`. They are intentionally narrow and should activate only when those
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentprojectcontext/apx",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.78.0",
|
|
4
4
|
"description": "APX — unified CLI + daemon for the Agent Project Context (APC) standard.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -29,16 +29,20 @@
|
|
|
29
29
|
"!**/*.tsbuildinfo"
|
|
30
30
|
],
|
|
31
31
|
"engines": {
|
|
32
|
-
"node": ">=
|
|
32
|
+
"node": ">=22"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"start": "node src/host/daemon/index.js",
|
|
36
36
|
"smoke": "node src/host/daemon/smoke.js",
|
|
37
37
|
"test": "node --test --test-reporter=spec tests/*.test.js",
|
|
38
|
+
"test:ci": "node scripts/test-ci.js",
|
|
39
|
+
"lint": "eslint .",
|
|
40
|
+
"lint:fix": "eslint . --fix",
|
|
41
|
+
"typecheck:tui": "node scripts/typecheck-tui.js",
|
|
38
42
|
"build:web": "node scripts/build-web.js",
|
|
39
|
-
"preflight": "npm test && node scripts/build-web.js && (cd src/interfaces/web && npx --no-install tsc --noEmit)",
|
|
43
|
+
"preflight": "npm run lint && npm run test:ci && node scripts/build-web.js && (cd src/interfaces/web && npx --no-install tsc --noEmit) && npm run typecheck:tui",
|
|
40
44
|
"upgrade": "pnpm install && pnpm add -g .",
|
|
41
|
-
"prepare": "node scripts/install-githooks.js",
|
|
45
|
+
"prepare": "node scripts/install-githooks.js && node scripts/sync-apc-skill.js",
|
|
42
46
|
"prepack": "node scripts/sync-apc-skill.js && node scripts/build-web.js",
|
|
43
47
|
"postinstall": "node src/interfaces/cli/postinstall.js",
|
|
44
48
|
"smoke:seam": "node --test --test-reporter=spec tests/smoke/*.smoke.js"
|
|
@@ -50,26 +54,20 @@
|
|
|
50
54
|
"@opentui/solid": "^0.2.16",
|
|
51
55
|
"@solid-primitives/event-bus": "^1.1.3",
|
|
52
56
|
"@solid-primitives/scheduled": "^1.5.2",
|
|
53
|
-
"chalk": "^5.6.2",
|
|
54
|
-
"cli-cursor": "^5.0.0",
|
|
55
57
|
"cli-sound": "^1.1.3",
|
|
56
58
|
"clipboardy": "^4.0.0",
|
|
57
59
|
"cron-parser": "^5.5.0",
|
|
58
60
|
"effect": "^3.21.2",
|
|
59
|
-
"esbuild-plugin-solid": "^0.6.0",
|
|
60
61
|
"express": "^4.21.0",
|
|
61
62
|
"fuzzysort": "^3.1.0",
|
|
62
|
-
"iconv-lite": "^0.6.3",
|
|
63
63
|
"node-fetch": "^3.3.2",
|
|
64
64
|
"open": "^10.1.2",
|
|
65
65
|
"opentui-spinner": "^0.0.6",
|
|
66
66
|
"qrcode-terminal": "^0.12.0",
|
|
67
|
-
"raw-body": "^3.0.0",
|
|
68
|
-
"react": "^19.2.0",
|
|
69
67
|
"remeda": "^2.21.0",
|
|
70
|
-
"safer-buffer": "^2.1.2",
|
|
71
68
|
"solid-js": "^1.9.13",
|
|
72
|
-
"ws": "^8.21.3"
|
|
69
|
+
"ws": "^8.21.3",
|
|
70
|
+
"zod": "^4.4.3"
|
|
73
71
|
},
|
|
74
72
|
"optionalDependencies": {
|
|
75
73
|
"better-sqlite3": "^11.3.0",
|
|
@@ -78,12 +76,14 @@
|
|
|
78
76
|
"sqlite-vec": "^0.1.6"
|
|
79
77
|
},
|
|
80
78
|
"devDependencies": {
|
|
79
|
+
"@eslint/js": "^9.39.5",
|
|
81
80
|
"@semantic-release/changelog": "^6.0.3",
|
|
82
81
|
"@semantic-release/git": "^10.0.1",
|
|
83
82
|
"@types/node": "^25.7.0",
|
|
84
83
|
"conventional-changelog-conventionalcommits": "^9.3.1",
|
|
85
84
|
"electron": "^33.4.11",
|
|
86
85
|
"esbuild": "^0.28.0",
|
|
86
|
+
"eslint": "^9.39.5",
|
|
87
87
|
"typescript": "^6.0.3"
|
|
88
88
|
},
|
|
89
89
|
"keywords": [
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
buildUserContextBlock,
|
|
18
18
|
buildSegmentDiscipline,
|
|
19
19
|
} from "./prompt-builder.js";
|
|
20
|
+
import { readJson } from "#core/util/json-file.js";
|
|
20
21
|
|
|
21
22
|
// Cap the injected agent body so an over-long authored file can't blow the
|
|
22
23
|
// token budget. Mirrors PROJECT_AGENTS_MAX_CHARS for AGENTS.md.
|
|
@@ -30,8 +31,8 @@ function listField(value) {
|
|
|
30
31
|
function projectName(project) {
|
|
31
32
|
if (project?.name) return project.name;
|
|
32
33
|
try {
|
|
33
|
-
const meta =
|
|
34
|
-
return meta
|
|
34
|
+
const meta = readJson(apcProjectFile(project.path), {});
|
|
35
|
+
return meta?.name || project.path?.split("/").pop() || "";
|
|
35
36
|
} catch {
|
|
36
37
|
return project?.path?.split("/").pop() || "";
|
|
37
38
|
}
|
|
@@ -15,10 +15,11 @@ export const MAX_TOOL_ITERS = 10;
|
|
|
15
15
|
// genuinely exhausts this budget — a rare safety floor, not the default close.
|
|
16
16
|
// Overridable per-deployment via config.super_agent.telegram_max_iters.
|
|
17
17
|
export const TELEGRAM_TOOL_ITERS = 24;
|
|
18
|
-
|
|
18
|
+
// Defined in tools/names.js, next to the names they reference.
|
|
19
|
+
export { ACK_ONLY_TOOLS, TURN_ENDING_TOOLS } from "./tools/names.js";
|
|
19
20
|
export const MAX_CONSECUTIVE_ACKS = 2;
|
|
20
21
|
// Tools whose semantics REQUIRE handing control back to the user. After the
|
|
21
22
|
// tool runs we break the loop — even under completionContract — because the
|
|
22
23
|
// task literally cannot advance without a human reply. Without this, models
|
|
23
24
|
// under forced toolChoice spam the same question across iterations.
|
|
24
|
-
|
|
25
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Collapse repeated greetings inside a single turn.
|
|
2
|
+
//
|
|
3
|
+
// A turn can emit several text segments (pre-tool narration, then the final
|
|
4
|
+
// answer) and weaker models greet in each one, so the user sees "¡Hola Manu!"
|
|
5
|
+
// twice. Keep the first greeting, strip any later one.
|
|
6
|
+
//
|
|
7
|
+
// Belt-and-suspenders over the action-discipline prompt rule: strong models
|
|
8
|
+
// follow that rule, gemini-flash and friends often do not.
|
|
9
|
+
//
|
|
10
|
+
// Extracted from runAgent, where it was two loose variables and a closure among
|
|
11
|
+
// twelve other concerns. As its own object it can be tested directly, which it
|
|
12
|
+
// could not be before.
|
|
13
|
+
// A leading greeting clause: "¡Hola Manu!", "Hola,", "Hi there!", "Buenas tardes…".
|
|
14
|
+
// Intentionally narrow — only the opening salutation up to its first terminator —
|
|
15
|
+
// so we never eat real content.
|
|
16
|
+
// The comma matters. Without it the class ran to the first sentence
|
|
17
|
+
// terminator, so "Hola de nuevo, ya está listo." matched in full and the guard
|
|
18
|
+
// deleted the whole sentence — the opposite of the "never eat real content"
|
|
19
|
+
// intent stated above. Stopping at a comma keeps the vocative ("¡Hola Manu!",
|
|
20
|
+
// "Hola de nuevo,") and leaves the sentence that follows.
|
|
21
|
+
//
|
|
22
|
+
// Known limit: a greeting glued to content with no separator at all
|
|
23
|
+
// ("Hey arranco.") is genuinely ambiguous and is still consumed. That is the
|
|
24
|
+
// rarer shape, and erring toward stripping there matches the original intent.
|
|
25
|
+
export const LEADING_GREETING_RE =
|
|
26
|
+
/^\s*[¡!]*\s*(hola+|holis?|buenas|buen[oa]s?\s+(d[ií]as|tardes|noches)|hey|hi|hello)\b[^,.!?¡\n]*[,.!?¡]*[\s]*/i;
|
|
27
|
+
|
|
28
|
+
/** If `text` opens with a greeting, return it with that greeting removed; else null. */
|
|
29
|
+
export function stripLeadingGreeting(text) {
|
|
30
|
+
const m = String(text).match(LEADING_GREETING_RE);
|
|
31
|
+
if (!m) return null;
|
|
32
|
+
return String(text).slice(m[0].length).replace(/^\s+/, "");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @returns {{ apply(text: string): string, greeted: boolean }} `apply` returns
|
|
37
|
+
* the text to show the user; call it once per emitted segment, in order.
|
|
38
|
+
*/
|
|
39
|
+
export function createGreetingGuard() {
|
|
40
|
+
let greeted = false;
|
|
41
|
+
return {
|
|
42
|
+
apply(text) {
|
|
43
|
+
if (!text) return text;
|
|
44
|
+
if (greeted) {
|
|
45
|
+
const stripped = stripLeadingGreeting(text);
|
|
46
|
+
return stripped == null ? text : stripped;
|
|
47
|
+
}
|
|
48
|
+
if (LEADING_GREETING_RE.test(text)) greeted = true;
|
|
49
|
+
return text;
|
|
50
|
+
},
|
|
51
|
+
get greeted() {
|
|
52
|
+
return greeted;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Remembers which world-changing tool calls a turn already made.
|
|
2
|
+
//
|
|
3
|
+
// Weaker models (Gemini especially) re-emit the SAME tool call across
|
|
4
|
+
// iterations — send_telegram three times with identical args, spamming the
|
|
5
|
+
// user. For tools that mutate something we record the (name + args) signature
|
|
6
|
+
// and answer a repeat with a synthetic "already done" instead of running it
|
|
7
|
+
// again. Read-only tools are exempt: they are idempotent and are legitimately
|
|
8
|
+
// repeated (list_tasks before and after a change).
|
|
9
|
+
//
|
|
10
|
+
// This is a user-facing protection, and it used to live as two loose locals
|
|
11
|
+
// inside runAgent alongside eleven other concerns, keyed off an inline array of
|
|
12
|
+
// tool-name literals. The names now come from tools/names.js (see
|
|
13
|
+
// SIDE_EFFECT_TOOLS) and the ledger is its own object, so both halves can be
|
|
14
|
+
// tested without driving a whole agent turn.
|
|
15
|
+
import { SIDE_EFFECT_TOOLS } from "../tools/names.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @returns {{
|
|
19
|
+
* signature(name: string, args: unknown): string|null,
|
|
20
|
+
* seen(sig: string|null): boolean,
|
|
21
|
+
* previous(sig: string): unknown,
|
|
22
|
+
* record(sig: string|null, result: unknown): void,
|
|
23
|
+
* }}
|
|
24
|
+
* `signature` returns null for read-only tools, which callers treat as
|
|
25
|
+
* "never dedupe this".
|
|
26
|
+
*/
|
|
27
|
+
export function createSideEffectLedger() {
|
|
28
|
+
const executed = new Map();
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
signature(name, args) {
|
|
32
|
+
if (!SIDE_EFFECT_TOOLS.has(name)) return null;
|
|
33
|
+
try {
|
|
34
|
+
return `${name}:${JSON.stringify(args)}`;
|
|
35
|
+
} catch {
|
|
36
|
+
// Circular or otherwise unserializable args: fall back to the tool
|
|
37
|
+
// name alone. Deduping slightly too eagerly is safer than sending the
|
|
38
|
+
// same message twice.
|
|
39
|
+
return `${name}:<unserializable>`;
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
seen(sig) {
|
|
43
|
+
return sig != null && executed.has(sig);
|
|
44
|
+
},
|
|
45
|
+
previous(sig) {
|
|
46
|
+
return executed.get(sig);
|
|
47
|
+
},
|
|
48
|
+
record(sig, result) {
|
|
49
|
+
if (sig != null) executed.set(sig, result);
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -125,19 +125,60 @@ function buildSegmentDiscipline({ channel, voice }) {
|
|
|
125
125
|
// Project guidance — AGENTS.md of the pinned project, size-capped.
|
|
126
126
|
// ---------------------------------------------------------------------------
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
// Budget guard for a FOREIGN project's AGENTS.md — a project we were merely
|
|
129
|
+
// pointed at should not be able to blow the prompt budget. It is deliberately
|
|
130
|
+
// generous: a real contract runs well past the old 6k cap, and a contract that
|
|
131
|
+
// arrives half-read is worse than none (the agent follows the rules it can see
|
|
132
|
+
// and silently violates the ones it cannot).
|
|
133
|
+
//
|
|
134
|
+
// Two escape hatches, because truncating a contract is never harmless:
|
|
135
|
+
// - `super_agent.project_agents_max_chars` overrides it; 0 disables the cap.
|
|
136
|
+
// - The project APX is *running inside* is never capped (see isOwnProject).
|
|
137
|
+
export const PROJECT_AGENTS_MAX_CHARS = 24000;
|
|
138
|
+
|
|
139
|
+
// The project that owns the running process reads its own contract in full.
|
|
140
|
+
// This is the dogfood case: `apx exec` inside a repo, or the daemon started
|
|
141
|
+
// from it. Capping there truncated APX's own AGENTS.md mid-rule.
|
|
142
|
+
function isOwnProject(projectPath) {
|
|
143
|
+
try {
|
|
144
|
+
return path.resolve(projectPath) === path.resolve(process.cwd());
|
|
145
|
+
} catch {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function resolveAgentsCap(projectPath, globalConfig) {
|
|
151
|
+
if (isOwnProject(projectPath)) return 0;
|
|
152
|
+
const raw = globalConfig?.super_agent?.project_agents_max_chars;
|
|
153
|
+
if (raw === 0) return 0;
|
|
154
|
+
const n = Number(raw);
|
|
155
|
+
return Number.isFinite(n) && n > 0 ? n : PROJECT_AGENTS_MAX_CHARS;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Cut on a line boundary so a rule is never severed mid-sentence, and say what
|
|
159
|
+
// was dropped — a bare "…(truncated)" gives the agent no way to know it is
|
|
160
|
+
// operating on a partial contract.
|
|
161
|
+
function capAgentsText(text, max) {
|
|
162
|
+
if (!max || text.length <= max) return text;
|
|
163
|
+
const head = text.slice(0, max);
|
|
164
|
+
const lastBreak = head.lastIndexOf("\n");
|
|
165
|
+
const kept = lastBreak > max * 0.5 ? head.slice(0, lastBreak) : head;
|
|
166
|
+
const dropped = text.length - kept.length;
|
|
167
|
+
return (
|
|
168
|
+
`${kept.trimEnd()}\n\n…(AGENTS.md truncated: ${dropped} of ${text.length} characters omitted. ` +
|
|
169
|
+
`Read the file directly before relying on rules not shown above.)`
|
|
170
|
+
);
|
|
171
|
+
}
|
|
129
172
|
|
|
130
|
-
export function buildProjectAgentsBlock(projectPath) {
|
|
173
|
+
export function buildProjectAgentsBlock(projectPath, globalConfig = {}) {
|
|
131
174
|
if (!projectPath) return "";
|
|
132
175
|
try {
|
|
133
176
|
const file = agentsMdFile(projectPath);
|
|
134
177
|
if (!fs.existsSync(file)) return "";
|
|
135
|
-
|
|
178
|
+
const text = fs.readFileSync(file, "utf8").trim();
|
|
136
179
|
if (!text) return "";
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
return `# Project guidance (AGENTS.md)\n\nStartup rules for THIS project — follow them:\n\n${text}`;
|
|
180
|
+
const capped = capAgentsText(text, resolveAgentsCap(projectPath, globalConfig));
|
|
181
|
+
return `# Project guidance (AGENTS.md)\n\nStartup rules for THIS project — follow them:\n\n${capped}`;
|
|
141
182
|
} catch {
|
|
142
183
|
return "";
|
|
143
184
|
}
|
|
@@ -329,7 +370,7 @@ export function buildSuperAgentSystem({
|
|
|
329
370
|
relationshipBlock,
|
|
330
371
|
extraContext,
|
|
331
372
|
buildProjectIndex(projects),
|
|
332
|
-
buildProjectAgentsBlock(channelMeta?.projectPath),
|
|
373
|
+
buildProjectAgentsBlock(channelMeta?.projectPath, globalConfig),
|
|
333
374
|
skipSkillsHint ? "" : buildSkillsHintBlock(listSkills),
|
|
334
375
|
lazyToolsBlock,
|
|
335
376
|
voiceBlock,
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from "./tools/tool-call-parser.js";
|
|
6
6
|
import { resolveActiveModel, fallbackModels } from "./model-router.js";
|
|
7
7
|
import { MAX_TOOL_ITERS, ACK_ONLY_TOOLS, MAX_CONSECUTIVE_ACKS, TURN_ENDING_TOOLS } from "./constants.js";
|
|
8
|
+
import { TOOLS } from "./tools/names.js";
|
|
8
9
|
import { pseudoToolSystem, shouldRetryWithPseudoTools } from "./tools/pseudo-tools.js";
|
|
9
10
|
import { filterToolSchemas } from "./tools-overlap.js";
|
|
10
11
|
import { isRetryableEngineError, shortRetryReason } from "./retry.js";
|
|
@@ -21,6 +22,8 @@ import {
|
|
|
21
22
|
createStuckDetector,
|
|
22
23
|
stuckNudgeSignal,
|
|
23
24
|
} from "./stuck-detector.js";
|
|
25
|
+
import { createGreetingGuard } from "./loop/greeting-guard.js";
|
|
26
|
+
import { createSideEffectLedger } from "./loop/side-effects.js";
|
|
24
27
|
|
|
25
28
|
async function emitProgress(onEvent, event) {
|
|
26
29
|
if (typeof onEvent !== "function") return;
|
|
@@ -46,19 +49,6 @@ function fallbackFinalText(trace, error) {
|
|
|
46
49
|
return lines.join("\n");
|
|
47
50
|
}
|
|
48
51
|
|
|
49
|
-
// A leading greeting clause: "¡Hola Manu!", "Hola,", "Hi there!", "Buenas tardes…".
|
|
50
|
-
// Intentionally narrow — only the opening salutation up to its first terminator —
|
|
51
|
-
// so we never eat real content.
|
|
52
|
-
const LEADING_GREETING_RE =
|
|
53
|
-
/^\s*[¡!]*\s*(hola+|holis?|buenas|buen[oa]s?\s+(d[ií]as|tardes|noches)|hey|hi|hello)\b[^.!?¡\n]*[.!?¡]*[\s,]*/i;
|
|
54
|
-
|
|
55
|
-
/** If `text` opens with a greeting, return it with that greeting removed; else null. */
|
|
56
|
-
function stripLeadingGreeting(text) {
|
|
57
|
-
const m = String(text).match(LEADING_GREETING_RE);
|
|
58
|
-
if (!m) return null;
|
|
59
|
-
return String(text).slice(m[0].length).replace(/^\s+/, "");
|
|
60
|
-
}
|
|
61
|
-
|
|
62
52
|
function previewTraceResult(result) {
|
|
63
53
|
if (result === null || result === undefined) return "ok";
|
|
64
54
|
if (typeof result === "string") return result.slice(0, 180);
|
|
@@ -78,7 +68,7 @@ function previewTraceResult(result) {
|
|
|
78
68
|
export const FINISH_TOOL_SCHEMA = {
|
|
79
69
|
type: "function",
|
|
80
70
|
function: {
|
|
81
|
-
name:
|
|
71
|
+
name: TOOLS.FINISH,
|
|
82
72
|
description:
|
|
83
73
|
"Call this ONLY when the user's request is fully complete and no step " +
|
|
84
74
|
"remains. Put your final answer / summary of what you did in `summary` " +
|
|
@@ -274,7 +264,7 @@ export async function runAgent({
|
|
|
274
264
|
|
|
275
265
|
const conversation = [...previousMessages, { role: "user", content: prompt }];
|
|
276
266
|
const trace = [];
|
|
277
|
-
|
|
267
|
+
const totalUsage = { input_tokens: 0, output_tokens: 0 };
|
|
278
268
|
let lastText = "";
|
|
279
269
|
|
|
280
270
|
// Collapse repeated greetings within a single turn. A turn can produce several
|
|
@@ -282,16 +272,8 @@ export async function runAgent({
|
|
|
282
272
|
// each one, so the user sees "¡Hola Manu!" twice. Keep the first greeting,
|
|
283
273
|
// strip any later one. Belt-and-suspenders over the action-discipline prompt
|
|
284
274
|
// rule (which strong models follow but gemini-flash et al. often ignore).
|
|
285
|
-
|
|
286
|
-
const dedupeGreeting = (text) =>
|
|
287
|
-
if (!text) return text;
|
|
288
|
-
if (greetedThisTurn) {
|
|
289
|
-
const stripped = stripLeadingGreeting(text);
|
|
290
|
-
return stripped == null ? text : stripped;
|
|
291
|
-
}
|
|
292
|
-
if (LEADING_GREETING_RE.test(text)) greetedThisTurn = true;
|
|
293
|
-
return text;
|
|
294
|
-
};
|
|
275
|
+
const greetingGuard = createGreetingGuard();
|
|
276
|
+
const dedupeGreeting = (text) => greetingGuard.apply(text);
|
|
295
277
|
let usePseudoTools = false;
|
|
296
278
|
let ackOnlyStreak = 0;
|
|
297
279
|
// "Never end on silence": a model call that returns no tool calls AND no
|
|
@@ -307,24 +289,7 @@ export async function runAgent({
|
|
|
307
289
|
// short-circuit duplicates with a synthetic "already done" result
|
|
308
290
|
// instead of re-running. Read-only tools are exempt (idempotent and
|
|
309
291
|
// sometimes legitimately repeated, like list_tasks before/after).
|
|
310
|
-
const
|
|
311
|
-
const SIDE_EFFECT_TOOLS = new Set([
|
|
312
|
-
"send_telegram",
|
|
313
|
-
"create_task",
|
|
314
|
-
"write_file",
|
|
315
|
-
"edit_file",
|
|
316
|
-
"run_shell",
|
|
317
|
-
"call_runtime",
|
|
318
|
-
"add_project",
|
|
319
|
-
"set_identity",
|
|
320
|
-
]);
|
|
321
|
-
const sideEffectSignature = (name, args) => {
|
|
322
|
-
try {
|
|
323
|
-
return `${name}:${JSON.stringify(args)}`;
|
|
324
|
-
} catch {
|
|
325
|
-
return `${name}:<unserializable>`;
|
|
326
|
-
}
|
|
327
|
-
};
|
|
292
|
+
const sideEffects = createSideEffectLedger();
|
|
328
293
|
|
|
329
294
|
// Stuck detection: catches the loops the side-effect dedupe can't — a
|
|
330
295
|
// read-only call repeated with identical results, or the same call erroring
|
|
@@ -498,7 +463,7 @@ export async function runAgent({
|
|
|
498
463
|
|
|
499
464
|
// Completion contract: `finish` declares the task done. Capture its
|
|
500
465
|
// summary as the final text and stop processing the rest of this turn.
|
|
501
|
-
if (name ===
|
|
466
|
+
if (name === TOOLS.FINISH) {
|
|
502
467
|
finishSummary = typeof args.summary === "string" ? args.summary : "";
|
|
503
468
|
break;
|
|
504
469
|
}
|
|
@@ -517,13 +482,13 @@ export async function runAgent({
|
|
|
517
482
|
iteration: iter + 1,
|
|
518
483
|
});
|
|
519
484
|
// Dedupe identical side-effecting calls within this turn.
|
|
520
|
-
const sig =
|
|
521
|
-
if (
|
|
485
|
+
const sig = sideEffects.signature(name, args);
|
|
486
|
+
if (sideEffects.seen(sig)) {
|
|
522
487
|
toolResult = {
|
|
523
488
|
ok: true,
|
|
524
489
|
deduped: true,
|
|
525
490
|
note: `Ya ejecuté "${name}" con estos mismos argumentos en este turno; no lo repito.`,
|
|
526
|
-
previous:
|
|
491
|
+
previous: sideEffects.previous(sig),
|
|
527
492
|
};
|
|
528
493
|
await emitProgress(onEvent, {
|
|
529
494
|
type: "tool_deduped",
|
|
@@ -570,7 +535,7 @@ export async function runAgent({
|
|
|
570
535
|
if (toolHandlerCtx) toolHandlerCtx.securityGateCleared = false;
|
|
571
536
|
}
|
|
572
537
|
}
|
|
573
|
-
|
|
538
|
+
sideEffects.record(sig, summarizeForTrace(toolResult));
|
|
574
539
|
}
|
|
575
540
|
|
|
576
541
|
const traceItem = {
|
|
@@ -17,7 +17,7 @@ const RISK_ORDER = { LOW: 1, MEDIUM: 2, HIGH: 3 };
|
|
|
17
17
|
|
|
18
18
|
// Loop-control / interaction tools that never touch the world — grading them
|
|
19
19
|
// only burns tokens and trains the model to rubber-stamp the field.
|
|
20
|
-
|
|
20
|
+
import { RISK_EXEMPT_TOOLS } from "./tools/names.js";
|
|
21
21
|
|
|
22
22
|
export function normalizeRisk(value) {
|
|
23
23
|
const v = String(value || "").trim().toUpperCase();
|
|
@@ -14,11 +14,13 @@
|
|
|
14
14
|
// The header inside the file picks up the current persona name from identity
|
|
15
15
|
// (resolveAgentName) — never hardcode the agent name here.
|
|
16
16
|
import fs from "node:fs";
|
|
17
|
-
import os from "node:os";
|
|
18
17
|
import path from "node:path";
|
|
18
|
+
import { SELF_MEMORY_PATH } from "../config/paths.js";
|
|
19
|
+
|
|
20
|
+
export { SELF_MEMORY_PATH };
|
|
19
21
|
import { resolveAgentName } from "../identity/index.js";
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
|
|
22
24
|
|
|
23
25
|
function notebookHeader() {
|
|
24
26
|
let name = "";
|
|
@@ -36,13 +36,13 @@
|
|
|
36
36
|
|
|
37
37
|
import fs from "node:fs";
|
|
38
38
|
import path from "node:path";
|
|
39
|
-
import
|
|
39
|
+
import { SKILLS_INDEX_PATH } from "#core/config/paths.js";
|
|
40
40
|
|
|
41
41
|
import { embedOne } from "#core/memory/embeddings.js";
|
|
42
42
|
import { listSkills } from "./loader.js";
|
|
43
43
|
import { condenseSkillDescription } from "./catalog.js";
|
|
44
44
|
|
|
45
|
-
const INDEX_PATH =
|
|
45
|
+
const INDEX_PATH = SKILLS_INDEX_PATH;
|
|
46
46
|
|
|
47
47
|
// ---------------------------------------------------------------------------
|
|
48
48
|
// Disk I/O
|
|
@@ -30,9 +30,10 @@
|
|
|
30
30
|
|
|
31
31
|
import fs from "node:fs";
|
|
32
32
|
import path from "node:path";
|
|
33
|
-
import
|
|
33
|
+
import { SKILLS_DIR } from "#core/config/paths.js";
|
|
34
34
|
import { fileURLToPath } from "node:url";
|
|
35
35
|
import { apcSkillsDir } from "#core/apc/paths.js";
|
|
36
|
+
import { parseFrontmatter } from "#core/apc/frontmatter.js";
|
|
36
37
|
|
|
37
38
|
const __filename = fileURLToPath(import.meta.url);
|
|
38
39
|
const __dirname = path.dirname(__filename);
|
|
@@ -44,32 +45,12 @@ const PACKAGE_ROOT = path.resolve(__dirname, "..", "..", "..", "..");
|
|
|
44
45
|
// <packageRoot>/skills/ so external tools that copy "skills/" from the repo
|
|
45
46
|
// don't accidentally pull the rich set or the runtime CLI docs.
|
|
46
47
|
const BUILTIN_SKILLS_DIR = path.join(PACKAGE_ROOT, "src", "core", "runtime-skills");
|
|
47
|
-
const GLOBAL_DIR =
|
|
48
|
+
const GLOBAL_DIR = SKILLS_DIR;
|
|
48
49
|
|
|
49
50
|
// ---------------------------------------------------------------------------
|
|
50
51
|
// Frontmatter parsing (minimal — handles the YAML we ship)
|
|
51
52
|
// ---------------------------------------------------------------------------
|
|
52
53
|
|
|
53
|
-
function parseFrontmatter(raw) {
|
|
54
|
-
if (!raw.startsWith("---")) return { fm: {}, body: raw };
|
|
55
|
-
const end = raw.indexOf("\n---", 3);
|
|
56
|
-
if (end < 0) return { fm: {}, body: raw };
|
|
57
|
-
|
|
58
|
-
const fmBlock = raw.slice(3, end).trim();
|
|
59
|
-
const body = raw.slice(end + 4).replace(/^\n/, "");
|
|
60
|
-
|
|
61
|
-
const fm = {};
|
|
62
|
-
for (const line of fmBlock.split("\n")) {
|
|
63
|
-
const m = line.match(/^([a-zA-Z_][a-zA-Z0-9_-]*)\s*:\s*(.*)$/);
|
|
64
|
-
if (!m) continue;
|
|
65
|
-
let val = m[2].trim();
|
|
66
|
-
if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
|
|
67
|
-
val = val.slice(1, -1);
|
|
68
|
-
}
|
|
69
|
-
fm[m[1]] = val;
|
|
70
|
-
}
|
|
71
|
-
return { fm, body };
|
|
72
|
-
}
|
|
73
54
|
|
|
74
55
|
// ---------------------------------------------------------------------------
|
|
75
56
|
// Directory scanners
|
|
@@ -80,8 +80,6 @@ export async function runSuperAgent({
|
|
|
80
80
|
throw new Error("super-agent not enabled (set super_agent.enabled and .model in ~/.apx/config.json)");
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
const sa = globalConfig.super_agent;
|
|
84
|
-
|
|
85
83
|
// Memory Broker (Pieza 4): assemble the [MEMORIA RELEVANTE] block before the
|
|
86
84
|
// turn. Silent + bounded (≤ broker_budget_ms); skipped for tool-free callers
|
|
87
85
|
// (summarize/ask) where injected context would only confuse the transcript.
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
1
|
import path from "node:path";
|
|
3
2
|
import { loggerFor } from "#core/logging.js";
|
|
4
3
|
import { readAgents } from "#core/apc/parser.js";
|
|
@@ -15,9 +14,10 @@ import {
|
|
|
15
14
|
findEngineSessionById,
|
|
16
15
|
readEngineSessionContext,
|
|
17
16
|
} from "#core/stores/engine-sessions.js";
|
|
18
|
-
import { runProcess } from "#
|
|
19
|
-
import { getRuntime, RUNTIME_IDS } from "#
|
|
17
|
+
import { runProcess } from "#core/runtimes/_spawn.js";
|
|
18
|
+
import { getRuntime, RUNTIME_IDS } from "#core/runtimes/index.js";
|
|
20
19
|
import { buildAgentSystem, resolveProject } from "../helpers.js";
|
|
20
|
+
import { readJson } from "#core/util/json-file.js";
|
|
21
21
|
|
|
22
22
|
const log = loggerFor("call_runtime");
|
|
23
23
|
|
|
@@ -90,7 +90,7 @@ function resolveProjectForAgent(projects, project, slug) {
|
|
|
90
90
|
|
|
91
91
|
function projectName(project) {
|
|
92
92
|
try {
|
|
93
|
-
const meta =
|
|
93
|
+
const meta = readJson(apcProjectFile(project.path), {});
|
|
94
94
|
if (meta.name) return meta.name;
|
|
95
95
|
} catch {}
|
|
96
96
|
return path.basename(project.path);
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
ENGINES,
|
|
4
4
|
collectAllSessions,
|
|
5
5
|
findSessionAcrossEngines,
|
|
6
|
-
} from "#
|
|
6
|
+
} from "#core/sessions/index.js";
|
|
7
7
|
|
|
8
8
|
// Your own work sessions. This is how you remember what you did — never tell
|
|
9
9
|
// the user to run `apx session ...`; call this and answer from the result.
|