@desplega.ai/agent-swarm 1.92.1 → 1.93.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/openapi.json +63 -3
- package/package.json +5 -5
- package/src/be/db.ts +180 -6
- package/src/be/memory/boot-reembed.ts +84 -0
- package/src/be/memory/constants.ts +42 -1
- package/src/be/memory/providers/openai-embedding.ts +13 -0
- package/src/be/memory/providers/sqlite-store.ts +75 -26
- package/src/be/memory/raters/llm-client.ts +12 -5
- package/src/be/memory/reranker.ts +35 -17
- package/src/be/memory/types.ts +11 -0
- package/src/be/migrations/088_script_runs_list_indexes.sql +10 -0
- package/src/be/migrations/089_harness_variant.sql +2 -0
- package/src/be/modelsdev-cache.json +6478 -3099
- package/src/be/seed-pricing.ts +1 -0
- package/src/be/seed-scripts/catalog/boot-triage.inline.ts +221 -0
- package/src/be/seed-scripts/catalog/catalog-report.inline.ts +457 -0
- package/src/be/seed-scripts/catalog/compound-insights.inline.ts +863 -0
- package/src/be/seed-scripts/catalog/compound-insights.ts +371 -0
- package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +506 -0
- package/src/be/seed-scripts/index.ts +5 -5
- package/src/be/skill-sync.ts +28 -179
- package/src/commands/runner.ts +124 -7
- package/src/http/api-keys.ts +42 -0
- package/src/http/index.ts +9 -0
- package/src/http/mcp-bridge.ts +1 -1
- package/src/http/memory.ts +27 -24
- package/src/http/tasks.ts +10 -6
- package/src/providers/claude-adapter.ts +33 -1
- package/src/providers/claude-managed-adapter.ts +3 -0
- package/src/providers/claude-managed-models.ts +7 -0
- package/src/providers/codex-adapter.ts +8 -1
- package/src/providers/codex-models.ts +1 -0
- package/src/providers/codex-oauth/auth-json.ts +1 -0
- package/src/providers/harness-version.ts +7 -0
- package/src/providers/opencode-adapter.ts +11 -4
- package/src/providers/pi-mono-adapter.ts +12 -2
- package/src/providers/types.ts +2 -0
- package/src/scripts-runtime/egress-secrets.ts +83 -0
- package/src/scripts-runtime/eval-harness.ts +4 -0
- package/src/scripts-runtime/executors/types.ts +7 -0
- package/src/scripts-runtime/loader.ts +2 -0
- package/src/server-user.ts +2 -2
- package/src/slack/channel-join.ts +41 -0
- package/src/tasks/worker-follow-up.ts +12 -0
- package/src/tests/additive-buffer.test.ts +0 -1
- package/src/tests/api-key-tracking.test.ts +113 -0
- package/src/tests/approval-requests.test.ts +0 -6
- package/src/tests/claude-managed-setup.test.ts +0 -4
- package/src/tests/codex-pool.test.ts +2 -6
- package/src/tests/http-api-integration.test.ts +4 -6
- package/src/tests/memory-e2e.test.ts +6 -6
- package/src/tests/memory-edges.test.ts +0 -2
- package/src/tests/memory-rate-endpoint.test.ts +0 -2
- package/src/tests/memory-rater-e2e.test.ts +4 -7
- package/src/tests/memory-reranker.test.ts +135 -124
- package/src/tests/memory-store.test.ts +19 -1
- package/src/tests/memory.test.ts +64 -12
- package/src/tests/model-control.test.ts +1 -1
- package/src/tests/reload-config.test.ts +33 -17
- package/src/tests/runner-skills-refresh.test.ts +216 -46
- package/src/tests/script-runs-http.test.ts +7 -1
- package/src/tests/scripts-runtime-secret-egress.test.ts +129 -0
- package/src/tests/seed-scripts.test.ts +218 -1
- package/src/tests/session-attach.test.ts +6 -6
- package/src/tests/skill-fs-writer.test.ts +250 -0
- package/src/tests/slack-attachments-block.test.ts +0 -1
- package/src/tests/slack-blocks.test.ts +0 -1
- package/src/tests/slack-channel-join.test.ts +80 -0
- package/src/tests/slack-identity-resolution.test.ts +0 -1
- package/src/tests/structured-output.test.ts +0 -2
- package/src/tests/task-cascade-fail.test.ts +304 -0
- package/src/tests/use-dismissible-card.test.ts +0 -4
- package/src/tools/schedules/create-schedule.ts +2 -2
- package/src/tools/schedules/update-schedule.ts +1 -1
- package/src/tools/send-task.ts +2 -2
- package/src/tools/slack-post.ts +18 -15
- package/src/tools/slack-read.ts +9 -11
- package/src/tools/slack-reply.ts +18 -15
- package/src/tools/slack-start-thread.ts +17 -14
- package/src/tools/task-action.ts +2 -2
- package/src/types.ts +11 -0
- package/src/utils/context-window.ts +3 -0
- package/src/utils/credentials.ts +22 -2
- package/src/utils/skill-fs-writer.ts +220 -0
- package/src/utils/skills-refresh.ts +123 -40
- package/templates/workflows/llm-safe-release-context/config.json +13 -0
- package/templates/workflows/llm-safe-release-context/content.md +69 -0
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { getDb, isSqliteVecAvailable } from "@/be/db";
|
|
2
2
|
import { cosineSimilarity, deserializeEmbedding, serializeEmbedding } from "@/be/embedding";
|
|
3
3
|
import type { AgentMemory, AgentMemoryScope, AgentMemorySource } from "@/types";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
EMBEDDING_DIMENSIONS,
|
|
6
|
+
MIN_SIMILARITY,
|
|
7
|
+
PROTECTED_SOURCES,
|
|
8
|
+
TTL_DEFAULTS,
|
|
9
|
+
} from "../constants";
|
|
5
10
|
import type {
|
|
6
11
|
MemoryCandidate,
|
|
7
12
|
MemoryHealth,
|
|
@@ -400,6 +405,7 @@ export class SqliteMemoryStore implements MemoryStore {
|
|
|
400
405
|
const candidates: MemoryCandidate[] = [];
|
|
401
406
|
for (const row of rows) {
|
|
402
407
|
const similarity = 1 - row.distance;
|
|
408
|
+
if (similarity < MIN_SIMILARITY) continue;
|
|
403
409
|
candidates.push(rowToCandidate(row, similarity));
|
|
404
410
|
}
|
|
405
411
|
|
|
@@ -446,6 +452,7 @@ export class SqliteMemoryStore implements MemoryStore {
|
|
|
446
452
|
const emb = deserializeEmbedding(row.embedding);
|
|
447
453
|
if (emb.length !== queryEmbedding.length) continue;
|
|
448
454
|
const similarity = cosineSimilarity(queryEmbedding, emb);
|
|
455
|
+
if (similarity < MIN_SIMILARITY) continue;
|
|
449
456
|
candidates.push(rowToCandidate(row, similarity));
|
|
450
457
|
}
|
|
451
458
|
|
|
@@ -481,29 +488,19 @@ export class SqliteMemoryStore implements MemoryStore {
|
|
|
481
488
|
}
|
|
482
489
|
}
|
|
483
490
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
491
|
+
private buildListWhereClause(
|
|
492
|
+
agentId: string,
|
|
493
|
+
options: MemoryListOptions,
|
|
494
|
+
): { whereClause: string; params: (Buffer | string | number | null)[] } {
|
|
495
|
+
const { scope = "all", isLead = false, ownerAgentId, source, sourcePath } = options;
|
|
488
496
|
const conditions: string[] = [];
|
|
489
|
-
const params: (string | number)[] = [];
|
|
497
|
+
const params: (Buffer | string | number | null)[] = [];
|
|
490
498
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
conditions.push("scope = 'swarm'");
|
|
497
|
-
} else {
|
|
498
|
-
conditions.push("(agentId = ? OR scope = 'swarm')");
|
|
499
|
-
params.push(agentId);
|
|
500
|
-
}
|
|
501
|
-
} else {
|
|
502
|
-
if (scope === "agent") {
|
|
503
|
-
conditions.push("scope = 'agent'");
|
|
504
|
-
} else if (scope === "swarm") {
|
|
505
|
-
conditions.push("scope = 'swarm'");
|
|
506
|
-
}
|
|
499
|
+
this.addScopeConditions(conditions, params, agentId, scope, isLead);
|
|
500
|
+
|
|
501
|
+
if (ownerAgentId) {
|
|
502
|
+
conditions.push("agentId = ?");
|
|
503
|
+
params.push(ownerAgentId);
|
|
507
504
|
}
|
|
508
505
|
|
|
509
506
|
if (source) {
|
|
@@ -511,18 +508,70 @@ export class SqliteMemoryStore implements MemoryStore {
|
|
|
511
508
|
params.push(source);
|
|
512
509
|
}
|
|
513
510
|
|
|
514
|
-
const
|
|
515
|
-
|
|
511
|
+
const sourcePathNeedle = sourcePath?.trim().toLowerCase();
|
|
512
|
+
if (sourcePathNeedle) {
|
|
513
|
+
conditions.push("instr(lower(coalesce(sourcePath, '')), ?) > 0");
|
|
514
|
+
params.push(sourcePathNeedle);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
return {
|
|
518
|
+
whereClause: conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "",
|
|
519
|
+
params,
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
list(agentId: string, options: MemoryListOptions = {}): AgentMemory[] {
|
|
524
|
+
const { limit = 20, offset = 0 } = options;
|
|
525
|
+
const db = getDb();
|
|
526
|
+
const { whereClause, params } = this.buildListWhereClause(agentId, options);
|
|
527
|
+
const queryParams = [...params, limit, offset];
|
|
516
528
|
|
|
517
529
|
const rows = db
|
|
518
|
-
.prepare<AgentMemoryRow, (string | number)[]>(
|
|
530
|
+
.prepare<AgentMemoryRow, (Buffer | string | number | null)[]>(
|
|
519
531
|
`SELECT * FROM agent_memory ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
|
|
520
532
|
)
|
|
521
|
-
.all(...
|
|
533
|
+
.all(...queryParams);
|
|
522
534
|
|
|
523
535
|
return rows.map(rowToAgentMemory);
|
|
524
536
|
}
|
|
525
537
|
|
|
538
|
+
count(agentId: string, options: MemoryListOptions = {}): number {
|
|
539
|
+
const db = getDb();
|
|
540
|
+
const { whereClause, params } = this.buildListWhereClause(agentId, options);
|
|
541
|
+
const row = db
|
|
542
|
+
.prepare<{ count: number }, (Buffer | string | number | null)[]>(
|
|
543
|
+
`SELECT COUNT(*) AS count FROM agent_memory ${whereClause}`,
|
|
544
|
+
)
|
|
545
|
+
.get(...params);
|
|
546
|
+
|
|
547
|
+
return row?.count ?? 0;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
isSourceProtected(source: AgentMemorySource): boolean {
|
|
551
|
+
return PROTECTED_SOURCES.has(source);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
listForCuration(
|
|
555
|
+
agentId?: string,
|
|
556
|
+
): { id: string; source: string; name: string; createdAt: string }[] {
|
|
557
|
+
const db = getDb();
|
|
558
|
+
const protectedList = [...PROTECTED_SOURCES].map((s) => `'${s}'`).join(",");
|
|
559
|
+
if (agentId) {
|
|
560
|
+
return db
|
|
561
|
+
.prepare<{ id: string; source: string; name: string; createdAt: string }, [string]>(
|
|
562
|
+
`SELECT id, source, name, createdAt FROM agent_memory
|
|
563
|
+
WHERE agentId = ? AND source NOT IN (${protectedList})`,
|
|
564
|
+
)
|
|
565
|
+
.all(agentId);
|
|
566
|
+
}
|
|
567
|
+
return db
|
|
568
|
+
.prepare<{ id: string; source: string; name: string; createdAt: string }, []>(
|
|
569
|
+
`SELECT id, source, name, createdAt FROM agent_memory
|
|
570
|
+
WHERE source NOT IN (${protectedList})`,
|
|
571
|
+
)
|
|
572
|
+
.all();
|
|
573
|
+
}
|
|
574
|
+
|
|
526
575
|
listForReembedding(options?: { agentId?: string }): { id: string; content: string }[] {
|
|
527
576
|
const db = getDb();
|
|
528
577
|
if (options?.agentId) {
|
|
@@ -75,6 +75,13 @@ AGENT RESPONSE / SUMMARY:
|
|
|
75
75
|
|
|
76
76
|
Score 0..1.`;
|
|
77
77
|
|
|
78
|
+
const PLACEHOLDER_PREFIX = "$";
|
|
79
|
+
const QUERY_PLACEHOLDER = `${PLACEHOLDER_PREFIX}{query}`;
|
|
80
|
+
const MEMORY_ID_PLACEHOLDER = `${PLACEHOLDER_PREFIX}{memoryId}`;
|
|
81
|
+
const MEMORY_NAME_PLACEHOLDER = `${PLACEHOLDER_PREFIX}{memoryName}`;
|
|
82
|
+
const MEMORY_CONTENT_PLACEHOLDER = `${PLACEHOLDER_PREFIX}{memoryContent}`;
|
|
83
|
+
const RESPONSE_PLACEHOLDER = `${PLACEHOLDER_PREFIX}{response}`;
|
|
84
|
+
|
|
78
85
|
/**
|
|
79
86
|
* `claude -p --output-format json` returns a JSON envelope of the shape
|
|
80
87
|
* `{ result: string, ... }`. We parse the envelope, then JSON-parse the
|
|
@@ -83,11 +90,11 @@ Score 0..1.`;
|
|
|
83
90
|
type ClaudeCliEnvelope = { result?: unknown };
|
|
84
91
|
|
|
85
92
|
function buildPrompt(input: LlmRaterInput): string {
|
|
86
|
-
return PROMPT_TEMPLATE.replace(
|
|
87
|
-
.replace(
|
|
88
|
-
.replace(
|
|
89
|
-
.replace(
|
|
90
|
-
.replace(
|
|
93
|
+
return PROMPT_TEMPLATE.replace(QUERY_PLACEHOLDER, input.query)
|
|
94
|
+
.replace(MEMORY_ID_PLACEHOLDER, input.memory.id)
|
|
95
|
+
.replace(MEMORY_NAME_PLACEHOLDER, input.memory.name)
|
|
96
|
+
.replace(MEMORY_CONTENT_PLACEHOLDER, input.memory.content)
|
|
97
|
+
.replace(RESPONSE_PLACEHOLDER, input.response);
|
|
91
98
|
}
|
|
92
99
|
|
|
93
100
|
function parseScoreAndReasoning(raw: unknown): LlmRaterResult | null {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import type { AgentMemorySource } from "@/types";
|
|
1
2
|
import {
|
|
2
3
|
ACCESS_BOOST_MAX_MULTIPLIER,
|
|
3
4
|
ACCESS_BOOST_RECENCY_WINDOW_HOURS,
|
|
5
|
+
RECENCY_DECAY_HALF_LIFE,
|
|
4
6
|
RECENCY_DECAY_HALF_LIFE_DAYS,
|
|
7
|
+
SOURCE_QUALITY_MULTIPLIER,
|
|
5
8
|
} from "./constants";
|
|
6
9
|
import type { MemoryCandidate, RerankOptions } from "./types";
|
|
7
10
|
|
|
@@ -9,13 +12,16 @@ const MS_PER_DAY = 1000 * 60 * 60 * 24;
|
|
|
9
12
|
const MS_PER_HOUR = 1000 * 60 * 60;
|
|
10
13
|
|
|
11
14
|
/**
|
|
12
|
-
* Exponential decay based on age
|
|
13
|
-
*
|
|
15
|
+
* Exponential decay based on age and memory source.
|
|
16
|
+
* Source-aware: manual memories have no decay (Infinity half-life),
|
|
17
|
+
* file_index = 180d, task_completion = 14d, session_summary = 7d.
|
|
14
18
|
*/
|
|
15
|
-
export function recencyDecay(createdAt: string, now: Date): number {
|
|
19
|
+
export function recencyDecay(createdAt: string, now: Date, source?: AgentMemorySource): number {
|
|
20
|
+
const halfLife = source ? RECENCY_DECAY_HALF_LIFE[source] : RECENCY_DECAY_HALF_LIFE_DAYS;
|
|
21
|
+
if (!Number.isFinite(halfLife)) return 1.0;
|
|
16
22
|
const ageDays = (now.getTime() - new Date(createdAt).getTime()) / MS_PER_DAY;
|
|
17
23
|
if (ageDays <= 0) return 1.0;
|
|
18
|
-
return 2 ** (-ageDays /
|
|
24
|
+
return 2 ** (-ageDays / halfLife);
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
/**
|
|
@@ -31,6 +37,14 @@ export function accessBoost(accessedAt: string, accessCount: number, now: Date):
|
|
|
31
37
|
return boost;
|
|
32
38
|
}
|
|
33
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Source-quality multiplier. Manual memories get a 1.5× boost,
|
|
42
|
+
* session summaries get 0.5×. Unknown sources default to 1.0.
|
|
43
|
+
*/
|
|
44
|
+
export function sourceQuality(source: AgentMemorySource): number {
|
|
45
|
+
return SOURCE_QUALITY_MULTIPLIER[source] ?? 1.0;
|
|
46
|
+
}
|
|
47
|
+
|
|
34
48
|
/**
|
|
35
49
|
* Beta-Binomial usefulness factor for reranking.
|
|
36
50
|
*
|
|
@@ -56,33 +70,37 @@ export function usefulness(alpha: number, beta: number): number {
|
|
|
56
70
|
}
|
|
57
71
|
|
|
58
72
|
/**
|
|
59
|
-
* Final score combining similarity, recency decay, access boost,
|
|
60
|
-
* Beta-Binomial usefulness.
|
|
61
|
-
* MEMORY_DEMOTION_FLOOR=1.0, the usefulness factor is exactly 1.0 and this
|
|
62
|
-
* computation matches the pre-rater behaviour byte-for-byte.
|
|
63
|
-
*
|
|
64
|
-
* v2: optional edge-aware boost — see thoughts/taras/plans/2026-05-05-memory-rater-v1.5/root.md
|
|
73
|
+
* Final score combining similarity, recency decay, access boost,
|
|
74
|
+
* source quality, and Beta-Binomial usefulness.
|
|
65
75
|
*/
|
|
66
76
|
export function computeScore(candidate: MemoryCandidate, now: Date): number {
|
|
67
77
|
return (
|
|
68
78
|
candidate.similarity *
|
|
69
|
-
recencyDecay(candidate.createdAt, now) *
|
|
79
|
+
recencyDecay(candidate.createdAt, now, candidate.source) *
|
|
70
80
|
accessBoost(candidate.accessedAt, candidate.accessCount, now) *
|
|
81
|
+
sourceQuality(candidate.source) *
|
|
71
82
|
usefulness(candidate.alpha, candidate.beta)
|
|
72
83
|
);
|
|
73
84
|
}
|
|
74
85
|
|
|
75
86
|
/**
|
|
76
|
-
* Rerank candidates by combining similarity with recency
|
|
77
|
-
* Returns the top `limit` candidates sorted by
|
|
87
|
+
* Rerank candidates by combining similarity with recency, source quality,
|
|
88
|
+
* and access signals. Returns the top `limit` candidates sorted by composite
|
|
89
|
+
* score. Preserves raw similarity in `rawSimilarity` and sets `compositeScore`.
|
|
78
90
|
*/
|
|
79
91
|
export function rerank(candidates: MemoryCandidate[], options: RerankOptions): MemoryCandidate[] {
|
|
80
92
|
const { limit, now = new Date() } = options;
|
|
81
93
|
|
|
82
|
-
const scored = candidates.map((candidate) =>
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
94
|
+
const scored = candidates.map((candidate) => {
|
|
95
|
+
const rawSimilarity = candidate.similarity;
|
|
96
|
+
const compositeScore = computeScore(candidate, now);
|
|
97
|
+
return {
|
|
98
|
+
...candidate,
|
|
99
|
+
rawSimilarity,
|
|
100
|
+
compositeScore,
|
|
101
|
+
similarity: compositeScore,
|
|
102
|
+
};
|
|
103
|
+
});
|
|
86
104
|
|
|
87
105
|
scored.sort((a, b) => b.similarity - a.similarity);
|
|
88
106
|
return scored.slice(0, limit);
|
package/src/be/memory/types.ts
CHANGED
|
@@ -22,6 +22,11 @@ export interface MemoryStore {
|
|
|
22
22
|
peek(id: string): AgentMemory | null;
|
|
23
23
|
search(embedding: Float32Array, agentId: string, options: MemorySearchOptions): MemoryCandidate[];
|
|
24
24
|
list(agentId: string, options: MemoryListOptions): AgentMemory[];
|
|
25
|
+
count(agentId: string, options: MemoryListOptions): number;
|
|
26
|
+
isSourceProtected(source: AgentMemorySource): boolean;
|
|
27
|
+
listForCuration(
|
|
28
|
+
agentId?: string,
|
|
29
|
+
): { id: string; source: string; name: string; createdAt: string }[];
|
|
25
30
|
listForReembedding(options?: { agentId?: string }): { id: string; content: string }[];
|
|
26
31
|
delete(id: string): boolean;
|
|
27
32
|
deleteBySourcePath(sourcePath: string, agentId: string): number;
|
|
@@ -51,6 +56,10 @@ export interface MemoryInput {
|
|
|
51
56
|
|
|
52
57
|
export interface MemoryCandidate extends AgentMemory {
|
|
53
58
|
similarity: number;
|
|
59
|
+
/** Raw cosine similarity before reranking (preserved for diagnostics). */
|
|
60
|
+
rawSimilarity?: number;
|
|
61
|
+
/** Final composite score after reranking (recency × source × usefulness × access). */
|
|
62
|
+
compositeScore?: number;
|
|
54
63
|
accessCount: number;
|
|
55
64
|
expiresAt: string | null;
|
|
56
65
|
embeddingModel: string | null;
|
|
@@ -72,7 +81,9 @@ export interface MemoryListOptions {
|
|
|
72
81
|
limit?: number;
|
|
73
82
|
offset?: number;
|
|
74
83
|
isLead?: boolean;
|
|
84
|
+
ownerAgentId?: string;
|
|
75
85
|
source?: AgentMemorySource;
|
|
86
|
+
sourcePath?: string;
|
|
76
87
|
}
|
|
77
88
|
|
|
78
89
|
export interface MemoryStats {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
-- Keep script run list pages fast as historical rows accumulate.
|
|
2
|
+
|
|
3
|
+
CREATE INDEX IF NOT EXISTS idx_script_runs_startedAt
|
|
4
|
+
ON script_runs(startedAt DESC);
|
|
5
|
+
|
|
6
|
+
CREATE INDEX IF NOT EXISTS idx_script_runs_status_startedAt
|
|
7
|
+
ON script_runs(status, startedAt DESC);
|
|
8
|
+
|
|
9
|
+
CREATE INDEX IF NOT EXISTS idx_script_runs_agentId_startedAt
|
|
10
|
+
ON script_runs(agentId, startedAt DESC);
|