@botcord/daemon 0.2.28 → 0.2.29
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/agent-workspace.d.ts +8 -0
- package/dist/agent-workspace.js +12 -2
- package/dist/gateway/runtimes/hermes-agent.js +8 -4
- package/dist/provision.js +4 -1
- package/package.json +1 -1
- package/src/__tests__/agent-workspace.test.ts +18 -0
- package/src/__tests__/provision.test.ts +5 -0
- package/src/agent-workspace.ts +13 -2
- package/src/gateway/runtimes/hermes-agent.ts +8 -3
- package/src/provision.ts +4 -0
|
@@ -54,6 +54,14 @@ export declare function ensureAgentHermesWorkspace(agentId: string, opts?: {
|
|
|
54
54
|
hermesHome: string;
|
|
55
55
|
hermesWorkspace: string;
|
|
56
56
|
};
|
|
57
|
+
/**
|
|
58
|
+
* Seed BotCord's bundled Hermes skills into a user-owned Hermes profile used
|
|
59
|
+
* by attach mode. Unlike `ensureAgentHermesWorkspace({ attached: true })`,
|
|
60
|
+
* this intentionally writes only the managed `botcord*` skill directories
|
|
61
|
+
* under the profile's `skills/` directory; it does not touch `.env`,
|
|
62
|
+
* `config.yaml`, sessions, or any user-authored skills.
|
|
63
|
+
*/
|
|
64
|
+
export declare function ensureAttachedHermesProfileSkills(profileHome: string): void;
|
|
57
65
|
/**
|
|
58
66
|
* Idempotently create the agent's home / workspace / state directories and
|
|
59
67
|
* seed the workspace Markdown files. Existing files are never overwritten —
|
package/dist/agent-workspace.js
CHANGED
|
@@ -334,8 +334,8 @@ export function ensureAgentHermesWorkspace(agentId, opts = {}) {
|
|
|
334
334
|
// Attach mode: HERMES_HOME points at the user's `~/.hermes/profiles/<n>/`
|
|
335
335
|
// so we MUST NOT touch the per-agent isolated home. The cwd
|
|
336
336
|
// (`hermesWorkspace`) is still ours and `prepareTurn` writes AGENTS.md
|
|
337
|
-
// there
|
|
338
|
-
//
|
|
337
|
+
// there. Profile-owned skill seeding is handled separately by
|
|
338
|
+
// `ensureAttachedHermesProfileSkills`.
|
|
339
339
|
if (opts.attached) {
|
|
340
340
|
return { hermesHome, hermesWorkspace };
|
|
341
341
|
}
|
|
@@ -420,6 +420,16 @@ function seedCodexSkills(codexHome) {
|
|
|
420
420
|
function seedHermesAgentSkills(hermesHome) {
|
|
421
421
|
copyBundledSkills(path.join(hermesHome, "skills"));
|
|
422
422
|
}
|
|
423
|
+
/**
|
|
424
|
+
* Seed BotCord's bundled Hermes skills into a user-owned Hermes profile used
|
|
425
|
+
* by attach mode. Unlike `ensureAgentHermesWorkspace({ attached: true })`,
|
|
426
|
+
* this intentionally writes only the managed `botcord*` skill directories
|
|
427
|
+
* under the profile's `skills/` directory; it does not touch `.env`,
|
|
428
|
+
* `config.yaml`, sessions, or any user-authored skills.
|
|
429
|
+
*/
|
|
430
|
+
export function ensureAttachedHermesProfileSkills(profileHome) {
|
|
431
|
+
seedHermesAgentSkills(profileHome);
|
|
432
|
+
}
|
|
423
433
|
/**
|
|
424
434
|
* Idempotently create the agent's home / workspace / state directories and
|
|
425
435
|
* seed the workspace Markdown files. Existing files are never overwritten —
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { agentHermesHomeDir, agentHermesWorkspaceDir, ensureAgentHermesWorkspace, } from "../../agent-workspace.js";
|
|
4
|
+
import { agentHermesHomeDir, agentHermesWorkspaceDir, ensureAttachedHermesProfileSkills, ensureAgentHermesWorkspace, } from "../../agent-workspace.js";
|
|
5
5
|
import { buildCliEnv } from "../cli-resolver.js";
|
|
6
6
|
import { AcpRuntimeAdapter, } from "./acp-stream.js";
|
|
7
7
|
import { firstExistingPath, readCommandVersion, resolveCommandOnPath, resolveHomePath, } from "./probe.js";
|
|
@@ -234,9 +234,10 @@ export class HermesAgentAdapter extends AcpRuntimeAdapter {
|
|
|
234
234
|
};
|
|
235
235
|
// Attach mode: BotCord agent shares a hermes profile (state.db /
|
|
236
236
|
// sessions / skills / .env) with the user's command-line `hermes`. In
|
|
237
|
-
// this mode we DO NOT seed a private home —
|
|
238
|
-
//
|
|
239
|
-
//
|
|
237
|
+
// this mode we DO NOT seed a private home — AGENTS.md is written under
|
|
238
|
+
// the per-agent hermes-workspace cwd (NOT into the profile root) by
|
|
239
|
+
// `prepareTurn`, while bundled BotCord skills are installed into the
|
|
240
|
+
// attached profile's `skills/` directory so hermes can discover them.
|
|
240
241
|
if (opts.hermesProfile) {
|
|
241
242
|
env.HERMES_HOME = hermesProfileHomeDir(opts.hermesProfile);
|
|
242
243
|
}
|
|
@@ -262,6 +263,9 @@ export class HermesAgentAdapter extends AcpRuntimeAdapter {
|
|
|
262
263
|
const { hermesWorkspace } = ensureAgentHermesWorkspace(opts.accountId, {
|
|
263
264
|
attached: !!opts.hermesProfile,
|
|
264
265
|
});
|
|
266
|
+
if (opts.hermesProfile) {
|
|
267
|
+
ensureAttachedHermesProfileSkills(hermesProfileHomeDir(opts.hermesProfile));
|
|
268
|
+
}
|
|
265
269
|
const target = path.join(hermesWorkspace, "AGENTS.md");
|
|
266
270
|
const tmp = path.join(hermesWorkspace, `.AGENTS.md.${process.pid}.tmp`);
|
|
267
271
|
mkdirSync(hermesWorkspace, { recursive: true, mode: 0o700 });
|
package/dist/provision.js
CHANGED
|
@@ -10,7 +10,7 @@ import path from "node:path";
|
|
|
10
10
|
import { BotCordClient, CONTROL_FRAME_TYPES, defaultCredentialsFile, derivePublicKey, loadStoredCredentials, writeCredentialsFile, } from "@botcord/protocol-core";
|
|
11
11
|
import { loadConfig, resolveConfiguredAgentIds, saveConfig, } from "./config.js";
|
|
12
12
|
import { BOTCORD_CHANNEL_TYPE, buildManagedRoutes, prepareGatewayProfile, } from "./daemon-config-map.js";
|
|
13
|
-
import { agentHomeDir, agentStateDir, agentWorkspaceDir, applyAgentIdentity, ensureAgentWorkspace, } from "./agent-workspace.js";
|
|
13
|
+
import { agentHomeDir, agentStateDir, agentWorkspaceDir, applyAgentIdentity, ensureAttachedHermesProfileSkills, ensureAgentWorkspace, } from "./agent-workspace.js";
|
|
14
14
|
import { detectRuntimes, getAdapterModule } from "./adapters/runtimes.js";
|
|
15
15
|
import { hermesProfileHomeDir, isValidHermesProfileName, listHermesProfiles, } from "./gateway/runtimes/hermes-agent.js";
|
|
16
16
|
import { log as daemonLog } from "./log.js";
|
|
@@ -285,6 +285,9 @@ async function installLocalAgent(credentials, ctx) {
|
|
|
285
285
|
keyId: credentials.keyId,
|
|
286
286
|
savedAt: credentials.savedAt,
|
|
287
287
|
});
|
|
288
|
+
if (credentials.runtime === "hermes-agent" && credentials.hermesProfile) {
|
|
289
|
+
ensureAttachedHermesProfileSkills(hermesProfileHomeDir(credentials.hermesProfile));
|
|
290
|
+
}
|
|
288
291
|
}
|
|
289
292
|
catch (err) {
|
|
290
293
|
try {
|
package/package.json
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
agentStateDir,
|
|
19
19
|
agentWorkspaceDir,
|
|
20
20
|
applyAgentIdentity,
|
|
21
|
+
ensureAttachedHermesProfileSkills,
|
|
21
22
|
ensureAgentCodexHome,
|
|
22
23
|
ensureAgentHermesWorkspace,
|
|
23
24
|
ensureAgentWorkspace,
|
|
@@ -150,6 +151,23 @@ describe("ensureAgentWorkspace", () => {
|
|
|
150
151
|
expect(reseeded).toContain("name: botcord");
|
|
151
152
|
});
|
|
152
153
|
|
|
154
|
+
it("seeds bundled skills into an attached Hermes profile without creating private home state", () => {
|
|
155
|
+
const profileHome = path.join(tmpHome, ".hermes", "profiles", "coder");
|
|
156
|
+
mkdirSync(profileHome, { recursive: true });
|
|
157
|
+
|
|
158
|
+
const { hermesHome, hermesWorkspace } = ensureAgentHermesWorkspace("ag_hermes_attach", {
|
|
159
|
+
attached: true,
|
|
160
|
+
});
|
|
161
|
+
ensureAttachedHermesProfileSkills(profileHome);
|
|
162
|
+
|
|
163
|
+
expect(existsSync(path.join(profileHome, "skills", "botcord", "SKILL.md"))).toBe(true);
|
|
164
|
+
expect(existsSync(path.join(profileHome, "skills", "botcord-user-guide", "SKILL.md"))).toBe(
|
|
165
|
+
true,
|
|
166
|
+
);
|
|
167
|
+
expect(existsSync(hermesWorkspace)).toBe(true);
|
|
168
|
+
expect(existsSync(hermesHome)).toBe(false);
|
|
169
|
+
});
|
|
170
|
+
|
|
153
171
|
it("does not overwrite a user-modified memory.md on a second call", () => {
|
|
154
172
|
ensureAgentWorkspace("ag_keep", {});
|
|
155
173
|
const memoryPath = path.join(agentWorkspaceDir("ag_keep"), "memory.md");
|
|
@@ -1527,6 +1527,11 @@ describe("provision_agent hermes profile attach", () => {
|
|
|
1527
1527
|
>;
|
|
1528
1528
|
expect(saved.hermesProfile).toBe("coder");
|
|
1529
1529
|
expect(saved.runtime).toBe("hermes-agent");
|
|
1530
|
+
expect(
|
|
1531
|
+
fs.existsSync(
|
|
1532
|
+
nodePath.join(tmp, ".hermes", "profiles", "coder", "skills", "botcord", "SKILL.md"),
|
|
1533
|
+
),
|
|
1534
|
+
).toBe(true);
|
|
1530
1535
|
});
|
|
1531
1536
|
});
|
|
1532
1537
|
|
package/src/agent-workspace.ts
CHANGED
|
@@ -370,8 +370,8 @@ export function ensureAgentHermesWorkspace(
|
|
|
370
370
|
// Attach mode: HERMES_HOME points at the user's `~/.hermes/profiles/<n>/`
|
|
371
371
|
// so we MUST NOT touch the per-agent isolated home. The cwd
|
|
372
372
|
// (`hermesWorkspace`) is still ours and `prepareTurn` writes AGENTS.md
|
|
373
|
-
// there
|
|
374
|
-
//
|
|
373
|
+
// there. Profile-owned skill seeding is handled separately by
|
|
374
|
+
// `ensureAttachedHermesProfileSkills`.
|
|
375
375
|
if (opts.attached) {
|
|
376
376
|
return { hermesHome, hermesWorkspace };
|
|
377
377
|
}
|
|
@@ -462,6 +462,17 @@ function seedHermesAgentSkills(hermesHome: string): void {
|
|
|
462
462
|
copyBundledSkills(path.join(hermesHome, "skills"));
|
|
463
463
|
}
|
|
464
464
|
|
|
465
|
+
/**
|
|
466
|
+
* Seed BotCord's bundled Hermes skills into a user-owned Hermes profile used
|
|
467
|
+
* by attach mode. Unlike `ensureAgentHermesWorkspace({ attached: true })`,
|
|
468
|
+
* this intentionally writes only the managed `botcord*` skill directories
|
|
469
|
+
* under the profile's `skills/` directory; it does not touch `.env`,
|
|
470
|
+
* `config.yaml`, sessions, or any user-authored skills.
|
|
471
|
+
*/
|
|
472
|
+
export function ensureAttachedHermesProfileSkills(profileHome: string): void {
|
|
473
|
+
seedHermesAgentSkills(profileHome);
|
|
474
|
+
}
|
|
475
|
+
|
|
465
476
|
/**
|
|
466
477
|
* Idempotently create the agent's home / workspace / state directories and
|
|
467
478
|
* seed the workspace Markdown files. Existing files are never overwritten —
|
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import {
|
|
5
5
|
agentHermesHomeDir,
|
|
6
6
|
agentHermesWorkspaceDir,
|
|
7
|
+
ensureAttachedHermesProfileSkills,
|
|
7
8
|
ensureAgentHermesWorkspace,
|
|
8
9
|
} from "../../agent-workspace.js";
|
|
9
10
|
import { buildCliEnv } from "../cli-resolver.js";
|
|
@@ -277,9 +278,10 @@ export class HermesAgentAdapter extends AcpRuntimeAdapter {
|
|
|
277
278
|
};
|
|
278
279
|
// Attach mode: BotCord agent shares a hermes profile (state.db /
|
|
279
280
|
// sessions / skills / .env) with the user's command-line `hermes`. In
|
|
280
|
-
// this mode we DO NOT seed a private home —
|
|
281
|
-
//
|
|
282
|
-
//
|
|
281
|
+
// this mode we DO NOT seed a private home — AGENTS.md is written under
|
|
282
|
+
// the per-agent hermes-workspace cwd (NOT into the profile root) by
|
|
283
|
+
// `prepareTurn`, while bundled BotCord skills are installed into the
|
|
284
|
+
// attached profile's `skills/` directory so hermes can discover them.
|
|
283
285
|
if (opts.hermesProfile) {
|
|
284
286
|
env.HERMES_HOME = hermesProfileHomeDir(opts.hermesProfile);
|
|
285
287
|
} else if (opts.accountId) {
|
|
@@ -304,6 +306,9 @@ export class HermesAgentAdapter extends AcpRuntimeAdapter {
|
|
|
304
306
|
const { hermesWorkspace } = ensureAgentHermesWorkspace(opts.accountId, {
|
|
305
307
|
attached: !!opts.hermesProfile,
|
|
306
308
|
});
|
|
309
|
+
if (opts.hermesProfile) {
|
|
310
|
+
ensureAttachedHermesProfileSkills(hermesProfileHomeDir(opts.hermesProfile));
|
|
311
|
+
}
|
|
307
312
|
const target = path.join(hermesWorkspace, "AGENTS.md");
|
|
308
313
|
const tmp = path.join(hermesWorkspace, `.AGENTS.md.${process.pid}.tmp`);
|
|
309
314
|
mkdirSync(hermesWorkspace, { recursive: true, mode: 0o700 });
|
package/src/provision.ts
CHANGED
|
@@ -52,6 +52,7 @@ import {
|
|
|
52
52
|
agentStateDir,
|
|
53
53
|
agentWorkspaceDir,
|
|
54
54
|
applyAgentIdentity,
|
|
55
|
+
ensureAttachedHermesProfileSkills,
|
|
55
56
|
ensureAgentWorkspace,
|
|
56
57
|
} from "./agent-workspace.js";
|
|
57
58
|
import { detectRuntimes, getAdapterModule } from "./adapters/runtimes.js";
|
|
@@ -431,6 +432,9 @@ async function installLocalAgent(
|
|
|
431
432
|
keyId: credentials.keyId,
|
|
432
433
|
savedAt: credentials.savedAt,
|
|
433
434
|
});
|
|
435
|
+
if (credentials.runtime === "hermes-agent" && credentials.hermesProfile) {
|
|
436
|
+
ensureAttachedHermesProfileSkills(hermesProfileHomeDir(credentials.hermesProfile));
|
|
437
|
+
}
|
|
434
438
|
} catch (err) {
|
|
435
439
|
try {
|
|
436
440
|
unlinkSync(credentialsFile);
|