@junejs/server 1.0.0-dev.19 → 1.0.0-dev.24
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/dist/adapter-DcZ-W1-7.d.ts +62 -0
- package/dist/adapter.d.ts +1 -61
- package/dist/agent-durable.js +1 -43
- package/dist/agent-native.d.ts +15 -5
- package/dist/agent-native.js +85 -16
- package/dist/app.js +2 -7
- package/dist/build.d.ts +5 -26
- package/dist/build.js +10 -276
- package/dist/config-freeze-DwI-Rmvq.d.ts +17 -0
- package/dist/config-freeze.d.ts +2 -0
- package/dist/config-freeze.js +51 -0
- package/dist/connection-auth.d.ts +39 -0
- package/dist/connection-auth.js +31 -0
- package/dist/content-freeze-CyiW2fRK.d.ts +4 -0
- package/dist/content-freeze.d.ts +2 -0
- package/dist/content-freeze.js +83 -0
- package/dist/core-version.js +1 -1
- package/dist/index.d.ts +9 -4
- package/dist/index.js +7 -2
- package/dist/manifest-gcNWqWoI.d.ts +11 -0
- package/dist/manifest.d.ts +2 -0
- package/dist/manifest.js +111 -0
- package/dist/route-scan-DMlwoESy.d.ts +13 -0
- package/dist/route-scan.d.ts +2 -0
- package/dist/route-scan.js +55 -0
- package/dist/turn-events.d.ts +5 -0
- package/dist/turn-events.js +46 -0
- package/dist/worker-BkFHGtxp.d.ts +61 -0
- package/dist/worker.d.ts +1 -60
- package/package.json +2 -2
- package/src/agent-discover.ts +10 -7
- package/src/agent-durable.ts +1 -39
- package/src/agent-native.ts +133 -21
- package/src/app.ts +4 -6
- package/src/build.ts +26 -440
- package/src/config-freeze.ts +104 -0
- package/src/connection-auth.ts +101 -0
- package/src/content-freeze.ts +151 -0
- package/src/core-version.ts +1 -1
- package/src/index.ts +14 -0
- package/src/manifest.ts +128 -0
- package/src/route-scan.ts +84 -0
- package/src/turn-events.ts +43 -0
package/dist/manifest.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { findMiddlewareFile } from "./router.js";
|
|
2
|
+
import { resolveBoundary } from "./segment.js";
|
|
3
|
+
import { freezeConfig } from "./config-freeze.js";
|
|
4
|
+
import { scanAppRoutes } from "./route-scan.js";
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
6
|
+
import { basename, join } from "node:path";
|
|
7
|
+
import { pathToFileURL } from "node:url";
|
|
8
|
+
import { routeFromModule } from "@junejs/core/route";
|
|
9
|
+
//#region src/manifest.ts
|
|
10
|
+
async function importLayout(file) {
|
|
11
|
+
const mod = await import(pathToFileURL(file).href);
|
|
12
|
+
return typeof mod.default === "function" ? {
|
|
13
|
+
component: mod.default,
|
|
14
|
+
boundary: mod.segmentBoundary === true
|
|
15
|
+
} : null;
|
|
16
|
+
}
|
|
17
|
+
async function buildManifest(appRoot) {
|
|
18
|
+
const appDir = join(appRoot, "app");
|
|
19
|
+
const frozen = await freezeConfig(appRoot);
|
|
20
|
+
const scanned = await scanAppRoutes(appRoot);
|
|
21
|
+
const layoutCache = /* @__PURE__ */ new Map();
|
|
22
|
+
const loadCached = async (f) => {
|
|
23
|
+
if (!layoutCache.has(f)) layoutCache.set(f, await importLayout(f));
|
|
24
|
+
return layoutCache.get(f) ?? null;
|
|
25
|
+
};
|
|
26
|
+
const componentsFor = async (files) => {
|
|
27
|
+
const items = [];
|
|
28
|
+
for (const f of files) {
|
|
29
|
+
const c = await loadCached(f);
|
|
30
|
+
items.push({
|
|
31
|
+
file: f,
|
|
32
|
+
entry: c?.component ?? null,
|
|
33
|
+
boundary: !!c?.boundary
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return resolveBoundary(items);
|
|
37
|
+
};
|
|
38
|
+
const loadingCache = /* @__PURE__ */ new Map();
|
|
39
|
+
const loadingFor = async (file) => {
|
|
40
|
+
if (!file) return void 0;
|
|
41
|
+
if (!loadingCache.has(file)) {
|
|
42
|
+
const loaded = await importLayout(file);
|
|
43
|
+
loadingCache.set(file, loaded ? loaded.component : null);
|
|
44
|
+
}
|
|
45
|
+
return loadingCache.get(file) ?? void 0;
|
|
46
|
+
};
|
|
47
|
+
const routes = {};
|
|
48
|
+
const dynamicRoutes = [];
|
|
49
|
+
const resourceRoutes = [];
|
|
50
|
+
const layoutChains = {};
|
|
51
|
+
const layoutBoundaries = {};
|
|
52
|
+
const loadings = {};
|
|
53
|
+
for (const r of scanned) {
|
|
54
|
+
const mod = await import(pathToFileURL(r.file).href);
|
|
55
|
+
if (r.resource) {
|
|
56
|
+
const handler = mod.default;
|
|
57
|
+
if (typeof handler === "function") resourceRoutes.push({
|
|
58
|
+
pattern: r.path,
|
|
59
|
+
handler
|
|
60
|
+
});
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const def = routeFromModule(mod);
|
|
64
|
+
if (!def) continue;
|
|
65
|
+
const { chain, boundaryIndex, key } = await componentsFor(r.layouts);
|
|
66
|
+
const loading = await loadingFor(r.loading);
|
|
67
|
+
if (r.dynamic) {
|
|
68
|
+
dynamicRoutes.push({
|
|
69
|
+
pattern: r.path,
|
|
70
|
+
def
|
|
71
|
+
});
|
|
72
|
+
layoutChains[r.path] = chain;
|
|
73
|
+
} else {
|
|
74
|
+
routes[r.path] = def;
|
|
75
|
+
layoutChains[r.path] = chain;
|
|
76
|
+
}
|
|
77
|
+
if (boundaryIndex !== null && key !== null) layoutBoundaries[r.path] = {
|
|
78
|
+
index: boundaryIndex,
|
|
79
|
+
key
|
|
80
|
+
};
|
|
81
|
+
if (loading) loadings[r.path] = loading;
|
|
82
|
+
}
|
|
83
|
+
let extra;
|
|
84
|
+
const extraFile = findMiddlewareFile(appDir);
|
|
85
|
+
if (extraFile) {
|
|
86
|
+
const mod = await import(pathToFileURL(extraFile).href);
|
|
87
|
+
if (typeof mod.default === "function") extra = mod.default;
|
|
88
|
+
}
|
|
89
|
+
let agentName;
|
|
90
|
+
const agentDir = join(appDir, frozen.agent.runtime.dir);
|
|
91
|
+
if (frozen.agent.runtime.enabled && existsSync(agentDir)) {
|
|
92
|
+
const cfgFile = join(agentDir, "agent.ts");
|
|
93
|
+
agentName = (existsSync(cfgFile) ? (await import(pathToFileURL(cfgFile).href)).default : void 0)?.name ?? basename(agentDir);
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
routes,
|
|
97
|
+
dynamicRoutes,
|
|
98
|
+
resourceRoutes,
|
|
99
|
+
layoutChains,
|
|
100
|
+
layoutBoundaries,
|
|
101
|
+
loadings,
|
|
102
|
+
document: frozen.document,
|
|
103
|
+
agent: frozen.agent,
|
|
104
|
+
agentName,
|
|
105
|
+
i18n: frozen.i18n,
|
|
106
|
+
earlyHints: frozen.earlyHints,
|
|
107
|
+
extra
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
export { buildManifest, importLayout };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/route-scan.d.ts
|
|
2
|
+
type RouteEntry = {
|
|
3
|
+
path: string;
|
|
4
|
+
file: string;
|
|
5
|
+
dynamic: boolean;
|
|
6
|
+
resource?: boolean;
|
|
7
|
+
layouts: string[];
|
|
8
|
+
loading?: string;
|
|
9
|
+
};
|
|
10
|
+
declare function scanRoutes(appDir: string, dir?: string, layouts?: string[], out?: RouteEntry[], loading?: string): Promise<RouteEntry[]>;
|
|
11
|
+
declare function scanAppRoutes(appRoot: string): Promise<RouteEntry[]>;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { scanAppRoutes as n, scanRoutes as r, RouteEntry as t };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { readdir } from "node:fs/promises";
|
|
3
|
+
import { basename, join, relative, sep } from "node:path";
|
|
4
|
+
//#region src/route-scan.ts
|
|
5
|
+
const PAGE_BASENAMES = /* @__PURE__ */ new Set(["page", "index"]);
|
|
6
|
+
const ROUTE_EXTS = [
|
|
7
|
+
".tsx",
|
|
8
|
+
".jsx",
|
|
9
|
+
".ts",
|
|
10
|
+
".js"
|
|
11
|
+
];
|
|
12
|
+
const isRouteGroup = (name) => /^\(.+\)$/.test(name);
|
|
13
|
+
function segmentFile(dir, base) {
|
|
14
|
+
return ROUTE_EXTS.map((e) => join(dir, `${base}${e}`)).find(existsSync);
|
|
15
|
+
}
|
|
16
|
+
async function scanRoutes(appDir, dir = appDir, layouts = [], out = [], loading) {
|
|
17
|
+
const ownLayout = segmentFile(dir, "layout");
|
|
18
|
+
const chain = ownLayout ? [...layouts, ownLayout] : layouts;
|
|
19
|
+
const nearestLoading = segmentFile(dir, "loading") ?? loading;
|
|
20
|
+
const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
21
|
+
for (const e of entries) {
|
|
22
|
+
if (e.name.startsWith("_") || e.name.startsWith(".")) continue;
|
|
23
|
+
const full = join(dir, e.name);
|
|
24
|
+
if (e.isDirectory()) {
|
|
25
|
+
await scanRoutes(appDir, full, chain, out, nearestLoading);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
const ext = e.name.match(/\.[^.]+$/)?.[0] ?? "";
|
|
29
|
+
if (!ROUTE_EXTS.includes(ext)) continue;
|
|
30
|
+
const base = basename(e.name, ext);
|
|
31
|
+
const resource = base === "route";
|
|
32
|
+
if (!PAGE_BASENAMES.has(base) && !resource) continue;
|
|
33
|
+
const relDir = relative(appDir, dir);
|
|
34
|
+
const path = "/" + (relDir === "" ? [] : relDir.split(sep).filter((s) => !isRouteGroup(s))).join("/");
|
|
35
|
+
out.push({
|
|
36
|
+
path: path === "/" ? "/" : path,
|
|
37
|
+
file: full,
|
|
38
|
+
dynamic: /\[.+\]/.test(path),
|
|
39
|
+
resource,
|
|
40
|
+
layouts: chain,
|
|
41
|
+
loading: nearestLoading
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
async function scanAppRoutes(appRoot) {
|
|
47
|
+
const appDir = join(appRoot, "app");
|
|
48
|
+
const juneRoutesDir = join(appRoot, ".june", "routes");
|
|
49
|
+
const appRoutes = await scanRoutes(appDir);
|
|
50
|
+
const frameworkRoutes = existsSync(juneRoutesDir) ? await scanRoutes(juneRoutesDir) : [];
|
|
51
|
+
const appPaths = new Set(appRoutes.map((r) => r.path));
|
|
52
|
+
return [...appRoutes, ...frameworkRoutes.filter((r) => !appPaths.has(r.path))].sort((a, b) => a.path.localeCompare(b.path));
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
export { scanAppRoutes, scanRoutes };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
//#region src/turn-events.ts
|
|
2
|
+
function observeTurnEvents(session, turnId) {
|
|
3
|
+
const queue = [];
|
|
4
|
+
let terminal = false;
|
|
5
|
+
let notify;
|
|
6
|
+
const unsub = session.observe((e) => {
|
|
7
|
+
queue.push(e);
|
|
8
|
+
if (e.type === "turn.completed" || e.type === "turn.failed" || e.type === "turn.cancelled" || e.type === "input.requested") terminal = true;
|
|
9
|
+
notify?.();
|
|
10
|
+
}, { turnId });
|
|
11
|
+
return { [Symbol.asyncIterator]() {
|
|
12
|
+
return {
|
|
13
|
+
async next() {
|
|
14
|
+
for (;;) {
|
|
15
|
+
const e = queue.shift();
|
|
16
|
+
if (e) return {
|
|
17
|
+
value: e,
|
|
18
|
+
done: false
|
|
19
|
+
};
|
|
20
|
+
if (terminal) {
|
|
21
|
+
unsub();
|
|
22
|
+
return {
|
|
23
|
+
value: void 0,
|
|
24
|
+
done: true
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
await new Promise((resolve) => {
|
|
28
|
+
notify = resolve;
|
|
29
|
+
});
|
|
30
|
+
notify = void 0;
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
async return() {
|
|
34
|
+
unsub();
|
|
35
|
+
terminal = true;
|
|
36
|
+
queue.length = 0;
|
|
37
|
+
return {
|
|
38
|
+
value: void 0,
|
|
39
|
+
done: true
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
} };
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
export { observeTurnEvents };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { l as ResourceHandler, n as LayoutComponent, r as LoadingComponent, t as ExtraHandler } from "./pipeline-cCR6MGNK.js";
|
|
2
|
+
import { Channel, ChannelFactory } from "@junejs/core/agent-config";
|
|
3
|
+
import { BrandedRoute } from "@junejs/core/route";
|
|
4
|
+
import { AgentConfig } from "@junejs/core/config";
|
|
5
|
+
import React from "react";
|
|
6
|
+
import { DocumentConfig } from "@junejs/core/document";
|
|
7
|
+
import { I18nConfig } from "@junejs/core/i18n";
|
|
8
|
+
import { Resources } from "@junejs/core/resources";
|
|
9
|
+
//#region src/worker.d.ts
|
|
10
|
+
type WorkerExecutionContext = {
|
|
11
|
+
waitUntil?(p: Promise<unknown>): void;
|
|
12
|
+
};
|
|
13
|
+
type WorkerManifest = {
|
|
14
|
+
routes: Record<string, BrandedRoute>;
|
|
15
|
+
dynamicRoutes?: Array<{
|
|
16
|
+
pattern: string;
|
|
17
|
+
def: BrandedRoute;
|
|
18
|
+
}>;
|
|
19
|
+
resourceRoutes?: Array<{
|
|
20
|
+
pattern: string;
|
|
21
|
+
handler: ResourceHandler;
|
|
22
|
+
}>;
|
|
23
|
+
layoutChains?: Record<string, LayoutComponent[]>;
|
|
24
|
+
layoutBoundaries?: Record<string, {
|
|
25
|
+
index: number;
|
|
26
|
+
key: string;
|
|
27
|
+
}>;
|
|
28
|
+
loadings?: Record<string, LoadingComponent>;
|
|
29
|
+
document: DocumentConfig;
|
|
30
|
+
agent: AgentConfig;
|
|
31
|
+
agentName?: string;
|
|
32
|
+
agentChannels?: (Channel | ChannelFactory)[];
|
|
33
|
+
i18n?: I18nConfig;
|
|
34
|
+
earlyHints?: string[];
|
|
35
|
+
htmlCacheControl?: string;
|
|
36
|
+
notFound?: React.ComponentType<{
|
|
37
|
+
pathname: string;
|
|
38
|
+
}>;
|
|
39
|
+
extra?: ExtraHandler;
|
|
40
|
+
resources?: (env?: unknown) => Promise<Resources> | Resources;
|
|
41
|
+
services?: (env?: unknown) => unknown;
|
|
42
|
+
};
|
|
43
|
+
declare function createWorker(manifest: WorkerManifest): {
|
|
44
|
+
fetch(request: Request, env?: unknown, ctx?: WorkerExecutionContext): Promise<Response>;
|
|
45
|
+
};
|
|
46
|
+
type AssetEnv = {
|
|
47
|
+
ASSETS?: {
|
|
48
|
+
fetch(request: Request): Promise<Response>;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
type FetchPipeline = {
|
|
52
|
+
fetch(request: Request, env?: unknown, ctx?: WorkerExecutionContext): Promise<Response>;
|
|
53
|
+
};
|
|
54
|
+
declare function withAssets(pipeline: FetchPipeline, opts?: {
|
|
55
|
+
link?: string | null;
|
|
56
|
+
}): {
|
|
57
|
+
fetch(request: Request, env?: AssetEnv, ctx?: WorkerExecutionContext): Promise<Response>;
|
|
58
|
+
};
|
|
59
|
+
declare function withDenoAssets(pipeline: FetchPipeline): (request: Request) => Promise<Response>;
|
|
60
|
+
//#endregion
|
|
61
|
+
export { withDenoAssets as a, withAssets as i, WorkerManifest as n, createWorker as r, WorkerExecutionContext as t };
|
package/dist/worker.d.ts
CHANGED
|
@@ -1,61 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Channel, ChannelFactory } from "@junejs/core/agent-config";
|
|
3
|
-
import { BrandedRoute } from "@junejs/core/route";
|
|
4
|
-
import { AgentConfig } from "@junejs/core/config";
|
|
5
|
-
import React from "react";
|
|
6
|
-
import { DocumentConfig } from "@junejs/core/document";
|
|
7
|
-
import { I18nConfig } from "@junejs/core/i18n";
|
|
8
|
-
import { Resources } from "@junejs/core/resources";
|
|
9
|
-
//#region src/worker.d.ts
|
|
10
|
-
type WorkerExecutionContext = {
|
|
11
|
-
waitUntil?(p: Promise<unknown>): void;
|
|
12
|
-
};
|
|
13
|
-
type WorkerManifest = {
|
|
14
|
-
routes: Record<string, BrandedRoute>;
|
|
15
|
-
dynamicRoutes?: Array<{
|
|
16
|
-
pattern: string;
|
|
17
|
-
def: BrandedRoute;
|
|
18
|
-
}>;
|
|
19
|
-
resourceRoutes?: Array<{
|
|
20
|
-
pattern: string;
|
|
21
|
-
handler: ResourceHandler;
|
|
22
|
-
}>;
|
|
23
|
-
layoutChains?: Record<string, LayoutComponent[]>;
|
|
24
|
-
layoutBoundaries?: Record<string, {
|
|
25
|
-
index: number;
|
|
26
|
-
key: string;
|
|
27
|
-
}>;
|
|
28
|
-
loadings?: Record<string, LoadingComponent>;
|
|
29
|
-
document: DocumentConfig;
|
|
30
|
-
agent: AgentConfig;
|
|
31
|
-
agentName?: string;
|
|
32
|
-
agentChannels?: (Channel | ChannelFactory)[];
|
|
33
|
-
i18n?: I18nConfig;
|
|
34
|
-
earlyHints?: string[];
|
|
35
|
-
htmlCacheControl?: string;
|
|
36
|
-
notFound?: React.ComponentType<{
|
|
37
|
-
pathname: string;
|
|
38
|
-
}>;
|
|
39
|
-
extra?: ExtraHandler;
|
|
40
|
-
resources?: (env?: unknown) => Promise<Resources> | Resources;
|
|
41
|
-
services?: (env?: unknown) => unknown;
|
|
42
|
-
};
|
|
43
|
-
declare function createWorker(manifest: WorkerManifest): {
|
|
44
|
-
fetch(request: Request, env?: unknown, ctx?: WorkerExecutionContext): Promise<Response>;
|
|
45
|
-
};
|
|
46
|
-
type AssetEnv = {
|
|
47
|
-
ASSETS?: {
|
|
48
|
-
fetch(request: Request): Promise<Response>;
|
|
49
|
-
};
|
|
50
|
-
};
|
|
51
|
-
type FetchPipeline = {
|
|
52
|
-
fetch(request: Request, env?: unknown, ctx?: WorkerExecutionContext): Promise<Response>;
|
|
53
|
-
};
|
|
54
|
-
declare function withAssets(pipeline: FetchPipeline, opts?: {
|
|
55
|
-
link?: string | null;
|
|
56
|
-
}): {
|
|
57
|
-
fetch(request: Request, env?: AssetEnv, ctx?: WorkerExecutionContext): Promise<Response>;
|
|
58
|
-
};
|
|
59
|
-
declare function withDenoAssets(pipeline: FetchPipeline): (request: Request) => Promise<Response>;
|
|
60
|
-
//#endregion
|
|
1
|
+
import { a as withDenoAssets, i as withAssets, n as WorkerManifest, r as createWorker, t as WorkerExecutionContext } from "./worker-BkFHGtxp.js";
|
|
61
2
|
export { WorkerExecutionContext, WorkerManifest, createWorker, withAssets, withDenoAssets };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@junejs/server",
|
|
3
|
-
"version": "1.0.0-dev.
|
|
3
|
+
"version": "1.0.0-dev.24",
|
|
4
4
|
"description": "June host adapters + dev server. Host-coupled; depends on the pure @junejs/core contract layer.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -138,7 +138,7 @@
|
|
|
138
138
|
"rolldown": "^1.1.0"
|
|
139
139
|
},
|
|
140
140
|
"peerDependencies": {
|
|
141
|
-
"@junejs/core": ">=0.2.0-dev.
|
|
141
|
+
"@junejs/core": ">=0.2.0-dev.44",
|
|
142
142
|
"react": "^19.2.0",
|
|
143
143
|
"react-dom": "^19.2.0",
|
|
144
144
|
"react-server-dom-webpack": "19.2.7",
|
package/src/agent-discover.ts
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
// agent.ts → default-exports a plain config { name, model?, surfaces? }
|
|
7
7
|
// instructions.md → base system prompt
|
|
8
8
|
// instructions.<source>.md → per-surface instruction variant (#149)
|
|
9
|
-
// tools/*.ts → each default-exports a defineAction (a tool)
|
|
9
|
+
// tools/*.ts → each default-exports a defineAction (a tool),
|
|
10
|
+
// OR an array of defineActions (an integration
|
|
11
|
+
// shipping several tools, e.g. googleDriveTools())
|
|
10
12
|
// skills/*.md → each a procedure, loaded on demand (progressive disclosure)
|
|
11
13
|
// channels/*.ts → each default-exports a Channel (an inbound edge — pure transport)
|
|
12
14
|
// channels/*.md → DEPRECATED overlay location (warned; use instructions.<source>.md)
|
|
@@ -24,8 +26,7 @@ import { readdir, readFile } from "node:fs/promises";
|
|
|
24
26
|
import { basename, extname, join } from "node:path";
|
|
25
27
|
import { pathToFileURL } from "node:url";
|
|
26
28
|
|
|
27
|
-
import type
|
|
28
|
-
import { assembleAgent, parseSkill, type AgentConfigFile, type AgentDefinition, type AgentModule, type Channel, type ChannelFactory, type Skill } from "@junejs/core/agent-config";
|
|
29
|
+
import { assembleAgent, parseSkill, type AgentConfigFile, type AgentDefinition, type AgentModule, type Channel, type ChannelFactory, type Skill, type ToolEntry } from "@junejs/core/agent-config";
|
|
29
30
|
import type { Connection } from "@junejs/core/connections";
|
|
30
31
|
|
|
31
32
|
// `_`-prefixed files are private by convention (mirrors the app/ router) — most
|
|
@@ -72,10 +73,12 @@ export async function discoverAgentModule(dir: string): Promise<AgentModule> {
|
|
|
72
73
|
}
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
// A tool file default-exports one tool OR an array of tools (an integration
|
|
77
|
+
// like googleDriveTools()); assembly flattens arrays.
|
|
78
|
+
const tools: ToolEntry[] = [];
|
|
76
79
|
for (const f of await scan(join(dir, "tools"), ".ts")) {
|
|
77
80
|
const mod = await import(pathToFileURL(f).href);
|
|
78
|
-
if (mod.default) tools.push(mod.default as
|
|
81
|
+
if (mod.default) tools.push(mod.default as ToolEntry);
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
const skills: Skill[] = [];
|
|
@@ -117,8 +120,8 @@ export async function discoverAgentModule(dir: string): Promise<AgentModule> {
|
|
|
117
120
|
|
|
118
121
|
// Discover an agent from its directory, returning the assembled AgentDefinition
|
|
119
122
|
// (tools already adapted from defineActions; read_skill added if any skills
|
|
120
|
-
// exist). Mount it on a runtime with createNativeRuntime({ [agent.name]:
|
|
121
|
-
//
|
|
123
|
+
// exist). Mount it on a runtime with createNativeRuntime({ [agent.name]:
|
|
124
|
+
// toAgentDef(agent, model) }).
|
|
122
125
|
export async function discoverAgent(dir: string): Promise<AgentDefinition> {
|
|
123
126
|
return assembleAgent(await discoverAgentModule(dir), process.env);
|
|
124
127
|
}
|
package/src/agent-durable.ts
CHANGED
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
type TurnFailurePhase,
|
|
34
34
|
} from "@junejs/core/agent-runtime";
|
|
35
35
|
import type { Resources } from "@junejs/core/resources";
|
|
36
|
+
import { observeTurnEvents } from "./turn-events";
|
|
36
37
|
import {
|
|
37
38
|
channelDispatch,
|
|
38
39
|
DeliverUnsupportedError,
|
|
@@ -692,45 +693,6 @@ function sseTurnStream(session: AgentSession, turnId: string): ReadableStream<Ui
|
|
|
692
693
|
});
|
|
693
694
|
}
|
|
694
695
|
|
|
695
|
-
// The IN-PROCESS sibling of sseTurnStream: one turn's TurnEvents as an AsyncIterable, for a
|
|
696
|
-
// consumer living in the SAME isolate as the session (a delivered render). Subscribes eagerly
|
|
697
|
-
// at call time — not at first iteration — so the sseTurnStream timing guarantee carries over
|
|
698
|
-
// (call this synchronously after start(), before any event can emit); events landing before
|
|
699
|
-
// the consumer catches up are buffered. Ends after the turn's terminal event (completed,
|
|
700
|
-
// failed, or input.requested — a park ends this stream; a later /resume is a new one), and an
|
|
701
|
-
// early consumer exit (for-await break/return) unsubscribes rather than buffering forever.
|
|
702
|
-
function observeTurnEvents(session: AgentSession, turnId: string): AsyncIterable<TurnEvent> {
|
|
703
|
-
const queue: TurnEvent[] = [];
|
|
704
|
-
let terminal = false;
|
|
705
|
-
let notify: (() => void) | undefined;
|
|
706
|
-
const unsub = session.observe((e) => {
|
|
707
|
-
queue.push(e);
|
|
708
|
-
if (e.type === "turn.completed" || e.type === "turn.failed" || e.type === "turn.cancelled" || e.type === "input.requested") terminal = true;
|
|
709
|
-
notify?.();
|
|
710
|
-
}, { turnId });
|
|
711
|
-
return {
|
|
712
|
-
[Symbol.asyncIterator]() {
|
|
713
|
-
return {
|
|
714
|
-
async next(): Promise<IteratorResult<TurnEvent>> {
|
|
715
|
-
for (;;) {
|
|
716
|
-
const e = queue.shift();
|
|
717
|
-
if (e) return { value: e, done: false };
|
|
718
|
-
if (terminal) { unsub(); return { value: undefined, done: true }; }
|
|
719
|
-
await new Promise<void>((resolve) => { notify = resolve; });
|
|
720
|
-
notify = undefined;
|
|
721
|
-
}
|
|
722
|
-
},
|
|
723
|
-
async return(): Promise<IteratorResult<TurnEvent>> {
|
|
724
|
-
unsub();
|
|
725
|
-
terminal = true;
|
|
726
|
-
queue.length = 0;
|
|
727
|
-
return { value: undefined, done: true };
|
|
728
|
-
},
|
|
729
|
-
};
|
|
730
|
-
},
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
|
-
|
|
734
696
|
// Consume an SSE turn stream to its terminal state: the final text, or throw on failure.
|
|
735
697
|
// The non-streaming path (channels, a JSON chat response) uses this. Guards a non-SSE /
|
|
736
698
|
// body-less upstream response (a misroute or an error) into a clear error, not a TypeError.
|