@agent-spaces/server 0.3.65 → 0.4.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/adapters/agent-runtime.js +8 -0
- package/dist/adapters/codex-function-tool-bridge.js +152 -0
- package/dist/adapters/codex-runtime.js +23 -4
- package/dist/adapters/git.js +25 -3
- package/dist/adapters/hermes-runtime.js +813 -0
- package/dist/adapters/langchain-runtime.js +384 -21
- package/dist/adapters/oh-my-pi-runtime.js +858 -0
- package/dist/adapters/open-agent-sdk-runtime.js +202 -5
- package/dist/adapters/open-agent-sdk-runtime.test.js +35 -0
- package/dist/agents/agent-message-parts.js +52 -2
- package/dist/agents/issue-task-controller.js +71 -2
- package/dist/agents/pull-request-agent.js +115 -0
- package/dist/agents/scheduler-agent.js +1 -1
- package/dist/agents/title-generator-agent.js +120 -0
- package/dist/app.js +61 -4
- package/dist/middleware/auth.js +1 -1
- package/dist/package.json +2 -1
- package/dist/routes/agent-sse.js +70 -6
- package/dist/routes/channel.js +23 -2
- package/dist/routes/chat-run.js +191 -0
- package/dist/routes/chat.js +142 -0
- package/dist/routes/command.js +16 -0
- package/dist/routes/data.js +189 -0
- package/dist/routes/git.js +10 -1
- package/dist/routes/import.js +199 -0
- package/dist/routes/issue.js +17 -5
- package/dist/routes/notification.js +10 -0
- package/dist/routes/plugin.js +69 -0
- package/dist/routes/version.js +95 -0
- package/dist/routes/workflow-hook.js +71 -0
- package/dist/routes/workflow.js +282 -7
- package/dist/routes/workspace.js +13 -4
- package/dist/routes/worktree.js +93 -0
- package/dist/services/agent.js +123 -36
- package/dist/services/ai-text.js +185 -0
- package/dist/services/builtin-tools/index.js +1 -0
- package/dist/services/builtin-tools/workflow-editor-tools.js +509 -0
- package/dist/services/builtin-tools/workflow-exec-tools.js +320 -0
- package/dist/services/chat.js +134 -0
- package/dist/services/command-process-manager.js +16 -0
- package/dist/services/execution-manager.js +1346 -0
- package/dist/services/generated-title.js +59 -0
- package/dist/services/gitignore.js +22 -18
- package/dist/services/interaction-manager.js +114 -0
- package/dist/services/issue-retry.js +25 -0
- package/dist/services/notification-center.js +9 -0
- package/dist/services/output-style.js +8 -1
- package/dist/services/plugin.js +257 -0
- package/dist/services/prompt-template.js +8 -1
- package/dist/services/pty.js +20 -0
- package/dist/services/search.js +16 -6
- package/dist/services/skill.js +2 -0
- package/dist/services/version.js +69 -0
- package/dist/services/workflow-command-runner.js +24 -0
- package/dist/services/workflow-trigger-service.js +137 -0
- package/dist/services/workflow.js +219 -39
- package/dist/services/workspace.js +23 -1
- package/dist/services/worktree.js +207 -0
- package/dist/storage/agent-store.js +8 -0
- package/dist/storage/chat-store.js +151 -0
- package/dist/storage/database-store.js +6 -0
- package/dist/storage/json-store.js +2 -1
- package/dist/storage/kanban-store.js +6 -0
- package/dist/storage/workflow-store.js +386 -22
- package/dist/storage/worktree-store.js +33 -0
- package/dist/web/404.html +1 -1
- package/dist/web/__next.__PAGE__.txt +4 -4
- package/dist/web/__next._full.txt +21 -21
- package/dist/web/__next._head.txt +4 -4
- package/dist/web/__next._index.txt +13 -13
- package/dist/web/__next._tree.txt +2 -2
- package/dist/web/_next/static/chunks/{08x85faoz58i7.js → 0-92u-tiuy40a.js} +4 -4
- package/dist/web/_next/static/chunks/{0db14450oqzhg.js → 0-yizd8osmas2.js} +1 -1
- package/dist/web/_next/static/chunks/0.ln~3mnv8.69.js +1 -0
- package/dist/web/_next/static/chunks/{11cchuwoupmbf.js → 01y3e9pemva7b.js} +10 -10
- package/dist/web/_next/static/chunks/02l~tmzrgny.~.js +1 -0
- package/dist/web/_next/static/chunks/03f1bwuynjdmk.js +1 -0
- package/dist/web/_next/static/chunks/04cb4kc9r2pd1.js +1 -0
- package/dist/web/_next/static/chunks/04de6ha5_e3pp.js +1 -0
- package/dist/web/_next/static/chunks/09he0p79ov1rn.js +1 -0
- package/dist/web/_next/static/chunks/09vpocs4zuc8h.js +1 -0
- package/dist/web/_next/static/chunks/0_o2exgr0e_nh.js +1 -0
- package/dist/web/_next/static/chunks/{0xbaakj0dfc0..js → 0_y9nskc-xt2k.js} +1 -1
- package/dist/web/_next/static/chunks/0clnj-1fi6u8j.js +3 -0
- package/dist/web/_next/static/chunks/0gv40by0zzxs_.js +1 -0
- package/dist/web/_next/static/chunks/0iusr8y88.kjg.js +1 -0
- package/dist/web/_next/static/chunks/{0-a.-hl3wv99k.js → 0j-8418fxvkd1.js} +1 -1
- package/dist/web/_next/static/chunks/0j.wxxyoclma5.js +1 -0
- package/dist/web/_next/static/chunks/{0.6b64sy0g5.3.js → 0jq.16sozqik..js} +1 -1
- package/dist/web/_next/static/chunks/0jw8xdk_fpnp0.css +1 -0
- package/dist/web/_next/static/chunks/0l1lkd-wcotma.js +1 -0
- package/dist/web/_next/static/chunks/{0u6h-hqc_i52e.js → 0ncxf_zr4n839.js} +1 -1
- package/dist/web/_next/static/chunks/0ndo7um~v7vco.js +1 -0
- package/dist/web/_next/static/chunks/0ngk.~kan8wq_.js +1 -0
- package/dist/web/_next/static/chunks/0ppr271hz0xoq.js +1 -0
- package/dist/web/_next/static/chunks/0rt9bofnn6qvn.js +1 -0
- package/dist/web/_next/static/chunks/0t64.9jd00hio.js +3 -0
- package/dist/web/_next/static/chunks/0taui9--5n6-5.js +1 -0
- package/dist/web/_next/static/chunks/0ty39169lh~z3.js +1 -0
- package/dist/web/_next/static/chunks/0v8nof~waedlu.js +1 -0
- package/dist/web/_next/static/chunks/{0-pi.67w.ymsr.js → 0vcfoagnpf.aw.js} +1 -1
- package/dist/web/_next/static/chunks/{0vis~lx0nxgn-.js → 0wwwq.gyyz0vg.js} +3 -3
- package/dist/web/_next/static/chunks/0x5lak8yz7pm7.js +1 -0
- package/dist/web/_next/static/chunks/0xon_.4-o0_zq.js +7 -0
- package/dist/web/_next/static/chunks/0zjnrbt7inime.js +1 -0
- package/dist/web/_next/static/chunks/0zwebpitvpuv1.js +1 -0
- package/dist/web/_next/static/chunks/0~02o4g-o6n~m.js +1 -0
- package/dist/web/_next/static/chunks/{0w7.tb29958zj.js → 0~fjezp.k4qk3.js} +1 -1
- package/dist/web/_next/static/chunks/0~sh93re1-_rp.js +1 -0
- package/dist/web/_next/static/chunks/10_niiq0k6o-p.js +5 -0
- package/dist/web/_next/static/chunks/12tjr0_2ccrtq.js +2 -0
- package/dist/web/_next/static/chunks/12x5.go_x9mfk.js +1 -0
- package/dist/web/_next/static/chunks/{0l2w0qz1ea7ql.js → 12~5dk9i9d16t.js} +2 -2
- package/dist/web/_next/static/chunks/13iye4rv5p31b.js +1 -0
- package/dist/web/_next/static/chunks/{01~quun6sefgl.js → 13~y-b.b9rrw4.js} +1 -1
- package/dist/web/_next/static/chunks/151wzhb5q0lo5.js +1 -0
- package/dist/web/_not-found/__next._full.txt +19 -19
- package/dist/web/_not-found/__next._head.txt +4 -4
- package/dist/web/_not-found/__next._index.txt +13 -13
- package/dist/web/_not-found/__next._not-found.__PAGE__.txt +2 -2
- package/dist/web/_not-found/__next._not-found.txt +3 -3
- package/dist/web/_not-found/__next._tree.txt +2 -2
- package/dist/web/_not-found.html +1 -1
- package/dist/web/_not-found.txt +19 -19
- package/dist/web/index.html +1 -1
- package/dist/web/index.txt +21 -21
- package/dist/web/login/__next._full.txt +21 -21
- package/dist/web/login/__next._head.txt +4 -4
- package/dist/web/login/__next._index.txt +13 -13
- package/dist/web/login/__next._tree.txt +2 -2
- package/dist/web/login/__next.login.__PAGE__.txt +4 -4
- package/dist/web/login/__next.login.txt +3 -3
- package/dist/web/login.html +1 -1
- package/dist/web/login.txt +21 -21
- package/dist/web/settings/__next._full.txt +23 -23
- package/dist/web/settings/__next._head.txt +4 -4
- package/dist/web/settings/__next._index.txt +13 -13
- package/dist/web/settings/__next._tree.txt +2 -2
- package/dist/web/settings/__next.settings.__PAGE__.txt +4 -4
- package/dist/web/settings/__next.settings.txt +5 -5
- package/dist/web/settings/agents/__next._full.txt +23 -23
- package/dist/web/settings/agents/__next._head.txt +4 -4
- package/dist/web/settings/agents/__next._index.txt +13 -13
- package/dist/web/settings/agents/__next._tree.txt +2 -2
- package/dist/web/settings/agents/__next.settings.agents.__PAGE__.txt +4 -4
- package/dist/web/settings/agents/__next.settings.agents.txt +3 -3
- package/dist/web/settings/agents/__next.settings.txt +5 -5
- package/dist/web/settings/agents.html +1 -1
- package/dist/web/settings/agents.txt +23 -23
- package/dist/web/settings/mcps/__next._full.txt +23 -23
- package/dist/web/settings/mcps/__next._head.txt +4 -4
- package/dist/web/settings/mcps/__next._index.txt +13 -13
- package/dist/web/settings/mcps/__next._tree.txt +2 -2
- package/dist/web/settings/mcps/__next.settings.mcps.__PAGE__.txt +4 -4
- package/dist/web/settings/mcps/__next.settings.mcps.txt +3 -3
- package/dist/web/settings/mcps/__next.settings.txt +5 -5
- package/dist/web/settings/mcps.html +1 -1
- package/dist/web/settings/mcps.txt +23 -23
- package/dist/web/settings/models/__next._full.txt +23 -23
- package/dist/web/settings/models/__next._head.txt +4 -4
- package/dist/web/settings/models/__next._index.txt +13 -13
- package/dist/web/settings/models/__next._tree.txt +2 -2
- package/dist/web/settings/models/__next.settings.models.__PAGE__.txt +4 -4
- package/dist/web/settings/models/__next.settings.models.txt +3 -3
- package/dist/web/settings/models/__next.settings.txt +5 -5
- package/dist/web/settings/models.html +1 -1
- package/dist/web/settings/models.txt +23 -23
- package/dist/web/settings/output-styles/__next._full.txt +23 -23
- package/dist/web/settings/output-styles/__next._head.txt +4 -4
- package/dist/web/settings/output-styles/__next._index.txt +13 -13
- package/dist/web/settings/output-styles/__next._tree.txt +2 -2
- package/dist/web/settings/output-styles/__next.settings.output-styles.__PAGE__.txt +4 -4
- package/dist/web/settings/output-styles/__next.settings.output-styles.txt +3 -3
- package/dist/web/settings/output-styles/__next.settings.txt +5 -5
- package/dist/web/settings/output-styles.html +1 -1
- package/dist/web/settings/output-styles.txt +23 -23
- package/dist/web/settings/prompts/__next._full.txt +23 -23
- package/dist/web/settings/prompts/__next._head.txt +4 -4
- package/dist/web/settings/prompts/__next._index.txt +13 -13
- package/dist/web/settings/prompts/__next._tree.txt +2 -2
- package/dist/web/settings/prompts/__next.settings.prompts.__PAGE__.txt +4 -4
- package/dist/web/settings/prompts/__next.settings.prompts.txt +3 -3
- package/dist/web/settings/prompts/__next.settings.txt +5 -5
- package/dist/web/settings/prompts.html +1 -1
- package/dist/web/settings/prompts.txt +23 -23
- package/dist/web/settings/providers/__next._full.txt +23 -23
- package/dist/web/settings/providers/__next._head.txt +4 -4
- package/dist/web/settings/providers/__next._index.txt +13 -13
- package/dist/web/settings/providers/__next._tree.txt +2 -2
- package/dist/web/settings/providers/__next.settings.providers.__PAGE__.txt +4 -4
- package/dist/web/settings/providers/__next.settings.providers.txt +3 -3
- package/dist/web/settings/providers/__next.settings.txt +5 -5
- package/dist/web/settings/providers.html +1 -1
- package/dist/web/settings/providers.txt +23 -23
- package/dist/web/settings/skills/__next._full.txt +23 -23
- package/dist/web/settings/skills/__next._head.txt +4 -4
- package/dist/web/settings/skills/__next._index.txt +13 -13
- package/dist/web/settings/skills/__next._tree.txt +2 -2
- package/dist/web/settings/skills/__next.settings.skills.__PAGE__.txt +4 -4
- package/dist/web/settings/skills/__next.settings.skills.txt +3 -3
- package/dist/web/settings/skills/__next.settings.txt +5 -5
- package/dist/web/settings/skills.html +1 -1
- package/dist/web/settings/skills.txt +23 -23
- package/dist/web/settings/tools/__next._full.txt +23 -23
- package/dist/web/settings/tools/__next._head.txt +4 -4
- package/dist/web/settings/tools/__next._index.txt +13 -13
- package/dist/web/settings/tools/__next._tree.txt +2 -2
- package/dist/web/settings/tools/__next.settings.tools.__PAGE__.txt +4 -4
- package/dist/web/settings/tools/__next.settings.tools.txt +3 -3
- package/dist/web/settings/tools/__next.settings.txt +5 -5
- package/dist/web/settings/tools.html +1 -1
- package/dist/web/settings/tools.txt +23 -23
- package/dist/web/settings.html +1 -1
- package/dist/web/settings.txt +23 -23
- package/dist/web/workflows/__next._full.txt +22 -22
- package/dist/web/workflows/__next._head.txt +4 -4
- package/dist/web/workflows/__next._index.txt +13 -13
- package/dist/web/workflows/__next._tree.txt +2 -2
- package/dist/web/workflows/__next.workflows.__PAGE__.txt +4 -4
- package/dist/web/workflows/__next.workflows.txt +3 -3
- package/dist/web/workflows.html +1 -1
- package/dist/web/workflows.txt +22 -22
- package/dist/web/workspace/_/__next._full.txt +20 -20
- package/dist/web/workspace/_/__next._head.txt +4 -4
- package/dist/web/workspace/_/__next._index.txt +13 -13
- package/dist/web/workspace/_/__next._tree.txt +2 -2
- package/dist/web/workspace/_/__next.workspace.$d$id.__PAGE__.txt +3 -3
- package/dist/web/workspace/_/__next.workspace.$d$id.txt +3 -3
- package/dist/web/workspace/_/__next.workspace.txt +3 -3
- package/dist/web/workspace/_.html +1 -1
- package/dist/web/workspace/_.txt +20 -20
- package/dist/web/workspaces/__next._full.txt +21 -21
- package/dist/web/workspaces/__next._head.txt +4 -4
- package/dist/web/workspaces/__next._index.txt +13 -13
- package/dist/web/workspaces/__next._tree.txt +2 -2
- package/dist/web/workspaces/__next.workspaces.__PAGE__.txt +4 -4
- package/dist/web/workspaces/__next.workspaces.txt +3 -3
- package/dist/web/workspaces.html +1 -1
- package/dist/web/workspaces.txt +21 -21
- package/dist/ws/agent-prompt.js +36 -3
- package/dist/ws/agent-runner.js +30 -18
- package/dist/ws/chat-handler.js +89 -0
- package/dist/ws/connection-manager.js +49 -2
- package/dist/ws/execution-channels.js +88 -0
- package/dist/ws/handler.js +32 -5
- package/dist/ws/message-parts.js +130 -12
- package/dist/ws/terminal-handler.js +26 -0
- package/package.json +13 -1
- package/public/avatars/user.jpg +0 -0
- package/dist/web/_next/static/chunks/0-v_p6j5u3_9l.js +0 -1
- package/dist/web/_next/static/chunks/0.-x29t-8gk0r.js +0 -1
- package/dist/web/_next/static/chunks/0.l5e8~r7_0~p.js +0 -1
- package/dist/web/_next/static/chunks/0.n~yfwf4~6w-.js +0 -1
- package/dist/web/_next/static/chunks/00-syqzi-6zxr.js +0 -1
- package/dist/web/_next/static/chunks/02j4sr9psucf-.js +0 -1
- package/dist/web/_next/static/chunks/06si8l0doqe8p.js +0 -1
- package/dist/web/_next/static/chunks/08kfx8on9oty~.js +0 -1
- package/dist/web/_next/static/chunks/08ugbvzr20drp.js +0 -2
- package/dist/web/_next/static/chunks/0_cqojbge48kq.js +0 -5
- package/dist/web/_next/static/chunks/0d6~~lv0-6kex.js +0 -3
- package/dist/web/_next/static/chunks/0gdwop9cnpmtu.js +0 -1
- package/dist/web/_next/static/chunks/0ghx3j_.-gdyd.js +0 -1
- package/dist/web/_next/static/chunks/0kbvoxla7f751.js +0 -1
- package/dist/web/_next/static/chunks/0paaat8y96ayc.js +0 -7
- package/dist/web/_next/static/chunks/0pq318f8xgafg.js +0 -1
- package/dist/web/_next/static/chunks/0t6s8of0qr6nc.js +0 -1
- package/dist/web/_next/static/chunks/0xt-8wk21gs7g.js +0 -1
- package/dist/web/_next/static/chunks/0yd.m1afqg.2t.js +0 -3
- package/dist/web/_next/static/chunks/0yhbr8hl09b-5.js +0 -1
- package/dist/web/_next/static/chunks/0z~vqzbm_q371.js +0 -1
- package/dist/web/_next/static/chunks/0~~_ltu6dx_ch.js +0 -1
- package/dist/web/_next/static/chunks/12oxcgyhpaxw~.js +0 -1
- package/dist/web/_next/static/chunks/1452eibz62gs6.css +0 -1
- package/dist/web/_next/static/chunks/16zlreiems5cn.js +0 -1
- package/dist/web/_next/static/chunks/18awqjk9-o5m0.js +0 -1
- /package/dist/web/_next/static/{x4-CsKe6qUQyFDWVtfvUg → rGXyAK8rgod1NjF3bLZAY}/_buildManifest.js +0 -0
- /package/dist/web/_next/static/{x4-CsKe6qUQyFDWVtfvUg → rGXyAK8rgod1NjF3bLZAY}/_clientMiddlewareManifest.js +0 -0
- /package/dist/web/_next/static/{x4-CsKe6qUQyFDWVtfvUg → rGXyAK8rgod1NjF3bLZAY}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,813 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { basename, delimiter, extname, join } from 'node:path';
|
|
4
|
+
import { appendOutputStyleToPrompt, summarizeResult } from './agent-runtime-types.js';
|
|
5
|
+
import { startCodexFunctionToolBridge } from './codex-function-tool-bridge.js';
|
|
6
|
+
/**
|
|
7
|
+
* Runtime backed by the external Hermes CLI.
|
|
8
|
+
*
|
|
9
|
+
* Hermes does not currently expose a structured JS SDK here, so this adapter
|
|
10
|
+
* classifies CLI text output before forwarding it to the UI.
|
|
11
|
+
*/
|
|
12
|
+
export class HermesRuntime {
|
|
13
|
+
config;
|
|
14
|
+
child = null;
|
|
15
|
+
constructor(config = {}) {
|
|
16
|
+
this.config = config;
|
|
17
|
+
}
|
|
18
|
+
async execute(prompt, workingDir, options) {
|
|
19
|
+
const output = [];
|
|
20
|
+
const cwd = workingDir || process.cwd();
|
|
21
|
+
const startTime = Date.now();
|
|
22
|
+
const d = (message) => console.log(`[hermes] ${truncateConsoleMessage(message)}`);
|
|
23
|
+
const agentDir = options?.configDir;
|
|
24
|
+
const hermesHome = agentDir ? join(agentDir, '.hermes') : undefined;
|
|
25
|
+
let functionToolBridge;
|
|
26
|
+
try {
|
|
27
|
+
functionToolBridge = await startCodexFunctionToolBridge(options?.functionTools, d);
|
|
28
|
+
const mcpServers = withFunctionToolBridge(options?.mcpServers, functionToolBridge);
|
|
29
|
+
if (hermesHome)
|
|
30
|
+
prepareHermesHome(hermesHome, this.config, agentDir, options?.skills, mcpServers, d);
|
|
31
|
+
const args = buildHermesArgs(appendOutputStyleToPrompt(prompt, options?.outputStyle), this.config, options);
|
|
32
|
+
const cliProvider = getHermesCliProvider(this.config.provider);
|
|
33
|
+
d(`starting | cwd=${cwd} model=${this.config.model ?? 'profile-default'} provider=${cliProvider ?? 'profile-default'} requestedProvider=${this.config.provider ?? 'profile-default'} hermesHome=${hermesHome ?? 'default'} mcpServers=${Object.keys(mcpServers ?? {}).join(',') || '-'} functionToolBridge=${functionToolBridge?.url ?? '-'} skills=${options?.skills?.join(',') || '-'}`);
|
|
34
|
+
return await new Promise((resolve) => {
|
|
35
|
+
let settled = false;
|
|
36
|
+
let stdoutBuffer = '';
|
|
37
|
+
let stderrBuffer = '';
|
|
38
|
+
let sessionId = options?.resumeSessionId;
|
|
39
|
+
let emittedSessionId = sessionId;
|
|
40
|
+
const lineParser = createHermesLineParser(output, options);
|
|
41
|
+
const finish = (result) => {
|
|
42
|
+
if (settled)
|
|
43
|
+
return;
|
|
44
|
+
settled = true;
|
|
45
|
+
this.child = null;
|
|
46
|
+
resolve(result);
|
|
47
|
+
};
|
|
48
|
+
try {
|
|
49
|
+
this.child = spawn(resolveHermesCommand(), args, {
|
|
50
|
+
cwd,
|
|
51
|
+
env: buildEnv(this.config, hermesHome),
|
|
52
|
+
windowsHide: true,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
57
|
+
d(`failed to start | ${message}`);
|
|
58
|
+
finish({
|
|
59
|
+
success: false,
|
|
60
|
+
summary: 'Hermes execution failed',
|
|
61
|
+
artifacts: [],
|
|
62
|
+
error: message,
|
|
63
|
+
output,
|
|
64
|
+
sessionId,
|
|
65
|
+
});
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
this.child.stdout.setEncoding('utf-8');
|
|
69
|
+
this.child.stderr.setEncoding('utf-8');
|
|
70
|
+
this.child.stdout.on('data', (chunk) => {
|
|
71
|
+
stdoutBuffer = consumeLines(stdoutBuffer + chunk, (line) => {
|
|
72
|
+
if (!line)
|
|
73
|
+
return;
|
|
74
|
+
d(`stdout | ${line}`);
|
|
75
|
+
const parsedSessionId = extractSessionId(line);
|
|
76
|
+
if (parsedSessionId && parsedSessionId !== emittedSessionId) {
|
|
77
|
+
sessionId = parsedSessionId;
|
|
78
|
+
emittedSessionId = parsedSessionId;
|
|
79
|
+
options?.onEvent?.({ type: 'session', sessionId });
|
|
80
|
+
}
|
|
81
|
+
lineParser.handleLine(line, 'stdout');
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
this.child.stderr.on('data', (chunk) => {
|
|
85
|
+
stderrBuffer = consumeLines(stderrBuffer + chunk, (line) => {
|
|
86
|
+
if (!line)
|
|
87
|
+
return;
|
|
88
|
+
d(`stderr | ${line}`);
|
|
89
|
+
lineParser.handleLine(line, 'stderr');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
this.child.on('error', (err) => {
|
|
93
|
+
const message = err.message.includes('ENOENT')
|
|
94
|
+
? 'Hermes CLI was not found. Install Hermes and ensure the `hermes` command is available on PATH.'
|
|
95
|
+
: err.message;
|
|
96
|
+
d(`failed ${Date.now() - startTime}ms | ${message}`);
|
|
97
|
+
finish({
|
|
98
|
+
success: false,
|
|
99
|
+
summary: 'Hermes execution failed',
|
|
100
|
+
artifacts: [],
|
|
101
|
+
error: message,
|
|
102
|
+
output,
|
|
103
|
+
sessionId,
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
this.child.on('close', (code, signal) => {
|
|
107
|
+
stdoutBuffer = flushLine(stdoutBuffer, (line) => lineParser.handleLine(line, 'stdout'));
|
|
108
|
+
stderrBuffer = flushLine(stderrBuffer, (line) => lineParser.handleLine(line, 'stderr'));
|
|
109
|
+
const elapsed = Date.now() - startTime;
|
|
110
|
+
if (code === 0) {
|
|
111
|
+
const text = lastMeaningfulLine(output);
|
|
112
|
+
d(`done ${elapsed}ms`);
|
|
113
|
+
finish({
|
|
114
|
+
success: true,
|
|
115
|
+
summary: summarizeResult(text),
|
|
116
|
+
artifacts: [],
|
|
117
|
+
output,
|
|
118
|
+
sessionId,
|
|
119
|
+
});
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const error = signal
|
|
123
|
+
? `Hermes execution stopped by signal ${signal}`
|
|
124
|
+
: `Hermes execution failed with exit code ${code ?? 'unknown'}`;
|
|
125
|
+
d(`failed ${elapsed}ms | ${error}`);
|
|
126
|
+
finish({
|
|
127
|
+
success: false,
|
|
128
|
+
summary: 'Hermes execution failed',
|
|
129
|
+
artifacts: [],
|
|
130
|
+
error,
|
|
131
|
+
output,
|
|
132
|
+
sessionId,
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
const elapsed = Date.now() - startTime;
|
|
139
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
140
|
+
d(`failed ${elapsed}ms | ${message}`);
|
|
141
|
+
if (err instanceof Error && err.stack)
|
|
142
|
+
console.error(err.stack);
|
|
143
|
+
return {
|
|
144
|
+
success: false,
|
|
145
|
+
summary: 'Hermes execution failed',
|
|
146
|
+
artifacts: [],
|
|
147
|
+
error: message,
|
|
148
|
+
output,
|
|
149
|
+
sessionId: options?.resumeSessionId,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
try {
|
|
154
|
+
await functionToolBridge?.close();
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
158
|
+
d(`function tool bridge close failed | ${message}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
stop() {
|
|
163
|
+
this.child?.kill();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function buildHermesArgs(prompt, config, options) {
|
|
167
|
+
const args = ['chat', '-q', prompt, '--verbose'];
|
|
168
|
+
for (const skill of normalizeSkillNames(options?.skills))
|
|
169
|
+
args.push('-s', skill);
|
|
170
|
+
if (config.model)
|
|
171
|
+
args.push('--model', config.model);
|
|
172
|
+
const provider = getHermesCliProvider(config.provider);
|
|
173
|
+
if (provider)
|
|
174
|
+
args.push('--provider', provider);
|
|
175
|
+
return args;
|
|
176
|
+
}
|
|
177
|
+
function getHermesCliProvider(provider) {
|
|
178
|
+
if (!provider || isAgentSpacesProtocolProvider(provider))
|
|
179
|
+
return undefined;
|
|
180
|
+
return String(provider);
|
|
181
|
+
}
|
|
182
|
+
function isAgentSpacesProtocolProvider(provider) {
|
|
183
|
+
return provider === 'anthropic-messages'
|
|
184
|
+
|| provider === 'openai-chat-completions'
|
|
185
|
+
|| provider === 'openai-responses'
|
|
186
|
+
|| provider === 'openai-responses-to-anthropic-messages'
|
|
187
|
+
|| provider === 'openai-chat-completions-to-anthropic-messages'
|
|
188
|
+
|| provider === 'gemini-generate-content';
|
|
189
|
+
}
|
|
190
|
+
function buildEnv(config, hermesHome) {
|
|
191
|
+
const apiKey = config.apiKey || process.env.HERMES_API_KEY || process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY;
|
|
192
|
+
return {
|
|
193
|
+
...process.env,
|
|
194
|
+
AGENT_SPACES_HERMES_API_KEY: apiKey,
|
|
195
|
+
HERMES_HOME: hermesHome || process.env.HERMES_HOME,
|
|
196
|
+
HERMES_API_KEY: apiKey,
|
|
197
|
+
HERMES_BASE_URL: config.baseURL || process.env.HERMES_BASE_URL,
|
|
198
|
+
OPENAI_API_KEY: config.apiKey || process.env.OPENAI_API_KEY,
|
|
199
|
+
OPENAI_BASE_URL: config.baseURL || process.env.OPENAI_BASE_URL,
|
|
200
|
+
ANTHROPIC_API_KEY: config.apiKey || process.env.ANTHROPIC_API_KEY,
|
|
201
|
+
NO_COLOR: process.env.NO_COLOR || '1',
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function resolveHermesCommand() {
|
|
205
|
+
const configured = process.env.HERMES_CLI_PATH?.trim();
|
|
206
|
+
if (configured)
|
|
207
|
+
return configured;
|
|
208
|
+
if (process.platform !== 'win32')
|
|
209
|
+
return 'hermes';
|
|
210
|
+
const pathCommand = resolveWindowsPathCommand('hermes.exe');
|
|
211
|
+
if (pathCommand)
|
|
212
|
+
return pathCommand;
|
|
213
|
+
const candidates = [
|
|
214
|
+
process.env.LOCALAPPDATA
|
|
215
|
+
? join(process.env.LOCALAPPDATA, 'hermes', 'hermes-agent', 'venv', 'Scripts', 'hermes.exe')
|
|
216
|
+
: undefined,
|
|
217
|
+
process.env.USERPROFILE
|
|
218
|
+
? join(process.env.USERPROFILE, 'AppData', 'Local', 'hermes', 'hermes-agent', 'venv', 'Scripts', 'hermes.exe')
|
|
219
|
+
: undefined,
|
|
220
|
+
].filter((candidate) => Boolean(candidate));
|
|
221
|
+
return candidates.find((candidate) => existsSync(candidate)) ?? 'hermes';
|
|
222
|
+
}
|
|
223
|
+
function resolveWindowsPathCommand(command) {
|
|
224
|
+
const pathValue = getPathEnvValue();
|
|
225
|
+
if (!pathValue)
|
|
226
|
+
return undefined;
|
|
227
|
+
for (const dir of pathValue.split(delimiter)) {
|
|
228
|
+
if (!dir.trim())
|
|
229
|
+
continue;
|
|
230
|
+
const candidate = join(dir.trim(), command);
|
|
231
|
+
if (existsSync(candidate))
|
|
232
|
+
return candidate;
|
|
233
|
+
}
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
function getPathEnvValue() {
|
|
237
|
+
return Object.entries(process.env).find(([key]) => key.toLowerCase() === 'path')?.[1];
|
|
238
|
+
}
|
|
239
|
+
function prepareHermesHome(hermesHome, config, agentDir, requestedSkills, mcpServers, log) {
|
|
240
|
+
mkdirSync(hermesHome, { recursive: true });
|
|
241
|
+
writeManagedHermesConfig(hermesHome, config, mcpServers, log);
|
|
242
|
+
const skillsDir = join(hermesHome, 'skills');
|
|
243
|
+
rmSync(skillsDir, { recursive: true, force: true });
|
|
244
|
+
mkdirSync(skillsDir, { recursive: true });
|
|
245
|
+
if (!agentDir)
|
|
246
|
+
return;
|
|
247
|
+
const sourceSkillsDir = join(agentDir, 'skills');
|
|
248
|
+
if (!existsSync(sourceSkillsDir))
|
|
249
|
+
return;
|
|
250
|
+
for (const file of readdirSync(sourceSkillsDir)) {
|
|
251
|
+
if (extname(file).toLowerCase() !== '.md')
|
|
252
|
+
continue;
|
|
253
|
+
const sourceFile = join(sourceSkillsDir, file);
|
|
254
|
+
if (!statSync(sourceFile).isFile())
|
|
255
|
+
continue;
|
|
256
|
+
copyFileSync(sourceFile, join(skillsDir, basename(file)));
|
|
257
|
+
}
|
|
258
|
+
for (const skill of normalizeSkillNames(requestedSkills)) {
|
|
259
|
+
const sourceFile = join(sourceSkillsDir, `${skill}.md`);
|
|
260
|
+
const targetFile = join(skillsDir, `${skill}.md`);
|
|
261
|
+
if (existsSync(targetFile) || !existsSync(sourceFile) || !statSync(sourceFile).isFile())
|
|
262
|
+
continue;
|
|
263
|
+
copyFileSync(sourceFile, targetFile);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function writeManagedHermesConfig(hermesHome, config, mcpServers, log) {
|
|
267
|
+
const normalizedMcpServers = normalizeHermesMcpServers(mcpServers);
|
|
268
|
+
const hasModelConfig = Boolean(config.baseURL && config.model && isAgentSpacesProtocolProvider(config.provider));
|
|
269
|
+
if (!hasModelConfig && !normalizedMcpServers)
|
|
270
|
+
return;
|
|
271
|
+
const configPath = join(hermesHome, 'config.yaml');
|
|
272
|
+
if (existsSync(configPath)) {
|
|
273
|
+
const content = readFileSync(configPath, 'utf-8');
|
|
274
|
+
if (!isWritableAgentSpacesHermesConfigContent(content)) {
|
|
275
|
+
if (!normalizedMcpServers)
|
|
276
|
+
return;
|
|
277
|
+
const nextContent = mergeManagedHermesAgentSpacesConfig(content, normalizedMcpServers);
|
|
278
|
+
if (nextContent !== content) {
|
|
279
|
+
writeFileSync(configPath, nextContent, 'utf-8');
|
|
280
|
+
log?.(`merged Hermes MCP config | path=${configPath} servers=${Object.keys(normalizedMcpServers ?? {}).join(',') || '-'}`);
|
|
281
|
+
}
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
const content = buildManagedHermesConfig(config, normalizedMcpServers);
|
|
286
|
+
writeFileSync(configPath, content, 'utf-8');
|
|
287
|
+
if (normalizedMcpServers) {
|
|
288
|
+
log?.(`wrote Hermes MCP config | path=${configPath} servers=${Object.keys(normalizedMcpServers).join(',')}`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function buildManagedHermesConfig(config, normalizedMcpServers) {
|
|
292
|
+
const hasModelConfig = Boolean(config.baseURL && config.model && isAgentSpacesProtocolProvider(config.provider));
|
|
293
|
+
const lines = [
|
|
294
|
+
'# Managed by Agent Spaces for this agent profile.',
|
|
295
|
+
];
|
|
296
|
+
if (hasModelConfig) {
|
|
297
|
+
const apiMode = getHermesApiMode(config.provider, config.baseURL);
|
|
298
|
+
lines.push('model:', ` default: ${yamlString(config.model)}`, ' provider: custom', ` base_url: ${yamlString(config.baseURL)}`, ' api_key: ${AGENT_SPACES_HERMES_API_KEY}');
|
|
299
|
+
if (apiMode)
|
|
300
|
+
lines.push(` api_mode: ${apiMode}`);
|
|
301
|
+
}
|
|
302
|
+
if (normalizedMcpServers) {
|
|
303
|
+
if (lines.length > 1)
|
|
304
|
+
lines.push('');
|
|
305
|
+
lines.push('mcp_servers:');
|
|
306
|
+
lines.push(...yamlMappingLines(normalizedMcpServers, 2));
|
|
307
|
+
}
|
|
308
|
+
return `${lines.join('\n')}\n`;
|
|
309
|
+
}
|
|
310
|
+
const AGENT_SPACES_MCP_START = ' # Agent Spaces managed MCP servers start';
|
|
311
|
+
const AGENT_SPACES_MCP_END = ' # Agent Spaces managed MCP servers end';
|
|
312
|
+
function mergeManagedHermesAgentSpacesConfig(content, normalizedMcpServers) {
|
|
313
|
+
return mergeManagedHermesMcpServers(content, normalizedMcpServers);
|
|
314
|
+
}
|
|
315
|
+
function mergeManagedHermesMcpServers(content, normalizedMcpServers) {
|
|
316
|
+
const lines = content.replace(/\r\n/g, '\n').split('\n');
|
|
317
|
+
if (lines.at(-1) === '')
|
|
318
|
+
lines.pop();
|
|
319
|
+
const mcpStart = lines.findIndex((line) => /^mcp_servers\s*:\s*(?:#.*)?$/.test(line));
|
|
320
|
+
const managedBlock = normalizedMcpServers
|
|
321
|
+
? [AGENT_SPACES_MCP_START, ...yamlMappingLines(normalizedMcpServers, 2), AGENT_SPACES_MCP_END]
|
|
322
|
+
: [];
|
|
323
|
+
if (mcpStart === -1) {
|
|
324
|
+
if (!managedBlock.length)
|
|
325
|
+
return content;
|
|
326
|
+
const nextLines = [...lines];
|
|
327
|
+
if (nextLines.length && nextLines.at(-1)?.trim())
|
|
328
|
+
nextLines.push('');
|
|
329
|
+
nextLines.push('mcp_servers:', ...managedBlock);
|
|
330
|
+
return `${nextLines.join('\n')}\n`;
|
|
331
|
+
}
|
|
332
|
+
const mcpEnd = findNextTopLevelYamlKey(lines, mcpStart + 1);
|
|
333
|
+
const before = lines.slice(0, mcpStart + 1);
|
|
334
|
+
const mcpBody = removeManagedHermesMcpEntries(lines.slice(mcpStart + 1, mcpEnd), Object.keys(normalizedMcpServers ?? {}));
|
|
335
|
+
const after = lines.slice(mcpEnd);
|
|
336
|
+
const nextLines = [
|
|
337
|
+
...before,
|
|
338
|
+
...managedBlock,
|
|
339
|
+
...mcpBody,
|
|
340
|
+
...after,
|
|
341
|
+
];
|
|
342
|
+
return `${nextLines.join('\n')}\n`;
|
|
343
|
+
}
|
|
344
|
+
function findNextTopLevelYamlKey(lines, startIndex) {
|
|
345
|
+
for (let index = startIndex; index < lines.length; index += 1) {
|
|
346
|
+
const line = lines[index];
|
|
347
|
+
if (!line.trim() || line.trimStart().startsWith('#'))
|
|
348
|
+
continue;
|
|
349
|
+
if (/^\S[^:]*:\s*/.test(line))
|
|
350
|
+
return index;
|
|
351
|
+
}
|
|
352
|
+
return lines.length;
|
|
353
|
+
}
|
|
354
|
+
function removeManagedHermesMcpEntries(lines, managedServerNames) {
|
|
355
|
+
const managedNameKeys = new Set(managedServerNames.flatMap((name) => [yamlKey(name), yamlString(name)]));
|
|
356
|
+
const result = [];
|
|
357
|
+
for (let index = 0; index < lines.length;) {
|
|
358
|
+
const line = lines[index];
|
|
359
|
+
if (line === AGENT_SPACES_MCP_START) {
|
|
360
|
+
index += 1;
|
|
361
|
+
while (index < lines.length && lines[index] !== AGENT_SPACES_MCP_END)
|
|
362
|
+
index += 1;
|
|
363
|
+
if (index < lines.length)
|
|
364
|
+
index += 1;
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
const entryKey = readIndentedYamlKey(line, 2);
|
|
368
|
+
if (entryKey && managedNameKeys.has(entryKey)) {
|
|
369
|
+
index += 1;
|
|
370
|
+
while (index < lines.length && !readIndentedYamlKey(lines[index], 2))
|
|
371
|
+
index += 1;
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
result.push(line);
|
|
375
|
+
index += 1;
|
|
376
|
+
}
|
|
377
|
+
return result;
|
|
378
|
+
}
|
|
379
|
+
function readIndentedYamlKey(line, indent) {
|
|
380
|
+
if (!line.startsWith(' '.repeat(indent)) || line.startsWith(' '.repeat(indent + 1)))
|
|
381
|
+
return undefined;
|
|
382
|
+
const match = line.slice(indent).match(/^((?:"(?:\\.|[^"])*")|[A-Za-z0-9_-]+)\s*:/);
|
|
383
|
+
return match?.[1];
|
|
384
|
+
}
|
|
385
|
+
function withFunctionToolBridge(mcpServers, bridge) {
|
|
386
|
+
if (!bridge)
|
|
387
|
+
return mcpServers;
|
|
388
|
+
return {
|
|
389
|
+
...(mcpServers ?? {}),
|
|
390
|
+
[bridge.name]: { url: bridge.url },
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
function normalizeHermesMcpServers(servers) {
|
|
394
|
+
if (!servers || Object.keys(servers).length === 0)
|
|
395
|
+
return undefined;
|
|
396
|
+
const normalized = Object.fromEntries(Object.entries(servers).flatMap(([name, server]) => {
|
|
397
|
+
const normalizedServer = normalizeHermesMcpServer(server);
|
|
398
|
+
return normalizedServer ? [[name, normalizedServer]] : [];
|
|
399
|
+
}));
|
|
400
|
+
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
401
|
+
}
|
|
402
|
+
function normalizeHermesMcpServer(server) {
|
|
403
|
+
if (!server || typeof server !== 'object' || Array.isArray(server))
|
|
404
|
+
return null;
|
|
405
|
+
const record = server;
|
|
406
|
+
if (typeof record.command === 'string') {
|
|
407
|
+
return removeUndefined({
|
|
408
|
+
command: record.command,
|
|
409
|
+
args: Array.isArray(record.args) ? record.args.map(String) : undefined,
|
|
410
|
+
env: isRecord(record.env) ? record.env : undefined,
|
|
411
|
+
ssl_verify: isBooleanOrString(record.ssl_verify) ? record.ssl_verify : undefined,
|
|
412
|
+
client_cert: isStringOrStringArray(record.client_cert) ? record.client_cert : undefined,
|
|
413
|
+
client_key: typeof record.client_key === 'string' ? record.client_key : undefined,
|
|
414
|
+
enabled: typeof record.enabled === 'boolean' ? record.enabled : true,
|
|
415
|
+
timeout: typeof record.timeout === 'number' ? record.timeout : undefined,
|
|
416
|
+
connect_timeout: typeof record.connect_timeout === 'number' ? record.connect_timeout : undefined,
|
|
417
|
+
supports_parallel_tool_calls: typeof record.supports_parallel_tool_calls === 'boolean'
|
|
418
|
+
? record.supports_parallel_tool_calls
|
|
419
|
+
: undefined,
|
|
420
|
+
tools: isRecord(record.tools) ? record.tools : undefined,
|
|
421
|
+
sampling: isRecord(record.sampling) ? record.sampling : undefined,
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
if (typeof record.url === 'string') {
|
|
425
|
+
return removeUndefined({
|
|
426
|
+
url: record.url,
|
|
427
|
+
headers: isRecord(record.headers) ? record.headers : undefined,
|
|
428
|
+
ssl_verify: isBooleanOrString(record.ssl_verify) ? record.ssl_verify : undefined,
|
|
429
|
+
client_cert: isStringOrStringArray(record.client_cert) ? record.client_cert : undefined,
|
|
430
|
+
client_key: typeof record.client_key === 'string' ? record.client_key : undefined,
|
|
431
|
+
enabled: typeof record.enabled === 'boolean' ? record.enabled : true,
|
|
432
|
+
timeout: typeof record.timeout === 'number' ? record.timeout : undefined,
|
|
433
|
+
connect_timeout: typeof record.connect_timeout === 'number' ? record.connect_timeout : undefined,
|
|
434
|
+
supports_parallel_tool_calls: typeof record.supports_parallel_tool_calls === 'boolean'
|
|
435
|
+
? record.supports_parallel_tool_calls
|
|
436
|
+
: undefined,
|
|
437
|
+
tools: isRecord(record.tools) ? record.tools : undefined,
|
|
438
|
+
auth: typeof record.auth === 'string' ? record.auth : undefined,
|
|
439
|
+
sampling: isRecord(record.sampling) ? record.sampling : undefined,
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
function yamlMappingLines(value, indent) {
|
|
445
|
+
return Object.entries(value).flatMap(([key, child]) => yamlEntryLines(key, child, indent));
|
|
446
|
+
}
|
|
447
|
+
function yamlEntryLines(key, value, indent) {
|
|
448
|
+
const prefix = ' '.repeat(indent);
|
|
449
|
+
if (Array.isArray(value)) {
|
|
450
|
+
if (value.length === 0)
|
|
451
|
+
return [`${prefix}${yamlKey(key)}: []`];
|
|
452
|
+
return [
|
|
453
|
+
`${prefix}${yamlKey(key)}:`,
|
|
454
|
+
...value.map((item) => `${' '.repeat(indent + 2)}- ${yamlInlineValue(item)}`),
|
|
455
|
+
];
|
|
456
|
+
}
|
|
457
|
+
if (isRecord(value)) {
|
|
458
|
+
const entries = yamlMappingLines(value, indent + 2);
|
|
459
|
+
return entries.length ? [`${prefix}${yamlKey(key)}:`, ...entries] : [`${prefix}${yamlKey(key)}: {}`];
|
|
460
|
+
}
|
|
461
|
+
return [`${prefix}${yamlKey(key)}: ${yamlInlineValue(value)}`];
|
|
462
|
+
}
|
|
463
|
+
function yamlInlineValue(value) {
|
|
464
|
+
if (typeof value === 'string')
|
|
465
|
+
return yamlString(value);
|
|
466
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
467
|
+
return String(value);
|
|
468
|
+
if (value === null)
|
|
469
|
+
return 'null';
|
|
470
|
+
return yamlString(JSON.stringify(value));
|
|
471
|
+
}
|
|
472
|
+
function yamlKey(value) {
|
|
473
|
+
return /^[A-Za-z0-9_-]+$/.test(value) ? value : yamlString(value);
|
|
474
|
+
}
|
|
475
|
+
function removeUndefined(record) {
|
|
476
|
+
return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
|
|
477
|
+
}
|
|
478
|
+
function isRecord(value) {
|
|
479
|
+
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
480
|
+
}
|
|
481
|
+
function isBooleanOrString(value) {
|
|
482
|
+
return typeof value === 'boolean' || typeof value === 'string';
|
|
483
|
+
}
|
|
484
|
+
function isStringOrStringArray(value) {
|
|
485
|
+
return typeof value === 'string' || (Array.isArray(value) && value.every((item) => typeof item === 'string'));
|
|
486
|
+
}
|
|
487
|
+
function isWritableAgentSpacesHermesConfigContent(content) {
|
|
488
|
+
return content.startsWith('# Managed by Agent Spaces for this agent profile.')
|
|
489
|
+
|| content.includes('api_key: ${AGENT_SPACES_HERMES_API_KEY}');
|
|
490
|
+
}
|
|
491
|
+
function getHermesApiMode(provider, baseURL) {
|
|
492
|
+
switch (provider) {
|
|
493
|
+
case 'anthropic-messages':
|
|
494
|
+
return isAnthropicCompatibleBaseURL(baseURL) ? 'anthropic_messages' : 'chat_completions';
|
|
495
|
+
case 'openai-chat-completions':
|
|
496
|
+
case 'openai-chat-completions-to-anthropic-messages':
|
|
497
|
+
return 'chat_completions';
|
|
498
|
+
case 'openai-responses':
|
|
499
|
+
case 'openai-responses-to-anthropic-messages':
|
|
500
|
+
return 'codex_responses';
|
|
501
|
+
default:
|
|
502
|
+
return undefined;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
function isAnthropicCompatibleBaseURL(baseURL) {
|
|
506
|
+
if (!baseURL)
|
|
507
|
+
return false;
|
|
508
|
+
try {
|
|
509
|
+
const url = new URL(baseURL);
|
|
510
|
+
return url.hostname.toLowerCase().endsWith('anthropic.com')
|
|
511
|
+
|| url.pathname.toLowerCase().split('/').includes('anthropic');
|
|
512
|
+
}
|
|
513
|
+
catch {
|
|
514
|
+
return false;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
function yamlString(value) {
|
|
518
|
+
return JSON.stringify(value);
|
|
519
|
+
}
|
|
520
|
+
function truncateConsoleMessage(message) {
|
|
521
|
+
const maxLength = 600;
|
|
522
|
+
if (message.length <= maxLength)
|
|
523
|
+
return message;
|
|
524
|
+
return `${message.slice(0, maxLength)}... [truncated ${message.length - maxLength} chars]`;
|
|
525
|
+
}
|
|
526
|
+
function consumeLines(buffer, onLine) {
|
|
527
|
+
const lines = buffer.split(/\r?\n/);
|
|
528
|
+
const remainder = lines.pop() ?? '';
|
|
529
|
+
for (const line of lines)
|
|
530
|
+
onLine(stripAnsi(line).trimEnd());
|
|
531
|
+
return remainder;
|
|
532
|
+
}
|
|
533
|
+
function flushLine(buffer, onLine) {
|
|
534
|
+
const line = stripAnsi(buffer).trimEnd();
|
|
535
|
+
if (line)
|
|
536
|
+
onLine(line);
|
|
537
|
+
return '';
|
|
538
|
+
}
|
|
539
|
+
function extractSessionId(line) {
|
|
540
|
+
const match = line.match(/\bsession(?:\s+id)?\s*[:=]\s*([a-zA-Z0-9._:-]+)/i);
|
|
541
|
+
return match?.[1];
|
|
542
|
+
}
|
|
543
|
+
function normalizeSkillNames(skills) {
|
|
544
|
+
if (!Array.isArray(skills))
|
|
545
|
+
return [];
|
|
546
|
+
return skills
|
|
547
|
+
.map((skill) => basename(skill).replace(/\.md$/i, '').trim())
|
|
548
|
+
.filter(Boolean);
|
|
549
|
+
}
|
|
550
|
+
function lastMeaningfulLine(output) {
|
|
551
|
+
return [...output].reverse().find((line) => line.trim() && !isDiagnosticOutputLine(line)) ?? '';
|
|
552
|
+
}
|
|
553
|
+
function stripAnsi(value) {
|
|
554
|
+
return value.replace(/\u001b\[[0-9;]*m/g, '');
|
|
555
|
+
}
|
|
556
|
+
function createHermesLineParser(output, options) {
|
|
557
|
+
let inEchoedQuery = false;
|
|
558
|
+
let inRuntimeConfig = false;
|
|
559
|
+
let inUserMessageEcho = false;
|
|
560
|
+
let inVerboseArgsBlock = false;
|
|
561
|
+
let inVerboseResultBlock = false;
|
|
562
|
+
let inVerboseThinkingBlock = false;
|
|
563
|
+
let inCapturedReasoningBlock = false;
|
|
564
|
+
let toolUseIndex = 0;
|
|
565
|
+
let emittedUsage = false;
|
|
566
|
+
const emitOutput = (line) => {
|
|
567
|
+
output.push(line);
|
|
568
|
+
options?.onEvent?.({ type: 'output', line });
|
|
569
|
+
};
|
|
570
|
+
const emitReasoning = (text) => {
|
|
571
|
+
const normalized = text.trim();
|
|
572
|
+
if (!normalized)
|
|
573
|
+
return;
|
|
574
|
+
options?.onEvent?.({ type: 'reasoning', text: normalized, status: 'completed' });
|
|
575
|
+
};
|
|
576
|
+
const emitUsage = (usageLine) => {
|
|
577
|
+
if (emittedUsage)
|
|
578
|
+
return;
|
|
579
|
+
emittedUsage = true;
|
|
580
|
+
emitOutput(usageLine);
|
|
581
|
+
};
|
|
582
|
+
const emitToolUse = (tool) => {
|
|
583
|
+
toolUseIndex += 1;
|
|
584
|
+
const id = `hermes-tool-${toolUseIndex}`;
|
|
585
|
+
const line = tool.rawInput ? `Tool: ${tool.name} ${tool.rawInput}` : `Tool: ${tool.name}`;
|
|
586
|
+
options?.onEvent?.({
|
|
587
|
+
type: 'tool_use',
|
|
588
|
+
id,
|
|
589
|
+
name: tool.name,
|
|
590
|
+
input: tool.input,
|
|
591
|
+
line,
|
|
592
|
+
});
|
|
593
|
+
};
|
|
594
|
+
return {
|
|
595
|
+
handleLine(line, source) {
|
|
596
|
+
const normalized = line.trim();
|
|
597
|
+
if (!normalized)
|
|
598
|
+
return;
|
|
599
|
+
if (inCapturedReasoningBlock) {
|
|
600
|
+
if (source === 'stderr' && !isHermesLogLine(normalized)) {
|
|
601
|
+
emitReasoning(normalized);
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
if (source === 'stderr')
|
|
605
|
+
inCapturedReasoningBlock = false;
|
|
606
|
+
}
|
|
607
|
+
const capturedReasoning = parseCapturedReasoningStart(normalized);
|
|
608
|
+
if (capturedReasoning !== undefined) {
|
|
609
|
+
if (capturedReasoning)
|
|
610
|
+
emitReasoning(capturedReasoning);
|
|
611
|
+
inCapturedReasoningBlock = true;
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
const toolUse = parseHermesToolUse(normalized);
|
|
615
|
+
if (toolUse) {
|
|
616
|
+
emitToolUse(toolUse);
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
const usageLine = parseHermesUsageLine(normalized);
|
|
620
|
+
if (usageLine) {
|
|
621
|
+
emitUsage(usageLine);
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
const reasoning = parseHermesReasoningLine(normalized);
|
|
625
|
+
if (reasoning) {
|
|
626
|
+
emitReasoning(reasoning);
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
if (source === 'stderr')
|
|
630
|
+
return;
|
|
631
|
+
if (inVerboseThinkingBlock) {
|
|
632
|
+
if (!isHermesThinkingContinuationLine(normalized)) {
|
|
633
|
+
inVerboseThinkingBlock = false;
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
emitReasoning(normalized);
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
if (inVerboseResultBlock) {
|
|
641
|
+
if (isHermesVerboseBlockBoundaryLine(normalized))
|
|
642
|
+
inVerboseResultBlock = false;
|
|
643
|
+
else {
|
|
644
|
+
if (normalized.endsWith('}'))
|
|
645
|
+
inVerboseResultBlock = false;
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
if (inVerboseArgsBlock) {
|
|
650
|
+
if (normalized === '}')
|
|
651
|
+
inVerboseArgsBlock = false;
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
if (inUserMessageEcho)
|
|
655
|
+
return;
|
|
656
|
+
if (/^\[thinking\]/i.test(normalized)) {
|
|
657
|
+
emitReasoning(normalized.replace(/^\[thinking\]\s*/i, ''));
|
|
658
|
+
inVerboseThinkingBlock = true;
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
if (/^Args:\s*\{$/i.test(normalized)) {
|
|
662
|
+
inVerboseArgsBlock = true;
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
if (/^Result:\s*/i.test(normalized)) {
|
|
666
|
+
if (!normalized.endsWith('}'))
|
|
667
|
+
inVerboseResultBlock = true;
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
if (/^Query:\s*/i.test(normalized)) {
|
|
671
|
+
inEchoedQuery = true;
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
if (inEchoedQuery && isHermesRuntimeBoundaryLine(normalized)) {
|
|
675
|
+
inEchoedQuery = false;
|
|
676
|
+
}
|
|
677
|
+
if (/^Agent runtime configuration:\s*$/i.test(normalized)) {
|
|
678
|
+
inEchoedQuery = false;
|
|
679
|
+
inRuntimeConfig = true;
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
if (inEchoedQuery)
|
|
683
|
+
return;
|
|
684
|
+
if (inRuntimeConfig) {
|
|
685
|
+
if (/^User message:\s*$/i.test(normalized)) {
|
|
686
|
+
inRuntimeConfig = false;
|
|
687
|
+
inUserMessageEcho = true;
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
if (isHermesRuntimeConfigLine(normalized))
|
|
691
|
+
return;
|
|
692
|
+
inRuntimeConfig = false;
|
|
693
|
+
}
|
|
694
|
+
if (isHermesNoiseLine(normalized))
|
|
695
|
+
return;
|
|
696
|
+
emitOutput(normalized);
|
|
697
|
+
},
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
function parseHermesToolUse(line) {
|
|
701
|
+
const match = line.match(/\bTool call:\s*([A-Za-z_][\w.-]*)\s+with args:\s*(.+)$/i);
|
|
702
|
+
if (!match)
|
|
703
|
+
return null;
|
|
704
|
+
const name = match[1];
|
|
705
|
+
const rawInput = match[2].replace(/\.\.\.$/, '').trim();
|
|
706
|
+
return {
|
|
707
|
+
name,
|
|
708
|
+
rawInput,
|
|
709
|
+
input: parseJsonObject(rawInput) ?? rawInput,
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
function parseHermesReasoningLine(line) {
|
|
713
|
+
return line.match(/\b(?:Reasoning|Thinking):\s*(.+)$/i)?.[1]?.trim();
|
|
714
|
+
}
|
|
715
|
+
function parseCapturedReasoningStart(line) {
|
|
716
|
+
const match = line.match(/\bCaptured reasoning \(\d+ chars\):\s*(.*)$/i);
|
|
717
|
+
if (!match)
|
|
718
|
+
return undefined;
|
|
719
|
+
return match[1].trim();
|
|
720
|
+
}
|
|
721
|
+
function isHermesLogLine(line) {
|
|
722
|
+
return /^\d{2}:\d{2}:\d{2}\s+-\s+/.test(line);
|
|
723
|
+
}
|
|
724
|
+
function parseHermesUsageLine(line) {
|
|
725
|
+
const apiCallMatch = line.match(/\bAPI call #\d+:.*?\bin=([\d,]+)\s+out=([\d,]+)\s+total=([\d,]+)/i);
|
|
726
|
+
if (apiCallMatch) {
|
|
727
|
+
const cache = line.match(/\bcache=([\d,]+)\/[\d,]+/i)?.[1];
|
|
728
|
+
return formatUsageLine({
|
|
729
|
+
input: apiCallMatch[1],
|
|
730
|
+
output: apiCallMatch[2],
|
|
731
|
+
total: apiCallMatch[3],
|
|
732
|
+
cached: cache,
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
const tokenUsageMatch = line.match(/\bToken usage:\s*prompt=([\d,]+),\s*completion=([\d,]+),\s*total=([\d,]+)/i);
|
|
736
|
+
if (tokenUsageMatch) {
|
|
737
|
+
return formatUsageLine({
|
|
738
|
+
input: tokenUsageMatch[1],
|
|
739
|
+
output: tokenUsageMatch[2],
|
|
740
|
+
total: tokenUsageMatch[3],
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
return undefined;
|
|
744
|
+
}
|
|
745
|
+
function formatUsageLine(usage) {
|
|
746
|
+
const parts = [
|
|
747
|
+
`[Usage] total: ${usage.total}`,
|
|
748
|
+
`input: ${usage.input}`,
|
|
749
|
+
`output: ${usage.output}`,
|
|
750
|
+
];
|
|
751
|
+
if (usage.cached)
|
|
752
|
+
parts.push(`cached: ${usage.cached}`);
|
|
753
|
+
return parts.join(' ');
|
|
754
|
+
}
|
|
755
|
+
function parseJsonObject(value) {
|
|
756
|
+
if (!value.startsWith('{') && !value.startsWith('['))
|
|
757
|
+
return undefined;
|
|
758
|
+
try {
|
|
759
|
+
return JSON.parse(value);
|
|
760
|
+
}
|
|
761
|
+
catch {
|
|
762
|
+
return undefined;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
function isHermesNoiseLine(line) {
|
|
766
|
+
return /^\u{1f916}\s*AI Agent initialized\b/iu.test(line)
|
|
767
|
+
|| /^\u{1f517}\s*Using custom base URL:/iu.test(line)
|
|
768
|
+
|| /^\u{1f511}\s*Using API key:/iu.test(line)
|
|
769
|
+
|| /^\u{1f511}\s*Using token:/iu.test(line)
|
|
770
|
+
|| isHermesRuntimeBoundaryLine(line)
|
|
771
|
+
|| isHermesVerboseUiLine(line);
|
|
772
|
+
}
|
|
773
|
+
function isHermesRuntimeBoundaryLine(line) {
|
|
774
|
+
return /^Initializing agent\b/i.test(line)
|
|
775
|
+
|| /^\u{1f916}\s*AI Agent initialized\b/iu.test(line);
|
|
776
|
+
}
|
|
777
|
+
function isHermesRuntimeConfigLine(line) {
|
|
778
|
+
return /^-\s+/.test(line)
|
|
779
|
+
|| /^(skills|directory,|paths\.|tools you have,|are|built-in runtime tools|internals, previous sessions, or filesystem settings\.)$/i.test(line)
|
|
780
|
+
|| /^For Bash commands that create or modify files under the current working\b/i.test(line)
|
|
781
|
+
|| /^When asked what MCP servers, skills, runtime tools, or Agent Spaces channel\b/i.test(line)
|
|
782
|
+
|| /^Important distinction: MCP servers configured for this agent are only the names\b/i.test(line)
|
|
783
|
+
|| /^in "MCP servers configured for this agent"\./i.test(line)
|
|
784
|
+
|| /^Do not infer availability from provider-side function names, hidden runtime\b/i.test(line);
|
|
785
|
+
}
|
|
786
|
+
function isHermesVerboseUiLine(line) {
|
|
787
|
+
return /^[\u{2705}\u{1f6e0}\u{fe0f}\u{26a0}\u{1f512}\u{1f4be}\u{1f4ca}\u{1f4ac}\u{1f389}\u{1f4de}]/u.test(line)
|
|
788
|
+
|| /^[\u{2500}\u{256d}\u{2570}\u{2502}]/u.test(line)
|
|
789
|
+
|| /^\[thinking\]/i.test(line)
|
|
790
|
+
|| /^┊\s*/u.test(line)
|
|
791
|
+
|| /^(Tool \d+:|Args:|Result:|Resume this session with:|Session:|Duration:|Messages:)/i.test(line)
|
|
792
|
+
|| /^hermes\s+--resume\b/i.test(line)
|
|
793
|
+
|| /^\{".*"\}?$/.test(line);
|
|
794
|
+
}
|
|
795
|
+
function isHermesVerboseBlockBoundaryLine(line) {
|
|
796
|
+
return /^\[thinking\]/i.test(line)
|
|
797
|
+
|| /^[\u{2705}\u{1f389}\u{1f4de}]/u.test(line)
|
|
798
|
+
|| /^[\u{2500}\u{256d}\u{2570}\u{2502}]/u.test(line)
|
|
799
|
+
|| /^(Tool \d+:|Args:|Result:|Resume this session with:|Session:|Duration:|Messages:)/i.test(line)
|
|
800
|
+
|| /^hermes\s+--resume\b/i.test(line);
|
|
801
|
+
}
|
|
802
|
+
function isHermesThinkingContinuationLine(line) {
|
|
803
|
+
return !isHermesVerboseBlockBoundaryLine(line)
|
|
804
|
+
&& !/^\[Usage\]/i.test(line)
|
|
805
|
+
&& !/^Tool:\s*/i.test(line);
|
|
806
|
+
}
|
|
807
|
+
function isDiagnosticOutputLine(line) {
|
|
808
|
+
return line.startsWith('[stderr]')
|
|
809
|
+
|| /^\[Usage\]/i.test(line)
|
|
810
|
+
|| /^Tool:\s*/i.test(line)
|
|
811
|
+
|| isHermesNoiseLine(line);
|
|
812
|
+
}
|
|
813
|
+
//# sourceMappingURL=hermes-runtime.js.map
|