nodal-agents 0.6.8 → 0.7.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.
- package/cli.js +359 -35
- package/migrations/0051_memory_search_fts.sql +2 -0
- package/migrations/0052_entities_memory_curation.sql +1 -0
- package/migrations/0053_memory_importance_locked.sql +1 -0
- package/migrations/0054_audit2_db_integrity.sql +52 -0
- package/migrations/0055_entity_settings.sql +22 -0
- package/migrations/0056_agents_skills_entity_scoped_unique.sql +23 -0
- package/migrations/0057_telegram_allowed_chats.sql +34 -0
- package/migrations/meta/_journal.json +49 -0
- package/package.json +1 -1
- package/runner.js +47144 -35235
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/app-path-routes-manifest.json +3 -3
- package/web/.next/build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/server/app/(dashboard)/agents/[id]/edit/page.js +3 -3
- package/web/.next/server/app/(dashboard)/agents/[id]/edit/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/agents/[id]/edit/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page.js +2 -2
- package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/agents/page.js +2 -2
- package/web/.next/server/app/(dashboard)/agents/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/agents/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/approvals/page.js +2 -2
- package/web/.next/server/app/(dashboard)/approvals/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/approvals/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/automations/page.js +2 -2
- package/web/.next/server/app/(dashboard)/automations/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/automations/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/chat/page.js +2 -2
- package/web/.next/server/app/(dashboard)/chat/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/chat/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/connectors/page.js +2 -2
- package/web/.next/server/app/(dashboard)/connectors/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/connectors/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/credentials/page.js +1 -1
- package/web/.next/server/app/(dashboard)/credentials/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/credentials/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/jobs/[id]/page.js +2 -2
- package/web/.next/server/app/(dashboard)/jobs/[id]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/jobs/[id]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/jobs/page.js +2 -2
- package/web/.next/server/app/(dashboard)/jobs/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/jobs/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/learned-skills/page.js +2 -2
- package/web/.next/server/app/(dashboard)/learned-skills/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/learned-skills/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/llm-providers/page.js +2 -2
- package/web/.next/server/app/(dashboard)/llm-providers/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/llm-providers/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/logs/page.js +2 -2
- package/web/.next/server/app/(dashboard)/logs/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/logs/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/mcp/page.js +2 -2
- package/web/.next/server/app/(dashboard)/mcp/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/mcp/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/memories/page.js +2 -2
- package/web/.next/server/app/(dashboard)/memories/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/memories/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/page.js +3 -3
- package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/settings/page.js +2 -2
- package/web/.next/server/app/(dashboard)/settings/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/settings/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/settings/root-context/page.js +2 -2
- package/web/.next/server/app/(dashboard)/settings/root-context/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/settings/root-context/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/skills/[id]/edit/page.js +2 -2
- package/web/.next/server/app/(dashboard)/skills/[id]/edit/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/skills/[id]/edit/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/skills/new/page.js +2 -2
- package/web/.next/server/app/(dashboard)/skills/new/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/skills/new/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/skills/page.js +2 -2
- package/web/.next/server/app/(dashboard)/skills/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_global-error.html +1 -1
- package/web/.next/server/app/_global-error.rsc +2 -2
- package/web/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
- package/web/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_index.segment.rsc +2 -2
- package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_not-found.html +1 -1
- package/web/.next/server/app/_not-found.rsc +3 -3
- package/web/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
- package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/web/.next/server/app/api/auth/[...all]/route.js +1 -1
- package/web/.next/server/app/api/auth/[...all]/route.js.nft.json +1 -1
- package/web/.next/server/app/api/oauth/[provider]/callback/route.js +1 -1
- package/web/.next/server/app/api/oauth/[provider]/callback/route.js.nft.json +1 -1
- package/web/.next/server/app/api/oauth/[provider]/start/route.js +1 -1
- package/web/.next/server/app/api/oauth/[provider]/start/route.js.nft.json +1 -1
- package/web/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/onboarding/page.js +214 -190
- package/web/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app-paths-manifest.json +3 -3
- package/web/.next/server/chunks/2713.js +47 -23
- package/web/.next/server/chunks/3223.js +1 -1
- package/web/.next/server/chunks/4574.js +1 -1
- package/web/.next/server/chunks/5515.js +1 -0
- package/web/.next/server/chunks/6468.js +1 -0
- package/web/.next/server/chunks/8410.js +1 -0
- package/web/.next/server/chunks/{4488.js → 8810.js} +2 -2
- package/web/.next/server/chunks/{3154.js → 960.js} +10 -10
- package/web/.next/server/middleware-build-manifest.js +1 -1
- package/web/.next/server/pages/404.html +1 -1
- package/web/.next/server/pages/500.html +1 -1
- package/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/server/server-reference-manifest.json +1 -1
- package/web/.next/static/chunks/4964-c6711d3d479b6085.js +1 -0
- package/web/.next/static/chunks/{9836-6a7e70d2572d9671.js → 9836-177f6ca2f15ff703.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/agents/[id]/edit/page-cfce874f4acede5e.js +6 -0
- package/web/.next/static/chunks/app/(dashboard)/agents/[id]/telegram/page-9249c2839f551ba1.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/agents/page-7d784d737acedccf.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/approvals/{page-6435e07096efe1ef.js → page-b3a838ecbc94a030.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/automations/page-b1b548de4f52696c.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/chat/page-bcf6dcc73b370eb0.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/connectors/page-2c6f913cc6aea4b7.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/jobs/[id]/page-43f033251eddcc4b.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/jobs/page-1f892f950bb30403.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/layout-e184673151ff8a05.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/learned-skills/page-f873bb6f8539e47e.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/llm-providers/page-64adda30176f4ccb.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/logs/{page-f9efb4dd80bf104a.js → page-79cfd9141c39d8ef.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/mcp/page-d276d7bab25286a1.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/memories/page-c66720494a340503.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/page-3649204dd27a0ad0.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/settings/page-1cab404d2eccfd80.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/settings/root-context/page-b5898c5444f88c9a.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/skills/[id]/edit/{page-d20237d23a440118.js → page-82d91fc6b6acc161.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/skills/new/{page-8454b37bed789c4e.js → page-58b5ed04583ca7d4.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/skills/page-312bd4ae8ddc8114.js +1 -0
- package/web/.next/static/chunks/app/onboarding/page-f2f04878e81fdf23.js +26 -0
- package/web/.next/static/css/4d808b91d7b58fce.css +3 -0
- package/web/.next/server/chunks/2053.js +0 -1
- package/web/.next/server/chunks/5290.js +0 -1
- package/web/.next/server/chunks/9282.js +0 -1
- package/web/.next/static/chunks/4964-09a4dc14acc324f3.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/agents/[id]/edit/page-1172f97f44c6b953.js +0 -6
- package/web/.next/static/chunks/app/(dashboard)/agents/[id]/telegram/page-a858be2a0b9d5d42.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/agents/page-637555c8b299e4d7.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/automations/page-416f63bbd11d1a93.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/chat/page-53507d81c2ceee4c.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/connectors/page-f6a4d52ef7ea8c7f.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/jobs/[id]/page-acb7d1383384f27b.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/jobs/page-b82e456c240416ad.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/layout-d0662737b23eaebb.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/learned-skills/page-3c67b5a3869a9224.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/llm-providers/page-294135b03955dd45.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/mcp/page-482dc02cf953c197.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/memories/page-ce432008a8a4c118.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/page-d76a7350bf5bea10.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/settings/page-80cb948b3c0cef7d.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/settings/root-context/page-8e7dabbb399e369d.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/skills/page-af8d329facf31c8f.js +0 -1
- package/web/.next/static/chunks/app/onboarding/page-3543225eab5f4029.js +0 -26
- package/web/.next/static/css/b06a60c5d9b30bc8.css +0 -3
- /package/web/.next/static/{RORb5LJdSgtd7i6KVr0zl → SgnP_MouhUmw0bBf-lUmD}/_buildManifest.js +0 -0
- /package/web/.next/static/{RORb5LJdSgtd7i6KVr0zl → SgnP_MouhUmw0bBf-lUmD}/_ssgManifest.js +0 -0
package/cli.js
CHANGED
|
@@ -13,7 +13,8 @@ var __export = (target, all) => {
|
|
|
13
13
|
var version_exports = {};
|
|
14
14
|
__export(version_exports, {
|
|
15
15
|
getInstalledVersion: () => getInstalledVersion,
|
|
16
|
-
getLatestVersion: () => getLatestVersion
|
|
16
|
+
getLatestVersion: () => getLatestVersion,
|
|
17
|
+
isNewerVersion: () => isNewerVersion
|
|
17
18
|
});
|
|
18
19
|
import { createRequire } from "module";
|
|
19
20
|
import { fileURLToPath } from "url";
|
|
@@ -49,6 +50,20 @@ async function getLatestVersion() {
|
|
|
49
50
|
return null;
|
|
50
51
|
}
|
|
51
52
|
}
|
|
53
|
+
function parseVersionTriple(v) {
|
|
54
|
+
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(v);
|
|
55
|
+
if (!match) return null;
|
|
56
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
57
|
+
}
|
|
58
|
+
function isNewerVersion(candidate, current) {
|
|
59
|
+
const a = parseVersionTriple(candidate);
|
|
60
|
+
const b2 = parseVersionTriple(current);
|
|
61
|
+
if (!a || !b2) return false;
|
|
62
|
+
for (let i = 0; i < 3; i++) {
|
|
63
|
+
if (a[i] !== b2[i]) return a[i] > b2[i];
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
52
67
|
var init_version = __esm({
|
|
53
68
|
"src/lib/version.ts"() {
|
|
54
69
|
"use strict";
|
|
@@ -59,7 +74,7 @@ var init_version = __esm({
|
|
|
59
74
|
import { z } from "zod";
|
|
60
75
|
import { homedir } from "os";
|
|
61
76
|
import { join as join2 } from "path";
|
|
62
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync as existsSync2 } from "fs";
|
|
77
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync as existsSync2, chmodSync } from "fs";
|
|
63
78
|
import { randomBytes } from "crypto";
|
|
64
79
|
function ensureConfigDir() {
|
|
65
80
|
for (const dir of [CONFIG_DIR, PID_DIR, LOG_DIR, PG_DATA_DIR]) {
|
|
@@ -81,11 +96,21 @@ function readConfig(configFile = CONFIG_FILE) {
|
|
|
81
96
|
parsed = { ...parsed, serverActionsKey: randomBytes(32).toString("base64") };
|
|
82
97
|
writeConfig(parsed, configFile);
|
|
83
98
|
}
|
|
99
|
+
if (!parsed.authSecret) {
|
|
100
|
+
parsed = { ...parsed, authSecret: randomBytes(32).toString("base64") };
|
|
101
|
+
writeConfig(parsed, configFile);
|
|
102
|
+
}
|
|
103
|
+
if (configFile === CONFIG_FILE) {
|
|
104
|
+
try {
|
|
105
|
+
chmodSync(configFile, 384);
|
|
106
|
+
} catch {
|
|
107
|
+
}
|
|
108
|
+
}
|
|
84
109
|
return parsed;
|
|
85
110
|
}
|
|
86
111
|
function writeConfig(config, configFile = CONFIG_FILE) {
|
|
87
112
|
if (configFile === CONFIG_FILE) ensureConfigDir();
|
|
88
|
-
writeFileSync(configFile, JSON.stringify(config, null, 2), "utf-8");
|
|
113
|
+
writeFileSync(configFile, JSON.stringify(config, null, 2), { encoding: "utf-8", mode: 384 });
|
|
89
114
|
}
|
|
90
115
|
var ConfigSchema, CONFIG_DIR, CONFIG_FILE, PID_DIR, LOG_DIR, PG_DATA_DIR;
|
|
91
116
|
var init_config = __esm({
|
|
@@ -133,6 +158,18 @@ var init_config = __esm({
|
|
|
133
158
|
* files; `readConfig` auto-mints + persists when absent.
|
|
134
159
|
*/
|
|
135
160
|
serverActionsKey: z.string().length(44).optional(),
|
|
161
|
+
/**
|
|
162
|
+
* Base64-encoded 32-byte AES key used as better-auth's AUTH_SECRET (session
|
|
163
|
+
* cookie signing) for the web process. Deliberately SEPARATE from
|
|
164
|
+
* `workerSecret` (M-4): `workerSecret` is the runner's auth frontier — a
|
|
165
|
+
* leak of it must not also let an attacker forge a signed session cookie.
|
|
166
|
+
*
|
|
167
|
+
* Optional in the schema for back-compat with pre-existing config.json
|
|
168
|
+
* files; `readConfig` auto-mints + persists when absent. On an existing
|
|
169
|
+
* install this invalidates any active better-auth sessions ONCE (users
|
|
170
|
+
* re-login) — an acceptable one-time cost for closing the amplification.
|
|
171
|
+
*/
|
|
172
|
+
authSecret: z.string().length(44).optional(),
|
|
136
173
|
bind: z.enum(["loopback", "lan"]).default("loopback"),
|
|
137
174
|
bearerToken: z.string().optional(),
|
|
138
175
|
/**
|
|
@@ -6037,6 +6074,12 @@ var init_policies = __esm({
|
|
|
6037
6074
|
});
|
|
6038
6075
|
|
|
6039
6076
|
// ../../node_modules/.pnpm/drizzle-orm@0.45.2_@electri_96bd1661c4535b1384d37581a38c7c74/node_modules/drizzle-orm/pg-core/primary-keys.js
|
|
6077
|
+
function primaryKey(...config) {
|
|
6078
|
+
if (config[0].columns) {
|
|
6079
|
+
return new PrimaryKeyBuilder(config[0].columns, config[0].name);
|
|
6080
|
+
}
|
|
6081
|
+
return new PrimaryKeyBuilder(config);
|
|
6082
|
+
}
|
|
6040
6083
|
var PrimaryKeyBuilder, PrimaryKey;
|
|
6041
6084
|
var init_primary_keys = __esm({
|
|
6042
6085
|
"../../node_modules/.pnpm/drizzle-orm@0.45.2_@electri_96bd1661c4535b1384d37581a38c7c74/node_modules/drizzle-orm/pg-core/primary-keys.js"() {
|
|
@@ -6400,19 +6443,19 @@ function extractTablesRelationalConfig(schema, configHelpers) {
|
|
|
6400
6443
|
const relations2 = value.config(
|
|
6401
6444
|
configHelpers(value.table)
|
|
6402
6445
|
);
|
|
6403
|
-
let
|
|
6446
|
+
let primaryKey2;
|
|
6404
6447
|
for (const [relationName, relation] of Object.entries(relations2)) {
|
|
6405
6448
|
if (tableName) {
|
|
6406
6449
|
const tableConfig = tablesConfig[tableName];
|
|
6407
6450
|
tableConfig.relations[relationName] = relation;
|
|
6408
|
-
if (
|
|
6409
|
-
tableConfig.primaryKey.push(...
|
|
6451
|
+
if (primaryKey2) {
|
|
6452
|
+
tableConfig.primaryKey.push(...primaryKey2);
|
|
6410
6453
|
}
|
|
6411
6454
|
} else {
|
|
6412
6455
|
if (!(dbName in relationsBuffer)) {
|
|
6413
6456
|
relationsBuffer[dbName] = {
|
|
6414
6457
|
relations: {},
|
|
6415
|
-
primaryKey
|
|
6458
|
+
primaryKey: primaryKey2
|
|
6416
6459
|
};
|
|
6417
6460
|
}
|
|
6418
6461
|
relationsBuffer[dbName].relations[relationName] = relation;
|
|
@@ -11511,6 +11554,10 @@ var init_memory = __esm({
|
|
|
11511
11554
|
valid_to: z13.string().datetime().nullable(),
|
|
11512
11555
|
fact_hash: z13.string().nullable(),
|
|
11513
11556
|
archived: z13.boolean(),
|
|
11557
|
+
// User override lock (migration 0053) — when true, the curator refuses to
|
|
11558
|
+
// re-score importance for this fact. Defaulted so rows/objects predating
|
|
11559
|
+
// the column still parse.
|
|
11560
|
+
importance_locked: z13.boolean().default(false),
|
|
11514
11561
|
last_accessed_at: z13.string().datetime().nullable(),
|
|
11515
11562
|
access_count: z13.number().int().min(0),
|
|
11516
11563
|
created_at: z13.string().datetime(),
|
|
@@ -11522,6 +11569,7 @@ var init_memory = __esm({
|
|
|
11522
11569
|
updated_at: true,
|
|
11523
11570
|
fact_hash: true,
|
|
11524
11571
|
archived: true,
|
|
11572
|
+
importance_locked: true,
|
|
11525
11573
|
last_accessed_at: true,
|
|
11526
11574
|
access_count: true,
|
|
11527
11575
|
valid_from: true,
|
|
@@ -11878,6 +11926,13 @@ var init_root_agent = __esm({
|
|
|
11878
11926
|
}
|
|
11879
11927
|
});
|
|
11880
11928
|
|
|
11929
|
+
// ../../packages/shared/src/system-prompt-cache.ts
|
|
11930
|
+
var init_system_prompt_cache = __esm({
|
|
11931
|
+
"../../packages/shared/src/system-prompt-cache.ts"() {
|
|
11932
|
+
"use strict";
|
|
11933
|
+
}
|
|
11934
|
+
});
|
|
11935
|
+
|
|
11881
11936
|
// ../../packages/shared/src/connector-catalog.ts
|
|
11882
11937
|
var init_connector_catalog = __esm({
|
|
11883
11938
|
"../../packages/shared/src/connector-catalog.ts"() {
|
|
@@ -11909,7 +11964,10 @@ var init_model_catalog = __esm({
|
|
|
11909
11964
|
// deepseek-chat is the production alias for DeepSeek V3. Standard tool
|
|
11910
11965
|
// calling with forced tool_choice supported.
|
|
11911
11966
|
capabilities: { tools: true, forcedToolChoice: true },
|
|
11912
|
-
contextWindow: 128e3
|
|
11967
|
+
contextWindow: 128e3,
|
|
11968
|
+
// Standard (cache-miss) rate per DeepSeek's public pricing page —
|
|
11969
|
+
// verify against api-docs.deepseek.com/quick_start/pricing if this drifts.
|
|
11970
|
+
pricing: { inputPerMillionUsd: 0.27, outputPerMillionUsd: 1.1 }
|
|
11913
11971
|
},
|
|
11914
11972
|
{
|
|
11915
11973
|
modelId: "deepseek-reasoner",
|
|
@@ -11919,7 +11977,8 @@ var init_model_catalog = __esm({
|
|
|
11919
11977
|
// reasoning:true). Also requires reasoning_content to be echoed back on
|
|
11920
11978
|
// assistant messages with tool_calls (the deepseek fetch shim handles this).
|
|
11921
11979
|
capabilities: { tools: true, forcedToolChoice: true, reasoning: true },
|
|
11922
|
-
contextWindow: 128e3
|
|
11980
|
+
contextWindow: 128e3,
|
|
11981
|
+
pricing: { inputPerMillionUsd: 0.55, outputPerMillionUsd: 2.19 }
|
|
11923
11982
|
}
|
|
11924
11983
|
],
|
|
11925
11984
|
// ─── Native MiniMax (api.minimax.io/anthropic) ───────────────────────────────
|
|
@@ -11932,6 +11991,11 @@ var init_model_catalog = __esm({
|
|
|
11932
11991
|
// forced tool_choice with a 400/404 (observed on M3 via OpenRouter); the
|
|
11933
11992
|
// runtime completion floor covers it. Context windows are docs-sourced where
|
|
11934
11993
|
// available, else a conservative 200K until a live probe confirms.
|
|
11994
|
+
// No `pricing` yet (unlike deepseek above) — no confidently-current MiniMax
|
|
11995
|
+
// rate card verified at write time. Left as documented debt (Fix #21): the
|
|
11996
|
+
// derived-cost fallback returns 0 for these until pricing is added, same as
|
|
11997
|
+
// every other unpriced entry in this catalog; Guard 1a (token budget) is the
|
|
11998
|
+
// backstop meanwhile.
|
|
11935
11999
|
minimax: [
|
|
11936
12000
|
{
|
|
11937
12001
|
modelId: "MiniMax-M3",
|
|
@@ -12215,6 +12279,7 @@ var init_src2 = __esm({
|
|
|
12215
12279
|
init_operation();
|
|
12216
12280
|
init_providers();
|
|
12217
12281
|
init_root_agent();
|
|
12282
|
+
init_system_prompt_cache();
|
|
12218
12283
|
init_connector_catalog();
|
|
12219
12284
|
init_community_skill_catalog();
|
|
12220
12285
|
init_model_catalog();
|
|
@@ -12330,6 +12395,10 @@ var init_entities = __esm({
|
|
|
12330
12395
|
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(),
|
|
12331
12396
|
lastCuratorRunAt: timestamp("last_curator_run_at", { withTimezone: true }),
|
|
12332
12397
|
reflectionEnabled: boolean("reflection_enabled").notNull().default(false),
|
|
12398
|
+
// Decoupled from reflection_enabled (which gates the skill-learning loop,
|
|
12399
|
+
// opt-in). Gates the memory curator (Phase 2 LLM pass) — ON by default so
|
|
12400
|
+
// memory curation runs even for entities that never opted into reflection.
|
|
12401
|
+
memoryCurationEnabled: boolean("memory_curation_enabled").notNull().default(true),
|
|
12333
12402
|
// Controls whether agent-authored skills are auto-assigned to the authoring
|
|
12334
12403
|
// agent ('auto') or queued for the entity owner to approve ('approval').
|
|
12335
12404
|
skillAssignmentMode: text("skill_assignment_mode").notNull().default("approval").$type(),
|
|
@@ -12361,7 +12430,13 @@ var init_entities = __esm({
|
|
|
12361
12430
|
check(
|
|
12362
12431
|
"entity_members_role_check",
|
|
12363
12432
|
sql`${table.role} IN ('owner', 'admin', 'member', 'viewer')`
|
|
12364
|
-
)
|
|
12433
|
+
),
|
|
12434
|
+
// R5 (audit #2 follow-up, found while sweeping helpers.ts for the SAME
|
|
12435
|
+
// class of phantom constraint): the pglite test DDL already assumed a
|
|
12436
|
+
// user can only be a member of an entity once — this table had no such
|
|
12437
|
+
// constraint in the real schema/migrations. Both columns are NOT NULL, so
|
|
12438
|
+
// a plain UNIQUE is correct (no NULLS NOT DISTINCT needed).
|
|
12439
|
+
uniqueIndex("entity_members_entity_user_unique").on(table.entityId, table.userId)
|
|
12365
12440
|
]
|
|
12366
12441
|
);
|
|
12367
12442
|
}
|
|
@@ -12413,7 +12488,7 @@ var init_agents = __esm({
|
|
|
12413
12488
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
12414
12489
|
entityId: uuid("entity_id").references(() => entities.id, { onDelete: "cascade" }),
|
|
12415
12490
|
name: text("name").notNull(),
|
|
12416
|
-
slug: text("slug").notNull()
|
|
12491
|
+
slug: text("slug").notNull(),
|
|
12417
12492
|
personality: text("personality").notNull(),
|
|
12418
12493
|
model: text("model").default("claude-sonnet-4-6-20260217"),
|
|
12419
12494
|
llmKeyId: uuid("llm_key_id").references(() => entityLlmKeys.id, { onDelete: "set null" }),
|
|
@@ -12463,7 +12538,17 @@ var init_agents = __esm({
|
|
|
12463
12538
|
"agents_orchestrator_mode_check",
|
|
12464
12539
|
sql`${table.orchestratorMode} IN ('router', 'planner') OR ${table.orchestratorMode} IS NULL`
|
|
12465
12540
|
),
|
|
12466
|
-
check("agents_max_tokens_per_job_check", sql`${table.maxTokensPerJob} >= 0`)
|
|
12541
|
+
check("agents_max_tokens_per_job_check", sql`${table.maxTokensPerJob} >= 0`),
|
|
12542
|
+
// F-6 (audit #2): slug was UNIQUE GLOBALLY, so a 2nd workspace/entity
|
|
12543
|
+
// installing the same community skill's companion agent (or any agent
|
|
12544
|
+
// sharing a slug with another entity's agent) would crash the insert —
|
|
12545
|
+
// and, in multi-user mode, let one entity enumerate/squat another's
|
|
12546
|
+
// slugs. Scoped to (entity_id, slug): every real insert always sets
|
|
12547
|
+
// entityId (createAgentRepo takes it as a required param — no production
|
|
12548
|
+
// path leaves it NULL), but the column itself is nullable, so
|
|
12549
|
+
// NULLS NOT DISTINCT closes the same NULL-entity gap as DB-1/agent_plugins
|
|
12550
|
+
// (multiple (NULL, 'x') rows would otherwise all satisfy a plain UNIQUE).
|
|
12551
|
+
unique("agents_entity_slug_unique").on(table.entityId, table.slug).nullsNotDistinct()
|
|
12467
12552
|
]
|
|
12468
12553
|
);
|
|
12469
12554
|
agentAssignments = pgTable(
|
|
@@ -12476,7 +12561,18 @@ var init_agents = __esm({
|
|
|
12476
12561
|
instructions: text("instructions"),
|
|
12477
12562
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
12478
12563
|
},
|
|
12479
|
-
() => [
|
|
12564
|
+
(table) => [
|
|
12565
|
+
// F-18 (audit #2): no index beyond the PK meant updateAgentAction's
|
|
12566
|
+
// delete-then-insert (apps/web/src/lib/actions.ts) could race and leave the
|
|
12567
|
+
// same sub-agent attached twice under one orchestrator (duplicated in the
|
|
12568
|
+
// team-block). The unique index's leading column (orchestrator_id) also
|
|
12569
|
+
// serves the hot read path (team-block.ts, assign-tools.ts, detach-agent.ts
|
|
12570
|
+
// all look up by orchestratorId alone) — no separate index needed.
|
|
12571
|
+
unique("agent_assignments_orchestrator_sub_agent_unique").on(
|
|
12572
|
+
table.orchestratorId,
|
|
12573
|
+
table.subAgentId
|
|
12574
|
+
)
|
|
12575
|
+
]
|
|
12480
12576
|
);
|
|
12481
12577
|
agentBudgets = pgTable(
|
|
12482
12578
|
"agent_budgets",
|
|
@@ -12605,8 +12701,19 @@ var init_jobs = __esm({
|
|
|
12605
12701
|
sql`${table.createdAt} DESC`
|
|
12606
12702
|
),
|
|
12607
12703
|
index("idx_agent_jobs_parent_job_id").on(table.parentJobId),
|
|
12608
|
-
|
|
12704
|
+
// idx_jobs_parent (F-20, audit #2) was an exact duplicate of
|
|
12705
|
+
// idx_agent_jobs_parent_job_id above — same column, same order, both from
|
|
12706
|
+
// migration 0000. Dropped in migration 0054; kept this one (matches the
|
|
12707
|
+
// idx_agent_jobs_* naming convention used everywhere else on this table).
|
|
12609
12708
|
index("idx_jobs_status").on(table.status, table.createdAt),
|
|
12709
|
+
// DB-3 (audit #2): findUndeliveredRootJobIds (cron/deliver-results.ts)
|
|
12710
|
+
// joins on this column filtered to IS NULL every tick; the cron recovery
|
|
12711
|
+
// scans (findPendingJobsToRecover, resetOrphanedJobs, failStalePendingJobs
|
|
12712
|
+
// in cron/reset-orphans.ts) also filter by status without an entity_id,
|
|
12713
|
+
// which idx_jobs_status (kept above) still serves — completed rows vastly
|
|
12714
|
+
// outnumber the still-open ones, so a partial index keyed on the open set
|
|
12715
|
+
// stays small regardless of table growth.
|
|
12716
|
+
index("idx_agent_jobs_completed_at_null").on(table.completedAt).where(sql`${table.completedAt} IS NULL`),
|
|
12610
12717
|
check(
|
|
12611
12718
|
"agent_jobs_status_check",
|
|
12612
12719
|
sql`${table.status} IN ('pending','processing','completed','failed','awaiting_approval','awaiting_delegation','cancelled')`
|
|
@@ -12669,6 +12776,10 @@ var init_tasks = __esm({
|
|
|
12669
12776
|
index("idx_agent_tasks_orchestrator_status").on(table.orchestratorId, table.status),
|
|
12670
12777
|
index("idx_agent_tasks_status").on(table.status),
|
|
12671
12778
|
index("idx_agent_tasks_assigned").on(table.assignedAgentId),
|
|
12779
|
+
// DB-3 (audit #2): findUndeliveredRootJobIds (apps/runner/src/cron/
|
|
12780
|
+
// deliver-results.ts) scans `WHERE root_job_id IS NOT NULL` on every cron
|
|
12781
|
+
// tick and had no index to support it — full scan on the hottest task table.
|
|
12782
|
+
index("idx_agent_tasks_root_job_id").on(table.rootJobId),
|
|
12672
12783
|
check(
|
|
12673
12784
|
"agent_tasks_status_check",
|
|
12674
12785
|
sql`${table.status} IN ('todo','in_progress','done','cancelled','blocked')`
|
|
@@ -12853,7 +12964,16 @@ var init_approvals = __esm({
|
|
|
12853
12964
|
check(
|
|
12854
12965
|
"approval_rules_action_check",
|
|
12855
12966
|
sql`${table.action} IN ('auto_approve','require_approval','block')`
|
|
12856
|
-
)
|
|
12967
|
+
),
|
|
12968
|
+
// DB-1 (audit #2): one canonical rule per (entity, agent-or-null, tool) —
|
|
12969
|
+
// without this, two concurrent setAgentApprovalRuleAction calls (or any
|
|
12970
|
+
// direct insert) can leave two rows for the same scope with DIVERGENT
|
|
12971
|
+
// actions, and matchApprovalRule's `.find()` picks whichever the SELECT
|
|
12972
|
+
// happens to return first — a non-deterministic approval gate. agentId IS
|
|
12973
|
+
// NULL marks an entity-wide rule (e.g. the run_command LAN master-switch),
|
|
12974
|
+
// so a plain UNIQUE would treat two NULL rows as distinct and let the
|
|
12975
|
+
// duplicate back in; NULLS NOT DISTINCT (PG15+) closes that gap.
|
|
12976
|
+
unique("approval_rules_entity_agent_tool_unique").on(table.entityId, table.agentId, table.toolName).nullsNotDistinct()
|
|
12857
12977
|
]
|
|
12858
12978
|
);
|
|
12859
12979
|
}
|
|
@@ -12886,6 +13006,15 @@ var init_memory2 = __esm({
|
|
|
12886
13006
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
12887
13007
|
entityId: uuid("entity_id").references(() => entities.id, { onDelete: "cascade" }),
|
|
12888
13008
|
agentId: uuid("agent_id").references(() => agents.id, { onDelete: "cascade" }),
|
|
13009
|
+
/**
|
|
13010
|
+
* Full-text source. A generated `search_tsv tsvector` column + GIN index
|
|
13011
|
+
* (raw SQL, migration 0051 — not expressible in the Drizzle schema builder,
|
|
13012
|
+
* same pattern as agent_jobs.search_tsv / migration 0050) makes this fact
|
|
13013
|
+
* rankable by relevance. Powers memory injection (selectMemoriesForInjection)
|
|
13014
|
+
* and the `query_memory` builtin (keywordSearchMemories) via ts_rank.
|
|
13015
|
+
* Config is `'english'` (stemming — "tokens" matches a "token" query),
|
|
13016
|
+
* unlike agent_jobs.search_tsv which stays `'simple'`.
|
|
13017
|
+
*/
|
|
12889
13018
|
fact: text("fact").notNull(),
|
|
12890
13019
|
category: text("category").default("context"),
|
|
12891
13020
|
importance: integer("importance").default(3),
|
|
@@ -12897,6 +13026,12 @@ var init_memory2 = __esm({
|
|
|
12897
13026
|
validTo: timestamp("valid_to", { withTimezone: true }),
|
|
12898
13027
|
factHash: text("fact_hash"),
|
|
12899
13028
|
archived: boolean("archived").default(false),
|
|
13029
|
+
/**
|
|
13030
|
+
* User override lock (migration 0053). When true, the curator's
|
|
13031
|
+
* usage-driven re-scoring MUST refuse to touch importance — a user-pinned
|
|
13032
|
+
* value wins over the agent's guess and the curator's correction.
|
|
13033
|
+
*/
|
|
13034
|
+
importanceLocked: boolean("importance_locked").notNull().default(false),
|
|
12900
13035
|
lastAccessedAt: timestamp("last_accessed_at", { withTimezone: true }).defaultNow(),
|
|
12901
13036
|
accessCount: integer("access_count").default(0),
|
|
12902
13037
|
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
|
@@ -12970,8 +13105,8 @@ var init_skills = __esm({
|
|
|
12970
13105
|
{
|
|
12971
13106
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
12972
13107
|
entityId: uuid("entity_id").references(() => entities.id, { onDelete: "cascade" }),
|
|
12973
|
-
name: text("name").notNull()
|
|
12974
|
-
slug: text("slug").notNull()
|
|
13108
|
+
name: text("name").notNull(),
|
|
13109
|
+
slug: text("slug").notNull(),
|
|
12975
13110
|
content: text("content").notNull(),
|
|
12976
13111
|
active: boolean("active").default(true),
|
|
12977
13112
|
description: text("description"),
|
|
@@ -13011,7 +13146,20 @@ var init_skills = __esm({
|
|
|
13011
13146
|
(table) => [
|
|
13012
13147
|
index("idx_agent_skills_entity_id").on(table.entityId),
|
|
13013
13148
|
index("idx_skills_active").on(table.active, table.slug),
|
|
13014
|
-
index("idx_agent_skills_is_community").on(table.isCommunity)
|
|
13149
|
+
index("idx_agent_skills_is_community").on(table.isCommunity),
|
|
13150
|
+
// F-6 (audit #2): slug AND name were UNIQUE GLOBALLY — a 2nd workspace/
|
|
13151
|
+
// entity installing the same community skill (e.g. slug 'comfyui') or
|
|
13152
|
+
// creating a skill with the same display name crashed the insert, and in
|
|
13153
|
+
// multi-user mode let one entity squat/enumerate another's slugs or
|
|
13154
|
+
// names. Scoped to (entity_id, slug) / (entity_id, name): every real
|
|
13155
|
+
// insert always sets entityId (createSkillRepo takes it as a required
|
|
13156
|
+
// param; the system-skill seeder and community-skill installer both pass
|
|
13157
|
+
// it too — no production path leaves it NULL), but the column itself is
|
|
13158
|
+
// nullable, so NULLS NOT DISTINCT closes the same NULL-entity gap as
|
|
13159
|
+
// DB-1/agent_plugins (multiple (NULL, 'x') rows would otherwise all
|
|
13160
|
+
// satisfy a plain UNIQUE).
|
|
13161
|
+
unique("agent_skills_entity_slug_unique").on(table.entityId, table.slug).nullsNotDistinct(),
|
|
13162
|
+
unique("agent_skills_entity_name_unique").on(table.entityId, table.name).nullsNotDistinct()
|
|
13015
13163
|
]
|
|
13016
13164
|
);
|
|
13017
13165
|
skillVersions = pgTable(
|
|
@@ -13068,7 +13216,14 @@ var init_skills = __esm({
|
|
|
13068
13216
|
filesWritable: boolean("files_writable").notNull().default(false),
|
|
13069
13217
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
13070
13218
|
},
|
|
13071
|
-
() => [
|
|
13219
|
+
(table) => [
|
|
13220
|
+
// DB-2 (audit #2): the table had no index beyond the PK, so assignSkillRepo's
|
|
13221
|
+
// check-then-insert (packages/db/src/repos/skills.ts) could race and leave
|
|
13222
|
+
// the same skill assigned twice to one agent. The unique index's leading
|
|
13223
|
+
// column (agent_id) also serves the hot read path (execute.ts loads all
|
|
13224
|
+
// skills for an agent) — no separate index needed on top of it.
|
|
13225
|
+
unique("agent_skill_assignments_agent_skill_unique").on(table.agentId, table.skillId)
|
|
13226
|
+
]
|
|
13072
13227
|
);
|
|
13073
13228
|
}
|
|
13074
13229
|
});
|
|
@@ -13200,7 +13355,14 @@ var init_mcp = __esm({
|
|
|
13200
13355
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
13201
13356
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
13202
13357
|
},
|
|
13203
|
-
(table) => [
|
|
13358
|
+
(table) => [
|
|
13359
|
+
index("idx_mcp_connections_entity").on(table.entityId),
|
|
13360
|
+
// R5 (audit #2 follow-up): the pglite test DDL (packages/db/src/tests/
|
|
13361
|
+
// helpers.ts) already assumed a per-entity unique slug here — same class
|
|
13362
|
+
// of phantom-constraint bug as DB-2/F-18. entityId is NOT NULL on this
|
|
13363
|
+
// table, so a plain UNIQUE (no NULLS NOT DISTINCT needed) is correct.
|
|
13364
|
+
uniqueIndex("mcp_connections_entity_slug_unique").on(table.entityId, table.slug)
|
|
13365
|
+
]
|
|
13204
13366
|
);
|
|
13205
13367
|
}
|
|
13206
13368
|
});
|
|
@@ -13255,7 +13417,14 @@ var init_misc = __esm({
|
|
|
13255
13417
|
check(
|
|
13256
13418
|
"agent_plugins_hook_check",
|
|
13257
13419
|
sql`${table.hook} IN ('pre_task','post_task','pre_tool','post_tool','on_memory_save')`
|
|
13258
|
-
)
|
|
13420
|
+
),
|
|
13421
|
+
// R5 (audit #2 follow-up): the pglite test DDL (packages/db/src/tests/
|
|
13422
|
+
// helpers.ts) already assumed a per-entity unique slug here — same class
|
|
13423
|
+
// of phantom-constraint bug as DB-2/F-18 (test mock ahead of the real
|
|
13424
|
+
// schema/migration). entityId is nullable (no `.notNull()` above), so
|
|
13425
|
+
// NULLS NOT DISTINCT closes the same NULL-entity gap as DB-1's
|
|
13426
|
+
// approval_rules constraint.
|
|
13427
|
+
unique("agent_plugins_entity_slug_unique").on(table.entityId, table.slug).nullsNotDistinct()
|
|
13259
13428
|
]
|
|
13260
13429
|
);
|
|
13261
13430
|
rateLimits = pgTable("rate_limits", {
|
|
@@ -13427,16 +13596,65 @@ var init_chat_messages = __esm({
|
|
|
13427
13596
|
});
|
|
13428
13597
|
|
|
13429
13598
|
// ../../packages/db/src/schema/app-settings.ts
|
|
13430
|
-
var appSettings;
|
|
13599
|
+
var appSettings, entitySettings;
|
|
13431
13600
|
var init_app_settings = __esm({
|
|
13432
13601
|
"../../packages/db/src/schema/app-settings.ts"() {
|
|
13433
13602
|
"use strict";
|
|
13434
13603
|
init_pg_core();
|
|
13604
|
+
init_entities();
|
|
13435
13605
|
appSettings = pgTable("app_settings", {
|
|
13436
13606
|
key: text("key").primaryKey(),
|
|
13437
13607
|
value: text("value").default("").notNull(),
|
|
13438
13608
|
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull()
|
|
13439
13609
|
});
|
|
13610
|
+
entitySettings = pgTable(
|
|
13611
|
+
"entity_settings",
|
|
13612
|
+
{
|
|
13613
|
+
entityId: uuid("entity_id").notNull().references(() => entities.id, { onDelete: "cascade" }),
|
|
13614
|
+
key: text("key").notNull(),
|
|
13615
|
+
value: text("value").default("").notNull(),
|
|
13616
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull()
|
|
13617
|
+
},
|
|
13618
|
+
(table) => [primaryKey({ columns: [table.entityId, table.key] })]
|
|
13619
|
+
);
|
|
13620
|
+
}
|
|
13621
|
+
});
|
|
13622
|
+
|
|
13623
|
+
// ../../packages/db/src/schema/telegram-allowed-chats.ts
|
|
13624
|
+
var telegramAllowedChats;
|
|
13625
|
+
var init_telegram_allowed_chats = __esm({
|
|
13626
|
+
"../../packages/db/src/schema/telegram-allowed-chats.ts"() {
|
|
13627
|
+
"use strict";
|
|
13628
|
+
init_pg_core();
|
|
13629
|
+
init_drizzle_orm();
|
|
13630
|
+
init_entities();
|
|
13631
|
+
init_agents();
|
|
13632
|
+
telegramAllowedChats = pgTable(
|
|
13633
|
+
"telegram_allowed_chats",
|
|
13634
|
+
{
|
|
13635
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
13636
|
+
entityId: uuid("entity_id").references(() => entities.id, { onDelete: "cascade" }),
|
|
13637
|
+
/** The bot the chat is talking to (the RECEIVING agent). */
|
|
13638
|
+
agentId: uuid("agent_id").notNull().references(() => agents.id, { onDelete: "cascade" }),
|
|
13639
|
+
/** Telegram chat id (private chat, group, or supergroup) as a string. */
|
|
13640
|
+
chatId: text("chat_id").notNull(),
|
|
13641
|
+
/** 'owner' = the bot's controller; 'member' = an approved additional chat. */
|
|
13642
|
+
role: text("role").notNull().default("member"),
|
|
13643
|
+
/** 'active' = may create jobs; 'pending' = awaiting the owner's confirmation. */
|
|
13644
|
+
status: text("status").notNull().default("pending"),
|
|
13645
|
+
/** Display name of the requester, for the owner's confirmation card. */
|
|
13646
|
+
requesterName: text("requester_name"),
|
|
13647
|
+
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
|
13648
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow()
|
|
13649
|
+
},
|
|
13650
|
+
(table) => [
|
|
13651
|
+
unique("telegram_allowed_chats_agent_chat_unique").on(table.agentId, table.chatId),
|
|
13652
|
+
index("idx_telegram_allowed_chats_agent").on(table.agentId),
|
|
13653
|
+
index("idx_telegram_allowed_chats_entity").on(table.entityId),
|
|
13654
|
+
check("telegram_allowed_chats_role_check", sql`${table.role} IN ('owner','member')`),
|
|
13655
|
+
check("telegram_allowed_chats_status_check", sql`${table.status} IN ('active','pending')`)
|
|
13656
|
+
]
|
|
13657
|
+
);
|
|
13440
13658
|
}
|
|
13441
13659
|
});
|
|
13442
13660
|
|
|
@@ -13488,12 +13706,14 @@ __export(schema_exports, {
|
|
|
13488
13706
|
entities: () => entities,
|
|
13489
13707
|
entityLlmKeys: () => entityLlmKeys,
|
|
13490
13708
|
entityMembers: () => entityMembers,
|
|
13709
|
+
entitySettings: () => entitySettings,
|
|
13491
13710
|
mcpConnections: () => mcpConnections,
|
|
13492
13711
|
mcpServers: () => mcpServers,
|
|
13493
13712
|
rateLimits: () => rateLimits,
|
|
13494
13713
|
sessions: () => sessions,
|
|
13495
13714
|
skillConnectors: () => skillConnectors,
|
|
13496
13715
|
skillVersions: () => skillVersions,
|
|
13716
|
+
telegramAllowedChats: () => telegramAllowedChats,
|
|
13497
13717
|
toolCalls: () => toolCalls,
|
|
13498
13718
|
userProfiles: () => userProfiles,
|
|
13499
13719
|
users: () => users,
|
|
@@ -13525,6 +13745,7 @@ var init_schema2 = __esm({
|
|
|
13525
13745
|
init_agent_workspaces();
|
|
13526
13746
|
init_chat_messages();
|
|
13527
13747
|
init_app_settings();
|
|
13748
|
+
init_telegram_allowed_chats();
|
|
13528
13749
|
}
|
|
13529
13750
|
});
|
|
13530
13751
|
|
|
@@ -13533,7 +13754,29 @@ function createClient(connectionString, options = {}) {
|
|
|
13533
13754
|
const pool = src_default(connectionString, {
|
|
13534
13755
|
max: options.max ?? 10,
|
|
13535
13756
|
// Fail fast on connection issues rather than hanging
|
|
13536
|
-
connect_timeout: 10
|
|
13757
|
+
connect_timeout: 10,
|
|
13758
|
+
// R2 (audit2 concurrency review): bound the two ways a query on this pool
|
|
13759
|
+
// can hang indefinitely with no client-side timeout at all. Sent as
|
|
13760
|
+
// Postgres startup/session parameters (postgres.js `connection` option),
|
|
13761
|
+
// so every connection in the pool gets them — no per-query opt-in needed.
|
|
13762
|
+
// - lock_timeout: the vector identified for the cron watchdog (R1) — a
|
|
13763
|
+
// query blocked waiting on a row lock previously waited forever.
|
|
13764
|
+
// 30s is comfortably longer than any legitimate lock wait in this
|
|
13765
|
+
// app's write patterns (single-row updates/inserts, no long-held
|
|
13766
|
+
// locks by design).
|
|
13767
|
+
// - idle_in_transaction_session_timeout: a transaction left open with
|
|
13768
|
+
// no further statements (BEGIN without COMMIT/ROLLBACK) is always a
|
|
13769
|
+
// bug, never a legitimate long-running operation — 60s is generous.
|
|
13770
|
+
// Deliberately NOT setting statement_timeout here: a global cap would
|
|
13771
|
+
// also apply to legitimate long-running operations (a large backfill, a
|
|
13772
|
+
// slow migration-adjacent query) and risk cutting them off mid-flight.
|
|
13773
|
+
// migrate.ts uses its own separate `postgres()` connection (not this
|
|
13774
|
+
// factory) for running migrations, so these timeouts don't touch
|
|
13775
|
+
// migrations at all either way.
|
|
13776
|
+
connection: {
|
|
13777
|
+
lock_timeout: 3e4,
|
|
13778
|
+
idle_in_transaction_session_timeout: 6e4
|
|
13779
|
+
}
|
|
13537
13780
|
});
|
|
13538
13781
|
const db = drizzle(pool, { schema: schema_exports });
|
|
13539
13782
|
return {
|
|
@@ -13560,12 +13803,23 @@ var init_transaction = __esm({
|
|
|
13560
13803
|
|
|
13561
13804
|
// ../../packages/secrets/src/index.ts
|
|
13562
13805
|
import { createCipheriv, createDecipheriv, randomBytes as randomBytes2 } from "crypto";
|
|
13563
|
-
import { chmodSync, existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
13806
|
+
import { chmodSync as chmodSync2, existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
13564
13807
|
import { homedir as homedir2 } from "os";
|
|
13565
13808
|
import { dirname as dirname3, join as join5 } from "path";
|
|
13809
|
+
function masterKeyPath() {
|
|
13810
|
+
return join5(homedir2(), ".nodalai", "secrets.key");
|
|
13811
|
+
}
|
|
13812
|
+
function masterKeyFileMissing(path = masterKeyPath()) {
|
|
13813
|
+
return !existsSync5(path);
|
|
13814
|
+
}
|
|
13815
|
+
function isEncrypted(value) {
|
|
13816
|
+
return value.startsWith(`enc:${VERSION}:`);
|
|
13817
|
+
}
|
|
13818
|
+
var VERSION;
|
|
13566
13819
|
var init_src3 = __esm({
|
|
13567
13820
|
"../../packages/secrets/src/index.ts"() {
|
|
13568
13821
|
"use strict";
|
|
13822
|
+
VERSION = "v1";
|
|
13569
13823
|
}
|
|
13570
13824
|
});
|
|
13571
13825
|
|
|
@@ -13581,6 +13835,29 @@ var init_credentials2 = __esm({
|
|
|
13581
13835
|
}
|
|
13582
13836
|
});
|
|
13583
13837
|
|
|
13838
|
+
// ../../packages/db/src/queries/master-key-guard.ts
|
|
13839
|
+
async function assertMasterKeyRestorable(db, options = {}) {
|
|
13840
|
+
if (!masterKeyFileMissing(options.keyPath)) return;
|
|
13841
|
+
const [credRow] = await db.select({ id: credentials.id }).from(credentials).limit(1);
|
|
13842
|
+
if (credRow) throw missingKeyError("a stored OAuth credential");
|
|
13843
|
+
const llmKeyRows = await db.select({ apiKey: entityLlmKeys.apiKey }).from(entityLlmKeys);
|
|
13844
|
+
const hasEncryptedLlmKey = llmKeyRows.some((r) => r.apiKey !== "" && isEncrypted(r.apiKey));
|
|
13845
|
+
if (hasEncryptedLlmKey) throw missingKeyError("a stored LLM API key");
|
|
13846
|
+
}
|
|
13847
|
+
function missingKeyError(what) {
|
|
13848
|
+
return new Error(
|
|
13849
|
+
`~/.nodalai/secrets.key is missing, but the database already has ${what} encrypted with it. Generating a NEW key here would make every existing credential / LLM key permanently undecryptable, with no further warning. Restore the original secrets.key from a backup before running \`nodal-agents up\` again. If you intend to start fresh, delete the affected rows (or wipe the database) first, then retry.`
|
|
13850
|
+
);
|
|
13851
|
+
}
|
|
13852
|
+
var init_master_key_guard = __esm({
|
|
13853
|
+
"../../packages/db/src/queries/master-key-guard.ts"() {
|
|
13854
|
+
"use strict";
|
|
13855
|
+
init_credentials();
|
|
13856
|
+
init_llm_keys();
|
|
13857
|
+
init_src3();
|
|
13858
|
+
}
|
|
13859
|
+
});
|
|
13860
|
+
|
|
13584
13861
|
// ../../packages/db/src/repos/agents.ts
|
|
13585
13862
|
var init_agents2 = __esm({
|
|
13586
13863
|
"../../packages/db/src/repos/agents.ts"() {
|
|
@@ -13624,6 +13901,7 @@ var init_src4 = __esm({
|
|
|
13624
13901
|
init_transaction();
|
|
13625
13902
|
init_schema2();
|
|
13626
13903
|
init_credentials2();
|
|
13904
|
+
init_master_key_guard();
|
|
13627
13905
|
init_agents2();
|
|
13628
13906
|
init_skills2();
|
|
13629
13907
|
init_retention();
|
|
@@ -13683,11 +13961,20 @@ var init_local_auth = __esm({
|
|
|
13683
13961
|
}
|
|
13684
13962
|
});
|
|
13685
13963
|
|
|
13964
|
+
// ../../packages/auth/src/lib/constant-time.ts
|
|
13965
|
+
import { createHash, timingSafeEqual } from "crypto";
|
|
13966
|
+
var init_constant_time = __esm({
|
|
13967
|
+
"../../packages/auth/src/lib/constant-time.ts"() {
|
|
13968
|
+
"use strict";
|
|
13969
|
+
}
|
|
13970
|
+
});
|
|
13971
|
+
|
|
13686
13972
|
// ../../packages/auth/src/providers/bearer-token.ts
|
|
13687
13973
|
var init_bearer_token = __esm({
|
|
13688
13974
|
"../../packages/auth/src/providers/bearer-token.ts"() {
|
|
13689
13975
|
"use strict";
|
|
13690
13976
|
init_local_trust();
|
|
13977
|
+
init_constant_time();
|
|
13691
13978
|
}
|
|
13692
13979
|
});
|
|
13693
13980
|
|
|
@@ -13726,6 +14013,7 @@ var init_seed = __esm({
|
|
|
13726
14013
|
});
|
|
13727
14014
|
|
|
13728
14015
|
// src/lib/env.ts
|
|
14016
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
13729
14017
|
function buildEnvForRunner(config, databaseUrl) {
|
|
13730
14018
|
const bind = config.bind === "loopback" ? "127.0.0.1" : "0.0.0.0";
|
|
13731
14019
|
const env = {
|
|
@@ -13749,7 +14037,19 @@ function buildEnvForRunner(config, databaseUrl) {
|
|
|
13749
14037
|
return env;
|
|
13750
14038
|
}
|
|
13751
14039
|
function resolveAuthMode(config) {
|
|
13752
|
-
if (config.
|
|
14040
|
+
if (config.bearerToken) {
|
|
14041
|
+
throw new Error(
|
|
14042
|
+
'Invalid config: "bearerToken" is set in config.json, which the docs describe as activating AUTH_MODE=bearer-token \u2014 but that mode is not fully wired end-to-end yet (the web dashboard\'s auth provider still falls back to no-auth for bearer-token mode; see apps/web/src/lib/server.ts getAuthProvider). Booting anyway would silently run in local-trust or local-auth instead \u2014 NOT the bearer-token protection you configured. Refused. Remove "bearerToken" from config.json until this is wired, or use `bind`/`auth.mode` for local-trust/local-auth.'
|
|
14043
|
+
);
|
|
14044
|
+
}
|
|
14045
|
+
if (config.auth?.mode) {
|
|
14046
|
+
if (config.auth.mode === "local-trust" && config.bind === "lan") {
|
|
14047
|
+
throw new Error(
|
|
14048
|
+
'Invalid config: auth.mode="local-trust" with bind="lan" would expose every runner route unauthenticated on the network (including run_command auto-approve). Refused. Use auth.mode="local-auth" for a LAN bind, or bind="loopback" for local-trust.'
|
|
14049
|
+
);
|
|
14050
|
+
}
|
|
14051
|
+
return config.auth.mode;
|
|
14052
|
+
}
|
|
13753
14053
|
return config.bind === "lan" ? "local-auth" : "local-trust";
|
|
13754
14054
|
}
|
|
13755
14055
|
function buildEnvForWeb(config, databaseUrl) {
|
|
@@ -13773,8 +14073,16 @@ function buildEnvForWeb(config, databaseUrl) {
|
|
|
13773
14073
|
// The web itself doesn't bind on this — Next.js listens on 0.0.0.0 anyway.
|
|
13774
14074
|
BIND: bind,
|
|
13775
14075
|
NODE_ENV: "production",
|
|
13776
|
-
// AUTH_SECRET is required by better-auth in local-auth mode; harmless in
|
|
13777
|
-
|
|
14076
|
+
// AUTH_SECRET is required by better-auth in local-auth mode; harmless in
|
|
14077
|
+
// local-trust. M-4: deliberately SEPARATE from workerSecret — the two
|
|
14078
|
+
// used to share a value, so a leak of workerSecret (the runner's auth
|
|
14079
|
+
// frontier) would also let an attacker forge a signed better-auth session
|
|
14080
|
+
// cookie. readConfig() auto-mints authSecret, so this is set in every
|
|
14081
|
+
// normal boot path; the random fallback below only guards a caller that
|
|
14082
|
+
// somehow bypasses readConfig (ephemeral — would invalidate sessions on
|
|
14083
|
+
// every restart, which is a symptom to fix at the config layer, not a
|
|
14084
|
+
// reason to crash boot or fall back to workerSecret).
|
|
14085
|
+
AUTH_SECRET: config.authSecret ?? randomBytes3(32).toString("base64"),
|
|
13778
14086
|
// WORKER_SECRET — same value as the runner — so sendTaskAction can sign
|
|
13779
14087
|
// the POST /api/worker call. Without this the runner returns 403 and the
|
|
13780
14088
|
// job stays pending forever (cron only scans task-board, not API jobs).
|
|
@@ -14057,7 +14365,7 @@ __export(up_exports, {
|
|
|
14057
14365
|
import chalk from "chalk";
|
|
14058
14366
|
import ora from "ora";
|
|
14059
14367
|
import open from "open";
|
|
14060
|
-
import { randomBytes as
|
|
14368
|
+
import { randomBytes as randomBytes4 } from "crypto";
|
|
14061
14369
|
async function killSilent(child) {
|
|
14062
14370
|
await killProcessTree(child);
|
|
14063
14371
|
}
|
|
@@ -14082,8 +14390,12 @@ async function runUp(opts = {}) {
|
|
|
14082
14390
|
const defaultConfig = {
|
|
14083
14391
|
// No `llm` section: the browser onboarding sets it via createLlmKeyAction.
|
|
14084
14392
|
ports: { web: 3e3, runner: 3001, postgres: 25432 },
|
|
14085
|
-
workerSecret:
|
|
14086
|
-
serverActionsKey:
|
|
14393
|
+
workerSecret: randomBytes4(32).toString("hex"),
|
|
14394
|
+
serverActionsKey: randomBytes4(32).toString("base64"),
|
|
14395
|
+
// M-4: authSecret is minted here (distinct from workerSecret) rather
|
|
14396
|
+
// than left for readConfig()'s auto-mint, because this in-memory
|
|
14397
|
+
// `config` is used directly below without a re-read.
|
|
14398
|
+
authSecret: randomBytes4(32).toString("base64"),
|
|
14087
14399
|
// loopback derives auth: local-trust (no login/signup). Leave `auth`
|
|
14088
14400
|
// unset so resolveAuthMode falls back to the loopback → local-trust map.
|
|
14089
14401
|
bind: "loopback"
|
|
@@ -14091,6 +14403,7 @@ async function runUp(opts = {}) {
|
|
|
14091
14403
|
writeConfig(defaultConfig);
|
|
14092
14404
|
config = defaultConfig;
|
|
14093
14405
|
}
|
|
14406
|
+
resolveAuthMode(config);
|
|
14094
14407
|
const orphans = [];
|
|
14095
14408
|
for (const [name, port] of [
|
|
14096
14409
|
["web", config.ports.web],
|
|
@@ -14196,6 +14509,17 @@ async function runUp(opts = {}) {
|
|
|
14196
14509
|
await pg.stop();
|
|
14197
14510
|
throw err;
|
|
14198
14511
|
}
|
|
14512
|
+
{
|
|
14513
|
+
const { db, close } = createClient(databaseUrl, { max: 1 });
|
|
14514
|
+
try {
|
|
14515
|
+
await assertMasterKeyRestorable(db);
|
|
14516
|
+
} catch (err) {
|
|
14517
|
+
await close();
|
|
14518
|
+
await pg.stop();
|
|
14519
|
+
throw err;
|
|
14520
|
+
}
|
|
14521
|
+
await close();
|
|
14522
|
+
}
|
|
14199
14523
|
const authMode = resolveAuthMode(config);
|
|
14200
14524
|
if (authMode === "local-trust") {
|
|
14201
14525
|
const seedSpinner = ora("Seeding default user and agent\u2026").start();
|
|
@@ -14245,13 +14569,13 @@ async function runUp(opts = {}) {
|
|
|
14245
14569
|
}
|
|
14246
14570
|
void (async () => {
|
|
14247
14571
|
try {
|
|
14248
|
-
const { getInstalledVersion: getInstalledVersion2, getLatestVersion: getLatestVersion2 } = await Promise.resolve().then(() => (init_version(), version_exports));
|
|
14572
|
+
const { getInstalledVersion: getInstalledVersion2, getLatestVersion: getLatestVersion2, isNewerVersion: isNewerVersion2 } = await Promise.resolve().then(() => (init_version(), version_exports));
|
|
14249
14573
|
const installed = getInstalledVersion2();
|
|
14250
14574
|
const latest = await Promise.race([
|
|
14251
14575
|
getLatestVersion2(),
|
|
14252
14576
|
new Promise((resolve2) => setTimeout(() => resolve2(null), 3e3))
|
|
14253
14577
|
]);
|
|
14254
|
-
if (latest !== null && latest
|
|
14578
|
+
if (latest !== null && isNewerVersion2(latest, installed)) {
|
|
14255
14579
|
console.log(chalk.cyan(` \u2139 v${latest} available \u2014 run \`nodal-agents update\``));
|
|
14256
14580
|
}
|
|
14257
14581
|
} catch {
|
|
@@ -14407,9 +14731,9 @@ __export(init_exports, {
|
|
|
14407
14731
|
import chalk2 from "chalk";
|
|
14408
14732
|
import prompts from "prompts";
|
|
14409
14733
|
import ora2 from "ora";
|
|
14410
|
-
import { randomBytes as
|
|
14734
|
+
import { randomBytes as randomBytes5 } from "crypto";
|
|
14411
14735
|
function generateSecret(bytes = 32) {
|
|
14412
|
-
return
|
|
14736
|
+
return randomBytes5(bytes).toString("hex");
|
|
14413
14737
|
}
|
|
14414
14738
|
async function runInit(options = {}) {
|
|
14415
14739
|
const existing = readConfig();
|
|
@@ -14430,7 +14754,7 @@ async function runInit(options = {}) {
|
|
|
14430
14754
|
const config2 = {
|
|
14431
14755
|
ports: { web: 3e3, runner: 3001, postgres: 25432 },
|
|
14432
14756
|
workerSecret: generateSecret(32),
|
|
14433
|
-
serverActionsKey:
|
|
14757
|
+
serverActionsKey: randomBytes5(32).toString("base64"),
|
|
14434
14758
|
bind: "lan",
|
|
14435
14759
|
auth: { mode: "local-auth" }
|
|
14436
14760
|
};
|