nodal-agents 0.8.6 → 0.8.7

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 (207) hide show
  1. package/cli.js +223 -32
  2. package/migrations/0082_entities_auto_run_paused.sql +32 -0
  3. package/migrations/0083_code_project_archives.sql +14 -0
  4. package/migrations/0084_release_wrong_auto_run_pause.sql +24 -0
  5. package/migrations/0085_workspaces_is_dev_folder.sql +33 -0
  6. package/migrations/0086_code_projects_rename_and_hide.sql +61 -0
  7. package/migrations/0087_workspaces_hidden_from_code.sql +44 -0
  8. package/migrations/meta/_journal.json +42 -0
  9. package/package.json +1 -1
  10. package/runner.js +2863 -1657
  11. package/web/.next/BUILD_ID +1 -1
  12. package/web/.next/app-path-routes-manifest.json +12 -12
  13. package/web/.next/build-manifest.json +2 -2
  14. package/web/.next/prerender-manifest.json +3 -3
  15. package/web/.next/server/app/(dashboard)/agents/[id]/channels/page.js +1 -1
  16. package/web/.next/server/app/(dashboard)/agents/[id]/channels/page_client-reference-manifest.js +1 -1
  17. package/web/.next/server/app/(dashboard)/agents/[id]/edit/page.js +4 -4
  18. package/web/.next/server/app/(dashboard)/agents/[id]/edit/page_client-reference-manifest.js +1 -1
  19. package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page.js +1 -1
  20. package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page_client-reference-manifest.js +1 -1
  21. package/web/.next/server/app/(dashboard)/agents/page.js +1 -1
  22. package/web/.next/server/app/(dashboard)/agents/page_client-reference-manifest.js +1 -1
  23. package/web/.next/server/app/(dashboard)/approvals/page.js +1 -1
  24. package/web/.next/server/app/(dashboard)/approvals/page_client-reference-manifest.js +1 -1
  25. package/web/.next/server/app/(dashboard)/automations/page.js +1 -1
  26. package/web/.next/server/app/(dashboard)/automations/page_client-reference-manifest.js +1 -1
  27. package/web/.next/server/app/(dashboard)/chat/page.js +1 -1
  28. package/web/.next/server/app/(dashboard)/chat/page_client-reference-manifest.js +1 -1
  29. package/web/.next/server/app/(dashboard)/code/[id]/page.js +1 -1
  30. package/web/.next/server/app/(dashboard)/code/[id]/page_client-reference-manifest.js +1 -1
  31. package/web/.next/server/app/(dashboard)/code/page.js +1 -1
  32. package/web/.next/server/app/(dashboard)/code/page_client-reference-manifest.js +1 -1
  33. package/web/.next/server/app/(dashboard)/connectors/page.js +1 -1
  34. package/web/.next/server/app/(dashboard)/connectors/page_client-reference-manifest.js +1 -1
  35. package/web/.next/server/app/(dashboard)/credentials/page.js +1 -1
  36. package/web/.next/server/app/(dashboard)/credentials/page_client-reference-manifest.js +1 -1
  37. package/web/.next/server/app/(dashboard)/jobs/[id]/page.js +1 -1
  38. package/web/.next/server/app/(dashboard)/jobs/[id]/page_client-reference-manifest.js +1 -1
  39. package/web/.next/server/app/(dashboard)/jobs/page.js +1 -1
  40. package/web/.next/server/app/(dashboard)/jobs/page_client-reference-manifest.js +1 -1
  41. package/web/.next/server/app/(dashboard)/learned-skills/page.js +1 -1
  42. package/web/.next/server/app/(dashboard)/learned-skills/page_client-reference-manifest.js +1 -1
  43. package/web/.next/server/app/(dashboard)/llm-providers/page.js +1 -1
  44. package/web/.next/server/app/(dashboard)/llm-providers/page_client-reference-manifest.js +1 -1
  45. package/web/.next/server/app/(dashboard)/logs/page.js +1 -1
  46. package/web/.next/server/app/(dashboard)/logs/page_client-reference-manifest.js +1 -1
  47. package/web/.next/server/app/(dashboard)/mcp/page.js +1 -1
  48. package/web/.next/server/app/(dashboard)/mcp/page_client-reference-manifest.js +1 -1
  49. package/web/.next/server/app/(dashboard)/memories/page.js +1 -1
  50. package/web/.next/server/app/(dashboard)/memories/page_client-reference-manifest.js +1 -1
  51. package/web/.next/server/app/(dashboard)/page.js +3 -3
  52. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  53. package/web/.next/server/app/(dashboard)/settings/page.js +1 -1
  54. package/web/.next/server/app/(dashboard)/settings/page_client-reference-manifest.js +1 -1
  55. package/web/.next/server/app/(dashboard)/settings/root-context/page.js +1 -1
  56. package/web/.next/server/app/(dashboard)/settings/root-context/page_client-reference-manifest.js +1 -1
  57. package/web/.next/server/app/(dashboard)/skills/[id]/edit/page.js +1 -1
  58. package/web/.next/server/app/(dashboard)/skills/[id]/edit/page_client-reference-manifest.js +1 -1
  59. package/web/.next/server/app/(dashboard)/skills/new/page.js +1 -1
  60. package/web/.next/server/app/(dashboard)/skills/new/page_client-reference-manifest.js +1 -1
  61. package/web/.next/server/app/(dashboard)/skills/page.js +1 -1
  62. package/web/.next/server/app/(dashboard)/skills/page_client-reference-manifest.js +1 -1
  63. package/web/.next/server/app/_global-error/page.js +1 -1
  64. package/web/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  65. package/web/.next/server/app/_global-error.html +1 -1
  66. package/web/.next/server/app/_global-error.rsc +3 -3
  67. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +3 -3
  68. package/web/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +3 -3
  69. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  70. package/web/.next/server/app/_not-found/page.js +1 -1
  71. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  72. package/web/.next/server/app/_not-found.html +1 -1
  73. package/web/.next/server/app/_not-found.rsc +4 -4
  74. package/web/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
  75. package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +4 -4
  76. package/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  77. package/web/.next/server/app/api/auth/[...all]/route.js +1 -1
  78. package/web/.next/server/app/api/health/route.js +1 -1
  79. package/web/.next/server/app/login/page.js +1 -1
  80. package/web/.next/server/app/login/page_client-reference-manifest.js +1 -1
  81. package/web/.next/server/app/onboarding/page.js +2 -2
  82. package/web/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
  83. package/web/.next/server/app-paths-manifest.json +12 -12
  84. package/web/.next/server/chunks/1126.js +1 -1
  85. package/web/.next/server/chunks/210.js +1 -0
  86. package/web/.next/server/chunks/2834.js +1 -0
  87. package/web/.next/server/chunks/3666.js +1 -0
  88. package/web/.next/server/chunks/3756.js +1 -0
  89. package/web/.next/server/chunks/3927.js +1597 -0
  90. package/web/.next/server/chunks/42.js +2 -2
  91. package/web/.next/server/chunks/4869.js +1629 -0
  92. package/web/.next/server/chunks/5384.js +1 -0
  93. package/web/.next/server/chunks/7048.js +1 -0
  94. package/web/.next/server/chunks/7251.js +1 -0
  95. package/web/.next/server/chunks/7650.js +1749 -0
  96. package/web/.next/server/chunks/7741.js +1 -1
  97. package/web/.next/server/chunks/{5877.js → 8191.js} +71 -89
  98. package/web/.next/server/chunks/9949.js +1746 -0
  99. package/web/.next/server/middleware-build-manifest.js +1 -1
  100. package/web/.next/server/middleware.js +3 -3
  101. package/web/.next/server/pages/404.html +1 -1
  102. package/web/.next/server/pages/500.html +1 -1
  103. package/web/.next/server/server-reference-manifest.js +1 -1
  104. package/web/.next/server/server-reference-manifest.json +1 -1
  105. package/web/.next/server/webpack-runtime.js +1 -1
  106. package/web/.next/static/chunks/2515-a9fd443091bb4291.js +1 -0
  107. package/web/.next/static/chunks/5034-df0e39b679214174.js +1 -0
  108. package/web/.next/static/chunks/6306-6448703d936feed3.js +1629 -0
  109. package/web/.next/static/chunks/6646-60db74d7d175dba0.js +1 -0
  110. package/web/.next/static/chunks/8423-57a00fc00778c1e1.js +1 -0
  111. package/web/.next/static/chunks/app/(dashboard)/agents/[id]/edit/page-42261de6e1de3f0d.js +6 -0
  112. package/web/.next/static/chunks/app/(dashboard)/agents/page-b028af9e30e8fb83.js +1 -0
  113. package/web/.next/static/chunks/app/(dashboard)/approvals/{page-99ec2b36c44ed3d3.js → page-a752b0ddc39d34f4.js} +1 -1
  114. package/web/.next/static/chunks/app/(dashboard)/automations/page-521ded8178a4513f.js +1 -0
  115. package/web/.next/static/chunks/app/(dashboard)/chat/page-1042ea3f25d7b430.js +1 -0
  116. package/web/.next/static/chunks/app/(dashboard)/code/[id]/page-bed8be1148fb7d1d.js +1 -0
  117. package/web/.next/static/chunks/app/(dashboard)/code/page-b72af9e2731d0bb1.js +1 -0
  118. package/web/.next/static/chunks/app/(dashboard)/connectors/page-8a1993ba5a7ba872.js +1 -0
  119. package/web/.next/static/chunks/app/(dashboard)/jobs/[id]/page-522a1f164f88f89b.js +1 -0
  120. package/web/.next/static/chunks/app/(dashboard)/jobs/page-15d22e17291edb01.js +1 -0
  121. package/web/.next/static/chunks/app/(dashboard)/layout-09b1990c5a3a9a09.js +1 -0
  122. package/web/.next/static/chunks/app/(dashboard)/learned-skills/page-93aa992e100c0b5a.js +1 -0
  123. package/web/.next/static/chunks/app/(dashboard)/llm-providers/page-d121748077e7c685.js +1 -0
  124. package/web/.next/static/chunks/app/(dashboard)/logs/page-e4b6136ecf9ccd14.js +1 -0
  125. package/web/.next/static/chunks/app/(dashboard)/mcp/page-b762d306176a4851.js +1 -0
  126. package/web/.next/static/chunks/app/(dashboard)/memories/page-de710d983bae276e.js +1 -0
  127. package/web/.next/static/chunks/app/(dashboard)/{page-1e05fa622609cdf1.js → page-8b3f80ee2f3d8c11.js} +1 -1
  128. package/web/.next/static/chunks/app/(dashboard)/settings/page-977685a40ec1a2f2.js +1 -0
  129. package/web/.next/static/chunks/app/(dashboard)/settings/root-context/page-db8f2599cc9771e1.js +1 -0
  130. package/web/.next/static/chunks/app/(dashboard)/skills/[id]/edit/page-b8e71ad3ef0b27d4.js +1 -0
  131. package/web/.next/static/chunks/app/(dashboard)/skills/new/page-0d777140d4c73ad8.js +1 -0
  132. package/web/.next/static/chunks/app/(dashboard)/skills/page-127e5d5374a4bb2a.js +1 -0
  133. package/web/.next/static/chunks/app/error-3d9af79e7856dcc8.js +1 -0
  134. package/web/.next/static/chunks/app/login/page-8d873e8ff103a226.js +1 -0
  135. package/web/.next/static/chunks/app/onboarding/{page-f02705f7710caa08.js → page-0145253536f365ee.js} +2 -2
  136. package/web/.next/static/css/be2ece2e5b18d112.css +3 -0
  137. package/web/.next/server/app/(dashboard)/agents/[id]/channels/page.js.nft.json +0 -1
  138. package/web/.next/server/app/(dashboard)/agents/[id]/edit/page.js.nft.json +0 -1
  139. package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page.js.nft.json +0 -1
  140. package/web/.next/server/app/(dashboard)/agents/page.js.nft.json +0 -1
  141. package/web/.next/server/app/(dashboard)/approvals/page.js.nft.json +0 -1
  142. package/web/.next/server/app/(dashboard)/automations/page.js.nft.json +0 -1
  143. package/web/.next/server/app/(dashboard)/chat/page.js.nft.json +0 -1
  144. package/web/.next/server/app/(dashboard)/code/[id]/page.js.nft.json +0 -1
  145. package/web/.next/server/app/(dashboard)/code/page.js.nft.json +0 -1
  146. package/web/.next/server/app/(dashboard)/connectors/page.js.nft.json +0 -1
  147. package/web/.next/server/app/(dashboard)/credentials/page.js.nft.json +0 -1
  148. package/web/.next/server/app/(dashboard)/jobs/[id]/page.js.nft.json +0 -1
  149. package/web/.next/server/app/(dashboard)/jobs/page.js.nft.json +0 -1
  150. package/web/.next/server/app/(dashboard)/learned-skills/page.js.nft.json +0 -1
  151. package/web/.next/server/app/(dashboard)/llm-providers/page.js.nft.json +0 -1
  152. package/web/.next/server/app/(dashboard)/logs/page.js.nft.json +0 -1
  153. package/web/.next/server/app/(dashboard)/mcp/page.js.nft.json +0 -1
  154. package/web/.next/server/app/(dashboard)/memories/page.js.nft.json +0 -1
  155. package/web/.next/server/app/(dashboard)/page.js.nft.json +0 -1
  156. package/web/.next/server/app/(dashboard)/settings/page.js.nft.json +0 -1
  157. package/web/.next/server/app/(dashboard)/settings/root-context/page.js.nft.json +0 -1
  158. package/web/.next/server/app/(dashboard)/skills/[id]/edit/page.js.nft.json +0 -1
  159. package/web/.next/server/app/(dashboard)/skills/new/page.js.nft.json +0 -1
  160. package/web/.next/server/app/(dashboard)/skills/page.js.nft.json +0 -1
  161. package/web/.next/server/app/_global-error/page.js.nft.json +0 -1
  162. package/web/.next/server/app/_not-found/page.js.nft.json +0 -1
  163. package/web/.next/server/app/api/auth/[...all]/route.js.nft.json +0 -1
  164. package/web/.next/server/app/api/health/route.js.nft.json +0 -1
  165. package/web/.next/server/app/api/oauth/[provider]/callback/route.js.nft.json +0 -1
  166. package/web/.next/server/app/api/oauth/[provider]/start/route.js.nft.json +0 -1
  167. package/web/.next/server/app/auth/callback/route.js.nft.json +0 -1
  168. package/web/.next/server/app/login/page.js.nft.json +0 -1
  169. package/web/.next/server/app/onboarding/page.js.nft.json +0 -1
  170. package/web/.next/server/chunks/2745.js +0 -1
  171. package/web/.next/server/chunks/2938.js +0 -1
  172. package/web/.next/server/chunks/4630.js +0 -1
  173. package/web/.next/server/chunks/4639.js +0 -1744
  174. package/web/.next/server/chunks/4711.js +0 -1
  175. package/web/.next/server/chunks/4924.js +0 -1741
  176. package/web/.next/server/chunks/5149.js +0 -1
  177. package/web/.next/server/chunks/6680.js +0 -1615
  178. package/web/.next/server/chunks/9729.js +0 -1
  179. package/web/.next/server/middleware.js.nft.json +0 -1
  180. package/web/.next/static/chunks/4994-7effa935c5703218.js +0 -1
  181. package/web/.next/static/chunks/5034-7b0857c231a2db7e.js +0 -1
  182. package/web/.next/static/chunks/8423-bf78a9de790d8ebc.js +0 -1
  183. package/web/.next/static/chunks/app/(dashboard)/agents/[id]/edit/page-f55badb51d6e10b1.js +0 -6
  184. package/web/.next/static/chunks/app/(dashboard)/agents/page-d256dbe832dfb238.js +0 -1
  185. package/web/.next/static/chunks/app/(dashboard)/automations/page-9242e9b754d98c68.js +0 -1
  186. package/web/.next/static/chunks/app/(dashboard)/chat/page-7f9c147e49730cb4.js +0 -1
  187. package/web/.next/static/chunks/app/(dashboard)/code/[id]/page-46acd3da2c78b6df.js +0 -1
  188. package/web/.next/static/chunks/app/(dashboard)/code/page-12598290e23cb30a.js +0 -1
  189. package/web/.next/static/chunks/app/(dashboard)/connectors/page-a67da30a55296b54.js +0 -1
  190. package/web/.next/static/chunks/app/(dashboard)/jobs/[id]/page-793efbdc85566e24.js +0 -1
  191. package/web/.next/static/chunks/app/(dashboard)/jobs/page-cce723799dad2458.js +0 -1
  192. package/web/.next/static/chunks/app/(dashboard)/layout-5976fd8a05f7cefd.js +0 -1
  193. package/web/.next/static/chunks/app/(dashboard)/learned-skills/page-0f3b9332eeaf4282.js +0 -1
  194. package/web/.next/static/chunks/app/(dashboard)/llm-providers/page-4e0fef0a652f4e8a.js +0 -1
  195. package/web/.next/static/chunks/app/(dashboard)/logs/page-31b25388df6e0212.js +0 -1
  196. package/web/.next/static/chunks/app/(dashboard)/mcp/page-2364c615affa47d3.js +0 -1
  197. package/web/.next/static/chunks/app/(dashboard)/memories/page-52944e872698b85f.js +0 -1
  198. package/web/.next/static/chunks/app/(dashboard)/settings/page-19ac2c3c794fa0f7.js +0 -1
  199. package/web/.next/static/chunks/app/(dashboard)/settings/root-context/page-cb20f211b06e807f.js +0 -1
  200. package/web/.next/static/chunks/app/(dashboard)/skills/[id]/edit/page-5bc99166041fcf0d.js +0 -1
  201. package/web/.next/static/chunks/app/(dashboard)/skills/new/page-453b7333a310658c.js +0 -1
  202. package/web/.next/static/chunks/app/(dashboard)/skills/page-055a17f39f670958.js +0 -1
  203. package/web/.next/static/chunks/app/error-d7d2badaa59bb441.js +0 -1
  204. package/web/.next/static/chunks/app/login/page-02aa17b387bdb416.js +0 -1
  205. package/web/.next/static/css/f0904e0d2f4e1435.css +0 -3
  206. /package/web/.next/static/{PMZOOK9t9OB35ORr4mOpd → FW6WAOvY3tiwZHzAR2ZUZ}/_buildManifest.js +0 -0
  207. /package/web/.next/static/{PMZOOK9t9OB35ORr4mOpd → FW6WAOvY3tiwZHzAR2ZUZ}/_ssgManifest.js +0 -0
package/cli.js CHANGED
@@ -13500,11 +13500,19 @@ var init_entities = __esm({
13500
13500
  // Controls whether agent-authored skills are auto-assigned to the authoring
13501
13501
  // agent ('auto') or queued for the entity owner to approve ('approval').
13502
13502
  skillAssignmentMode: text("skill_assignment_mode").notNull().default("approval").$type(),
13503
- // Allows the workspace OWNER to opt into Yolo (auto-approve run_command)
13504
- // even in non-local-trust (LAN/local-auth) mode. Off by default: in
13505
- // local-auth mode, commands always require approval unless the owner
13506
- // explicitly opts in here.
13507
- lanCommandYolo: boolean("lan_command_yolo").notNull().default(false),
13503
+ /**
13504
+ * Frein d'urgence de l'auto-exécution (0082, inversion du modèle à deux
13505
+ * clés décision Quentin 24/08). true = TOUT l'auto-run de code du
13506
+ * workspace est en pause : les règles auto_approve des outils d'exécution
13507
+ * (run_command, code_task, scripts, MCP stdio) deviennent dormantes sans
13508
+ * être supprimées — le runner les déshabille à l'exécution. false
13509
+ * (défaut) = les toggles Yolo par agent (owner-only) s'appliquent tels
13510
+ * quels. Remplace lan_command_yolo, qui était une PRÉ-CONDITION
13511
+ * d'activation : redondante (les toggles par agent étaient déjà
13512
+ * owner-only) et pénible — sa vraie valeur était le coupe-circuit, qui
13513
+ * devient son seul rôle.
13514
+ */
13515
+ autoRunPaused: boolean("auto_run_paused").notNull().default(false),
13508
13516
  /**
13509
13517
  * Interrupteur maitre du serveur MCP (PR C, decision Quentin 23/08).
13510
13518
  * Defaut FERME : un point d'entree externe qui cree des jobs doit etre
@@ -13654,12 +13662,16 @@ var init_agents = __esm({
13654
13662
  // action layer guarantees at least one provider stays enabled.
13655
13663
  cliDefaults: jsonb("cli_defaults").$type(),
13656
13664
  // Which harness drives this agent (étape E). 'nodal' (default) = the
13657
- // Nodal runner loop. 'claude-code' = the agent IS a Claude Code session:
13658
- // persona from DB via --append-system-prompt, channels/cron/workspaces/
13659
- // budget/approvals stay Nodal's, but the LOOP, tools and context are the
13660
- // CLI's — said as-is to the user (dispatcher, not brain). 'codex' is
13661
- // reserved (fails loud runtime_not_supported until implemented). A DATA
13662
- // field, never a hardcoded per-agent branch (invariant #1).
13665
+ // Nodal runner loop. 'claude-code' and 'codex' = the agent IS a session of
13666
+ // that CLI: persona from DB, channels/cron/workspaces/budget/approvals stay
13667
+ // Nodal's, but the LOOP, tools and context are the CLI's — said as-is to
13668
+ // the user (dispatcher, not brain). A DATA field, never a hardcoded
13669
+ // per-agent branch (invariant #1).
13670
+ //
13671
+ // 'codex' a été RÉSERVÉ ici du 19/08 au 27/08 : accepté par la contrainte,
13672
+ // refusé par le Zod de l'action et par le runner. Ouvert le 27/08 —
13673
+ // apps/runner/src/cli-runtime/provider.ts porte désormais LE tableau des
13674
+ // runtimes servis ; cette contrainte et lui doivent rester d'accord.
13663
13675
  runtime: text("runtime").default("nodal").notNull(),
13664
13676
  // Runtime-agent permission posture, as DATA: mode 'read' (default when
13665
13677
  // NULL) hides the CLI's write tools; 'write' allows workspace edits
@@ -14621,6 +14633,35 @@ var init_mcp = __esm({
14621
14633
  }
14622
14634
  });
14623
14635
 
14636
+ // ../../packages/db/src/schema/code-projects.ts
14637
+ var codeProjects;
14638
+ var init_code_projects = __esm({
14639
+ "../../packages/db/src/schema/code-projects.ts"() {
14640
+ "use strict";
14641
+ init_pg_core();
14642
+ init_entities();
14643
+ codeProjects = pgTable(
14644
+ "code_projects",
14645
+ {
14646
+ id: uuid("id").primaryKey().defaultRandom(),
14647
+ entityId: uuid("entity_id").notNull().references(() => entities.id, { onDelete: "cascade" }),
14648
+ /** Chemin absolu slash-normalisé du projet (la clé de groupement dérivée). */
14649
+ projectPath: text("project_path").notNull(),
14650
+ /** Nom choisi par le propriétaire. NULL = nom du dossier. */
14651
+ displayName: text("display_name"),
14652
+ /** Masqué de la liste ET du contexte injecté aux agents. */
14653
+ hidden: boolean("hidden").notNull().default(false),
14654
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
14655
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
14656
+ },
14657
+ (table) => [
14658
+ unique("code_projects_entity_path_unique").on(table.entityId, table.projectPath),
14659
+ index("idx_code_projects_entity").on(table.entityId)
14660
+ ]
14661
+ );
14662
+ }
14663
+ });
14664
+
14624
14665
  // ../../packages/db/src/schema/auth.ts
14625
14666
  var sessions, accounts, verifications;
14626
14667
  var init_auth = __esm({
@@ -14718,6 +14759,28 @@ var init_agent_workspaces = __esm({
14718
14759
  path: text("path").notNull(),
14719
14760
  // UI display order; lower = appears first. Default 0.
14720
14761
  position: integer("position").default(0).notNull(),
14762
+ /**
14763
+ * Ticked by the owner: "nothing written here is a project of mine."
14764
+ *
14765
+ * NOT the return of `is_dev_folder` (0085, dropped by 0086). That one asked
14766
+ * which folders to INCLUDE, so nothing showed until you ticked, a folder
14767
+ * you forgot lost real work in silence, and you still had to guess whether
14768
+ * the ticked folder WAS a project or CONTAINED projects.
14769
+ *
14770
+ * This one only ever removes, and it removes a whole subtree — so the
14771
+ * "is it a project?" question never arises. Everything shows by default,
14772
+ * which keeps the honest failure mode: visible noise you clear, rather than
14773
+ * missing work nothing tells you about. See 0087.
14774
+ *
14775
+ * The folder stays a full workspace: the agent reads and writes there as
14776
+ * before, and its LABEL is still used to resolve relative paths. Only the
14777
+ * project view ignores it — the Code tab and the injected `## Runtime`
14778
+ * block alike.
14779
+ *
14780
+ * A PATH counts as hidden when at least one of its rows is ticked: the same
14781
+ * folder attached to five agents is one gesture, not five.
14782
+ */
14783
+ hiddenFromCode: boolean("hidden_from_code").default(false).notNull(),
14721
14784
  createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
14722
14785
  updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow()
14723
14786
  },
@@ -15158,6 +15221,7 @@ __export(schema_exports, {
15158
15221
  chatMessages: () => chatMessages,
15159
15222
  cliRuns: () => cliRuns,
15160
15223
  cliSessions: () => cliSessions,
15224
+ codeProjects: () => codeProjects,
15161
15225
  connectors: () => connectors,
15162
15226
  conversations: () => conversations,
15163
15227
  credentials: () => credentials,
@@ -15198,6 +15262,7 @@ var init_schema2 = __esm({
15198
15262
  init_schedules();
15199
15263
  init_llm_keys();
15200
15264
  init_mcp();
15265
+ init_code_projects();
15201
15266
  init_auth();
15202
15267
  init_agent_connector_assignments();
15203
15268
  init_agent_workspaces();
@@ -16140,6 +16205,7 @@ __export(src_exports, {
16140
16205
  chatMessages: () => chatMessages,
16141
16206
  cliRuns: () => cliRuns,
16142
16207
  cliSessions: () => cliSessions,
16208
+ codeProjects: () => codeProjects,
16143
16209
  connectors: () => connectors,
16144
16210
  conversations: () => conversations,
16145
16211
  count: () => count,
@@ -16502,9 +16568,34 @@ var init_ports = __esm({
16502
16568
  }
16503
16569
  });
16504
16570
 
16571
+ // src/lib/log-rotation.ts
16572
+ import { existsSync as existsSync6, renameSync, rmSync, statSync } from "fs";
16573
+ function rotateLogIfNeeded(logFile, maxBytes = LOG_ROTATE_MAX_BYTES) {
16574
+ try {
16575
+ if (!existsSync6(logFile)) return false;
16576
+ if (statSync(logFile).size < maxBytes) return false;
16577
+ const archived = `${logFile}.1`;
16578
+ rmSync(archived, { force: true });
16579
+ renameSync(logFile, archived);
16580
+ return true;
16581
+ } catch (err) {
16582
+ console.warn(
16583
+ `[nodalai] log rotation skipped for ${logFile}: ${err instanceof Error ? err.message : String(err)}`
16584
+ );
16585
+ return false;
16586
+ }
16587
+ }
16588
+ var LOG_ROTATE_MAX_BYTES;
16589
+ var init_log_rotation = __esm({
16590
+ "src/lib/log-rotation.ts"() {
16591
+ "use strict";
16592
+ LOG_ROTATE_MAX_BYTES = 20 * 1024 * 1024;
16593
+ }
16594
+ });
16595
+
16505
16596
  // src/lib/processes.ts
16506
16597
  import { execa as execa2 } from "execa";
16507
- import { createWriteStream, openSync, writeFileSync as writeFileSync4, readFileSync as readFileSync5, existsSync as existsSync6 } from "fs";
16598
+ import { createWriteStream, openSync, writeFileSync as writeFileSync4, readFileSync as readFileSync5, existsSync as existsSync7 } from "fs";
16508
16599
  import { join as join6, dirname as dirname4, resolve } from "path";
16509
16600
  import { fileURLToPath as fileURLToPath3 } from "url";
16510
16601
  async function killProcessTree(child) {
@@ -16653,7 +16744,7 @@ function resolveAppDir(appName) {
16653
16744
  const fileDir = dirname4(here);
16654
16745
  let cursor = fileDir;
16655
16746
  for (let i = 0; i < 8; i++) {
16656
- if (existsSync6(join6(cursor, "pnpm-workspace.yaml"))) {
16747
+ if (existsSync7(join6(cursor, "pnpm-workspace.yaml"))) {
16657
16748
  return join6(cursor, appName);
16658
16749
  }
16659
16750
  const parent = dirname4(cursor);
@@ -16672,6 +16763,7 @@ function spawnWiring(logFile, detach) {
16672
16763
  }
16673
16764
  function spawnRunner(env, opts = {}) {
16674
16765
  const logFile = join6(LOG_DIR, "runner.log");
16766
+ rotateLogIfNeeded(logFile);
16675
16767
  const outStream = createWriteStream(logFile, { flags: "a" });
16676
16768
  let bin;
16677
16769
  let args;
@@ -16717,6 +16809,7 @@ env keys: ${Object.keys(env).join(",")}
16717
16809
  }
16718
16810
  function spawnWeb(env, opts = {}) {
16719
16811
  const logFile = join6(LOG_DIR, "web.log");
16812
+ rotateLogIfNeeded(logFile);
16720
16813
  const outStream = createWriteStream(logFile, { flags: "a" });
16721
16814
  let bin;
16722
16815
  let args;
@@ -16804,7 +16897,7 @@ function writePids(pids) {
16804
16897
  }
16805
16898
  function readPids() {
16806
16899
  const pidFile = join6(PID_DIR, "processes.json");
16807
- if (!existsSync6(pidFile)) return null;
16900
+ if (!existsSync7(pidFile)) return null;
16808
16901
  try {
16809
16902
  return JSON.parse(readFileSync5(pidFile, "utf-8"));
16810
16903
  } catch {
@@ -16813,7 +16906,7 @@ function readPids() {
16813
16906
  }
16814
16907
  function clearPids() {
16815
16908
  const pidFile = join6(PID_DIR, "processes.json");
16816
- if (existsSync6(pidFile)) {
16909
+ if (existsSync7(pidFile)) {
16817
16910
  writeFileSync4(pidFile, JSON.stringify({}), "utf-8");
16818
16911
  }
16819
16912
  }
@@ -16859,6 +16952,7 @@ var init_processes = __esm({
16859
16952
  "src/lib/processes.ts"() {
16860
16953
  "use strict";
16861
16954
  init_config();
16955
+ init_log_rotation();
16862
16956
  SNAPSHOT_BUDGET_MS = 2e4;
16863
16957
  SHUTDOWN_SNAPSHOT_BUDGET_MS = 6e3;
16864
16958
  inFlightSnapshot = null;
@@ -16890,11 +16984,76 @@ var init_processes = __esm({
16890
16984
  lastSnapshotFailure = null;
16891
16985
  BUNDLED_ROOT = (() => {
16892
16986
  const here = dirname4(fileURLToPath3(import.meta.url));
16893
- return existsSync6(join6(here, "runner.js")) ? here : null;
16987
+ return existsSync7(join6(here, "runner.js")) ? here : null;
16894
16988
  })();
16895
16989
  }
16896
16990
  });
16897
16991
 
16992
+ // src/lib/watchdog.ts
16993
+ async function probeRunnerHealth(runnerUrl) {
16994
+ try {
16995
+ const controller = new AbortController();
16996
+ const t = setTimeout(() => controller.abort(), 3e3);
16997
+ const res = await fetch(`${runnerUrl}/api/health`, { signal: controller.signal });
16998
+ clearTimeout(t);
16999
+ if (res.status === 200) return { state: "healthy", detail: "" };
17000
+ const body = await res.json().catch(() => null);
17001
+ const detail = body?.db === "error" ? "database unreachable (runner reports db: error)" : `runner is unhealthy (HTTP ${res.status})`;
17002
+ return { state: "degraded", detail };
17003
+ } catch (err) {
17004
+ const aborted = err instanceof Error && err.name === "AbortError";
17005
+ return {
17006
+ state: "unreachable",
17007
+ detail: aborted ? "runner did not answer /api/health within 3s (busy or stalled)" : "runner is not answering /api/health"
17008
+ };
17009
+ }
17010
+ }
17011
+ function startHealthWatchdog(opts) {
17012
+ const intervalMs = opts.intervalMs ?? 3e4;
17013
+ const failureThreshold = opts.failureThreshold ?? 2;
17014
+ let announced = "healthy";
17015
+ let consecutiveFailures = 0;
17016
+ let stopped = false;
17017
+ let inFlight = false;
17018
+ const tick = async () => {
17019
+ if (stopped || inFlight) return;
17020
+ inFlight = true;
17021
+ try {
17022
+ const result = await opts.probe();
17023
+ if (stopped) return;
17024
+ if (result.state === "healthy") {
17025
+ consecutiveFailures = 0;
17026
+ if (announced !== "healthy") {
17027
+ announced = "healthy";
17028
+ opts.onTransition("healthy", "services are answering again");
17029
+ }
17030
+ return;
17031
+ }
17032
+ consecutiveFailures += 1;
17033
+ if (consecutiveFailures < failureThreshold) return;
17034
+ if (announced !== result.state) {
17035
+ announced = result.state;
17036
+ opts.onTransition(result.state, result.detail);
17037
+ }
17038
+ } finally {
17039
+ inFlight = false;
17040
+ }
17041
+ };
17042
+ const timer2 = setInterval(() => void tick(), intervalMs);
17043
+ timer2.unref?.();
17044
+ return {
17045
+ stop: () => {
17046
+ stopped = true;
17047
+ clearInterval(timer2);
17048
+ }
17049
+ };
17050
+ }
17051
+ var init_watchdog = __esm({
17052
+ "src/lib/watchdog.ts"() {
17053
+ "use strict";
17054
+ }
17055
+ });
17056
+
16898
17057
  // src/commands/up.ts
16899
17058
  var up_exports = {};
16900
17059
  __export(up_exports, {
@@ -16904,7 +17063,7 @@ import chalk from "chalk";
16904
17063
  import ora from "ora";
16905
17064
  import open from "open";
16906
17065
  import { randomBytes as randomBytes4 } from "crypto";
16907
- import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
17066
+ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
16908
17067
  import { join as join7 } from "path";
16909
17068
  async function killSilent(child) {
16910
17069
  await killProcessTree(child);
@@ -16912,7 +17071,7 @@ async function killSilent(child) {
16912
17071
  function printLogTail(service, lines) {
16913
17072
  const logFile = join7(LOG_DIR, `${service}.log`);
16914
17073
  try {
16915
- if (!existsSync7(logFile)) {
17074
+ if (!existsSync8(logFile)) {
16916
17075
  console.error(chalk.gray(`
16917
17076
  ${service}: no log at ${logFile}`));
16918
17077
  return;
@@ -17274,7 +17433,32 @@ ${lines}
17274
17433
  }
17275
17434
  console.log(chalk.gray(" Ctrl+C to stop all services"));
17276
17435
  console.log("");
17436
+ const watchdog = startHealthWatchdog({
17437
+ probe: () => probeRunnerHealth(runnerUrl),
17438
+ onTransition: (state, detail) => {
17439
+ const at = (/* @__PURE__ */ new Date()).toLocaleTimeString();
17440
+ if (state === "healthy") {
17441
+ console.log(chalk.green(`
17442
+ [${at}] Recovered \u2014 ${detail}.`));
17443
+ return;
17444
+ }
17445
+ console.error(chalk.red.bold(`
17446
+ [${at}] DEGRADED \u2014 ${detail}.`));
17447
+ if (state === "degraded") {
17448
+ console.error(
17449
+ chalk.red(
17450
+ " The dashboard may still render, but nothing can read or write the database:\n jobs are not created, chats and schedules are failing."
17451
+ )
17452
+ );
17453
+ printLogTail("runner", 10);
17454
+ }
17455
+ console.error(
17456
+ chalk.yellow(" \u2192 Restart the stack: `nodal-agents down`, then `nodal-agents up`.")
17457
+ );
17458
+ }
17459
+ });
17277
17460
  await Promise.race([runnerProcess, webProcess]);
17461
+ watchdog.stop();
17278
17462
  await shutdown();
17279
17463
  }
17280
17464
  var init_up = __esm({
@@ -17287,6 +17471,7 @@ var init_up = __esm({
17287
17471
  init_ports();
17288
17472
  init_processes();
17289
17473
  init_src4();
17474
+ init_watchdog();
17290
17475
  }
17291
17476
  });
17292
17477
 
@@ -17574,7 +17759,7 @@ __export(down_exports, {
17574
17759
  runDown: () => runDown
17575
17760
  });
17576
17761
  import chalk3 from "chalk";
17577
- import { existsSync as existsSync8 } from "fs";
17762
+ import { existsSync as existsSync9 } from "fs";
17578
17763
  import { join as join8 } from "path";
17579
17764
  async function killPid(pid, label) {
17580
17765
  if (!isPidAlive(pid)) {
@@ -17591,7 +17776,7 @@ async function killPid(pid, label) {
17591
17776
  }
17592
17777
  async function stopPostgresGracefully() {
17593
17778
  const pidFile = join8(PG_DATA_DIR, "postmaster.pid");
17594
- if (!existsSync8(pidFile)) return false;
17779
+ if (!existsSync9(pidFile)) return false;
17595
17780
  const pgPid = readPostmasterPid(PG_DATA_DIR);
17596
17781
  try {
17597
17782
  const EmbeddedPostgres = (await import("embedded-postgres")).default;
@@ -17661,7 +17846,7 @@ __export(logs_exports, {
17661
17846
  runLogs: () => runLogs
17662
17847
  });
17663
17848
  import chalk4 from "chalk";
17664
- import { existsSync as existsSync9, createReadStream, watch, statSync } from "fs";
17849
+ import { existsSync as existsSync10, createReadStream, watch, statSync as statSync2 } from "fs";
17665
17850
  import { join as join9 } from "path";
17666
17851
  function isServiceName(s) {
17667
17852
  return VALID_SERVICES.includes(s);
@@ -17675,7 +17860,7 @@ async function runLogs(service) {
17675
17860
  const services = target === "all" ? VALID_SERVICES : [target];
17676
17861
  for (const svc of services) {
17677
17862
  const logFile = join9(LOG_DIR, `${svc}.log`);
17678
- if (!existsSync9(logFile)) {
17863
+ if (!existsSync10(logFile)) {
17679
17864
  console.log(chalk4.gray(` [${svc}] No log file yet at ${logFile}`));
17680
17865
  continue;
17681
17866
  }
@@ -17690,18 +17875,18 @@ async function runLogs(service) {
17690
17875
  }
17691
17876
  if (target !== "all") {
17692
17877
  const logFile = join9(LOG_DIR, `${target}.log`);
17693
- if (!existsSync9(logFile)) {
17878
+ if (!existsSync10(logFile)) {
17694
17879
  console.log(chalk4.yellow("Waiting for log file to be created\u2026"));
17695
17880
  }
17696
17881
  console.log(chalk4.gray("\nFollowing log (Ctrl+C to stop)...\n"));
17697
17882
  let fileSize = 0;
17698
17883
  try {
17699
- fileSize = statSync(logFile).size;
17884
+ fileSize = statSync2(logFile).size;
17700
17885
  } catch {
17701
17886
  }
17702
17887
  const watcher = watch(logFile, () => {
17703
17888
  try {
17704
- const newSize = statSync(logFile).size;
17889
+ const newSize = statSync2(logFile).size;
17705
17890
  if (newSize > fileSize) {
17706
17891
  const stream = createReadStream(logFile, { start: fileSize, encoding: "utf-8" });
17707
17892
  stream.on("data", (chunk) => process.stdout.write(chunk));
@@ -17728,7 +17913,7 @@ var init_logs = __esm({
17728
17913
  import { execFile } from "child_process";
17729
17914
  import { createHash as createHash2 } from "crypto";
17730
17915
  import { mkdir, writeFile } from "fs/promises";
17731
- import { existsSync as existsSync10 } from "fs";
17916
+ import { existsSync as existsSync11 } from "fs";
17732
17917
  import { join as join10 } from "path";
17733
17918
  import { promisify } from "util";
17734
17919
  function workspaceKey(workspace) {
@@ -17768,7 +17953,7 @@ async function ensureStore(store) {
17768
17953
  const gitDir = join10(store, "store");
17769
17954
  await mkdir(join10(store, "indexes"), { recursive: true });
17770
17955
  await writeFile(join10(store, "gitconfig"), "", { flag: "a" });
17771
- if (!existsSync10(join10(gitDir, "HEAD"))) {
17956
+ if (!existsSync11(join10(gitDir, "HEAD"))) {
17772
17957
  await mkdir(gitDir, { recursive: true });
17773
17958
  await run("git", ["init", "--bare", "--quiet", gitDir], {
17774
17959
  timeout: GIT_TIMEOUT_MS,
@@ -17801,7 +17986,7 @@ async function snapshot(store, workspace, label) {
17801
17986
  return { sha, workspace, at, label };
17802
17987
  }
17803
17988
  async function listCheckpoints(store, workspace, limit = 20) {
17804
- if (!existsSync10(join10(store, "store", "HEAD"))) return [];
17989
+ if (!existsSync11(join10(store, "store", "HEAD"))) return [];
17805
17990
  const ref = `refs/nodal/${workspaceKey(workspace)}`;
17806
17991
  const out = await git(store, workspace, [
17807
17992
  "log",
@@ -30975,7 +31160,13 @@ async function buildNodalMcpServer(opts) {
30975
31160
  }
30976
31161
  targetAgent = root;
30977
31162
  } else {
30978
- const [launcher] = await opts.db.select({ id: agents.id }).from(agents).where(and(eq(agents.id, agentRow.id), eq(agents.active, true))).limit(1);
31163
+ const [launcher] = await opts.db.select({ id: agents.id }).from(agents).where(
31164
+ and(
31165
+ eq(agents.id, agentRow.id),
31166
+ eq(agents.active, true),
31167
+ eq(agents.entityId, entityId)
31168
+ )
31169
+ ).limit(1);
30979
31170
  if (!launcher) {
30980
31171
  throw new Error(
30981
31172
  "mcp_agent_not_found: the agent this server was launched for has been deactivated. Reactivate it, set a root agent, or restart the server for another agent."
@@ -31185,7 +31376,7 @@ __export(reset_exports, {
31185
31376
  });
31186
31377
  import chalk6 from "chalk";
31187
31378
  import prompts2 from "prompts";
31188
- import { rmSync, existsSync as existsSync11 } from "fs";
31379
+ import { rmSync as rmSync2, existsSync as existsSync12 } from "fs";
31189
31380
  async function runReset(options = {}) {
31190
31381
  if (!options.yes) {
31191
31382
  const { confirm } = await prompts2({
@@ -31203,8 +31394,8 @@ async function runReset(options = {}) {
31203
31394
  }
31204
31395
  console.log(chalk6.yellow("Stopping any running processes\u2026"));
31205
31396
  await runDown();
31206
- if (existsSync11(CONFIG_DIR)) {
31207
- rmSync(CONFIG_DIR, { recursive: true, force: true });
31397
+ if (existsSync12(CONFIG_DIR)) {
31398
+ rmSync2(CONFIG_DIR, { recursive: true, force: true });
31208
31399
  console.log(chalk6.green(` Removed ${CONFIG_DIR}`));
31209
31400
  } else {
31210
31401
  console.log(chalk6.gray(` ${CONFIG_DIR} was already empty`));
@@ -0,0 +1,32 @@
1
+ -- 0082 — inversion du modele a deux cles Yolo (decision Quentin 24/08).
2
+ --
3
+ -- L'ancien lan_command_yolo etait une PRE-CONDITION : sur une install reseau,
4
+ -- il fallait l'allumer pour avoir LE DROIT d'activer le Yolo par agent — deux
5
+ -- serrures, une seule cle (les deux gestes etaient deja owner-only). Sa vraie
6
+ -- valeur etait le coupe-circuit : l'eteindre rendait dormantes toutes les
7
+ -- regles d'un coup. auto_run_paused ne garde QUE ce role : un frein
8
+ -- d'urgence, inactif par defaut, qui met en pause tout l'auto-run de code du
9
+ -- workspace quand on l'enclenche.
10
+ --
11
+ -- Le backfill preserve l'INTENTION de chaque install : un master OFF alors
12
+ -- que des regles auto_approve d'outils d'execution existent = le proprietaire
13
+ -- avait deliberement coupe (usage coupe-circuit) -> pause maintenue. Un
14
+ -- master OFF sans aucune regle armee (l'immense majorite, dont toute install
15
+ -- neuve) = rien a couper -> pas de pause. Un master ON = il autorisait ->
16
+ -- pas de pause. Aucun comportement effectif ne change au reboot.
17
+ ALTER TABLE entities ADD COLUMN auto_run_paused boolean NOT NULL DEFAULT false;
18
+
19
+ UPDATE entities e
20
+ SET auto_run_paused = true
21
+ WHERE e.lan_command_yolo = false
22
+ AND EXISTS (
23
+ SELECT 1
24
+ FROM approval_rules ar
25
+ LEFT JOIN agents a ON a.id = ar.agent_id
26
+ WHERE ar.action = 'auto_approve'
27
+ AND ar.tool_name IN ('run_command', 'code_task', 'run_skill_script',
28
+ 'skill_file_write', 'create_mcp', 'attach_mcp')
29
+ AND (a.entity_id = e.id OR ar.entity_id = e.id)
30
+ );
31
+
32
+ ALTER TABLE entities DROP COLUMN lan_command_yolo;
@@ -0,0 +1,14 @@
1
+ -- 0083 — archives de projets de l'onglet Code (décision Quentin 25/08).
2
+ -- Un « projet » est DÉRIVÉ à l'affichage (racine git / workspace des fichiers
3
+ -- touchés) — il n'existe pas en base. L'archivage, lui, est un état UI qui
4
+ -- doit survivre : une ligne par (workspace, chemin de projet) archivé.
5
+ -- AUCUN effet sur le dossier réel — désarchiver = supprimer la ligne.
6
+ CREATE TABLE IF NOT EXISTS "code_project_archives" (
7
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
8
+ "entity_id" uuid NOT NULL REFERENCES "entities"("id") ON DELETE CASCADE,
9
+ "project_path" text NOT NULL,
10
+ "archived_at" timestamp with time zone DEFAULT now() NOT NULL,
11
+ CONSTRAINT "code_project_archives_entity_path_unique" UNIQUE ("entity_id", "project_path")
12
+ );
13
+ --> statement-breakpoint
14
+ CREATE INDEX IF NOT EXISTS "idx_code_project_archives_entity" ON "code_project_archives" ("entity_id");
@@ -0,0 +1,24 @@
1
+ -- 0084 — annule le backfill fautif de 0082 (revue P0 du 25/08, finding bloquant).
2
+ --
3
+ -- Le backfill de 0082 posait `auto_run_paused = true` quand l'ancien
4
+ -- `lan_command_yolo` valait false ET qu'une regle auto_approve d'outil de code
5
+ -- existait, en lisant ce false comme « le proprietaire avait deliberement
6
+ -- coupe ». La premisse est FAUSSE sur le mode par defaut : hors LAN
7
+ -- (local-trust), l'ancienne garde ne s'appliquait pas du tout et la section
8
+ -- Settings etait masquee — le drapeau y valait false par construction, sans
9
+ -- jamais avoir rien voulu dire. Consequence : une install locale qui avait
10
+ -- active un Yolo se retrouvait, apres mise a jour, avec ses crons et ses
11
+ -- taches Telegram suspendus en attente d'approbation, sans qu'aucun geste
12
+ -- n'ait ete fait.
13
+ --
14
+ -- Le frein est un GESTE DELIBERE, pas un etat devine : il repart relache
15
+ -- partout (sa valeur par defaut), et quiconque le veut l'enclenche en un clic
16
+ -- dans Settings. La fenetre concernee est courte (0082 date du 24/08) et
17
+ -- l'etat est visible dans l'interface, donc la correction ne peut pas
18
+ -- surprendre en silence.
19
+ --
20
+ -- Le durcissement qui compte vit ailleurs, dans le code : l'autonomie du
21
+ -- workspace ne relaxe plus JAMAIS les outils d'execution de code
22
+ -- (packages/tools/src/execute.ts, isCodeExecutionTool) — seule une regle
23
+ -- explicite par agent le fait.
24
+ UPDATE entities SET auto_run_paused = false WHERE auto_run_paused = true;
@@ -0,0 +1,33 @@
1
+ -- 0085 — un dossier de developpement se COCHE, il ne se devine pas.
2
+ --
3
+ -- L'onglet Code doit montrer le travail de developpement, et rien d'autre.
4
+ -- Quatre definitions ont ete essayees et ecartees, toutes parce qu'elles
5
+ -- DEVINAIENT :
6
+ --
7
+ -- * la nature des fichiers edites (extensions) : « une exclusion par langage
8
+ -- ratera tot ou tard du vrai code » — un .json de mock-data EST du code.
9
+ -- Et le coffre Obsidian qualifiait quand meme, a cause de vrais .py ecrits
10
+ -- dedans en juillet ;
11
+ -- * le skill porte par l'agent : ne marche que si l'utilisateur emploie NOS
12
+ -- skills, jamais les siens ni ceux du catalogue communautaire ;
13
+ -- * la structure du dossier (package.json, .git…) : symetrique du premier
14
+ -- echec — « on va 100 % avoir des faux positifs », un depot clone ou un
15
+ -- theme jamais touche en porte un ;
16
+ -- * une case sur l'AGENT : repond au « qui », pas au « ou ». Un agent qui
17
+ -- code le matin et range le coffre l'apres-midi y ferait entrer le coffre.
18
+ --
19
+ -- D'ou cette colonne : la designation porte sur le DOSSIER, et elle est
20
+ -- explicite. La case definit un PERIMETRE, pas un projet — cocher `Dev` ne
21
+ -- fait pas de `Dev` un projet, ses sous-dossiers en sont.
22
+ --
23
+ -- Aucun backfill : cocher un dossier a la place du proprietaire serait une
24
+ -- devinette de plus, exactement ce que ce lot supprime. L'onglet Code nomme le
25
+ -- geste tant qu'aucun dossier n'est coche.
26
+ ALTER TABLE agent_workspaces
27
+ ADD COLUMN IF NOT EXISTS is_dev_folder boolean NOT NULL DEFAULT false;
28
+ --> statement-breakpoint
29
+ -- La derivation des projets lit les dossiers coches a chaque job et a chaque
30
+ -- chargement de l'onglet : un index partiel garde ces lectures triviales.
31
+ CREATE INDEX IF NOT EXISTS idx_agent_workspaces_dev_folder
32
+ ON agent_workspaces (entity_id)
33
+ WHERE is_dev_folder;
@@ -0,0 +1,61 @@
1
+ -- 0086 — l'onglet Code cesse de DEVINER, et l'utilisateur reprend la main.
2
+ --
3
+ -- Six definitions ont ete essayees pour savoir ce qui merite d'apparaitre dans
4
+ -- l'onglet Code. Toutes ecartees, toutes pour la meme raison : elles
5
+ -- decidaient a la place du proprietaire, sur un indice indirect.
6
+ --
7
+ -- 1. l'extension des fichiers « une exclusion par langage ratera tot
8
+ -- ou tard du vrai code »
9
+ -- 2. le skill porte par l'agent ne marche qu'avec NOS skills
10
+ -- 3. la structure du dossier « on va 100 % avoir des faux positifs »
11
+ -- 4. une case sur l'agent repond au « qui », pas au « ou »
12
+ -- 5. une case sur le dossier (0085) il faut encore deviner si le
13
+ -- dossier coche EST un projet ou en
14
+ -- CONTIENT
15
+ -- 6. une nature devinee par dossier liste sans fin : Obsidian, ComfyUI,
16
+ -- Blender, Unity, Godot, un CMS…
17
+ --
18
+ -- Decision Quentin (26/08) : ne rien deviner. L'onglet montre les dossiers ou
19
+ -- les agents ont travaille, et le proprietaire dispose des deux seuls gestes
20
+ -- que le produit ne peut pas poser a sa place : RENOMMER ce qui porte mal son
21
+ -- nom, MASQUER ce qu'il ne veut plus voir.
22
+ --
23
+ -- D'ou cette migration :
24
+ -- * `agent_workspaces.is_dev_folder` disparait — le filtrage n'existe plus ;
25
+ -- * `code_project_archives` devient `code_projects`, qui porte desormais les
26
+ -- DEUX gestes au lieu du seul masquage.
27
+ --
28
+ -- « Masquer » plutot qu'« archiver » : le dossier n'est jamais touche, rien
29
+ -- n'est fini ni range definitivement — c'est un choix d'affichage, et le mot
30
+ -- doit le dire. Le masquage retire aussi le projet du contexte injecte aux
31
+ -- agents, ce que l'archivage ne faisait pas : un projet range n'a pas a etre
32
+ -- annonce dans le prompt de tout le monde.
33
+ ALTER TABLE agent_workspaces DROP COLUMN IF EXISTS is_dev_folder;
34
+ --> statement-breakpoint
35
+ CREATE TABLE IF NOT EXISTS code_projects (
36
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
37
+ entity_id uuid NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
38
+ -- Chemin absolu du projet, slash-normalise (la forme que rend la derivation).
39
+ project_path text NOT NULL,
40
+ -- Nom choisi par le proprietaire. NULL = on affiche le nom du dossier.
41
+ display_name text,
42
+ -- Masque de la liste ET du contexte des agents.
43
+ hidden boolean NOT NULL DEFAULT false,
44
+ created_at timestamptz NOT NULL DEFAULT now(),
45
+ updated_at timestamptz NOT NULL DEFAULT now(),
46
+ UNIQUE (entity_id, project_path)
47
+ );
48
+ --> statement-breakpoint
49
+ -- Les projets deja archives restent masques : le geste avait ete pose, il est
50
+ -- conserve tel quel.
51
+ INSERT INTO code_projects (entity_id, project_path, hidden, created_at)
52
+ SELECT entity_id, project_path, true, archived_at
53
+ FROM code_project_archives
54
+ ON CONFLICT (entity_id, project_path) DO NOTHING;
55
+ --> statement-breakpoint
56
+ DROP TABLE IF EXISTS code_project_archives;
57
+ --> statement-breakpoint
58
+ -- La liste est lue a chaque chargement de l'onglet ET a chaque job (le contexte
59
+ -- des agents doit savoir ce qui est masque) : l'index garde ces lectures
60
+ -- triviales.
61
+ CREATE INDEX IF NOT EXISTS idx_code_projects_entity ON code_projects (entity_id);