create-loopengine 0.1.9 → 0.1.12
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/cli.js +5 -1
- package/package.json +1 -1
- package/template/.env.example +8 -2
- package/template/README.md +17 -0
- package/template/_gitignore +6 -0
- package/template/adapters/cli.ts +70 -11
- package/template/adapters/http.ts +1837 -36
- package/template/agent-registry.ts +58 -6
- package/template/package.json +3 -1
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
// Every adapter resolves "which agent" through this — the thing that
|
|
2
|
-
// makes agents pluggable. Defining a new agent means adding a file
|
|
3
|
-
// agents/ that exports `config` and `createModelCall` —
|
|
4
|
-
// in any adapter needs to change; discoverAgents scans
|
|
5
|
-
// keys each entry by AgentConfig.name (not the
|
|
2
|
+
// makes agents pluggable. Defining a new agent means adding a file (or a
|
|
3
|
+
// folder) to agents/ that exports `config` and `createModelCall` —
|
|
4
|
+
// nothing here or in any adapter needs to change; discoverAgents scans
|
|
5
|
+
// that directory and keys each entry by AgentConfig.name (not the
|
|
6
|
+
// filename) at import time.
|
|
6
7
|
import { dirname, join } from 'node:path'
|
|
7
8
|
import { fileURLToPath } from 'node:url'
|
|
8
|
-
import { discoverAgents, type AgentModule } from 'loopengine'
|
|
9
|
+
import { discoverAgents, type AgentConfig, type AgentModule, type ModelCall } from 'loopengine'
|
|
9
10
|
|
|
10
11
|
export type RegistryEntry = AgentModule
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
// This file lives at the project root, alongside agents/ — no '..' needed
|
|
14
|
+
// (unlike loopengine's own core/agent-registry.ts, which lives nested one
|
|
15
|
+
// level down in core/).
|
|
16
|
+
const projectRoot = dirname(fileURLToPath(import.meta.url))
|
|
17
|
+
const agentsDir = join(projectRoot, 'agents')
|
|
13
18
|
|
|
14
19
|
// Top-level await: ESM guarantees an importer's own evaluation (adapters/
|
|
15
20
|
// http.ts, adapters/cli.ts) waits for this module's top-level await to
|
|
@@ -25,3 +30,50 @@ export function listAgents(): string[] {
|
|
|
25
30
|
export function getEntry(name: string): RegistryEntry | undefined {
|
|
26
31
|
return entries.get(name)
|
|
27
32
|
}
|
|
33
|
+
|
|
34
|
+
/** Where this registry's own discoverAgents call resolved agents/
|
|
35
|
+
* against — exported so a caller creating a *new* agent module (the
|
|
36
|
+
* Admin UI's "Create new agent" flow, via loopengine's own scaffoldAgent)
|
|
37
|
+
* writes it to the exact same place this registry would find it on a
|
|
38
|
+
* future restart, rather than re-deriving a base dir independently (e.g.
|
|
39
|
+
* from process.cwd(), which isn't guaranteed to match if the process is
|
|
40
|
+
* launched from elsewhere) and risking the two drifting apart. */
|
|
41
|
+
export function projectDir(): string {
|
|
42
|
+
return projectRoot
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Adds one already-loaded agent module to the live registry in place —
|
|
46
|
+
* how a newly created agent becomes runnable in *this* process
|
|
47
|
+
* immediately, with no restart — unlike discoverAgents' own directory
|
|
48
|
+
* scan, which only ever runs once, at this module's own import time (see
|
|
49
|
+
* the top-level await above). Throws on a name collision with an
|
|
50
|
+
* already-registered agent, same as discoverAgents itself does for a
|
|
51
|
+
* collision found during its initial scan — a duplicate AgentConfig.name
|
|
52
|
+
* is a bug either way, not something to silently overwrite. */
|
|
53
|
+
export function registerAgent(agentModule: RegistryEntry): void {
|
|
54
|
+
const name = agentModule.config.name
|
|
55
|
+
if (entries.has(name)) {
|
|
56
|
+
throw new Error(`Duplicate agent name '${name}' — an agent with this name is already registered.`)
|
|
57
|
+
}
|
|
58
|
+
entries.set(name, agentModule)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Applies a partial update to an already-registered agent — how an edit
|
|
62
|
+
* made through the Admin UI takes effect in *this* running process
|
|
63
|
+
* immediately, the same "no restart needed" principle gateway-tools.yml/
|
|
64
|
+
* actauth.yml/SKILL.md edits already have. Mutates the same AgentConfig
|
|
65
|
+
* object every existing getEntry() caller already holds a reference to —
|
|
66
|
+
* via Object.assign, not a full replacement — so nothing needs to
|
|
67
|
+
* re-fetch it to see the change, and any field left out of `patch.config`
|
|
68
|
+
* (tools, rules, skillsDirs, ...) stays exactly as it was.
|
|
69
|
+
* `patch.createModelCall`, if given, replaces the cached one wholesale.
|
|
70
|
+
* Throws if `name` isn't already registered — this updates an existing
|
|
71
|
+
* entry, it's not a back door around registerAgent's own creation path. */
|
|
72
|
+
export function updateAgent(name: string, patch: { config?: Partial<AgentConfig>; createModelCall?: () => ModelCall }): void {
|
|
73
|
+
const entry = entries.get(name)
|
|
74
|
+
if (!entry) {
|
|
75
|
+
throw new Error(`No registered agent named '${name}' to update.`)
|
|
76
|
+
}
|
|
77
|
+
if (patch.config) Object.assign(entry.config, patch.config)
|
|
78
|
+
if (patch.createModelCall) entry.createModelCall = patch.createModelCall
|
|
79
|
+
}
|
package/template/package.json
CHANGED