arcie 0.1.9 → 0.2.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/dist/_tsup-dts-rollup.d.ts +49 -0
- package/dist/{build-TWNCPXBU.js → build-GKOF7N76.js} +2 -2
- package/dist/{chunk-4WSILP75.js → chunk-3R65GQAE.js} +5 -4
- package/dist/{chunk-4WSILP75.js.map → chunk-3R65GQAE.js.map} +1 -1
- package/dist/{chunk-KVSX4MLK.js → chunk-XZPC3JSU.js} +59 -2
- package/dist/chunk-XZPC3JSU.js.map +1 -0
- package/dist/cli/index.js +7 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/{dev-KO2CRPG3.js → dev-XBXOVOVE.js} +193 -41
- package/dist/dev-XBXOVOVE.js.map +1 -0
- package/dist/index.js +2 -2
- package/dist/{init-Y7VNAVF7.js → init-BQK2LLTM.js} +11 -24
- package/dist/init-BQK2LLTM.js.map +1 -0
- package/dist/runner/index.js +2 -2
- package/package.json +1 -1
- package/templates/web-chat/app/api/agents/route.ts +37 -0
- package/templates/web-chat/app/api/chat/route.ts +10 -2
- package/templates/web-chat/components/agent-picker.tsx +102 -0
- package/templates/web-chat/components/chat.tsx +42 -2
- package/templates/web-chat/components/input-bar.tsx +4 -0
- package/dist/chunk-KVSX4MLK.js.map +0 -1
- package/dist/dev-KO2CRPG3.js.map +0 -1
- package/dist/init-Y7VNAVF7.js.map +0 -1
- /package/dist/{build-TWNCPXBU.js.map → build-GKOF7N76.js.map} +0 -0
package/dist/runner/index.js
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { NextRequest } from "next/server";
|
|
2
|
+
|
|
3
|
+
export const runtime = "nodejs";
|
|
4
|
+
export const dynamic = "force-dynamic";
|
|
5
|
+
|
|
6
|
+
const ARCIE_URL = process.env.ARCIE_URL ?? "http://localhost:3000";
|
|
7
|
+
|
|
8
|
+
export async function GET(_req: NextRequest) {
|
|
9
|
+
try {
|
|
10
|
+
const upstream = await fetch(`${ARCIE_URL}/agents`, {
|
|
11
|
+
headers: { "Content-Type": "application/json" },
|
|
12
|
+
});
|
|
13
|
+
if (!upstream.ok) {
|
|
14
|
+
return Response.json(
|
|
15
|
+
{ error: `Upstream ${upstream.status}`, agents: [] },
|
|
16
|
+
{ status: upstream.status },
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
const agents = (await upstream.json()) as Array<{
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
model: string;
|
|
23
|
+
description: string;
|
|
24
|
+
}>;
|
|
25
|
+
return Response.json(agents);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
return Response.json(
|
|
28
|
+
{
|
|
29
|
+
error: "Could not reach arcie server",
|
|
30
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
31
|
+
hint: `Is arcie running at ${ARCIE_URL}? Start it with \`arcie dev\`.`,
|
|
32
|
+
agents: [],
|
|
33
|
+
},
|
|
34
|
+
{ status: 502 },
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -6,14 +6,22 @@ export const dynamic = "force-dynamic";
|
|
|
6
6
|
const ARCIE_URL = process.env.ARCIE_URL ?? "http://localhost:3000";
|
|
7
7
|
|
|
8
8
|
export async function POST(req: NextRequest) {
|
|
9
|
-
const { message } = (await req.json()) as {
|
|
9
|
+
const { message, agentId } = (await req.json()) as {
|
|
10
|
+
message: string;
|
|
11
|
+
agentId?: string;
|
|
12
|
+
};
|
|
10
13
|
if (typeof message !== "string" || message.length === 0) {
|
|
11
14
|
return new Response("message required", { status: 400 });
|
|
12
15
|
}
|
|
13
16
|
|
|
17
|
+
const target =
|
|
18
|
+
typeof agentId === "string" && agentId.length > 0
|
|
19
|
+
? `${ARCIE_URL}/agents/${encodeURIComponent(agentId)}`
|
|
20
|
+
: ARCIE_URL;
|
|
21
|
+
|
|
14
22
|
let upstream: Response;
|
|
15
23
|
try {
|
|
16
|
-
upstream = await fetch(
|
|
24
|
+
upstream = await fetch(target, {
|
|
17
25
|
method: "POST",
|
|
18
26
|
headers: { "Content-Type": "application/json" },
|
|
19
27
|
body: JSON.stringify({ message, stream: true }),
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Check, ChevronDown } from "lucide-react";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
export interface AgentSummary {
|
|
8
|
+
readonly id: string;
|
|
9
|
+
readonly name: string;
|
|
10
|
+
readonly model: string;
|
|
11
|
+
readonly description: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface AgentPickerProps {
|
|
15
|
+
agents: readonly AgentSummary[];
|
|
16
|
+
value: string;
|
|
17
|
+
onChange(id: string): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function AgentPicker({ agents, value, onChange }: AgentPickerProps) {
|
|
21
|
+
const [open, setOpen] = React.useState(false);
|
|
22
|
+
const rootRef = React.useRef<HTMLDivElement>(null);
|
|
23
|
+
|
|
24
|
+
React.useEffect(() => {
|
|
25
|
+
if (!open) return;
|
|
26
|
+
const onClick = (event: MouseEvent) => {
|
|
27
|
+
if (rootRef.current !== null && !rootRef.current.contains(event.target as Node)) {
|
|
28
|
+
setOpen(false);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
document.addEventListener("mousedown", onClick);
|
|
32
|
+
return () => document.removeEventListener("mousedown", onClick);
|
|
33
|
+
}, [open]);
|
|
34
|
+
|
|
35
|
+
const selected = agents.find((a) => a.id === value) ?? agents[0];
|
|
36
|
+
|
|
37
|
+
if (agents.length === 0) return null;
|
|
38
|
+
if (agents.length === 1) {
|
|
39
|
+
return (
|
|
40
|
+
<div className="px-2.5 py-1.5 rounded-xl text-[11px] font-medium bg-card/60 text-foreground truncate max-w-[220px]">
|
|
41
|
+
{selected?.name ?? value}
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<div ref={rootRef} className="relative">
|
|
48
|
+
<button
|
|
49
|
+
type="button"
|
|
50
|
+
onClick={() => setOpen((prev) => !prev)}
|
|
51
|
+
className={cn(
|
|
52
|
+
"flex items-center gap-1.5 px-2.5 py-1.5 rounded-xl text-[11px] font-medium transition-all cursor-pointer",
|
|
53
|
+
open ? "bg-primary/15 text-foreground ring-1 ring-primary/30" : "bg-card/60 hover:bg-card/85 text-foreground",
|
|
54
|
+
)}
|
|
55
|
+
>
|
|
56
|
+
<span className="truncate max-w-[160px]">{selected?.name ?? value}</span>
|
|
57
|
+
<ChevronDown className="h-3 w-3 opacity-70" />
|
|
58
|
+
</button>
|
|
59
|
+
|
|
60
|
+
{open && (
|
|
61
|
+
<div className="absolute z-50 bottom-full mb-2 left-0 w-72 rounded-2xl border border-border/40 bg-popover shadow-2xl p-1.5 flex flex-col max-h-[320px] overflow-y-auto backdrop-blur-xl animate-in fade-in slide-in-from-bottom-2 duration-150">
|
|
62
|
+
{agents.map((agent) => {
|
|
63
|
+
const isSelected = agent.id === value;
|
|
64
|
+
return (
|
|
65
|
+
<button
|
|
66
|
+
key={agent.id}
|
|
67
|
+
type="button"
|
|
68
|
+
onClick={() => {
|
|
69
|
+
onChange(agent.id);
|
|
70
|
+
setOpen(false);
|
|
71
|
+
}}
|
|
72
|
+
className={cn(
|
|
73
|
+
"w-full flex items-start gap-2 px-2.5 py-2 rounded-lg text-left transition-all hover:bg-muted/50 cursor-pointer",
|
|
74
|
+
isSelected && "bg-muted",
|
|
75
|
+
)}
|
|
76
|
+
>
|
|
77
|
+
<div className="min-w-0 flex-1">
|
|
78
|
+
<div className="flex items-center gap-1.5">
|
|
79
|
+
<span className="text-[11px] font-medium text-foreground truncate">
|
|
80
|
+
{agent.name}
|
|
81
|
+
</span>
|
|
82
|
+
{agent.model.length > 0 && (
|
|
83
|
+
<span className="text-[9px] text-muted-foreground/60 truncate font-mono">
|
|
84
|
+
{agent.model}
|
|
85
|
+
</span>
|
|
86
|
+
)}
|
|
87
|
+
</div>
|
|
88
|
+
{agent.description.length > 0 && (
|
|
89
|
+
<div className="text-[10px] text-muted-foreground/70 mt-0.5 leading-relaxed line-clamp-2">
|
|
90
|
+
{agent.description}
|
|
91
|
+
</div>
|
|
92
|
+
)}
|
|
93
|
+
</div>
|
|
94
|
+
{isSelected && <Check className="h-3.5 w-3.5 text-primary shrink-0 mt-0.5" />}
|
|
95
|
+
</button>
|
|
96
|
+
);
|
|
97
|
+
})}
|
|
98
|
+
</div>
|
|
99
|
+
)}
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import * as React from "react";
|
|
4
|
+
import { AgentPicker, type AgentSummary } from "@/components/agent-picker";
|
|
4
5
|
import { InputBar } from "@/components/input-bar";
|
|
5
6
|
import { Message } from "@/components/message";
|
|
6
7
|
import { readArcieStream } from "@/lib/stream";
|
|
@@ -10,12 +11,46 @@ function newId(prefix: string): string {
|
|
|
10
11
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
11
12
|
}
|
|
12
13
|
|
|
14
|
+
const AGENT_STORAGE_KEY = "arcie:selected-agent";
|
|
15
|
+
|
|
13
16
|
export function Chat() {
|
|
14
17
|
const [messages, setMessages] = React.useState<UiMessage[]>([]);
|
|
15
18
|
const [streaming, setStreaming] = React.useState(false);
|
|
19
|
+
const [agents, setAgents] = React.useState<AgentSummary[]>([]);
|
|
20
|
+
const [agentId, setAgentId] = React.useState<string>("agent");
|
|
16
21
|
const abortRef = React.useRef<AbortController | undefined>(undefined);
|
|
17
22
|
const containerRef = React.useRef<HTMLDivElement>(null);
|
|
18
23
|
|
|
24
|
+
React.useEffect(() => {
|
|
25
|
+
let cancelled = false;
|
|
26
|
+
void (async () => {
|
|
27
|
+
try {
|
|
28
|
+
const response = await fetch("/api/agents");
|
|
29
|
+
if (!response.ok) return;
|
|
30
|
+
const list = (await response.json()) as AgentSummary[];
|
|
31
|
+
if (cancelled || !Array.isArray(list)) return;
|
|
32
|
+
setAgents(list);
|
|
33
|
+
const stored = typeof window !== "undefined" ? localStorage.getItem(AGENT_STORAGE_KEY) : null;
|
|
34
|
+
if (stored !== null && list.some((a) => a.id === stored)) {
|
|
35
|
+
setAgentId(stored);
|
|
36
|
+
} else if (list.length > 0) {
|
|
37
|
+
setAgentId(list[0]!.id);
|
|
38
|
+
}
|
|
39
|
+
} catch {
|
|
40
|
+
// Non-fatal — chat still works, just posts to primary.
|
|
41
|
+
}
|
|
42
|
+
})();
|
|
43
|
+
return () => {
|
|
44
|
+
cancelled = true;
|
|
45
|
+
};
|
|
46
|
+
}, []);
|
|
47
|
+
|
|
48
|
+
const changeAgent = (id: string) => {
|
|
49
|
+
setAgentId(id);
|
|
50
|
+
if (typeof window !== "undefined") localStorage.setItem(AGENT_STORAGE_KEY, id);
|
|
51
|
+
clearAll();
|
|
52
|
+
};
|
|
53
|
+
|
|
19
54
|
React.useEffect(() => {
|
|
20
55
|
const el = containerRef.current;
|
|
21
56
|
if (el === null) return;
|
|
@@ -66,7 +101,7 @@ export function Chat() {
|
|
|
66
101
|
const response = await fetch("/api/chat", {
|
|
67
102
|
method: "POST",
|
|
68
103
|
headers: { "Content-Type": "application/json" },
|
|
69
|
-
body: JSON.stringify({ message: text }),
|
|
104
|
+
body: JSON.stringify({ message: text, agentId }),
|
|
70
105
|
signal: controller.signal,
|
|
71
106
|
});
|
|
72
107
|
if (!response.ok) {
|
|
@@ -175,7 +210,7 @@ export function Chat() {
|
|
|
175
210
|
abortRef.current = undefined;
|
|
176
211
|
}
|
|
177
212
|
},
|
|
178
|
-
[messages],
|
|
213
|
+
[messages, agentId],
|
|
179
214
|
);
|
|
180
215
|
|
|
181
216
|
const regenerate = () => {
|
|
@@ -213,6 +248,11 @@ export function Chat() {
|
|
|
213
248
|
onClear={clearAll}
|
|
214
249
|
streaming={streaming}
|
|
215
250
|
hasMessages={messages.length > 0}
|
|
251
|
+
leftSlot={
|
|
252
|
+
agents.length > 0 ? (
|
|
253
|
+
<AgentPicker agents={agents} value={agentId} onChange={changeAgent} />
|
|
254
|
+
) : undefined
|
|
255
|
+
}
|
|
216
256
|
/>
|
|
217
257
|
</div>
|
|
218
258
|
);
|
|
@@ -11,6 +11,8 @@ interface InputBarProps {
|
|
|
11
11
|
streaming: boolean;
|
|
12
12
|
disabled?: boolean;
|
|
13
13
|
hasMessages?: boolean;
|
|
14
|
+
/** Optional slot rendered on the left of the footer bar (e.g. an agent picker). */
|
|
15
|
+
leftSlot?: React.ReactNode;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export function InputBar({
|
|
@@ -20,6 +22,7 @@ export function InputBar({
|
|
|
20
22
|
streaming,
|
|
21
23
|
disabled,
|
|
22
24
|
hasMessages,
|
|
25
|
+
leftSlot,
|
|
23
26
|
}: InputBarProps) {
|
|
24
27
|
const [value, setValue] = React.useState("");
|
|
25
28
|
const [files, setFiles] = React.useState<File[]>([]);
|
|
@@ -134,6 +137,7 @@ export function InputBar({
|
|
|
134
137
|
>
|
|
135
138
|
<Paperclip className="h-4 w-4" />
|
|
136
139
|
</button>
|
|
140
|
+
{leftSlot}
|
|
137
141
|
</div>
|
|
138
142
|
|
|
139
143
|
<div className="flex items-center gap-2 shrink-0">
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/loader.ts"],"sourcesContent":["import { readFileSync, existsSync, readdirSync, statSync } from \"node:fs\";\nimport { resolve, basename, extname } from \"node:path\";\nimport type {\n AgentManifest,\n AgentConfig,\n ToolConfig,\n SkillConfig,\n HookConfig,\n ChannelConfig,\n ConnectionConfig,\n ScheduleConfig,\n SessionConfig,\n PolicyConfig,\n SubagentManifest,\n} from \"./types\";\n\nexport interface LoadedAgent {\n manifest: AgentManifest;\n agentDir: string;\n}\n\nexport async function loadAgent(agentDir: string): Promise<LoadedAgent> {\n const absDir = resolve(process.cwd(), agentDir);\n\n if (!existsSync(absDir)) {\n throw new Error(`Agent directory not found: ${absDir}`);\n }\n\n const agentFile = resolve(absDir, \"agent.ts\");\n let config: AgentConfig = { model: \"gpt-4o\" };\n\n if (existsSync(agentFile)) {\n try {\n const mod = await import(agentFile);\n if (mod.default) {\n config = { ...config, ...mod.default };\n }\n } catch (err) {\n console.warn(`[arcie] Failed to load agent.ts: ${(err as Error).message}. Using defaults.`);\n }\n }\n\n const instructions = loadInstructionsFile(absDir);\n const tools = await loadDirectory<ToolConfig>(absDir, \"tools\");\n const skills = await loadDirectory<SkillConfig>(absDir, \"skills\");\n const hooks = await loadDirectory<HookConfig>(absDir, \"hooks\");\n const channels = await loadDirectory<ChannelConfig>(absDir, \"channels\");\n const connections = await loadDirectory<ConnectionConfig>(absDir, \"connections\");\n const schedules = await loadDirectory<ScheduleConfig>(absDir, \"schedules\");\n const subagents = await loadSubagents(absDir);\n const session = await loadSessionConfig(absDir);\n const policy = await loadPolicyConfig(absDir);\n\n return {\n agentDir: absDir,\n manifest: {\n config,\n instructions,\n tools,\n skills,\n hooks,\n channels,\n connections,\n schedules,\n subagents,\n session: session ?? undefined,\n policy: policy ?? undefined,\n },\n };\n}\n\n/**\n * Loads each `subagents/<id>/` directory as a self-contained child agent.\n * Unlike the other slots, subagents are *directories* (not flat files): every\n * one is a mini-agent with its own config, instructions, tools, and skills.\n * A subagent must declare a `description` so the orchestrator knows when to\n * delegate to it — a missing one is a hard authoring error.\n */\nasync function loadSubagents(\n agentDir: string\n): Promise<Record<string, SubagentManifest>> {\n const dirPath = resolve(agentDir, \"subagents\");\n if (!existsSync(dirPath)) return {};\n\n const result: Record<string, SubagentManifest> = {};\n\n for (const entry of readdirSync(dirPath, { withFileTypes: true })) {\n if (!entry.isDirectory() || entry.name.startsWith(\".\")) continue;\n const id = entry.name;\n const subDir = resolve(dirPath, id);\n\n const agentFile = resolve(subDir, \"agent.ts\");\n if (!existsSync(agentFile)) {\n throw new Error(`Subagent \"${id}\" is missing agent.ts`);\n }\n\n let mod: { default?: AgentConfig };\n try {\n mod = await import(agentFile);\n } catch (err) {\n throw new Error(`Failed to load subagent \"${id}\": ${(err as Error).message}`);\n }\n\n const config = mod.default;\n if (!config) {\n throw new Error(`Subagent \"${id}\" agent.ts must have a default export`);\n }\n if (!config.description) {\n throw new Error(`Subagent \"${id}\" must declare a description in agent.ts`);\n }\n\n result[id] = {\n config,\n instructions: loadInstructionsFile(subDir),\n tools: await loadDirectory<ToolConfig>(subDir, \"tools\"),\n skills: await loadDirectory<SkillConfig>(subDir, \"skills\"),\n };\n }\n\n return result;\n}\n\nfunction loadInstructionsFile(agentDir: string): string {\n const mdPath = resolve(agentDir, \"instructions.md\");\n if (existsSync(mdPath)) {\n return readFileSync(mdPath, \"utf-8\");\n }\n return \"You are a helpful AI agent.\";\n}\n\nasync function loadDirectory<T>(\n agentDir: string,\n dirName: string\n): Promise<Record<string, T>> {\n const dirPath = resolve(agentDir, dirName);\n if (!existsSync(dirPath)) return {};\n\n const entries = readdirSync(dirPath).filter(\n (f) => !f.startsWith(\".\") && (f.endsWith(\".ts\") || f.endsWith(\".js\"))\n );\n\n const result: Record<string, T> = {};\n\n for (const entry of entries) {\n const name = basename(entry, extname(entry));\n const filePath = resolve(dirPath, entry);\n\n if (statSync(filePath).isFile()) {\n try {\n const mod = await import(filePath);\n if (mod.default) {\n result[name] = mod.default;\n }\n } catch {\n // skip unloadable files\n }\n }\n }\n\n return result;\n}\n\nasync function loadSessionConfig(agentDir: string): Promise<SessionConfig | null> {\n const tsPath = resolve(agentDir, \"sessions\", \"config.ts\");\n if (existsSync(tsPath)) {\n try {\n const mod = await import(tsPath);\n return mod.default ?? null;\n } catch {\n return null;\n }\n }\n return null;\n}\n\nasync function loadPolicyConfig(agentDir: string): Promise<PolicyConfig | null> {\n const tsPath = resolve(agentDir, \"policies\", \"index.ts\");\n if (existsSync(tsPath)) {\n try {\n const mod = await import(tsPath);\n return mod.default ?? null;\n } catch {\n return null;\n }\n }\n return null;\n}\n"],"mappings":"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","names":[]}
|