@tenex-chat/backend 0.9.4 → 0.9.6
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 +5 -1
- package/dist/daemon-wrapper.cjs +47 -0
- package/dist/index.js +59268 -0
- package/dist/wrapper.js +171 -0
- package/package.json +19 -27
- package/src/agents/AgentRegistry.ts +9 -7
- package/src/agents/AgentStorage.ts +24 -1
- package/src/agents/agent-installer.ts +6 -0
- package/src/agents/agent-loader.ts +7 -2
- package/src/agents/constants.ts +10 -2
- package/src/agents/execution/AgentExecutor.ts +35 -6
- package/src/agents/execution/StreamCallbacks.ts +53 -13
- package/src/agents/execution/StreamExecutionHandler.ts +110 -16
- package/src/agents/execution/StreamSetup.ts +19 -9
- package/src/agents/execution/ToolEventHandlers.ts +112 -0
- package/src/agents/role-categories.ts +53 -0
- package/src/agents/types/runtime.ts +7 -0
- package/src/agents/types/storage.ts +7 -0
- package/src/commands/agent/import/openclaw-distiller.ts +63 -7
- package/src/commands/agent/import/openclaw-reader.ts +54 -0
- package/src/commands/agent/import/openclaw.ts +120 -29
- package/src/commands/agent/index.ts +83 -2
- package/src/commands/setup/display.ts +123 -0
- package/src/commands/setup/embed.ts +13 -13
- package/src/commands/setup/global-system-prompt.ts +15 -17
- package/src/commands/setup/image.ts +17 -20
- package/src/commands/setup/interactive.ts +37 -20
- package/src/commands/setup/llm.ts +12 -7
- package/src/commands/setup/onboarding.ts +1580 -248
- package/src/commands/setup/providers.ts +3 -3
- package/src/conversations/ConversationStore.ts +23 -2
- package/src/conversations/MessageBuilder.ts +51 -73
- package/src/conversations/formatters/utils/conversation-transcript-formatter.ts +425 -0
- package/src/conversations/search/embeddings/ConversationEmbeddingService.ts +40 -98
- package/src/conversations/search/embeddings/ConversationIndexingJob.ts +40 -52
- package/src/conversations/services/ConversationSummarizer.ts +1 -2
- package/src/conversations/types.ts +11 -0
- package/src/daemon/Daemon.ts +78 -57
- package/src/daemon/ProjectRuntime.ts +6 -12
- package/src/daemon/SubscriptionManager.ts +13 -0
- package/src/daemon/index.ts +0 -1
- package/src/event-handler/index.ts +1 -0
- package/src/index.ts +20 -1
- package/src/llm/ChunkHandler.ts +1 -1
- package/src/llm/FinishHandler.ts +28 -4
- package/src/llm/LLMConfigEditor.ts +218 -106
- package/src/llm/index.ts +0 -4
- package/src/llm/meta/MetaModelResolver.ts +3 -18
- package/src/llm/middleware/message-sanitizer.ts +153 -0
- package/src/llm/providers/ollama-models.ts +0 -38
- package/src/llm/service.ts +50 -15
- package/src/llm/types.ts +0 -12
- package/src/llm/utils/ConfigurationManager.ts +88 -465
- package/src/llm/utils/ConfigurationTester.ts +42 -185
- package/src/llm/utils/ModelSelector.ts +156 -92
- package/src/llm/utils/ProviderConfigUI.ts +10 -141
- package/src/llm/utils/models-dev-cache.ts +102 -23
- package/src/llm/utils/provider-select-prompt.ts +284 -0
- package/src/llm/utils/provider-setup.ts +81 -34
- package/src/llm/utils/variant-list-prompt.ts +361 -0
- package/src/nostr/AgentEventDecoder.ts +1 -0
- package/src/nostr/AgentEventEncoder.ts +37 -0
- package/src/nostr/AgentProfilePublisher.ts +13 -0
- package/src/nostr/AgentPublisher.ts +26 -0
- package/src/nostr/kinds.ts +1 -0
- package/src/nostr/ndkClient.ts +4 -1
- package/src/nostr/types.ts +12 -0
- package/src/prompts/fragments/25-rag-instructions.ts +22 -21
- package/src/prompts/fragments/31-agents-md-guidance.ts +7 -21
- package/src/prompts/fragments/index.ts +2 -0
- package/src/prompts/utils/systemPromptBuilder.ts +18 -28
- package/src/services/AgentDefinitionMonitor.ts +8 -0
- package/src/services/ConfigService.ts +34 -0
- package/src/services/PubkeyService.ts +7 -1
- package/src/services/compression/CompressionService.ts +133 -74
- package/src/services/compression/compression-utils.ts +110 -19
- package/src/services/config/types.ts +0 -6
- package/src/services/dispatch/AgentDispatchService.ts +79 -0
- package/src/services/intervention/InterventionService.ts +78 -5
- package/src/services/nip46/Nip46SigningService.ts +30 -1
- package/src/services/projects/ProjectContext.ts +8 -6
- package/src/services/rag/RAGCollectionRegistry.ts +199 -0
- package/src/services/rag/RAGDatabaseService.ts +2 -7
- package/src/services/rag/RAGOperations.ts +25 -45
- package/src/services/rag/RAGService.ts +0 -31
- package/src/services/rag/RagSubscriptionService.ts +71 -122
- package/src/services/rag/rag-utils.ts +13 -0
- package/src/services/ral/RALRegistry.ts +25 -184
- package/src/services/reports/ReportEmbeddingService.ts +63 -113
- package/src/services/search/UnifiedSearchService.ts +115 -4
- package/src/services/search/index.ts +1 -0
- package/src/services/search/projectFilter.ts +20 -4
- package/src/services/search/providers/ConversationSearchProvider.ts +1 -0
- package/src/services/search/providers/GenericCollectionSearchProvider.ts +81 -0
- package/src/services/search/providers/LessonSearchProvider.ts +1 -8
- package/src/services/search/providers/ReportSearchProvider.ts +1 -0
- package/src/services/search/types.ts +24 -3
- package/src/services/trust-pubkeys/SystemPubkeyListService.ts +148 -0
- package/src/services/trust-pubkeys/TrustPubkeyService.ts +70 -9
- package/src/telemetry/setup.ts +2 -13
- package/src/tools/implementations/ask.ts +3 -3
- package/src/tools/implementations/conversation_get.ts +28 -268
- package/src/tools/implementations/fs_grep.ts +6 -6
- package/src/tools/implementations/fs_read.ts +2 -0
- package/src/tools/implementations/fs_write.ts +2 -0
- package/src/tools/implementations/learn.ts +38 -50
- package/src/tools/implementations/rag_add_documents.ts +6 -4
- package/src/tools/implementations/rag_create_collection.ts +37 -4
- package/src/tools/implementations/rag_delete_collection.ts +9 -0
- package/src/tools/implementations/{search.ts → rag_search.ts} +31 -25
- package/src/tools/registry.ts +7 -8
- package/src/tools/types.ts +11 -2
- package/src/tools/utils/transcript-args.ts +13 -0
- package/src/utils/cli-theme.ts +13 -0
- package/src/utils/logger.ts +55 -0
- package/src/utils/metadataKeys.ts +17 -0
- package/src/utils/sqlEscaping.ts +39 -0
- package/src/wrapper.ts +7 -3
- package/dist/src/index.js +0 -46778
- package/dist/tenex-backend-wrapper.cjs +0 -3
- package/src/agents/execution/constants.ts +0 -16
- package/src/agents/execution/index.ts +0 -3
- package/src/agents/index.ts +0 -4
- package/src/commands/agent.ts +0 -215
- package/src/conversations/formatters/DelegationXmlFormatter.ts +0 -64
- package/src/conversations/formatters/index.ts +0 -9
- package/src/conversations/index.ts +0 -2
- package/src/conversations/utils/content-utils.ts +0 -69
- package/src/daemon/UnixSocketTransport.ts +0 -318
- package/src/event-handler/newConversation.ts +0 -165
- package/src/events/NDKProjectStatus.ts +0 -384
- package/src/events/index.ts +0 -4
- package/src/lib/json-parser.ts +0 -30
- package/src/llm/RecordingState.ts +0 -37
- package/src/llm/StreamPublisher.ts +0 -40
- package/src/llm/middleware/flight-recorder.ts +0 -188
- package/src/llm/utils/claudeCodePromptCompiler.ts +0 -141
- package/src/nostr/constants.ts +0 -38
- package/src/prompts/core/index.ts +0 -3
- package/src/prompts/index.ts +0 -21
- package/src/services/image/index.ts +0 -12
- package/src/services/status/index.ts +0 -11
- package/src/telemetry/diagnostics.ts +0 -27
- package/src/tools/implementations/rag_query.ts +0 -107
- package/src/types/index.ts +0 -46
- package/src/utils/agentFetcher.ts +0 -107
- package/src/utils/conversation-utils.ts +0 -1
- package/src/utils/process.ts +0 -49
package/dist/wrapper.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
// src/wrapper.ts
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
6
|
+
import * as path from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
var __dirname = path.dirname(__filename);
|
|
10
|
+
function resolveEntryPoint() {
|
|
11
|
+
const srcIndex = path.join(__dirname, "index.ts");
|
|
12
|
+
if (existsSync(srcIndex)) {
|
|
13
|
+
return srcIndex;
|
|
14
|
+
}
|
|
15
|
+
const distIndex = path.join(__dirname, "index.js");
|
|
16
|
+
if (existsSync(distIndex)) {
|
|
17
|
+
return distIndex;
|
|
18
|
+
}
|
|
19
|
+
const parentSrcIndex = path.join(path.dirname(__dirname), "src", "index.ts");
|
|
20
|
+
if (existsSync(parentSrcIndex)) {
|
|
21
|
+
return parentSrcIndex;
|
|
22
|
+
}
|
|
23
|
+
const parentDistIndex = path.join(path.dirname(__dirname), "dist", "index.js");
|
|
24
|
+
if (existsSync(parentDistIndex)) {
|
|
25
|
+
return parentDistIndex;
|
|
26
|
+
}
|
|
27
|
+
return srcIndex;
|
|
28
|
+
}
|
|
29
|
+
var MIN_UPTIME_MS = 5e3;
|
|
30
|
+
var MAX_CRASHES = 5;
|
|
31
|
+
var CRASH_WINDOW_MS = 6e4;
|
|
32
|
+
var DaemonWrapper = class {
|
|
33
|
+
child = null;
|
|
34
|
+
crashHistory = [];
|
|
35
|
+
startTime = 0;
|
|
36
|
+
isShuttingDown = false;
|
|
37
|
+
/**
|
|
38
|
+
* Start the daemon wrapper
|
|
39
|
+
*/
|
|
40
|
+
async start(args2) {
|
|
41
|
+
console.log("[Wrapper] Starting TENEX daemon supervisor");
|
|
42
|
+
console.log("[Wrapper] Daemon arguments:", args2.join(" ") || "(none)");
|
|
43
|
+
this.setupSignalHandlers();
|
|
44
|
+
await this.runDaemonLoop(args2);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Setup signal handlers for the wrapper process
|
|
48
|
+
*/
|
|
49
|
+
setupSignalHandlers() {
|
|
50
|
+
process.on("SIGHUP", () => {
|
|
51
|
+
if (this.child && this.child.pid) {
|
|
52
|
+
console.log("[Wrapper] SIGHUP received - forwarding to daemon");
|
|
53
|
+
this.child.kill("SIGHUP");
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
const handleTermination = (signal) => {
|
|
57
|
+
if (this.isShuttingDown) {
|
|
58
|
+
console.log(`[Wrapper] ${signal} received during shutdown - forcing exit`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
console.log(`[Wrapper] ${signal} received - shutting down`);
|
|
62
|
+
this.isShuttingDown = true;
|
|
63
|
+
if (this.child && this.child.pid) {
|
|
64
|
+
this.child.kill(signal === "SIGTERM" ? "SIGTERM" : "SIGINT");
|
|
65
|
+
setTimeout(() => {
|
|
66
|
+
if (this.child && this.child.pid) {
|
|
67
|
+
console.log("[Wrapper] Child still running after timeout - forcing kill");
|
|
68
|
+
this.child.kill("SIGKILL");
|
|
69
|
+
}
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}, 3e4);
|
|
72
|
+
} else {
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
process.on("SIGTERM", () => handleTermination("SIGTERM"));
|
|
77
|
+
process.on("SIGINT", () => handleTermination("SIGINT"));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Main daemon loop - spawn daemon and respawn on clean exit
|
|
81
|
+
*/
|
|
82
|
+
async runDaemonLoop(args2) {
|
|
83
|
+
while (!this.isShuttingDown) {
|
|
84
|
+
if (this.isInCrashLoop()) {
|
|
85
|
+
console.error("[Wrapper] Crash loop detected - too many crashes in short period");
|
|
86
|
+
console.error(`[Wrapper] ${this.crashHistory.length} crashes in last ${CRASH_WINDOW_MS / 1e3}s`);
|
|
87
|
+
console.error("[Wrapper] Giving up - please check daemon logs");
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
this.startTime = Date.now();
|
|
91
|
+
const exitCode = await this.spawnDaemon(args2);
|
|
92
|
+
const uptime = Date.now() - this.startTime;
|
|
93
|
+
console.log(`[Wrapper] Daemon exited with code ${exitCode} after ${Math.round(uptime / 1e3)}s`);
|
|
94
|
+
if (this.isShuttingDown) {
|
|
95
|
+
console.log("[Wrapper] Wrapper shutting down, not respawning");
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
if (exitCode === 0) {
|
|
99
|
+
console.log("[Wrapper] Daemon exited cleanly - respawning for graceful restart");
|
|
100
|
+
if (uptime >= MIN_UPTIME_MS) {
|
|
101
|
+
this.crashHistory = [];
|
|
102
|
+
}
|
|
103
|
+
await this.sleep(100);
|
|
104
|
+
} else {
|
|
105
|
+
console.error(`[Wrapper] Daemon crashed with exit code ${exitCode}`);
|
|
106
|
+
this.recordCrash();
|
|
107
|
+
const backoffMs = Math.min(1e3 * Math.pow(2, this.crashHistory.length - 1), 3e4);
|
|
108
|
+
console.log(`[Wrapper] Waiting ${backoffMs / 1e3}s before respawn...`);
|
|
109
|
+
await this.sleep(backoffMs);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
console.log("[Wrapper] Exiting");
|
|
113
|
+
process.exit(0);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Spawn the daemon process and wait for it to exit
|
|
117
|
+
*/
|
|
118
|
+
spawnDaemon(args2) {
|
|
119
|
+
return new Promise((resolve) => {
|
|
120
|
+
const indexPath = resolveEntryPoint();
|
|
121
|
+
const daemonArgs = [indexPath, "daemon", "--supervised", ...args2];
|
|
122
|
+
const runtimeBinary = indexPath.endsWith(".ts") ? process.env.TENEX_BUN_BIN || "bun" : process.execPath;
|
|
123
|
+
console.log(`[Wrapper] Spawning: ${runtimeBinary} ${daemonArgs.join(" ")}`);
|
|
124
|
+
this.child = spawn(runtimeBinary, daemonArgs, {
|
|
125
|
+
stdio: "inherit",
|
|
126
|
+
env: process.env
|
|
127
|
+
});
|
|
128
|
+
this.child.on("exit", (code) => {
|
|
129
|
+
this.child = null;
|
|
130
|
+
resolve(code ?? 1);
|
|
131
|
+
});
|
|
132
|
+
this.child.on("error", (error) => {
|
|
133
|
+
console.error("[Wrapper] Failed to spawn daemon:", error.message);
|
|
134
|
+
this.child = null;
|
|
135
|
+
resolve(1);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Record a crash for crash loop detection
|
|
141
|
+
*/
|
|
142
|
+
recordCrash() {
|
|
143
|
+
const now = Date.now();
|
|
144
|
+
this.crashHistory.push({ timestamp: now });
|
|
145
|
+
this.crashHistory = this.crashHistory.filter(
|
|
146
|
+
(c) => now - c.timestamp < CRASH_WINDOW_MS
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Check if we're in a crash loop
|
|
151
|
+
*/
|
|
152
|
+
isInCrashLoop() {
|
|
153
|
+
const now = Date.now();
|
|
154
|
+
const recentCrashes = this.crashHistory.filter(
|
|
155
|
+
(c) => now - c.timestamp < CRASH_WINDOW_MS
|
|
156
|
+
);
|
|
157
|
+
return recentCrashes.length >= MAX_CRASHES;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Sleep for a given duration
|
|
161
|
+
*/
|
|
162
|
+
sleep(ms) {
|
|
163
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
var wrapper = new DaemonWrapper();
|
|
167
|
+
var args = process.argv.slice(2);
|
|
168
|
+
wrapper.start(args).catch((error) => {
|
|
169
|
+
console.error("[Wrapper] Fatal error:", error);
|
|
170
|
+
process.exit(1);
|
|
171
|
+
});
|
package/package.json
CHANGED
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tenex-chat/backend",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.6",
|
|
4
4
|
"description": "TENEX Command Line Interface",
|
|
5
|
-
"main": "./dist/index.js",
|
|
6
|
-
"types": "./dist/index.d.ts",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": {
|
|
9
|
-
"import": "./dist/index.js",
|
|
10
|
-
"types": "./dist/index.d.ts"
|
|
11
|
-
}
|
|
12
|
-
},
|
|
13
5
|
"bin": {
|
|
14
|
-
"tenex-backend": "./dist/
|
|
6
|
+
"tenex-backend": "./dist/daemon-wrapper.cjs"
|
|
15
7
|
},
|
|
16
8
|
"files": [
|
|
17
9
|
"dist/**/*.js",
|
|
@@ -54,23 +46,23 @@
|
|
|
54
46
|
"registry": "https://registry.npmjs.org/"
|
|
55
47
|
},
|
|
56
48
|
"dependencies": {
|
|
57
|
-
"@ai-sdk/anthropic": "^3.0.
|
|
58
|
-
"@ai-sdk/openai": "^3.0.
|
|
49
|
+
"@ai-sdk/anthropic": "^3.0.58",
|
|
50
|
+
"@ai-sdk/openai": "^3.0.41",
|
|
59
51
|
"@huggingface/transformers": "^3.8.1",
|
|
60
52
|
"@lancedb/lancedb": "^0.26.2",
|
|
61
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
62
|
-
"@nostr-dev-kit/ndk": "3.0.
|
|
63
|
-
"@openrouter/ai-sdk-provider": "^2.
|
|
53
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
54
|
+
"@nostr-dev-kit/ndk": "3.0.3",
|
|
55
|
+
"@openrouter/ai-sdk-provider": "^2.2.5",
|
|
64
56
|
"@opentelemetry/api": "^1.9.0",
|
|
65
|
-
"@opentelemetry/auto-instrumentations-node": "^0.
|
|
66
|
-
"@opentelemetry/exporter-metrics-otlp-http": "^0.
|
|
67
|
-
"@opentelemetry/exporter-trace-otlp-http": "^0.
|
|
68
|
-
"@opentelemetry/resources": "^2.
|
|
69
|
-
"@opentelemetry/sdk-node": "^0.
|
|
70
|
-
"@opentelemetry/sdk-trace-node": "^2.
|
|
71
|
-
"@opentelemetry/semantic-conventions": "^1.
|
|
72
|
-
"ai": "^6.0.
|
|
73
|
-
"ai-sdk-provider-claude-code": "
|
|
57
|
+
"@opentelemetry/auto-instrumentations-node": "^0.71.0",
|
|
58
|
+
"@opentelemetry/exporter-metrics-otlp-http": "^0.213.0",
|
|
59
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.213.0",
|
|
60
|
+
"@opentelemetry/resources": "^2.6.0",
|
|
61
|
+
"@opentelemetry/sdk-node": "^0.213.0",
|
|
62
|
+
"@opentelemetry/sdk-trace-node": "^2.6.0",
|
|
63
|
+
"@opentelemetry/semantic-conventions": "^1.40.0",
|
|
64
|
+
"ai": "^6.0.116",
|
|
65
|
+
"ai-sdk-provider-claude-code": "3.4.3",
|
|
74
66
|
"ai-sdk-provider-codex-app-server": "1.1.7",
|
|
75
67
|
"chalk": "^5.6.2",
|
|
76
68
|
"commander": "^14.0.3",
|
|
@@ -81,7 +73,7 @@
|
|
|
81
73
|
"lodash": "^4.17.23",
|
|
82
74
|
"node-cron": "^4.2.1",
|
|
83
75
|
"node-html-markdown": "^2.0.0",
|
|
84
|
-
"nostr-tools": "^2.
|
|
76
|
+
"nostr-tools": "^2.23.3",
|
|
85
77
|
"ollama-ai-provider-v2": "^3.0.3",
|
|
86
78
|
"tseep": "^1.3.1",
|
|
87
79
|
"zod": "^4.3.6"
|
|
@@ -90,10 +82,10 @@
|
|
|
90
82
|
"@eslint/js": "^10.0.1",
|
|
91
83
|
"esbuild": "^0.27.2",
|
|
92
84
|
"esbuild-plugin-alias": "^0.2.1",
|
|
93
|
-
"eslint": "^10.0.
|
|
85
|
+
"eslint": "^10.0.3",
|
|
94
86
|
"husky": "^9.1.7",
|
|
95
87
|
"typescript": "^5.9.3",
|
|
96
|
-
"typescript-eslint": "^8.
|
|
88
|
+
"typescript-eslint": "^8.56.1",
|
|
97
89
|
"vitest": "^4.0.16"
|
|
98
90
|
},
|
|
99
91
|
"type": "module",
|
|
@@ -141,18 +141,20 @@ export class AgentRegistry {
|
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
//
|
|
144
|
+
// Log failed agents but don't block project boot
|
|
145
145
|
if (failedAgents.length > 0) {
|
|
146
|
-
// PM is the first agent in tags
|
|
147
146
|
const pmEventId = agentEventIds[0];
|
|
148
147
|
if (failedAgents.includes(pmEventId)) {
|
|
149
|
-
|
|
150
|
-
`
|
|
148
|
+
logger.error(
|
|
149
|
+
`PM agent (first in tags) failed to load — project will boot without a PM. Agent event ID ${pmEventId} could not be fetched. This might be due to network issues or the event not being available on the configured relays.`,
|
|
150
|
+
{ pmEventId, failedAgents }
|
|
151
|
+
);
|
|
152
|
+
} else {
|
|
153
|
+
logger.warn(
|
|
154
|
+
`${failedAgents.length} agent(s) could not be installed but continuing with available agents`,
|
|
155
|
+
{ failedAgents }
|
|
151
156
|
);
|
|
152
157
|
}
|
|
153
|
-
logger.warn(
|
|
154
|
-
`${failedAgents.length} agent(s) could not be installed but continuing with available agents`
|
|
155
|
-
);
|
|
156
158
|
}
|
|
157
159
|
|
|
158
160
|
// Load locally-associated agents from storage
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import type { StoredAgentData, ProjectScopedConfig, AgentDefaultConfig, AgentProjectConfig } from "@/agents/types";
|
|
4
|
+
import type { AgentCategory } from "@/agents/role-categories";
|
|
4
5
|
import type { MCPServerConfig } from "@/llm/providers/types";
|
|
5
6
|
import {
|
|
6
7
|
resolveEffectiveConfig,
|
|
@@ -100,6 +101,7 @@ export function createStoredAgent(config: {
|
|
|
100
101
|
slug: string;
|
|
101
102
|
name: string;
|
|
102
103
|
role: string;
|
|
104
|
+
category?: AgentCategory;
|
|
103
105
|
description?: string | null;
|
|
104
106
|
instructions?: string | null;
|
|
105
107
|
useCriteria?: string | null;
|
|
@@ -118,6 +120,7 @@ export function createStoredAgent(config: {
|
|
|
118
120
|
slug: config.slug,
|
|
119
121
|
name: config.name,
|
|
120
122
|
role: config.role,
|
|
123
|
+
category: config.category,
|
|
121
124
|
description: config.description ?? undefined,
|
|
122
125
|
instructions: config.instructions ?? undefined,
|
|
123
126
|
useCriteria: config.useCriteria ?? undefined,
|
|
@@ -658,7 +661,7 @@ export class AgentStorage {
|
|
|
658
661
|
|
|
659
662
|
/**
|
|
660
663
|
* Get agent by slug (uses index for O(1) lookup).
|
|
661
|
-
* Returns the agent
|
|
664
|
+
* Returns the agent regardless of project assignment.
|
|
662
665
|
* Use getAgentBySlugForProject() when you need project-scoped lookups.
|
|
663
666
|
*/
|
|
664
667
|
async getAgentBySlug(slug: string): Promise<StoredAgent | null> {
|
|
@@ -1098,6 +1101,26 @@ export class AgentStorage {
|
|
|
1098
1101
|
return true;
|
|
1099
1102
|
}
|
|
1100
1103
|
|
|
1104
|
+
/**
|
|
1105
|
+
* Get all known agent pubkeys across all projects from the index.
|
|
1106
|
+
* Returns pubkeys from the byProject index (source of truth for project associations).
|
|
1107
|
+
*
|
|
1108
|
+
* Used by the Daemon to seed the TrustPubkeyService global agent pubkeys set
|
|
1109
|
+
* at startup, covering agents from not-yet-running projects.
|
|
1110
|
+
*/
|
|
1111
|
+
async getAllKnownPubkeys(): Promise<Set<string>> {
|
|
1112
|
+
if (!this.index) await this.loadIndex();
|
|
1113
|
+
if (!this.index) return new Set();
|
|
1114
|
+
|
|
1115
|
+
const pubkeys = new Set<string>();
|
|
1116
|
+
for (const projectPubkeys of Object.values(this.index.byProject)) {
|
|
1117
|
+
for (const pubkey of projectPubkeys) {
|
|
1118
|
+
pubkeys.add(pubkey);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
return pubkeys;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1101
1124
|
/**
|
|
1102
1125
|
* Get all agents (for debugging/admin purposes)
|
|
1103
1126
|
*/
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { agentStorage, createStoredAgent, type StoredAgent } from "@/agents/AgentStorage";
|
|
2
2
|
import { AgentNotFoundError, AgentValidationError } from "@/agents/errors";
|
|
3
|
+
import { isValidCategory, type AgentCategory } from "@/agents/role-categories";
|
|
3
4
|
import { installAgentScripts } from "@/agents/script-installer";
|
|
4
5
|
import { DEFAULT_AGENT_LLM_CONFIG } from "@/llm/constants";
|
|
5
6
|
import { getNDK } from "@/nostr";
|
|
@@ -77,6 +78,7 @@ interface ParsedAgentEvent {
|
|
|
77
78
|
slug: string;
|
|
78
79
|
name: string;
|
|
79
80
|
role: string;
|
|
81
|
+
category?: AgentCategory;
|
|
80
82
|
description: string;
|
|
81
83
|
instructions: string;
|
|
82
84
|
useCriteria: string;
|
|
@@ -96,6 +98,8 @@ function parseAgentEvent(event: NDKEvent, slug: string): ParsedAgentEvent {
|
|
|
96
98
|
const title = agentDef.title || "Unnamed Agent";
|
|
97
99
|
const description = agentDef.description || "";
|
|
98
100
|
const role = agentDef.role || "assistant";
|
|
101
|
+
const rawCategory = agentDef.category || undefined;
|
|
102
|
+
const category = rawCategory && isValidCategory(rawCategory) ? rawCategory : undefined;
|
|
99
103
|
const instructions = agentDef.instructions || "";
|
|
100
104
|
const useCriteria = agentDef.useCriteria || "";
|
|
101
105
|
|
|
@@ -117,6 +121,7 @@ function parseAgentEvent(event: NDKEvent, slug: string): ParsedAgentEvent {
|
|
|
117
121
|
slug,
|
|
118
122
|
name: title,
|
|
119
123
|
role,
|
|
124
|
+
category,
|
|
120
125
|
description,
|
|
121
126
|
instructions,
|
|
122
127
|
useCriteria,
|
|
@@ -191,6 +196,7 @@ export async function installAgentFromNostrEvent(
|
|
|
191
196
|
slug: agentData.slug,
|
|
192
197
|
name: agentData.name,
|
|
193
198
|
role: agentData.role,
|
|
199
|
+
category: agentData.category,
|
|
194
200
|
description: agentData.description,
|
|
195
201
|
instructions: agentData.instructions,
|
|
196
202
|
useCriteria: agentData.useCriteria,
|
|
@@ -2,6 +2,7 @@ import type { AgentRegistry } from "@/agents/AgentRegistry";
|
|
|
2
2
|
import { agentStorage, type StoredAgent } from "@/agents/AgentStorage";
|
|
3
3
|
import { installAgentFromNostr } from "@/agents/agent-installer";
|
|
4
4
|
import { AgentSlugConflictError } from "@/agents/errors";
|
|
5
|
+
import { resolveCategory } from "@/agents/role-categories";
|
|
5
6
|
import { processAgentTools } from "@/agents/tool-normalization";
|
|
6
7
|
import type { AgentInstance } from "@/agents/types";
|
|
7
8
|
import { AgentMetadataStore } from "@/services/agents";
|
|
@@ -42,7 +43,10 @@ export function createAgentInstance(
|
|
|
42
43
|
const effectiveTools = resolvedConfig.tools;
|
|
43
44
|
|
|
44
45
|
// Process tools using pure functions
|
|
45
|
-
const
|
|
46
|
+
const normalizedTools = processAgentTools(effectiveTools || [], storedAgent.slug);
|
|
47
|
+
|
|
48
|
+
// Resolve category for organizational purposes (no tool restrictions)
|
|
49
|
+
const resolvedCategory = resolveCategory(storedAgent.category);
|
|
46
50
|
|
|
47
51
|
// Build agent-specific MCP config from stored mcpServers
|
|
48
52
|
const agentMcpConfig: MCPConfig | undefined = storedAgent.mcpServers
|
|
@@ -57,11 +61,12 @@ export function createAgentInstance(
|
|
|
57
61
|
pubkey,
|
|
58
62
|
signer,
|
|
59
63
|
role: storedAgent.role,
|
|
64
|
+
category: resolvedCategory,
|
|
60
65
|
description: storedAgent.description,
|
|
61
66
|
instructions: storedAgent.instructions,
|
|
62
67
|
useCriteria: storedAgent.useCriteria,
|
|
63
68
|
llmConfig: effectiveLLMConfig || DEFAULT_AGENT_LLM_CONFIG,
|
|
64
|
-
tools:
|
|
69
|
+
tools: normalizedTools,
|
|
65
70
|
eventId: storedAgent.eventId,
|
|
66
71
|
slug: storedAgent.slug,
|
|
67
72
|
mcpServers: storedAgent.mcpServers,
|
package/src/agents/constants.ts
CHANGED
|
@@ -19,8 +19,16 @@ export const CORE_AGENT_TOOLS: ToolName[] = [
|
|
|
19
19
|
// Conversation tools for project introspection
|
|
20
20
|
"conversation_get", // All agents should access conversation details
|
|
21
21
|
"conversation_list", // All agents should list conversations
|
|
22
|
-
//
|
|
23
|
-
"
|
|
22
|
+
// RAG tools for knowledge management
|
|
23
|
+
"rag_search", // All agents should be able to search across reports, conversations, and lessons
|
|
24
|
+
"rag_create_collection", // All agents should be able to create RAG collections
|
|
25
|
+
"rag_add_documents", // All agents should be able to add documents to collections
|
|
26
|
+
"rag_delete_collection", // All agents should be able to delete RAG collections
|
|
27
|
+
"rag_list_collections", // All agents should be able to list RAG collections
|
|
28
|
+
"rag_subscription_create", // All agents should be able to create RAG subscriptions
|
|
29
|
+
"rag_subscription_list", // All agents should be able to list RAG subscriptions
|
|
30
|
+
"rag_subscription_get", // All agents should be able to get RAG subscription details
|
|
31
|
+
"rag_subscription_delete", // All agents should be able to delete RAG subscriptions
|
|
24
32
|
// Process control
|
|
25
33
|
"kill", // All agents should be able to terminate processes
|
|
26
34
|
// MCP resource reading and subscriptions (self-gating: only works if agent has MCP tools from that server)
|
|
@@ -241,6 +241,23 @@ export class AgentExecutor {
|
|
|
241
241
|
}
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
+
logger.writeToWarnLog({
|
|
245
|
+
timestamp: new Date().toISOString(),
|
|
246
|
+
level: "error",
|
|
247
|
+
component: "AgentExecutor",
|
|
248
|
+
message: isCreditsError
|
|
249
|
+
? "Execution failed due to insufficient credits"
|
|
250
|
+
: "Agent execution failed",
|
|
251
|
+
context: {
|
|
252
|
+
agentSlug: context.agent.slug,
|
|
253
|
+
conversationId: conversation?.id?.substring(0, 12),
|
|
254
|
+
isCreditsError,
|
|
255
|
+
errorType: isCreditsError ? "insufficient_credits" : "execution_error",
|
|
256
|
+
},
|
|
257
|
+
error: errorMessage,
|
|
258
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
259
|
+
});
|
|
260
|
+
|
|
244
261
|
logger.error(
|
|
245
262
|
isCreditsError
|
|
246
263
|
? "[AgentExecutor] Execution failed due to insufficient credits"
|
|
@@ -320,6 +337,18 @@ export class AgentExecutor {
|
|
|
320
337
|
agent: context.agent.slug,
|
|
321
338
|
error: formatAnyError(streamError),
|
|
322
339
|
});
|
|
340
|
+
logger.writeToWarnLog({
|
|
341
|
+
timestamp: new Date().toISOString(),
|
|
342
|
+
level: "error",
|
|
343
|
+
component: "AgentExecutor",
|
|
344
|
+
message: "LLM streaming execution failed",
|
|
345
|
+
context: {
|
|
346
|
+
agentSlug: context.agent.slug,
|
|
347
|
+
ralNumber,
|
|
348
|
+
},
|
|
349
|
+
error: formatAnyError(streamError),
|
|
350
|
+
stack: streamError instanceof Error ? streamError.stack : undefined,
|
|
351
|
+
});
|
|
323
352
|
throw streamError;
|
|
324
353
|
}
|
|
325
354
|
|
|
@@ -388,20 +417,18 @@ export class AgentExecutor {
|
|
|
388
417
|
"outstanding.has_work": outstandingWork.hasWork,
|
|
389
418
|
"outstanding.queued_injections": outstandingWork.details.queuedInjections,
|
|
390
419
|
"outstanding.pending_delegations": outstandingWork.details.pendingDelegations,
|
|
391
|
-
"
|
|
420
|
+
"outstanding.completed_delegations": outstandingWork.details.completedDelegations,
|
|
392
421
|
});
|
|
393
422
|
|
|
394
|
-
// INVARIANT GUARD: If there's outstanding work (queued injections
|
|
395
|
-
//
|
|
423
|
+
// INVARIANT GUARD: If there's outstanding work (queued injections, pending delegations,
|
|
424
|
+
// or completed delegations not yet consumed), we should NOT finalize.
|
|
396
425
|
const hasMessageContent = completionEvent?.message && completionEvent.message.length > 0;
|
|
397
426
|
if (!hasMessageContent && outstandingWork.hasWork) {
|
|
398
|
-
// This is the expected path when delegation results arrive via debounce.
|
|
399
|
-
// The executor returns undefined to allow the dispatch loop to continue
|
|
400
|
-
// and process the queued injections in the next iteration.
|
|
401
427
|
trace.getActiveSpan()?.addEvent("executor.awaiting_outstanding_work", {
|
|
402
428
|
"ral.number": ralNumber,
|
|
403
429
|
"outstanding.queued_injections": outstandingWork.details.queuedInjections,
|
|
404
430
|
"outstanding.pending_delegations": outstandingWork.details.pendingDelegations,
|
|
431
|
+
"outstanding.completed_delegations": outstandingWork.details.completedDelegations,
|
|
405
432
|
"completion_event_exists": Boolean(completionEvent),
|
|
406
433
|
"scenario": "injection_debounce_await",
|
|
407
434
|
});
|
|
@@ -410,6 +437,7 @@ export class AgentExecutor {
|
|
|
410
437
|
ralNumber,
|
|
411
438
|
queuedInjections: outstandingWork.details.queuedInjections,
|
|
412
439
|
pendingDelegations: outstandingWork.details.pendingDelegations,
|
|
440
|
+
completedDelegations: outstandingWork.details.completedDelegations,
|
|
413
441
|
});
|
|
414
442
|
return undefined;
|
|
415
443
|
}
|
|
@@ -486,6 +514,7 @@ export class AgentExecutor {
|
|
|
486
514
|
"outstanding.has_work": finalOutstandingWork.hasWork,
|
|
487
515
|
"outstanding.queued_injections": finalOutstandingWork.details.queuedInjections,
|
|
488
516
|
"outstanding.pending_delegations": finalOutstandingWork.details.pendingDelegations,
|
|
517
|
+
"outstanding.completed_delegations": finalOutstandingWork.details.completedDelegations,
|
|
489
518
|
});
|
|
490
519
|
|
|
491
520
|
let responseEvent: NDKEvent | undefined;
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { formatAnyError } from "@/lib/error-formatter";
|
|
9
|
-
import { LLMService } from "@/llm/service";
|
|
10
9
|
import { llmServiceFactory } from "@/llm/LLMServiceFactory";
|
|
11
10
|
import { shortenConversationId } from "@/utils/conversation-id";
|
|
12
11
|
import { config as configService } from "@/services/ConfigService";
|
|
@@ -14,7 +13,7 @@ import { RALRegistry } from "@/services/ral";
|
|
|
14
13
|
import type { SkillData } from "@/services/skill";
|
|
15
14
|
import { logger } from "@/utils/logger";
|
|
16
15
|
import { SpanStatusCode, trace } from "@opentelemetry/api";
|
|
17
|
-
import type { LanguageModel, ModelMessage } from "ai";
|
|
16
|
+
import type { LanguageModel, ModelMessage, ProviderRegistryProvider } from "ai";
|
|
18
17
|
|
|
19
18
|
const tracer = trace.getTracer("tenex.stream-callbacks");
|
|
20
19
|
import { MessageCompiler } from "./MessageCompiler";
|
|
@@ -53,7 +52,11 @@ export interface StepData {
|
|
|
53
52
|
*/
|
|
54
53
|
export interface PrepareStepConfig {
|
|
55
54
|
context: FullRuntimeContext;
|
|
56
|
-
llmService: {
|
|
55
|
+
llmService: {
|
|
56
|
+
provider: string;
|
|
57
|
+
updateUsageFromSteps: (steps: StepData["steps"]) => void;
|
|
58
|
+
createLanguageModelFromRegistry: (provider: string, model: string, registry: ProviderRegistryProvider) => LanguageModel;
|
|
59
|
+
};
|
|
57
60
|
messageCompiler: MessageCompiler;
|
|
58
61
|
ephemeralMessages: Array<{ role: "user" | "system"; content: string }>;
|
|
59
62
|
nudgeContent: string;
|
|
@@ -147,15 +150,25 @@ export function createPrepareStep(
|
|
|
147
150
|
content: injection.content,
|
|
148
151
|
});
|
|
149
152
|
} else {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
153
|
+
const relocated = injection.eventId
|
|
154
|
+
? conversationStore.relocateToEnd(injection.eventId, {
|
|
155
|
+
ral: ralNumber,
|
|
156
|
+
senderPubkey: injection.senderPubkey,
|
|
157
|
+
targetedPubkeys: [context.agent.pubkey],
|
|
158
|
+
})
|
|
159
|
+
: false;
|
|
160
|
+
|
|
161
|
+
if (!relocated) {
|
|
162
|
+
conversationStore.addMessage({
|
|
163
|
+
pubkey: context.triggeringEvent.pubkey,
|
|
164
|
+
ral: ralNumber,
|
|
165
|
+
content: injection.content,
|
|
166
|
+
messageType: "text",
|
|
167
|
+
targetedPubkeys: [context.agent.pubkey],
|
|
168
|
+
senderPubkey: injection.senderPubkey,
|
|
169
|
+
eventId: injection.eventId,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
159
172
|
}
|
|
160
173
|
}
|
|
161
174
|
|
|
@@ -259,7 +272,7 @@ export function createPrepareStep(
|
|
|
259
272
|
|
|
260
273
|
try {
|
|
261
274
|
const registry = llmServiceFactory.getRegistry();
|
|
262
|
-
const newModel =
|
|
275
|
+
const newModel = llmService.createLanguageModelFromRegistry(
|
|
263
276
|
newLlmConfig.provider,
|
|
264
277
|
newLlmConfig.model,
|
|
265
278
|
registry
|
|
@@ -291,6 +304,19 @@ export function createPrepareStep(
|
|
|
291
304
|
variant: currentVariant,
|
|
292
305
|
config: resolution.configName,
|
|
293
306
|
});
|
|
307
|
+
logger.writeToWarnLog({
|
|
308
|
+
timestamp: new Date().toISOString(),
|
|
309
|
+
level: "error",
|
|
310
|
+
component: "StreamCallbacks",
|
|
311
|
+
message: "Failed to create new model for variant switch",
|
|
312
|
+
context: {
|
|
313
|
+
agent: context.agent.slug,
|
|
314
|
+
variant: currentVariant,
|
|
315
|
+
config: resolution.configName,
|
|
316
|
+
},
|
|
317
|
+
error: formatAnyError(modelError),
|
|
318
|
+
stack: modelError instanceof Error ? modelError.stack : undefined,
|
|
319
|
+
});
|
|
294
320
|
}
|
|
295
321
|
}
|
|
296
322
|
}
|
|
@@ -302,6 +328,20 @@ export function createPrepareStep(
|
|
|
302
328
|
} catch (error) {
|
|
303
329
|
span.recordException(error as Error);
|
|
304
330
|
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
331
|
+
logger.writeToWarnLog({
|
|
332
|
+
timestamp: new Date().toISOString(),
|
|
333
|
+
level: "error",
|
|
334
|
+
component: "StreamCallbacks",
|
|
335
|
+
message: "LLM streaming prepareStep failed",
|
|
336
|
+
context: {
|
|
337
|
+
agent: context.agent.slug,
|
|
338
|
+
conversationId: context.conversationId,
|
|
339
|
+
stepNumber: step.stepNumber,
|
|
340
|
+
ralNumber,
|
|
341
|
+
},
|
|
342
|
+
error: error instanceof Error ? error.message : String(error),
|
|
343
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
344
|
+
});
|
|
305
345
|
throw error;
|
|
306
346
|
} finally {
|
|
307
347
|
span.end();
|