nodal-agents 0.3.10 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/README.md +4 -4
  2. package/cli.js +68 -2
  3. package/migrations/0024_agent_jobs_dashboard_channel.sql +11 -0
  4. package/migrations/0025_agent_jobs_conversational.sql +9 -0
  5. package/migrations/0026_chat_messages.sql +19 -0
  6. package/migrations/0027_drop_agent_jobs_conversational.sql +6 -0
  7. package/migrations/0028_conversations.sql +27 -0
  8. package/migrations/0029_backfill_root_agent.sql +33 -0
  9. package/migrations/meta/_journal.json +216 -174
  10. package/package.json +1 -1
  11. package/runner.js +1666 -1161
  12. package/web/.next/BUILD_ID +1 -1
  13. package/web/.next/app-path-routes-manifest.json +1 -0
  14. package/web/.next/build-manifest.json +2 -2
  15. package/web/.next/routes-manifest.json +6 -0
  16. package/web/.next/server/app/(dashboard)/agents/[id]/edit/page.js +3 -3
  17. package/web/.next/server/app/(dashboard)/agents/[id]/edit/page.js.nft.json +1 -1
  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 +2 -2
  20. package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page.js.nft.json +1 -1
  21. package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page_client-reference-manifest.js +1 -1
  22. package/web/.next/server/app/(dashboard)/agents/page.js +2 -2
  23. package/web/.next/server/app/(dashboard)/agents/page.js.nft.json +1 -1
  24. package/web/.next/server/app/(dashboard)/agents/page_client-reference-manifest.js +1 -1
  25. package/web/.next/server/app/(dashboard)/approvals/page.js +2 -2
  26. package/web/.next/server/app/(dashboard)/approvals/page.js.nft.json +1 -1
  27. package/web/.next/server/app/(dashboard)/approvals/page_client-reference-manifest.js +1 -1
  28. package/web/.next/server/app/(dashboard)/automations/page.js +2 -2
  29. package/web/.next/server/app/(dashboard)/automations/page.js.nft.json +1 -1
  30. package/web/.next/server/app/(dashboard)/automations/page_client-reference-manifest.js +1 -1
  31. package/web/.next/server/app/(dashboard)/billing/page.js +2 -2
  32. package/web/.next/server/app/(dashboard)/billing/page.js.nft.json +1 -1
  33. package/web/.next/server/app/(dashboard)/billing/page_client-reference-manifest.js +1 -1
  34. package/web/.next/server/app/(dashboard)/chat/page.js +2 -0
  35. package/web/.next/server/app/(dashboard)/chat/page.js.nft.json +1 -0
  36. package/web/.next/server/app/(dashboard)/chat/page_client-reference-manifest.js +1 -0
  37. package/web/.next/server/app/(dashboard)/connectors/page.js +2 -2
  38. package/web/.next/server/app/(dashboard)/connectors/page.js.nft.json +1 -1
  39. package/web/.next/server/app/(dashboard)/connectors/page_client-reference-manifest.js +1 -1
  40. package/web/.next/server/app/(dashboard)/credentials/page.js +2 -2
  41. package/web/.next/server/app/(dashboard)/credentials/page.js.nft.json +1 -1
  42. package/web/.next/server/app/(dashboard)/credentials/page_client-reference-manifest.js +1 -1
  43. package/web/.next/server/app/(dashboard)/jobs/[id]/page.js +2 -2
  44. package/web/.next/server/app/(dashboard)/jobs/[id]/page.js.nft.json +1 -1
  45. package/web/.next/server/app/(dashboard)/jobs/[id]/page_client-reference-manifest.js +1 -1
  46. package/web/.next/server/app/(dashboard)/jobs/page.js +2 -2
  47. package/web/.next/server/app/(dashboard)/jobs/page.js.nft.json +1 -1
  48. package/web/.next/server/app/(dashboard)/jobs/page_client-reference-manifest.js +1 -1
  49. package/web/.next/server/app/(dashboard)/llm-providers/page.js +2 -2
  50. package/web/.next/server/app/(dashboard)/llm-providers/page.js.nft.json +1 -1
  51. package/web/.next/server/app/(dashboard)/llm-providers/page_client-reference-manifest.js +1 -1
  52. package/web/.next/server/app/(dashboard)/logs/page.js +2 -2
  53. package/web/.next/server/app/(dashboard)/logs/page.js.nft.json +1 -1
  54. package/web/.next/server/app/(dashboard)/logs/page_client-reference-manifest.js +1 -1
  55. package/web/.next/server/app/(dashboard)/mcp/page.js +2 -2
  56. package/web/.next/server/app/(dashboard)/mcp/page.js.nft.json +1 -1
  57. package/web/.next/server/app/(dashboard)/mcp/page_client-reference-manifest.js +1 -1
  58. package/web/.next/server/app/(dashboard)/memories/page.js +2 -2
  59. package/web/.next/server/app/(dashboard)/memories/page.js.nft.json +1 -1
  60. package/web/.next/server/app/(dashboard)/memories/page_client-reference-manifest.js +1 -1
  61. package/web/.next/server/app/(dashboard)/page.js +3 -3
  62. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  63. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  64. package/web/.next/server/app/(dashboard)/settings/page.js +2 -63
  65. package/web/.next/server/app/(dashboard)/settings/page.js.nft.json +1 -1
  66. package/web/.next/server/app/(dashboard)/settings/page_client-reference-manifest.js +1 -1
  67. package/web/.next/server/app/(dashboard)/skills/[id]/edit/page.js +2 -2
  68. package/web/.next/server/app/(dashboard)/skills/[id]/edit/page.js.nft.json +1 -1
  69. package/web/.next/server/app/(dashboard)/skills/[id]/edit/page_client-reference-manifest.js +1 -1
  70. package/web/.next/server/app/(dashboard)/skills/new/page.js +2 -2
  71. package/web/.next/server/app/(dashboard)/skills/new/page.js.nft.json +1 -1
  72. package/web/.next/server/app/(dashboard)/skills/new/page_client-reference-manifest.js +1 -1
  73. package/web/.next/server/app/(dashboard)/skills/page.js +2 -2
  74. package/web/.next/server/app/(dashboard)/skills/page.js.nft.json +1 -1
  75. package/web/.next/server/app/(dashboard)/skills/page_client-reference-manifest.js +1 -1
  76. package/web/.next/server/app/_global-error/page.js +2 -2
  77. package/web/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  78. package/web/.next/server/app/_global-error.html +1 -1
  79. package/web/.next/server/app/_global-error.rsc +1 -1
  80. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  81. package/web/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  82. package/web/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  83. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  84. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  85. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  86. package/web/.next/server/app/_not-found/page.js +2 -2
  87. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  88. package/web/.next/server/app/_not-found.html +1 -1
  89. package/web/.next/server/app/_not-found.rsc +2 -2
  90. package/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  91. package/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  92. package/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  93. package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  94. package/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  95. package/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  96. package/web/.next/server/app/api/auth/[...all]/route.js +1 -1
  97. package/web/.next/server/app/api/health/route.js +1 -1
  98. package/web/.next/server/app/api/oauth/[provider]/callback/route.js +1 -1
  99. package/web/.next/server/app/api/oauth/[provider]/start/route.js +1 -1
  100. package/web/.next/server/app/auth/callback/route.js +1 -1
  101. package/web/.next/server/app/login/page.js +2 -2
  102. package/web/.next/server/app/login/page_client-reference-manifest.js +1 -1
  103. package/web/.next/server/app/onboarding/page.js +2 -2
  104. package/web/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
  105. package/web/.next/server/app/onboarding.html +1 -1
  106. package/web/.next/server/app/onboarding.rsc +2 -2
  107. package/web/.next/server/app/onboarding.segments/_full.segment.rsc +2 -2
  108. package/web/.next/server/app/onboarding.segments/_head.segment.rsc +1 -1
  109. package/web/.next/server/app/onboarding.segments/_index.segment.rsc +2 -2
  110. package/web/.next/server/app/onboarding.segments/_tree.segment.rsc +2 -2
  111. package/web/.next/server/app/onboarding.segments/onboarding/__PAGE__.segment.rsc +1 -1
  112. package/web/.next/server/app/onboarding.segments/onboarding.segment.rsc +1 -1
  113. package/web/.next/server/app-paths-manifest.json +1 -0
  114. package/web/.next/server/chunks/1511.js +1 -0
  115. package/web/.next/server/chunks/2103.js +1 -0
  116. package/web/.next/server/chunks/{6937.js → 3057.js} +1 -1
  117. package/web/.next/server/chunks/4574.js +1 -1
  118. package/web/.next/server/chunks/7557.js +62 -0
  119. package/web/.next/server/chunks/7741.js +2 -2
  120. package/web/.next/server/chunks/8178.js +1 -0
  121. package/web/.next/server/chunks/9201.js +1 -0
  122. package/web/.next/server/chunks/9824.js +1 -0
  123. package/web/.next/server/middleware-build-manifest.js +1 -1
  124. package/web/.next/server/pages/404.html +1 -1
  125. package/web/.next/server/pages/500.html +1 -1
  126. package/web/.next/server/server-reference-manifest.js +1 -1
  127. package/web/.next/server/server-reference-manifest.json +1 -1
  128. package/web/.next/static/9FXcaPSw8KYgjKzjKLpT2/_buildManifest.js +1 -0
  129. package/web/.next/static/chunks/2569-6b5e0af9c1f584a4.js +1 -0
  130. package/web/.next/static/chunks/5801-e411029984b17b8b.js +1 -0
  131. package/web/.next/static/chunks/8503-ced632da5c3fce79.js +1 -0
  132. package/web/.next/static/chunks/9421-d522a48618c4fe37.js +62 -0
  133. package/web/.next/static/chunks/app/(dashboard)/chat/page-2c8f9571a443f250.js +1 -0
  134. package/web/.next/static/chunks/app/(dashboard)/connectors/page-72ccb0e3a5ed6f2d.js +1 -0
  135. package/web/.next/static/chunks/app/(dashboard)/layout-4d5634ba460464d7.js +1 -0
  136. package/web/.next/static/chunks/app/(dashboard)/mcp/page-426478332dfe8313.js +1 -0
  137. package/web/.next/static/chunks/app/(dashboard)/settings/page-1cc10beb46234c7d.js +1 -0
  138. package/web/.next/static/chunks/app/(dashboard)/skills/page-4566512d74e54bfe.js +1 -0
  139. package/web/.next/static/css/0a81480f93d3ab37.css +3 -0
  140. package/web/.next/server/chunks/5163.js +0 -1
  141. package/web/.next/server/chunks/6680.js +0 -1
  142. package/web/.next/server/chunks/8013.js +0 -1
  143. package/web/.next/server/chunks/9140.js +0 -1
  144. package/web/.next/static/chunks/374-60415230f01c844a.js +0 -1
  145. package/web/.next/static/chunks/5070-4385fb454f6ec84b.js +0 -62
  146. package/web/.next/static/chunks/639-79c3c2ac769ef007.js +0 -1
  147. package/web/.next/static/chunks/app/(dashboard)/connectors/page-e12174534c2c2acf.js +0 -1
  148. package/web/.next/static/chunks/app/(dashboard)/layout-d01f5919f54fb37a.js +0 -1
  149. package/web/.next/static/chunks/app/(dashboard)/mcp/page-3fa9d4448a31b696.js +0 -1
  150. package/web/.next/static/chunks/app/(dashboard)/settings/page-8bc69f353f8d48a3.js +0 -1
  151. package/web/.next/static/chunks/app/(dashboard)/skills/page-54c6adeb65475a1a.js +0 -1
  152. package/web/.next/static/css/90e1bf719df42081.css +0 -3
  153. package/web/.next/static/oCvSEboPKulEdUGcfE5Px/_buildManifest.js +0 -1
  154. /package/web/.next/static/{oCvSEboPKulEdUGcfE5Px → 9FXcaPSw8KYgjKzjKLpT2}/_ssgManifest.js +0 -0
package/README.md CHANGED
@@ -220,7 +220,8 @@ changes are still possible between minors.
220
220
  Airtable) and API-key (Notion, Airtable, Apify, Firecrawl, Tavily)
221
221
  - 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
222
222
  - Top-level workspaces — multiple isolated entities (agents/skills/connectors/jobs/memory per workspace), switch in the sidebar
223
- - ROOT agentdesignate an orchestrator that can create *and update* skills, create agents and assign them, gated by per-grant toggles + an autonomy/approval level; skill authoring is grounded in the workspace's real tools (a linter rejects skills referencing tools the agent doesn't have)
223
+ - In-app ROOT chattalk to your workspace ROOT right in the dashboard: conversation-first (pure chat never creates a job — recall is free, the agent's memory is auto-loaded), multiple conversations with searchable history, and inline "dispatched to N agents" cards when it escalates a real action into a tracked job
224
+ - 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)
224
225
  - Office file editing — Excel in-place edit, Word/PowerPoint create, in the agent workspace (office-editing skill)
225
226
  - Multiple filesystem folders per agent (sandboxed `file_*` tools)
226
227
  - Telegram delivery (long-poll, group filters, multi-agent routing,
@@ -237,9 +238,8 @@ changes are still possible between minors.
237
238
 
238
239
  - **MCP OAuth flow** → unlocks Linear, Notion remote, GitHub remote,
239
240
  Atlassian, Sentry, and the rest of the SaaS-as-MCP ecosystem.
240
- - **Dashboard chat for the ROOT companion** → talk to your ROOT agent in
241
- the app, multi-turn, instead of via Telegram; plus a dry-run mode and a
242
- test-workflow meta-tool.
241
+ - **Dry-run mode + a test-workflow meta-tool** → preview what the ROOT would
242
+ do before it runs, and let it validate an automation end-to-end.
243
243
  - **pgvector binaries bundled in the npm pack** → semantic memory search
244
244
  active out-of-the-box. Today, installs without pgvector fall back to
245
245
  keyword search (which works, just less smart for cross-vocabulary
package/cli.js CHANGED
@@ -11272,7 +11272,8 @@ var init_enums = __esm({
11272
11272
  "cron",
11273
11273
  "task-board",
11274
11274
  "slack",
11275
- "discord"
11275
+ "discord",
11276
+ "dashboard"
11276
11277
  ];
11277
11278
  JobChannelSchema = z2.enum(JOB_CHANNELS);
11278
11279
  JOB_STATUSES = [
@@ -12170,11 +12171,20 @@ var init_root_agent = __esm({
12170
12171
  createSkill: z19.boolean(),
12171
12172
  updateSkill: z19.boolean(),
12172
12173
  assignSkill: z19.boolean(),
12174
+ createMcp: z19.boolean(),
12175
+ createConnector: z19.boolean(),
12173
12176
  autonomy: AutonomyLevelSchema
12174
12177
  });
12175
12178
  }
12176
12179
  });
12177
12180
 
12181
+ // ../../packages/shared/src/connector-catalog.ts
12182
+ var init_connector_catalog = __esm({
12183
+ "../../packages/shared/src/connector-catalog.ts"() {
12184
+ "use strict";
12185
+ }
12186
+ });
12187
+
12178
12188
  // ../../packages/shared/src/index.ts
12179
12189
  var init_src2 = __esm({
12180
12190
  "../../packages/shared/src/index.ts"() {
@@ -12199,6 +12209,7 @@ var init_src2 = __esm({
12199
12209
  init_operation();
12200
12210
  init_providers();
12201
12211
  init_root_agent();
12212
+ init_connector_catalog();
12202
12213
  }
12203
12214
  });
12204
12215
 
@@ -12531,7 +12542,7 @@ var init_jobs = __esm({
12531
12542
  ),
12532
12543
  check(
12533
12544
  "agent_jobs_channel_check",
12534
- sql`${table.channel} IN ('telegram','api','whatsapp','internal','cron','task-board','slack','discord')`
12545
+ sql`${table.channel} IN ('telegram','api','whatsapp','internal','cron','task-board','slack','discord','dashboard')`
12535
12546
  )
12536
12547
  ]
12537
12548
  );
@@ -13290,6 +13301,56 @@ var init_agent_workspaces = __esm({
13290
13301
  }
13291
13302
  });
13292
13303
 
13304
+ // ../../packages/db/src/schema/chat-messages.ts
13305
+ var conversations, chatMessages;
13306
+ var init_chat_messages = __esm({
13307
+ "../../packages/db/src/schema/chat-messages.ts"() {
13308
+ "use strict";
13309
+ init_pg_core();
13310
+ init_drizzle_orm();
13311
+ init_entities();
13312
+ init_agents();
13313
+ init_jobs();
13314
+ conversations = pgTable(
13315
+ "conversations",
13316
+ {
13317
+ id: uuid("id").primaryKey().defaultRandom(),
13318
+ entityId: uuid("entity_id").references(() => entities.id, { onDelete: "cascade" }),
13319
+ // The agent this conversation is with (the ROOT, today).
13320
+ agentId: uuid("agent_id").notNull().references(() => agents.id, { onDelete: "cascade" }),
13321
+ title: text("title").notNull().default(""),
13322
+ createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
13323
+ // Bumped on each new turn — drives the recency sort in the sidebar.
13324
+ updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow()
13325
+ },
13326
+ (table) => [
13327
+ index("idx_conversations_entity_agent").on(table.entityId, table.agentId, table.updatedAt)
13328
+ ]
13329
+ );
13330
+ chatMessages = pgTable(
13331
+ "chat_messages",
13332
+ {
13333
+ id: uuid("id").primaryKey().defaultRandom(),
13334
+ entityId: uuid("entity_id").references(() => entities.id, { onDelete: "cascade" }),
13335
+ agentId: uuid("agent_id").notNull().references(() => agents.id, { onDelete: "cascade" }),
13336
+ conversationId: uuid("conversation_id").references(() => conversations.id, {
13337
+ onDelete: "cascade"
13338
+ }),
13339
+ role: text("role").notNull(),
13340
+ content: text("content").notNull(),
13341
+ // Set when this (assistant) turn escalated into a real action job — lets the
13342
+ // UI render the job's dispatch/progress inline. NULL for pure conversation.
13343
+ jobId: uuid("job_id").references(() => agentJobs.id, { onDelete: "set null" }),
13344
+ createdAt: timestamp("created_at", { withTimezone: true }).defaultNow()
13345
+ },
13346
+ (table) => [
13347
+ index("idx_chat_messages_conversation").on(table.conversationId, table.createdAt),
13348
+ check("chat_messages_role_check", sql`${table.role} IN ('user','assistant')`)
13349
+ ]
13350
+ );
13351
+ }
13352
+ });
13353
+
13293
13354
  // ../../packages/db/src/schema/index.ts
13294
13355
  var schema_exports = {};
13295
13356
  __export(schema_exports, {
@@ -13331,8 +13392,10 @@ __export(schema_exports, {
13331
13392
  agents: () => agents,
13332
13393
  approvalRequests: () => approvalRequests,
13333
13394
  approvalRules: () => approvalRules,
13395
+ chatMessages: () => chatMessages,
13334
13396
  configuratorSessions: () => configuratorSessions,
13335
13397
  connectors: () => connectors,
13398
+ conversations: () => conversations,
13336
13399
  credentials: () => credentials,
13337
13400
  entities: () => entities,
13338
13401
  entityLlmKeys: () => entityLlmKeys,
@@ -13373,6 +13436,7 @@ var init_schema2 = __esm({
13373
13436
  init_auth();
13374
13437
  init_agent_connector_assignments();
13375
13438
  init_agent_workspaces();
13439
+ init_chat_messages();
13376
13440
  }
13377
13441
  });
13378
13442
 
@@ -13433,7 +13497,9 @@ var init_credentials2 = __esm({
13433
13497
  var init_agents2 = __esm({
13434
13498
  "../../packages/db/src/repos/agents.ts"() {
13435
13499
  "use strict";
13500
+ init_src2();
13436
13501
  init_agents();
13502
+ init_entities();
13437
13503
  }
13438
13504
  });
13439
13505
 
@@ -0,0 +1,11 @@
1
+ -- 0024_agent_jobs_dashboard_channel.sql
2
+ -- V4 — in-app ROOT chat (2026-05-31):
3
+ -- Allow 'dashboard' as an agent_jobs.channel value so the dashboard chat with
4
+ -- the ROOT agent reuses the same job lifecycle + thread continuity as Telegram.
5
+ -- Rebuild the CHECK constraint to include 'dashboard'. Idempotent.
6
+
7
+ ALTER TABLE agent_jobs DROP CONSTRAINT IF EXISTS agent_jobs_channel_check;
8
+
9
+ ALTER TABLE agent_jobs
10
+ ADD CONSTRAINT agent_jobs_channel_check
11
+ CHECK (channel IN ('telegram','api','whatsapp','internal','cron','task-board','slack','discord','dashboard'));
@@ -0,0 +1,9 @@
1
+ -- 0025_agent_jobs_conversational.sql
2
+ -- V4 — in-app ROOT chat, "jobless" conversation model (2026-05-31):
3
+ -- A chat turn that only used conversational tools (text + memory + delivery)
4
+ -- is conversation, not work — it belongs in /chat, never in /jobs. The runner
5
+ -- flags it at completion; the dashboard filters Runs/stats on this column.
6
+ -- Default false → all existing + non-chat jobs remain visible as Runs.
7
+
8
+ ALTER TABLE agent_jobs
9
+ ADD COLUMN IF NOT EXISTS conversational boolean NOT NULL DEFAULT false;
@@ -0,0 +1,19 @@
1
+ -- 0026_chat_messages.sql
2
+ -- V4 — conversation-first in-app chat (2026-05-31):
3
+ -- A conversation is NOT a job. Pure chat turns (text + memory) are stored here
4
+ -- and never create an agent_jobs row. Only an ACTION turn escalates to a job,
5
+ -- whose id is linked via job_id so the UI shows progress inline.
6
+
7
+ CREATE TABLE IF NOT EXISTS chat_messages (
8
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
9
+ entity_id uuid REFERENCES entities(id) ON DELETE CASCADE,
10
+ agent_id uuid NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
11
+ thread_id text NOT NULL DEFAULT 'main',
12
+ role text NOT NULL CHECK (role IN ('user','assistant')),
13
+ content text NOT NULL,
14
+ job_id uuid REFERENCES agent_jobs(id) ON DELETE SET NULL,
15
+ created_at timestamptz DEFAULT now()
16
+ );
17
+
18
+ CREATE INDEX IF NOT EXISTS idx_chat_messages_thread
19
+ ON chat_messages (entity_id, agent_id, thread_id, created_at);
@@ -0,0 +1,6 @@
1
+ -- 0027_drop_agent_jobs_conversational.sql
2
+ -- Revert the short-lived "conversational" flag (migration 0025). The in-app chat
3
+ -- is now conversation-first: pure chat lives in chat_messages and never creates
4
+ -- an agent_jobs row at all, so flagging/hiding jobs is no longer needed.
5
+
6
+ ALTER TABLE agent_jobs DROP COLUMN IF EXISTS conversational;
@@ -0,0 +1,27 @@
1
+ -- 0028_conversations.sql
2
+ -- V4 chat — multi-conversation backbone (2026-06-01):
3
+ -- Group chat_messages under named `conversations` (the sidebar entries), so
4
+ -- the chat gets multiple threads, "+ New", recency sort, and a clean history
5
+ -- model. Replaces the single hardcoded thread_id on chat_messages.
6
+
7
+ CREATE TABLE IF NOT EXISTS conversations (
8
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
9
+ entity_id uuid REFERENCES entities(id) ON DELETE CASCADE,
10
+ agent_id uuid NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
11
+ title text NOT NULL DEFAULT '',
12
+ created_at timestamptz DEFAULT now(),
13
+ updated_at timestamptz DEFAULT now()
14
+ );
15
+
16
+ CREATE INDEX IF NOT EXISTS idx_conversations_entity_agent
17
+ ON conversations (entity_id, agent_id, updated_at);
18
+
19
+ ALTER TABLE chat_messages
20
+ ADD COLUMN IF NOT EXISTS conversation_id uuid REFERENCES conversations(id) ON DELETE CASCADE;
21
+
22
+ -- thread_id is superseded by conversation_id. Existing dev rows (if any) had
23
+ -- thread_id='main' and get a NULL conversation_id (orphaned, not shown).
24
+ ALTER TABLE chat_messages DROP COLUMN IF EXISTS thread_id;
25
+
26
+ CREATE INDEX IF NOT EXISTS idx_chat_messages_conversation
27
+ ON chat_messages (conversation_id, created_at);
@@ -0,0 +1,33 @@
1
+ -- 0029_backfill_root_agent.sql
2
+ -- Brique F — "ROOT = origin orchestrator" (2026-06-01).
3
+ --
4
+ -- Designate a ROOT for any entity that has at least one orchestrator but no
5
+ -- root_agent_id yet: the earliest-created orchestrator wins. Additive only —
6
+ -- entities that already have a ROOT (and its configured grants) are left
7
+ -- untouched, so a manually-set ROOT and its powers are preserved.
8
+ --
9
+ -- Going forward, createAgentRepo auto-designates the first orchestrator of an
10
+ -- entity as ROOT and forces subsequent ones under it. Grants start all-off
11
+ -- (opt-in powers in Settings → ROOT agent); writing them explicitly matters
12
+ -- because a null root_grants parses to all-on (un-gated meta-tools).
13
+ --
14
+ -- Idempotent: the `root_agent_id IS NULL` guard makes re-runs a no-op.
15
+
16
+ UPDATE entities e
17
+ SET
18
+ root_agent_id = sub.first_orch,
19
+ root_grants = '{"createAgent":false,"createSkill":false,"updateSkill":false,"assignSkill":false,"createMcp":false,"autonomy":"propose_confirm"}'::jsonb,
20
+ updated_at = now()
21
+ FROM (
22
+ SELECT a.entity_id, a.id AS first_orch
23
+ FROM agents a
24
+ WHERE a.role = 'orchestrator'
25
+ AND NOT EXISTS (
26
+ SELECT 1 FROM agents a2
27
+ WHERE a2.entity_id = a.entity_id
28
+ AND a2.role = 'orchestrator'
29
+ AND (a2.created_at < a.created_at OR (a2.created_at = a.created_at AND a2.id < a.id))
30
+ )
31
+ ) AS sub
32
+ WHERE e.id = sub.entity_id
33
+ AND e.root_agent_id IS NULL;
@@ -1,174 +1,216 @@
1
- {
2
- "version": "7",
3
- "dialect": "postgresql",
4
- "entries": [
5
- {
6
- "idx": 0,
7
- "version": "7",
8
- "when": 1777221376983,
9
- "tag": "0000_flashy_clea",
10
- "breakpoints": true
11
- },
12
- {
13
- "idx": 1,
14
- "version": "7",
15
- "when": 1777222639218,
16
- "tag": "0001_exotic_shooting_star",
17
- "breakpoints": true
18
- },
19
- {
20
- "idx": 2,
21
- "version": "7",
22
- "when": 1777222668100,
23
- "tag": "0002_gray_devos",
24
- "breakpoints": true
25
- },
26
- {
27
- "idx": 3,
28
- "version": "7",
29
- "when": 1778116559502,
30
- "tag": "0003_stormy_thaddeus_ross",
31
- "breakpoints": true
32
- },
33
- {
34
- "idx": 4,
35
- "version": "7",
36
- "when": 1778141889404,
37
- "tag": "0004_married_deadpool",
38
- "breakpoints": true
39
- },
40
- {
41
- "idx": 5,
42
- "version": "7",
43
- "when": 1778237574443,
44
- "tag": "0005_tense_hedge_knight",
45
- "breakpoints": true
46
- },
47
- {
48
- "idx": 6,
49
- "version": "7",
50
- "when": 1778341607214,
51
- "tag": "0006_credentials_table",
52
- "breakpoints": true
53
- },
54
- {
55
- "idx": 7,
56
- "version": "7",
57
- "when": 1778421836079,
58
- "tag": "0007_free_speed",
59
- "breakpoints": true
60
- },
61
- {
62
- "idx": 8,
63
- "version": "7",
64
- "when": 1778459305708,
65
- "tag": "0008_curious_thor_girl",
66
- "breakpoints": true
67
- },
68
- {
69
- "idx": 9,
70
- "version": "7",
71
- "when": 1778860776473,
72
- "tag": "0009_agent_memory_token_budget",
73
- "breakpoints": true
74
- },
75
- {
76
- "idx": 10,
77
- "version": "7",
78
- "when": 1778940762250,
79
- "tag": "0010_agent_workspace_root",
80
- "breakpoints": true
81
- },
82
- {
83
- "idx": 11,
84
- "version": "7",
85
- "when": 1779000000000,
86
- "tag": "0011_agent_jobs_failed_delegations_count",
87
- "breakpoints": true
88
- },
89
- {
90
- "idx": 12,
91
- "version": "7",
92
- "when": 1779100000000,
93
- "tag": "0012_agent_jobs_last_failed_delegation_slug",
94
- "breakpoints": true
95
- },
96
- {
97
- "idx": 13,
98
- "version": "7",
99
- "when": 1779200000000,
100
- "tag": "0013_cascade_agent_fks",
101
- "breakpoints": true
102
- },
103
- {
104
- "idx": 14,
105
- "version": "7",
106
- "when": 1779400000000,
107
- "tag": "0014_mcp_server_credentials",
108
- "breakpoints": true
109
- },
110
- {
111
- "idx": 15,
112
- "version": "7",
113
- "when": 1779500000000,
114
- "tag": "0015_agent_mcp_servers_unique",
115
- "breakpoints": true
116
- },
117
- {
118
- "idx": 16,
119
- "version": "7",
120
- "when": 1779600000000,
121
- "tag": "0016_drop_connector_entity_slug_unique",
122
- "breakpoints": true
123
- },
124
- {
125
- "idx": 17,
126
- "version": "7",
127
- "when": 1779700000000,
128
- "tag": "0017_drop_mcp_server_entity_slug_unique",
129
- "breakpoints": true
130
- },
131
- {
132
- "idx": 18,
133
- "version": "7",
134
- "when": 1779800000000,
135
- "tag": "0018_mcp_servers_bearer_auth",
136
- "breakpoints": true
137
- },
138
- {
139
- "idx": 19,
140
- "version": "7",
141
- "when": 1779900000000,
142
- "tag": "0019_agents_position",
143
- "breakpoints": true
144
- },
145
- {
146
- "idx": 20,
147
- "version": "7",
148
- "when": 1780000000000,
149
- "tag": "0020_agent_workspaces",
150
- "breakpoints": true
151
- },
152
- {
153
- "idx": 21,
154
- "version": "7",
155
- "when": 1780100000000,
156
- "tag": "0021_entities_root_agent_grants",
157
- "breakpoints": true
158
- },
159
- {
160
- "idx": 22,
161
- "version": "7",
162
- "when": 1780200000000,
163
- "tag": "0022_approval_requests_executed_at",
164
- "breakpoints": true
165
- },
166
- {
167
- "idx": 23,
168
- "version": "7",
169
- "when": 1780300000000,
170
- "tag": "0023_agent_schedules_notify_on_success",
171
- "breakpoints": true
172
- }
173
- ]
174
- }
1
+ {
2
+ "version": "7",
3
+ "dialect": "postgresql",
4
+ "entries": [
5
+ {
6
+ "idx": 0,
7
+ "version": "7",
8
+ "when": 1777221376983,
9
+ "tag": "0000_flashy_clea",
10
+ "breakpoints": true
11
+ },
12
+ {
13
+ "idx": 1,
14
+ "version": "7",
15
+ "when": 1777222639218,
16
+ "tag": "0001_exotic_shooting_star",
17
+ "breakpoints": true
18
+ },
19
+ {
20
+ "idx": 2,
21
+ "version": "7",
22
+ "when": 1777222668100,
23
+ "tag": "0002_gray_devos",
24
+ "breakpoints": true
25
+ },
26
+ {
27
+ "idx": 3,
28
+ "version": "7",
29
+ "when": 1778116559502,
30
+ "tag": "0003_stormy_thaddeus_ross",
31
+ "breakpoints": true
32
+ },
33
+ {
34
+ "idx": 4,
35
+ "version": "7",
36
+ "when": 1778141889404,
37
+ "tag": "0004_married_deadpool",
38
+ "breakpoints": true
39
+ },
40
+ {
41
+ "idx": 5,
42
+ "version": "7",
43
+ "when": 1778237574443,
44
+ "tag": "0005_tense_hedge_knight",
45
+ "breakpoints": true
46
+ },
47
+ {
48
+ "idx": 6,
49
+ "version": "7",
50
+ "when": 1778341607214,
51
+ "tag": "0006_credentials_table",
52
+ "breakpoints": true
53
+ },
54
+ {
55
+ "idx": 7,
56
+ "version": "7",
57
+ "when": 1778421836079,
58
+ "tag": "0007_free_speed",
59
+ "breakpoints": true
60
+ },
61
+ {
62
+ "idx": 8,
63
+ "version": "7",
64
+ "when": 1778459305708,
65
+ "tag": "0008_curious_thor_girl",
66
+ "breakpoints": true
67
+ },
68
+ {
69
+ "idx": 9,
70
+ "version": "7",
71
+ "when": 1778860776473,
72
+ "tag": "0009_agent_memory_token_budget",
73
+ "breakpoints": true
74
+ },
75
+ {
76
+ "idx": 10,
77
+ "version": "7",
78
+ "when": 1778940762250,
79
+ "tag": "0010_agent_workspace_root",
80
+ "breakpoints": true
81
+ },
82
+ {
83
+ "idx": 11,
84
+ "version": "7",
85
+ "when": 1779000000000,
86
+ "tag": "0011_agent_jobs_failed_delegations_count",
87
+ "breakpoints": true
88
+ },
89
+ {
90
+ "idx": 12,
91
+ "version": "7",
92
+ "when": 1779100000000,
93
+ "tag": "0012_agent_jobs_last_failed_delegation_slug",
94
+ "breakpoints": true
95
+ },
96
+ {
97
+ "idx": 13,
98
+ "version": "7",
99
+ "when": 1779200000000,
100
+ "tag": "0013_cascade_agent_fks",
101
+ "breakpoints": true
102
+ },
103
+ {
104
+ "idx": 14,
105
+ "version": "7",
106
+ "when": 1779400000000,
107
+ "tag": "0014_mcp_server_credentials",
108
+ "breakpoints": true
109
+ },
110
+ {
111
+ "idx": 15,
112
+ "version": "7",
113
+ "when": 1779500000000,
114
+ "tag": "0015_agent_mcp_servers_unique",
115
+ "breakpoints": true
116
+ },
117
+ {
118
+ "idx": 16,
119
+ "version": "7",
120
+ "when": 1779600000000,
121
+ "tag": "0016_drop_connector_entity_slug_unique",
122
+ "breakpoints": true
123
+ },
124
+ {
125
+ "idx": 17,
126
+ "version": "7",
127
+ "when": 1779700000000,
128
+ "tag": "0017_drop_mcp_server_entity_slug_unique",
129
+ "breakpoints": true
130
+ },
131
+ {
132
+ "idx": 18,
133
+ "version": "7",
134
+ "when": 1779800000000,
135
+ "tag": "0018_mcp_servers_bearer_auth",
136
+ "breakpoints": true
137
+ },
138
+ {
139
+ "idx": 19,
140
+ "version": "7",
141
+ "when": 1779900000000,
142
+ "tag": "0019_agents_position",
143
+ "breakpoints": true
144
+ },
145
+ {
146
+ "idx": 20,
147
+ "version": "7",
148
+ "when": 1780000000000,
149
+ "tag": "0020_agent_workspaces",
150
+ "breakpoints": true
151
+ },
152
+ {
153
+ "idx": 21,
154
+ "version": "7",
155
+ "when": 1780100000000,
156
+ "tag": "0021_entities_root_agent_grants",
157
+ "breakpoints": true
158
+ },
159
+ {
160
+ "idx": 22,
161
+ "version": "7",
162
+ "when": 1780200000000,
163
+ "tag": "0022_approval_requests_executed_at",
164
+ "breakpoints": true
165
+ },
166
+ {
167
+ "idx": 23,
168
+ "version": "7",
169
+ "when": 1780300000000,
170
+ "tag": "0023_agent_schedules_notify_on_success",
171
+ "breakpoints": true
172
+ },
173
+ {
174
+ "idx": 24,
175
+ "version": "7",
176
+ "when": 1780400000000,
177
+ "tag": "0024_agent_jobs_dashboard_channel",
178
+ "breakpoints": true
179
+ },
180
+ {
181
+ "idx": 25,
182
+ "version": "7",
183
+ "when": 1780500000000,
184
+ "tag": "0025_agent_jobs_conversational",
185
+ "breakpoints": true
186
+ },
187
+ {
188
+ "idx": 26,
189
+ "version": "7",
190
+ "when": 1780600000000,
191
+ "tag": "0026_chat_messages",
192
+ "breakpoints": true
193
+ },
194
+ {
195
+ "idx": 27,
196
+ "version": "7",
197
+ "when": 1780700000000,
198
+ "tag": "0027_drop_agent_jobs_conversational",
199
+ "breakpoints": true
200
+ },
201
+ {
202
+ "idx": 28,
203
+ "version": "7",
204
+ "when": 1780800000000,
205
+ "tag": "0028_conversations",
206
+ "breakpoints": true
207
+ },
208
+ {
209
+ "idx": 29,
210
+ "version": "7",
211
+ "when": 1780900000000,
212
+ "tag": "0029_backfill_root_agent",
213
+ "breakpoints": true
214
+ }
215
+ ]
216
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodal-agents",
3
- "version": "0.3.10",
3
+ "version": "0.4.0",
4
4
  "description": "Local-first AI agent platform with a web dashboard — install in one command.",
5
5
  "license": "MIT",
6
6
  "repository": {