nodal-agents 0.4.4 → 0.5.2

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 (206) hide show
  1. package/README.md +54 -21
  2. package/cli.js +95 -105
  3. package/migrations/0035_community_agent_skills.sql +13 -0
  4. package/migrations/0036_agent_jobs_effective_input_tokens.sql +18 -0
  5. package/migrations/0037_agent_jobs_total_cost_usd.sql +11 -0
  6. package/migrations/0038_agent_jobs_served_provider.sql +7 -0
  7. package/migrations/0039_agent_skills_learning_loop.sql +20 -0
  8. package/migrations/0040_entities_last_curator_run.sql +7 -0
  9. package/migrations/0041_reflection_enabled.sql +1 -0
  10. package/migrations/0042_drop_agent_runs.sql +1 -0
  11. package/migrations/0043_skill_assignment.sql +2 -0
  12. package/migrations/0044_entities_lan_command_yolo.sql +1 -0
  13. package/migrations/meta/_journal.json +314 -244
  14. package/package.json +4 -1
  15. package/runner.js +12187 -2147
  16. package/web/.next/BUILD_ID +1 -1
  17. package/web/.next/app-path-routes-manifest.json +11 -10
  18. package/web/.next/build-manifest.json +2 -2
  19. package/web/.next/prerender-manifest.json +3 -3
  20. package/web/.next/routes-manifest.json +6 -0
  21. package/web/.next/server/app/(dashboard)/agents/[id]/edit/page.js +3 -3
  22. package/web/.next/server/app/(dashboard)/agents/[id]/edit/page.js.nft.json +1 -1
  23. package/web/.next/server/app/(dashboard)/agents/[id]/edit/page_client-reference-manifest.js +1 -1
  24. package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page.js +2 -2
  25. package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page.js.nft.json +1 -1
  26. package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page_client-reference-manifest.js +1 -1
  27. package/web/.next/server/app/(dashboard)/agents/page.js +2 -2
  28. package/web/.next/server/app/(dashboard)/agents/page.js.nft.json +1 -1
  29. package/web/.next/server/app/(dashboard)/agents/page_client-reference-manifest.js +1 -1
  30. package/web/.next/server/app/(dashboard)/approvals/page.js +2 -2
  31. package/web/.next/server/app/(dashboard)/approvals/page.js.nft.json +1 -1
  32. package/web/.next/server/app/(dashboard)/approvals/page_client-reference-manifest.js +1 -1
  33. package/web/.next/server/app/(dashboard)/automations/page.js +2 -2
  34. package/web/.next/server/app/(dashboard)/automations/page.js.nft.json +1 -1
  35. package/web/.next/server/app/(dashboard)/automations/page_client-reference-manifest.js +1 -1
  36. package/web/.next/server/app/(dashboard)/billing/page.js +2 -2
  37. package/web/.next/server/app/(dashboard)/billing/page.js.nft.json +1 -1
  38. package/web/.next/server/app/(dashboard)/billing/page_client-reference-manifest.js +1 -1
  39. package/web/.next/server/app/(dashboard)/chat/page.js +2 -2
  40. package/web/.next/server/app/(dashboard)/chat/page.js.nft.json +1 -1
  41. package/web/.next/server/app/(dashboard)/chat/page_client-reference-manifest.js +1 -1
  42. package/web/.next/server/app/(dashboard)/connectors/page.js +2 -2
  43. package/web/.next/server/app/(dashboard)/connectors/page.js.nft.json +1 -1
  44. package/web/.next/server/app/(dashboard)/connectors/page_client-reference-manifest.js +1 -1
  45. package/web/.next/server/app/(dashboard)/credentials/page.js +2 -2
  46. package/web/.next/server/app/(dashboard)/credentials/page.js.nft.json +1 -1
  47. package/web/.next/server/app/(dashboard)/credentials/page_client-reference-manifest.js +1 -1
  48. package/web/.next/server/app/(dashboard)/jobs/[id]/page.js +2 -2
  49. package/web/.next/server/app/(dashboard)/jobs/[id]/page.js.nft.json +1 -1
  50. package/web/.next/server/app/(dashboard)/jobs/[id]/page_client-reference-manifest.js +1 -1
  51. package/web/.next/server/app/(dashboard)/jobs/page.js +2 -2
  52. package/web/.next/server/app/(dashboard)/jobs/page.js.nft.json +1 -1
  53. package/web/.next/server/app/(dashboard)/jobs/page_client-reference-manifest.js +1 -1
  54. package/web/.next/server/app/(dashboard)/learned-skills/page.js +2 -0
  55. package/web/.next/server/app/(dashboard)/learned-skills/page.js.nft.json +1 -0
  56. package/web/.next/server/app/(dashboard)/learned-skills/page_client-reference-manifest.js +1 -0
  57. package/web/.next/server/app/(dashboard)/llm-providers/page.js +2 -2
  58. package/web/.next/server/app/(dashboard)/llm-providers/page.js.nft.json +1 -1
  59. package/web/.next/server/app/(dashboard)/llm-providers/page_client-reference-manifest.js +1 -1
  60. package/web/.next/server/app/(dashboard)/logs/page.js +2 -2
  61. package/web/.next/server/app/(dashboard)/logs/page.js.nft.json +1 -1
  62. package/web/.next/server/app/(dashboard)/logs/page_client-reference-manifest.js +1 -1
  63. package/web/.next/server/app/(dashboard)/mcp/page.js +2 -2
  64. package/web/.next/server/app/(dashboard)/mcp/page.js.nft.json +1 -1
  65. package/web/.next/server/app/(dashboard)/mcp/page_client-reference-manifest.js +1 -1
  66. package/web/.next/server/app/(dashboard)/memories/page.js +2 -2
  67. package/web/.next/server/app/(dashboard)/memories/page.js.nft.json +1 -1
  68. package/web/.next/server/app/(dashboard)/memories/page_client-reference-manifest.js +1 -1
  69. package/web/.next/server/app/(dashboard)/page.js +4 -4
  70. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  71. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  72. package/web/.next/server/app/(dashboard)/settings/page.js +2 -2
  73. package/web/.next/server/app/(dashboard)/settings/page.js.nft.json +1 -1
  74. package/web/.next/server/app/(dashboard)/settings/page_client-reference-manifest.js +1 -1
  75. package/web/.next/server/app/(dashboard)/skills/[id]/edit/page.js +2 -2
  76. package/web/.next/server/app/(dashboard)/skills/[id]/edit/page.js.nft.json +1 -1
  77. package/web/.next/server/app/(dashboard)/skills/[id]/edit/page_client-reference-manifest.js +1 -1
  78. package/web/.next/server/app/(dashboard)/skills/new/page.js +2 -2
  79. package/web/.next/server/app/(dashboard)/skills/new/page.js.nft.json +1 -1
  80. package/web/.next/server/app/(dashboard)/skills/new/page_client-reference-manifest.js +1 -1
  81. package/web/.next/server/app/(dashboard)/skills/page.js +2 -2
  82. package/web/.next/server/app/(dashboard)/skills/page.js.nft.json +1 -1
  83. package/web/.next/server/app/(dashboard)/skills/page_client-reference-manifest.js +1 -1
  84. package/web/.next/server/app/_global-error/page.js +3 -3
  85. package/web/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  86. package/web/.next/server/app/_global-error.html +1 -1
  87. package/web/.next/server/app/_global-error.rsc +3 -3
  88. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +3 -3
  89. package/web/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  90. package/web/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  91. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  92. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  93. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  94. package/web/.next/server/app/_not-found/page.js +2 -2
  95. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  96. package/web/.next/server/app/_not-found.html +1 -1
  97. package/web/.next/server/app/_not-found.rsc +4 -4
  98. package/web/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
  99. package/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  100. package/web/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
  101. package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  102. package/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  103. package/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  104. package/web/.next/server/app/api/auth/[...all]/route.js +1 -1
  105. package/web/.next/server/app/api/auth/[...all]/route.js.nft.json +1 -1
  106. package/web/.next/server/app/api/health/route.js +1 -1
  107. package/web/.next/server/app/api/oauth/[provider]/callback/route.js +1 -1
  108. package/web/.next/server/app/api/oauth/[provider]/callback/route.js.nft.json +1 -1
  109. package/web/.next/server/app/api/oauth/[provider]/start/route.js +1 -1
  110. package/web/.next/server/app/api/oauth/[provider]/start/route.js.nft.json +1 -1
  111. package/web/.next/server/app/auth/callback/route.js +1 -1
  112. package/web/.next/server/app/login/page.js +2 -2
  113. package/web/.next/server/app/login/page_client-reference-manifest.js +1 -1
  114. package/web/.next/server/app/onboarding/page.js +2 -2
  115. package/web/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
  116. package/web/.next/server/app/onboarding.html +1 -1
  117. package/web/.next/server/app/onboarding.rsc +4 -4
  118. package/web/.next/server/app/onboarding.segments/_full.segment.rsc +4 -4
  119. package/web/.next/server/app/onboarding.segments/_head.segment.rsc +1 -1
  120. package/web/.next/server/app/onboarding.segments/_index.segment.rsc +4 -4
  121. package/web/.next/server/app/onboarding.segments/_tree.segment.rsc +2 -2
  122. package/web/.next/server/app/onboarding.segments/onboarding/__PAGE__.segment.rsc +1 -1
  123. package/web/.next/server/app/onboarding.segments/onboarding.segment.rsc +1 -1
  124. package/web/.next/server/app-paths-manifest.json +11 -10
  125. package/web/.next/server/chunks/2556.js +1 -0
  126. package/web/.next/server/chunks/3279.js +1 -0
  127. package/web/.next/server/chunks/3636.js +1 -0
  128. package/web/.next/server/chunks/3966.js +1 -0
  129. package/web/.next/server/chunks/4574.js +1 -1
  130. package/web/.next/server/chunks/{3889.js → 5869.js} +1 -1
  131. package/web/.next/server/chunks/6606.js +1 -0
  132. package/web/.next/server/chunks/{7826.js → 8732.js} +27 -27
  133. package/web/.next/server/chunks/9267.js +16 -0
  134. package/web/.next/server/chunks/9656.js +1 -0
  135. package/web/.next/server/chunks/{7741.js → 9942.js} +17 -17
  136. package/web/.next/server/middleware-build-manifest.js +1 -1
  137. package/web/.next/server/pages/404.html +1 -1
  138. package/web/.next/server/pages/500.html +1 -1
  139. package/web/.next/server/server-reference-manifest.js +1 -1
  140. package/web/.next/server/server-reference-manifest.json +1 -1
  141. package/web/.next/static/FgBmSI8AhGeFcxmEUF1VS/_buildManifest.js +1 -0
  142. package/web/.next/static/chunks/3178-bde220b789f10130.js +1 -0
  143. package/web/.next/static/chunks/4454-882460b60106f0d5.js +1 -0
  144. package/web/.next/static/chunks/6078-abdc3955397924b3.js +1 -0
  145. package/web/.next/static/chunks/6413-c2d4ac3709048f19.js +1 -0
  146. package/web/.next/static/chunks/{8666-90a1f5d99b79411e.js → 6908-e8114db85dca4371.js} +1 -1
  147. package/web/.next/static/chunks/7164-a5cae6a1833041a7.js +1 -0
  148. package/web/.next/static/chunks/9711-628acc64d23620d2.js +1 -0
  149. package/web/.next/static/chunks/app/(dashboard)/agents/[id]/edit/page-9d2c7d640411e42d.js +2 -0
  150. package/web/.next/static/chunks/app/(dashboard)/agents/[id]/telegram/page-1471b733a0d6dd17.js +1 -0
  151. package/web/.next/static/chunks/app/(dashboard)/agents/page-2a30943e74c78967.js +1 -0
  152. package/web/.next/static/chunks/app/(dashboard)/approvals/page-8556dfa45a425603.js +1 -0
  153. package/web/.next/static/chunks/app/(dashboard)/automations/page-3b724d8f6e8c957f.js +1 -0
  154. package/web/.next/static/chunks/app/(dashboard)/chat/page-b98de38c17f2cd96.js +1 -0
  155. package/web/.next/static/chunks/app/(dashboard)/connectors/page-6591a8753b6a19c1.js +1 -0
  156. package/web/.next/static/chunks/app/(dashboard)/credentials/page-19061f4a8bf1978f.js +1 -0
  157. package/web/.next/static/chunks/app/(dashboard)/jobs/[id]/page-7e9af908ef12f252.js +1 -0
  158. package/web/.next/static/chunks/app/(dashboard)/jobs/page-9b09d95cc1a8053c.js +1 -0
  159. package/web/.next/static/chunks/app/(dashboard)/layout-a34502d1a8386a62.js +1 -0
  160. package/web/.next/static/chunks/app/(dashboard)/learned-skills/page-447ba18bc6af3c57.js +1 -0
  161. package/web/.next/static/chunks/app/(dashboard)/llm-providers/page-c0c5aa806b9b47d4.js +1 -0
  162. package/web/.next/static/chunks/app/(dashboard)/logs/page-21d587049e8a6ccb.js +1 -0
  163. package/web/.next/static/chunks/app/(dashboard)/mcp/page-f9337250ed07249e.js +1 -0
  164. package/web/.next/static/chunks/app/(dashboard)/memories/page-b935752c6cb36a04.js +1 -0
  165. package/web/.next/static/chunks/app/(dashboard)/{page-29cfae3bf701076e.js → page-2a69294b49293833.js} +1 -1
  166. package/web/.next/static/chunks/app/(dashboard)/settings/page-cc13aebbb8d33d19.js +1 -0
  167. package/web/.next/static/chunks/app/(dashboard)/skills/[id]/edit/page-85c75cc88ee1d6a8.js +1 -0
  168. package/web/.next/static/chunks/app/(dashboard)/skills/new/page-9b0777fccb89a00b.js +1 -0
  169. package/web/.next/static/chunks/app/(dashboard)/skills/page-e25db7f660751dfd.js +1 -0
  170. package/web/.next/static/chunks/app/error-f106e9e65e16edb3.js +1 -0
  171. package/web/.next/static/css/de0fc0bc138be3b3.css +3 -0
  172. package/web/.next/server/chunks/3233.js +0 -1
  173. package/web/.next/server/chunks/4808.js +0 -1
  174. package/web/.next/server/chunks/5329.js +0 -1
  175. package/web/.next/server/chunks/593.js +0 -1
  176. package/web/.next/server/chunks/6128.js +0 -1
  177. package/web/.next/server/chunks/7231.js +0 -1
  178. package/web/.next/server/chunks/7466.js +0 -16
  179. package/web/.next/static/PV5qpGylrVW3jZ7zKHuhE/_buildManifest.js +0 -1
  180. package/web/.next/static/chunks/8396-f3502b9af3172006.js +0 -1
  181. package/web/.next/static/chunks/9098-2bfef80a73c706b3.js +0 -1
  182. package/web/.next/static/chunks/9123-67530ba510c58003.js +0 -1
  183. package/web/.next/static/chunks/9167-12a4a42d1a7d7db5.js +0 -1
  184. package/web/.next/static/chunks/9582-fbf7c8d9b2a39101.js +0 -1
  185. package/web/.next/static/chunks/app/(dashboard)/agents/[id]/edit/page-a2c267f563cd1a70.js +0 -2
  186. package/web/.next/static/chunks/app/(dashboard)/agents/[id]/telegram/page-2d7cae43f5e6952a.js +0 -1
  187. package/web/.next/static/chunks/app/(dashboard)/agents/page-bed833ebdd3646fb.js +0 -1
  188. package/web/.next/static/chunks/app/(dashboard)/approvals/page-4b27762472802ce0.js +0 -1
  189. package/web/.next/static/chunks/app/(dashboard)/automations/page-9e5d2ce807ce1c37.js +0 -1
  190. package/web/.next/static/chunks/app/(dashboard)/chat/page-8f81d7e3abb475a6.js +0 -1
  191. package/web/.next/static/chunks/app/(dashboard)/connectors/page-39b1d9560235d574.js +0 -1
  192. package/web/.next/static/chunks/app/(dashboard)/credentials/page-f1e797e327ecdf7c.js +0 -1
  193. package/web/.next/static/chunks/app/(dashboard)/jobs/[id]/page-112bb22a9ec43be0.js +0 -1
  194. package/web/.next/static/chunks/app/(dashboard)/jobs/page-44351170ed5f9180.js +0 -1
  195. package/web/.next/static/chunks/app/(dashboard)/layout-53e69de869cba0d0.js +0 -1
  196. package/web/.next/static/chunks/app/(dashboard)/llm-providers/page-8f6ce47ba228e38b.js +0 -1
  197. package/web/.next/static/chunks/app/(dashboard)/logs/page-b814deb9854b0cb5.js +0 -1
  198. package/web/.next/static/chunks/app/(dashboard)/mcp/page-cdf057e468c6e92e.js +0 -1
  199. package/web/.next/static/chunks/app/(dashboard)/memories/page-e706ab4aa681fa99.js +0 -1
  200. package/web/.next/static/chunks/app/(dashboard)/settings/page-312ae27ae71ea6d1.js +0 -1
  201. package/web/.next/static/chunks/app/(dashboard)/skills/[id]/edit/page-89a8a30db5eb96e1.js +0 -1
  202. package/web/.next/static/chunks/app/(dashboard)/skills/new/page-46a424b203591560.js +0 -1
  203. package/web/.next/static/chunks/app/(dashboard)/skills/page-edad7bbd3230fb7a.js +0 -1
  204. package/web/.next/static/chunks/app/error-61e58aa304e1b34e.js +0 -1
  205. package/web/.next/static/css/78ead23854ab041e.css +0 -3
  206. /package/web/.next/static/{PV5qpGylrVW3jZ7zKHuhE → FgBmSI8AhGeFcxmEUF1VS}/_ssgManifest.js +0 -0
package/README.md CHANGED
@@ -23,11 +23,13 @@ with **any LLM** — frontier or local, paid or free.
23
23
  | | |
24
24
  | --- | --- |
25
25
  | 🏠  **Local-first** | Single binary, embedded Postgres, zero cloud dependency. Your conversations, memory, and credentials stay on your machine. |
26
- | 🔌  **Any model, per agent** | Anthropic, OpenAI, Google, Groq, Mistral, OpenRouter, or any local model (LM Studio, Ollama). Setup is just an API key per provider — **each agent picks its own model**, so you can run Claude for the orchestrator and **DeepSeek V4** for the workers. The quirks of OSS frontier models (DeepSeek's non-spec tool args, Kimi/Qwen/GLM XML tool formats, and round-tripping a reasoning model's chain-of-thought across tool calls so MiniMax M3 / DeepSeek / Gemini 3 don't degrade mid-task) are handled automatically. |
26
+ | 🔌  **Any model, per agent** | Anthropic, OpenAI, Google, Groq, Mistral, OpenRouter, **native DeepSeek** (`api.deepseek.com`) and **native MiniMax** (`api.minimax.io`), or any local model (LM Studio, Ollama). Setup is just an API key per provider — **each agent picks its own model**, so you can run Claude for the orchestrator and **DeepSeek V4** for the workers. The quirks of OSS frontier models (DeepSeek's non-spec tool args, Kimi/Qwen/GLM XML tool formats, and round-tripping a reasoning model's chain-of-thought across tool calls so MiniMax M3 / DeepSeek / Gemini 3 don't degrade mid-task) are handled automatically. |
27
27
  | 🛟  **Provider failover** | Give an agent a backup key chain — if its provider 5xx's, times out, or hits quota mid-job, the runner fails over to the next one and keeps going (and fails loud only when the whole chain is down). |
28
28
  | 🧠  **Memory that compounds** | Persistent facts (entity-scoped, auto-injected into every job) and chat-thread continuity (your agent remembers what it said 30 seconds ago — and what it said yesterday). |
29
- | 🤝  **Orchestrators that finish** | Router and planner orchestrators delegate to specialist sub-agents, then resume on completion to wrap up the answer. |
30
- | 🛡️  **Agents that don't lie, loop, or die** | Generic runtime guards: a per-job token budget and a no-progress detector kill runaway loops; a no-false-success guard refuses to report "done" when an action actually failed (fail loud, never fake it); turn/chain/delegation caps bound everything. |
29
+ | 🤝  **Orchestrators that finish** | Every orchestrator picks the delegation style per request — route to one specialist and resume on its result, or fan out independent work to many sub-agents in parallel and compile their results — then wraps up the answer. |
30
+ | 🌱  **Self-improving agents** | Opt-in learning loop: after a substantial job, an agent reflects on the transcript and writes itself a reusable skill; a weekly curator consolidates and prunes them. Every learned skill is reviewable, assignable, and revocable from the dashboard (off by default, per workspace). |
31
+ | 📥  **Install any community skill** | Point it at any open `SKILL.md` — a GitHub repo, a skills.sh slug, or a ClawHub package — and it fetches, unpacks, and installs it as a first-class skill. Pure HTTPS fetch with SSRF allow-listing and zip-slip guards; bundled scripts are flagged, never executed. |
32
+ | 🛡️  **Agents that don't lie, loop, or die** | Generic runtime guards: a per-job **real-dollar cost cap** (from the provider's actual billed cost) and a no-progress detector kill runaway loops; a no-false-success guard refuses to report "done" when an action actually failed; an atomic job claim prevents the same job running twice; and every failed or blocked job hands you a short, specific reason — never a silent stop. Turn/chain/delegation caps bound everything. |
31
33
  | 🔧  **Multi-instance connectors** | Gmail perso *and* Gmail boulot on the same install. OAuth *and* API-key supported. Active list + Marketplace UI in the dashboard. |
32
34
  | 🗂️  **Workspaces** | Multiple isolated workspaces on one install (personal vs work) — each with its own agents, skills, connectors, jobs and memory. Switch from the sidebar. |
33
35
  | 🤖  **Self-extending (ROOT agent)** | Designate an orchestrator as ROOT and let it create skills/agents and assign them on your behalf — gated by per-grant toggles and an autonomy level (propose-confirm → fully-autonomous). |
@@ -84,7 +86,7 @@ When a newer version is available, `nodal-agents up` also prints a one-line
84
86
  notice:
85
87
 
86
88
  ```
87
- ℹ v0.4.4 available — run `nodal-agents update`
89
+ ℹ v0.5.1 available — run `nodal-agents update`
88
90
  ```
89
91
 
90
92
  ### Build from source
@@ -144,7 +146,8 @@ Delegations create child jobs that resume the parent on completion.
144
146
  | `/connectors` | Active connector instances + Marketplace (multi-instance, OAuth or API-key). |
145
147
  | `/mcp` | Active MCP servers + Marketplace — HTTP & stdio, a growing catalogue, plus add/edit your own custom servers. |
146
148
  | `/memories` | Persistent facts per entity — search, edit, archive. |
147
- | `/skills` | Assigned / Custom / Built-in Library tabs — reusable instructions appended to an agent's prompt; create your own or customise built-ins. |
149
+ | `/skills` | Assigned / Custom / Built-in Library tabs — reusable instructions appended to an agent's prompt; create your own, customise built-ins, or install any community `SKILL.md` from GitHub / skills.sh / ClawHub. |
150
+ | `/learned-skills` | Skills the agents wrote themselves (learning loop) — review, assign, archive, restore; toggle reflection + auto-assign per workspace. |
148
151
  | `/logs` | Tool-call audit — input/output JSON per call, filterable by tool name. |
149
152
  | `/approvals` | Human-in-the-loop gates for risky tools (and the ROOT agent's meta-tools under propose-confirm). |
150
153
  | `/automations` | Cron-scheduled agent triggers. |
@@ -212,7 +215,7 @@ pnpm deps:check # runs locally and in CI before every release
212
215
 
213
216
  ## Status
214
217
 
215
- **Current release:** `0.4.4` on npm `latest`. Used daily by the
218
+ **Current release:** `0.5.2` on npm `latest`. Used daily by the
216
219
  maintainer, stable enough for personal production. Pre-1.0 — breaking
217
220
  changes are still possible between minors.
218
221
 
@@ -220,26 +223,45 @@ changes are still possible between minors.
220
223
 
221
224
  - Multi-LLM, **per-agent model selection** — provider setup is just an API key
222
225
  (one per provider); each agent chooses its own model on top. Frontier and
223
- local providers, including **DeepSeek V4** (non-spec tool-call args normalized
224
- automatically) and **reasoning models like MiniMax M3** (their hidden
225
- chain-of-thought is round-tripped across tool calls so they keep reasoning on
226
- multi-turn tasks), plus native tool-call parsing for Kimi K2 / Qwen3-Coder / GLM
226
+ local providers, plus **native DeepSeek** (`api.deepseek.com`) and **native
227
+ MiniMax** (`api.minimax.io`) endpoints alongside OpenRouter pick the route
228
+ per key. **DeepSeek V4** non-spec tool-call args are normalized automatically
229
+ and **reasoning models like MiniMax M3** have their hidden chain-of-thought
230
+ round-tripped across tool calls so they keep reasoning on multi-turn tasks,
231
+ plus native tool-call parsing for Kimi K2 / Qwen3-Coder / GLM
227
232
  - **Provider failover** — an opt-in ordered key chain per agent; on a 5xx /
228
233
  timeout / quota mid-job the runner fails over to the next key, and fails loud
229
234
  (`all_providers_failed`) only when the whole chain is exhausted
230
- - **Reliability guards (generic, model-agnostic)** — per-job token budget +
231
- no-progress detector (kill runaway loops), a no-false-success guard that
232
- refuses to complete a job as "success" when a tool action failed and was never
233
- resolved (fail loud, never fake it), and context compaction that evicts stale
234
- tool output before the model's context window overflows
235
- - **Diagnosable failures** every failed job persists its full transcript and
236
- the real upstream provider error (not an opaque "provider returned error"), so
237
- you can see exactly what the agent did and why it stopped
235
+ - **Reliability guards (generic, model-agnostic)** — a per-job **real-dollar
236
+ cost cap** (read from the provider's actually-billed cost, `cost_budget_exceeded`)
237
+ + token budget + no-progress / no-delivery detectors (kill runaway loops), an
238
+ **atomic job claim** so the same job never executes twice, a no-false-success
239
+ guard that refuses to complete a job as "success" when a tool action failed and
240
+ was never resolved (fail loud, never fake it), a **recoverable** path for a
241
+ mis-named tool call (a bounded "did you mean…" nudge, not an instant kill), and
242
+ context compaction that evicts stale tool output before the context window overflows
243
+ - **Never a silent stop** — every failed *or blocked* job hands the user a short,
244
+ specific reason (in the agent's own words when it blocks), propagated up through
245
+ delegation; a blocked job is honestly `failed`, never a fake "completed"
246
+ - **Diagnosable failures** — every failed job persists its full transcript, the
247
+ real upstream provider error (not an opaque "provider returned error"), and the
248
+ actual upstream that served each turn, so you can see exactly what the agent did
249
+ and why it stopped
250
+ - **Self-improving agents (opt-in learning loop)** — after a substantial job, an
251
+ agent reflects on its transcript and writes itself a reusable skill (sandboxed
252
+ provenance); a weekly curator consolidates and prunes them. Review, assign,
253
+ archive or revoke every learned skill from `/learned-skills` (off by default,
254
+ per workspace; auto-assign or require approval)
255
+ - **Install any community skill** — fetch any open `SKILL.md` from a GitHub repo,
256
+ a skills.sh slug, or a ClawHub package and install it as a first-class skill
257
+ (pure HTTPS, SSRF allow-list, zip-slip guard; bundled scripts flagged, never run)
238
258
  - Persistent memory (sanitation, dedup, importance ranking, auto-injection,
239
259
  feedback loop)
240
260
  - Session-thread continuity on chat channels (Telegram today)
241
- - Orchestrator (router + planner) with delegation chains that resume the
242
- parent on completion
261
+ - **Unified orchestrator** every orchestrator gets both delegation styles and
262
+ picks per request: route to one specialist and resume on its result (router),
263
+ or fan out independent tasks to a parallel task board that compiles + delivers
264
+ the combined result (planner). One commits to a single style per job
243
265
  - Multi-instance connectors with OAuth (Gmail, Drive, Sheets, Docs, Notion,
244
266
  Airtable) and API-key (Notion, Airtable, Apify, Firecrawl, Tavily)
245
267
  - MCP catalog — Streamable HTTP *and* stdio (local subprocess) servers, API-key auth; a growing catalogue (Stripe, n8n, Supabase, Airtable, Notion…) with a "test pending" badge on entries not yet verified live, plus add *and edit* your own custom HTTP/stdio servers from the dashboard
@@ -248,14 +270,25 @@ changes are still possible between minors.
248
270
  - ROOT agent — your first orchestrator automatically becomes the workspace ROOT (the single top-level agent; later orchestrators slot under it). It can create *and update* skills, create agents and assign them, and create MCP servers + API connectors — each gated by per-grant toggles + an autonomy/approval level (powers start off, opt in per grant). Provisioning verifies before it writes (an MCP server is connected and its tools listed first); skill authoring is grounded in the workspace's real tools (a linter rejects skills referencing tools the agent doesn't have)
249
271
  - Office file editing — Excel in-place edit, Word/PowerPoint create, in the agent workspace (office-editing skill)
250
272
  - Multiple filesystem folders per agent (sandboxed `file_*` tools)
273
+ - **Shell execution** — agents can run shell commands in their workspace
274
+ (`run_command`: install deps, run scripts, build steps) via an opt-in
275
+ `command-execution` skill. Safe-by-default: every command pauses for your
276
+ approval, with an optional per-agent auto-run ("Yolo"). On a shared / LAN
277
+ install auto-run is gated behind an explicit workspace-owner opt-in and
278
+ **enforced at execution time** — flip the workspace switch off and even an
279
+ already-trusted agent drops back to requiring approval. A live bell surfaces
280
+ pending approvals from anywhere in the dashboard
251
281
  - Telegram delivery (long-poll, group filters, multi-agent routing,
252
282
  delegation gracefulness) — exactly-once delivery contract: anti-spam guard
253
283
  against runaway message loops + a guard that re-prompts (then fails loud)
254
284
  rather than completing a job without ever replying
255
285
  - Approval gates for risky tools (execute-the-approved-action on resume) — on a chat channel, the agent sends a heads-up before the job pauses so the user knows an approval is waiting
256
286
  - Cron scheduling — trigger any automation out-of-band ("Run now") + opt-in Telegram confirmation when a scheduled job succeeds
287
+ - Duplicate any automation in one click; opt-in retention to purge old terminal jobs
257
288
  - `nodal-agents update` — one-command upgrade + boot version notice
258
- - Encryption at rest for LLM keys + MCP keys
289
+ - Encryption at rest for LLM keys + MCP keys; in LAN / multi-user modes the
290
+ runner requires a shared `WORKER_SECRET` on every mutating route (no
291
+ unauthenticated job/LLM spend from other devices on the network)
259
292
  - Embedded Postgres distribution via npm (no external DB to install)
260
293
 
261
294
  ### On the roadmap (genuine, not vaporware)
package/cli.js CHANGED
@@ -11260,7 +11260,7 @@ var init_types2 = __esm({
11260
11260
 
11261
11261
  // ../../packages/shared/src/enums.ts
11262
11262
  import { z as z2 } from "zod";
11263
- var JOB_CHANNELS, JobChannelSchema, JOB_STATUSES, JobStatusSchema, TASK_STATUSES, TaskStatusSchema, TASK_PRIORITIES, TaskPrioritySchema, AGENT_ROLES, AgentRoleSchema, ORCHESTRATOR_MODES, OrchestratorModeSchema, CONNECTOR_AUTH_TYPES, ConnectorAuthTypeSchema, APPROVAL_STATUSES, ApprovalStatusSchema, APPROVAL_RULE_ACTIONS, ApprovalRuleActionSchema, MEMORY_CATEGORIES, MemoryCategorySchema, MEMORY_SOURCES, MemorySourceSchema, MEMORY_LAYERS, MemoryLayerSchema, SCHEDULE_TYPES, ScheduleTypeSchema, SCHEDULE_LAST_STATUSES, ScheduleLastStatusSchema, RUN_KEY_SOURCES, RunKeySourceSchema, ENTITY_MEMBER_ROLES, EntityMemberRoleSchema, ENTITY_INDUSTRIES, EntityIndustrySchema, OPERATION_RISK_LEVELS, OperationRiskLevelSchema, PLUGIN_TYPES, PluginTypeSchema, PLUGIN_HOOKS, PluginHookSchema, MCP_TRANSPORTS, McpTransportSchema;
11263
+ var JOB_CHANNELS, JobChannelSchema, JOB_STATUSES, JobStatusSchema, TASK_STATUSES, TaskStatusSchema, TASK_PRIORITIES, TaskPrioritySchema, AGENT_ROLES, AgentRoleSchema, ORCHESTRATOR_MODES, OrchestratorModeSchema, CONNECTOR_AUTH_TYPES, ConnectorAuthTypeSchema, APPROVAL_STATUSES, ApprovalStatusSchema, APPROVAL_RULE_ACTIONS, ApprovalRuleActionSchema, MEMORY_CATEGORIES, MemoryCategorySchema, MEMORY_SOURCES, MemorySourceSchema, MEMORY_LAYERS, MemoryLayerSchema, SCHEDULE_TYPES, ScheduleTypeSchema, SCHEDULE_LAST_STATUSES, ScheduleLastStatusSchema, ENTITY_MEMBER_ROLES, EntityMemberRoleSchema, ENTITY_INDUSTRIES, EntityIndustrySchema, OPERATION_RISK_LEVELS, OperationRiskLevelSchema, PLUGIN_TYPES, PluginTypeSchema, PLUGIN_HOOKS, PluginHookSchema, MCP_TRANSPORTS, McpTransportSchema;
11264
11264
  var init_enums = __esm({
11265
11265
  "../../packages/shared/src/enums.ts"() {
11266
11266
  "use strict";
@@ -11310,8 +11310,6 @@ var init_enums = __esm({
11310
11310
  ScheduleTypeSchema = z2.enum(SCHEDULE_TYPES);
11311
11311
  SCHEDULE_LAST_STATUSES = ["success", "failed", "no_action"];
11312
11312
  ScheduleLastStatusSchema = z2.enum(SCHEDULE_LAST_STATUSES);
11313
- RUN_KEY_SOURCES = ["entity", "operator"];
11314
- RunKeySourceSchema = z2.enum(RUN_KEY_SOURCES);
11315
11313
  ENTITY_MEMBER_ROLES = ["owner", "admin", "member", "viewer"];
11316
11314
  EntityMemberRoleSchema = z2.enum(ENTITY_MEMBER_ROLES);
11317
11315
  ENTITY_INDUSTRIES = [
@@ -11342,7 +11340,7 @@ var init_enums = __esm({
11342
11340
 
11343
11341
  // ../../packages/shared/src/ids.ts
11344
11342
  import { z as z3 } from "zod";
11345
- var EntityIdSchema, UserIdSchema, AgentIdSchema, JobIdSchema, TaskIdSchema, ConnectorIdSchema, SkillIdSchema, ToolCallIdSchema, ApprovalRequestIdSchema, ApprovalRuleIdSchema, MemoryIdSchema, WebhookTriggerIdSchema, ScheduleIdSchema, McpServerIdSchema, PluginIdSchema, LlmKeyIdSchema, AgentRunIdSchema;
11343
+ var EntityIdSchema, UserIdSchema, AgentIdSchema, JobIdSchema, TaskIdSchema, ConnectorIdSchema, SkillIdSchema, ToolCallIdSchema, ApprovalRequestIdSchema, ApprovalRuleIdSchema, MemoryIdSchema, WebhookTriggerIdSchema, ScheduleIdSchema, McpServerIdSchema, PluginIdSchema, LlmKeyIdSchema;
11346
11344
  var init_ids = __esm({
11347
11345
  "../../packages/shared/src/ids.ts"() {
11348
11346
  "use strict";
@@ -11362,7 +11360,6 @@ var init_ids = __esm({
11362
11360
  McpServerIdSchema = z3.string().guid().brand();
11363
11361
  PluginIdSchema = z3.string().guid().brand();
11364
11362
  LlmKeyIdSchema = z3.string().guid().brand();
11365
- AgentRunIdSchema = z3.string().guid().brand();
11366
11363
  }
11367
11364
  });
11368
11365
 
@@ -11994,44 +11991,6 @@ var init_llm_key = __esm({
11994
11991
  }
11995
11992
  });
11996
11993
 
11997
- // ../../packages/shared/src/entities/agent-run.ts
11998
- import { z as z18 } from "zod";
11999
- var AgentRunSchema, AgentRunInsertSchema;
12000
- var init_agent_run = __esm({
12001
- "../../packages/shared/src/entities/agent-run.ts"() {
12002
- "use strict";
12003
- init_enums();
12004
- AgentRunSchema = z18.object({
12005
- id: z18.string().guid(),
12006
- entity_id: z18.string().guid().nullable(),
12007
- agent_id: z18.string().guid().nullable(),
12008
- task: z18.string().min(1),
12009
- result: z18.string().nullable(),
12010
- success: z18.boolean(),
12011
- tools_used: z18.array(z18.string()),
12012
- tokens_used: z18.number().int().min(0).nullable(),
12013
- input_tokens: z18.number().int().min(0).nullable(),
12014
- output_tokens: z18.number().int().min(0).nullable(),
12015
- duration_ms: z18.number().int().min(0).nullable(),
12016
- key_source: RunKeySourceSchema.nullable(),
12017
- created_at: z18.string().datetime()
12018
- }).strict();
12019
- AgentRunInsertSchema = AgentRunSchema.omit({
12020
- id: true,
12021
- created_at: true
12022
- }).extend({
12023
- success: z18.boolean().default(true),
12024
- tools_used: z18.array(z18.string()).default([]),
12025
- result: z18.string().nullable().optional(),
12026
- tokens_used: z18.number().int().min(0).nullable().optional(),
12027
- input_tokens: z18.number().int().min(0).nullable().optional(),
12028
- output_tokens: z18.number().int().min(0).nullable().optional(),
12029
- duration_ms: z18.number().int().min(0).nullable().optional(),
12030
- key_source: RunKeySourceSchema.nullable().optional()
12031
- });
12032
- }
12033
- });
12034
-
12035
11994
  // ../../packages/shared/src/types/operation.ts
12036
11995
  var init_operation = __esm({
12037
11996
  "../../packages/shared/src/types/operation.ts"() {
@@ -12160,19 +12119,20 @@ var init_providers = __esm({
12160
12119
  });
12161
12120
 
12162
12121
  // ../../packages/shared/src/root-agent.ts
12163
- import { z as z19 } from "zod";
12122
+ import { z as z18 } from "zod";
12164
12123
  var AutonomyLevelSchema, RootGrantsSchema;
12165
12124
  var init_root_agent = __esm({
12166
12125
  "../../packages/shared/src/root-agent.ts"() {
12167
12126
  "use strict";
12168
- AutonomyLevelSchema = z19.enum(["propose_confirm", "destructive_gate", "fully_autonomous"]);
12169
- RootGrantsSchema = z19.object({
12170
- createAgent: z19.boolean(),
12171
- createSkill: z19.boolean(),
12172
- updateSkill: z19.boolean(),
12173
- assignSkill: z19.boolean(),
12174
- createMcp: z19.boolean(),
12175
- createConnector: z19.boolean(),
12127
+ AutonomyLevelSchema = z18.enum(["propose_confirm", "destructive_gate", "fully_autonomous"]);
12128
+ RootGrantsSchema = z18.object({
12129
+ createAgent: z18.boolean(),
12130
+ attachAgent: z18.boolean(),
12131
+ createSkill: z18.boolean(),
12132
+ updateSkill: z18.boolean(),
12133
+ assignSkill: z18.boolean(),
12134
+ createMcp: z18.boolean(),
12135
+ createConnector: z18.boolean(),
12176
12136
  autonomy: AutonomyLevelSchema
12177
12137
  });
12178
12138
  }
@@ -12212,7 +12172,6 @@ var init_src2 = __esm({
12212
12172
  init_skill();
12213
12173
  init_schedule();
12214
12174
  init_llm_key();
12215
- init_agent_run();
12216
12175
  init_operation();
12217
12176
  init_providers();
12218
12177
  init_root_agent();
@@ -12320,7 +12279,17 @@ var init_entities = __esm({
12320
12279
  // Default is an empty object — the runtime falls back to DEFAULT_ROOT_GRANTS.
12321
12280
  rootGrants: jsonb("root_grants").notNull().default(sql`'{}'::jsonb`),
12322
12281
  createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
12323
- updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow()
12282
+ updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(),
12283
+ lastCuratorRunAt: timestamp("last_curator_run_at", { withTimezone: true }),
12284
+ reflectionEnabled: boolean("reflection_enabled").notNull().default(false),
12285
+ // Controls whether agent-authored skills are auto-assigned to the authoring
12286
+ // agent ('auto') or queued for the entity owner to approve ('approval').
12287
+ skillAssignmentMode: text("skill_assignment_mode").notNull().default("approval").$type(),
12288
+ // Allows the workspace OWNER to opt into Yolo (auto-approve run_command)
12289
+ // even in non-local-trust (LAN/local-auth) mode. Off by default: in
12290
+ // local-auth mode, commands always require approval unless the owner
12291
+ // explicitly opts in here.
12292
+ lanCommandYolo: boolean("lan_command_yolo").notNull().default(false)
12324
12293
  },
12325
12294
  (table) => [
12326
12295
  uniqueIndex("entities_mcp_token_idx").on(table.mcpToken),
@@ -12519,6 +12488,37 @@ var init_jobs = __esm({
12519
12488
  totalDurationMs: integer("total_duration_ms").default(0),
12520
12489
  inputTokens: integer("input_tokens").default(0),
12521
12490
  outputTokens: integer("output_tokens").default(0),
12491
+ /**
12492
+ * Cumulative EFFECTIVE (non-cached) input tokens = Σ(inputTokens −
12493
+ * cachedInputTokens) per turn. The token budget (Guard 1a) measures this,
12494
+ * not raw input, so a job that re-sends a prompt-cached history (cheap, the
12495
+ * bulk read from cache) is not penalised as if every re-send were fresh.
12496
+ * Persisted alongside input_tokens so the budget stays cumulative across
12497
+ * self-chain resumes.
12498
+ */
12499
+ effectiveInputTokens: integer("effective_input_tokens").default(0),
12500
+ /**
12501
+ * Cumulative real dollar cost billed to the provider across all turns of
12502
+ * this job (sum of per-call costs reported by the provider). Populated only
12503
+ * when the provider reports per-call cost (OpenRouter with
12504
+ * `usage:{include:true}`); undefined/null for providers that don't report it
12505
+ * (Anthropic, Ollama, etc.). Used by the cost-budget guard (Guard 1e) and
12506
+ * for cost observability in the dashboard.
12507
+ *
12508
+ * Stored as a real/float because sub-cent values are common (e.g. $0.00056).
12509
+ * Cumulative across self-chain / approval / delegation resumes, exactly like
12510
+ * effectiveInputTokens.
12511
+ */
12512
+ totalCostUsd: real("total_cost_usd").default(0),
12513
+ /**
12514
+ * The upstream provider that actually served the last LLM call for this job
12515
+ * (e.g. 'DeepSeek' when an OpenRouter job was routed to the DeepSeek
12516
+ * upstream via provider-order preference). Populated from
12517
+ * `providerMetadata.openrouter.provider` on each LLM call; only the last
12518
+ * non-empty value is stored. NULL for providers that don't report upstream
12519
+ * identity (Anthropic, Ollama, etc.) or when the field was absent.
12520
+ */
12521
+ servedProvider: text("served_provider"),
12522
12522
  delegationDepth: integer("delegation_depth").default(0),
12523
12523
  /**
12524
12524
  * The slug of the last delegated child that failed on this parent job.
@@ -12924,12 +12924,38 @@ var init_skills = __esm({
12924
12924
  requiredConfig: jsonb("required_config").default(sql`'[]'::jsonb`),
12925
12925
  operations: jsonb("operations").default(sql`'[]'::jsonb`),
12926
12926
  requiredBuiltins: text("required_builtins").array().notNull().default(sql`'{}'::text[]`),
12927
+ // ─── Community-installed skills (open Agent Skills / SKILL.md format) ───
12928
+ // is_community = true for skills installed at runtime from an external
12929
+ // source (vs system catalog or user-authored custom skills). source is the
12930
+ // install origin (GitHub URL / skills.sh path). installed_scripts records
12931
+ // the bundled scripts detected at install (.py/.sh/etc.) — surfaced to the
12932
+ // user as a warning, since the runtime does NOT execute skill scripts.
12933
+ isCommunity: boolean("is_community").notNull().default(false),
12934
+ source: text("source"),
12935
+ installedScripts: jsonb("installed_scripts").$type(),
12936
+ // ─── Learning-loop columns (Phase A) ─────────────────────────────────────
12937
+ // createdBy: provenance — 'user' (default) | 'system' | 'agent'
12938
+ // state: lifecycle — 'active' (default) | 'stale' | 'archived'
12939
+ // lastUsedAt: fire-and-forget timestamp bumped each job, NULL = never used
12940
+ // patchCount: agent-authored patches applied so far
12941
+ // archivedAt: when the skill moved to state='archived' (NULL while active)
12942
+ createdBy: text("created_by").notNull().default("user"),
12943
+ // Which agent authored this skill (populated by the Tier-1 reflection pass).
12944
+ // NULL for user/system skills and curator umbrella skills (created_by_agent_id=null).
12945
+ createdByAgentId: uuid("created_by_agent_id").references(() => agents.id, {
12946
+ onDelete: "set null"
12947
+ }),
12948
+ state: text("state").notNull().default("active"),
12949
+ lastUsedAt: timestamp("last_used_at", { withTimezone: true }),
12950
+ patchCount: integer("patch_count").notNull().default(0),
12951
+ archivedAt: timestamp("archived_at", { withTimezone: true }),
12927
12952
  createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
12928
12953
  updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow()
12929
12954
  },
12930
12955
  (table) => [
12931
12956
  index("idx_agent_skills_entity_id").on(table.entityId),
12932
- index("idx_skills_active").on(table.active, table.slug)
12957
+ index("idx_skills_active").on(table.active, table.slug),
12958
+ index("idx_agent_skills_is_community").on(table.isCommunity)
12933
12959
  ]
12934
12960
  );
12935
12961
  skillVersions = pgTable(
@@ -13022,46 +13048,6 @@ var init_schedules = __esm({
13022
13048
  }
13023
13049
  });
13024
13050
 
13025
- // ../../packages/db/src/schema/runs.ts
13026
- var agentRuns;
13027
- var init_runs = __esm({
13028
- "../../packages/db/src/schema/runs.ts"() {
13029
- "use strict";
13030
- init_pg_core();
13031
- init_drizzle_orm();
13032
- init_entities();
13033
- init_agents();
13034
- agentRuns = pgTable(
13035
- "agent_runs",
13036
- {
13037
- id: uuid("id").primaryKey().defaultRandom(),
13038
- entityId: uuid("entity_id").references(() => entities.id, { onDelete: "cascade" }),
13039
- agentId: uuid("agent_id").references(() => agents.id, { onDelete: "cascade" }),
13040
- task: text("task").notNull(),
13041
- result: text("result"),
13042
- success: boolean("success").default(true),
13043
- toolsUsed: text("tools_used").array().default(sql`'{}'::text[]`),
13044
- tokensUsed: integer("tokens_used"),
13045
- inputTokens: integer("input_tokens"),
13046
- outputTokens: integer("output_tokens"),
13047
- durationMs: integer("duration_ms"),
13048
- keySource: text("key_source"),
13049
- createdAt: timestamp("created_at", { withTimezone: true }).defaultNow()
13050
- },
13051
- (table) => [
13052
- index("idx_agent_runs_entity_id").on(table.entityId),
13053
- index("idx_agent_runs_agent_created").on(table.agentId, sql`${table.createdAt} DESC`),
13054
- index("idx_runs_agent").on(table.agentId, sql`${table.createdAt} DESC`),
13055
- index("idx_runs_recent").on(sql`${table.createdAt} DESC`),
13056
- check(
13057
- "agent_runs_key_source_check",
13058
- sql`${table.keySource} IN ('entity','operator') OR ${table.keySource} IS NULL`
13059
- )
13060
- ]
13061
- );
13062
- }
13063
- });
13064
-
13065
13051
  // ../../packages/db/src/schema/mcp.ts
13066
13052
  var mcpServers, agentMcpServers, mcpConnections;
13067
13053
  var init_mcp = __esm({
@@ -13385,7 +13371,6 @@ __export(schema_exports, {
13385
13371
  ORCHESTRATOR_MODES: () => ORCHESTRATOR_MODES,
13386
13372
  PLUGIN_HOOKS: () => PLUGIN_HOOKS,
13387
13373
  PLUGIN_TYPES: () => PLUGIN_TYPES,
13388
- RUN_KEY_SOURCES: () => RUN_KEY_SOURCES,
13389
13374
  SCHEDULE_LAST_STATUSES: () => SCHEDULE_LAST_STATUSES,
13390
13375
  SCHEDULE_TYPES: () => SCHEDULE_TYPES,
13391
13376
  TASK_PRIORITIES: () => TASK_PRIORITIES,
@@ -13398,7 +13383,6 @@ __export(schema_exports, {
13398
13383
  agentMcpServers: () => agentMcpServers,
13399
13384
  agentMemory: () => agentMemory,
13400
13385
  agentPlugins: () => agentPlugins,
13401
- agentRuns: () => agentRuns,
13402
13386
  agentSchedules: () => agentSchedules,
13403
13387
  agentSkillAssignments: () => agentSkillAssignments,
13404
13388
  agentSkills: () => agentSkills,
@@ -13445,7 +13429,6 @@ var init_schema2 = __esm({
13445
13429
  init_skills();
13446
13430
  init_schedules();
13447
13431
  init_llm_keys();
13448
- init_runs();
13449
13432
  init_mcp();
13450
13433
  init_misc();
13451
13434
  init_auth();
@@ -13527,6 +13510,14 @@ var init_skills2 = __esm({
13527
13510
  }
13528
13511
  });
13529
13512
 
13513
+ // ../../packages/db/src/repos/retention.ts
13514
+ var init_retention = __esm({
13515
+ "../../packages/db/src/repos/retention.ts"() {
13516
+ "use strict";
13517
+ init_schema2();
13518
+ }
13519
+ });
13520
+
13530
13521
  // ../../packages/db/src/index.ts
13531
13522
  var init_src4 = __esm({
13532
13523
  "../../packages/db/src/index.ts"() {
@@ -13537,6 +13528,7 @@ var init_src4 = __esm({
13537
13528
  init_credentials2();
13538
13529
  init_agents2();
13539
13530
  init_skills2();
13531
+ init_retention();
13540
13532
  init_drizzle_orm();
13541
13533
  }
13542
13534
  });
@@ -13666,7 +13658,9 @@ function buildEnvForWeb(config, databaseUrl) {
13666
13658
  const bind = config.bind === "loopback" ? "127.0.0.1" : "0.0.0.0";
13667
13659
  const env = {
13668
13660
  DATABASE_URL: databaseUrl,
13669
- RUNNER_URL: `http://localhost:${config.ports.runner}`,
13661
+ // 127.0.0.1, not localhost: on Windows localhost prefers IPv6 (::1), letting a
13662
+ // foreign IPv6 server on the runner port silently steal the web→runner traffic.
13663
+ RUNNER_URL: `http://127.0.0.1:${config.ports.runner}`,
13670
13664
  AUTH_MODE: authMode,
13671
13665
  // Expose auth mode to the client so login/page.tsx can render the right form.
13672
13666
  NEXT_PUBLIC_AUTH_MODE: authMode,
@@ -14008,17 +14002,13 @@ async function runUp(opts = {}) {
14008
14002
  await stopOrphanPostgres();
14009
14003
  if (isPidAlive(pgOrphan.pid)) {
14010
14004
  try {
14011
- if (process.platform === "win32") {
14012
- await execa4("taskkill", ["/T", "/F", "/PID", String(pgOrphan.pid)], { reject: false });
14013
- } else {
14014
- process.kill(pgOrphan.pid, "SIGKILL");
14015
- }
14005
+ process.kill(pgOrphan.pid, "SIGKILL");
14016
14006
  } catch {
14017
14007
  }
14018
14008
  await waitForPidDead(pgOrphan.pid, 5e3);
14019
14009
  }
14020
14010
  if (isPidAlive(pgOrphan.pid)) {
14021
- const killCmd = process.platform === "win32" ? `taskkill /T /F /PID ${pgOrphan.pid}` : `kill -9 ${pgOrphan.pid}`;
14011
+ const killCmd = process.platform === "win32" ? `powershell Stop-Process -Id ${pgOrphan.pid} -Force` : `kill -9 ${pgOrphan.pid}`;
14022
14012
  throw new Error(
14023
14013
  `An orphaned Postgres (pid ${pgOrphan.pid}) is still running and holds the shared-memory block for the data dir. Rotating ports won't help \u2014 the SHM segment is keyed to the data dir, not the port.
14024
14014
  Fix: run \`nodal-agents down\`, then \`${killCmd}\`. If it persists, reboot to clear the orphan kernel object. No data is lost \u2014 pg-data is preserved.`
@@ -0,0 +1,13 @@
1
+ -- Community-installed skills (open Agent Skills / SKILL.md format).
2
+ -- is_community marks a skill installed at runtime from an external source (vs a
3
+ -- system-catalog skill or a user-authored custom skill). source records the
4
+ -- install origin (GitHub URL / skills.sh path) and doubles as the reinstall key.
5
+ -- installed_scripts lists the bundled scripts detected at install (.py/.sh/...)
6
+ -- — surfaced to the user as a warning, because the runtime does NOT execute
7
+ -- skill scripts (agents act through a controlled tool whitelist).
8
+ ALTER TABLE "agent_skills"
9
+ ADD COLUMN IF NOT EXISTS "is_community" boolean DEFAULT false NOT NULL,
10
+ ADD COLUMN IF NOT EXISTS "source" text,
11
+ ADD COLUMN IF NOT EXISTS "installed_scripts" jsonb;
12
+
13
+ CREATE INDEX IF NOT EXISTS "idx_agent_skills_is_community" ON "agent_skills" ("is_community");
@@ -0,0 +1,18 @@
1
+ -- Cache-aware token budget (Guard 1a). The cumulative per-job token budget now
2
+ -- counts EFFECTIVE (non-cached) input — inputTokens minus cachedInputTokens —
3
+ -- so a job that re-sends a prompt-cached history (cheap, ~10x via caching) is
4
+ -- no longer killed at the budget wall as if every re-send were fresh tokens.
5
+ -- This column persists the cumulative effective-input across self-chain resumes,
6
+ -- mirroring input_tokens / output_tokens.
7
+ ALTER TABLE "agent_jobs"
8
+ ADD COLUMN IF NOT EXISTS "effective_input_tokens" integer DEFAULT 0;
9
+
10
+ -- Backfill in-flight (resumable) jobs conservatively: seed effective from the
11
+ -- already-accumulated raw input so a resume AFTER this migration never
12
+ -- under-counts (treats prior raw as fully effective). Terminal jobs never
13
+ -- resume, so this only matters for jobs suspended in awaiting_* at upgrade time.
14
+ UPDATE "agent_jobs"
15
+ SET "effective_input_tokens" = COALESCE("input_tokens", 0)
16
+ WHERE COALESCE("effective_input_tokens", 0) = 0
17
+ AND COALESCE("input_tokens", 0) > 0
18
+ AND "completed_at" IS NULL;
@@ -0,0 +1,11 @@
1
+ -- Real dollar cost budget (Guard 1e). Track what was ACTUALLY BILLED per job
2
+ -- (not token proxies) so the runner can enforce a hard dollar cap and the
3
+ -- dashboard can surface real $/job cost. Populated by OpenRouter when
4
+ -- `usage:{include:true}` is sent — `usage.cost` in the response lands in
5
+ -- providerMetadata.openrouter.usage.cost on the AI SDK result. NULL / 0 for
6
+ -- providers that don't report per-call cost (Anthropic, Ollama, etc.).
7
+ --
8
+ -- Stored as real (float4) — sub-cent values are common (e.g. $0.00056).
9
+ -- Default 0 so existing rows are valid without a backfill.
10
+ ALTER TABLE "agent_jobs"
11
+ ADD COLUMN IF NOT EXISTS "total_cost_usd" real DEFAULT 0;
@@ -0,0 +1,7 @@
1
+ -- Served-upstream observability (P0-B). Tracks which upstream provider actually
2
+ -- handled a given job execution (e.g. 'DeepSeek' when routed via OpenRouter's
3
+ -- provider-order preference). Populated from providerMetadata.openrouter.provider
4
+ -- on each LLM call; only the LAST non-empty value is stored per job. NULL for
5
+ -- providers that don't report upstream identity (Anthropic, Ollama, etc.).
6
+ ALTER TABLE "agent_jobs"
7
+ ADD COLUMN IF NOT EXISTS "served_provider" text;
@@ -0,0 +1,20 @@
1
+ -- Learning-loop columns for agent_skills (Phase A).
2
+ -- created_by: provenance — who created this skill ('user' | 'system' | 'agent').
3
+ -- Default 'user' for forward-compatibility; backfilled to 'system' for
4
+ -- community/bundled skills below.
5
+ -- state: lifecycle state — 'active' (default) | 'stale' | 'archived'.
6
+ -- No hard-delete path — skills are archived, never deleted.
7
+ -- last_used_at: fire-and-forget timestamp, bumped each time the skill is injected
8
+ -- into a job's system prompt. NULL = never used. Used to surface stale skills.
9
+ -- patch_count: how many agent-authored patches have been applied. Starts at 0.
10
+ -- archived_at: when the skill was moved to state='archived'. NULL while active.
11
+
12
+ ALTER TABLE "agent_skills"
13
+ ADD COLUMN IF NOT EXISTS "created_by" text NOT NULL DEFAULT 'user',
14
+ ADD COLUMN IF NOT EXISTS "state" text NOT NULL DEFAULT 'active',
15
+ ADD COLUMN IF NOT EXISTS "last_used_at" timestamptz,
16
+ ADD COLUMN IF NOT EXISTS "patch_count" integer NOT NULL DEFAULT 0,
17
+ ADD COLUMN IF NOT EXISTS "archived_at" timestamptz;
18
+
19
+ -- Backfill: community/bundled skills are 'system' provenance
20
+ UPDATE "agent_skills" SET "created_by" = 'system' WHERE "is_community" = true;
@@ -0,0 +1,7 @@
1
+ -- Tier-2 CURATOR cadence tracker.
2
+ -- Per-entity timestamp of the last LLM consolidation pass. NULL = never run.
3
+ -- Used by run-curator.ts to gate the first-run-deferred pattern (skip first
4
+ -- run, set to now(), run on subsequent ticks beyond CURATOR_INTERVAL_DAYS).
5
+
6
+ ALTER TABLE "entities"
7
+ ADD COLUMN IF NOT EXISTS "last_curator_run_at" timestamptz;
@@ -0,0 +1 @@
1
+ ALTER TABLE entities ADD COLUMN reflection_enabled boolean NOT NULL DEFAULT false;
@@ -0,0 +1 @@
1
+ DROP TABLE IF EXISTS "agent_runs" CASCADE;
@@ -0,0 +1,2 @@
1
+ ALTER TABLE entities ADD COLUMN skill_assignment_mode text NOT NULL DEFAULT 'approval';
2
+ ALTER TABLE agent_skills ADD COLUMN created_by_agent_id uuid REFERENCES agents(id) ON DELETE SET NULL;
@@ -0,0 +1 @@
1
+ ALTER TABLE "entities" ADD COLUMN "lan_command_yolo" boolean DEFAULT false NOT NULL;