veryfront 0.1.59 → 0.1.60
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/esm/deno.js +1 -1
- package/esm/src/agent/chat-handler.d.ts +15 -1
- package/esm/src/agent/chat-handler.d.ts.map +1 -1
- package/esm/src/agent/chat-handler.js +21 -19
- package/esm/src/agent/index.d.ts +1 -1
- package/esm/src/agent/index.d.ts.map +1 -1
- package/esm/src/discovery/discovery-engine.d.ts.map +1 -1
- package/esm/src/discovery/discovery-engine.js +57 -8
- package/package.json +1 -1
- package/src/deno.js +1 -1
- package/src/src/agent/chat-handler.ts +50 -6
- package/src/src/agent/index.ts +1 -0
- package/src/src/discovery/discovery-engine.ts +81 -9
package/esm/deno.js
CHANGED
|
@@ -26,6 +26,11 @@ export interface ChatHandlerOptions {
|
|
|
26
26
|
*/
|
|
27
27
|
beforeStream?: ChatHandlerBeforeStream;
|
|
28
28
|
}
|
|
29
|
+
/** Options when passing an agent instance directly. */
|
|
30
|
+
export interface ChatHandlerConfigWithAgent extends ChatHandlerOptions {
|
|
31
|
+
/** The agent instance to use (bypasses registry lookup). */
|
|
32
|
+
agent: import("./types.js").Agent;
|
|
33
|
+
}
|
|
29
34
|
/**
|
|
30
35
|
* Create a POST handler for a chat API route.
|
|
31
36
|
*
|
|
@@ -33,11 +38,20 @@ export interface ChatHandlerOptions {
|
|
|
33
38
|
* - App Router: `app/api/chat/route.ts` — handler receives `(request, context)`
|
|
34
39
|
* - Pages Router: `pages/api/chat.ts` — handler receives `(ctx)`
|
|
35
40
|
*
|
|
41
|
+
* Accepts either:
|
|
42
|
+
* - `createChatHandler("agentId", options?)` — looks up agent by ID from the registry
|
|
43
|
+
* - `createChatHandler({ agent, ...options })` — uses the provided agent instance directly
|
|
44
|
+
*
|
|
36
45
|
* @example
|
|
37
46
|
* ```ts
|
|
38
|
-
*
|
|
47
|
+
* // By agent ID (requires auto-discovery registration)
|
|
39
48
|
* export const POST = createChatHandler("assistant");
|
|
49
|
+
*
|
|
50
|
+
* // By agent instance (no registry needed)
|
|
51
|
+
* import { myAgent } from "../../agents/my-agent";
|
|
52
|
+
* export const POST = createChatHandler({ agent: myAgent, beforeStream: ... });
|
|
40
53
|
* ```
|
|
41
54
|
*/
|
|
42
55
|
export declare function createChatHandler(agentId: string, options?: ChatHandlerOptions): (requestOrCtx: unknown) => Promise<dntShim.Response>;
|
|
56
|
+
export declare function createChatHandler(config: ChatHandlerConfigWithAgent, options?: ChatHandlerOptions): (requestOrCtx: unknown) => Promise<dntShim.Response>;
|
|
43
57
|
//# sourceMappingURL=chat-handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-handler.d.ts","sourceRoot":"","sources":["../../../src/src/agent/chat-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,qBAAqB,CAAC;AAG/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AA8K1C,MAAM,MAAM,uBAAuB,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5E,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC;IACzB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,6BAA6B;IAC5C,OAAO,CAAC,EAAE,uBAAuB,EAAE,CAAC;IACpC,MAAM,CAAC,EAAE,uBAAuB,EAAE,CAAC;IACnC,eAAe,CAAC,EAAE,uBAAuB,EAAE,CAAC;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,MAAM,uBAAuB,GAAG,CACpC,KAAK,EAAE,8BAA8B,KAEnC,IAAI,GACJ,OAAO,CAAC,QAAQ,GAChB,6BAA6B,GAC7B,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,QAAQ,GAAG,6BAA6B,CAAC,CAAC;AAiCrE,mFAAmF;AACnF,MAAM,WAAW,kBAAkB;IACjC,uFAAuF;IACvF,OAAO,CAAC,EACJ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACvB,CAAC,CACD,OAAO,EAAE,OAAO,CAAC,OAAO,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACnE;;;OAGG;IACH,YAAY,CAAC,EAAE,uBAAuB,CAAC;CACxC;
|
|
1
|
+
{"version":3,"file":"chat-handler.d.ts","sourceRoot":"","sources":["../../../src/src/agent/chat-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,qBAAqB,CAAC;AAG/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AA8K1C,MAAM,MAAM,uBAAuB,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5E,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC;IACzB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,6BAA6B;IAC5C,OAAO,CAAC,EAAE,uBAAuB,EAAE,CAAC;IACpC,MAAM,CAAC,EAAE,uBAAuB,EAAE,CAAC;IACnC,eAAe,CAAC,EAAE,uBAAuB,EAAE,CAAC;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,MAAM,uBAAuB,GAAG,CACpC,KAAK,EAAE,8BAA8B,KAEnC,IAAI,GACJ,OAAO,CAAC,QAAQ,GAChB,6BAA6B,GAC7B,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,QAAQ,GAAG,6BAA6B,CAAC,CAAC;AAiCrE,mFAAmF;AACnF,MAAM,WAAW,kBAAkB;IACjC,uFAAuF;IACvF,OAAO,CAAC,EACJ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACvB,CAAC,CACD,OAAO,EAAE,OAAO,CAAC,OAAO,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACnE;;;OAGG;IACH,YAAY,CAAC,EAAE,uBAAuB,CAAC;CACxC;AAED,uDAAuD;AACvD,MAAM,WAAW,0BAA2B,SAAQ,kBAAkB;IACpE,4DAA4D;IAC5D,KAAK,EAAE,OAAO,YAAY,EAAE,KAAK,CAAC;CACnC;AAwDD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,kBAAkB,GAC3B,CAAC,YAAY,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;AACxD,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,0BAA0B,EAClC,OAAO,CAAC,EAAE,kBAAkB,GAC3B,CAAC,YAAY,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC"}
|
|
@@ -155,6 +155,11 @@ function applyBeforeStreamResult(baseMessages, result) {
|
|
|
155
155
|
...normalizeHookMessages(result.append, "append", idCounter),
|
|
156
156
|
];
|
|
157
157
|
}
|
|
158
|
+
function mergeChatHandlerConfig(config, options) {
|
|
159
|
+
if (!options)
|
|
160
|
+
return config;
|
|
161
|
+
return { ...options, ...config };
|
|
162
|
+
}
|
|
158
163
|
/**
|
|
159
164
|
* Extract the raw Request from either a raw Request or a Pages Router APIContext.
|
|
160
165
|
* Pages Router handlers receive `(ctx)` where `ctx.request` is the Request.
|
|
@@ -195,29 +200,26 @@ function extractRequest(requestOrCtx) {
|
|
|
195
200
|
detail: "Invalid handler argument: expected Request or APIContext",
|
|
196
201
|
});
|
|
197
202
|
}
|
|
198
|
-
|
|
199
|
-
* Create a POST handler for a chat API route.
|
|
200
|
-
*
|
|
201
|
-
* Works with both App Router and Pages Router:
|
|
202
|
-
* - App Router: `app/api/chat/route.ts` — handler receives `(request, context)`
|
|
203
|
-
* - Pages Router: `pages/api/chat.ts` — handler receives `(ctx)`
|
|
204
|
-
*
|
|
205
|
-
* @example
|
|
206
|
-
* ```ts
|
|
207
|
-
* import { createChatHandler } from "veryfront/agent";
|
|
208
|
-
* export const POST = createChatHandler("assistant");
|
|
209
|
-
* ```
|
|
210
|
-
*/
|
|
211
|
-
export function createChatHandler(agentId, options) {
|
|
203
|
+
export function createChatHandler(agentIdOrConfig, options) {
|
|
212
204
|
return async function POST(requestOrCtx) {
|
|
213
205
|
const request = extractRequest(requestOrCtx);
|
|
214
206
|
let agent;
|
|
215
|
-
|
|
216
|
-
agent
|
|
207
|
+
if (typeof agentIdOrConfig === "object" && agentIdOrConfig !== null && "agent" in agentIdOrConfig) {
|
|
208
|
+
// Object-based API: createChatHandler({ agent, beforeStream, ... })
|
|
209
|
+
const config = mergeChatHandlerConfig(agentIdOrConfig, options);
|
|
210
|
+
agent = config.agent;
|
|
211
|
+
options = config;
|
|
217
212
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
213
|
+
else {
|
|
214
|
+
// String-based API: createChatHandler("agentId", options?)
|
|
215
|
+
const agentId = agentIdOrConfig;
|
|
216
|
+
try {
|
|
217
|
+
agent = getAgent(agentId);
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
agentLogger.debug("getAgent lookup failed", { error });
|
|
221
|
+
return dntShim.Response.json({ error: "Agent not found" }, { status: 404 });
|
|
222
|
+
}
|
|
221
223
|
}
|
|
222
224
|
if (!agent) {
|
|
223
225
|
return dntShim.Response.json({ error: "Agent not found" }, { status: 404 });
|
package/esm/src/agent/index.d.ts
CHANGED
|
@@ -84,6 +84,6 @@ export { getTextFromParts, getToolArguments, hasArgs, hasInput } from "./types.j
|
|
|
84
84
|
export { BufferMemory, ConversationMemory, createMemory, createRedisMemory, type Memory, type MemoryPersistence, type MemoryStats, type RedisClient, RedisMemory, type RedisMemoryConfig, SummaryMemory, } from "./memory/index.js";
|
|
85
85
|
export { agentAsTool, createWorkflow, getAgent, getAgentsAsTools, getAllAgentIds, registerAgent, type WorkflowConfig, type WorkflowResult, type WorkflowStep, } from "./composition/index.js";
|
|
86
86
|
export { agent } from "./factory.js";
|
|
87
|
-
export { type ChatHandlerBeforeStream, type ChatHandlerBeforeStreamContext, type ChatHandlerBeforeStreamResult, type ChatHandlerMessageInput, type ChatHandlerOptions, createChatHandler, } from "./chat-handler.js";
|
|
87
|
+
export { type ChatHandlerBeforeStream, type ChatHandlerBeforeStreamContext, type ChatHandlerBeforeStreamResult, type ChatHandlerConfigWithAgent, type ChatHandlerMessageInput, type ChatHandlerOptions, createChatHandler, } from "./chat-handler.js";
|
|
88
88
|
export { AgentRuntime } from "./runtime/index.js";
|
|
89
89
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/src/agent/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+EG;AACH,OAAO,yBAAyB,CAAC;AAGjC,YAAY,EACV,KAAK,EACL,WAAW,EACX,YAAY,EACZ,eAAe,EACf,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,OAAO,IAAI,YAAY,EACvB,WAAW,EACX,aAAa,EACb,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,oBAAoB,EACpB,qBAAqB,EACrB,cAAc,GACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEnF,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,YAAY,EACZ,iBAAiB,EACjB,KAAK,MAAM,EACX,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,WAAW,EACX,KAAK,iBAAiB,EACtB,aAAa,GACd,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,WAAW,EACX,cAAc,EACd,QAAQ,EACR,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,YAAY,GAClB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,8BAA8B,EACnC,KAAK,6BAA6B,EAClC,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/src/agent/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+EG;AACH,OAAO,yBAAyB,CAAC;AAGjC,YAAY,EACV,KAAK,EACL,WAAW,EACX,YAAY,EACZ,eAAe,EACf,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,OAAO,IAAI,YAAY,EACvB,WAAW,EACX,aAAa,EACb,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,oBAAoB,EACpB,qBAAqB,EACrB,cAAc,GACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEnF,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,YAAY,EACZ,iBAAiB,EACjB,KAAK,MAAM,EACX,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,WAAW,EACX,KAAK,iBAAiB,EACtB,aAAa,GACd,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,WAAW,EACX,cAAc,EACd,QAAQ,EACR,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,YAAY,GAClB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,8BAA8B,EACnC,KAAK,6BAA6B,EAClC,KAAK,0BAA0B,EAC/B,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery-engine.d.ts","sourceRoot":"","sources":["../../../src/src/discovery/discovery-engine.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EACV,eAAe,EAEf,eAAe,EAEhB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"discovery-engine.d.ts","sourceRoot":"","sources":["../../../src/src/discovery/discovery-engine.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EACV,eAAe,EAEf,eAAe,EAEhB,MAAM,YAAY,CAAC;AAqIpB;;GAEG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CA0EnF"}
|
|
@@ -11,31 +11,80 @@ import { registerSkill, skillRegistry } from "../skill/registry.js";
|
|
|
11
11
|
import { importModule } from "./transpiler.js";
|
|
12
12
|
import { findTypeScriptFiles } from "./file-discovery.js";
|
|
13
13
|
import { agentHandler, discoverSkills, promptHandler, resourceHandler, taskHandler, toolHandler, workflowHandler, } from "./handlers/index.js";
|
|
14
|
+
import { filenameToId } from "./discovery-utils.js";
|
|
14
15
|
import { join } from "../platform/compat/path/index.js";
|
|
15
16
|
const logger = agentLogger.component("discovery");
|
|
17
|
+
function isIndexModule(file) {
|
|
18
|
+
const normalized = file.replace("file://", "");
|
|
19
|
+
return /(?:^|\/)index\.(?:ts|tsx|js|jsx)$/.test(normalized);
|
|
20
|
+
}
|
|
21
|
+
function compareDiscoveryFiles(a, b) {
|
|
22
|
+
const aIsIndex = isIndexModule(a);
|
|
23
|
+
const bIsIndex = isIndexModule(b);
|
|
24
|
+
if (aIsIndex !== bIsIndex)
|
|
25
|
+
return aIsIndex ? 1 : -1;
|
|
26
|
+
return a.localeCompare(b);
|
|
27
|
+
}
|
|
28
|
+
function collectDiscoveryCandidates(module, handler) {
|
|
29
|
+
const defaultItem = module.default;
|
|
30
|
+
if (handler.validate(defaultItem)) {
|
|
31
|
+
return [{ exportName: "default", item: defaultItem }];
|
|
32
|
+
}
|
|
33
|
+
const candidates = [];
|
|
34
|
+
for (const [exportName, value] of Object.entries(module)) {
|
|
35
|
+
if (exportName === "default")
|
|
36
|
+
continue;
|
|
37
|
+
if (!handler.validate(value))
|
|
38
|
+
continue;
|
|
39
|
+
candidates.push({ exportName, item: value });
|
|
40
|
+
}
|
|
41
|
+
return candidates;
|
|
42
|
+
}
|
|
43
|
+
function getCandidateId(candidate, file, dir, handler, useExportNameFallback) {
|
|
44
|
+
const derivedId = handler.getId(candidate.item, file, dir);
|
|
45
|
+
if (!useExportNameFallback)
|
|
46
|
+
return derivedId;
|
|
47
|
+
const fileId = filenameToId(file);
|
|
48
|
+
if (derivedId !== fileId)
|
|
49
|
+
return derivedId;
|
|
50
|
+
return candidate.exportName;
|
|
51
|
+
}
|
|
16
52
|
/**
|
|
17
53
|
* Discover items of a specific type in a directory
|
|
18
54
|
*/
|
|
19
55
|
async function discoverItems(dir, result, context, handler, verbose) {
|
|
20
|
-
const files = await findTypeScriptFiles(dir, context);
|
|
56
|
+
const files = (await findTypeScriptFiles(dir, context)).sort(compareDiscoveryFiles);
|
|
57
|
+
const resultMap = handler.getResultMap(result);
|
|
21
58
|
if (verbose) {
|
|
22
59
|
logger.info(`Found ${files.length} ${handler.typeName} files in ${dir}`);
|
|
23
60
|
}
|
|
24
61
|
for (const file of files) {
|
|
25
62
|
try {
|
|
26
63
|
const module = await importModule(file, context);
|
|
27
|
-
const
|
|
28
|
-
if (
|
|
64
|
+
const candidates = collectDiscoveryCandidates(module, handler);
|
|
65
|
+
if (candidates.length === 0) {
|
|
29
66
|
if (verbose) {
|
|
30
67
|
logger.warn(`${file} does not export a valid ${handler.typeName}`);
|
|
31
68
|
}
|
|
32
69
|
continue;
|
|
33
70
|
}
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
71
|
+
const useExportNameFallback = candidates.length > 1 || isIndexModule(file);
|
|
72
|
+
for (const candidate of candidates) {
|
|
73
|
+
const id = getCandidateId(candidate, file, dir, handler, useExportNameFallback);
|
|
74
|
+
if (resultMap.has(id)) {
|
|
75
|
+
if (verbose) {
|
|
76
|
+
logger.warn(`Duplicate ${handler.typeName} "${id}" in ${file}; keeping first`);
|
|
77
|
+
}
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const registered = handler.register(id, candidate.item, file, dir);
|
|
81
|
+
resultMap.set(id, registered);
|
|
82
|
+
if (verbose) {
|
|
83
|
+
const exportSuffix = candidate.exportName === "default"
|
|
84
|
+
? ""
|
|
85
|
+
: ` (export: ${candidate.exportName})`;
|
|
86
|
+
logger.info(`Registered ${handler.typeName}: ${id}${exportSuffix}`);
|
|
87
|
+
}
|
|
39
88
|
}
|
|
40
89
|
}
|
|
41
90
|
catch (error) {
|
package/package.json
CHANGED
package/src/deno.js
CHANGED
|
@@ -245,6 +245,20 @@ export interface ChatHandlerOptions {
|
|
|
245
245
|
beforeStream?: ChatHandlerBeforeStream;
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
+
/** Options when passing an agent instance directly. */
|
|
249
|
+
export interface ChatHandlerConfigWithAgent extends ChatHandlerOptions {
|
|
250
|
+
/** The agent instance to use (bypasses registry lookup). */
|
|
251
|
+
agent: import("./types.js").Agent;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function mergeChatHandlerConfig(
|
|
255
|
+
config: ChatHandlerConfigWithAgent,
|
|
256
|
+
options?: ChatHandlerOptions,
|
|
257
|
+
): ChatHandlerConfigWithAgent {
|
|
258
|
+
if (!options) return config;
|
|
259
|
+
return { ...options, ...config };
|
|
260
|
+
}
|
|
261
|
+
|
|
248
262
|
/**
|
|
249
263
|
* Extract the raw Request from either a raw Request or a Pages Router APIContext.
|
|
250
264
|
* Pages Router handlers receive `(ctx)` where `ctx.request` is the Request.
|
|
@@ -298,25 +312,55 @@ function extractRequest(requestOrCtx: unknown): dntShim.Request {
|
|
|
298
312
|
* - App Router: `app/api/chat/route.ts` — handler receives `(request, context)`
|
|
299
313
|
* - Pages Router: `pages/api/chat.ts` — handler receives `(ctx)`
|
|
300
314
|
*
|
|
315
|
+
* Accepts either:
|
|
316
|
+
* - `createChatHandler("agentId", options?)` — looks up agent by ID from the registry
|
|
317
|
+
* - `createChatHandler({ agent, ...options })` — uses the provided agent instance directly
|
|
318
|
+
*
|
|
301
319
|
* @example
|
|
302
320
|
* ```ts
|
|
303
|
-
*
|
|
321
|
+
* // By agent ID (requires auto-discovery registration)
|
|
304
322
|
* export const POST = createChatHandler("assistant");
|
|
323
|
+
*
|
|
324
|
+
* // By agent instance (no registry needed)
|
|
325
|
+
* import { myAgent } from "../../agents/my-agent";
|
|
326
|
+
* export const POST = createChatHandler({ agent: myAgent, beforeStream: ... });
|
|
305
327
|
* ```
|
|
306
328
|
*/
|
|
307
329
|
export function createChatHandler(
|
|
308
330
|
agentId: string,
|
|
309
331
|
options?: ChatHandlerOptions,
|
|
332
|
+
): (requestOrCtx: unknown) => Promise<dntShim.Response>;
|
|
333
|
+
export function createChatHandler(
|
|
334
|
+
config: ChatHandlerConfigWithAgent,
|
|
335
|
+
options?: ChatHandlerOptions,
|
|
336
|
+
): (requestOrCtx: unknown) => Promise<dntShim.Response>;
|
|
337
|
+
export function createChatHandler(
|
|
338
|
+
agentIdOrConfig: string | ChatHandlerConfigWithAgent,
|
|
339
|
+
options?: ChatHandlerOptions,
|
|
310
340
|
) {
|
|
311
341
|
return async function POST(requestOrCtx: unknown): Promise<dntShim.Response> {
|
|
312
342
|
const request = extractRequest(requestOrCtx);
|
|
343
|
+
|
|
313
344
|
let agent: ReturnType<typeof getAgent> | undefined;
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
345
|
+
|
|
346
|
+
if (
|
|
347
|
+
typeof agentIdOrConfig === "object" && agentIdOrConfig !== null && "agent" in agentIdOrConfig
|
|
348
|
+
) {
|
|
349
|
+
// Object-based API: createChatHandler({ agent, beforeStream, ... })
|
|
350
|
+
const config = mergeChatHandlerConfig(agentIdOrConfig, options);
|
|
351
|
+
agent = config.agent;
|
|
352
|
+
options = config;
|
|
353
|
+
} else {
|
|
354
|
+
// String-based API: createChatHandler("agentId", options?)
|
|
355
|
+
const agentId = agentIdOrConfig as string;
|
|
356
|
+
try {
|
|
357
|
+
agent = getAgent(agentId);
|
|
358
|
+
} catch (error) {
|
|
359
|
+
agentLogger.debug("getAgent lookup failed", { error });
|
|
360
|
+
return dntShim.Response.json({ error: "Agent not found" }, { status: 404 });
|
|
361
|
+
}
|
|
319
362
|
}
|
|
363
|
+
|
|
320
364
|
if (!agent) {
|
|
321
365
|
return dntShim.Response.json({ error: "Agent not found" }, { status: 404 });
|
|
322
366
|
}
|
package/src/src/agent/index.ts
CHANGED
|
@@ -26,10 +26,62 @@ import {
|
|
|
26
26
|
toolHandler,
|
|
27
27
|
workflowHandler,
|
|
28
28
|
} from "./handlers/index.js";
|
|
29
|
+
import { filenameToId } from "./discovery-utils.js";
|
|
29
30
|
import { join } from "../platform/compat/path/index.js";
|
|
30
31
|
|
|
31
32
|
const logger = agentLogger.component("discovery");
|
|
32
33
|
|
|
34
|
+
type DiscoveryCandidate<T> = {
|
|
35
|
+
exportName: string;
|
|
36
|
+
item: T;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function isIndexModule(file: string): boolean {
|
|
40
|
+
const normalized = file.replace("file://", "");
|
|
41
|
+
return /(?:^|\/)index\.(?:ts|tsx|js|jsx)$/.test(normalized);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function compareDiscoveryFiles(a: string, b: string): number {
|
|
45
|
+
const aIsIndex = isIndexModule(a);
|
|
46
|
+
const bIsIndex = isIndexModule(b);
|
|
47
|
+
if (aIsIndex !== bIsIndex) return aIsIndex ? 1 : -1;
|
|
48
|
+
return a.localeCompare(b);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function collectDiscoveryCandidates<T>(
|
|
52
|
+
module: unknown,
|
|
53
|
+
handler: DiscoveryHandler<T>,
|
|
54
|
+
): DiscoveryCandidate<T>[] {
|
|
55
|
+
const defaultItem = (module as { default?: T }).default;
|
|
56
|
+
if (handler.validate(defaultItem)) {
|
|
57
|
+
return [{ exportName: "default", item: defaultItem }];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const candidates: DiscoveryCandidate<T>[] = [];
|
|
61
|
+
for (const [exportName, value] of Object.entries(module as Record<string, unknown>)) {
|
|
62
|
+
if (exportName === "default") continue;
|
|
63
|
+
if (!handler.validate(value)) continue;
|
|
64
|
+
candidates.push({ exportName, item: value });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return candidates;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function getCandidateId<T>(
|
|
71
|
+
candidate: DiscoveryCandidate<T>,
|
|
72
|
+
file: string,
|
|
73
|
+
dir: string,
|
|
74
|
+
handler: DiscoveryHandler<T>,
|
|
75
|
+
useExportNameFallback: boolean,
|
|
76
|
+
): string {
|
|
77
|
+
const derivedId = handler.getId(candidate.item, file, dir);
|
|
78
|
+
if (!useExportNameFallback) return derivedId;
|
|
79
|
+
|
|
80
|
+
const fileId = filenameToId(file);
|
|
81
|
+
if (derivedId !== fileId) return derivedId;
|
|
82
|
+
return candidate.exportName;
|
|
83
|
+
}
|
|
84
|
+
|
|
33
85
|
/**
|
|
34
86
|
* Discover items of a specific type in a directory
|
|
35
87
|
*/
|
|
@@ -40,7 +92,8 @@ async function discoverItems<T>(
|
|
|
40
92
|
handler: DiscoveryHandler<T>,
|
|
41
93
|
verbose?: boolean,
|
|
42
94
|
): Promise<void> {
|
|
43
|
-
const files = await findTypeScriptFiles(dir, context);
|
|
95
|
+
const files = (await findTypeScriptFiles(dir, context)).sort(compareDiscoveryFiles);
|
|
96
|
+
const resultMap = handler.getResultMap(result);
|
|
44
97
|
|
|
45
98
|
if (verbose) {
|
|
46
99
|
logger.info(`Found ${files.length} ${handler.typeName} files in ${dir}`);
|
|
@@ -49,21 +102,40 @@ async function discoverItems<T>(
|
|
|
49
102
|
for (const file of files) {
|
|
50
103
|
try {
|
|
51
104
|
const module = await importModule(file, context);
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
if (!handler.validate(item)) {
|
|
105
|
+
const candidates = collectDiscoveryCandidates(module, handler);
|
|
106
|
+
if (candidates.length === 0) {
|
|
55
107
|
if (verbose) {
|
|
56
108
|
logger.warn(`${file} does not export a valid ${handler.typeName}`);
|
|
57
109
|
}
|
|
58
110
|
continue;
|
|
59
111
|
}
|
|
60
112
|
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
113
|
+
const useExportNameFallback = candidates.length > 1 || isIndexModule(file);
|
|
114
|
+
for (const candidate of candidates) {
|
|
115
|
+
const id = getCandidateId(
|
|
116
|
+
candidate,
|
|
117
|
+
file,
|
|
118
|
+
dir,
|
|
119
|
+
handler,
|
|
120
|
+
useExportNameFallback,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
if (resultMap.has(id)) {
|
|
124
|
+
if (verbose) {
|
|
125
|
+
logger.warn(`Duplicate ${handler.typeName} "${id}" in ${file}; keeping first`);
|
|
126
|
+
}
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
64
129
|
|
|
65
|
-
|
|
66
|
-
|
|
130
|
+
const registered = handler.register(id, candidate.item, file, dir);
|
|
131
|
+
resultMap.set(id, registered);
|
|
132
|
+
|
|
133
|
+
if (verbose) {
|
|
134
|
+
const exportSuffix = candidate.exportName === "default"
|
|
135
|
+
? ""
|
|
136
|
+
: ` (export: ${candidate.exportName})`;
|
|
137
|
+
logger.info(`Registered ${handler.typeName}: ${id}${exportSuffix}`);
|
|
138
|
+
}
|
|
67
139
|
}
|
|
68
140
|
} catch (error) {
|
|
69
141
|
result.errors.push({ file, error: ensureError(error) });
|