nodal-agents 0.6.7 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/cli.js +24 -0
- 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/meta/_journal.json +21 -0
- package/package.json +1 -1
- package/runner.js +275 -57
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/app-path-routes-manifest.json +2 -2
- 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_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 +1 -1
- 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_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/llm-providers/page.js +1 -1
- 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 +1 -1
- 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_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 +2 -2
- 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_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/oauth/[provider]/callback/route.js +1 -1
- package/web/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/onboarding/page.js +1319 -1315
- package/web/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app-paths-manifest.json +2 -2
- package/web/.next/server/chunks/2713.js +17 -13
- package/web/.next/server/chunks/3223.js +1 -1
- package/web/.next/server/chunks/4574.js +1 -1
- package/web/.next/server/chunks/4626.js +1 -0
- package/web/.next/server/chunks/8971.js +1 -0
- package/web/.next/server/chunks/9043.js +1 -0
- 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-08171c654e41b0a4.js +1 -0
- package/web/.next/static/chunks/{9836-14f7e30f77745e80.js → 9836-6830cb9d49ad261f.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/agents/[id]/edit/page-fa5717f0d0450659.js +6 -0
- package/web/.next/static/chunks/app/(dashboard)/agents/[id]/telegram/page-73012222871598aa.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/agents/page-7e9616b28c098e9e.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/approvals/{page-64cdc8de03714da8.js → page-9a01cb723a6ccff0.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/automations/page-85c985cd687406ed.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/chat/page-a6cc7a58986c0340.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/connectors/{page-c417cceb9b25870c.js → page-21d6c042800c8cd3.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/jobs/[id]/{page-4a258111bca49454.js → page-f6b993a88c70a2fe.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/jobs/{page-4ef0c0002fb31c13.js → page-d0d6b9db1b68451f.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/layout-d2ac2646967496bb.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/learned-skills/page-ec6312f6dfd7ee23.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/llm-providers/page-1fb26463eddf509c.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/logs/{page-69a02d29acd3d812.js → page-0bca733315e8a208.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/mcp/page-ff56ecf2f65f5466.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/memories/page-fe4d9438c93750d6.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/page-9402fe2931216f4b.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/settings/page-995662f10ebff365.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/settings/root-context/page-785b6ce3e4c5890b.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/skills/[id]/edit/page-2106201c4df8b5a4.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/skills/new/page-0135bcb86ab60dc2.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/skills/{page-c47e251b87d38cc7.js → page-54e939d40c8b5b6d.js} +1 -1
- package/web/.next/static/chunks/app/onboarding/page-a4116e2f430eaff8.js +26 -0
- package/web/.next/static/css/eff119dd24f92093.css +3 -0
- package/web/.next/server/chunks/4088.js +0 -1
- package/web/.next/server/chunks/4122.js +0 -1
- package/web/.next/server/chunks/7495.js +0 -1
- package/web/.next/static/chunks/4964-09a4dc14acc324f3.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/agents/[id]/edit/page-499eb81b66c74dc5.js +0 -6
- package/web/.next/static/chunks/app/(dashboard)/agents/[id]/telegram/page-e26c56155c37f38e.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/agents/page-f4c535f30d385857.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/automations/page-b654ebf59b7a0a76.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/chat/page-687c72476c5097c1.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/layout-04e80131398f1551.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/learned-skills/page-31b01dbe3baa44cc.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/llm-providers/page-96df36ea1bf14242.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/mcp/page-3ddd2e2c8973fb2f.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/memories/page-2e127cc802ee8e72.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/page-a25fd0011e21ba38.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/settings/page-06062fc5a2d536f6.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/settings/root-context/page-fd80a1645212a6d1.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/skills/[id]/edit/page-59bb2de0f81b5ed6.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/skills/new/page-0970daf889c085c9.js +0 -1
- package/web/.next/static/chunks/app/onboarding/page-995403cba38e26cc.js +0 -26
- package/web/.next/static/css/9fc24595c3687cfb.css +0 -3
- /package/web/.next/static/{vB-0ke6pDH6vYyme7Pz5G → KFac7DQ7gdp9XPQ2bnb-2}/_buildManifest.js +0 -0
- /package/web/.next/static/{vB-0ke6pDH6vYyme7Pz5G → KFac7DQ7gdp9XPQ2bnb-2}/_ssgManifest.js +0 -0
package/runner.js
CHANGED
|
@@ -574,6 +574,10 @@ var init_memory = __esm({
|
|
|
574
574
|
valid_to: z13.string().datetime().nullable(),
|
|
575
575
|
fact_hash: z13.string().nullable(),
|
|
576
576
|
archived: z13.boolean(),
|
|
577
|
+
// User override lock (migration 0053) — when true, the curator refuses to
|
|
578
|
+
// re-score importance for this fact. Defaulted so rows/objects predating
|
|
579
|
+
// the column still parse.
|
|
580
|
+
importance_locked: z13.boolean().default(false),
|
|
577
581
|
last_accessed_at: z13.string().datetime().nullable(),
|
|
578
582
|
access_count: z13.number().int().min(0),
|
|
579
583
|
created_at: z13.string().datetime(),
|
|
@@ -585,6 +589,7 @@ var init_memory = __esm({
|
|
|
585
589
|
updated_at: true,
|
|
586
590
|
fact_hash: true,
|
|
587
591
|
archived: true,
|
|
592
|
+
importance_locked: true,
|
|
588
593
|
last_accessed_at: true,
|
|
589
594
|
access_count: true,
|
|
590
595
|
valid_from: true,
|
|
@@ -1593,6 +1598,10 @@ var init_entities = __esm({
|
|
|
1593
1598
|
updatedAt: timestamp2("updated_at", { withTimezone: true }).defaultNow(),
|
|
1594
1599
|
lastCuratorRunAt: timestamp2("last_curator_run_at", { withTimezone: true }),
|
|
1595
1600
|
reflectionEnabled: boolean2("reflection_enabled").notNull().default(false),
|
|
1601
|
+
// Decoupled from reflection_enabled (which gates the skill-learning loop,
|
|
1602
|
+
// opt-in). Gates the memory curator (Phase 2 LLM pass) — ON by default so
|
|
1603
|
+
// memory curation runs even for entities that never opted into reflection.
|
|
1604
|
+
memoryCurationEnabled: boolean2("memory_curation_enabled").notNull().default(true),
|
|
1596
1605
|
// Controls whether agent-authored skills are auto-assigned to the authoring
|
|
1597
1606
|
// agent ('auto') or queued for the entity owner to approve ('approval').
|
|
1598
1607
|
skillAssignmentMode: text2("skill_assignment_mode").notNull().default("approval").$type(),
|
|
@@ -2190,6 +2199,15 @@ var init_memory2 = __esm({
|
|
|
2190
2199
|
id: uuid11("id").primaryKey().defaultRandom(),
|
|
2191
2200
|
entityId: uuid11("entity_id").references(() => entities.id, { onDelete: "cascade" }),
|
|
2192
2201
|
agentId: uuid11("agent_id").references(() => agents.id, { onDelete: "cascade" }),
|
|
2202
|
+
/**
|
|
2203
|
+
* Full-text source. A generated `search_tsv tsvector` column + GIN index
|
|
2204
|
+
* (raw SQL, migration 0051 — not expressible in the Drizzle schema builder,
|
|
2205
|
+
* same pattern as agent_jobs.search_tsv / migration 0050) makes this fact
|
|
2206
|
+
* rankable by relevance. Powers memory injection (selectMemoriesForInjection)
|
|
2207
|
+
* and the `query_memory` builtin (keywordSearchMemories) via ts_rank.
|
|
2208
|
+
* Config is `'english'` (stemming — "tokens" matches a "token" query),
|
|
2209
|
+
* unlike agent_jobs.search_tsv which stays `'simple'`.
|
|
2210
|
+
*/
|
|
2193
2211
|
fact: text11("fact").notNull(),
|
|
2194
2212
|
category: text11("category").default("context"),
|
|
2195
2213
|
importance: integer5("importance").default(3),
|
|
@@ -2201,6 +2219,12 @@ var init_memory2 = __esm({
|
|
|
2201
2219
|
validTo: timestamp11("valid_to", { withTimezone: true }),
|
|
2202
2220
|
factHash: text11("fact_hash"),
|
|
2203
2221
|
archived: boolean6("archived").default(false),
|
|
2222
|
+
/**
|
|
2223
|
+
* User override lock (migration 0053). When true, the curator's
|
|
2224
|
+
* usage-driven re-scoring MUST refuse to touch importance — a user-pinned
|
|
2225
|
+
* value wins over the agent's guess and the curator's correction.
|
|
2226
|
+
*/
|
|
2227
|
+
importanceLocked: boolean6("importance_locked").notNull().default(false),
|
|
2204
2228
|
lastAccessedAt: timestamp11("last_accessed_at", { withTimezone: true }).defaultNow(),
|
|
2205
2229
|
accessCount: integer5("access_count").default(0),
|
|
2206
2230
|
createdAt: timestamp11("created_at", { withTimezone: true }).defaultNow(),
|
|
@@ -17705,6 +17729,17 @@ var envSchema = z.object({
|
|
|
17705
17729
|
CURATOR_MEMORY_STALE_DAYS: z.coerce.number().default(60),
|
|
17706
17730
|
CURATOR_MEMORY_IMPORTANCE_MAX: z.coerce.number().default(2),
|
|
17707
17731
|
CURATOR_MEMORY_MIN: z.coerce.number().default(8),
|
|
17732
|
+
// Kill-switch semantics mirror REFLECTION_ENABLED but gate the MEMORY curator
|
|
17733
|
+
// (Phase 2 LLM pass) specifically — decoupled from the skill-learning loop:
|
|
17734
|
+
// MEMORY_CURATION_ENABLED='false' → global kill: disables memory curation
|
|
17735
|
+
// for every entity.
|
|
17736
|
+
// MEMORY_CURATION_ENABLED='' → per-entity decides (production default
|
|
17737
|
+
// (unset) when the var is absent). Since
|
|
17738
|
+
// entities.memory_curation_enabled
|
|
17739
|
+
// defaults TRUE, memory curation runs
|
|
17740
|
+
// out of the box — unlike reflection.
|
|
17741
|
+
// MEMORY_CURATION_ENABLED='true' → per-entity decides (same as unset).
|
|
17742
|
+
MEMORY_CURATION_ENABLED: z.string().default(""),
|
|
17708
17743
|
// Optional. When set, the reflection + curator passes use THIS model id instead of
|
|
17709
17744
|
// the agent's model — resolved against the agent's existing LLM key (ideal for an
|
|
17710
17745
|
// OpenRouter key where one key serves many models; point the cheap/reliable
|
|
@@ -19575,13 +19610,6 @@ var WhitelistDriftError = class extends Error {
|
|
|
19575
19610
|
undeclaredTools;
|
|
19576
19611
|
code = "whitelist_drift";
|
|
19577
19612
|
};
|
|
19578
|
-
var WebSearchNotConfiguredError = class extends Error {
|
|
19579
|
-
code = "web_search_not_configured";
|
|
19580
|
-
constructor() {
|
|
19581
|
-
super("web_search_not_configured");
|
|
19582
|
-
this.name = "WebSearchNotConfiguredError";
|
|
19583
|
-
}
|
|
19584
|
-
};
|
|
19585
19613
|
|
|
19586
19614
|
// ../../packages/tools/src/registry.ts
|
|
19587
19615
|
function createToolRegistry() {
|
|
@@ -20033,6 +20061,7 @@ function rowToMemory(row) {
|
|
|
20033
20061
|
valid_to: row.validTo?.toISOString() ?? null,
|
|
20034
20062
|
fact_hash: row.factHash ?? null,
|
|
20035
20063
|
archived: row.archived ?? false,
|
|
20064
|
+
importance_locked: row.importanceLocked ?? false,
|
|
20036
20065
|
last_accessed_at: row.lastAccessedAt?.toISOString() ?? null,
|
|
20037
20066
|
access_count: row.accessCount ?? 0,
|
|
20038
20067
|
created_at: row.createdAt?.toISOString() ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -20058,19 +20087,29 @@ function keywordOrderBy(sort) {
|
|
|
20058
20087
|
}
|
|
20059
20088
|
async function keywordSearch(db, query, filters, sort = "importance") {
|
|
20060
20089
|
const { entityId, agentId, skillTags, category, limit } = filters;
|
|
20061
|
-
const
|
|
20090
|
+
const orTerms = query.replace(/[^\w\s]/g, " ").split(/\s+/).filter((w) => w.length > 2).slice(0, 8).map((w) => w.replace(/[^a-zA-Z0-9]/g, "")).filter((w) => w.length > 0);
|
|
20062
20091
|
const conditions = buildWhereConditions({ entityId, agentId, skillTags, category });
|
|
20063
20092
|
const orderBy = keywordOrderBy(sort);
|
|
20064
|
-
if (
|
|
20065
|
-
const
|
|
20066
|
-
const
|
|
20093
|
+
if (orTerms.length > 0) {
|
|
20094
|
+
const tsv = sql18`"agent_memory"."search_tsv"`;
|
|
20095
|
+
const tsq = sql18`to_tsquery('english', ${orTerms.join(" | ")})`;
|
|
20096
|
+
const rows2 = await db.select().from(agentMemory).where(and3(...conditions, sql18`${tsv} @@ ${tsq}`)).orderBy(sql18`ts_rank(${tsv}, ${tsq}) DESC`, ...orderBy).limit(limit);
|
|
20067
20097
|
return rows2;
|
|
20068
20098
|
}
|
|
20069
20099
|
const rows = await db.select().from(agentMemory).where(and3(...conditions)).orderBy(...orderBy).limit(limit);
|
|
20070
20100
|
return rows;
|
|
20071
20101
|
}
|
|
20072
20102
|
async function keywordSearchMemories(db, opts) {
|
|
20073
|
-
const {
|
|
20103
|
+
const {
|
|
20104
|
+
query,
|
|
20105
|
+
entityId,
|
|
20106
|
+
agentId,
|
|
20107
|
+
skillTags,
|
|
20108
|
+
category,
|
|
20109
|
+
limit = 10,
|
|
20110
|
+
sort = "importance",
|
|
20111
|
+
touch = true
|
|
20112
|
+
} = opts;
|
|
20074
20113
|
const rows = await keywordSearch(
|
|
20075
20114
|
db,
|
|
20076
20115
|
query,
|
|
@@ -20078,7 +20117,7 @@ async function keywordSearchMemories(db, opts) {
|
|
|
20078
20117
|
sort
|
|
20079
20118
|
);
|
|
20080
20119
|
const memories = rows.map((r) => rowToMemory(r));
|
|
20081
|
-
if (memories.length > 0) {
|
|
20120
|
+
if (touch && memories.length > 0) {
|
|
20082
20121
|
await touchMemories(
|
|
20083
20122
|
db,
|
|
20084
20123
|
memories.map((m) => m.id),
|
|
@@ -20150,6 +20189,10 @@ async function loadSource(db, entityId, memoryId) {
|
|
|
20150
20189
|
const [row] = await db.select({ source: agentMemory.source }).from(agentMemory).where(and3(eq5(agentMemory.id, memoryId), eq5(agentMemory.entityId, entityId))).limit(1);
|
|
20151
20190
|
return row ? row.source : void 0;
|
|
20152
20191
|
}
|
|
20192
|
+
async function loadSourceAndLock(db, entityId, memoryId) {
|
|
20193
|
+
const [row] = await db.select({ source: agentMemory.source, importanceLocked: agentMemory.importanceLocked }).from(agentMemory).where(and3(eq5(agentMemory.id, memoryId), eq5(agentMemory.entityId, entityId))).limit(1);
|
|
20194
|
+
return row ? { source: row.source, importanceLocked: row.importanceLocked } : void 0;
|
|
20195
|
+
}
|
|
20153
20196
|
async function archiveAgentMemory(db, entityId, memoryId) {
|
|
20154
20197
|
const source = await loadSource(db, entityId, memoryId);
|
|
20155
20198
|
if (source === void 0) return { error: "not_found" };
|
|
@@ -20165,6 +20208,17 @@ async function updateAgentMemoryFact(db, entityId, memoryId, fact) {
|
|
|
20165
20208
|
await db.update(agentMemory).set({ fact: trimmed, factHash: computeFactHash(trimmed), updatedAt: /* @__PURE__ */ new Date() }).where(and3(eq5(agentMemory.id, memoryId), eq5(agentMemory.entityId, entityId)));
|
|
20166
20209
|
return { ok: true };
|
|
20167
20210
|
}
|
|
20211
|
+
async function updateAgentMemoryImportance(db, entityId, memoryId, importance) {
|
|
20212
|
+
if (!Number.isInteger(importance) || importance < 1 || importance > 5) {
|
|
20213
|
+
return { error: "invalid_importance" };
|
|
20214
|
+
}
|
|
20215
|
+
const row = await loadSourceAndLock(db, entityId, memoryId);
|
|
20216
|
+
if (row === void 0) return { error: "not_found" };
|
|
20217
|
+
if (row.source === "manual") return { error: "not_agent_memory" };
|
|
20218
|
+
if (row.importanceLocked) return { error: "importance_locked" };
|
|
20219
|
+
await db.update(agentMemory).set({ importance, updatedAt: /* @__PURE__ */ new Date() }).where(and3(eq5(agentMemory.id, memoryId), eq5(agentMemory.entityId, entityId)));
|
|
20220
|
+
return { ok: true };
|
|
20221
|
+
}
|
|
20168
20222
|
|
|
20169
20223
|
// ../../packages/memory/src/inject.ts
|
|
20170
20224
|
var RENDER_OVERHEAD_PER_ENTRY = 20;
|
|
@@ -20310,9 +20364,13 @@ function selectMemoriesUnderBudget(memories, maxChars, query) {
|
|
|
20310
20364
|
if (aTouched !== bTouched) return bTouched.localeCompare(aTouched);
|
|
20311
20365
|
return a.id.localeCompare(b.id);
|
|
20312
20366
|
});
|
|
20367
|
+
return packUnderBudget(sorted, maxChars);
|
|
20368
|
+
}
|
|
20369
|
+
function packUnderBudget(memories, maxChars) {
|
|
20370
|
+
if (maxChars <= 0) return [];
|
|
20313
20371
|
const picked = [];
|
|
20314
20372
|
let used = 0;
|
|
20315
|
-
for (const m of
|
|
20373
|
+
for (const m of memories) {
|
|
20316
20374
|
const cost = m.fact.length + RENDER_OVERHEAD_PER_ENTRY;
|
|
20317
20375
|
if (used + cost > maxChars) continue;
|
|
20318
20376
|
picked.push(m);
|
|
@@ -20323,13 +20381,25 @@ function selectMemoriesUnderBudget(memories, maxChars, query) {
|
|
|
20323
20381
|
var MAX_CANDIDATES = 200;
|
|
20324
20382
|
async function selectMemoriesForInjection(db, opts) {
|
|
20325
20383
|
if (opts.maxChars <= 0) return [];
|
|
20326
|
-
const
|
|
20327
|
-
|
|
20328
|
-
|
|
20329
|
-
|
|
20330
|
-
|
|
20331
|
-
|
|
20332
|
-
|
|
20384
|
+
const baseConditions = and3(
|
|
20385
|
+
eq5(agentMemory.entityId, opts.entityId),
|
|
20386
|
+
eq5(agentMemory.archived, false),
|
|
20387
|
+
sql18`(${agentMemory.validTo} IS NULL OR ${agentMemory.validTo} > now())`
|
|
20388
|
+
);
|
|
20389
|
+
const rawQuery = opts.query ?? "";
|
|
20390
|
+
const tokens = tokenizeQuery(rawQuery);
|
|
20391
|
+
if (tokens.length > 0) {
|
|
20392
|
+
const tsv = sql18`"agent_memory"."search_tsv"`;
|
|
20393
|
+
const tsq = sql18`plainto_tsquery('english', ${rawQuery})`;
|
|
20394
|
+
const candidates2 = await db.select().from(agentMemory).where(baseConditions).orderBy(
|
|
20395
|
+
sql18`ts_rank(${tsv}, ${tsq}) DESC`,
|
|
20396
|
+
desc(agentMemory.importance),
|
|
20397
|
+
desc(agentMemory.lastAccessedAt)
|
|
20398
|
+
).limit(MAX_CANDIDATES);
|
|
20399
|
+
const memories2 = candidates2.map((row) => rowToMemory(row));
|
|
20400
|
+
return packUnderBudget(memories2, opts.maxChars);
|
|
20401
|
+
}
|
|
20402
|
+
const candidates = await db.select().from(agentMemory).where(baseConditions).orderBy(desc(agentMemory.importance), desc(agentMemory.lastAccessedAt)).limit(MAX_CANDIDATES);
|
|
20333
20403
|
const memories = candidates.map((row) => rowToMemory(row));
|
|
20334
20404
|
return selectMemoriesUnderBudget(memories, opts.maxChars, opts.query);
|
|
20335
20405
|
}
|
|
@@ -20584,17 +20654,80 @@ import { z as z25 } from "zod";
|
|
|
20584
20654
|
var WebSearchInputSchema = z25.object({
|
|
20585
20655
|
query: z25.string().min(1).describe("The search query.")
|
|
20586
20656
|
});
|
|
20657
|
+
var DEGRADED_GUIDANCE = "Free best-effort web search failed (it can be rate-limited or blocked). For reliable results, add a Tavily key (free tier) in the dashboard under Connectors, and I'll use it automatically.";
|
|
20658
|
+
var DDG_ENDPOINT = "https://html.duckduckgo.com/html/";
|
|
20659
|
+
function stripHtml(html) {
|
|
20660
|
+
return html.replace(/<[^>]*>/g, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/'/g, "'").replace(/ /g, " ").replace(/\s+/g, " ").trim();
|
|
20661
|
+
}
|
|
20662
|
+
function resolveDdgHref(href) {
|
|
20663
|
+
const normalized = href.startsWith("//") ? `https:${href}` : href;
|
|
20664
|
+
const uddg = normalized.match(/[?&]uddg=([^&]+)/);
|
|
20665
|
+
if (uddg?.[1]) {
|
|
20666
|
+
try {
|
|
20667
|
+
return decodeURIComponent(uddg[1]);
|
|
20668
|
+
} catch {
|
|
20669
|
+
}
|
|
20670
|
+
}
|
|
20671
|
+
return normalized;
|
|
20672
|
+
}
|
|
20673
|
+
function parseDuckDuckGoHtml(html) {
|
|
20674
|
+
const results = [];
|
|
20675
|
+
const anchorRe = /<a[^>]*class="[^"]*result__a[^"]*"[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi;
|
|
20676
|
+
const snippetRe = /<a[^>]*class="[^"]*result__snippet[^"]*"[^>]*>([\s\S]*?)<\/a>/gi;
|
|
20677
|
+
let match;
|
|
20678
|
+
while ((match = anchorRe.exec(html)) !== null) {
|
|
20679
|
+
const href = match[1];
|
|
20680
|
+
const rawTitle = match[2];
|
|
20681
|
+
if (!href || rawTitle === void 0) continue;
|
|
20682
|
+
const title = stripHtml(rawTitle);
|
|
20683
|
+
const url2 = resolveDdgHref(href);
|
|
20684
|
+
if (!title || !url2) continue;
|
|
20685
|
+
snippetRe.lastIndex = anchorRe.lastIndex;
|
|
20686
|
+
const snippetMatch = snippetRe.exec(html);
|
|
20687
|
+
const snippet = snippetMatch?.[1] ? stripHtml(snippetMatch[1]) : "";
|
|
20688
|
+
results.push({ title, url: url2, snippet });
|
|
20689
|
+
}
|
|
20690
|
+
return results;
|
|
20691
|
+
}
|
|
20692
|
+
async function duckDuckGoSearch(query) {
|
|
20693
|
+
let html;
|
|
20694
|
+
try {
|
|
20695
|
+
const res = await fetch(DDG_ENDPOINT, {
|
|
20696
|
+
// The html endpoint blocks GET (returns 202 with an empty challenge page)
|
|
20697
|
+
// but serves 200 + real results for a form POST. A browser-ish UA is also
|
|
20698
|
+
// required (bare requests are refused).
|
|
20699
|
+
method: "POST",
|
|
20700
|
+
headers: {
|
|
20701
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36",
|
|
20702
|
+
Accept: "text/html",
|
|
20703
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
20704
|
+
},
|
|
20705
|
+
body: "q=" + encodeURIComponent(query)
|
|
20706
|
+
});
|
|
20707
|
+
if (!res.ok) {
|
|
20708
|
+
return { results: [], degraded: true, guidance: DEGRADED_GUIDANCE };
|
|
20709
|
+
}
|
|
20710
|
+
html = await res.text();
|
|
20711
|
+
} catch {
|
|
20712
|
+
return { results: [], degraded: true, guidance: DEGRADED_GUIDANCE };
|
|
20713
|
+
}
|
|
20714
|
+
const results = parseDuckDuckGoHtml(html);
|
|
20715
|
+
if (results.length === 0) {
|
|
20716
|
+
return { results: [], degraded: true, guidance: DEGRADED_GUIDANCE };
|
|
20717
|
+
}
|
|
20718
|
+
return { results };
|
|
20719
|
+
}
|
|
20587
20720
|
var webSearchTool = {
|
|
20588
20721
|
name: "web_search",
|
|
20589
|
-
description: "Search the web
|
|
20722
|
+
description: "Search the web. Uses your configured Tavily/Firecrawl provider if any, otherwise a free best-effort search.",
|
|
20590
20723
|
inputSchema: WebSearchInputSchema,
|
|
20591
20724
|
riskLevel: "read",
|
|
20592
|
-
execute: async (
|
|
20593
|
-
|
|
20594
|
-
|
|
20595
|
-
|
|
20725
|
+
execute: async (input, ctx) => {
|
|
20726
|
+
if (ctx.searchBackend) {
|
|
20727
|
+
const { results } = await ctx.searchBackend(input.query);
|
|
20728
|
+
return { results };
|
|
20596
20729
|
}
|
|
20597
|
-
|
|
20730
|
+
return duckDuckGoSearch(input.query);
|
|
20598
20731
|
}
|
|
20599
20732
|
};
|
|
20600
20733
|
|
|
@@ -23519,6 +23652,7 @@ var ALWAYS_ON_TOOLS = [
|
|
|
23519
23652
|
"search_history",
|
|
23520
23653
|
"mark_memory_helpful",
|
|
23521
23654
|
"mark_memory_outdated",
|
|
23655
|
+
"web_search",
|
|
23522
23656
|
"dashboard_publish",
|
|
23523
23657
|
"file_read",
|
|
23524
23658
|
"file_write",
|
|
@@ -23536,6 +23670,7 @@ var ALWAYS_ON_TOOL_DOCS = [
|
|
|
23536
23670
|
{ name: searchHistoryTool.name, description: searchHistoryTool.description },
|
|
23537
23671
|
{ name: markMemoryHelpfulTool.name, description: markMemoryHelpfulTool.description },
|
|
23538
23672
|
{ name: markMemoryOutdatedTool.name, description: markMemoryOutdatedTool.description },
|
|
23673
|
+
{ name: webSearchTool.name, description: webSearchTool.description },
|
|
23539
23674
|
{ name: dashboardPublishTool.name, description: dashboardPublishTool.description },
|
|
23540
23675
|
{ name: fileReadTool.name, description: fileReadTool.description },
|
|
23541
23676
|
{ name: fileWriteTool.name, description: fileWriteTool.description },
|
|
@@ -27325,7 +27460,7 @@ function decodeBase64Url(encoded) {
|
|
|
27325
27460
|
return "";
|
|
27326
27461
|
}
|
|
27327
27462
|
}
|
|
27328
|
-
function
|
|
27463
|
+
function stripHtml2(html) {
|
|
27329
27464
|
return html.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<[^>]+>/g, " ").replace(/ /gi, " ").replace(/&/gi, "&").replace(/</gi, "<").replace(/>/gi, ">").replace(/"/gi, '"').replace(/'/gi, "'").replace(/[ \t]+/g, " ").replace(/\n\s*\n\s*\n/g, "\n\n").trim();
|
|
27330
27465
|
}
|
|
27331
27466
|
function decodePartBody(part) {
|
|
@@ -27340,7 +27475,7 @@ function extractTextFromPayload(payload) {
|
|
|
27340
27475
|
return decodePartBody(payload);
|
|
27341
27476
|
}
|
|
27342
27477
|
if (mime.startsWith("text/html")) {
|
|
27343
|
-
return
|
|
27478
|
+
return stripHtml2(decodePartBody(payload));
|
|
27344
27479
|
}
|
|
27345
27480
|
if (mime.startsWith("multipart/")) {
|
|
27346
27481
|
const parts = payload.parts ?? [];
|
|
@@ -42081,6 +42216,44 @@ var TOOL_PROVISIONING = {
|
|
|
42081
42216
|
},
|
|
42082
42217
|
encrypt
|
|
42083
42218
|
};
|
|
42219
|
+
async function resolveSearchBackend(db, agentId) {
|
|
42220
|
+
const rows = await db.select({
|
|
42221
|
+
slug: connectors.slug,
|
|
42222
|
+
apiKey: connectors.apiKey
|
|
42223
|
+
}).from(agentConnectorAssignments).innerJoin(connectors, eq5(connectors.id, agentConnectorAssignments.connectorId)).where(eq5(agentConnectorAssignments.agentId, agentId));
|
|
42224
|
+
for (const slug of ["tavily", "firecrawl"]) {
|
|
42225
|
+
const row = rows.find((r) => r.slug === slug);
|
|
42226
|
+
if (!row?.apiKey) continue;
|
|
42227
|
+
let key;
|
|
42228
|
+
try {
|
|
42229
|
+
key = decrypt(row.apiKey);
|
|
42230
|
+
} catch {
|
|
42231
|
+
continue;
|
|
42232
|
+
}
|
|
42233
|
+
const entry = ADAPTER_REGISTRY[slug];
|
|
42234
|
+
if (!entry) continue;
|
|
42235
|
+
const toolName = slug === "tavily" ? "tavily_search" : "firecrawl_search";
|
|
42236
|
+
const searchTool = entry.toolFactory(key).find((t) => t.name === toolName);
|
|
42237
|
+
if (!searchTool) continue;
|
|
42238
|
+
return async (query) => {
|
|
42239
|
+
const out = await searchTool.execute({ query }, {
|
|
42240
|
+
jobId: "",
|
|
42241
|
+
agentId,
|
|
42242
|
+
entityId: "",
|
|
42243
|
+
db,
|
|
42244
|
+
jobChatId: null
|
|
42245
|
+
});
|
|
42246
|
+
const results = (out.results ?? []).map((r) => ({
|
|
42247
|
+
title: r.title ?? "",
|
|
42248
|
+
url: r.url ?? "",
|
|
42249
|
+
// Tavily → `content`; Firecrawl → `description`.
|
|
42250
|
+
snippet: r.content ?? r.description ?? ""
|
|
42251
|
+
}));
|
|
42252
|
+
return { results };
|
|
42253
|
+
};
|
|
42254
|
+
}
|
|
42255
|
+
return void 0;
|
|
42256
|
+
}
|
|
42084
42257
|
var DELIVERY_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
42085
42258
|
"telegram_send_message",
|
|
42086
42259
|
"send_image",
|
|
@@ -42328,6 +42501,7 @@ async function runJob(jobId, deps, runnerEnv, opts) {
|
|
|
42328
42501
|
const scriptAuthorizedSkillSlugs = assignedSkillRows.filter((r) => r.scriptsAuthorized).map((r) => r.slug);
|
|
42329
42502
|
const fileWritableSkillSlugs = assignedSkillRows.filter((r) => r.filesWritable).map((r) => r.slug);
|
|
42330
42503
|
const skillStore = skillStoreDir(job.entityId);
|
|
42504
|
+
const searchBackend = await resolveSearchBackend(db, agentRow.id);
|
|
42331
42505
|
if (!agentRow.llmKeyId) {
|
|
42332
42506
|
await failJob(db, jobId, "agent_no_llm_configured", runStats(), messages);
|
|
42333
42507
|
return { status: "failed", error: "agent_no_llm_configured" };
|
|
@@ -42647,7 +42821,8 @@ async function runJob(jobId, deps, runnerEnv, opts) {
|
|
|
42647
42821
|
assignedSkillSlugs,
|
|
42648
42822
|
scriptAuthorizedSkillSlugs,
|
|
42649
42823
|
fileWritableSkillSlugs,
|
|
42650
|
-
provisioning: TOOL_PROVISIONING
|
|
42824
|
+
provisioning: TOOL_PROVISIONING,
|
|
42825
|
+
searchBackend
|
|
42651
42826
|
},
|
|
42652
42827
|
{
|
|
42653
42828
|
approvalRules: resumeApprovalRules,
|
|
@@ -43059,7 +43234,8 @@ async function runJob(jobId, deps, runnerEnv, opts) {
|
|
|
43059
43234
|
assignedSkillSlugs,
|
|
43060
43235
|
scriptAuthorizedSkillSlugs,
|
|
43061
43236
|
fileWritableSkillSlugs,
|
|
43062
|
-
provisioning: TOOL_PROVISIONING
|
|
43237
|
+
provisioning: TOOL_PROVISIONING,
|
|
43238
|
+
searchBackend
|
|
43063
43239
|
};
|
|
43064
43240
|
const sharedToolOpts = {
|
|
43065
43241
|
approvalRules: approvalRuleList,
|
|
@@ -43190,7 +43366,8 @@ async function runJob(jobId, deps, runnerEnv, opts) {
|
|
|
43190
43366
|
assignedSkillSlugs,
|
|
43191
43367
|
scriptAuthorizedSkillSlugs,
|
|
43192
43368
|
fileWritableSkillSlugs,
|
|
43193
|
-
provisioning: TOOL_PROVISIONING
|
|
43369
|
+
provisioning: TOOL_PROVISIONING,
|
|
43370
|
+
searchBackend
|
|
43194
43371
|
},
|
|
43195
43372
|
{
|
|
43196
43373
|
approvalRules: approvalRuleList,
|
|
@@ -44495,10 +44672,15 @@ var EditMemoryArgs = z113.object({
|
|
|
44495
44672
|
var ArchiveMemoryArgs = z113.object({
|
|
44496
44673
|
memoryId: z113.string().uuid("memoryId must be a valid UUID.")
|
|
44497
44674
|
});
|
|
44675
|
+
var SetImportanceArgs = z113.object({
|
|
44676
|
+
memoryId: z113.string().uuid("memoryId must be a valid UUID."),
|
|
44677
|
+
importance: z113.number().int().min(1).max(5),
|
|
44678
|
+
reason: z113.string().min(1).max(200)
|
|
44679
|
+
});
|
|
44498
44680
|
function buildSystemPrompt2(candidateList) {
|
|
44499
44681
|
return `You are the MEMORY CURATOR for this entity. Your job is to keep the durable-fact memory SMALL, DENSE, and NON-REDUNDANT, so the limited injection budget is spent on signal.
|
|
44500
44682
|
|
|
44501
|
-
CANDIDATE FACTS (id, source, importance, length, text):
|
|
44683
|
+
CANDIDATE FACTS (id, source, importance, access_count, length, text):
|
|
44502
44684
|
${candidateList}
|
|
44503
44685
|
|
|
44504
44686
|
WHAT A GOOD FACT IS: ONE short, durable statement (a preference, an ID, a convention, a learned rule) \u2014 typically under ${HEALTHY_FACT_CHARS} characters. NOT a document, a raw dump, or a session log.
|
|
@@ -44507,12 +44689,14 @@ ACTIONS:
|
|
|
44507
44689
|
1. DISTILL oversized facts \u2014 if a fact is a big blob (a pasted workflow/JSON, a long log, a transcript), call edit_memory to replace it with just the key statement + a pointer to where the artifact actually lives (e.g. "... saved in workspace file workflows/X.json"). Keep the SIGNAL, drop the bulk.
|
|
44508
44690
|
2. MERGE near-duplicates \u2014 when several facts say the same thing, edit_memory ONE of them to the best consolidated wording, then archive_memory the others.
|
|
44509
44691
|
3. ARCHIVE the clearly obsolete or contradictory.
|
|
44692
|
+
4. RE-SCORE importance \u2014 a fact's initial importance is just the authoring agent's one-shot estimate. access_count is PROVEN usage, and it is more reliable than that estimate. Call set_importance to RAISE the importance of a durable fact that is accessed often (it clearly matters), and to LOWER the importance of a fact that has never been used despite being around a while (it probably doesn't). Give a short reason.
|
|
44510
44693
|
|
|
44511
44694
|
HARD RULES:
|
|
44512
|
-
- You may ONLY edit/archive AGENT-authored facts. The tools REFUSE source='manual' (user-entered) \u2014 do NOT attempt them, and do not retry a refused call.
|
|
44695
|
+
- You may ONLY edit/archive/re-score AGENT-authored facts. The tools REFUSE source='manual' (user-entered) \u2014 do NOT attempt them, and do not retry a refused call.
|
|
44513
44696
|
- Archiving is the maximum action \u2014 it is reversible; never invent or fabricate facts.
|
|
44514
44697
|
- When a fact is already a clean short statement, LEAVE IT. A no-op pass is correct and common on a tidy pool.
|
|
44515
|
-
- Do NOT merge facts that are merely related but distinct \u2014 only true duplicates
|
|
44698
|
+
- Do NOT merge facts that are merely related but distinct \u2014 only true duplicates.
|
|
44699
|
+
- Do NOT re-score every fact out of habit \u2014 only when access_count clearly disagrees with the current importance.`;
|
|
44516
44700
|
}
|
|
44517
44701
|
function renderCandidates(facts) {
|
|
44518
44702
|
if (facts.length === 0) return "(none)";
|
|
@@ -44520,7 +44704,7 @@ function renderCandidates(facts) {
|
|
|
44520
44704
|
const len = f.fact.length;
|
|
44521
44705
|
const oversized = len > HEALTHY_FACT_CHARS ? " OVERSIZED" : "";
|
|
44522
44706
|
const text22 = len > 200 ? `${f.fact.slice(0, 200)}\u2026` : f.fact;
|
|
44523
|
-
return `- id=${f.id} source=${f.source ?? "agent"} importance=${f.importance ?? 3} len=${len}${oversized}
|
|
44707
|
+
return `- id=${f.id} source=${f.source ?? "agent"} importance=${f.importance ?? 3} access_count=${f.accessCount ?? 0} len=${len}${oversized}
|
|
44524
44708
|
${text22.replace(/\s+/g, " ")}`;
|
|
44525
44709
|
}).join("\n");
|
|
44526
44710
|
}
|
|
@@ -44529,9 +44713,10 @@ async function runMemoryCuration(db, entityId, maxTurns, reflectionModel) {
|
|
|
44529
44713
|
id: agentMemory.id,
|
|
44530
44714
|
fact: agentMemory.fact,
|
|
44531
44715
|
source: agentMemory.source,
|
|
44532
|
-
importance: agentMemory.importance
|
|
44716
|
+
importance: agentMemory.importance,
|
|
44717
|
+
accessCount: agentMemory.accessCount
|
|
44533
44718
|
}).from(agentMemory).where(and3(eq5(agentMemory.entityId, entityId), eq5(agentMemory.archived, false))).limit(MAX_CANDIDATES2);
|
|
44534
|
-
if (candidates.length === 0) return { edited: 0, archived: 0, turns: 0 };
|
|
44719
|
+
if (candidates.length === 0) return { edited: 0, archived: 0, rescored: 0, turns: 0 };
|
|
44535
44720
|
const agentRows = await db.select({
|
|
44536
44721
|
llmKeyId: agents.llmKeyId,
|
|
44537
44722
|
fallbackChain: agents.fallbackChain,
|
|
@@ -44554,7 +44739,7 @@ async function runMemoryCuration(db, entityId, maxTurns, reflectionModel) {
|
|
|
44554
44739
|
}
|
|
44555
44740
|
if (!resolved?.ok) {
|
|
44556
44741
|
console.warn(`${TRACE} no LLM client for entity ${entityId}, skipping`);
|
|
44557
|
-
return { edited: 0, archived: 0, turns: 0 };
|
|
44742
|
+
return { edited: 0, archived: 0, rescored: 0, turns: 0 };
|
|
44558
44743
|
}
|
|
44559
44744
|
const llmClient = resolved.client;
|
|
44560
44745
|
const systemPrompt = buildSystemPrompt2(renderCandidates(candidates));
|
|
@@ -44572,10 +44757,15 @@ async function runMemoryCuration(db, entityId, maxTurns, reflectionModel) {
|
|
|
44572
44757
|
archive_memory: {
|
|
44573
44758
|
description: "Soft-archive a redundant/obsolete agent-authored fact (reversible). Provide the memory UUID. Refuses user-entered (manual) facts.",
|
|
44574
44759
|
inputSchema: ArchiveMemoryArgs
|
|
44760
|
+
},
|
|
44761
|
+
set_importance: {
|
|
44762
|
+
description: "Re-score a fact's importance (1-5) from REAL usage (access_count) + relevance. Raise for frequently-accessed durable facts; lower for never-used ones. Refuses user-entered (manual) facts.",
|
|
44763
|
+
inputSchema: SetImportanceArgs
|
|
44575
44764
|
}
|
|
44576
44765
|
};
|
|
44577
44766
|
let edited = 0;
|
|
44578
44767
|
let archived = 0;
|
|
44768
|
+
let rescored = 0;
|
|
44579
44769
|
let turns = 0;
|
|
44580
44770
|
console.warn(`${TRACE} start`, { entityId, candidateCount: candidates.length });
|
|
44581
44771
|
for (let turn = 0; turn < maxTurns; turn += 1) {
|
|
@@ -44633,6 +44823,24 @@ async function runMemoryCuration(db, entityId, maxTurns, reflectionModel) {
|
|
|
44633
44823
|
outcome = `archived memory ${parsed.data.memoryId}`;
|
|
44634
44824
|
}
|
|
44635
44825
|
}
|
|
44826
|
+
} else if (tc.toolName === "set_importance") {
|
|
44827
|
+
const parsed = SetImportanceArgs.safeParse(tc.input);
|
|
44828
|
+
if (!parsed.success) {
|
|
44829
|
+
outcome = `error: invalid_input: ${parsed.error.message}`;
|
|
44830
|
+
} else {
|
|
44831
|
+
const res = await updateAgentMemoryImportance(
|
|
44832
|
+
db,
|
|
44833
|
+
entityId,
|
|
44834
|
+
parsed.data.memoryId,
|
|
44835
|
+
parsed.data.importance
|
|
44836
|
+
);
|
|
44837
|
+
if ("error" in res) {
|
|
44838
|
+
outcome = res.error === "not_agent_memory" ? `error: memory ${parsed.data.memoryId} is user-entered and cannot be re-scored by the curator.` : res.error === "invalid_importance" ? `error: importance must be an integer 1-5.` : res.error === "importance_locked" ? `error: memory ${parsed.data.memoryId} importance is user-locked and cannot be re-scored.` : `error: memory ${parsed.data.memoryId} not found in this entity.`;
|
|
44839
|
+
} else {
|
|
44840
|
+
rescored += 1;
|
|
44841
|
+
outcome = `set importance of memory ${parsed.data.memoryId} to ${parsed.data.importance} (${parsed.data.reason})`;
|
|
44842
|
+
}
|
|
44843
|
+
}
|
|
44636
44844
|
} else {
|
|
44637
44845
|
outcome = `error: unknown tool ${tc.toolName}`;
|
|
44638
44846
|
}
|
|
@@ -44645,8 +44853,8 @@ async function runMemoryCuration(db, entityId, maxTurns, reflectionModel) {
|
|
|
44645
44853
|
}
|
|
44646
44854
|
messages.push({ role: "tool", content: resultParts });
|
|
44647
44855
|
}
|
|
44648
|
-
console.warn(`${TRACE} done`, { entityId, edited, archived, turns });
|
|
44649
|
-
return { edited, archived, turns };
|
|
44856
|
+
console.warn(`${TRACE} done`, { entityId, edited, archived, rescored, turns });
|
|
44857
|
+
return { edited, archived, rescored, turns };
|
|
44650
44858
|
}
|
|
44651
44859
|
|
|
44652
44860
|
// src/cron/run-curator.ts
|
|
@@ -44661,6 +44869,11 @@ var SAFE_LIFECYCLE_DEFAULTS = {
|
|
|
44661
44869
|
CURATOR_MEMORY_IMPORTANCE_MAX: 2,
|
|
44662
44870
|
CURATOR_MEMORY_MIN: 8,
|
|
44663
44871
|
REFLECTION_ENABLED: "false",
|
|
44872
|
+
// Conservative fallback for environments without DATABASE_URL (test envs) —
|
|
44873
|
+
// mirrors REFLECTION_ENABLED's safe default. The REAL production default
|
|
44874
|
+
// (per-entity decides, ON since entities.memory_curation_enabled defaults
|
|
44875
|
+
// true) comes from the zod schema in env.ts, not from this fallback.
|
|
44876
|
+
MEMORY_CURATION_ENABLED: "false",
|
|
44664
44877
|
REFLECTION_MODEL: void 0
|
|
44665
44878
|
};
|
|
44666
44879
|
function resolveCuratorEnv(runnerEnv) {
|
|
@@ -44677,6 +44890,7 @@ function resolveCuratorEnv(runnerEnv) {
|
|
|
44677
44890
|
CURATOR_MEMORY_IMPORTANCE_MAX: env.CURATOR_MEMORY_IMPORTANCE_MAX,
|
|
44678
44891
|
CURATOR_MEMORY_MIN: env.CURATOR_MEMORY_MIN,
|
|
44679
44892
|
REFLECTION_ENABLED: env.REFLECTION_ENABLED,
|
|
44893
|
+
MEMORY_CURATION_ENABLED: env.MEMORY_CURATION_ENABLED,
|
|
44680
44894
|
REFLECTION_MODEL: env.REFLECTION_MODEL
|
|
44681
44895
|
};
|
|
44682
44896
|
} catch {
|
|
@@ -44693,7 +44907,26 @@ async function runCuratorTick(db, _deps, runnerEnv) {
|
|
|
44693
44907
|
staleDays: e.CURATOR_MEMORY_STALE_DAYS,
|
|
44694
44908
|
importanceMax: e.CURATOR_MEMORY_IMPORTANCE_MAX
|
|
44695
44909
|
});
|
|
44696
|
-
|
|
44910
|
+
const intervalMs = e.CURATOR_INTERVAL_DAYS * 24 * 60 * 60 * 1e3;
|
|
44911
|
+
const intervalCutoff = new Date(Date.now() - intervalMs);
|
|
44912
|
+
const now = /* @__PURE__ */ new Date();
|
|
44913
|
+
const skillSet = /* @__PURE__ */ new Set();
|
|
44914
|
+
if (e.REFLECTION_ENABLED !== "false") {
|
|
44915
|
+
const skillCandidates = await db.select({ entityId: agentSkills.entityId }).from(agentSkills).innerJoin(entities, eq5(entities.id, agentSkills.entityId)).where(
|
|
44916
|
+
and3(
|
|
44917
|
+
eq5(agentSkills.createdBy, "agent"),
|
|
44918
|
+
eq5(agentSkills.state, "active"),
|
|
44919
|
+
eq5(entities.reflectionEnabled, true)
|
|
44920
|
+
)
|
|
44921
|
+
).groupBy(agentSkills.entityId).having(sql18`count(${agentSkills.id}) >= ${e.CURATOR_MIN_SKILLS}`);
|
|
44922
|
+
for (const c of skillCandidates) if (c.entityId) skillSet.add(c.entityId);
|
|
44923
|
+
}
|
|
44924
|
+
const memorySet = /* @__PURE__ */ new Set();
|
|
44925
|
+
if (e.MEMORY_CURATION_ENABLED !== "false") {
|
|
44926
|
+
const memoryCandidates = await db.select({ entityId: agentMemory.entityId }).from(agentMemory).innerJoin(entities, eq5(entities.id, agentMemory.entityId)).where(and3(eq5(agentMemory.archived, false), eq5(entities.memoryCurationEnabled, true))).groupBy(agentMemory.entityId).having(sql18`count(${agentMemory.id}) >= ${e.CURATOR_MEMORY_MIN}`);
|
|
44927
|
+
for (const c of memoryCandidates) if (c.entityId) memorySet.add(c.entityId);
|
|
44928
|
+
}
|
|
44929
|
+
if (skillSet.size === 0 && memorySet.size === 0) {
|
|
44697
44930
|
return {
|
|
44698
44931
|
staled: lifecycle.staled,
|
|
44699
44932
|
archived: lifecycle.archived,
|
|
@@ -44704,21 +44937,6 @@ async function runCuratorTick(db, _deps, runnerEnv) {
|
|
|
44704
44937
|
memoryCurationRan: 0
|
|
44705
44938
|
};
|
|
44706
44939
|
}
|
|
44707
|
-
const intervalMs = e.CURATOR_INTERVAL_DAYS * 24 * 60 * 60 * 1e3;
|
|
44708
|
-
const intervalCutoff = new Date(Date.now() - intervalMs);
|
|
44709
|
-
const now = /* @__PURE__ */ new Date();
|
|
44710
|
-
const skillCandidates = await db.select({ entityId: agentSkills.entityId }).from(agentSkills).innerJoin(entities, eq5(entities.id, agentSkills.entityId)).where(
|
|
44711
|
-
and3(
|
|
44712
|
-
eq5(agentSkills.createdBy, "agent"),
|
|
44713
|
-
eq5(agentSkills.state, "active"),
|
|
44714
|
-
eq5(entities.reflectionEnabled, true)
|
|
44715
|
-
)
|
|
44716
|
-
).groupBy(agentSkills.entityId).having(sql18`count(${agentSkills.id}) >= ${e.CURATOR_MIN_SKILLS}`);
|
|
44717
|
-
const memoryCandidates = await db.select({ entityId: agentMemory.entityId }).from(agentMemory).innerJoin(entities, eq5(entities.id, agentMemory.entityId)).where(and3(eq5(agentMemory.archived, false), eq5(entities.reflectionEnabled, true))).groupBy(agentMemory.entityId).having(sql18`count(${agentMemory.id}) >= ${e.CURATOR_MEMORY_MIN}`);
|
|
44718
|
-
const skillSet = /* @__PURE__ */ new Set();
|
|
44719
|
-
for (const c of skillCandidates) if (c.entityId) skillSet.add(c.entityId);
|
|
44720
|
-
const memorySet = /* @__PURE__ */ new Set();
|
|
44721
|
-
for (const c of memoryCandidates) if (c.entityId) memorySet.add(c.entityId);
|
|
44722
44940
|
let consolidationDeferred = 0;
|
|
44723
44941
|
let consolidationRan = 0;
|
|
44724
44942
|
let memoryCurationRan = 0;
|
package/web/.next/BUILD_ID
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
KFac7DQ7gdp9XPQ2bnb-2
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
"/(dashboard)/agents/[id]/edit/page": "/agents/[id]/edit",
|
|
13
13
|
"/(dashboard)/agents/[id]/telegram/page": "/agents/[id]/telegram",
|
|
14
14
|
"/(dashboard)/agents/page": "/agents",
|
|
15
|
-
"/(dashboard)/approvals/page": "/approvals",
|
|
16
15
|
"/(dashboard)/automations/page": "/automations",
|
|
16
|
+
"/(dashboard)/approvals/page": "/approvals",
|
|
17
17
|
"/(dashboard)/chat/page": "/chat",
|
|
18
18
|
"/(dashboard)/connectors/page": "/connectors",
|
|
19
19
|
"/(dashboard)/credentials/page": "/credentials",
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"/(dashboard)/logs/page": "/logs",
|
|
25
25
|
"/(dashboard)/mcp/page": "/mcp",
|
|
26
26
|
"/(dashboard)/memories/page": "/memories",
|
|
27
|
+
"/(dashboard)/settings/page": "/settings",
|
|
27
28
|
"/(dashboard)/settings/root-context/page": "/settings/root-context",
|
|
28
29
|
"/(dashboard)/page": "/",
|
|
29
|
-
"/(dashboard)/settings/page": "/settings",
|
|
30
30
|
"/(dashboard)/skills/[id]/edit/page": "/skills/[id]/edit",
|
|
31
31
|
"/(dashboard)/skills/page": "/skills"
|
|
32
32
|
}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
],
|
|
5
5
|
"devFiles": [],
|
|
6
6
|
"lowPriorityFiles": [
|
|
7
|
-
"static/
|
|
8
|
-
"static/
|
|
7
|
+
"static/KFac7DQ7gdp9XPQ2bnb-2/_buildManifest.js",
|
|
8
|
+
"static/KFac7DQ7gdp9XPQ2bnb-2/_ssgManifest.js"
|
|
9
9
|
],
|
|
10
10
|
"rootMainFiles": [
|
|
11
11
|
"static/chunks/webpack-3741bf0a7636d65e.js",
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
"dynamicRoutes": {},
|
|
55
55
|
"notFoundRoutes": [],
|
|
56
56
|
"preview": {
|
|
57
|
-
"previewModeId": "
|
|
58
|
-
"previewModeSigningKey": "
|
|
59
|
-
"previewModeEncryptionKey": "
|
|
57
|
+
"previewModeId": "9ca9c6c94be2ac6b133cc8186cc3e575",
|
|
58
|
+
"previewModeSigningKey": "e0be6b0a50047a39c91cb1f3c0c956afeeca98f59c75c182395022d820c862a6",
|
|
59
|
+
"previewModeEncryptionKey": "8eb9a91dc33886853caf249132d193b1db18b4ad8b66413c2343f5f4a4c31d37"
|
|
60
60
|
}
|
|
61
61
|
}
|